form_test.dart 7.33 KB
Newer Older
1 2 3 4 5 6 7 8
// Copyright 2016 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_test/flutter_test.dart';
import 'package:flutter/material.dart';

void main() {
Matt Perry's avatar
Matt Perry committed
9
  testWidgets('onSaved callback is called', (WidgetTester tester) async {
10
    final GlobalKey<FormState> formKey = new GlobalKey<FormState>();
11 12 13 14 15 16
    String fieldValue;

    Widget builder() {
      return new Center(
        child: new Material(
          child: new Form(
Matt Perry's avatar
Matt Perry committed
17
            key: formKey,
18 19
            child: new TextFormField(
              onSaved: (String value) { fieldValue = value; },
20
            ),
21 22
          ),
        ),
23 24
      );
    }
25

26
    await tester.pumpWidget(builder());
27

28
    expect(fieldValue, isNull);
29

30
    Future<Null> checkText(String testValue) async {
31
      await tester.enterText(find.byType(EditableText), testValue);
Matt Perry's avatar
Matt Perry committed
32
      formKey.currentState.save();
33
      // pump'ing is unnecessary because callback happens regardless of frames
34 35
      expect(fieldValue, equals(testValue));
    }
36

37 38
    await checkText('Test');
    await checkText('');
39 40
  });

41 42 43 44 45 46 47
  testWidgets('onChanged callback is called', (WidgetTester tester) async {
    String fieldValue;

    Widget builder() {
      return new Center(
        child: new Material(
          child: new Form(
48
            child: new TextField(
49
              onChanged: (String value) { fieldValue = value; },
50
            ),
51 52
          ),
        ),
53 54 55 56 57 58 59 60
      );
    }

    await tester.pumpWidget(builder());

    expect(fieldValue, isNull);

    Future<Null> checkText(String testValue) async {
61
      await tester.enterText(find.byType(EditableText), testValue);
62 63 64 65 66 67 68 69
      // pump'ing is unnecessary because callback happens regardless of frames
      expect(fieldValue, equals(testValue));
    }

    await checkText('Test');
    await checkText('');
  });

70
  testWidgets('Validator sets the error text only when validate is called', (WidgetTester tester) async {
71
    final GlobalKey<FormState> formKey = new GlobalKey<FormState>();
72
    String errorText(String value) => value + '/error';
73

74
    Widget builder(bool autovalidate) {
75 76 77
      return new Center(
        child: new Material(
          child: new Form(
78 79
            key: formKey,
            autovalidate: autovalidate,
80
            child: new TextFormField(
81 82
              validator: errorText,
            ),
83 84
          ),
        ),
85 86
      );
    }
87

88 89
    // Start off not autovalidating.
    await tester.pumpWidget(builder(false));
90

91
    Future<Null> checkErrorText(String testValue) async {
92
      formKey.currentState.reset();
93
      await tester.enterText(find.byType(EditableText), testValue);
94 95 96
      await tester.pumpWidget(builder(false));

      // We have to manually validate if we're not autovalidating.
97
      expect(find.text(errorText(testValue)), findsNothing);
98
      formKey.currentState.validate();
99
      await tester.pump();
100
      expect(find.text(errorText(testValue)), findsOneWidget);
101 102 103

      // Try again with autovalidation. Should validate immediately.
      formKey.currentState.reset();
104
      await tester.enterText(find.byType(EditableText), testValue);
105 106
      await tester.pumpWidget(builder(true));

107
      expect(find.text(errorText(testValue)), findsOneWidget);
108
    }
109

110 111
    await checkErrorText('Test');
    await checkErrorText('');
112 113
  });

114
  testWidgets('Multiple TextFormFields communicate', (WidgetTester tester) async {
115
    final GlobalKey<FormState> formKey = new GlobalKey<FormState>();
116
    final GlobalKey<FormFieldState<String>> fieldKey = new GlobalKey<FormFieldState<String>>();
117
    // Input 2's validator depends on a input 1's value.
118
    String errorText(String input) => fieldKey.currentState.value?.toString() + '/error';
119 120 121 122 123

    Widget builder() {
      return new Center(
        child: new Material(
          child: new Form(
Matt Perry's avatar
Matt Perry committed
124
            key: formKey,
125
            autovalidate: true,
126 127
            child: new ListView(
              children: <Widget>[
128
                new TextFormField(
129 130
                  key: fieldKey,
                ),
131
                new TextFormField(
132 133 134
                  validator: errorText,
                ),
              ],
135
            ),
136 137
          ),
        ),
138 139
      );
    }
140

141
    await tester.pumpWidget(builder());
142

143
    Future<Null> checkErrorText(String testValue) async {
144
      await tester.enterText(find.byType(EditableText).first, testValue);
145
      await tester.pump();
146

147
      // Check for a new Text widget with our error text.
Matt Perry's avatar
Matt Perry committed
148
      expect(find.text(testValue + '/error'), findsOneWidget);
149
      return null;
150
    }
151

152 153
    await checkErrorText('Test');
    await checkErrorText('');
154
  });
155 156

  testWidgets('Provide initial value to input', (WidgetTester tester) async {
157
    final String initialValue = 'hello';
158 159
    final TextEditingController controller = new TextEditingController(text: initialValue);
    final GlobalKey<FormFieldState<String>> inputKey = new GlobalKey<FormFieldState<String>>();
160 161 162

    Widget builder() {
      return new Center(
Matt Perry's avatar
Matt Perry committed
163 164
        child: new Material(
          child: new Form(
165
            child: new TextFormField(
Matt Perry's avatar
Matt Perry committed
166
              key: inputKey,
167
              controller: controller,
168
            ),
169 170
          ),
        ),
171 172 173 174
      );
    }

    await tester.pumpWidget(builder());
175
    await tester.showKeyboard(find.byType(EditableText));
176 177

    // initial value should be loaded into keyboard editing state
178 179
    expect(tester.testTextInput.editingState, isNotNull);
    expect(tester.testTextInput.editingState['text'], equals(initialValue));
180 181

    // initial value should also be visible in the raw input line
182
    final EditableTextState editableText = tester.state(find.byType(EditableText));
183
    expect(editableText.config.controller.text, equals(initialValue));
184 185

    // sanity check, make sure we can still edit the text and everything updates
186
    expect(inputKey.currentState.value, equals(initialValue));
187
    await tester.enterText(find.byType(EditableText), 'world');
188
    await tester.pump();
189 190
    expect(inputKey.currentState.value, equals('world'));
    expect(editableText.config.controller.text, equals('world'));
Matt Perry's avatar
Matt Perry committed
191 192
  });

193
  testWidgets('No crash when a TextFormField is removed from the tree', (WidgetTester tester) async {
194
    final GlobalKey<FormState> formKey = new GlobalKey<FormState>();
Matt Perry's avatar
Matt Perry committed
195 196 197 198 199 200 201
    String fieldValue;

    Widget builder(bool remove) {
      return new Center(
        child: new Material(
          child: new Form(
            key: formKey,
202
            child: remove ? new Container() : new TextFormField(
203
              autofocus: true,
204 205
              onSaved: (String value) { fieldValue = value; },
              validator: (String value) { return value.isEmpty ? null : 'yes'; }
206
            ),
207 208
          ),
        ),
Matt Perry's avatar
Matt Perry committed
209 210 211 212 213 214
      );
    }

    await tester.pumpWidget(builder(false));

    expect(fieldValue, isNull);
215
    expect(formKey.currentState.validate(), isTrue);
Matt Perry's avatar
Matt Perry committed
216

217
    await tester.enterText(find.byType(EditableText), 'Test');
Matt Perry's avatar
Matt Perry committed
218 219
    await tester.pumpWidget(builder(false));

220
    // Form wasn't saved yet.
Matt Perry's avatar
Matt Perry committed
221
    expect(fieldValue, null);
222
    expect(formKey.currentState.validate(), isFalse);
Matt Perry's avatar
Matt Perry committed
223 224 225 226 227

    formKey.currentState.save();

    // Now fieldValue is saved.
    expect(fieldValue, 'Test');
228
    expect(formKey.currentState.validate(), isFalse);
Matt Perry's avatar
Matt Perry committed
229 230 231

    // Now remove the field with an error.
    await tester.pumpWidget(builder(true));
232

Matt Perry's avatar
Matt Perry committed
233 234 235
    // Reset the form. Should not crash.
    formKey.currentState.reset();
    formKey.currentState.save();
236
    expect(formKey.currentState.validate(), isTrue);
237
  });
238
}