Skip to content

Commit

Permalink
Merge pull request #53 from hoangnguyen92dn/release/0.2.0
Browse files Browse the repository at this point in the history
Release - 0.2.0
  • Loading branch information
hoangnguyen92dn authored Mar 31, 2023
2 parents 8218551 + f407dcf commit 78a0ebe
Show file tree
Hide file tree
Showing 77 changed files with 2,183 additions and 180 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/android_deploy_production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
env:
ENV_STAGING: ${{ secrets.ENV }}
run: |
echo $ENV > .env
echo "$ENV" > .env
# App Bundle requires Firebase connected to Play Store to upload https://appdistribution.page.link/KPoa
- name: Build Android apk
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/android_deploy_staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
env:
ENV_STAGING: ${{ secrets.ENV_STAGING }}
run: |
echo $ENV_STAGING > .env.staging
echo "$ENV_STAGING" > .env.staging
# App Bundle requires Firebase connected to Play Store to upload https://appdistribution.page.link/KPoa
- name: Build Android apk
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Clone the repository

- For example:

`$ fvm flutter drive --driver=test_driver/integration_test.dart --target=integration_test/my_home_page_test.dart --flavor staging`
`$ fvm flutter drive --driver=test_driver/integration_test.dart --target=integration_test/home_screen_test.dart --flavor staging`

- Code coverage integration:

Expand Down
Binary file added assets/fonts/neuzeit_bold.otf
Binary file not shown.
File renamed without changes.
Binary file added assets/images/placeholder_avatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 65 additions & 0 deletions integration_test/fake_data/fake_data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import 'package:dio/dio.dart';
import 'package:equatable/equatable.dart';

import '../utils/file_util.dart';

class FakeResponseModel extends Equatable {
final int statusCode;
final Map<String, dynamic> json;

const FakeResponseModel(
this.statusCode,
this.json,
);

@override
List<Object?> get props => [
statusCode,
json,
];
}

const String keySignIn = 'signIn';
const String keyUserProfile = 'userProfile';

class FakeData {
FakeData._();

static final Map<String, FakeResponseModel> _apiAndResponse = {};

static Map<String, FakeResponseModel> get apiAndResponse => _apiAndResponse;

static Future<void> initDefault() async {
_apiAndResponse.addAll({
keySignIn: FakeResponseModel(
200,
await FileUtil.loadFile(
'integration_test/fake_data/fake_sign_in_response.json'),
),
keyUserProfile: FakeResponseModel(
200,
await FileUtil.loadFile(
'integration_test/fake_data/fake_user_profile_response.json'),
),
});
}

static void updateResponse(String key, FakeResponseModel newValue) {
_apiAndResponse.update(
key,
(value) => newValue,
ifAbsent: () => newValue,
);
}
}

DioError generateDioError(int statusCode) {
return DioError(
response: Response(
statusCode: statusCode,
requestOptions: RequestOptions(path: ''),
),
type: DioErrorType.badResponse,
requestOptions: RequestOptions(path: ''),
);
}
21 changes: 21 additions & 0 deletions integration_test/fake_data/fake_services/fake_auth_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:retrofit/retrofit.dart';
import 'package:survey_flutter_ic/api/request/sign_in_request.dart';
import 'package:survey_flutter_ic/api/response/auth_response.dart';
import 'package:survey_flutter_ic/api/service/auth_service.dart';

import '../fake_data.dart';

class FakeAuthService extends Fake implements AuthService {
@override
Future<AuthResponse> signIn(
@Body() SignInRequest body,
) async {
await Future.delayed(const Duration(seconds: 5));
final response = FakeData.apiAndResponse[keySignIn]!;
if (response.statusCode != 200) {
throw generateDioError(response.statusCode);
}
return AuthResponse.fromJson(response.json);
}
}
17 changes: 17 additions & 0 deletions integration_test/fake_data/fake_services/fake_user_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:survey_flutter_ic/api/response/profile_response.dart';
import 'package:survey_flutter_ic/api/service/user_service.dart';

import '../fake_data.dart';

class FakeUserService extends Fake implements UserService {
@override
Future<ProfileResponse> getProfile() async {
await Future.delayed(const Duration(seconds: 5));
final response = FakeData.apiAndResponse[keyUserProfile]!;
if (response.statusCode != 200) {
throw generateDioError(response.statusCode);
}
return ProfileResponse.fromJson(response.json);
}
}
35 changes: 35 additions & 0 deletions integration_test/home_screen_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:survey_flutter_ic/ui/home/home_screen.dart';
import 'package:survey_flutter_ic/ui/home/home_widget_id.dart';

import 'utils/test_util.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
homeScreenTest();
}

void homeScreenTest() {
group('Home Page', () {
late Finder profileAvatar;

setUpAll(() async {
await TestUtil.setupTestEnvironment();
});

setUp(() {
profileAvatar = find.byKey(HomeWidgetId.profileAvatarImage);
});

testWidgets(
"When the home screen shown, it displays the Home screen correctly",
(WidgetTester tester) async {
await tester
.pumpWidget(TestUtil.pumpWidgetWithShellApp(const HomeScreen()));
await tester.pumpAndSettle();

expect(profileAvatar, findsOneWidget);
});
});
}
20 changes: 0 additions & 20 deletions integration_test/my_home_page_test.dart

This file was deleted.

57 changes: 57 additions & 0 deletions integration_test/sign_in_screen_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:survey_flutter_ic/navigation/route.dart';
import 'package:survey_flutter_ic/ui/signin/sign_in_widget_id.dart';

import 'fake_data/fake_data.dart';
import 'utils/test_util.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
signInTest();
}

void signInTest() {
group('SignIn Page', () {
late Finder emailField;
late Finder passwordField;
late Finder signInButton;

setUpAll(() async {
await TestUtil.setupTestEnvironment();
});

setUp(() {
emailField = find.byKey(SignInWidgetId.emailInputField);
passwordField = find.byKey(SignInWidgetId.passwordInputField);
signInButton = find.byKey(SignInWidgetId.signInButton);
});

testWidgets(
"When the sign in screen shown, it displays the Sign In screen correctly",
(WidgetTester tester) async {
await tester
.pumpWidget(TestUtil.pumpWidgetWithRoutePath(routePathSignInScreen));

expect(emailField, findsOneWidget);
expect(passwordField, findsOneWidget);
expect(signInButton, findsOneWidget);
});

testWidgets(
"When sign in with valid email and password, it navigate to Home screen",
(WidgetTester tester) async {
await FakeData.initDefault();
await tester
.pumpWidget(TestUtil.pumpWidgetWithRoutePath(routePathSignInScreen));
await tester.enterText(emailField, '[email protected]');
await tester.enterText(passwordField, '1111111');
await tester.tap(signInButton);
await tester.pump(const Duration(milliseconds: 200));

// TODO: Skip this text, this require refactor to handle the route
// https://guillaume.bernos.dev/testing-go-router/
// expect(find.byKey(const Key(routePathHomeScreen)), findsOneWidget);
});
});
}
11 changes: 11 additions & 0 deletions integration_test/utils/file_util.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'dart:convert';

import 'package:flutter/services.dart' show rootBundle;

class FileUtil {
FileUtil._();

static Future<Map<String, dynamic>> loadFile(String filePath) {
return rootBundle.loadString(filePath).then((json) => jsonDecode(json));
}
}
39 changes: 34 additions & 5 deletions integration_test/utils/test_util.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import 'package:flutter/material.dart';
import 'package:flutter_config/flutter_config.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:survey_flutter_ic/main.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:survey_flutter_ic/api/service/auth_service.dart';
import 'package:survey_flutter_ic/di/provider/di.dart';
import 'package:survey_flutter_ic/main.dart';
import 'package:survey_flutter_ic/navigation/route.dart';

import '../fake_data/fake_services/fake_auth_service.dart';

class TestUtil {
/// This is useful when we test the whole app with the real configs(styling,
/// localization, routes, etc)
static Widget pumpWidgetWithRealApp(String initialRoute) {
_initDependencies();
return MyApp();
return const MyApp();
}

/// We normally use this function to test a specific [widget] without
Expand All @@ -19,18 +25,41 @@ class TestUtil {
return MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: widget,
home: ProviderScope(child: widget),
);
}

static Widget pumpWidgetWithRoutePath(String route) {
_initDependencies();

return ProviderScope(
child: MaterialApp.router(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
routeInformationProvider: router(route).routeInformationProvider,
routeInformationParser: router(route).routeInformationParser,
routerDelegate: router(route).routerDelegate,
));
}

static void _initDependencies() {
PackageInfo.setMockInitialValues(
appName: 'Flutter templates testing',
appName: 'Flutter IC testing',
packageName: '',
version: '',
buildNumber: '',
buildSignature: '',
installerStore: '');
FlutterConfig.loadValueForTesting({'SECRET': 'This is only for testing'});
FlutterConfig.loadValueForTesting({'CLIENT_SECRET': 'CLIENT_SECRET'});
FlutterConfig.loadValueForTesting({'CLIENT_ID': 'CLIENT_ID'});
FlutterConfig.loadValueForTesting(
{'REST_API_ENDPOINT': 'REST_API_ENDPOINT'});
}

static Future setupTestEnvironment() async {
_initDependencies();
getIt.allowReassignment = true;
getIt.registerSingleton<AuthService>(FakeAuthService());
configureInjection();
}
}
2 changes: 1 addition & 1 deletion ios/Gemfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
source "https://rubygems.org"

gem "fastlane"
gem "fastlane", "= 2.210.1"
gem "cocoapods"
2 changes: 1 addition & 1 deletion ios/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ GEM
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.6)
fastlane (2.212.1)
fastlane (2.210.1)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
Expand Down
Loading

0 comments on commit 78a0ebe

Please sign in to comment.