blob: e6a5ae6a051ab73dd30f34214cdbbeb254abc42e [file] [log] [blame]
// 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.
// @dart = 2.8
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
SemanticsUpdateTestBinding();
testWidgets('Semantics update does not send update for merged nodes.', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
// Pumps a placeholder to trigger the warm up frame.
await tester.pumpWidget(
const Placeholder(),
// Stops right after the warm up frame.
null,
EnginePhase.build
);
// The warm up frame will send update for an empty semantics tree. We
// ignore this one time update.
SemanticsUpdateBuilderSpy.observations.clear();
// Builds the real widget tree.
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: MergeSemantics(
child: Semantics(
label: 'outer',
// This semantics node should not be part of the semantics update
// because it is under another semantics container.
child: Semantics(
label: 'inner',
container: true,
child: const Text('text'),
),
),
),
),
);
expect(SemanticsUpdateBuilderSpy.observations.length, 2);
expect(SemanticsUpdateBuilderSpy.observations.containsKey(0), isTrue);
expect(SemanticsUpdateBuilderSpy.observations[0].childrenInTraversalOrder.length, 1);
expect(SemanticsUpdateBuilderSpy.observations[0].childrenInTraversalOrder[0], 1);
expect(SemanticsUpdateBuilderSpy.observations.containsKey(1), isTrue);
expect(SemanticsUpdateBuilderSpy.observations[1].childrenInTraversalOrder.length, 0);
expect(SemanticsUpdateBuilderSpy.observations[1].label, 'outer\ninner\ntext');
SemanticsUpdateBuilderSpy.observations.clear();
// Updates the inner semantics label and verifies it only sends update for
// the merged parent.
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: MergeSemantics(
child: Semantics(
label: 'outer',
// This semantics node should not be part of the semantics update
// because it is under another semantics container.
child: Semantics(
label: 'inner-updated',
container: true,
child: const Text('text'),
),
),
),
),
);
expect(SemanticsUpdateBuilderSpy.observations.length, 1);
expect(SemanticsUpdateBuilderSpy.observations.containsKey(1), isTrue);
expect(SemanticsUpdateBuilderSpy.observations[1].childrenInTraversalOrder.length, 0);
expect(SemanticsUpdateBuilderSpy.observations[1].label, 'outer\ninner-updated\ntext');
SemanticsUpdateBuilderSpy.observations.clear();
handle.dispose();
});
}
class SemanticsUpdateTestBinding extends AutomatedTestWidgetsFlutterBinding {
@override
ui.SemanticsUpdateBuilder createSemanticsUpdateBuilder() {
return SemanticsUpdateBuilderSpy();
}
}
class SemanticsUpdateBuilderSpy extends ui.SemanticsUpdateBuilder {
static Map<int, SemanticsNodeUpdateObservation> observations = <int, SemanticsNodeUpdateObservation>{};
@override
void updateNode({
@required int id,
@required int flags,
@required int actions,
@required int maxValueLength,
@required int currentValueLength,
@required int textSelectionBase,
@required int textSelectionExtent,
@required int platformViewId,
@required int scrollChildren,
@required int scrollIndex,
@required double scrollPosition,
@required double scrollExtentMax,
@required double scrollExtentMin,
@required double elevation,
@required double thickness,
@required Rect rect,
@required String label,
@required String hint,
@required String value,
@required String increasedValue,
@required String decreasedValue,
TextDirection textDirection,
@required Float64List transform,
@required Int32List childrenInTraversalOrder,
@required Int32List childrenInHitTestOrder,
@required Int32List additionalActions,
}) {
// Makes sure we don't send the same id twice.
assert(!observations.containsKey(id));
observations[id] = SemanticsNodeUpdateObservation(
id: id,
flags: flags,
actions: actions,
maxValueLength: maxValueLength,
currentValueLength: currentValueLength,
textSelectionBase: textSelectionBase,
textSelectionExtent: textSelectionExtent,
platformViewId: platformViewId,
scrollChildren: scrollChildren,
scrollIndex: scrollIndex,
scrollPosition: scrollPosition,
scrollExtentMax: scrollExtentMax,
scrollExtentMin: scrollExtentMin,
elevation: elevation,
thickness: thickness,
rect: rect,
label: label,
hint: hint,
value: value,
increasedValue: increasedValue,
decreasedValue: decreasedValue,
textDirection: textDirection,
transform: transform,
childrenInTraversalOrder: childrenInTraversalOrder,
childrenInHitTestOrder: childrenInHitTestOrder,
additionalActions: additionalActions,
);
}
}
class SemanticsNodeUpdateObservation {
const SemanticsNodeUpdateObservation({
@required this.id,
@required this.flags,
@required this.actions,
@required this.maxValueLength,
@required this.currentValueLength,
@required this.textSelectionBase,
@required this.textSelectionExtent,
@required this.platformViewId,
@required this.scrollChildren,
@required this.scrollIndex,
@required this.scrollPosition,
@required this.scrollExtentMax,
@required this.scrollExtentMin,
@required this.elevation,
@required this.thickness,
@required this.rect,
@required this.label,
@required this.hint,
@required this.value,
@required this.increasedValue,
@required this.decreasedValue,
this.textDirection,
@required this.transform,
@required this.childrenInTraversalOrder,
@required this.childrenInHitTestOrder,
@required this.additionalActions,
});
final int id;
final int flags;
final int actions;
final int maxValueLength;
final int currentValueLength;
final int textSelectionBase;
final int textSelectionExtent;
final int platformViewId;
final int scrollChildren;
final int scrollIndex;
final double scrollPosition;
final double scrollExtentMax;
final double scrollExtentMin;
final double elevation;
final double thickness;
final Rect rect;
final String label;
final String hint;
final String value;
final String increasedValue;
final String decreasedValue;
final TextDirection textDirection;
final Float64List transform;
final Int32List childrenInTraversalOrder;
final Int32List childrenInHitTestOrder;
final Int32List additionalActions;
}