blob: ae925ceda121e9b6d9ba9df1959692336f87ede9 [file] [log] [blame]
// Copyright 2015 The Chromium 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:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'semantics_tester.dart';
void main() {
setUp(() {
debugResetSemanticsIdCounter();
});
testWidgets('Semantics shutdown and restart', (WidgetTester tester) async {
SemanticsTester semantics = new SemanticsTester(tester);
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
label: 'test1',
textDirection: TextDirection.ltr,
)
],
);
await tester.pumpWidget(
new Container(
child: new Semantics(
label: 'test1',
textDirection: TextDirection.ltr,
child: new Container()
)
)
);
expect(semantics, hasSemantics(
expectedSemantics,
ignoreTransform: true,
ignoreRect: true,
ignoreId: true,
));
semantics.dispose();
semantics = null;
expect(tester.binding.hasScheduledFrame, isFalse);
semantics = new SemanticsTester(tester);
expect(tester.binding.hasScheduledFrame, isTrue);
await tester.pump();
expect(semantics, hasSemantics(
expectedSemantics,
ignoreTransform: true,
ignoreRect: true,
ignoreId: true,
));
semantics.dispose();
});
testWidgets('Detach and reattach assert', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
final GlobalKey key = new GlobalKey();
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Container(
child: new Semantics(
label: 'test1',
child: new Semantics(
key: key,
container: true,
label: 'test2a',
child: new Container()
)
)
)
));
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
label: 'test1',
children: <TestSemantics>[
new TestSemantics(
label: 'test2a',
)
]
)
]
),
ignoreId: true,
ignoreRect: true,
ignoreTransform: true,
));
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Container(
child: new Semantics(
label: 'test1',
child: new Semantics(
container: true,
label: 'middle',
child: new Semantics(
key: key,
container: true,
label: 'test2b',
child: new Container()
)
)
)
)
));
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
label: 'test1',
children: <TestSemantics>[
new TestSemantics(
label: 'middle',
children: <TestSemantics>[
new TestSemantics(
label: 'test2b',
),
],
)
]
)
]
),
ignoreId: true,
ignoreRect: true,
ignoreTransform: true,
));
semantics.dispose();
});
testWidgets('Semantics and Directionality - RTL', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.rtl,
child: new Semantics(
label: 'test1',
child: new Container(),
),
),
);
expect(semantics, includesNodeWith(label: 'test1', textDirection: TextDirection.rtl));
});
testWidgets('Semantics and Directionality - LTR', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Semantics(
label: 'test1',
child: new Container(),
),
),
);
expect(semantics, includesNodeWith(label: 'test1', textDirection: TextDirection.ltr));
});
testWidgets('Semantics and Directionality - cannot override RTL with LTR', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
label: 'test1',
textDirection: TextDirection.ltr,
)
]
);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.rtl,
child: new Semantics(
label: 'test1',
textDirection: TextDirection.ltr,
child: new Container(),
),
),
);
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreRect: true, ignoreId: true));
});
testWidgets('Semantics and Directionality - cannot override LTR with RTL', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
label: 'test1',
textDirection: TextDirection.rtl,
)
]
);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Semantics(
label: 'test1',
textDirection: TextDirection.rtl,
child: new Container(),
),
),
);
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreRect: true, ignoreId: true));
});
testWidgets('Semantics label and hint', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Semantics(
label: 'label',
hint: 'hint',
value: 'value',
child: new Container(),
),
),
);
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
label: 'label',
hint: 'hint',
value: 'value',
textDirection: TextDirection.ltr,
)
]
);
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreRect: true, ignoreId: true));
});
testWidgets('Semantics hints can merge', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Semantics(
container: true,
child: new Column(
children: <Widget>[
new Semantics(
hint: 'hint one',
),
new Semantics(
hint: 'hint two',
)
],
),
),
),
);
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
hint: 'hint one\nhint two',
textDirection: TextDirection.ltr,
)
]
);
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreRect: true, ignoreId: true));
});
testWidgets('Semantics values do not merge', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Semantics(
container: true,
child: new Column(
children: <Widget>[
new Semantics(
value: 'value one',
child: new Container(
height: 10.0,
width: 10.0,
)
),
new Semantics(
value: 'value two',
child: new Container(
height: 10.0,
width: 10.0,
)
)
],
),
),
),
);
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
children: <TestSemantics>[
new TestSemantics(
value: 'value one',
textDirection: TextDirection.ltr,
),
new TestSemantics(
value: 'value two',
textDirection: TextDirection.ltr,
),
]
)
],
);
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreRect: true, ignoreId: true));
});
testWidgets('Semantics value and hint can merge', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Semantics(
container: true,
child: new Column(
children: <Widget>[
new Semantics(
hint: 'hint',
),
new Semantics(
value: 'value',
),
],
),
),
),
);
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
hint: 'hint',
value: 'value',
textDirection: TextDirection.ltr,
)
]
);
expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreRect: true, ignoreId: true));
});
testWidgets('Semantics widget supports all actions', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
final List<SemanticsAction> performedActions = <SemanticsAction>[];
await tester.pumpWidget(
new Semantics(
container: true,
onTap: () => performedActions.add(SemanticsAction.tap),
onLongPress: () => performedActions.add(SemanticsAction.longPress),
onScrollLeft: () => performedActions.add(SemanticsAction.scrollLeft),
onScrollRight: () => performedActions.add(SemanticsAction.scrollRight),
onScrollUp: () => performedActions.add(SemanticsAction.scrollUp),
onScrollDown: () => performedActions.add(SemanticsAction.scrollDown),
onIncrease: () => performedActions.add(SemanticsAction.increase),
onDecrease: () => performedActions.add(SemanticsAction.decrease),
onCopy: () => performedActions.add(SemanticsAction.copy),
onCut: () => performedActions.add(SemanticsAction.cut),
onPaste: () => performedActions.add(SemanticsAction.paste),
onMoveCursorForwardByCharacter: (bool _) => performedActions.add(SemanticsAction.moveCursorForwardByCharacter),
onMoveCursorBackwardByCharacter: (bool _) => performedActions.add(SemanticsAction.moveCursorBackwardByCharacter),
onSetSelection: (TextSelection _) => performedActions.add(SemanticsAction.setSelection),
onDidGainAccessibilityFocus: () => performedActions.add(SemanticsAction.didGainAccessibilityFocus),
onDidLoseAccessibilityFocus: () => performedActions.add(SemanticsAction.didLoseAccessibilityFocus),
)
);
final Set<SemanticsAction> allActions = SemanticsAction.values.values.toSet()
..remove(SemanticsAction.showOnScreen); // showOnScreen is non user-exposed.
const int expectedId = 2;
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: expectedId,
rect: TestSemantics.fullScreen,
actions: allActions.fold(0, (int previous, SemanticsAction action) => previous | action.index),
previousNodeId: -1,
nextNodeId: -1,
),
],
);
expect(semantics, hasSemantics(expectedSemantics));
// Do the actions work?
final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner;
int expectedLength = 1;
for (SemanticsAction action in allActions) {
switch (action) {
case SemanticsAction.moveCursorBackwardByCharacter:
case SemanticsAction.moveCursorForwardByCharacter:
semanticsOwner.performAction(expectedId, action, true);
break;
case SemanticsAction.setSelection:
semanticsOwner.performAction(expectedId, action, <String, int>{
'base': 4,
'extent': 5,
});
break;
default:
semanticsOwner.performAction(expectedId, action);
}
expect(performedActions.length, expectedLength);
expect(performedActions.last, action);
expectedLength += 1;
}
semantics.dispose();
});
testWidgets('Actions can be replaced without triggering semantics update', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
int semanticsUpdateCount = 0;
tester.binding.pipelineOwner.ensureSemantics(
listener: () {
semanticsUpdateCount += 1;
}
);
final List<String> performedActions = <String>[];
await tester.pumpWidget(
new Semantics(
container: true,
onTap: () => performedActions.add('first'),
),
);
const int expectedId = 2;
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: expectedId,
rect: TestSemantics.fullScreen,
actions: SemanticsAction.tap.index,
),
],
);
final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner;
expect(semantics, hasSemantics(expectedSemantics));
semanticsOwner.performAction(expectedId, SemanticsAction.tap);
expect(semanticsUpdateCount, 1);
expect(performedActions, <String>['first']);
semanticsUpdateCount = 0;
performedActions.clear();
// Updating existing handler should not trigger semantics update
await tester.pumpWidget(
new Semantics(
container: true,
onTap: () => performedActions.add('second'),
),
);
expect(semantics, hasSemantics(expectedSemantics));
semanticsOwner.performAction(expectedId, SemanticsAction.tap);
expect(semanticsUpdateCount, 0);
expect(performedActions, <String>['second']);
semanticsUpdateCount = 0;
performedActions.clear();
// Adding a handler works
await tester.pumpWidget(
new Semantics(
container: true,
onTap: () => performedActions.add('second'),
onLongPress: () => performedActions.add('longPress'),
),
);
final TestSemantics expectedSemanticsWithLongPress = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: expectedId,
rect: TestSemantics.fullScreen,
actions: SemanticsAction.tap.index | SemanticsAction.longPress.index,
),
],
);
expect(semantics, hasSemantics(expectedSemanticsWithLongPress));
semanticsOwner.performAction(expectedId, SemanticsAction.longPress);
expect(semanticsUpdateCount, 1);
expect(performedActions, <String>['longPress']);
semanticsUpdateCount = 0;
performedActions.clear();
// Removing a handler works
await tester.pumpWidget(
new Semantics(
container: true,
onTap: () => performedActions.add('second'),
),
);
expect(semantics, hasSemantics(expectedSemantics));
expect(semanticsUpdateCount, 1);
semantics.dispose();
});
testWidgets('Increased/decreased values are annotated', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Semantics(
container: true,
value: '10s',
increasedValue: '11s',
decreasedValue: '9s',
onIncrease: () => () {},
onDecrease: () => () {},
),
),
);
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
actions: SemanticsAction.increase.index | SemanticsAction.decrease.index,
textDirection: TextDirection.ltr,
value: '10s',
increasedValue: '11s',
decreasedValue: '9s',
),
],
), ignoreTransform: true, ignoreRect: true, ignoreId: true));
semantics.dispose();
});
testWidgets('Semantics widgets built in a widget tree are sorted properly', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
int semanticsUpdateCount = 0;
tester.binding.pipelineOwner.ensureSemantics(
listener: () {
semanticsUpdateCount += 1;
}
);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Semantics(
sortKey: const CustomSortKey(0.0),
explicitChildNodes: true,
child: new Column(
children: <Widget>[
new Semantics(sortKey: const CustomSortKey(3.0), child: const Text('Label 1')),
new Semantics(sortKey: const CustomSortKey(2.0), child: const Text('Label 2')),
new Semantics(
sortKey: const CustomSortKey(1.0),
explicitChildNodes: true,
child: new Row(
children: <Widget>[
new Semantics(sortKey: const OrdinalSortKey(3.0), child: const Text('Label 3')),
new Semantics(sortKey: const OrdinalSortKey(2.0), child: const Text('Label 4')),
new Semantics(sortKey: const OrdinalSortKey(1.0), child: const Text('Label 5')),
],
),
),
],
),
),
),
);
expect(semanticsUpdateCount, 1);
expect(semantics, hasSemantics(
new TestSemantics(
id: 0,
children: <TestSemantics>[
new TestSemantics(
id: 2,
nextNodeId: 5,
previousNodeId: -1,
children: <TestSemantics>[
new TestSemantics(
id: 3,
label: r'Label 1',
textDirection: TextDirection.ltr,
nextNodeId: -1,
previousNodeId: 4,
),
new TestSemantics(
id: 4,
label: r'Label 2',
textDirection: TextDirection.ltr,
nextNodeId: 3,
previousNodeId: 6,
),
new TestSemantics(
id: 5,
nextNodeId: 8,
previousNodeId: 2,
children: <TestSemantics>[
new TestSemantics(
id: 6,
label: r'Label 3',
textDirection: TextDirection.ltr,
nextNodeId: 4,
previousNodeId: 7,
),
new TestSemantics(
id: 7,
label: r'Label 4',
textDirection: TextDirection.ltr,
nextNodeId: 6,
previousNodeId: 8,
),
new TestSemantics(
id: 8,
label: r'Label 5',
textDirection: TextDirection.ltr,
nextNodeId: 7,
previousNodeId: 5,
),
],
),
],
),
],
), ignoreTransform: true, ignoreRect: true),
);
semantics.dispose();
});
testWidgets('Semantics widgets built with explicit sort orders are sorted properly', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
int semanticsUpdateCount = 0;
tester.binding.pipelineOwner.ensureSemantics(
listener: () {
semanticsUpdateCount += 1;
}
);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Column(
children: <Widget>[
new Semantics(
sortOrder: new SemanticsSortOrder(
keys: <SemanticsSortKey>[const CustomSortKey(3.0), const OrdinalSortKey(5.0)],
),
child: const Text('Label 1'),
),
new Semantics(
sortOrder: new SemanticsSortOrder(
keys: <SemanticsSortKey>[const CustomSortKey(2.0), const OrdinalSortKey(4.0)],
),
child: const Text('Label 2'),
),
new Row(
children: <Widget>[
new Semantics(
sortOrder: new SemanticsSortOrder(
keys: <SemanticsSortKey>[const CustomSortKey(1.0), const OrdinalSortKey(3.0)],
),
child: const Text('Label 3'),
),
new Semantics(
sortOrder: new SemanticsSortOrder(
keys: <SemanticsSortKey>[const CustomSortKey(1.0), const OrdinalSortKey(2.0)],
),
child: const Text('Label 4'),
),
new Semantics(
sortOrder: new SemanticsSortOrder(
keys: <SemanticsSortKey>[const CustomSortKey(1.0), const OrdinalSortKey(1.0)],
),
child: const Text('Label 5'),
),
],
),
],
),
),
);
expect(semanticsUpdateCount, 1);
expect(semantics, hasSemantics(
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
label: r'Label 1',
textDirection: TextDirection.ltr,
nextNodeId: -1,
previousNodeId: 3,
),
new TestSemantics(
label: r'Label 2',
textDirection: TextDirection.ltr,
nextNodeId: 2,
previousNodeId: 4,
),
new TestSemantics(
label: r'Label 3',
textDirection: TextDirection.ltr,
nextNodeId: 3,
previousNodeId: 5,
),
new TestSemantics(
label: r'Label 4',
textDirection: TextDirection.ltr,
nextNodeId: 4,
previousNodeId: 6,
),
new TestSemantics(
label: r'Label 5',
textDirection: TextDirection.ltr,
nextNodeId: 5,
previousNodeId: -1,
),
],
), ignoreTransform: true, ignoreRect: true, ignoreId: true));
semantics.dispose();
});
testWidgets('Semantics widgets built with some discarded sort orders are sorted properly', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
int semanticsUpdateCount = 0;
tester.binding.pipelineOwner.ensureSemantics(
listener: () {
semanticsUpdateCount += 1;
}
);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Semantics(
sortKey: const OrdinalSortKey(0.0),
explicitChildNodes: true,
child: new Column(
children: <Widget>[
new Semantics(
sortOrder: new SemanticsSortOrder(
keys: <SemanticsSortKey>[const CustomSortKey(3.0), const OrdinalSortKey(5.0)],
discardParentOrder: true, // Replace this one.
),
child: const Text('Label 1'),
),
new Semantics(
sortOrder: new SemanticsSortOrder(
keys: <SemanticsSortKey>[const CustomSortKey(2.0), const OrdinalSortKey(4.0)],
),
child: const Text('Label 2'),
),
new Row(
children: <Widget>[
new Semantics(
sortOrder: new SemanticsSortOrder(
keys: <SemanticsSortKey>[const CustomSortKey(1.0), const OrdinalSortKey(3.0)],
discardParentOrder: true, // Replace this one.
),
child: const Text('Label 3'),
),
new Semantics(
sortOrder: new SemanticsSortOrder(
keys: <SemanticsSortKey>[const CustomSortKey(1.0), const OrdinalSortKey(2.0)],
),
child: const Text('Label 4'),
),
new Semantics(
sortOrder: new SemanticsSortOrder(
keys: <SemanticsSortKey>[const CustomSortKey(1.0), const OrdinalSortKey(1.0)],
),
child: const Text('Label 5'),
),
],
),
],
),
),
),
);
expect(semanticsUpdateCount, 1);
expect(semantics, hasSemantics(
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
nextNodeId: 5,
previousNodeId: -1,
children: <TestSemantics>[
new TestSemantics(
label: r'Label 1',
textDirection: TextDirection.ltr,
nextNodeId: 7,
previousNodeId: 5,
),
new TestSemantics(
label: r'Label 2',
textDirection: TextDirection.ltr,
nextNodeId: -1,
previousNodeId: 6,
),
new TestSemantics(
label: r'Label 3',
textDirection: TextDirection.ltr,
nextNodeId: 3,
previousNodeId: 2,
),
new TestSemantics(
label: r'Label 4',
textDirection: TextDirection.ltr,
nextNodeId: 4,
previousNodeId: 7,
),
new TestSemantics(
label: r'Label 5',
textDirection: TextDirection.ltr,
nextNodeId: 6,
previousNodeId: 3,
),
],
),
],
), ignoreTransform: true, ignoreRect: true, ignoreId: true),
);
semantics.dispose();
});
testWidgets('Semantics widgets without sort orders are sorted properly', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
int semanticsUpdateCount = 0;
tester.binding.pipelineOwner.ensureSemantics(
listener: () {
semanticsUpdateCount += 1;
}
);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Column(
children: <Widget>[
const Text('Label 1'),
const Text('Label 2'),
new Row(
children: <Widget>[
const Text('Label 3'),
const Text('Label 4'),
const Text('Label 5'),
],
),
],
),
),
);
expect(semanticsUpdateCount, 1);
expect(semantics, hasSemantics(
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
label: r'Label 1',
textDirection: TextDirection.ltr,
previousNodeId: -1,
),
new TestSemantics(
label: r'Label 2',
textDirection: TextDirection.ltr,
previousNodeId: 2,
),
new TestSemantics(
label: r'Label 3',
textDirection: TextDirection.ltr,
previousNodeId: 3,
),
new TestSemantics(
label: r'Label 4',
textDirection: TextDirection.ltr,
previousNodeId: 4,
),
new TestSemantics(
label: r'Label 5',
textDirection: TextDirection.ltr,
previousNodeId: 5,
),
],
), ignoreTransform: true, ignoreRect: true, ignoreId: true),
);
semantics.dispose();
});
testWidgets('Semantics widgets that are transformed are sorted properly', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
int semanticsUpdateCount = 0;
tester.binding.pipelineOwner.ensureSemantics(
listener: () {
semanticsUpdateCount += 1;
}
);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Column(
children: <Widget>[
const Text('Label 1'),
const Text('Label 2'),
new Transform.rotate(
angle: pi / 2.0,
child: new Row(
children: <Widget>[
const Text('Label 3'),
const Text('Label 4'),
const Text('Label 5'),
],
),
),
],
),
),
);
expect(semanticsUpdateCount, 1);
expect(semantics, hasSemantics(
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
label: r'Label 1',
textDirection: TextDirection.ltr,
previousNodeId: 6,
),
new TestSemantics(
label: r'Label 2',
textDirection: TextDirection.ltr,
previousNodeId: 2,
),
new TestSemantics(
label: r'Label 3',
textDirection: TextDirection.ltr,
previousNodeId: -1,
),
new TestSemantics(
label: r'Label 4',
textDirection: TextDirection.ltr,
previousNodeId: 4,
),
new TestSemantics(
label: r'Label 5',
textDirection: TextDirection.ltr,
previousNodeId: 5,
),
],
), ignoreTransform: true, ignoreRect: true, ignoreId: true),
);
semantics.dispose();
});
testWidgets(
'Semantics widgets without sort orders are sorted properly when no Directionality is present',
(WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
int semanticsUpdateCount = 0;
tester.binding.pipelineOwner.ensureSemantics(listener: () {
semanticsUpdateCount += 1;
});
await tester.pumpWidget(
new Stack(
alignment: Alignment.center,
children: <Widget>[
// Set this up so that the placeholder takes up the whole screen,
// and place the positioned boxes so that if we traverse in the
// geometric order, we would go from box [4, 3, 2, 1, 0], but if we
// go in child order, then we go from box [4, 1, 2, 3, 0]. We're verifying
// that we go in child order here, not geometric order, since there
// is no directionality, so we don't have a geometric opinion about
// horizontal order. We do still want to sort vertically, however,
// which is why the order isn't [0, 1, 2, 3, 4].
new Semantics(
button: true,
child: const Placeholder(),
),
new Positioned(
top: 200.0,
left: 100.0,
child: new Semantics( // Box 0
button: true,
child: const SizedBox(width: 30.0, height: 30.0),
),
),
new Positioned(
top: 100.0,
left: 200.0,
child: new Semantics( // Box 1
button: true,
child: const SizedBox(width: 30.0, height: 30.0),
),
),
new Positioned(
top: 100.0,
left: 100.0,
child: new Semantics( // Box 2
button: true,
child: const SizedBox(width: 30.0, height: 30.0),
),
),
new Positioned(
top: 100.0,
left: 0.0,
child: new Semantics( // Box 3
button: true,
child: const SizedBox(width: 30.0, height: 30.0),
),
),
new Positioned(
top: 10.0,
left: 100.0,
child: new Semantics( // Box 4
button: true,
child: const SizedBox(width: 30.0, height: 30.0),
),
),
],
),
);
expect(semanticsUpdateCount, 1);
expect(
semantics,
hasSemantics(
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
previousNodeId: -1,
),
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
previousNodeId: 6,
),
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
previousNodeId: 7,
),
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
previousNodeId: 4,
),
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
previousNodeId: 5,
),
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
previousNodeId: 2,
),
],
),
ignoreTransform: true,
ignoreRect: true,
ignoreId: true),
);
semantics.dispose();
});
}
class CustomSortKey extends OrdinalSortKey {
const CustomSortKey(double order, {String name}) : super(order, name: name);
}