Skip to content

Commit 511814f

Browse files
nicolanteanmirland
andauthored
feat: integration test for SignIn flow (#204)
Co-authored-by: Matías Irland <[email protected]>
1 parent 733e23c commit 511814f

25 files changed

+263
-50
lines changed

.fvmrc

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"flutter": "3.22.1"
2+
"flutter": "3.22.1",
3+
"flavors": {}
34
}

.gitignore

+3-3
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,13 @@ app.*.map.json
5454
*.env
5555
*.env.default
5656

57-
# Fvm
58-
.fvm/
59-
6057
# fastlane specific
6158
**/fastlane/report.xml
6259
**/fastlane/Preview.html
6360
**/fastlane/screenshots
6461
**/fastlane/test_output
6562
.bundle/
6663
vendor/bundle/
64+
65+
# FVM Version Cache
66+
.fvm/

.vscode/launch.json

+10-11
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,14 @@
66
"request": "launch",
77
"type": "dart",
88
"program": "lib/main.dart",
9-
"args": [
10-
"--flavor=dev",
11-
"--dart-define=ENV=dev",
12-
"--web-port=3003"
13-
]
9+
"args": ["--flavor=dev", "--dart-define=ENV=dev", "--web-port=3003"]
1410
},
1511
{
1612
"name": "Main prod",
1713
"request": "launch",
1814
"type": "dart",
1915
"program": "lib/main.dart",
20-
"args": [
21-
"--flavor=prod",
22-
"--web-port=3003",
23-
"--dart-define=ENV=prod"
24-
]
16+
"args": ["--flavor=prod", "--web-port=3003", "--dart-define=ENV=prod"]
2517
},
2618
{
2719
"name": "Main staging",
@@ -33,6 +25,13 @@
3325
"--web-port=3003",
3426
"--dart-define=ENV=staging"
3527
]
28+
},
29+
{
30+
"name": "Integration Test",
31+
"program": "integration_test/test",
32+
"request": "launch",
33+
"type": "dart",
34+
"args": ["--flavor=dev"]
3635
}
3736
]
38-
}
37+
}

README.md

+25-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Xmartlabs' Flutter template
22

3-
## Arch Overview
3+
## Arch Overview
44

55
The project is divided into two main folders:
6+
67
- The UI contains all app screens.
78
- The Core contains the models and the data layer.
89

@@ -29,34 +30,47 @@ These components are injected in the Cubits using [get_it][get_it].
2930
## Project Overview
3031

3132
### Assets
33+
3234
The [`/assets/`](./assets) folder contains the assets used by the application, such as images, fonts, and other files.
3335

3436
### Environments
3537

3638
The environment variables are defined in the `default.env` file located in [`/environments/`](./environments) folder.
3739
You can read more information about the environment variables in the [README.md](./environments/README.md) file.
3840

41+
### Testing
42+
43+
#### Mocks
44+
45+
For moking the projects uses [mocktail][mocktail], a library inspired on [mockito][mockito] which deletes the code generation part.
46+
47+
#### Integration test
48+
49+
The integration tests for the project are defined in [integration_test][integration_test]. Dart package integration_test is used for the implementation.
50+
3951
## Project Setup
4052

4153
The project setup is based on some plugins which generate the required native code.
4254

4355
You can use [project_setup.sh](scripts/project_setup.sh) to reload all project setups.
4456

4557
### Flavor setup: Project name, properties BundleId & Application id
46-
This information is set using [flavorizr], a flutter utility to easily create flavors in your flutter application.
58+
59+
This information is set using [flavorizr], a flutter utility to easily create flavors in your flutter application.
4760
To change it go to `flavorizr` section in the [pubspec] file.
4861

4962
For example, to add a new flavour, you can do something like:
63+
5064
```yaml
5165
flavorizr:
5266
flavors:
5367
qa:
5468
app:
55-
name: 'My Project - QA'
69+
name: "My Project - QA"
5670
android:
57-
applicationId: 'com.xmartlabs.myproject.qa'
71+
applicationId: "com.xmartlabs.myproject.qa"
5872
ios:
59-
bundleId: 'com.xmartlabs.myproject.qa'
73+
bundleId: "com.xmartlabs.myproject.qa"
6074
```
6175
6276
After a change is made, you need to regenerate your native files.
@@ -72,7 +86,6 @@ To change it go to `flutter_icons` section in the [pubspec] file.
7286
After a change is made, you need to regenerate your native files.
7387
You can do that by executing `flutter pub run flutter_launcher_icons:main`.
7488

75-
7689
### Splash screen
7790

7891
Splash screen is generated using [flutter_native_splash].
@@ -82,13 +95,14 @@ After a change is made, you need to regenerate your native files.
8295
You can do that by executing `flutter pub run flutter_native_splash:create`.
8396

8497
Although you can setup a bunch of features in this library, it doesn't provide a way to display animations.
85-
If you need a more personalized splash screen, you can edit the native code or just remove this library.
98+
If you need a more personalized splash screen, you can edit the native code or just remove this library.
8699

87100
### Code generation
88101

89102
Code generation is created using `build_runner` package.\
90103
To configure this package edit the `build.yaml`\
91104
To add new files to watch for code generation add the following lines:
105+
92106
```
93107
targets:
94108
$default:
@@ -100,6 +114,7 @@ targets:
100114
# Example glob for only the Dart files under `lib/models`
101115
- lib/models/*.dart
102116
```
117+
103118
To create generated code run `clean_up.sh` under [scripts] folder or the following command: `flutter pub run build_runner build --delete-conflicting-outputs`
104119

105120
### Pre Push config
@@ -120,3 +135,6 @@ In order to setup pre-push hook you need to go to the root of the project and ru
120135
[data_source_folder]: https://github.com/xmartlabs/flutter-template/tree/main/lib/core/source
121136
[get_it]: https://pub.dev/packages/get_it
122137
[scripts]: https://github.com/xmartlabs/flutter-template/tree/main/scripts
138+
[integration_test]: https://github.com/xmartlabs/flutter-template/tree/main/intgration_test
139+
[mocktail]: https://pub.dev/packages/mocktail
140+
[mockito]: https://pub.dev/packages/mockito

flavorizr.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# IMPORTANT
2+
# To keep integration test working, after excecuting flavorizr for creating more schemas
3+
# you need to remove FLUTTER_TARGET line from all .xcconfig files on ios/Flutter directory
4+
15
flavors:
26
dev:
37
app:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:flutter/widgets.dart';
2+
import 'package:flutter_template/core/di/di_provider.dart';
3+
import 'package:flutter_template/core/source/auth_remote_source.dart';
4+
import 'package:flutter_template/core/source/project_remote_source.dart';
5+
import 'package:flutter_template/main.dart' as app;
6+
import 'package:flutter_test/flutter_test.dart';
7+
8+
import 'repository_mocks.dart';
9+
10+
Future<void> commonSetup({
11+
required MockAuthRemoteSource mockAuthRemoteSource,
12+
required MockProjectRemoteSource mockProjectRemoteSource,
13+
}) async {
14+
await app.initSdks();
15+
16+
DiProvider.instance.unregister<AuthRemoteSource>();
17+
DiProvider.instance.unregister<ProjectRemoteSource>();
18+
19+
DiProvider.instance.registerSingleton<AuthRemoteSource>(mockAuthRemoteSource);
20+
DiProvider.instance
21+
.registerSingleton<ProjectRemoteSource>(mockProjectRemoteSource);
22+
}
23+
24+
extension WidgetTesterExtension on WidgetTester {
25+
BuildContext contextOfType<T extends Widget>() {
26+
final finder = find.byType(T);
27+
expect(finder, findsOneWidget);
28+
return element(finder);
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import 'package:flutter_template/core/source/auth_remote_source.dart';
2+
import 'package:flutter_template/core/source/project_remote_source.dart';
3+
import 'package:mocktail/mocktail.dart';
4+
5+
class MockProjectRemoteSource extends Mock implements ProjectRemoteSource {}
6+
7+
class MockAuthRemoteSource extends Mock implements AuthRemoteSource {}
+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_template/core/common/network_exceptions.dart';
3+
import 'package:flutter_template/core/di/di_provider.dart';
4+
import 'package:flutter_template/core/model/service/auth_models.dart';
5+
import 'package:flutter_template/core/model/user.dart';
6+
import 'package:flutter_template/main.dart' as app;
7+
import 'package:flutter_template/ui/extensions/context_extensions.dart';
8+
import 'package:flutter_template/ui/signin/signin_screen.dart';
9+
import 'package:flutter_template/ui/welcome/welcome_screen.dart';
10+
import 'package:integration_test/integration_test.dart';
11+
import 'package:flutter_test/flutter_test.dart';
12+
import 'package:mocktail/mocktail.dart';
13+
14+
import '../common/general_helpers.dart';
15+
import '../common/repository_mocks.dart';
16+
17+
void main() {
18+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
19+
late MockAuthRemoteSource mockAuthRemoteSource;
20+
late MockProjectRemoteSource mockProjectRemoteSource;
21+
22+
setUp(() async {
23+
mockAuthRemoteSource = MockAuthRemoteSource();
24+
mockProjectRemoteSource = MockProjectRemoteSource();
25+
await commonSetup(
26+
mockAuthRemoteSource: mockAuthRemoteSource,
27+
mockProjectRemoteSource: mockProjectRemoteSource,
28+
);
29+
});
30+
31+
tearDown(() {
32+
DiProvider.instance.reset();
33+
});
34+
group('SignIn screen tests', () {
35+
const email = "[email protected]";
36+
const password = "test";
37+
38+
testWidgets('SignIn with wrong credentials', (WidgetTester tester) async {
39+
when(() => mockAuthRemoteSource.signIn(email, password)).thenAnswer(
40+
(_) => Future.error(
41+
const NetworkException.unauthorizedRequest("Unauthorized"),
42+
),
43+
);
44+
45+
await tester.pumpWidget(const app.MyApp());
46+
await tester.pumpAndSettle();
47+
48+
final context = tester.contextOfType<SignInScreen>();
49+
await tester.enterText(
50+
find.widgetWithText(TextField, context.localizations.mail),
51+
email,
52+
);
53+
await tester.enterText(
54+
find.widgetWithText(TextField, context.localizations.password),
55+
password,
56+
);
57+
await tester.tap(
58+
find.widgetWithText(TextButton, context.localizations.sign_in),
59+
);
60+
await tester.pumpAndSettle();
61+
expect(find.byType(AlertDialog), findsOneWidget);
62+
});
63+
testWidgets('SignIn with correct credentials', (WidgetTester tester) async {
64+
when(() => mockAuthRemoteSource.signIn(email, password)).thenAnswer(
65+
(_) => Future.value(
66+
SignInResponse(
67+
accessToken: "testing-token",
68+
user: User(email: "[email protected]"),
69+
),
70+
),
71+
);
72+
when(() => mockProjectRemoteSource.getProjects())
73+
.thenAnswer((_) => Future.value([]));
74+
75+
await tester.pumpWidget(const app.MyApp());
76+
await tester.pumpAndSettle();
77+
78+
final context = tester.contextOfType<SignInScreen>();
79+
await tester.enterText(
80+
find.widgetWithText(TextField, context.localizations.mail),
81+
email,
82+
);
83+
await tester.enterText(
84+
find.widgetWithText(TextField, context.localizations.password),
85+
password,
86+
);
87+
await tester
88+
.tap(find.widgetWithText(TextButton, context.localizations.sign_in));
89+
await tester.pumpAndSettle();
90+
expect(find.byType(WelcomeScreen), findsOneWidget);
91+
});
92+
});
93+
}

ios/Flutter/devDebug.xcconfig

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "Generated.xcconfig"
22

3-
FLUTTER_TARGET=lib/main.dart
43
ASSET_PREFIX=dev
54
BUNDLE_NAME=Template Dev
65
BUNDLE_DISPLAY_NAME=Template Dev

ios/Flutter/devProfile.xcconfig

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "Generated.xcconfig"
22

3-
FLUTTER_TARGET=lib/main.dart
43
ASSET_PREFIX=dev
54
BUNDLE_NAME=Template Dev
65
BUNDLE_DISPLAY_NAME=Template Dev

ios/Flutter/devRelease.xcconfig

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "Generated.xcconfig"
22

3-
FLUTTER_TARGET=lib/main.dart
43
ASSET_PREFIX=dev
54
BUNDLE_NAME=Template Dev
65
BUNDLE_DISPLAY_NAME=Template Dev

ios/Flutter/prodDebug.xcconfig

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "Generated.xcconfig"
22

3-
FLUTTER_TARGET=lib/main.dart
43
ASSET_PREFIX=prod
54
BUNDLE_NAME=Template
65
BUNDLE_DISPLAY_NAME=Template

ios/Flutter/prodProfile.xcconfig

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "Generated.xcconfig"
22

3-
FLUTTER_TARGET=lib/main.dart
43
ASSET_PREFIX=prod
54
BUNDLE_NAME=Template
65
BUNDLE_DISPLAY_NAME=Template

ios/Flutter/prodRelease.xcconfig

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "Generated.xcconfig"
22

3-
FLUTTER_TARGET=lib/main.dart
43
ASSET_PREFIX=prod
54
BUNDLE_NAME=Template
65
BUNDLE_DISPLAY_NAME=Template

ios/Flutter/stagingDebug.xcconfig

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "Generated.xcconfig"
22

3-
FLUTTER_TARGET=lib/main.dart
43
ASSET_PREFIX=staging
54
BUNDLE_NAME=Template Sta
65
BUNDLE_DISPLAY_NAME=Template Sta

ios/Flutter/stagingProfile.xcconfig

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "Generated.xcconfig"
22

3-
FLUTTER_TARGET=lib/main.dart
43
ASSET_PREFIX=staging
54
BUNDLE_NAME=Template Sta
65
BUNDLE_DISPLAY_NAME=Template Sta

ios/Flutter/stagingRelease.xcconfig

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "Generated.xcconfig"
22

3-
FLUTTER_TARGET=lib/main.dart
43
ASSET_PREFIX=staging
54
BUNDLE_NAME=Template Sta
65
BUNDLE_DISPLAY_NAME=Template Sta

0 commit comments

Comments
 (0)