Skip to content

Commit

Permalink
Add tests for FTextField
Browse files Browse the repository at this point in the history
  • Loading branch information
Pante committed Jun 6, 2024
1 parent 9cf49f2 commit c7a72db
Show file tree
Hide file tree
Showing 45 changed files with 314 additions and 23 deletions.
13 changes: 13 additions & 0 deletions forui/example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_localizations:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_svg:
dependency: transitive
description:
Expand Down Expand Up @@ -297,6 +302,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.2"
intl:
dependency: transitive
description:
name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev"
source: hosted
version: "0.19.0"
io:
dependency: transitive
description:
Expand Down
56 changes: 55 additions & 1 deletion forui/lib/src/widgets/text_field/text_field.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:forui/forui.dart';

part 'text_field_state.dart';
Expand Down Expand Up @@ -475,13 +478,64 @@ final class FTextField extends StatefulWidget {
this.suffixIcon,
});

/// Creates a [FTextField] configured for emails.
const FTextField.email({
this.style,
this.hint = 'Email',
this.magnifierConfiguration,
this.controller,
this.focusNode,
this.keyboardType = TextInputType.emailAddress,
this.textInputAction,
this.textCapitalization = TextCapitalization.none,
this.textAlign = TextAlign.start,
this.textAlignVertical,
this.textDirection,
this.autofocus = false,
this.statesController,
this.obscureText = false,
this.autocorrect = false,
this.smartDashesType,
this.smartQuotesType,
this.enableSuggestions = true,
this.minLines,
this.maxLines = 1,
this.expands = false,
this.readOnly = false,
this.showCursor,
this.maxLength,
this.maxLengthEnforcement,
this.onChange,
this.onEditingComplete,
this.onSubmit,
this.onAppPrivateCommand,
this.inputFormatters,
this.enabled = true,
this.ignorePointers,
this.enableInteractSelection = true,
this.selectionControls,
this.dragStartBehavior = DragStartBehavior.start,
this.scrollPhysics,
this.scrollController,
this.autofillHints = const [AutofillHints.email],
this.restorationId,
this.scribbleEnabled = true,
this.enableIMEPersonalizedLearning = true,
this.contextMenuBuilder = _defaultContextMenuBuilder,
this.canRequestFocus = true,
this.undoController,
this.spellCheckConfiguration,
this.label,
this.suffixIcon,
});

/// Creates a [FTextField] configured for passwords.
///
/// [autofillHints] defaults to [AutofillHints.password]. It should be overridden with [AutofillHints.newPassword]
/// when handling the creation of new passwords.
const FTextField.password({
this.style,
this.hint,
this.hint = 'Password',
this.magnifierConfiguration,
this.controller,
this.focusNode,
Expand Down
62 changes: 41 additions & 21 deletions forui/lib/src/widgets/text_field/text_field_state.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
part of 'text_field.dart';


class _State extends State<FTextField> {

late final WidgetStatesController controller;
Expand All @@ -14,8 +15,9 @@ class _State extends State<FTextField> {
Widget build(BuildContext context) {
final theme = context.theme;
final style = widget.style ?? theme.textFieldStyle;
final materialLocalizations = Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);

return MergeSemantics(
final textField = MergeSemantics(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expand All @@ -27,33 +29,51 @@ class _State extends State<FTextField> {
style: style.label,
),
),
Theme(
// The selection colors are defined in a Theme instead of TextField since TextField does not expose parameters
// for overriding selectionHandleColor.
data: Theme.of(context).copyWith(
textSelectionTheme: TextSelectionThemeData(
cursorColor: style.cursor,
selectionColor: style.cursor.withOpacity(0.4),
selectionHandleColor: style.cursor,
Material(
color: Colors.transparent,
child: Theme(
// The selection colors are defined in a Theme instead of TextField since TextField does not expose parameters
// for overriding selectionHandleColor.
data: Theme.of(context).copyWith(
textSelectionTheme: TextSelectionThemeData(
cursorColor: style.cursor,
selectionColor: style.cursor.withOpacity(0.4),
selectionHandleColor: style.cursor,
),
cupertinoOverrideTheme: CupertinoThemeData(
primaryColor: style.cursor,
),
),
cupertinoOverrideTheme: CupertinoThemeData(
primaryColor: style.cursor,
// This is done because InputDecoration.errorBorder and InputDecoration.focusedErrorBorder aren't shown unless an
// additional error help text is supplied. That error help text has few configuration options.
child: ValueListenableBuilder(
valueListenable: widget.statesController ?? controller,
builder: (context, states, _) {
final (enabled, focus) = states.contains(WidgetState.error) ? (style.error, style.focusedError) : (style.enabled, style.focused);
final current = states.contains(WidgetState.focused) ? focus : enabled;
return _build(context, style, current, enabled, focus);
},
),
),
// This is done because InputDecoration.errorBorder and InputDecoration.focusedErrorBorder aren't shown unless an
// additional error help text is supplied. That error help text has few configuration options.
child: ValueListenableBuilder(
valueListenable: widget.statesController ?? controller,
builder: (context, states, _) {
final (enabled, focus) = states.contains(WidgetState.error) ? (style.error, style.focusedError) : (style.enabled, style.focused);
final current = states.contains(WidgetState.focused) ? focus : enabled;
return _build(context, style, current, enabled, focus);
},
),
),
],
),
);


if (materialLocalizations == null) {
return Localizations(
locale: Localizations.maybeLocaleOf(context) ?? const Locale('en', 'US'),
delegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
child: textField,
);
}

return textField;
}

Widget _build(
Expand Down
2 changes: 1 addition & 1 deletion forui/lib/src/widgets/text_field/text_field_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ final class FTextFieldStyle with Diagnosticable {
label = TextStyle(
color: colorScheme.primary,
fontSize: font.sm,
fontWeight: FontWeight.bold,
fontWeight: FontWeight.w600,
),
keyboardAppearance = colorScheme.brightness,
cursor = CupertinoColors.activeBlue,
Expand Down
2 changes: 2 additions & 0 deletions forui/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ environment:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
flutter_svg: ^2.0.10+1
forui_assets:
path: ../forui_assets
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
@Tags(['golden'])
library;

import 'package:flutter/cupertino.dart';

import 'package:flutter_test/flutter_test.dart';
Expand Down
133 changes: 133 additions & 0 deletions forui/test/src/widgets/text_field_golden_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
@Tags(['golden'])
library;

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:forui/forui.dart';

import '../test_scaffold.dart';

const _text =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. '
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure '
'dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non '
'proident, sunt in culpa qui officia deserunt mollit anim id est laborum ';

void main() {
group('FTextField', () {
for (final (theme, theme_, _) in TestScaffold.themes) {
for (final (focused, focused_) in [('focused', true), ('unfocused', false)]) {
for (final (text, text_) in [('text', _text), ('no-text', null)]) {
testWidgets('default - $theme - $focused', (tester) async {
final controller = text_ == null ? null : TextEditingController(text: text_);
await tester.pumpWidget(
MaterialApp(
home: TestScaffold(
data: theme_,
child: Padding(
padding: const EdgeInsets.all(20),
child: FTextField(
controller: controller,
autofocus: focused_,
label: 'My Label',
hint: 'hint',
),
),
),
),
);

await tester.pumpAndSettle();

await expectLater(
find.byType(TestScaffold),
matchesGoldenFile('text_field/default-$theme-$focused-$text.png'),
);
});

testWidgets('email - $theme - $focused', (tester) async {
final controller = text_ == null ? null : TextEditingController(text: text_);
await tester.pumpWidget(
MaterialApp(
home: TestScaffold(
data: theme_,
child: Padding(
padding: const EdgeInsets.all(20),
child: FTextField.email(
controller: controller,
autofocus: focused_,
label: 'Email',
hint: '[email protected]',
),
),
),
),
);

await tester.pumpAndSettle();

await expectLater(
find.byType(TestScaffold),
matchesGoldenFile('text_field/email-$theme-$focused-$text.png'),
);
});

testWidgets('password - $theme - $focused', (tester) async {
final controller = text_ == null ? null : TextEditingController(text: text_);
await tester.pumpWidget(
MaterialApp(
home: TestScaffold(
data: theme_,
child: Padding(
padding: const EdgeInsets.all(20),
child: FTextField.password(
controller: controller,
autofocus: focused_,
label: 'Password',
hint: 'password',
),
),
),
),
);

await tester.pumpAndSettle();

await expectLater(
find.byType(TestScaffold),
matchesGoldenFile('text_field/password-$theme-$focused-$text.png'),
);
});

testWidgets('multiline - $theme - $focused', (tester) async {
final controller = text_ == null ? null : TextEditingController(text: text_);
await tester.pumpWidget(
MaterialApp(
home: TestScaffold(
data: theme_,
child: Padding(
padding: const EdgeInsets.all(20),
child: FTextField.multiline(
controller: controller,
autofocus: focused_,
label: 'My Label',
hint: 'hint',
),
),
),
),
);

await tester.pumpAndSettle();

await expectLater(
find.byType(TestScaffold),
matchesGoldenFile('text_field/multiline-$theme-$focused-$text.png'),
);
});
}

}
}
});
}
Loading

0 comments on commit c7a72db

Please sign in to comment.