| // 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/material.dart'; |
| import 'package:flutter/services.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| import '../widgets/semantics_tester.dart'; |
| import 'feedback_tester.dart'; |
| |
| void main () { |
| const Duration kWaitDuration = Duration(seconds: 1); |
| |
| late FeedbackTester feedback; |
| |
| setUp(() { |
| feedback = FeedbackTester(); |
| }); |
| |
| tearDown(() { |
| feedback.dispose(); |
| }); |
| |
| group('Feedback on Android', () { |
| late List<Map<String, Object>> semanticEvents; |
| |
| setUp(() { |
| semanticEvents = <Map<String, Object>>[]; |
| TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockDecodedMessageHandler<dynamic>(SystemChannels.accessibility, (dynamic message) async { |
| final Map<dynamic, dynamic> typedMessage = message as Map<dynamic, dynamic>; |
| semanticEvents.add(typedMessage.cast<String, Object>()); |
| }); |
| }); |
| |
| tearDown(() { |
| TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockDecodedMessageHandler<dynamic>(SystemChannels.accessibility, null); |
| }); |
| |
| testWidgets('forTap', (WidgetTester tester) async { |
| final SemanticsTester semanticsTester = SemanticsTester(tester); |
| |
| await tester.pumpWidget(TestWidget( |
| tapHandler: (BuildContext context) { |
| return () => Feedback.forTap(context); |
| }, |
| )); |
| await tester.pumpAndSettle(kWaitDuration); |
| expect(feedback.hapticCount, 0); |
| expect(feedback.clickSoundCount, 0); |
| expect(semanticEvents, isEmpty); |
| |
| await tester.tap(find.text('X')); |
| await tester.pumpAndSettle(kWaitDuration); |
| final RenderObject object = tester.firstRenderObject(find.byType(GestureDetector)); |
| |
| expect(feedback.hapticCount, 0); |
| expect(feedback.clickSoundCount, 1); |
| expect(semanticEvents.single, <String, dynamic>{ |
| 'type': 'tap', |
| 'nodeId': object.debugSemantics!.id, |
| 'data': <String, dynamic>{}, |
| }); |
| expect(object.debugSemantics!.getSemanticsData().hasAction(SemanticsAction.tap), true); |
| |
| semanticsTester.dispose(); |
| }); |
| |
| testWidgets('forTap Wrapper', (WidgetTester tester) async { |
| final SemanticsTester semanticsTester = SemanticsTester(tester); |
| |
| int callbackCount = 0; |
| void callback() { |
| callbackCount++; |
| } |
| |
| await tester.pumpWidget(TestWidget( |
| tapHandler: (BuildContext context) { |
| return Feedback.wrapForTap(callback, context)!; |
| }, |
| )); |
| await tester.pumpAndSettle(kWaitDuration); |
| expect(feedback.hapticCount, 0); |
| expect(feedback.clickSoundCount, 0); |
| expect(callbackCount, 0); |
| |
| await tester.tap(find.text('X')); |
| await tester.pumpAndSettle(kWaitDuration); |
| final RenderObject object = tester.firstRenderObject(find.byType(GestureDetector)); |
| |
| expect(feedback.hapticCount, 0); |
| expect(feedback.clickSoundCount, 1); |
| expect(callbackCount, 1); |
| expect(semanticEvents.single, <String, dynamic>{ |
| 'type': 'tap', |
| 'nodeId': object.debugSemantics!.id, |
| 'data': <String, dynamic>{}, |
| }); |
| expect(object.debugSemantics!.getSemanticsData().hasAction(SemanticsAction.tap), true); |
| |
| semanticsTester.dispose(); |
| }); |
| |
| testWidgets('forLongPress', (WidgetTester tester) async { |
| final SemanticsTester semanticsTester = SemanticsTester(tester); |
| |
| await tester.pumpWidget(TestWidget( |
| longPressHandler: (BuildContext context) { |
| return () => Feedback.forLongPress(context); |
| }, |
| )); |
| await tester.pumpAndSettle(kWaitDuration); |
| expect(feedback.hapticCount, 0); |
| expect(feedback.clickSoundCount, 0); |
| |
| await tester.longPress(find.text('X')); |
| await tester.pumpAndSettle(kWaitDuration); |
| final RenderObject object = tester.firstRenderObject(find.byType(GestureDetector)); |
| |
| expect(feedback.hapticCount, 1); |
| expect(feedback.clickSoundCount, 0); |
| expect(semanticEvents.single, <String, dynamic>{ |
| 'type': 'longPress', |
| 'nodeId': object.debugSemantics!.id, |
| 'data': <String, dynamic>{}, |
| }); |
| expect(object.debugSemantics!.getSemanticsData().hasAction(SemanticsAction.longPress), true); |
| |
| semanticsTester.dispose(); |
| }); |
| |
| testWidgets('forLongPress Wrapper', (WidgetTester tester) async { |
| final SemanticsTester semanticsTester = SemanticsTester(tester); |
| int callbackCount = 0; |
| void callback() { |
| callbackCount++; |
| } |
| |
| await tester.pumpWidget(TestWidget( |
| longPressHandler: (BuildContext context) { |
| return Feedback.wrapForLongPress(callback, context)!; |
| }, |
| )); |
| await tester.pumpAndSettle(kWaitDuration); |
| final RenderObject object = tester.firstRenderObject(find.byType(GestureDetector)); |
| |
| expect(feedback.hapticCount, 0); |
| expect(feedback.clickSoundCount, 0); |
| expect(callbackCount, 0); |
| |
| await tester.longPress(find.text('X')); |
| await tester.pumpAndSettle(kWaitDuration); |
| expect(feedback.hapticCount, 1); |
| expect(feedback.clickSoundCount, 0); |
| expect(callbackCount, 1); |
| expect(semanticEvents.single, <String, dynamic>{ |
| 'type': 'longPress', |
| 'nodeId': object.debugSemantics!.id, |
| 'data': <String, dynamic>{}, |
| }); |
| expect(object.debugSemantics!.getSemanticsData().hasAction(SemanticsAction.longPress), true); |
| |
| semanticsTester.dispose(); |
| }); |
| |
| }); |
| |
| group('Feedback on iOS', () { |
| testWidgets('forTap', (WidgetTester tester) async { |
| await tester.pumpWidget(Theme( |
| data: ThemeData(platform: TargetPlatform.iOS), |
| child: TestWidget( |
| tapHandler: (BuildContext context) { |
| return () => Feedback.forTap(context); |
| }, |
| ), |
| )); |
| |
| await tester.tap(find.text('X')); |
| await tester.pumpAndSettle(kWaitDuration); |
| expect(feedback.hapticCount, 0); |
| expect(feedback.clickSoundCount, 0); |
| }); |
| |
| testWidgets('forLongPress', (WidgetTester tester) async { |
| await tester.pumpWidget(Theme( |
| data: ThemeData(platform: TargetPlatform.iOS), |
| child: TestWidget( |
| longPressHandler: (BuildContext context) { |
| return () => Feedback.forLongPress(context); |
| }, |
| ), |
| )); |
| |
| await tester.longPress(find.text('X')); |
| await tester.pumpAndSettle(kWaitDuration); |
| expect(feedback.hapticCount, 0); |
| expect(feedback.clickSoundCount, 0); |
| }); |
| }); |
| } |
| |
| class TestWidget extends StatelessWidget { |
| const TestWidget({ |
| super.key, |
| this.tapHandler = nullHandler, |
| this.longPressHandler = nullHandler, |
| }); |
| |
| final HandlerCreator tapHandler; |
| final HandlerCreator longPressHandler; |
| |
| static VoidCallback? nullHandler(BuildContext context) => null; |
| |
| @override |
| Widget build(BuildContext context) { |
| return GestureDetector( |
| onTap: tapHandler(context), |
| onLongPress: longPressHandler(context), |
| child: const Text('X', textDirection: TextDirection.ltr), |
| ); |
| } |
| } |
| |
| typedef HandlerCreator = VoidCallback? Function(BuildContext context); |