// 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_test/flutter_test.dart'; final Matcher _matchesCommit = isMethodCall('TextInput.finishAutofillContext', arguments: true); final Matcher _matchesCancel = isMethodCall('TextInput.finishAutofillContext', arguments: false); void main() { testWidgets('AutofillGroup has the right clients', (WidgetTester tester) async { const Key outerKey = Key('outer'); const Key innerKey = Key('inner'); const TextField client1 = TextField(autofillHints: <String>['1']); const TextField client2 = TextField(autofillHints: <String>['2']); await tester.pumpWidget( MaterialApp( home: Scaffold( body: AutofillGroup( key: outerKey, child: Column(children: <Widget>[ client1, AutofillGroup( key: innerKey, child: Column(children: const <Widget>[client2, TextField(autofillHints: null)]), ), ]), ), ), ), ); final AutofillGroupState innerState = tester.state<AutofillGroupState>(find.byKey(innerKey)); final AutofillGroupState outerState = tester.state<AutofillGroupState>(find.byKey(outerKey)); final State<TextField> clientState1 = tester.state<State<TextField>>(find.byWidget(client1)); final State<TextField> clientState2 = tester.state<State<TextField>>(find.byWidget(client2)); expect(outerState.autofillClients.toList(), <State<TextField>>[clientState1]); // The second TextField in the AutofillGroup doesn't have autofill enabled. expect(innerState.autofillClients.toList(), <State<TextField>>[clientState2]); }); testWidgets('new clients can be added & removed to a scope', (WidgetTester tester) async { const Key scopeKey = Key('scope'); const TextField client1 = TextField(autofillHints: <String>['1']); TextField client2 = const TextField(autofillHints: null); late StateSetter setState; await tester.pumpWidget( MaterialApp( home: Scaffold( body: AutofillGroup( key: scopeKey, child: StatefulBuilder( builder: (BuildContext context, StateSetter setter) { setState = setter; return Column(children: <Widget>[client1, client2]); }, ), ), ), ), ); final AutofillGroupState scopeState = tester.state<AutofillGroupState>(find.byKey(scopeKey)); final State<TextField> clientState1 = tester.state<State<TextField>>(find.byWidget(client1)); final State<TextField> clientState2 = tester.state<State<TextField>>(find.byWidget(client2)); expect(scopeState.autofillClients.toList(), <State<TextField>>[clientState1]); // Add to scope. setState(() { client2 = const TextField(autofillHints: <String>['2']); }); await tester.pump(); expect(scopeState.autofillClients, contains(clientState1)); expect(scopeState.autofillClients, contains(clientState2)); expect(scopeState.autofillClients.length, 2); // Remove from scope again. setState(() { client2 = const TextField(autofillHints: null); }); await tester.pump(); expect(scopeState.autofillClients, <State<TextField>>[clientState1]); }); testWidgets('AutofillGroup has the right clients after reparenting', (WidgetTester tester) async { const Key outerKey = Key('outer'); const Key innerKey = Key('inner'); final GlobalKey keyClient3 = GlobalKey(); const TextField client1 = TextField(autofillHints: <String>['1']); const TextField client2 = TextField(autofillHints: <String>['2']); await tester.pumpWidget( MaterialApp( home: Scaffold( body: AutofillGroup( key: outerKey, child: Column(children: <Widget>[ client1, AutofillGroup( key: innerKey, child: Column(children: <Widget>[ client2, TextField(key: keyClient3, autofillHints: const <String>['3']), ]), ), ]), ), ), ), ); final AutofillGroupState innerState = tester.state<AutofillGroupState>(find.byKey(innerKey)); final AutofillGroupState outerState = tester.state<AutofillGroupState>(find.byKey(outerKey)); final State<TextField> clientState1 = tester.state<State<TextField>>(find.byWidget(client1)); final State<TextField> clientState2 = tester.state<State<TextField>>(find.byWidget(client2)); final State<TextField> clientState3 = tester.state<State<TextField>>(find.byKey(keyClient3)); await tester.pumpWidget( MaterialApp( home: Scaffold( body: AutofillGroup( key: outerKey, child: Column(children: <Widget>[ client1, TextField(key: keyClient3, autofillHints: const <String>['3']), AutofillGroup( key: innerKey, child: Column(children: const <Widget>[client2]), ), ]), ), ), ), ); expect(outerState.autofillClients.length, 2); expect(outerState.autofillClients, contains(clientState1)); expect(outerState.autofillClients, contains(clientState3)); expect(innerState.autofillClients, <State<TextField>>[clientState2]); }); testWidgets('disposing AutofillGroups', (WidgetTester tester) async { late StateSetter setState; const Key group1 = Key('group1'); const Key group2 = Key('group2'); const Key group3 = Key('group3'); const TextField placeholder = TextField(autofillHints: <String>[AutofillHints.name]); List<Widget> children = const <Widget> [ AutofillGroup( key: group1, child: AutofillGroup(child: placeholder), ), AutofillGroup(key: group2, onDisposeAction: AutofillContextAction.cancel, child: placeholder), AutofillGroup( key: group3, child: AutofillGroup(child: placeholder), ), ]; await tester.pumpWidget( MaterialApp( home: Scaffold( body: StatefulBuilder( builder: (BuildContext context, StateSetter setter) { setState = setter; return Column(children: children); }, ), ), ), ); expect( tester.testTextInput.log, isNot(contains(_matchesCommit)), ); tester.testTextInput.log.clear(); // Remove the first topmost group group1. Should commit. setState(() { children = const <Widget> [ AutofillGroup(key: group2, onDisposeAction: AutofillContextAction.cancel, child: placeholder), AutofillGroup( key: group3, child: AutofillGroup(child: placeholder), ), ]; }); await tester.pump(); expect( tester.testTextInput.log.single, _matchesCommit, ); tester.testTextInput.log.clear(); // Remove the topmost group group2. Should cancel. setState(() { children = const <Widget> [ AutofillGroup( key: group3, child: AutofillGroup(child: placeholder), ), ]; }); await tester.pump(); expect( tester.testTextInput.log.single, _matchesCancel, ); tester.testTextInput.log.clear(); // Remove the inner group within group3. No action. setState(() { children = const <Widget> [ AutofillGroup( key: group3, child: placeholder, ), ]; }); await tester.pump(); expect( tester.testTextInput.log, isNot(contains('TextInput.finishAutofillContext')), ); tester.testTextInput.log.clear(); // Remove the topmosts group group3. Should commit. setState(() { children = const <Widget> []; }); await tester.pump(); expect( tester.testTextInput.log.single, _matchesCommit, ); }); }