| // Copyright 2019 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 'package:flutter/foundation.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/widgets.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| typedef PostInvokeCallback = void Function({Action action, Intent intent, FocusNode focusNode, ActionDispatcher dispatcher}); |
| |
| class TestAction extends CallbackAction { |
| const TestAction({ |
| @required OnInvokeCallback onInvoke, |
| }) : assert(onInvoke != null), |
| super(key, onInvoke: onInvoke); |
| |
| static const LocalKey key = ValueKey<Type>(TestAction); |
| |
| void _testInvoke(FocusNode node, Intent invocation) => invoke(node, invocation); |
| } |
| |
| class TestDispatcher extends ActionDispatcher { |
| const TestDispatcher({this.postInvoke}); |
| |
| final PostInvokeCallback postInvoke; |
| |
| @override |
| bool invokeAction(Action action, Intent intent, {FocusNode focusNode}) { |
| final bool result = super.invokeAction(action, intent, focusNode: focusNode); |
| postInvoke?.call(action: action, intent: intent, focusNode: focusNode, dispatcher: this); |
| return result; |
| } |
| } |
| |
| class TestDispatcher1 extends TestDispatcher { |
| const TestDispatcher1({PostInvokeCallback postInvoke}) : super(postInvoke: postInvoke); |
| } |
| |
| void main() { |
| test('$Action passes parameters on when invoked.', () { |
| bool invoked = false; |
| FocusNode passedNode; |
| final TestAction action = TestAction(onInvoke: (FocusNode node, Intent invocation) { |
| invoked = true; |
| passedNode = node; |
| }); |
| final FocusNode testNode = FocusNode(debugLabel: 'Test Node'); |
| action._testInvoke(testNode, null); |
| expect(passedNode, equals(testNode)); |
| expect(action.intentKey, equals(TestAction.key)); |
| expect(invoked, isTrue); |
| }); |
| group(ActionDispatcher, () { |
| test('$ActionDispatcher invokes actions when asked.', () { |
| bool invoked = false; |
| FocusNode passedNode; |
| const ActionDispatcher dispatcher = ActionDispatcher(); |
| final FocusNode testNode = FocusNode(debugLabel: 'Test Node'); |
| final bool result = dispatcher.invokeAction( |
| TestAction( |
| onInvoke: (FocusNode node, Intent invocation) { |
| invoked = true; |
| passedNode = node; |
| }, |
| ), |
| const Intent(TestAction.key), |
| focusNode: testNode, |
| ); |
| expect(passedNode, equals(testNode)); |
| expect(result, isTrue); |
| expect(invoked, isTrue); |
| }); |
| }); |
| group(Actions, () { |
| Intent invokedIntent; |
| Action invokedAction; |
| FocusNode invokedNode; |
| ActionDispatcher invokedDispatcher; |
| |
| void collect({Action action, Intent intent, FocusNode focusNode, ActionDispatcher dispatcher}) { |
| invokedIntent = intent; |
| invokedAction = action; |
| invokedNode = focusNode; |
| invokedDispatcher = dispatcher; |
| } |
| |
| void clear() { |
| invokedIntent = null; |
| invokedAction = null; |
| invokedNode = null; |
| invokedDispatcher = null; |
| } |
| |
| setUp(clear); |
| |
| testWidgets('$Actions widget can invoke actions with default dispatcher', (WidgetTester tester) async { |
| final GlobalKey containerKey = GlobalKey(); |
| bool invoked = false; |
| FocusNode passedNode; |
| final FocusNode testNode = FocusNode(debugLabel: 'Test Node'); |
| |
| await tester.pumpWidget( |
| Actions( |
| actions: <LocalKey, ActionFactory>{ |
| TestAction.key: () => TestAction( |
| onInvoke: (FocusNode node, Intent invocation) { |
| invoked = true; |
| passedNode = node; |
| }, |
| ), |
| }, |
| child: Container(key: containerKey), |
| ), |
| ); |
| |
| await tester.pump(); |
| final bool result = Actions.invoke( |
| containerKey.currentContext, |
| const Intent(TestAction.key), |
| focusNode: testNode, |
| ); |
| expect(passedNode, equals(testNode)); |
| expect(result, isTrue); |
| expect(invoked, isTrue); |
| }); |
| testWidgets('$Actions widget can invoke actions with custom dispatcher', (WidgetTester tester) async { |
| final GlobalKey containerKey = GlobalKey(); |
| bool invoked = false; |
| const Intent intent = Intent(TestAction.key); |
| FocusNode passedNode; |
| final FocusNode testNode = FocusNode(debugLabel: 'Test Node'); |
| final Action testAction = TestAction( |
| onInvoke: (FocusNode node, Intent intent) { |
| invoked = true; |
| passedNode = node; |
| }, |
| ); |
| |
| await tester.pumpWidget( |
| Actions( |
| dispatcher: TestDispatcher(postInvoke: collect), |
| actions: <LocalKey, ActionFactory>{ |
| TestAction.key: () => testAction, |
| }, |
| child: Container(key: containerKey), |
| ), |
| ); |
| |
| await tester.pump(); |
| final bool result = Actions.invoke( |
| containerKey.currentContext, |
| intent, |
| focusNode: testNode, |
| ); |
| expect(passedNode, equals(testNode)); |
| expect(invokedNode, equals(testNode)); |
| expect(result, isTrue); |
| expect(invoked, isTrue); |
| expect(invokedIntent, equals(intent)); |
| }); |
| testWidgets('$Actions can invoke actions in ancestor dispatcher', (WidgetTester tester) async { |
| final GlobalKey containerKey = GlobalKey(); |
| bool invoked = false; |
| const Intent intent = Intent(TestAction.key); |
| FocusNode passedNode; |
| final FocusNode testNode = FocusNode(debugLabel: 'Test Node'); |
| final Action testAction = TestAction( |
| onInvoke: (FocusNode node, Intent invocation) { |
| invoked = true; |
| passedNode = node; |
| }, |
| ); |
| |
| await tester.pumpWidget( |
| Actions( |
| dispatcher: TestDispatcher1(postInvoke: collect), |
| actions: <LocalKey, ActionFactory>{ |
| TestAction.key: () => testAction, |
| }, |
| child: Actions( |
| dispatcher: TestDispatcher(postInvoke: collect), |
| actions: const <LocalKey, ActionFactory>{}, |
| child: Container(key: containerKey), |
| ), |
| ), |
| ); |
| |
| await tester.pump(); |
| final bool result = Actions.invoke( |
| containerKey.currentContext, |
| intent, |
| focusNode: testNode, |
| ); |
| expect(passedNode, equals(testNode)); |
| expect(invokedNode, equals(testNode)); |
| expect(result, isTrue); |
| expect(invoked, isTrue); |
| expect(invokedIntent, equals(intent)); |
| expect(invokedAction, equals(testAction)); |
| expect(invokedDispatcher.runtimeType, equals(TestDispatcher1)); |
| }); |
| testWidgets("$Actions can invoke actions in ancestor dispatcher if a lower one isn't specified", (WidgetTester tester) async { |
| final GlobalKey containerKey = GlobalKey(); |
| bool invoked = false; |
| const Intent intent = Intent(TestAction.key); |
| FocusNode passedNode; |
| final FocusNode testNode = FocusNode(debugLabel: 'Test Node'); |
| final Action testAction = TestAction( |
| onInvoke: (FocusNode node, Intent invocation) { |
| invoked = true; |
| passedNode = node; |
| }, |
| ); |
| |
| await tester.pumpWidget( |
| Actions( |
| dispatcher: TestDispatcher1(postInvoke: collect), |
| actions: <LocalKey, ActionFactory>{ |
| TestAction.key: () => testAction, |
| }, |
| child: Actions( |
| actions: const <LocalKey, ActionFactory>{}, |
| child: Container(key: containerKey), |
| ), |
| ), |
| ); |
| |
| await tester.pump(); |
| final bool result = Actions.invoke( |
| containerKey.currentContext, |
| intent, |
| focusNode: testNode, |
| ); |
| expect(passedNode, equals(testNode)); |
| expect(invokedNode, equals(testNode)); |
| expect(result, isTrue); |
| expect(invoked, isTrue); |
| expect(invokedIntent, equals(intent)); |
| expect(invokedAction, equals(testAction)); |
| expect(invokedDispatcher.runtimeType, equals(TestDispatcher1)); |
| }); |
| testWidgets('$Actions widget can be found with of', (WidgetTester tester) async { |
| final GlobalKey containerKey = GlobalKey(); |
| final ActionDispatcher testDispatcher = TestDispatcher1(postInvoke: collect); |
| |
| await tester.pumpWidget( |
| Actions( |
| dispatcher: testDispatcher, |
| actions: const <LocalKey, ActionFactory>{}, |
| child: Container(key: containerKey), |
| ), |
| ); |
| |
| await tester.pump(); |
| final ActionDispatcher dispatcher = Actions.of( |
| containerKey.currentContext, |
| nullOk: true, |
| ); |
| expect(dispatcher, equals(testDispatcher)); |
| }); |
| }); |
| group('Diagnostics', () { |
| testWidgets('default $Intent debugFillProperties', (WidgetTester tester) async { |
| final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); |
| |
| const Intent(ValueKey<String>('foo')).debugFillProperties(builder); |
| |
| final List<String> description = builder.properties |
| .where((DiagnosticsNode node) { |
| return !node.isFiltered(DiagnosticLevel.info); |
| }) |
| .map((DiagnosticsNode node) => node.toString()) |
| .toList(); |
| |
| expect(description, equals(<String>['key: [<\'foo\'>]'])); |
| }); |
| testWidgets('$CallbackAction debugFillProperties', (WidgetTester tester) async { |
| final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); |
| |
| CallbackAction( |
| const ValueKey<String>('foo'), |
| onInvoke: (FocusNode node, Intent intent) {}, |
| ).debugFillProperties(builder); |
| |
| final List<String> description = builder.properties |
| .where((DiagnosticsNode node) { |
| return !node.isFiltered(DiagnosticLevel.info); |
| }) |
| .map((DiagnosticsNode node) => node.toString()) |
| .toList(); |
| |
| expect(description, equals(<String>['intentKey: [<\'foo\'>]'])); |
| }); |
| testWidgets('default $Actions debugFillProperties', (WidgetTester tester) async { |
| final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); |
| |
| Actions( |
| actions: const <LocalKey, ActionFactory>{}, |
| dispatcher: const ActionDispatcher(), |
| child: Container(), |
| ).debugFillProperties(builder); |
| |
| final List<String> description = builder.properties |
| .where((DiagnosticsNode node) { |
| return !node.isFiltered(DiagnosticLevel.info); |
| }) |
| .map((DiagnosticsNode node) => node.toString()) |
| .toList(); |
| |
| expect(description[0], equalsIgnoringHashCodes('dispatcher: ActionDispatcher#00000')); |
| expect(description[1], equals('actions: {}')); |
| }); |
| testWidgets('$Actions implements debugFillProperties', (WidgetTester tester) async { |
| final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); |
| |
| Actions( |
| key: const ValueKey<String>('foo'), |
| dispatcher: const ActionDispatcher(), |
| actions: <LocalKey, ActionFactory>{ |
| const ValueKey<String>('bar'): () => TestAction(onInvoke: (FocusNode node, Intent intent) {}), |
| }, |
| child: Container(key: const ValueKey<String>('baz')), |
| ).debugFillProperties(builder); |
| |
| final List<String> description = builder.properties |
| .where((DiagnosticsNode node) { |
| return !node.isFiltered(DiagnosticLevel.info); |
| }) |
| .map((DiagnosticsNode node) => node.toString()) |
| .toList(); |
| |
| expect(description[0], equalsIgnoringHashCodes('dispatcher: ActionDispatcher#00000')); |
| expect(description[1], equals('actions: {[<\'bar\'>]: Closure: () => TestAction}')); |
| }, skip: isBrowser); |
| }); |
| } |