Thanks to visit codestin.com
Credit goes to github.com

Skip to content

110598: expect() in semantic test producing unhelpful output #110613

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 31 additions & 14 deletions packages/flutter_test/lib/src/matchers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2272,10 +2272,10 @@ class _MatchesSemanticsData extends Matcher {
.toList();

if (expectedActions.isNotEmpty) {
description.add(' with actions: ').addDescriptionOf(expectedActions);
description.add(' with actions: ${_createEnumsSummary(expectedActions)} ');
}
if (notExpectedActions.isNotEmpty) {
description.add(' without actions: ').addDescriptionOf(notExpectedActions);
description.add(' without actions: ${_createEnumsSummary(notExpectedActions)} ');
}
}
if (flags.isNotEmpty) {
Expand All @@ -2289,10 +2289,10 @@ class _MatchesSemanticsData extends Matcher {
.toList();

if (expectedFlags.isNotEmpty) {
description.add(' with flags: ').addDescriptionOf(expectedFlags);
description.add(' with flags: ${_createEnumsSummary(expectedFlags)} ');
}
if (notExpectedFlags.isNotEmpty) {
description.add(' without flags: ').addDescriptionOf(notExpectedFlags);
description.add(' without flags: ${_createEnumsSummary(notExpectedFlags)} ');
}
}
if (textDirection != null) {
Expand Down Expand Up @@ -2434,18 +2434,24 @@ class _MatchesSemanticsData extends Matcher {
return failWithDescription(matchState, 'maxValueLength was: ${data.maxValueLength}');
}
if (actions.isNotEmpty) {
final List<SemanticsAction> unexpectedActions = <SemanticsAction>[];
final List<SemanticsAction> missingActions = <SemanticsAction>[];
for (final MapEntry<ui.SemanticsAction, bool> actionEntry in actions.entries) {
final ui.SemanticsAction action = actionEntry.key;
final bool actionExpected = actionEntry.value;
final bool actionPresent = (action.index & data.actions) == action.index;
if (actionPresent != actionExpected) {
final List<String> actionSummary = <String>[
for (final int action in SemanticsAction.values.keys)
if ((data.actions & action) != 0) describeEnum(action),
];
return failWithDescription(matchState, 'actions were: $actionSummary');
if(actionExpected) {
missingActions.add(action);
} else {
unexpectedActions.add(action);
}
}
}

if (unexpectedActions.isNotEmpty || missingActions.isNotEmpty) {
return failWithDescription(matchState, 'missing actions: ${_createEnumsSummary(missingActions)} unexpected actions: ${_createEnumsSummary(unexpectedActions)}');
}
}
if (customActions != null || hintOverrides != null) {
final List<CustomSemanticsAction> providedCustomActions = data.customSemanticsActionIds?.map<CustomSemanticsAction>((int id) {
Expand Down Expand Up @@ -2473,18 +2479,24 @@ class _MatchesSemanticsData extends Matcher {
}
}
if (flags.isNotEmpty) {
final List<SemanticsFlag> unexpectedFlags = <SemanticsFlag>[];
final List<SemanticsFlag> missingFlags = <SemanticsFlag>[];
for (final MapEntry<ui.SemanticsFlag, bool> flagEntry in flags.entries) {
final ui.SemanticsFlag flag = flagEntry.key;
final bool flagExpected = flagEntry.value;
final bool flagPresent = flag.index & data.flags == flag.index;
if (flagPresent != flagExpected) {
final List<String> flagSummary = <String>[
for (final int flag in SemanticsFlag.values.keys)
if ((data.flags & flag) != 0) describeEnum(flag),
];
return failWithDescription(matchState, 'flags were: $flagSummary');
if(flagExpected) {
missingFlags.add(flag);
} else {
unexpectedFlags.add(flag);
}
}
}

if (unexpectedFlags.isNotEmpty || missingFlags.isNotEmpty) {
return failWithDescription(matchState, 'missing flags: ${_createEnumsSummary(missingFlags)} unexpected flags: ${_createEnumsSummary(unexpectedFlags)}');
}
}
bool allMatched = true;
if (children != null) {
Expand Down Expand Up @@ -2512,6 +2524,11 @@ class _MatchesSemanticsData extends Matcher {
) {
return mismatchDescription.add(matchState['failure'] as String);
}

static String _createEnumsSummary<T extends Object>(List<T> enums) {
assert(T == SemanticsAction || T == SemanticsFlag, 'This method is only intended for lists of SemanticsActions or SemanticsFlags.');
return '[${enums.map(describeEnum).join(', ')}]';
}
}

class _MatchesAccessibilityGuideline extends AsyncMatcher {
Expand Down
110 changes: 110 additions & 0 deletions packages/flutter_test/test/matchers_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,62 @@ void main() {
));
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', () {
Expand Down Expand Up @@ -1173,6 +1229,60 @@ void main() {

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', () {
Expand Down