Skip to content

Commit 943e730

Browse files
authored
chore: add tests and workflow for example app (#107)
* chore: add tests and workflow for exampl * spell
1 parent 5f6e806 commit 943e730

4 files changed

Lines changed: 191 additions & 4 deletions

File tree

.github/workflows/example.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: example
2+
3+
concurrency:
4+
group: ${{ github.workflow }}-${{ github.ref }}
5+
cancel-in-progress: true
6+
7+
on:
8+
push:
9+
branches:
10+
- main
11+
pull_request:
12+
branches:
13+
- main
14+
15+
jobs:
16+
semantic-pull-request:
17+
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/semantic_pull_request.yml@v1
18+
19+
build:
20+
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1
21+
with:
22+
flutter_channel: stable
23+
24+
spell-check:
25+
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/spell_check.yml@v1
26+
with:
27+
includes: |
28+
**/*.md
29+
modified_files_only: false

example/lib/main.dart

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ class MyApp extends StatelessWidget {
1313
return MaterialApp(
1414
home: Scaffold(
1515
appBar: AppBar(title: const Text('Formz Example')),
16-
body: const Padding(
17-
padding: EdgeInsets.all(24),
16+
body: Padding(
17+
padding: const EdgeInsets.all(24),
1818
child: SingleChildScrollView(child: MyForm()),
1919
),
2020
),
@@ -23,7 +23,9 @@ class MyApp extends StatelessWidget {
2323
}
2424

2525
class MyForm extends StatefulWidget {
26-
const MyForm({super.key});
26+
MyForm({super.key, Random? seed}) : seed = seed ?? Random();
27+
28+
final Random seed;
2729

2830
@override
2931
State<MyForm> createState() => _MyFormState();
@@ -89,7 +91,7 @@ class _MyFormState extends State<MyForm> {
8991

9092
Future<void> _submitForm() async {
9193
await Future<void>.delayed(const Duration(seconds: 1));
92-
if (Random().nextInt(2) == 0) throw Exception();
94+
if (widget.seed.nextInt(2) == 0) throw Exception();
9395
}
9496

9597
void _resetForm() {
@@ -123,6 +125,7 @@ class _MyFormState extends State<MyForm> {
123125
child: Column(
124126
children: [
125127
TextFormField(
128+
key: const Key('myForm_emailInput'),
126129
controller: _emailController,
127130
decoration: const InputDecoration(
128131
icon: Icon(Icons.email),
@@ -134,6 +137,7 @@ class _MyFormState extends State<MyForm> {
134137
textInputAction: TextInputAction.next,
135138
),
136139
TextFormField(
140+
key: const Key('myForm_passwordInput'),
137141
controller: _passwordController,
138142
decoration: const InputDecoration(
139143
icon: Icon(Icons.lock),
@@ -153,6 +157,7 @@ class _MyFormState extends State<MyForm> {
153157
const CircularProgressIndicator()
154158
else
155159
ElevatedButton(
160+
key: const Key('myForm_submit'),
156161
onPressed: _onSubmit,
157162
child: const Text('Submit'),
158163
),

example/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ dependencies:
1111
sdk: flutter
1212
formz:
1313
path: ../
14+
mocktail: ^1.0.0
1415

1516
dev_dependencies:
1617
flutter_test:

example/test/main_test.dart

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import 'dart:math';
2+
3+
import 'package:example/main.dart';
4+
import 'package:flutter/material.dart';
5+
import 'package:flutter_test/flutter_test.dart';
6+
import 'package:mocktail/mocktail.dart';
7+
8+
class MockRandom extends Mock implements Random {}
9+
10+
11+
final _seed = MockRandom();
12+
13+
void main() {
14+
15+
group('$MyApp', () {
16+
testWidgets('render example', (tester) async {
17+
await tester.pumpWidget(const MyApp());
18+
expect(find.text('Formz Example'), findsOneWidget);
19+
});
20+
});
21+
22+
group('$MyForm', () {
23+
24+
setUp(() {
25+
when(()=> _seed.nextInt(any())).thenReturn(1);
26+
});
27+
28+
testWidgets('submits valid values', (tester) async {
29+
await tester.pumpMyForm();
30+
31+
await tester.enterText(
32+
find.byKey(const Key('myForm_emailInput')),
33+
'email@example.com',
34+
);
35+
await tester.pumpAndSettle();
36+
37+
await tester.enterText(
38+
find.byKey(const Key('myForm_passwordInput')),
39+
'123password',
40+
);
41+
await tester.pumpAndSettle();
42+
await tester.tap(find.byKey(const Key('myForm_submit')));
43+
await tester.pumpAndSettle();
44+
await tester.pumpAndSettle();
45+
expect(find.text('Submitted successfully! 🎉'), findsOneWidget);
46+
});
47+
48+
group('invalid values', () {
49+
testWidgets('invalid email', (tester) async {
50+
await tester.pumpMyForm();
51+
52+
await tester.enterText(
53+
find.byKey(const Key('myForm_emailInput')),
54+
'example.com',
55+
);
56+
await tester.pumpAndSettle();
57+
58+
await tester.enterText(
59+
find.byKey(const Key('myForm_passwordInput')),
60+
'123password',
61+
);
62+
await tester.pumpAndSettle();
63+
await tester.tap(find.byKey(const Key('myForm_submit')));
64+
await tester.pumpAndSettle();
65+
await tester.pumpAndSettle();
66+
67+
expect(
68+
find.text('Please ensure the email entered is valid'),
69+
findsOneWidget,
70+
);
71+
});
72+
73+
testWidgets('empty email', (tester) async {
74+
await tester.pumpMyForm();
75+
76+
await tester.enterText(
77+
find.byKey(const Key('myForm_emailInput')),
78+
'',
79+
);
80+
await tester.pumpAndSettle();
81+
82+
await tester.enterText(
83+
find.byKey(const Key('myForm_passwordInput')),
84+
'123password',
85+
);
86+
await tester.pumpAndSettle();
87+
await tester.tap(find.byKey(const Key('myForm_submit')));
88+
await tester.pumpAndSettle();
89+
await tester.pumpAndSettle();
90+
91+
expect(
92+
find.text('Please ensure the email entered is valid'),
93+
findsOneWidget,
94+
);
95+
});
96+
97+
testWidgets('invalid password', (tester) async {
98+
await tester.pumpMyForm();
99+
100+
await tester.enterText(
101+
find.byKey(const Key('myForm_emailInput')),
102+
'email@example.com',
103+
);
104+
await tester.pumpAndSettle();
105+
106+
await tester.enterText(
107+
find.byKey(const Key('myForm_passwordInput')),
108+
'12345678',
109+
);
110+
await tester.pumpAndSettle();
111+
await tester.tap(find.byKey(const Key('myForm_submit')));
112+
await tester.pumpAndSettle();
113+
await tester.pumpAndSettle();
114+
115+
expect(
116+
find.text('''Password must be at least 8 characters and contain at least one letter and number'''),
117+
findsOneWidget,
118+
);
119+
});
120+
121+
testWidgets('empty password', (tester) async {
122+
await tester.pumpMyForm();
123+
124+
await tester.enterText(
125+
find.byKey(const Key('myForm_emailInput')),
126+
'email@example.com',
127+
);
128+
await tester.pumpAndSettle();
129+
130+
await tester.enterText(
131+
find.byKey(const Key('myForm_passwordInput')),
132+
'',
133+
);
134+
await tester.pumpAndSettle();
135+
await tester.tap(find.byKey(const Key('myForm_submit')));
136+
await tester.pumpAndSettle();
137+
await tester.pumpAndSettle();
138+
139+
expect(
140+
find.text('''Password must be at least 8 characters and contain at least one letter and number'''),
141+
findsOneWidget,
142+
);
143+
});
144+
});
145+
});
146+
}
147+
148+
extension on WidgetTester {
149+
Future<void> pumpMyForm() async {
150+
await pumpWidget(MaterialApp(home: Scaffold(body: MyForm(seed: _seed))));
151+
}
152+
}

0 commit comments

Comments
 (0)