flutter/packages/flutter_test/test/matchers_test.dart

1391 lines
45 KiB
Dart
Raw Normal View History

// 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.
// flutter_ignore_for_file: golden_tag (see analyze.dart)
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
/// Class that makes it easy to mock common toStringDeep behavior.
class _MockToStringDeep {
_MockToStringDeep(String str) : _lines = <String>[] {
final List<String> lines = str.split('\n');
for (int i = 0; i < lines.length - 1; ++i) {
_lines.add('${lines[i]}\n');
}
// If the last line is empty, that really just means that the previous
// line was terminated with a line break.
if (lines.isNotEmpty && lines.last.isNotEmpty) {
_lines.add(lines.last);
}
}
_MockToStringDeep.fromLines(this._lines);
/// Lines in the message to display when [toStringDeep] is called.
/// For correct toStringDeep behavior, each line should be terminated with a
/// line break.
final List<String> _lines;
String toStringDeep({ String prefixLineOne = '', String prefixOtherLines = '' }) {
final StringBuffer sb = StringBuffer();
if (_lines.isNotEmpty) {
sb.write('$prefixLineOne${_lines.first}');
}
for (int i = 1; i < _lines.length; ++i) {
sb.write('$prefixOtherLines${_lines[i]}');
}
return sb.toString();
}
@override
String toString() => toStringDeep();
}
void main() {
test('hasOneLineDescription', () {
expect('Hello', hasOneLineDescription);
expect('Hello\nHello', isNot(hasOneLineDescription));
expect(' Hello', isNot(hasOneLineDescription));
expect('Hello ', isNot(hasOneLineDescription));
expect(Object(), isNot(hasOneLineDescription));
});
test('hasAGoodToStringDeep', () {
expect(_MockToStringDeep('Hello\n World\n'), hasAGoodToStringDeep);
// Not terminated with a line break.
expect(_MockToStringDeep('Hello\n World'), isNot(hasAGoodToStringDeep));
// Trailing whitespace on last line.
expect(_MockToStringDeep('Hello\n World \n'),
isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('Hello\n World\t\n'),
isNot(hasAGoodToStringDeep));
// Leading whitespace on line 1.
expect(_MockToStringDeep(' Hello\n World \n'),
isNot(hasAGoodToStringDeep));
// Single line.
expect(_MockToStringDeep('Hello World'), isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('Hello World\n'), isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('Hello: World\nFoo: bar\n'),
hasAGoodToStringDeep);
expect(_MockToStringDeep('Hello: World\nFoo: 42\n'),
hasAGoodToStringDeep);
// Contains default Object.toString().
expect(_MockToStringDeep('Hello: World\nFoo: ${Object()}\n'),
isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('A\n├─B\n'), hasAGoodToStringDeep);
expect(_MockToStringDeep('A\n├─B\n╘══════\n'), hasAGoodToStringDeep);
// Last line is all whitespace or vertical line art.
expect(_MockToStringDeep('A\n├─B\n\n'), isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('A\n├─B\n\n'), isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('A\n├─B\n\n'), isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('A\n├─B\n\n'), isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('A\n├─B\n\n'), isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('A\n├─B\n\n'), isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('A\n├─B\n\n'), isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('A\n├─B\n\n'), isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('A\n├─B\n\n'), isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep('A\n├─B\n ││\n'), isNot(hasAGoodToStringDeep));
expect(_MockToStringDeep(
'A\n'
'├─B\n'
'\n'
'└─C\n'), hasAGoodToStringDeep);
// Last line is all whitespace or vertical line art.
expect(_MockToStringDeep(
'A\n'
'├─B\n'
'\n'), isNot(hasAGoodToStringDeep));
expect(
_MockToStringDeep.fromLines(<String>[
'Paragraph#00000\n',
' │ size: (400x200)\n',
' ╘═╦══ text ═══\n',
' ║ TextSpan:\n',
' ║ "I polished up that handle so carefullee\n',
' ║ That now I am the Ruler of the Queen\'s Navee!"\n',
' ╚═══════════\n',
]),
hasAGoodToStringDeep,
);
// Text span
expect(
_MockToStringDeep.fromLines(<String>[
'Paragraph#00000\n',
' │ size: (400x200)\n',
' ╘═╦══ text ═══\n',
' ║ TextSpan:\n',
' ║ "I polished up that handle so carefullee\nThat now I am the Ruler of the Queen\'s Navee!"\n',
' ╚═══════════\n',
]),
isNot(hasAGoodToStringDeep),
);
});
test('equalsIgnoringHashCodes', () {
expect('Foo#34219', equalsIgnoringHashCodes('Foo#00000'));
expect('Foo#34219', equalsIgnoringHashCodes('Foo#12345'));
expect('Foo#34219', equalsIgnoringHashCodes('Foo#abcdf'));
expect('Foo#34219', isNot(equalsIgnoringHashCodes('Foo')));
expect('Foo#34219', isNot(equalsIgnoringHashCodes('Foo#')));
expect('Foo#34219', isNot(equalsIgnoringHashCodes('Foo#0')));
expect('Foo#34219', isNot(equalsIgnoringHashCodes('Foo#00')));
expect('Foo#34219', isNot(equalsIgnoringHashCodes('Foo#00000 ')));
expect('Foo#34219', isNot(equalsIgnoringHashCodes('Foo#000000')));
expect('Foo#34219', isNot(equalsIgnoringHashCodes('Foo#123456')));
expect('Foo#34219:', equalsIgnoringHashCodes('Foo#00000:'));
expect('Foo#34219:', isNot(equalsIgnoringHashCodes('Foo#00000')));
expect('Foo#a3b4d', equalsIgnoringHashCodes('Foo#00000'));
expect('Foo#a3b4d', equalsIgnoringHashCodes('Foo#12345'));
expect('Foo#a3b4d', equalsIgnoringHashCodes('Foo#abcdf'));
expect('Foo#a3b4d', isNot(equalsIgnoringHashCodes('Foo')));
expect('Foo#a3b4d', isNot(equalsIgnoringHashCodes('Foo#')));
expect('Foo#a3b4d', isNot(equalsIgnoringHashCodes('Foo#0')));
expect('Foo#a3b4d', isNot(equalsIgnoringHashCodes('Foo#00')));
expect('Foo#a3b4d', isNot(equalsIgnoringHashCodes('Foo#00000 ')));
expect('Foo#a3b4d', isNot(equalsIgnoringHashCodes('Foo#000000')));
expect('Foo#a3b4d', isNot(equalsIgnoringHashCodes('Foo#123456')));
expect('FOO#A3b4D', equalsIgnoringHashCodes('FOO#00000'));
expect('FOO#A3b4J', isNot(equalsIgnoringHashCodes('FOO#00000')));
expect('Foo#12345(Bar#9110f)',
equalsIgnoringHashCodes('Foo#00000(Bar#00000)'));
expect('Foo#12345(Bar#9110f)',
isNot(equalsIgnoringHashCodes('Foo#00000(Bar#)')));
expect('Foo', isNot(equalsIgnoringHashCodes('Foo#00000')));
expect('Foo#', isNot(equalsIgnoringHashCodes('Foo#00000')));
expect('Foo#3421', isNot(equalsIgnoringHashCodes('Foo#00000')));
expect('Foo#342193', isNot(equalsIgnoringHashCodes('Foo#00000')));
expect(<String>['Foo#a3b4d'], equalsIgnoringHashCodes(<String>['Foo#12345']));
expect(
<String>['Foo#a3b4d', 'Foo#12345'],
equalsIgnoringHashCodes(<String>['Foo#00000', 'Foo#00000']),
);
expect(
<String>['Foo#a3b4d', 'Bar#12345'],
equalsIgnoringHashCodes(<String>['Foo#00000', 'Bar#00000']),
);
expect(
<String>['Foo#a3b4d', 'Bar#12345'],
isNot(equalsIgnoringHashCodes(<String>['Bar#00000', 'Foo#00000'])),
);
expect(<String>['Foo#a3b4d'], isNot(equalsIgnoringHashCodes(<String>['Foo'])));
expect(
<String>['Foo#a3b4d'],
isNot(equalsIgnoringHashCodes(<String>['Foo#00000', 'Bar#00000'])),
);
});
test('moreOrLessEquals', () {
expect(0.0, moreOrLessEquals(1e-11));
expect(1e-11, moreOrLessEquals(0.0));
expect(-1e-11, moreOrLessEquals(0.0));
expect(0.0, isNot(moreOrLessEquals(1e11)));
expect(1e11, isNot(moreOrLessEquals(0.0)));
expect(-1e11, isNot(moreOrLessEquals(0.0)));
expect(0.0, isNot(moreOrLessEquals(1.0)));
expect(1.0, isNot(moreOrLessEquals(0.0)));
expect(-1.0, isNot(moreOrLessEquals(0.0)));
expect(1e-11, moreOrLessEquals(-1e-11));
expect(-1e-11, moreOrLessEquals(1e-11));
expect(11.0, isNot(moreOrLessEquals(-11.0, epsilon: 1.0)));
expect(-11.0, isNot(moreOrLessEquals(11.0, epsilon: 1.0)));
expect(11.0, moreOrLessEquals(-11.0, epsilon: 100.0));
expect(-11.0, moreOrLessEquals(11.0, epsilon: 100.0));
});
test('matrixMoreOrLessEquals', () {
expect(
Matrix4.rotationZ(math.pi),
matrixMoreOrLessEquals(Matrix4.fromList(<double>[
-1, 0, 0, 0,
0, -1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]))
);
expect(
Matrix4.rotationZ(math.pi),
matrixMoreOrLessEquals(Matrix4.fromList(<double>[
-2, 0, 0, 0,
0, -2, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]), epsilon: 2)
);
expect(
Matrix4.rotationZ(math.pi),
isNot(matrixMoreOrLessEquals(Matrix4.fromList(<double>[
-2, 0, 0, 0,
0, -2, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
])))
);
});
test('rectMoreOrLessEquals', () {
expect(
const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
rectMoreOrLessEquals(const Rect.fromLTRB(0.0, 0.0, 10.0, 10.00000000001)),
);
expect(
const Rect.fromLTRB(11.0, 11.0, 20.0, 20.0),
isNot(rectMoreOrLessEquals(const Rect.fromLTRB(-11.0, -11.0, 20.0, 20.0), epsilon: 1.0)),
);
expect(
const Rect.fromLTRB(11.0, 11.0, 20.0, 20.0),
rectMoreOrLessEquals(const Rect.fromLTRB(-11.0, -11.0, 20.0, 20.0), epsilon: 100.0),
);
});
test('within', () {
expect(0.0, within<double>(distance: 0.1, from: 0.05));
expect(0.0, isNot(within<double>(distance: 0.1, from: 0.2)));
expect(0, within<int>(distance: 1, from: 1));
expect(0, isNot(within<int>(distance: 1, from: 2)));
expect(const Color(0x00000000), within<Color>(distance: 1, from: const Color(0x01000000)));
expect(const Color(0x00000000), within<Color>(distance: 1, from: const Color(0x00010000)));
expect(const Color(0x00000000), within<Color>(distance: 1, from: const Color(0x00000100)));
expect(const Color(0x00000000), within<Color>(distance: 1, from: const Color(0x00000001)));
expect(const Color(0x00000000), within<Color>(distance: 1, from: const Color(0x01010101)));
expect(const Color(0x00000000), isNot(within<Color>(distance: 1, from: const Color(0x02000000))));
expect(const Offset(1.0, 0.0), within(distance: 1.0, from: Offset.zero));
expect(const Offset(1.0, 0.0), isNot(within(distance: 1.0, from: const Offset(-1.0, 0.0))));
expect(const Rect.fromLTRB(0.0, 1.0, 2.0, 3.0), within<Rect>(distance: 4.0, from: const Rect.fromLTRB(1.0, 3.0, 5.0, 7.0)));
expect(const Rect.fromLTRB(0.0, 1.0, 2.0, 3.0), isNot(within<Rect>(distance: 3.9, from: const Rect.fromLTRB(1.0, 3.0, 5.0, 7.0))));
expect(const Size(1.0, 1.0), within<Size>(distance: 1.415, from: const Size(2.0, 2.0)));
expect(const Size(1.0, 1.0), isNot(within<Size>(distance: 1.414, from: const Size(2.0, 2.0))));
expect(
() => within<bool>(distance: 1, from: false),
throwsArgumentError,
);
expect(
() => within<int>(distance: 1, from: 2, distanceFunction: (int a, int b) => -1).matches(1, <dynamic, dynamic>{}),
throwsArgumentError,
);
});
test('isSameColorAs', () {
expect(
const Color(0x87654321),
isSameColorAs(const _CustomColor(0x87654321)),
);
expect(
const _CustomColor(0x87654321),
isSameColorAs(const Color(0x87654321)),
);
expect(
const Color(0x12345678),
isNot(isSameColorAs(const _CustomColor(0x87654321))),
);
expect(
const _CustomColor(0x87654321),
isNot(isSameColorAs(const Color(0x12345678))),
);
expect(
const _CustomColor(0xFF123456),
isSameColorAs(const _CustomColor(0xFF123456, isEqual: false)),
);
});
group('coversSameAreaAs', () {
test('empty Paths', () {
expect(
Path(),
coversSameAreaAs(
Path(),
areaToCompare: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
),
);
});
test('mismatch', () {
final Path rectPath = Path()
..addRect(const Rect.fromLTRB(5.0, 5.0, 6.0, 6.0));
expect(
Path(),
isNot(coversSameAreaAs(
rectPath,
areaToCompare: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
)),
);
});
test('mismatch out of examined area', () {
final Path rectPath = Path()
..addRect(const Rect.fromLTRB(5.0, 5.0, 6.0, 6.0));
rectPath.addRect(const Rect.fromLTRB(5.0, 5.0, 6.0, 6.0));
expect(
Path(),
coversSameAreaAs(
rectPath,
areaToCompare: const Rect.fromLTRB(0.0, 0.0, 4.0, 4.0),
),
);
});
test('differently constructed rects match', () {
final Path rectPath = Path()
..addRect(const Rect.fromLTRB(5.0, 5.0, 6.0, 6.0));
final Path linePath = Path()
..moveTo(5.0, 5.0)
..lineTo(5.0, 6.0)
..lineTo(6.0, 6.0)
..lineTo(6.0, 5.0)
..close();
expect(
linePath,
coversSameAreaAs(
rectPath,
areaToCompare: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
),
);
});
test('partially overlapping paths', () {
final Path rectPath = Path()
..addRect(const Rect.fromLTRB(5.0, 5.0, 6.0, 6.0));
final Path linePath = Path()
..moveTo(5.0, 5.0)
..lineTo(5.0, 6.0)
..lineTo(6.0, 6.0)
..lineTo(6.0, 5.5)
..close();
expect(
linePath,
isNot(coversSameAreaAs(
rectPath,
areaToCompare: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
)),
);
});
});
group('matchesGoldenFile', () {
late _FakeComparator comparator;
Widget boilerplate(Widget child) {
return Directionality(
textDirection: TextDirection.ltr,
child: child,
);
}
setUp(() {
comparator = _FakeComparator();
goldenFileComparator = comparator;
});
group('matches', () {
testWidgets('if comparator succeeds', (WidgetTester tester) async {
await tester.pumpWidget(boilerplate(const Text('hello')));
final Finder finder = find.byType(Text);
await expectLater(finder, matchesGoldenFile('foo.png'));
expect(comparator.invocation, _ComparatorInvocation.compare);
expect(comparator.imageBytes, hasLength(greaterThan(0)));
expect(comparator.golden, Uri.parse('foo.png'));
});
2021-07-20 20:36:04 +00:00
testWidgets('list of integers', (WidgetTester tester) async {
await expectLater(<int>[1, 2], matchesGoldenFile('foo.png'));
expect(comparator.invocation, _ComparatorInvocation.compare);
expect(comparator.imageBytes, equals(<int>[1, 2]));
expect(comparator.golden, Uri.parse('foo.png'));
});
testWidgets('future list of integers', (WidgetTester tester) async {
await expectLater(Future<List<int>>.value(<int>[1, 2]), matchesGoldenFile('foo.png'));
expect(comparator.invocation, _ComparatorInvocation.compare);
expect(comparator.imageBytes, equals(<int>[1, 2]));
expect(comparator.golden, Uri.parse('foo.png'));
});
});
group('does not match', () {
testWidgets('if comparator returns false', (WidgetTester tester) async {
comparator.behavior = _ComparatorBehavior.returnFalse;
await tester.pumpWidget(boilerplate(const Text('hello')));
final Finder finder = find.byType(Text);
await expectLater(
() => expectLater(finder, matchesGoldenFile('foo.png')),
throwsA(isA<TestFailure>().having(
(TestFailure error) => error.message,
'message',
contains('does not match'),
)),
);
expect(comparator.invocation, _ComparatorInvocation.compare);
});
testWidgets('if comparator throws', (WidgetTester tester) async {
comparator.behavior = _ComparatorBehavior.throwTestFailure;
await tester.pumpWidget(boilerplate(const Text('hello')));
final Finder finder = find.byType(Text);
await expectLater(
() => expectLater(finder, matchesGoldenFile('foo.png')),
throwsA(isA<TestFailure>().having(
(TestFailure error) => error.message,
'message',
contains('fake message'),
)),
);
expect(comparator.invocation, _ComparatorInvocation.compare);
});
testWidgets('if finder finds no widgets', (WidgetTester tester) async {
await tester.pumpWidget(boilerplate(Container()));
final Finder finder = find.byType(Text);
await expectLater(
() => expectLater(finder, matchesGoldenFile('foo.png')),
throwsA(isA<TestFailure>().having(
(TestFailure error) => error.message,
'message',
contains('no widget was found'),
)),
);
expect(comparator.invocation, isNull);
});
testWidgets('if finder finds multiple widgets', (WidgetTester tester) async {
await tester.pumpWidget(boilerplate(Column(
children: const <Widget>[Text('hello'), Text('world')],
)));
final Finder finder = find.byType(Text);
await expectLater(
() => expectLater(finder, matchesGoldenFile('foo.png')),
throwsA(isA<TestFailure>().having(
(TestFailure error) => error.message,
'message',
contains('too many widgets'),
)),
);
expect(comparator.invocation, isNull);
});
});
testWidgets('calls update on comparator if autoUpdateGoldenFiles is true', (WidgetTester tester) async {
autoUpdateGoldenFiles = true;
await tester.pumpWidget(boilerplate(const Text('hello')));
final Finder finder = find.byType(Text);
await expectLater(finder, matchesGoldenFile('foo.png'));
expect(comparator.invocation, _ComparatorInvocation.update);
expect(comparator.imageBytes, hasLength(greaterThan(0)));
expect(comparator.golden, Uri.parse('foo.png'));
autoUpdateGoldenFiles = false;
});
});
group('matchesSemanticsData', () {
testWidgets('matches SemanticsData', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
const Key key = Key('semantics');
await tester.pumpWidget(Semantics(
key: key,
namesRoute: true,
header: true,
button: true,
2019-10-09 01:16:11 +00:00
link: true,
2019-03-09 08:03:11 +00:00
onTap: () { },
onLongPress: () { },
label: 'foo',
hint: 'bar',
value: 'baz',
increasedValue: 'a',
decreasedValue: 'b',
textDirection: TextDirection.rtl,
onTapHint: 'scan',
onLongPressHint: 'fill',
customSemanticsActions: <CustomSemanticsAction, VoidCallback>{
2019-03-09 08:03:11 +00:00
const CustomSemanticsAction(label: 'foo'): () { },
const CustomSemanticsAction(label: 'bar'): () { },
},
));
expect(tester.getSemantics(find.byKey(key)),
matchesSemantics(
label: 'foo',
hint: 'bar',
value: 'baz',
increasedValue: 'a',
decreasedValue: 'b',
textDirection: TextDirection.rtl,
hasTapAction: true,
hasLongPressAction: true,
isButton: true,
2019-10-09 01:16:11 +00:00
isLink: true,
isHeader: true,
namesRoute: true,
onTapHint: 'scan',
onLongPressHint: 'fill',
customActions: <CustomSemanticsAction>[
const CustomSemanticsAction(label: 'foo'),
const CustomSemanticsAction(label: 'bar'),
],
),
);
// Doesn't match custom actions
expect(tester.getSemantics(find.byKey(key)),
isNot(matchesSemantics(
label: 'foo',
hint: 'bar',
value: 'baz',
textDirection: TextDirection.rtl,
hasTapAction: true,
hasLongPressAction: true,
isButton: true,
2019-10-09 01:16:11 +00:00
isLink: true,
isHeader: true,
namesRoute: true,
onTapHint: 'scan',
onLongPressHint: 'fill',
customActions: <CustomSemanticsAction>[
const CustomSemanticsAction(label: 'foo'),
const CustomSemanticsAction(label: 'barz'),
],
)),
);
// Doesn't match wrong hints
expect(tester.getSemantics(find.byKey(key)),
isNot(matchesSemantics(
label: 'foo',
hint: 'bar',
value: 'baz',
textDirection: TextDirection.rtl,
hasTapAction: true,
hasLongPressAction: true,
isButton: true,
2019-10-09 01:16:11 +00:00
isLink: true,
isHeader: true,
namesRoute: true,
onTapHint: 'scans',
onLongPressHint: 'fills',
customActions: <CustomSemanticsAction>[
const CustomSemanticsAction(label: 'foo'),
const CustomSemanticsAction(label: 'bar'),
],
)),
);
handle.dispose();
});
testWidgets('Can match all semantics flags and actions', (WidgetTester tester) async {
int actions = 0;
int flags = 0;
2018-08-02 17:37:17 +00:00
const CustomSemanticsAction action = CustomSemanticsAction(label: 'test');
for (final int index in SemanticsAction.values.keys) {
actions |= index;
}
for (final int index in SemanticsFlag.values.keys) {
flags |= index;
}
final SemanticsData data = SemanticsData(
flags: flags,
actions: actions,
attributedLabel: AttributedString('a'),
attributedIncreasedValue: AttributedString('b'),
attributedValue: AttributedString('c'),
attributedDecreasedValue: AttributedString('d'),
attributedHint: AttributedString('e'),
tooltip: 'f',
textDirection: TextDirection.ltr,
rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
2019-01-16 18:29:00 +00:00
elevation: 3.0,
thickness: 4.0,
textSelection: null,
scrollIndex: null,
scrollChildCount: null,
scrollPosition: null,
scrollExtentMax: null,
scrollExtentMin: null,
platformViewId: 105,
customSemanticsActionIds: <int>[CustomSemanticsAction.getIdentifier(action)],
currentValueLength: 10,
maxValueLength: 15,
);
final _FakeSemanticsNode node = _FakeSemanticsNode(data);
expect(node, matchesSemantics(
rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
size: const Size(10.0, 10.0),
2019-01-16 18:29:00 +00:00
elevation: 3.0,
thickness: 4.0,
platformViewId: 105,
currentValueLength: 10,
maxValueLength: 15,
/* Flags */
hasCheckedState: true,
isChecked: true,
isCheckStateMixed: true,
isSelected: true,
isButton: true,
isSlider: true,
isKeyboardKey: true,
2019-10-09 01:16:11 +00:00
isLink: true,
isTextField: true,
2019-06-19 21:41:44 +00:00
isReadOnly: true,
hasEnabledState: true,
isFocused: true,
isFocusable: true,
isEnabled: true,
isInMutuallyExclusiveGroup: true,
isHeader: true,
isObscured: true,
isMultiline: true,
namesRoute: true,
scopesRoute: true,
isHidden: true,
isImage: true,
isLiveRegion: true,
hasToggledState: true,
isToggled: true,
Roll engine to 7f083e54fe75 (#20310) * Roll engine to 7f083e54fe75 7f083e54f Don't implicitly fall through in switch statement (flutter/engine#5964) c7ce6dd69 Apply translation to accessibility tree when in landscape (flutter/engine#5950) aef94b73d Reland "Updated background execution implementation for Android" (flutter/engine#5954) 3421bca7a Roll src/third_party/skia 36216fb0acbc..177742435e52 (15 commits) (flutter/engine#5963) 9d1e67399 Temporarily add travis/analyze.sh back for Chrome bot (flutter/engine#5961) 4386afdaa Roll src/third_party/skia e6d0618f677c..36216fb0acbc (11 commits) (flutter/engine#5960) 7cef3da03 Roll src/third_party/skia 01d9a344b575..e6d0618f677c (1 commits) (flutter/engine#5958) ad82cb154 Roll src/third_party/skia fdf05f4ff4e9..01d9a344b575 (1 commits) (flutter/engine#5957) 6ea410cfe Revert "Roll Dart to 17b54c76ce9b945c6f013ad08c19268409c0694a (flutter/engine#5955)" (#5956) f20c58f65 Roll Dart to 17b54c76ce9b945c6f013ad08c19268409c0694a (flutter/engine#5955) ed1938e99 Roll src/third_party/skia 641ac7daa81c..fdf05f4ff4e9 (3 commits) (flutter/engine#5953) 5f04e00d7 Remove travis directory (flutter/engine#5935) 5442c0a7d Revert "Updated background execution implementation for Android" (flutter/engine#5949) a5215ce50 Add hasImplicitScrolling SemanticFlag and support in Android bridge (flutter/engine#5941) 4681351af Roll src/third_party/skia 6e487e67a3f3..641ac7daa81c (16 commits) (flutter/engine#5948) bc885f319 Updated background execution implementation for Android 5770cbd2a Roll src/third_party/skia 12fb9cfeee07..6e487e67a3f3 (8 commits) (flutter/engine#5946) f4464a8d9 Roll src/third_party/skia 2e77f54f46e8..12fb9cfeee07 (1 commits) (flutter/engine#5945) c6baaaf75 Replace acquire+release thread annotation with excludes (flutter/engine#5944) 63ede2e35 Roll src/third_party/skia 59aabbcf3b0d..2e77f54f46e8 (1 commits) (flutter/engine#5943) 97aea093e Roll src/third_party/skia 71fe8baccc01..59aabbcf3b0d (1 commits) (flutter/engine#5942) aaf4a9a24 Roll src/third_party/skia 4c3b63e289c5..71fe8baccc01 (3 commits) (flutter/engine#5940) * Sync framework changes
2018-08-07 23:10:29 +00:00
hasImplicitScrolling: true,
/* Actions */
hasTapAction: true,
hasLongPressAction: true,
hasScrollLeftAction: true,
hasScrollRightAction: true,
hasScrollUpAction: true,
hasScrollDownAction: true,
hasIncreaseAction: true,
hasDecreaseAction: true,
hasShowOnScreenAction: true,
hasMoveCursorForwardByCharacterAction: true,
hasMoveCursorBackwardByCharacterAction: true,
hasMoveCursorForwardByWordAction: true,
hasMoveCursorBackwardByWordAction: true,
Roll Engine from 299eb3e710c1 to 67abe7f9a9e4 (13 revisions) (#77386) This is a manual roll since it requires adding a new SemanticsAction to an element in the flutter_test SemanticsAction matchers test which was added as part of https://github.com/flutter/engine/pull/24734. https://github.com/flutter/engine/compare/299eb3e710c1...67abe7f9a9e4 2021-03-05 skia-flutter-autoroll@skia.org Roll Skia from 994a571fc79a to 532138cea7ea (4 revisions) (flutter/engine#24812) 2021-03-05 skia-flutter-autoroll@skia.org Roll Dart SDK from b321d01eec78 to 4693d6d4f88e (1 revision) (flutter/engine#24811) 2021-03-05 skia-flutter-autoroll@skia.org Roll Dart SDK from 835da00aa6b5 to b321d01eec78 (1 revision) (flutter/engine#24808) 2021-03-05 skia-flutter-autoroll@skia.org Roll Skia from d42fe062d0a7 to 994a571fc79a (1 revision) (flutter/engine#24806) 2021-03-05 skia-flutter-autoroll@skia.org Roll Skia from 3b58d38966f4 to d42fe062d0a7 (2 revisions) (flutter/engine#24805) 2021-03-05 skia-flutter-autoroll@skia.org Roll Skia from 6ba242d2a201 to 3b58d38966f4 (1 revision) (flutter/engine#24804) 2021-03-05 skia-flutter-autoroll@skia.org Roll Dart SDK from 1b674d73af53 to 835da00aa6b5 (1 revision) (flutter/engine#24803) 2021-03-05 chris@bracken.jp Update cppwinrt to build 2.0.210304.5 (flutter/engine#24801) 2021-03-05 skia-flutter-autoroll@skia.org Roll Skia from 55aaefe687c7 to 6ba242d2a201 (18 revisions) (flutter/engine#24799) 2021-03-05 47866232+chunhtai@users.noreply.github.com Adds set text action for voice access (flutter/engine#24734) 2021-03-04 jason-simmons@users.noreply.github.com SkParagraph: support multiple default font families (flutter/engine#24662) 2021-03-04 skia-flutter-autoroll@skia.org Roll Clang Mac from MQQ43_LGr... to MRLGJYv8V... (flutter/engine#24794) 2021-03-04 jason-simmons@users.noreply.github.com Allow calls to legacyMakeTypeface in the AssetFontManager (flutter/engine#24612) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC cbracken@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/master/autoroll/README.md
2021-03-05 18:10:32 +00:00
hasSetTextAction: true,
hasSetSelectionAction: true,
hasCopyAction: true,
hasCutAction: true,
hasPasteAction: true,
hasDidGainAccessibilityFocusAction: true,
hasDidLoseAccessibilityFocusAction: true,
hasDismissAction: true,
customActions: <CustomSemanticsAction>[action],
));
});
testWidgets('Can match child semantics', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
const Key key = Key('a');
await tester.pumpWidget(Semantics(
key: key,
label: 'Foo',
container: true,
explicitChildNodes: true,
textDirection: TextDirection.ltr,
child: Semantics(
label: 'Bar',
textDirection: TextDirection.ltr,
),
));
final SemanticsNode node = tester.getSemantics(find.byKey(key));
expect(node, matchesSemantics(
label: 'Foo',
textDirection: TextDirection.ltr,
children: <Matcher>[
matchesSemantics(
label: 'Bar',
textDirection: TextDirection.ltr,
),
],
));
handle.dispose();
});
testWidgets('failure does not throw unexpected errors', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
addTearDown(() => handle.dispose());
const Key key = Key('semantics');
await tester.pumpWidget(Semantics(
key: key,
namesRoute: true,
header: true,
button: true,
link: true,
onTap: () { },
onLongPress: () { },
label: 'foo',
hint: 'bar',
value: 'baz',
increasedValue: 'a',
decreasedValue: 'b',
textDirection: TextDirection.rtl,
onTapHint: 'scan',
onLongPressHint: 'fill',
customSemanticsActions: <CustomSemanticsAction, VoidCallback>{
const CustomSemanticsAction(label: 'foo'): () { },
const CustomSemanticsAction(label: 'bar'): () { },
},
));
// This should fail due to the mis-match between the `namesRoute` value.
void failedExpectation() => expect(tester.getSemantics(find.byKey(key)),
matchesSemantics(
// Adding the explicit `false` for test readability
// ignore: avoid_redundant_argument_values
namesRoute: false,
label: 'foo',
hint: 'bar',
value: 'baz',
increasedValue: 'a',
decreasedValue: 'b',
textDirection: TextDirection.rtl,
hasTapAction: true,
hasLongPressAction: true,
isButton: true,
isLink: true,
isHeader: true,
onTapHint: 'scan',
onLongPressHint: 'fill',
customActions: <CustomSemanticsAction>[
const CustomSemanticsAction(label: 'foo'),
const CustomSemanticsAction(label: 'bar'),
],
),
);
expect(failedExpectation, throwsA(isA<TestFailure>()));
});
});
group('containsSemantics', () {
testWidgets('matches SemanticsData', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
addTearDown(() => handle.dispose());
const Key key = Key('semantics');
await tester.pumpWidget(Semantics(
key: key,
namesRoute: true,
header: true,
button: true,
link: true,
onTap: () { },
onLongPress: () { },
label: 'foo',
hint: 'bar',
value: 'baz',
increasedValue: 'a',
decreasedValue: 'b',
textDirection: TextDirection.rtl,
onTapHint: 'scan',
onLongPressHint: 'fill',
customSemanticsActions: <CustomSemanticsAction, VoidCallback>{
const CustomSemanticsAction(label: 'foo'): () { },
const CustomSemanticsAction(label: 'bar'): () { },
},
));
expect(
tester.getSemantics(find.byKey(key)),
containsSemantics(
label: 'foo',
hint: 'bar',
value: 'baz',
increasedValue: 'a',
decreasedValue: 'b',
textDirection: TextDirection.rtl,
hasTapAction: true,
hasLongPressAction: true,
isButton: true,
isLink: true,
isHeader: true,
namesRoute: true,
onTapHint: 'scan',
onLongPressHint: 'fill',
customActions: <CustomSemanticsAction>[
const CustomSemanticsAction(label: 'foo'),
const CustomSemanticsAction(label: 'bar'),
],
),
);
expect(
tester.getSemantics(find.byKey(key)),
isNot(containsSemantics(
label: 'foo',
hint: 'bar',
value: 'baz',
textDirection: TextDirection.rtl,
hasTapAction: true,
hasLongPressAction: true,
isButton: true,
isLink: true,
isHeader: true,
namesRoute: true,
onTapHint: 'scan',
onLongPressHint: 'fill',
customActions: <CustomSemanticsAction>[
const CustomSemanticsAction(label: 'foo'),
const CustomSemanticsAction(label: 'barz'),
],
)),
reason: 'CustomSemanticsAction "barz" should not have matched "bar".'
);
expect(
tester.getSemantics(find.byKey(key)),
isNot(matchesSemantics(
label: 'foo',
hint: 'bar',
value: 'baz',
textDirection: TextDirection.rtl,
hasTapAction: true,
hasLongPressAction: true,
isButton: true,
isLink: true,
isHeader: true,
namesRoute: true,
onTapHint: 'scans',
onLongPressHint: 'fills',
customActions: <CustomSemanticsAction>[
const CustomSemanticsAction(label: 'foo'),
const CustomSemanticsAction(label: 'bar'),
],
)),
reason: 'onTapHint "scans" should not have matched "scan".',
);
});
testWidgets('can match all semantics flags and actions enabled', (WidgetTester tester) async {
int actions = 0;
int flags = 0;
const CustomSemanticsAction action = CustomSemanticsAction(label: 'test');
for (final int index in SemanticsAction.values.keys) {
actions |= index;
}
for (final int index in SemanticsFlag.values.keys) {
flags |= index;
}
final SemanticsData data = SemanticsData(
flags: flags,
actions: actions,
attributedLabel: AttributedString('a'),
attributedIncreasedValue: AttributedString('b'),
attributedValue: AttributedString('c'),
attributedDecreasedValue: AttributedString('d'),
attributedHint: AttributedString('e'),
tooltip: 'f',
textDirection: TextDirection.ltr,
rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
elevation: 3.0,
thickness: 4.0,
textSelection: null,
scrollIndex: null,
scrollChildCount: null,
scrollPosition: null,
scrollExtentMax: null,
scrollExtentMin: null,
platformViewId: 105,
customSemanticsActionIds: <int>[CustomSemanticsAction.getIdentifier(action)],
currentValueLength: 10,
maxValueLength: 15,
);
final _FakeSemanticsNode node = _FakeSemanticsNode(data);
expect(
node,
containsSemantics(
rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
size: const Size(10.0, 10.0),
elevation: 3.0,
thickness: 4.0,
platformViewId: 105,
currentValueLength: 10,
maxValueLength: 15,
/* Flags */
hasCheckedState: true,
isChecked: true,
isSelected: true,
isButton: true,
isSlider: true,
isKeyboardKey: true,
isLink: true,
isTextField: true,
isReadOnly: true,
hasEnabledState: true,
isFocused: true,
isFocusable: true,
isEnabled: true,
isInMutuallyExclusiveGroup: true,
isHeader: true,
isObscured: true,
isMultiline: true,
namesRoute: true,
scopesRoute: true,
isHidden: true,
isImage: true,
isLiveRegion: true,
hasToggledState: true,
isToggled: true,
hasImplicitScrolling: true,
/* Actions */
hasTapAction: true,
hasLongPressAction: true,
hasScrollLeftAction: true,
hasScrollRightAction: true,
hasScrollUpAction: true,
hasScrollDownAction: true,
hasIncreaseAction: true,
hasDecreaseAction: true,
hasShowOnScreenAction: true,
hasMoveCursorForwardByCharacterAction: true,
hasMoveCursorBackwardByCharacterAction: true,
hasMoveCursorForwardByWordAction: true,
hasMoveCursorBackwardByWordAction: true,
hasSetTextAction: true,
hasSetSelectionAction: true,
hasCopyAction: true,
hasCutAction: true,
hasPasteAction: true,
hasDidGainAccessibilityFocusAction: true,
hasDidLoseAccessibilityFocusAction: true,
hasDismissAction: true,
customActions: <CustomSemanticsAction>[action],
),
);
});
testWidgets('can match all flags and actions disabled', (WidgetTester tester) async {
final SemanticsData data = SemanticsData(
flags: 0,
actions: 0,
attributedLabel: AttributedString('a'),
attributedIncreasedValue: AttributedString('b'),
attributedValue: AttributedString('c'),
attributedDecreasedValue: AttributedString('d'),
attributedHint: AttributedString('e'),
tooltip: 'f',
textDirection: TextDirection.ltr,
rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
elevation: 3.0,
thickness: 4.0,
textSelection: null,
scrollIndex: null,
scrollChildCount: null,
scrollPosition: null,
scrollExtentMax: null,
scrollExtentMin: null,
platformViewId: 105,
currentValueLength: 10,
maxValueLength: 15,
);
final _FakeSemanticsNode node = _FakeSemanticsNode(data);
expect(
node,
containsSemantics(
rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
size: const Size(10.0, 10.0),
elevation: 3.0,
thickness: 4.0,
platformViewId: 105,
currentValueLength: 10,
maxValueLength: 15,
/* Flags */
hasCheckedState: false,
isChecked: false,
isSelected: false,
isButton: false,
isSlider: false,
isKeyboardKey: false,
isLink: false,
isTextField: false,
isReadOnly: false,
hasEnabledState: false,
isFocused: false,
isFocusable: false,
isEnabled: false,
isInMutuallyExclusiveGroup: false,
isHeader: false,
isObscured: false,
isMultiline: false,
namesRoute: false,
scopesRoute: false,
isHidden: false,
isImage: false,
isLiveRegion: false,
hasToggledState: false,
isToggled: false,
hasImplicitScrolling: false,
/* Actions */
hasTapAction: false,
hasLongPressAction: false,
hasScrollLeftAction: false,
hasScrollRightAction: false,
hasScrollUpAction: false,
hasScrollDownAction: false,
hasIncreaseAction: false,
hasDecreaseAction: false,
hasShowOnScreenAction: false,
hasMoveCursorForwardByCharacterAction: false,
hasMoveCursorBackwardByCharacterAction: false,
hasMoveCursorForwardByWordAction: false,
hasMoveCursorBackwardByWordAction: false,
hasSetTextAction: false,
hasSetSelectionAction: false,
hasCopyAction: false,
hasCutAction: false,
hasPasteAction: false,
hasDidGainAccessibilityFocusAction: false,
hasDidLoseAccessibilityFocusAction: false,
hasDismissAction: false,
),
);
});
testWidgets('only matches given flags and actions', (WidgetTester tester) async {
int allActions = 0;
int allFlags = 0;
for (final int index in SemanticsAction.values.keys) {
allActions |= index;
}
for (final int index in SemanticsFlag.values.keys) {
allFlags |= index;
}
final SemanticsData emptyData = SemanticsData(
flags: 0,
actions: 0,
attributedLabel: AttributedString('a'),
attributedIncreasedValue: AttributedString('b'),
attributedValue: AttributedString('c'),
attributedDecreasedValue: AttributedString('d'),
attributedHint: AttributedString('e'),
tooltip: 'f',
textDirection: TextDirection.ltr,
rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
elevation: 3.0,
thickness: 4.0,
textSelection: null,
scrollIndex: null,
scrollChildCount: null,
scrollPosition: null,
scrollExtentMax: null,
scrollExtentMin: null,
platformViewId: 105,
currentValueLength: 10,
maxValueLength: 15,
);
final _FakeSemanticsNode emptyNode = _FakeSemanticsNode(emptyData);
const CustomSemanticsAction action = CustomSemanticsAction(label: 'test');
final SemanticsData fullData = SemanticsData(
flags: allFlags,
actions: allActions,
attributedLabel: AttributedString('a'),
attributedIncreasedValue: AttributedString('b'),
attributedValue: AttributedString('c'),
attributedDecreasedValue: AttributedString('d'),
attributedHint: AttributedString('e'),
tooltip: 'f',
textDirection: TextDirection.ltr,
rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
elevation: 3.0,
thickness: 4.0,
textSelection: null,
scrollIndex: null,
scrollChildCount: null,
scrollPosition: null,
scrollExtentMax: null,
scrollExtentMin: null,
platformViewId: 105,
currentValueLength: 10,
maxValueLength: 15,
customSemanticsActionIds: <int>[CustomSemanticsAction.getIdentifier(action)],
);
final _FakeSemanticsNode fullNode = _FakeSemanticsNode(fullData);
expect(
emptyNode,
containsSemantics(
rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
size: const Size(10.0, 10.0),
elevation: 3.0,
thickness: 4.0,
platformViewId: 105,
currentValueLength: 10,
maxValueLength: 15,
),
);
expect(
fullNode,
containsSemantics(
rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
size: const Size(10.0, 10.0),
elevation: 3.0,
thickness: 4.0,
platformViewId: 105,
currentValueLength: 10,
maxValueLength: 15,
customActions: <CustomSemanticsAction>[action],
),
);
});
testWidgets('can match child semantics', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
const Key key = Key('a');
await tester.pumpWidget(Semantics(
key: key,
label: 'Foo',
container: true,
explicitChildNodes: true,
textDirection: TextDirection.ltr,
child: Semantics(
label: 'Bar',
textDirection: TextDirection.ltr,
),
));
final SemanticsNode node = tester.getSemantics(find.byKey(key));
expect(
node,
containsSemantics(
label: 'Foo',
textDirection: TextDirection.ltr,
children: <Matcher>[
containsSemantics(
label: 'Bar',
textDirection: TextDirection.ltr,
),
],
),
);
handle.dispose();
});
testWidgets('can match only custom actions', (WidgetTester tester) async {
const CustomSemanticsAction action = CustomSemanticsAction(label: 'test');
final SemanticsData data = SemanticsData(
flags: 0,
actions: SemanticsAction.customAction.index,
attributedLabel: AttributedString('a'),
attributedIncreasedValue: AttributedString('b'),
attributedValue: AttributedString('c'),
attributedDecreasedValue: AttributedString('d'),
attributedHint: AttributedString('e'),
tooltip: 'f',
textDirection: TextDirection.ltr,
rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0),
elevation: 3.0,
thickness: 4.0,
textSelection: null,
scrollIndex: null,
scrollChildCount: null,
scrollPosition: null,
scrollExtentMax: null,
scrollExtentMin: null,
platformViewId: 105,
currentValueLength: 10,
maxValueLength: 15,
customSemanticsActionIds: <int>[CustomSemanticsAction.getIdentifier(action)],
);
final _FakeSemanticsNode node = _FakeSemanticsNode(data);
expect(node, containsSemantics(customActions: <CustomSemanticsAction>[action]));
});
testWidgets('failure does not throw unexpected errors', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
addTearDown(() => handle.dispose());
const Key key = Key('semantics');
await tester.pumpWidget(Semantics(
key: key,
namesRoute: true,
header: true,
button: true,
link: true,
onTap: () { },
onLongPress: () { },
label: 'foo',
hint: 'bar',
value: 'baz',
increasedValue: 'a',
decreasedValue: 'b',
textDirection: TextDirection.rtl,
onTapHint: 'scan',
onLongPressHint: 'fill',
customSemanticsActions: <CustomSemanticsAction, VoidCallback>{
const CustomSemanticsAction(label: 'foo'): () { },
const CustomSemanticsAction(label: 'bar'): () { },
},
));
// This should fail due to the mis-match between the `namesRoute` value.
void failedExpectation() => expect(tester.getSemantics(find.byKey(key)),
containsSemantics(
label: 'foo',
hint: 'bar',
value: 'baz',
increasedValue: 'a',
decreasedValue: 'b',
textDirection: TextDirection.rtl,
hasTapAction: true,
hasLongPressAction: true,
isButton: true,
isLink: true,
isHeader: true,
namesRoute: false,
onTapHint: 'scan',
onLongPressHint: 'fill',
customActions: <CustomSemanticsAction>[
const CustomSemanticsAction(label: 'foo'),
const CustomSemanticsAction(label: 'bar'),
],
),
);
expect(failedExpectation, throwsA(isA<TestFailure>()));
});
});
group('findsAtLeastNWidgets', () {
Widget boilerplate(Widget child) {
return Directionality(
textDirection: TextDirection.ltr,
child: child,
);
}
testWidgets('succeeds when finds more then the specified count',
(WidgetTester tester) async {
await tester.pumpWidget(boilerplate(Column(
children: const <Widget>[Text('1'), Text('2'), Text('3')],
)));
expect(find.byType(Text), findsAtLeastNWidgets(2));
});
testWidgets('succeeds when finds the exact specified count',
(WidgetTester tester) async {
await tester.pumpWidget(boilerplate(Column(
children: const <Widget>[Text('1'), Text('2')],
)));
expect(find.byType(Text), findsAtLeastNWidgets(2));
});
testWidgets('fails when finds less then specified count',
(WidgetTester tester) async {
await tester.pumpWidget(boilerplate(Column(
children: const <Widget>[Text('1'), Text('2')],
)));
expect(find.byType(Text), isNot(findsAtLeastNWidgets(3)));
});
});
}
enum _ComparatorBehavior {
returnTrue,
returnFalse,
throwTestFailure,
}
enum _ComparatorInvocation {
compare,
update,
}
class _FakeComparator implements GoldenFileComparator {
_ComparatorBehavior behavior = _ComparatorBehavior.returnTrue;
_ComparatorInvocation? invocation;
Uint8List? imageBytes;
Uri? golden;
@override
Future<bool> compare(Uint8List imageBytes, Uri golden) {
invocation = _ComparatorInvocation.compare;
this.imageBytes = imageBytes;
this.golden = golden;
switch (behavior) {
case _ComparatorBehavior.returnTrue:
return Future<bool>.value(true);
case _ComparatorBehavior.returnFalse:
return Future<bool>.value(false);
case _ComparatorBehavior.throwTestFailure:
2021-10-11 21:13:03 +00:00
fail('fake message');
}
}
@override
Future<void> update(Uri golden, Uint8List imageBytes) {
invocation = _ComparatorInvocation.update;
this.golden = golden;
this.imageBytes = imageBytes;
return Future<void>.value();
}
@override
Uri getTestUri(Uri key, int? version) {
return key;
}
}
class _FakeSemanticsNode extends SemanticsNode {
_FakeSemanticsNode(this.data);
SemanticsData data;
@override
SemanticsData getSemanticsData() => data;
2018-12-18 09:05:12 +00:00
}
@immutable
class _CustomColor extends Color {
const _CustomColor(super.value, {this.isEqual});
final bool? isEqual;
@override
bool operator ==(Object other) => isEqual ?? super == other;
@override
int get hashCode => Object.hash(super.hashCode, isEqual);
}