// 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 outerKey = Key('outer'); const innerKey = Key('inner'); const client1 = TextField(autofillHints: ['1']); const client2 = TextField(autofillHints: ['2']); await tester.pumpWidget( const MaterialApp( home: Scaffold( body: AutofillGroup( key: outerKey, child: Column( children: [ client1, AutofillGroup( key: innerKey, child: Column(children: [client2, TextField(autofillHints: null)]), ), ], ), ), ), ), ); final AutofillGroupState innerState = tester.state(find.byKey(innerKey)); final AutofillGroupState outerState = tester.state(find.byKey(outerKey)); final State clientState1 = tester.state>(find.byWidget(client1)); final State clientState2 = tester.state>(find.byWidget(client2)); expect(outerState.autofillClients.toList(), >[clientState1]); // The second TextField in the AutofillGroup doesn't have autofill enabled. expect(innerState.autofillClients.toList(), >[clientState2]); }); testWidgets('new clients can be added & removed to a scope', (WidgetTester tester) async { const scopeKey = Key('scope'); const client1 = TextField(autofillHints: ['1']); var 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: [client1, client2]); }, ), ), ), ), ); final AutofillGroupState scopeState = tester.state(find.byKey(scopeKey)); final State clientState1 = tester.state>(find.byWidget(client1)); final State clientState2 = tester.state>(find.byWidget(client2)); expect(scopeState.autofillClients.toList(), >[clientState1]); // Add to scope. setState(() { client2 = const TextField(autofillHints: ['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, >[clientState1]); }); testWidgets('AutofillGroup has the right clients after reparenting', (WidgetTester tester) async { const outerKey = Key('outer'); const innerKey = Key('inner'); final GlobalKey keyClient3 = GlobalKey(); const client1 = TextField(autofillHints: ['1']); const client2 = TextField(autofillHints: ['2']); await tester.pumpWidget( MaterialApp( home: Scaffold( body: AutofillGroup( key: outerKey, child: Column( children: [ client1, AutofillGroup( key: innerKey, child: Column( children: [ client2, TextField(key: keyClient3, autofillHints: const ['3']), ], ), ), ], ), ), ), ), ); final AutofillGroupState innerState = tester.state(find.byKey(innerKey)); final AutofillGroupState outerState = tester.state(find.byKey(outerKey)); final State clientState1 = tester.state>(find.byWidget(client1)); final State clientState2 = tester.state>(find.byWidget(client2)); final State clientState3 = tester.state>(find.byKey(keyClient3)); await tester.pumpWidget( MaterialApp( home: Scaffold( body: AutofillGroup( key: outerKey, child: Column( children: [ client1, TextField(key: keyClient3, autofillHints: const ['3']), const AutofillGroup( key: innerKey, child: Column(children: [client2]), ), ], ), ), ), ), ); expect(outerState.autofillClients.length, 2); expect(outerState.autofillClients, contains(clientState1)); expect(outerState.autofillClients, contains(clientState3)); expect(innerState.autofillClients, >[clientState2]); }); testWidgets('disposing AutofillGroups', (WidgetTester tester) async { late StateSetter setState; const group1 = Key('group1'); const group2 = Key('group2'); const group3 = Key('group3'); const placeholder = TextField(autofillHints: [AutofillHints.name]); var children = const [ 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 [ 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 [ 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 [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 []; }); await tester.pump(); expect(tester.testTextInput.log.single, _matchesCommit); }); }