1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:flutter/material.dart';
6import 'package:flutter_test/flutter_test.dart';
7
8void main() {
9 testWidgets('TextFormField restorationId is passed to inner TextField', (
10 WidgetTester tester,
11 ) async {
12 final GlobalKey<FormFieldState<String>> formState = GlobalKey<FormFieldState<String>>();
13 const String restorationId = 'text_form_field';
14
15 await tester.pumpWidget(
16 MaterialApp(
17 restorationScopeId: 'app',
18 home: Material(
19 child: TextFormField(
20 key: formState,
21 autovalidateMode: AutovalidateMode.onUserInteraction,
22 restorationId: restorationId,
23 initialValue: 'foo',
24 ),
25 ),
26 ),
27 );
28
29 expect(find.byType(TextField), findsOne);
30
31 final TextField textField = tester.firstWidget(find.byType(TextField));
32 expect(textField.restorationId, restorationId);
33 });
34
35 testWidgets('TextFormField value is restorable', (WidgetTester tester) async {
36 final GlobalKey<FormFieldState<String>> formState = GlobalKey<FormFieldState<String>>();
37
38 await tester.pumpWidget(
39 MaterialApp(
40 restorationScopeId: 'app',
41 home: Material(
42 child: TextFormField(
43 key: formState,
44 autovalidateMode: AutovalidateMode.onUserInteraction,
45 restorationId: 'text_form_field',
46 initialValue: 'foo',
47 ),
48 ),
49 ),
50 );
51
52 expect(find.text('foo'), findsOne);
53 expect(find.text('bar'), findsNothing);
54
55 await tester.enterText(find.byKey(formState), 'bar');
56 await tester.pumpAndSettle();
57
58 expect(find.text('foo'), findsNothing);
59 expect(find.text('bar'), findsOne);
60
61 final TestRestorationData data = await tester.getRestorationData();
62 await tester.restartAndRestore();
63
64 expect(find.text('foo'), findsNothing);
65 expect(find.text('bar'), findsOne);
66
67 formState.currentState!.reset();
68
69 expect(find.text('foo'), findsOne);
70 expect(find.text('bar'), findsNothing);
71
72 await tester.restoreFrom(data);
73
74 expect(find.text('foo'), findsNothing);
75 expect(find.text('bar'), findsOne);
76 });
77
78 testWidgets('State restoration (No Form ancestor) - onUserInteraction error text validation', (
79 WidgetTester tester,
80 ) async {
81 String? errorText(String? value) => '$value/error';
82 late GlobalKey<FormFieldState<String>> formState;
83
84 Widget builder() {
85 return MaterialApp(
86 restorationScopeId: 'app',
87 home: MediaQuery(
88 data: const MediaQueryData(),
89 child: Directionality(
90 textDirection: TextDirection.ltr,
91 child: Center(
92 child: StatefulBuilder(
93 builder: (BuildContext context, StateSetter state) {
94 formState = GlobalKey<FormFieldState<String>>();
95 return Material(
96 child: TextFormField(
97 key: formState,
98 autovalidateMode: AutovalidateMode.onUserInteraction,
99 restorationId: 'text_form_field',
100 initialValue: 'foo',
101 validator: errorText,
102 ),
103 );
104 },
105 ),
106 ),
107 ),
108 ),
109 );
110 }
111
112 await tester.pumpWidget(builder());
113
114 // No error text is visible yet.
115 expect(find.text(errorText('foo')!), findsNothing);
116
117 await tester.enterText(find.byType(TextFormField), 'bar');
118 await tester.pumpAndSettle();
119 expect(find.text(errorText('bar')!), findsOneWidget);
120
121 final TestRestorationData data = await tester.getRestorationData();
122 await tester.restartAndRestore();
123 // Error text should be present after restart and restore.
124 expect(find.text(errorText('bar')!), findsOneWidget);
125
126 // Resetting the form state should remove the error text.
127 formState.currentState!.reset();
128 await tester.pumpAndSettle();
129 expect(find.text(errorText('bar')!), findsNothing);
130 await tester.restartAndRestore();
131 // Error text should still be removed after restart and restore.
132 expect(find.text(errorText('bar')!), findsNothing);
133
134 await tester.restoreFrom(data);
135 expect(find.text(errorText('bar')!), findsOneWidget);
136 });
137
138 testWidgets(
139 'State Restoration (No Form ancestor) - validator sets the error text only when validate is called',
140 (WidgetTester tester) async {
141 String? errorText(String? value) => '$value/error';
142 late GlobalKey<FormFieldState<String>> formState;
143
144 Widget builder(AutovalidateMode mode) {
145 return MaterialApp(
146 restorationScopeId: 'app',
147 home: MediaQuery(
148 data: const MediaQueryData(),
149 child: Directionality(
150 textDirection: TextDirection.ltr,
151 child: Center(
152 child: StatefulBuilder(
153 builder: (BuildContext context, StateSetter state) {
154 formState = GlobalKey<FormFieldState<String>>();
155 return Material(
156 child: TextFormField(
157 key: formState,
158 restorationId: 'form_field',
159 autovalidateMode: mode,
160 initialValue: 'foo',
161 validator: errorText,
162 ),
163 );
164 },
165 ),
166 ),
167 ),
168 ),
169 );
170 }
171
172 // Start off not autovalidating.
173 await tester.pumpWidget(builder(AutovalidateMode.disabled));
174
175 Future<void> checkErrorText(String testValue) async {
176 formState.currentState!.reset();
177 await tester.pumpWidget(builder(AutovalidateMode.disabled));
178 await tester.enterText(find.byType(TextFormField), testValue);
179 await tester.pump();
180
181 // We have to manually validate if we're not autovalidating.
182 expect(find.text(errorText(testValue)!), findsNothing);
183 formState.currentState!.validate();
184 await tester.pump();
185 expect(find.text(errorText(testValue)!), findsOneWidget);
186 final TestRestorationData data = await tester.getRestorationData();
187 await tester.restartAndRestore();
188 // Error text should be present after restart and restore.
189 expect(find.text(errorText(testValue)!), findsOneWidget);
190
191 formState.currentState!.reset();
192 await tester.pumpAndSettle();
193 expect(find.text(errorText(testValue)!), findsNothing);
194
195 await tester.restoreFrom(data);
196 expect(find.text(errorText(testValue)!), findsOneWidget);
197
198 // Try again with autovalidation. Should validate immediately.
199 formState.currentState!.reset();
200 await tester.pumpWidget(builder(AutovalidateMode.always));
201 await tester.enterText(find.byType(TextFormField), testValue);
202 await tester.pump();
203
204 expect(find.text(errorText(testValue)!), findsOneWidget);
205 await tester.restartAndRestore();
206 // Error text should be present after restart and restore.
207 expect(find.text(errorText(testValue)!), findsOneWidget);
208 }
209
210 await checkErrorText('Test');
211 await checkErrorText('');
212 },
213 );
214}
215