TF-2953 Set up Patrol tests
This commit is contained in:
@@ -120,3 +120,4 @@ app.*.symbols
|
|||||||
*.g.dart
|
*.g.dart
|
||||||
messages_*.dart
|
messages_*.dart
|
||||||
*.mocks.dart
|
*.mocks.dart
|
||||||
|
integration_test/test_bundle.dart
|
||||||
@@ -23,7 +23,7 @@ if (flutterVersionName == null) {
|
|||||||
|
|
||||||
def flutterMinSdkVersion = localProperties.getProperty('flutter.minSdkVersion')
|
def flutterMinSdkVersion = localProperties.getProperty('flutter.minSdkVersion')
|
||||||
if (flutterMinSdkVersion == null) {
|
if (flutterMinSdkVersion == null) {
|
||||||
flutterMinSdkVersion = '19'
|
flutterMinSdkVersion = '21'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
@@ -54,6 +54,8 @@ android {
|
|||||||
manifestPlaceholders = [
|
manifestPlaceholders = [
|
||||||
'appAuthRedirectScheme': 'teammail.mobile'
|
'appAuthRedirectScheme': 'teammail.mobile'
|
||||||
]
|
]
|
||||||
|
testInstrumentationRunner "pl.leancode.patrol.PatrolJUnitRunner"
|
||||||
|
testInstrumentationRunnerArguments clearPackageData: "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@@ -77,7 +79,15 @@ android {
|
|||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
signingConfig signingConfigs.release
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
|
debug {
|
||||||
|
minifyEnabled false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
execution "ANDROIDX_TEST_ORCHESTRATOR"
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
flutter {
|
flutter {
|
||||||
@@ -90,4 +100,5 @@ dependencies {
|
|||||||
implementation 'androidx.work:work-runtime-ktx:2.7.0'
|
implementation 'androidx.work:work-runtime-ktx:2.7.0'
|
||||||
implementation 'com.android.support:multidex:1.0.3'
|
implementation 'com.android.support:multidex:1.0.3'
|
||||||
implementation 'androidx.window:window:1.0.0'
|
implementation 'androidx.window:window:1.0.0'
|
||||||
|
androidTestUtil "androidx.test:orchestrator:1.5.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.linagora.android.tmail;
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
import pl.leancode.patrol.PatrolJUnitRunner;
|
||||||
|
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
|
public class MainActivityTest {
|
||||||
|
@Parameters(name = "{0}")
|
||||||
|
public static Object[] testCases() {
|
||||||
|
PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation();
|
||||||
|
// replace "MainActivity.class" with "io.flutter.embedding.android.FlutterActivity.class"
|
||||||
|
// if your AndroidManifest is using: android:name="io.flutter.embedding.android.FlutterActivity"
|
||||||
|
instrumentation.setUp(MainActivity.class);
|
||||||
|
instrumentation.waitForPatrolAppService();
|
||||||
|
return instrumentation.listDartTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MainActivityTest(String dartTestName) {
|
||||||
|
this.dartTestName = dartTestName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String dartTestName;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void runDartTest() {
|
||||||
|
PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation();
|
||||||
|
instrumentation.runDartTest(dartTestName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# 52. Patrol integration test
|
||||||
|
|
||||||
|
Date: 2024-04-10
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
- A need for integration testing for Twake Mail mobile arised.
|
||||||
|
- The testing tool must be able to handle native UI and webview.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
- Patrol was chosen to write and test Twake Mail.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Developers are now able to integration test Twake Mail
|
||||||
|
- Set up:
|
||||||
|
- Run `dart pub global activate patrol_cli` to enable Patrol CLI
|
||||||
|
- Install ngrok and jq
|
||||||
|
- Open docker and Android emulator/connect Android device
|
||||||
|
- Remember to use `dart-define` neccessary for each test command
|
||||||
|
- Test individual test locally by edit `scripts/patrol-local-integration-test-with-docker.sh`
|
||||||
|
- Replace `patrol test -v` with `patrol test -v -t path/to/test/file`
|
||||||
|
- Run the `scripts/patrol-local-integration-test-with-docker.sh`
|
||||||
|
- Test every tests locally by running `scripts/patrol-local-integration-test-with-docker.sh` script
|
||||||
|
- Read more about Patrol in [Patrol homepage](https://patrol.leancode.co/)
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
- Backend docker container is initiated before Patrol tests run, and close after all tests have run. This lead to no data isolation between tests
|
||||||
|
- Patrol gives no way of accessing Docker from system, due to when it runs, it bundle all tests into a single apk, and apk cannot access the system's terminal
|
||||||
|
- Tried https://github.com/testcontainers/testcontainers-java but also failed, with the same reason as Patrol.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import 'package:patrol/patrol.dart';
|
||||||
|
|
||||||
|
abstract class BaseScenario {
|
||||||
|
final PatrolIntegrationTester $;
|
||||||
|
|
||||||
|
const BaseScenario(this.$);
|
||||||
|
|
||||||
|
Future<void> execute();
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:patrol/patrol.dart';
|
||||||
|
|
||||||
|
abstract class CoreRobot {
|
||||||
|
final PatrolIntegrationTester $;
|
||||||
|
|
||||||
|
CoreRobot(this.$);
|
||||||
|
|
||||||
|
Future<void> ensureViewVisible(PatrolFinder patrolFinder) async {
|
||||||
|
await $.waitUntilVisible(patrolFinder);
|
||||||
|
expect(patrolFinder, findsWidgets);
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic ignoreException() => $.tester.takeException();
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:patrol/patrol.dart';
|
||||||
|
import 'package:tmail_ui_user/main.dart' as app;
|
||||||
|
|
||||||
|
class TestBase {
|
||||||
|
void runPatrolTest({
|
||||||
|
required String description,
|
||||||
|
required Function(PatrolIntegrationTester $) test,
|
||||||
|
}) {
|
||||||
|
patrolTest(
|
||||||
|
description,
|
||||||
|
config: const PatrolTesterConfig(
|
||||||
|
settlePolicy: SettlePolicy.trySettle,
|
||||||
|
visibleTimeout: Duration(minutes: 1)),
|
||||||
|
nativeAutomatorConfig: const NativeAutomatorConfig(
|
||||||
|
findTimeout: Duration(seconds: 10),
|
||||||
|
),
|
||||||
|
framePolicy: LiveTestWidgetsFlutterBindingFramePolicy.benchmarkLive,
|
||||||
|
($) async {
|
||||||
|
await app.runTmail();
|
||||||
|
// https://github.com/leancodepl/patrol/issues/1602#issuecomment-1665317814
|
||||||
|
final originalOnError = FlutterError.onError!;
|
||||||
|
FlutterError.onError = (FlutterErrorDetails details) {
|
||||||
|
originalOnError(details);
|
||||||
|
};
|
||||||
|
|
||||||
|
await test($);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:model/email/prefix_email_address.dart';
|
||||||
|
import 'package:rich_text_composer/rich_text_composer.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/composer_view.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/view/mobile/mobile_editor_view.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/app_bar_composer_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_suggestion_item_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/subject_composer_widget.dart';
|
||||||
|
|
||||||
|
import '../base/core_robot.dart';
|
||||||
|
|
||||||
|
class ComposerRobot extends CoreRobot {
|
||||||
|
ComposerRobot(super.$);
|
||||||
|
|
||||||
|
Future<void> addRecipient(String email) async {
|
||||||
|
await $(RecipientComposerWidget)
|
||||||
|
.which<RecipientComposerWidget>((widget) => widget.prefix == PrefixEmailAddress.to)
|
||||||
|
.enterText(email);
|
||||||
|
await $(RecipientSuggestionItemWidget)
|
||||||
|
.which<RecipientSuggestionItemWidget>((widget) => widget.emailAddress.email?.contains(email) ?? false)
|
||||||
|
.tap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addSubject(String subject) async {
|
||||||
|
await $(SubjectComposerWidget).enterText(subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addContent(String content) async {
|
||||||
|
ComposerController? composerController;
|
||||||
|
await $(ComposerView)
|
||||||
|
.which<ComposerView>((widget) {
|
||||||
|
composerController = widget.controller;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.$(MobileEditorView).$(HtmlEditor).$(InAppWebView).tap();
|
||||||
|
|
||||||
|
await composerController?.htmlEditorApi?.requestFocusLastChild();
|
||||||
|
|
||||||
|
await composerController!.htmlEditorApi!.insertHtml('$content <br><br>');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> sendEmail() async {
|
||||||
|
await $(AppBarComposerWidget)
|
||||||
|
.$(TMailButtonWidget)
|
||||||
|
.which<TMailButtonWidget>((widget) => widget.icon == ImagePaths().icSendMobile)
|
||||||
|
.tap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> expectSendEmailSuccessToast() async {
|
||||||
|
expect($('Message has been sent successfully'), findsOneWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> grantContactPermission() async {
|
||||||
|
if (await $.native.isPermissionDialogVisible(timeout: const Duration(seconds: 5))) {
|
||||||
|
await $.native.grantPermissionWhenInUse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import 'package:core/presentation/views/text/type_ahead_form_field_builder.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/presentation/login_view.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/presentation/widgets/login_text_input_builder.dart';
|
||||||
|
|
||||||
|
import '../base/core_robot.dart';
|
||||||
|
|
||||||
|
class LoginRobot extends CoreRobot {
|
||||||
|
LoginRobot(super.$);
|
||||||
|
|
||||||
|
Future<void> expectLoginViewVisible() => ensureViewVisible($(LoginView));
|
||||||
|
|
||||||
|
Future<void> enterEmail(String email) async {
|
||||||
|
final finder = $(LoginView).$(TextField);
|
||||||
|
await finder.enterText(email);
|
||||||
|
await $('Next').tap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> enterHostUrl(String url) async {
|
||||||
|
final finder = $(LoginView).$(TextField);
|
||||||
|
await finder.enterText(url);
|
||||||
|
await $('Next').tap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> enterBasicAuthEmail(String email) async {
|
||||||
|
await $(LoginView)
|
||||||
|
.$(TypeAheadFormFieldBuilder<RecentLoginUsername>)
|
||||||
|
.$(TextField)
|
||||||
|
.enterText(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> enterBasicAuthPassword(String password) async {
|
||||||
|
await $(LoginView)
|
||||||
|
.$(LoginTextInputBuilder)
|
||||||
|
.$(TextField)
|
||||||
|
.enterText(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loginBasicAuth() async {
|
||||||
|
await $(Container).$(ElevatedButton).tap();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/compose_floating_button.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/composer_view.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart';
|
||||||
|
|
||||||
|
import '../base/core_robot.dart';
|
||||||
|
|
||||||
|
class ThreadRobot extends CoreRobot {
|
||||||
|
ThreadRobot(super.$);
|
||||||
|
|
||||||
|
Future<void> expectThreadViewVisible() => ensureViewVisible($(ThreadView));
|
||||||
|
|
||||||
|
Future<void> openComposer() async {
|
||||||
|
await $(ComposeFloatingButton).$(InkWell).tap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> expectComposerViewVisible() => ensureViewVisible($(ComposerView));
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import '../base/base_scenario.dart';
|
||||||
|
import '../robots/login_robot.dart';
|
||||||
|
import '../robots/thread_robot.dart';
|
||||||
|
import '../utils/scenario_utils_mixin.dart';
|
||||||
|
|
||||||
|
class LoginWithBasicAuthScenario extends BaseScenario with ScenarioUtilsMixin {
|
||||||
|
const LoginWithBasicAuthScenario(
|
||||||
|
super.$,
|
||||||
|
{
|
||||||
|
required this.username,
|
||||||
|
required this.hostUrl,
|
||||||
|
required this.email,
|
||||||
|
required this.password,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
final String username;
|
||||||
|
final String hostUrl;
|
||||||
|
final String email;
|
||||||
|
final String password;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> execute() async {
|
||||||
|
final loginRobot = LoginRobot($);
|
||||||
|
final threadRobot = ThreadRobot($);
|
||||||
|
|
||||||
|
await loginRobot.expectLoginViewVisible();
|
||||||
|
await loginRobot.enterEmail(username);
|
||||||
|
await loginRobot.enterHostUrl(hostUrl);
|
||||||
|
|
||||||
|
await loginRobot.enterBasicAuthEmail(email);
|
||||||
|
await loginRobot.enterBasicAuthPassword(password);
|
||||||
|
await loginRobot.loginBasicAuth();
|
||||||
|
|
||||||
|
await grantNotificationPermission($.native);
|
||||||
|
|
||||||
|
await threadRobot.expectThreadViewVisible();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import '../base/base_scenario.dart';
|
||||||
|
import '../robots/composer_robot.dart';
|
||||||
|
import '../robots/thread_robot.dart';
|
||||||
|
import 'login_with_basic_auth_scenario.dart';
|
||||||
|
|
||||||
|
class SendEmailScenario extends BaseScenario {
|
||||||
|
const SendEmailScenario(
|
||||||
|
super.$,
|
||||||
|
{
|
||||||
|
required this.loginWithBasicAuthScenario,
|
||||||
|
required this.additionalRecipient,
|
||||||
|
required this.subject,
|
||||||
|
required this.content
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
final LoginWithBasicAuthScenario loginWithBasicAuthScenario;
|
||||||
|
final String additionalRecipient;
|
||||||
|
final String subject;
|
||||||
|
final String content;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> execute() async {
|
||||||
|
final threadRobot = ThreadRobot($);
|
||||||
|
final composerRobot = ComposerRobot($);
|
||||||
|
|
||||||
|
await loginWithBasicAuthScenario.execute();
|
||||||
|
|
||||||
|
await threadRobot.openComposer();
|
||||||
|
await threadRobot.expectComposerViewVisible();
|
||||||
|
|
||||||
|
await composerRobot.grantContactPermission();
|
||||||
|
|
||||||
|
await composerRobot.addRecipient(loginWithBasicAuthScenario.email);
|
||||||
|
await composerRobot.addRecipient(additionalRecipient);
|
||||||
|
await composerRobot.addSubject(subject);
|
||||||
|
await composerRobot.addContent(content);
|
||||||
|
await composerRobot.sendEmail();
|
||||||
|
await composerRobot.expectSendEmailSuccessToast();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import '../../base/test_base.dart';
|
||||||
|
import '../../scenarios/login_with_basic_auth_scenario.dart';
|
||||||
|
import '../../scenarios/send_email_scenario.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestBase().runPatrolTest(
|
||||||
|
description: 'Should see success toast when send email successfully',
|
||||||
|
test: ($) async {
|
||||||
|
final loginWithBasicAuthScenario = LoginWithBasicAuthScenario($,
|
||||||
|
username: const String.fromEnvironment('USERNAME'),
|
||||||
|
hostUrl: const String.fromEnvironment('BASIC_AUTH_URL'),
|
||||||
|
email: const String.fromEnvironment('BASIC_AUTH_EMAIL'),
|
||||||
|
password: const String.fromEnvironment('PASSWORD'),
|
||||||
|
);
|
||||||
|
final sendEmailScenario = SendEmailScenario($,
|
||||||
|
loginWithBasicAuthScenario: loginWithBasicAuthScenario,
|
||||||
|
additionalRecipient: const String.fromEnvironment('ADDITIONAL_MAIL_RECIPIENT'),
|
||||||
|
subject: 'Test subject',
|
||||||
|
content: 'Test content');
|
||||||
|
|
||||||
|
await sendEmailScenario.execute();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import '../../base/test_base.dart';
|
||||||
|
import '../../scenarios/login_with_basic_auth_scenario.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestBase().runPatrolTest(
|
||||||
|
description: 'Should see thread view when login with basic auth successfully',
|
||||||
|
test: ($) async {
|
||||||
|
final loginWithBasicAuthScenario = LoginWithBasicAuthScenario($,
|
||||||
|
username: const String.fromEnvironment('USERNAME'),
|
||||||
|
hostUrl: const String.fromEnvironment('BASIC_AUTH_URL'),
|
||||||
|
email: const String.fromEnvironment('BASIC_AUTH_EMAIL'),
|
||||||
|
password: const String.fromEnvironment('PASSWORD'),
|
||||||
|
);
|
||||||
|
|
||||||
|
await loginWithBasicAuthScenario.execute();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import 'package:patrol/patrol.dart';
|
||||||
|
|
||||||
|
mixin ScenarioUtilsMixin {
|
||||||
|
Future<void> grantNotificationPermission(NativeAutomator nativeAutomator) async {
|
||||||
|
if (await nativeAutomator.isPermissionDialogVisible(timeout: const Duration(seconds: 5))) {
|
||||||
|
await nativeAutomator.grantPermissionWhenInUse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-14
@@ -15,25 +15,29 @@ import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
|||||||
import 'package:url_strategy/url_strategy.dart';
|
import 'package:url_strategy/url_strategy.dart';
|
||||||
import 'package:worker_manager/worker_manager.dart';
|
import 'package:worker_manager/worker_manager.dart';
|
||||||
|
|
||||||
void main() async {
|
Future<void> main() async {
|
||||||
initLogger(() async {
|
initLogger(() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
ThemeUtils.setSystemLightUIStyle();
|
await runTmail();
|
||||||
|
|
||||||
await Future.wait([
|
|
||||||
MainBindings().dependencies(),
|
|
||||||
HiveCacheConfig.instance.setUp(),
|
|
||||||
Executor().warmUp(log: BuildUtils.isDebugMode),
|
|
||||||
AppUtils.loadEnvFile()
|
|
||||||
]);
|
|
||||||
await HiveCacheConfig.instance.initializeEncryptionKey();
|
|
||||||
|
|
||||||
setPathUrlStrategy();
|
|
||||||
|
|
||||||
runApp(const TMailApp());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> runTmail() async {
|
||||||
|
ThemeUtils.setSystemLightUIStyle();
|
||||||
|
|
||||||
|
await Future.wait([
|
||||||
|
MainBindings().dependencies(),
|
||||||
|
HiveCacheConfig.instance.setUp(),
|
||||||
|
Executor().warmUp(log: BuildUtils.isDebugMode),
|
||||||
|
AppUtils.loadEnvFile()
|
||||||
|
]);
|
||||||
|
await HiveCacheConfig.instance.initializeEncryptionKey();
|
||||||
|
|
||||||
|
setPathUrlStrategy();
|
||||||
|
|
||||||
|
runApp(const TMailApp());
|
||||||
|
}
|
||||||
|
|
||||||
class TMailApp extends StatelessWidget {
|
class TMailApp extends StatelessWidget {
|
||||||
const TMailApp({Key? key}) : super(key: key);
|
const TMailApp({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
|||||||
+19
-3
@@ -1252,10 +1252,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
|
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.0"
|
version: "4.8.1"
|
||||||
json_serializable:
|
json_serializable:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -1480,6 +1480,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.1"
|
||||||
|
patrol:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: patrol
|
||||||
|
sha256: ef07b0022f6eabee77655a3cde2364ff57cf22c29018d524476e972a5476724f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.11.1"
|
||||||
|
patrol_finders:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: patrol_finders
|
||||||
|
sha256: "6bf2c3093fbccd02f80f73fafc1bd021d76410cbab6e329be220b5e3bc58f072"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
pattern_formatter:
|
pattern_formatter:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -2201,4 +2217,4 @@ packages:
|
|||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.3.0 <4.0.0"
|
dart: ">=3.3.0 <4.0.0"
|
||||||
flutter: ">=3.20.0-7.0.pre.48"
|
flutter: ">=3.22.0"
|
||||||
|
|||||||
@@ -270,6 +270,8 @@ dev_dependencies:
|
|||||||
|
|
||||||
http_mock_adapter: 0.4.2
|
http_mock_adapter: 0.4.2
|
||||||
|
|
||||||
|
patrol: 3.11.1
|
||||||
|
|
||||||
plugin_platform_interface: 2.1.8
|
plugin_platform_interface: 2.1.8
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
@@ -282,6 +284,8 @@ dependency_overrides:
|
|||||||
url: https://github.com/linagora/flutter_file_picker
|
url: https://github.com/linagora/flutter_file_picker
|
||||||
ref: email_supported_5.3.1
|
ref: email_supported_5.3.1
|
||||||
|
|
||||||
|
json_annotation: 4.8.1
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
@@ -355,3 +359,8 @@ flutter_native_splash:
|
|||||||
cider:
|
cider:
|
||||||
link_template:
|
link_template:
|
||||||
tag: https://github.com/linagora/tmail-flutter/releases/tag/v%tag% # initial release link template
|
tag: https://github.com/linagora/tmail-flutter/releases/tag/v%tag% # initial release link template
|
||||||
|
|
||||||
|
patrol:
|
||||||
|
app_name: Twake Mail
|
||||||
|
android:
|
||||||
|
package_name: com.linagora.android.teammail
|
||||||
|
|||||||
Reference in New Issue
Block a user