blob: 6947fcf317f63a5367b01090f45cfcc99b6247c4 [file] [log] [blame]
// 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';
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()]),
),
]),
),
),
),
);
final AutofillGroupState innerState = tester.state<AutofillGroupState>(find.byKey(innerKey));
final AutofillGroupState outerState = tester.state<AutofillGroupState>(find.byKey(outerKey));
final EditableTextState clientState1 = tester.state<EditableTextState>(
find.descendant(of: find.byWidget(client1), matching: find.byType(EditableText)),
);
final EditableTextState clientState2 = tester.state<EditableTextState>(
find.descendant(of: find.byWidget(client2), matching: find.byType(EditableText)),
);
expect(outerState.autofillClients, <EditableTextState>[clientState1]);
// The second TextField doesn't have autofill enabled.
expect(innerState.autofillClients, <EditableTextState>[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: <String>[]);
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 EditableTextState clientState1 = tester.state<EditableTextState>(
find.descendant(of: find.byWidget(client1), matching: find.byType(EditableText)),
);
final EditableTextState clientState2 = tester.state<EditableTextState>(
find.descendant(of: find.byWidget(client2), matching: find.byType(EditableText)),
);
expect(scopeState.autofillClients, <EditableTextState>[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: <String>[]); });
await tester.pump();
expect(scopeState.autofillClients, <EditableTextState>[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 EditableTextState clientState1 = tester.state<EditableTextState>(
find.descendant(of: find.byWidget(client1), matching: find.byType(EditableText)),
);
final EditableTextState clientState2 = tester.state<EditableTextState>(
find.descendant(of: find.byWidget(client2), matching: find.byType(EditableText)),
);
final EditableTextState clientState3 = tester.state<EditableTextState>(
find.descendant(of: find.byKey(keyClient3), matching: find.byType(EditableText)),
);
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, <EditableTextState>[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,
onDisposeAction: AutofillContextAction.commit,
child: AutofillGroup(child: placeholder),
),
AutofillGroup(key: group2, onDisposeAction: AutofillContextAction.cancel, child: placeholder),
AutofillGroup(
key: group3,
onDisposeAction: AutofillContextAction.commit,
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,
onDisposeAction: AutofillContextAction.commit,
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,
onDisposeAction: AutofillContextAction.commit,
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,
onDisposeAction: AutofillContextAction.commit,
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,
);
});
}