| // 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 'package:flutter/gestures.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter/widgets.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| import 'semantics_tester.dart'; |
| |
| void main() { |
| testWidgets('Vertical gesture detector has up/down actions', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| int callCount = 0; |
| final GlobalKey detectorKey = GlobalKey(); |
| |
| await tester.pumpWidget( |
| Center( |
| child: GestureDetector( |
| key: detectorKey, |
| onVerticalDragStart: (DragStartDetails _) { |
| callCount += 1; |
| }, |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown], |
| )); |
| |
| final int detectorId = detectorKey.currentContext!.findRenderObject()!.debugSemantics!.id; |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollLeft); |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollRight); |
| expect(callCount, 0); |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollUp); |
| expect(callCount, 1); |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollDown); |
| expect(callCount, 2); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('Horizontal gesture detector has up/down actions', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| int callCount = 0; |
| final GlobalKey detectorKey = GlobalKey(); |
| |
| await tester.pumpWidget( |
| Center( |
| child: GestureDetector( |
| key: detectorKey, |
| onHorizontalDragStart: (DragStartDetails _) { |
| callCount += 1; |
| }, |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.scrollLeft, SemanticsAction.scrollRight], |
| )); |
| |
| final int detectorId = detectorKey.currentContext!.findRenderObject()!.debugSemantics!.id; |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollUp); |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollDown); |
| expect(callCount, 0); |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollLeft); |
| expect(callCount, 1); |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollRight); |
| expect(callCount, 2); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('All registered handlers for the gesture kind are called', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| final Set<String> logs = <String>{}; |
| final GlobalKey detectorKey = GlobalKey(); |
| |
| await tester.pumpWidget( |
| Center( |
| child: GestureDetector( |
| key: detectorKey, |
| onHorizontalDragStart: (_) { logs.add('horizontal'); }, |
| onPanStart: (_) { logs.add('pan'); }, |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| final int detectorId = detectorKey.currentContext!.findRenderObject()!.debugSemantics!.id; |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollLeft); |
| expect(logs, <String>{'horizontal', 'pan'}); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('Replacing recognizers should update semantic handlers', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| // How the test is set up: |
| // |
| // * In the base state, RawGestureDetector's recognizer is a HorizontalGR |
| // * Calling `introduceLayoutPerformer()` adds a `_TestLayoutPerformer` as |
| // child of RawGestureDetector, which invokes a given callback during |
| // layout phase. |
| // * The aforementioned callback replaces the detector's recognizer with a |
| // TapGR. |
| // * This test makes sure the replacement correctly updates semantics. |
| |
| final Set<String> logs = <String>{}; |
| final GlobalKey<RawGestureDetectorState> detectorKey = GlobalKey(); |
| void performLayout() { |
| detectorKey.currentState!.replaceGestureRecognizers(<Type, GestureRecognizerFactory>{ |
| TapGestureRecognizer: GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>( |
| () => TapGestureRecognizer(), |
| (TapGestureRecognizer instance) { |
| instance.onTap = () { logs.add('tap'); }; |
| }, |
| ), |
| }); |
| } |
| |
| bool hasLayoutPerformer = false; |
| late VoidCallback introduceLayoutPerformer; |
| await tester.pumpWidget( |
| StatefulBuilder( |
| builder: (BuildContext context, StateSetter setter) { |
| introduceLayoutPerformer = () { |
| setter(() { |
| hasLayoutPerformer = true; |
| }); |
| }; |
| return Center( |
| child: RawGestureDetector( |
| key: detectorKey, |
| gestures: <Type, GestureRecognizerFactory>{ |
| HorizontalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>( |
| () => HorizontalDragGestureRecognizer(), |
| (HorizontalDragGestureRecognizer instance) { |
| instance.onStart = (_) { logs.add('horizontal'); }; |
| }, |
| ), |
| }, |
| child: hasLayoutPerformer ? _TestLayoutPerformer(performLayout: performLayout) : null, |
| ), |
| ); |
| }, |
| ), |
| ); |
| |
| final int detectorId = detectorKey.currentContext!.findRenderObject()!.debugSemantics!.id; |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollLeft); |
| expect(logs, <String>{'horizontal'}); |
| logs.clear(); |
| |
| introduceLayoutPerformer(); |
| await tester.pumpAndSettle(); |
| |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollLeft); |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.tap); |
| expect(logs, <String>{'tap'}); |
| logs.clear(); |
| |
| semantics.dispose(); |
| }); |
| |
| group("RawGestureDetector's custom semantics delegate", () { |
| testWidgets('should update semantics notations when switching from the default delegate', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| final Map<Type, GestureRecognizerFactory> gestures = |
| _buildGestureMap(() => LongPressGestureRecognizer(), null) |
| ..addAll( _buildGestureMap(() => TapGestureRecognizer(), null)); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: gestures, |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.longPress, SemanticsAction.tap], |
| )); |
| |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: gestures, |
| semantics: _TestSemanticsGestureDelegate(onTap: () {}), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.tap], |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('should update semantics notations when switching to the default delegate', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| final Map<Type, GestureRecognizerFactory> gestures = |
| _buildGestureMap(() => LongPressGestureRecognizer(), null) |
| ..addAll( _buildGestureMap(() => TapGestureRecognizer(), null)); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: gestures, |
| semantics: _TestSemanticsGestureDelegate(onTap: () {}), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.tap], |
| )); |
| |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: gestures, |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.longPress, SemanticsAction.tap], |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('should update semantics notations when switching from a different custom delegate', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| final Map<Type, GestureRecognizerFactory> gestures = |
| _buildGestureMap(() => LongPressGestureRecognizer(), null) |
| ..addAll( _buildGestureMap(() => TapGestureRecognizer(), null)); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: gestures, |
| semantics: _TestSemanticsGestureDelegate(onTap: () {}), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.tap], |
| )); |
| |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: gestures, |
| semantics: _TestSemanticsGestureDelegate(onLongPress: () {}), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.longPress], |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('should correctly call callbacks', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| final List<String> logs = <String>[]; |
| final GlobalKey<RawGestureDetectorState> detectorKey = GlobalKey(); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| key: detectorKey, |
| semantics: _TestSemanticsGestureDelegate( |
| onTap: () { logs.add('tap'); }, |
| onLongPress: () { logs.add('longPress'); }, |
| onHorizontalDragUpdate: (_) { logs.add('horizontal'); }, |
| onVerticalDragUpdate: (_) { logs.add('vertical'); }, |
| ), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| final int detectorId = detectorKey.currentContext!.findRenderObject()!.debugSemantics!.id; |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.tap); |
| expect(logs, <String>['tap']); |
| logs.clear(); |
| |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.longPress); |
| expect(logs, <String>['longPress']); |
| logs.clear(); |
| |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollLeft); |
| expect(logs, <String>['horizontal']); |
| logs.clear(); |
| |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollUp); |
| expect(logs, <String>['vertical']); |
| logs.clear(); |
| |
| semantics.dispose(); |
| }); |
| }); |
| |
| group("RawGestureDetector's default semantics delegate", () { |
| group('should map onTap to', () { |
| testWidgets('null when there is no TapGR', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: _buildGestureMap(null, null), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, isNot(includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.tap], |
| ))); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('non-null when there is TapGR with no callbacks', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: _buildGestureMap( |
| () => TapGestureRecognizer(), |
| null, |
| ), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.tap], |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('a callback that correctly calls callbacks', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| final GlobalKey detectorKey = GlobalKey(); |
| final List<String> logs = <String>[]; |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| key: detectorKey, |
| gestures: _buildGestureMap( |
| () => TapGestureRecognizer(), |
| (TapGestureRecognizer tap) { |
| tap |
| ..onTap = () {logs.add('tap');} |
| ..onTapUp = (_) {logs.add('tapUp');} |
| ..onTapDown = (_) {logs.add('tapDown');} |
| ..onTapCancel = () {logs.add('WRONG');} |
| ..onSecondaryTapDown = (_) {logs.add('WRONG');} |
| ..onTertiaryTapDown = (_) {logs.add('WRONG');}; |
| }, |
| ), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| final int detectorId = detectorKey.currentContext!.findRenderObject()!.debugSemantics!.id; |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.tap); |
| expect(logs, <String>['tapDown', 'tapUp', 'tap']); |
| |
| semantics.dispose(); |
| }); |
| }); |
| |
| group('should map onLongPress to', () { |
| testWidgets('null when there is no LongPressGR ', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: _buildGestureMap(null, null), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, isNot(includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.longPress], |
| ))); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('non-null when there is LongPressGR with no callbacks', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: _buildGestureMap( |
| () => LongPressGestureRecognizer(), |
| null, |
| ), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.longPress], |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('a callback that correctly calls callbacks', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| final GlobalKey detectorKey = GlobalKey(); |
| final List<String> logs = <String>[]; |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| key: detectorKey, |
| gestures: _buildGestureMap( |
| () => LongPressGestureRecognizer(), |
| (LongPressGestureRecognizer longPress) { |
| longPress |
| ..onLongPress = () {logs.add('LP');} |
| ..onLongPressStart = (_) {logs.add('LPStart');} |
| ..onLongPressUp = () {logs.add('LPUp');} |
| ..onLongPressEnd = (_) {logs.add('LPEnd');} |
| ..onLongPressMoveUpdate = (_) {logs.add('WRONG');}; |
| }, |
| ), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| final int detectorId = detectorKey.currentContext!.findRenderObject()!.debugSemantics!.id; |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.longPress); |
| expect(logs, <String>['LPStart', 'LP', 'LPEnd', 'LPUp']); |
| |
| semantics.dispose(); |
| }); |
| }); |
| |
| group('should map onHorizontalDragUpdate to', () { |
| testWidgets('null when there is no matching recognizers ', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: _buildGestureMap(null, null), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, isNot(includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.scrollLeft, SemanticsAction.scrollRight], |
| ))); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('non-null when there is either matching recognizer with no callbacks', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: _buildGestureMap( |
| () => HorizontalDragGestureRecognizer(), |
| null, |
| ), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.scrollLeft, SemanticsAction.scrollRight], |
| )); |
| |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: _buildGestureMap( |
| () => PanGestureRecognizer(), |
| null, |
| ), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[ |
| SemanticsAction.scrollLeft, |
| SemanticsAction.scrollRight, |
| SemanticsAction.scrollDown, |
| SemanticsAction.scrollUp, |
| ], |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('a callback that correctly calls callbacks', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| final GlobalKey detectorKey = GlobalKey(); |
| final List<String> logs = <String>[]; |
| final Map<Type, GestureRecognizerFactory> gestures = _buildGestureMap( |
| () => HorizontalDragGestureRecognizer(), |
| (HorizontalDragGestureRecognizer horizontal) { |
| horizontal |
| ..onStart = (_) {logs.add('HStart');} |
| ..onDown = (_) {logs.add('HDown');} |
| ..onEnd = (_) {logs.add('HEnd');} |
| ..onUpdate = (_) {logs.add('HUpdate');} |
| ..onCancel = () {logs.add('WRONG');}; |
| }, |
| )..addAll(_buildGestureMap( |
| () => PanGestureRecognizer(), |
| (PanGestureRecognizer pan) { |
| pan |
| ..onStart = (_) {logs.add('PStart');} |
| ..onDown = (_) {logs.add('PDown');} |
| ..onEnd = (_) {logs.add('PEnd');} |
| ..onUpdate = (_) {logs.add('PUpdate');} |
| ..onCancel = () {logs.add('WRONG');}; |
| }, |
| )); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| key: detectorKey, |
| gestures: gestures, |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| final int detectorId = detectorKey.currentContext!.findRenderObject()!.debugSemantics!.id; |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollLeft); |
| expect(logs, <String>['HDown', 'HStart', 'HUpdate', 'HEnd', |
| 'PDown', 'PStart', 'PUpdate', 'PEnd',]); |
| logs.clear(); |
| |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollLeft); |
| expect(logs, <String>['HDown', 'HStart', 'HUpdate', 'HEnd', |
| 'PDown', 'PStart', 'PUpdate', 'PEnd',]); |
| |
| semantics.dispose(); |
| }); |
| }); |
| |
| group('should map onVerticalDragUpdate to', () { |
| testWidgets('null when there is no matching recognizers ', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: _buildGestureMap(null, null), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, isNot(includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown], |
| ))); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('non-null when there is either matching recognizer with no callbacks', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: _buildGestureMap( |
| () => VerticalDragGestureRecognizer(), |
| null, |
| ), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown], |
| )); |
| |
| // Pan has bene tested in Horizontal |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('a callback that correctly calls callbacks', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| final GlobalKey detectorKey = GlobalKey(); |
| final List<String> logs = <String>[]; |
| final Map<Type, GestureRecognizerFactory> gestures = _buildGestureMap( |
| () => VerticalDragGestureRecognizer(), |
| (VerticalDragGestureRecognizer horizontal) { |
| horizontal |
| ..onStart = (_) {logs.add('VStart');} |
| ..onDown = (_) {logs.add('VDown');} |
| ..onEnd = (_) {logs.add('VEnd');} |
| ..onUpdate = (_) {logs.add('VUpdate');} |
| ..onCancel = () {logs.add('WRONG');}; |
| }, |
| )..addAll(_buildGestureMap( |
| () => PanGestureRecognizer(), |
| (PanGestureRecognizer pan) { |
| pan |
| ..onStart = (_) {logs.add('PStart');} |
| ..onDown = (_) {logs.add('PDown');} |
| ..onEnd = (_) {logs.add('PEnd');} |
| ..onUpdate = (_) {logs.add('PUpdate');} |
| ..onCancel = () {logs.add('WRONG');}; |
| }, |
| )); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| key: detectorKey, |
| gestures: gestures, |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| final int detectorId = detectorKey.currentContext!.findRenderObject()!.debugSemantics!.id; |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollUp); |
| expect(logs, <String>['VDown', 'VStart', 'VUpdate', 'VEnd', |
| 'PDown', 'PStart', 'PUpdate', 'PEnd',]); |
| logs.clear(); |
| |
| tester.binding.pipelineOwner.semanticsOwner!.performAction(detectorId, SemanticsAction.scrollDown); |
| expect(logs, <String>['VDown', 'VStart', 'VUpdate', 'VEnd', |
| 'PDown', 'PStart', 'PUpdate', 'PEnd',]); |
| |
| semantics.dispose(); |
| }); |
| }); |
| |
| testWidgets('should update semantics notations when receiving new gestures', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: _buildGestureMap(() => LongPressGestureRecognizer(), null), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.longPress], |
| )); |
| |
| await tester.pumpWidget( |
| Center( |
| child: RawGestureDetector( |
| gestures: _buildGestureMap(() => TapGestureRecognizer(), null), |
| child: Container(), |
| ), |
| ), |
| ); |
| |
| expect(semantics, includesNodeWith( |
| actions: <SemanticsAction>[SemanticsAction.tap], |
| )); |
| |
| semantics.dispose(); |
| }); |
| }); |
| } |
| |
| class _TestLayoutPerformer extends SingleChildRenderObjectWidget { |
| const _TestLayoutPerformer({ |
| Key? key, |
| required this.performLayout, |
| }) : super(key: key); |
| |
| final VoidCallback performLayout; |
| |
| @override |
| _RenderTestLayoutPerformer createRenderObject(BuildContext context) { |
| return _RenderTestLayoutPerformer(performLayout: performLayout); |
| } |
| } |
| |
| class _RenderTestLayoutPerformer extends RenderBox { |
| _RenderTestLayoutPerformer({required VoidCallback performLayout}) : _performLayout = performLayout; |
| |
| final VoidCallback _performLayout; |
| |
| @override |
| Size computeDryLayout(BoxConstraints constraints) { |
| return const Size(1, 1); |
| } |
| |
| @override |
| void performLayout() { |
| size = const Size(1, 1); |
| if (_performLayout != null) |
| _performLayout(); |
| } |
| } |
| |
| Map<Type, GestureRecognizerFactory> _buildGestureMap<T extends GestureRecognizer>( |
| GestureRecognizerFactoryConstructor<T>? constructor, |
| GestureRecognizerFactoryInitializer<T>? initializer, |
| ) { |
| if (constructor == null) |
| return <Type, GestureRecognizerFactory>{}; |
| return <Type, GestureRecognizerFactory>{ |
| T: GestureRecognizerFactoryWithHandlers<T>( |
| constructor, |
| initializer ?? (T o) {}, |
| ), |
| }; |
| } |
| |
| class _TestSemanticsGestureDelegate extends SemanticsGestureDelegate { |
| const _TestSemanticsGestureDelegate({ |
| this.onTap, |
| this.onLongPress, |
| this.onHorizontalDragUpdate, |
| this.onVerticalDragUpdate, |
| }); |
| |
| final GestureTapCallback? onTap; |
| final GestureLongPressCallback? onLongPress; |
| final GestureDragUpdateCallback? onHorizontalDragUpdate; |
| final GestureDragUpdateCallback? onVerticalDragUpdate; |
| |
| @override |
| void assignSemantics(RenderSemanticsGestureHandler renderObject) { |
| renderObject |
| ..onTap = onTap |
| ..onLongPress = onLongPress |
| ..onHorizontalDragUpdate = onHorizontalDragUpdate |
| ..onVerticalDragUpdate = onVerticalDragUpdate; |
| } |
| } |