| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:typed_data'; |
| import 'dart:ui' as ui; |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| const double _crispText = 100.0; // this font size is selected to avoid needing any antialiasing. |
| const String _expText = 'Éxp'; // renders in the test font as: |
| |
| // ######## |
| // ######## |
| // ######## |
| // ######## |
| // |
| // ÉÉÉÉxxxxpppp |
| |
| void main() { |
| testWidgets('Default background', (WidgetTester tester) async { |
| await tester.pumpWidget(const Align( |
| alignment: Alignment.topLeft, |
| child: Text(_expText, textDirection: TextDirection.ltr, style: TextStyle(color: Color(0xFF345678), fontSize: _crispText))), |
| ); |
| await _expectColors( |
| tester, |
| find.byType(Align), |
| <Color>{ const Color(0x00000000), const Color(0xFF345678) }, |
| <Offset, Color>{ |
| Offset.zero: const Color(0xFF345678), // the text |
| const Offset(10, 10): const Color(0xFF345678), // the text |
| const Offset(50, 95): const Color(0x00000000), // the background (under the É) |
| const Offset(250, 50): const Color(0x00000000), // the text (above the p) |
| const Offset(250, 95): const Color(0xFF345678), // the text (the p) |
| const Offset(400, 400): const Color(0x00000000), // the background |
| const Offset(799, 599): const Color(0x00000000), // the background |
| }, |
| ); |
| }, skip: !canCaptureImage); // [intended] Test relies on captureImage, which is not supported on web currently. |
| |
| testWidgets('Default text color', (WidgetTester tester) async { |
| await tester.pumpWidget(const ColoredBox( |
| color: Color(0xFFABCDEF), |
| child: Align( |
| alignment: Alignment.topLeft, |
| child: Text('Éxp', textDirection: TextDirection.ltr, style: TextStyle(fontSize: _crispText)), |
| ), |
| ), |
| ); |
| await _expectColors( |
| tester, |
| find.byType(Align), |
| <Color>{ const Color(0xFFABCDEF), const Color(0xFFFFFFFF) }, |
| <Offset, Color>{ |
| Offset.zero: const Color(0xFFFFFFFF), // the text |
| const Offset(10, 10): const Color(0xFFFFFFFF), // the text |
| const Offset(50, 95): const Color(0xFFABCDEF), // the background (under the É) |
| const Offset(250, 50): const Color(0xFFABCDEF), // the text (above the p) |
| const Offset(250, 95): const Color(0xFFFFFFFF), // the text (the p) |
| const Offset(400, 400): const Color(0xFFABCDEF), // the background |
| const Offset(799, 599): const Color(0xFFABCDEF), // the background |
| }, |
| ); |
| }, skip: !canCaptureImage); // [intended] Test relies on captureImage, which is not supported on web currently. |
| |
| testWidgets('Default text selection color', (WidgetTester tester) async { |
| final GlobalKey key = GlobalKey(); |
| await tester.pumpWidget( |
| ColoredBox( |
| color: const Color(0xFFFFFFFF), |
| child: Directionality( |
| textDirection: TextDirection.ltr, |
| child: MediaQuery( |
| data: const MediaQueryData(), |
| child: Overlay( |
| initialEntries: <OverlayEntry>[ |
| OverlayEntry( |
| builder: (BuildContext context) => SelectableRegion( |
| focusNode: FocusNode(), |
| selectionControls: emptyTextSelectionControls, |
| child: Align( |
| key: key, |
| alignment: Alignment.topLeft, |
| child: const Text('Éxp', textDirection: TextDirection.ltr, style: TextStyle(fontSize: _crispText, color: Color(0xFF000000))), |
| ), |
| ), |
| ), |
| ], |
| ), |
| ), |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| await _expectColors( |
| tester, |
| find.byType(Align), |
| <Color>{ const Color(0xFFFFFFFF), const Color(0xFF000000) }, |
| ); |
| // fake a "select all" event to select the text |
| Actions.invoke(key.currentContext!, const SelectAllTextIntent(SelectionChangedCause.keyboard)); |
| await tester.pump(); |
| await _expectColors( |
| tester, |
| find.byType(Align), |
| <Color>{ const Color(0xFFFFFFFF), const Color(0xFF000000), const Color(0xFFBFBFBF) }, // 0x80808080 blended with 0xFFFFFFFF |
| <Offset, Color>{ |
| Offset.zero: const Color(0xFF000000), // the selected text |
| const Offset(10, 10): const Color(0xFF000000), // the selected text |
| const Offset(50, 95): const Color(0xFFBFBFBF), // the selected background (under the É) |
| const Offset(250, 50): const Color(0xFFBFBFBF), // the selected background (above the p) |
| const Offset(250, 95): const Color(0xFF000000), // the selected text (the p) |
| const Offset(400, 400): const Color(0xFFFFFFFF), // the background |
| const Offset(799, 599): const Color(0xFFFFFFFF), // the background |
| }, |
| ); |
| }, skip: !canCaptureImage); // [intended] Test relies on captureImage, which is not supported on web currently. |
| } |
| |
| Color _getPixel(ByteData bytes, int x, int y, int width) { |
| final int offset = (x + y * width) * 4; |
| return Color.fromARGB( |
| bytes.getUint8(offset + 3), |
| bytes.getUint8(offset + 0), |
| bytes.getUint8(offset + 1), |
| bytes.getUint8(offset + 2), |
| ); |
| } |
| |
| Future<void> _expectColors(WidgetTester tester, Finder finder, Set<Color> allowedColors, [ Map<Offset, Color>? spotChecks ]) async { |
| final TestWidgetsFlutterBinding binding = tester.binding; |
| final ui.Image image = (await binding.runAsync<ui.Image>(() => captureImage(finder.evaluate().single)))!; |
| final ByteData bytes = (await binding.runAsync<ByteData?>(() => image.toByteData(format: ui.ImageByteFormat.rawStraightRgba)))!; |
| final Set<int> actualColorValues = <int>{}; |
| for (int offset = 0; offset < bytes.lengthInBytes; offset += 4) { |
| actualColorValues.add((bytes.getUint8(offset + 3) << 24) + |
| (bytes.getUint8(offset + 0) << 16) + |
| (bytes.getUint8(offset + 1) << 8) + |
| (bytes.getUint8(offset + 2))); |
| } |
| final Set<Color> actualColors = actualColorValues.map((int value) => Color(value)).toSet(); |
| expect(actualColors, allowedColors); |
| spotChecks?.forEach((Offset position, Color expected) { |
| assert(position.dx.round() >= 0); |
| assert(position.dx.round() < image.width); |
| assert(position.dy.round() >= 0); |
| assert(position.dy.round() < image.height); |
| final Offset precisePosition = position * tester.view.devicePixelRatio; |
| final Color actual = _getPixel(bytes, precisePosition.dx.round(), precisePosition.dy.round(), image.width); |
| expect(actual, expected, reason: 'Pixel at $position is $actual but expected $expected.'); |
| }); |
| } |