blob: a440bd0aa987bcb9d53a41a4f39c0d7a1f16b48c [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';
import '../widgets/clipboard_utils.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final MockClipboard mockClipboard = MockClipboard();
Widget _inputDatePickerField({
Key? key,
DateTime? initialDate,
DateTime? firstDate,
DateTime? lastDate,
ValueChanged<DateTime>? onDateSubmitted,
ValueChanged<DateTime>? onDateSaved,
SelectableDayPredicate? selectableDayPredicate,
String? errorFormatText,
String? errorInvalidText,
String? fieldHintText,
String? fieldLabelText,
bool autofocus = false,
Key? formKey,
ThemeData? theme,
}) {
return MaterialApp(
theme: theme ?? ThemeData.from(colorScheme: const ColorScheme.light()),
home: Material(
child: Form(
key: formKey,
child: InputDatePickerFormField(
key: key,
initialDate: initialDate ?? DateTime(2016, DateTime.january, 15),
firstDate: firstDate ?? DateTime(2001),
lastDate: lastDate ?? DateTime(2031, DateTime.december, 31),
onDateSubmitted: onDateSubmitted,
onDateSaved: onDateSaved,
selectableDayPredicate: selectableDayPredicate,
errorFormatText: errorFormatText,
errorInvalidText: errorInvalidText,
fieldHintText: fieldHintText,
fieldLabelText: fieldLabelText,
autofocus: autofocus,
),
),
),
);
}
TextField _textField(WidgetTester tester) {
return tester.widget<TextField>(find.byType(TextField));
}
TextEditingController _textFieldController(WidgetTester tester) {
return _textField(tester).controller!;
}
double _textOpacity(WidgetTester tester, String textValue) {
final FadeTransition opacityWidget = tester.widget<FadeTransition>(
find.ancestor(
of: find.text(textValue),
matching: find.byType(FadeTransition),
).first,
);
return opacityWidget.opacity.value;
}
group('InputDatePickerFormField', () {
testWidgets('Initial date is the default', (WidgetTester tester) async {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
final DateTime initialDate = DateTime(2016, DateTime.february, 21);
DateTime? inputDate;
await tester.pumpWidget(_inputDatePickerField(
initialDate: initialDate,
onDateSaved: (DateTime date) => inputDate = date,
formKey: formKey,
));
expect(_textFieldController(tester).value.text, equals('02/21/2016'));
formKey.currentState!.save();
expect(inputDate, equals(initialDate));
});
testWidgets('Changing initial date is reflected in text value', (WidgetTester tester) async {
final DateTime initialDate = DateTime(2016, DateTime.february, 21);
final DateTime updatedInitialDate = DateTime(2016, DateTime.february, 23);
await tester.pumpWidget(_inputDatePickerField(
initialDate: initialDate,
));
expect(_textFieldController(tester).value.text, equals('02/21/2016'));
await tester.pumpWidget(_inputDatePickerField(
initialDate: updatedInitialDate,
));
await tester.pumpAndSettle();
expect(_textFieldController(tester).value.text, equals('02/23/2016'));
});
testWidgets('Valid date entry', (WidgetTester tester) async {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
DateTime? inputDate;
await tester.pumpWidget(_inputDatePickerField(
onDateSaved: (DateTime date) => inputDate = date,
formKey: formKey,
));
_textFieldController(tester).text = '02/21/2016';
formKey.currentState!.save();
expect(inputDate, equals(DateTime(2016, DateTime.february, 21)));
});
testWidgets('Invalid text entry shows errorFormat text', (WidgetTester tester) async {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
DateTime? inputDate;
await tester.pumpWidget(_inputDatePickerField(
onDateSaved: (DateTime date) => inputDate = date,
formKey: formKey,
));
// Default errorFormat text
expect(find.text('Invalid format.'), findsNothing);
await tester.enterText(find.byType(TextField), 'foobar');
expect(formKey.currentState!.validate(), isFalse);
await tester.pumpAndSettle();
expect(inputDate, isNull);
expect(find.text('Invalid format.'), findsOneWidget);
// Change to a custom errorFormat text
await tester.pumpWidget(_inputDatePickerField(
onDateSaved: (DateTime date) => inputDate = date,
errorFormatText: 'That is not a date.',
formKey: formKey,
));
expect(formKey.currentState!.validate(), isFalse);
await tester.pumpAndSettle();
expect(find.text('Invalid format.'), findsNothing);
expect(find.text('That is not a date.'), findsOneWidget);
});
testWidgets('Valid text entry, but date outside first or last date shows bounds shows errorInvalid text', (WidgetTester tester) async {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
DateTime? inputDate;
await tester.pumpWidget(_inputDatePickerField(
firstDate: DateTime(1966, DateTime.february, 21),
lastDate: DateTime(2040, DateTime.february, 23),
onDateSaved: (DateTime date) => inputDate = date,
formKey: formKey,
));
// Default errorInvalid text
expect(find.text('Out of range.'), findsNothing);
// Before first date
await tester.enterText(find.byType(TextField), '02/21/1950');
expect(formKey.currentState!.validate(), isFalse);
await tester.pumpAndSettle();
expect(inputDate, isNull);
expect(find.text('Out of range.'), findsOneWidget);
// After last date
await tester.enterText(find.byType(TextField), '02/23/2050');
expect(formKey.currentState!.validate(), isFalse);
await tester.pumpAndSettle();
expect(inputDate, isNull);
expect(find.text('Out of range.'), findsOneWidget);
await tester.pumpWidget(_inputDatePickerField(
onDateSaved: (DateTime date) => inputDate = date,
errorInvalidText: 'Not in given range.',
formKey: formKey,
));
expect(formKey.currentState!.validate(), isFalse);
await tester.pumpAndSettle();
expect(find.text('Out of range.'), findsNothing);
expect(find.text('Not in given range.'), findsOneWidget);
});
testWidgets('selectableDatePredicate will be used to show errorInvalid if date is not selectable', (WidgetTester tester) async {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
DateTime? inputDate;
await tester.pumpWidget(_inputDatePickerField(
initialDate: DateTime(2016, DateTime.january, 16),
onDateSaved: (DateTime date) => inputDate = date,
selectableDayPredicate: (DateTime date) => date.day.isEven,
formKey: formKey,
));
// Default errorInvalid text
expect(find.text('Out of range.'), findsNothing);
// Odd day shouldn't be valid
await tester.enterText(find.byType(TextField), '02/21/1966');
expect(formKey.currentState!.validate(), isFalse);
await tester.pumpAndSettle();
expect(inputDate, isNull);
expect(find.text('Out of range.'), findsOneWidget);
// Even day is valid
await tester.enterText(find.byType(TextField), '02/24/2030');
expect(formKey.currentState!.validate(), isTrue);
formKey.currentState!.save();
await tester.pumpAndSettle();
expect(inputDate, equals(DateTime(2030, DateTime.february, 24)));
expect(find.text('Out of range.'), findsNothing);
});
testWidgets('Empty field shows hint text when focused', (WidgetTester tester) async {
await tester.pumpWidget(_inputDatePickerField());
// Focus on it
await tester.tap(find.byType(TextField));
await tester.pumpAndSettle();
// Hint text should be invisible
expect(_textOpacity(tester, 'mm/dd/yyyy'), equals(0.0));
_textFieldController(tester).clear();
await tester.pumpAndSettle();
// Hint text should be visible
expect(_textOpacity(tester, 'mm/dd/yyyy'), equals(1.0));
// Change to a different hint text
await tester.pumpWidget(_inputDatePickerField(fieldHintText: 'Enter some date'));
await tester.pumpAndSettle();
expect(find.text('mm/dd/yyyy'), findsNothing);
expect(_textOpacity(tester, 'Enter some date'), equals(1.0));
await tester.enterText(find.byType(TextField), 'foobar');
await tester.pumpAndSettle();
expect(_textOpacity(tester, 'Enter some date'), equals(0.0));
});
testWidgets('Label text', (WidgetTester tester) async {
await tester.pumpWidget(_inputDatePickerField());
// Default label
expect(find.text('Enter Date'), findsOneWidget);
await tester.pumpWidget(_inputDatePickerField(
fieldLabelText: 'Give me a date!',
));
expect(find.text('Enter Date'), findsNothing);
expect(find.text('Give me a date!'), findsOneWidget);
});
testWidgets('Semantics', (WidgetTester tester) async {
final SemanticsHandle semantics = tester.ensureSemantics();
addTearDown(semantics.dispose);
// Fill the clipboard so that the Paste option is available in the text
// selection menu.
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, mockClipboard.handleMethodCall);
await Clipboard.setData(const ClipboardData(text: 'Clipboard data'));
addTearDown(() => tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, null));
await tester.pumpWidget(_inputDatePickerField(autofocus: true));
await tester.pumpAndSettle();
expect(tester.getSemantics(find.byType(EditableText)), matchesSemantics(
label: 'Enter Date\nmm/dd/yyyy',
isTextField: true,
isFocused: true,
value: '01/15/2016',
hasTapAction: true,
hasSetTextAction: true,
hasSetSelectionAction: true,
hasCopyAction: true,
hasCutAction: true,
hasPasteAction: true,
hasMoveCursorBackwardByCharacterAction: true,
hasMoveCursorBackwardByWordAction: true,
));
});
testWidgets('InputDecorationTheme is honored', (WidgetTester tester) async {
const InputBorder border = InputBorder.none;
await tester.pumpWidget(_inputDatePickerField(
theme: ThemeData.from(colorScheme: const ColorScheme.light()).copyWith(
inputDecorationTheme: const InputDecorationTheme(
border: border,
),
),
));
await tester.pumpAndSettle();
// Get the border and container color from the painter of the _BorderContainer
// (this was cribbed from input_decorator_test.dart).
final CustomPaint customPaint = tester.widget(find.descendant(
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_BorderContainer'),
matching: find.byWidgetPredicate((Widget w) => w is CustomPaint),
));
final dynamic/*_InputBorderPainter*/ inputBorderPainter = customPaint.foregroundPainter;
// ignore: avoid_dynamic_calls
final dynamic/*_InputBorderTween*/ inputBorderTween = inputBorderPainter.border;
// ignore: avoid_dynamic_calls
final Animation<double> animation = inputBorderPainter.borderAnimation as Animation<double>;
// ignore: avoid_dynamic_calls
final InputBorder actualBorder = inputBorderTween.evaluate(animation) as InputBorder;
// ignore: avoid_dynamic_calls
final Color containerColor = inputBorderPainter.blendedColor as Color;
// Border should match
expect(actualBorder, equals(border));
// It shouldn't be filled, so the color should be transparent
expect(containerColor, equals(Colors.transparent));
});
});
}