瀏覽代碼

fix: windows integration test (#2917)

* fix:windows integration test

* fix: load asset

* fix: windows test

* fix: test

* test: refactor the folder test

---------

Co-authored-by: vedon <[email protected]>
Co-authored-by: Lucas.Xu <[email protected]>
Nathan.fooo 1 年之前
父節點
當前提交
f0d5f51703
共有 37 個文件被更改,包括 299 次插入563 次删除
  1. 1 1
      .github/workflows/integration_test.yml
  2. 0 15
      frontend/appflowy_flutter/integration_test/database_calendar_test.dart
  3. 0 15
      frontend/appflowy_flutter/integration_test/database_cell_test.dart
  4. 0 15
      frontend/appflowy_flutter/integration_test/database_field_test.dart
  5. 0 16
      frontend/appflowy_flutter/integration_test/database_filter_test.dart
  6. 0 15
      frontend/appflowy_flutter/integration_test/database_row_page_test.dart
  7. 0 15
      frontend/appflowy_flutter/integration_test/database_row_test.dart
  8. 0 15
      frontend/appflowy_flutter/integration_test/database_setting_test.dart
  9. 1 52
      frontend/appflowy_flutter/integration_test/database_share_test.dart
  10. 0 16
      frontend/appflowy_flutter/integration_test/database_sort_test.dart
  11. 0 15
      frontend/appflowy_flutter/integration_test/database_view_test.dart
  12. 0 15
      frontend/appflowy_flutter/integration_test/document/cover_image_test.dart
  13. 0 15
      frontend/appflowy_flutter/integration_test/document/document_test.dart
  14. 0 11
      frontend/appflowy_flutter/integration_test/document/document_with_database_test.dart
  15. 0 11
      frontend/appflowy_flutter/integration_test/document/document_with_inline_page_test.dart
  16. 0 11
      frontend/appflowy_flutter/integration_test/document/edit_document_test.dart
  17. 11 23
      frontend/appflowy_flutter/integration_test/import_files_test.dart
  18. 0 10
      frontend/appflowy_flutter/integration_test/language_test.dart
  19. 0 11
      frontend/appflowy_flutter/integration_test/plugins/outline_block_test.dart
  20. 12 21
      frontend/appflowy_flutter/integration_test/share_markdown_test.dart
  21. 27 36
      frontend/appflowy_flutter/integration_test/switch_folder_test.dart
  22. 48 52
      frontend/appflowy_flutter/integration_test/util/base.dart
  23. 0 5
      frontend/appflowy_flutter/integration_test/util/common_operations.dart
  24. 16 9
      frontend/appflowy_flutter/integration_test/util/database_test_op.dart
  25. 16 21
      frontend/appflowy_flutter/integration_test/util/mock/mock_file_picker.dart
  26. 1 1
      frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/custom_cover_picker_bloc.dart
  27. 19 10
      frontend/appflowy_flutter/lib/startup/deps_resolver.dart
  28. 31 21
      frontend/appflowy_flutter/lib/startup/startup.dart
  29. 1 0
      frontend/appflowy_flutter/lib/startup/tasks/localization.dart
  30. 3 3
      frontend/appflowy_flutter/lib/startup/tasks/rust_sdk.dart
  31. 1 1
      frontend/appflowy_flutter/lib/startup/tasks/windows.dart
  32. 1 1
      frontend/appflowy_flutter/lib/user/presentation/folder/folder_widget.dart
  33. 1 1
      frontend/appflowy_flutter/lib/workspace/application/appearance.dart
  34. 105 0
      frontend/appflowy_flutter/lib/workspace/application/settings/application_data_storage.dart
  35. 1 0
      frontend/appflowy_flutter/lib/workspace/application/settings/prelude.dart
  36. 2 83
      frontend/appflowy_flutter/lib/workspace/application/settings/settings_location_cubit.dart
  37. 1 1
      frontend/appflowy_flutter/test/util.dart

+ 1 - 1
.github/workflows/integration_test.yml

@@ -33,7 +33,7 @@ jobs:
     if: github.event.pull_request.draft != true
     if: github.event.pull_request.draft != true
     strategy:
     strategy:
       matrix:
       matrix:
-        os: [ubuntu-latest]
+        os: [ubuntu-latest, windows-latest]
 
 
     runs-on: ${{ matrix.os }}
     runs-on: ${{ matrix.os }}
 
 

+ 0 - 15
frontend/appflowy_flutter/integration_test/database_calendar_test.dart

@@ -10,21 +10,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('database', () {
   group('database', () {
-    const location = 'appflowy';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('update calendar layout', (tester) async {
     testWidgets('update calendar layout', (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();

+ 0 - 15
frontend/appflowy_flutter/integration_test/database_cell_test.dart

@@ -11,21 +11,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('grid cell', () {
   group('grid cell', () {
-    const location = 'appflowy';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('edit text cell', (tester) async {
     testWidgets('edit text cell', (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();

+ 0 - 15
frontend/appflowy_flutter/integration_test/database_field_test.dart

@@ -10,21 +10,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('grid page', () {
   group('grid page', () {
-    const location = 'appflowy';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('rename existing field', (tester) async {
     testWidgets('rename existing field', (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();

+ 0 - 16
frontend/appflowy_flutter/integration_test/database_filter_test.dart

@@ -5,27 +5,11 @@ import 'package:flutter_test/flutter_test.dart';
 import 'package:integration_test/integration_test.dart';
 import 'package:integration_test/integration_test.dart';
 
 
 import 'util/database_test_op.dart';
 import 'util/database_test_op.dart';
-import 'util/util.dart';
 
 
 void main() {
 void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('grid', () {
   group('grid', () {
-    const location = 'import_files';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('add text filter', (tester) async {
     testWidgets('add text filter', (tester) async {
       await tester.openV020database();
       await tester.openV020database();
 
 

+ 0 - 15
frontend/appflowy_flutter/integration_test/database_row_page_test.dart

@@ -14,21 +14,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('grid', () {
   group('grid', () {
-    const location = 'appflowy';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('row details page opens', (tester) async {
     testWidgets('row details page opens', (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();

+ 0 - 15
frontend/appflowy_flutter/integration_test/database_row_test.dart

@@ -8,21 +8,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('grid', () {
   group('grid', () {
-    const location = 'appflowy';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('create row of the grid', (tester) async {
     testWidgets('create row of the grid', (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();

+ 0 - 15
frontend/appflowy_flutter/integration_test/database_setting_test.dart

@@ -9,21 +9,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('grid', () {
   group('grid', () {
-    const location = 'appflowy';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('update layout', (tester) async {
     testWidgets('update layout', (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();

+ 1 - 52
frontend/appflowy_flutter/integration_test/database_share_test.dart

@@ -1,66 +1,15 @@
-import 'dart:io';
-
 import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
 import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
-import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:integration_test/integration_test.dart';
 import 'package:integration_test/integration_test.dart';
 
 
 import 'util/database_test_op.dart';
 import 'util/database_test_op.dart';
-import 'util/mock/mock_file_picker.dart';
-import 'util/util.dart';
-import 'package:path/path.dart' as p;
 
 
 void main() {
 void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('database', () {
   group('database', () {
-    const location = 'import_files';
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('import v0.2.0 database data', (tester) async {
     testWidgets('import v0.2.0 database data', (tester) async {
-      await tester.initializeAppFlowy();
-      await tester.tapGoButton();
-
-      // expect to see a readme page
-      tester.expectToSeePageName(readme);
-
-      await tester.tapAddButton();
-      await tester.tapImportButton();
-
-      final testFileNames = ['v020.afdb'];
-      final fileLocation = await tester.currentFileLocation();
-      for (final fileName in testFileNames) {
-        final str = await rootBundle.loadString(
-          p.join(
-            'assets/test/workspaces/database',
-            fileName,
-          ),
-        );
-        File(p.join(fileLocation, fileName)).writeAsStringSync(str);
-      }
-      // mock get files
-      await mockPickFilePaths(testFileNames, name: location);
-      await tester.tapDatabaseRawDataButton();
-      await tester.openPage('v020');
-
-      // check the import content
-      // await tester.assertCellContent(
-      //   rowIndex: 7,
-      //   fieldType: FieldType.RichText,
-      //   // fieldName: 'Name',
-      //   content: '',
-      // );
+      await tester.openV020database();
 
 
       // check the text cell
       // check the text cell
       final textCells = <String>['A', 'B', 'C', 'D', 'E', '', '', '', '', ''];
       final textCells = <String>['A', 'B', 'C', 'D', 'E', '', '', '', '', ''];

+ 0 - 16
frontend/appflowy_flutter/integration_test/database_sort_test.dart

@@ -3,27 +3,11 @@ import 'package:flutter_test/flutter_test.dart';
 import 'package:integration_test/integration_test.dart';
 import 'package:integration_test/integration_test.dart';
 
 
 import 'util/database_test_op.dart';
 import 'util/database_test_op.dart';
-import 'util/util.dart';
 
 
 void main() {
 void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('grid', () {
   group('grid', () {
-    const location = 'import_files';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('add text sort', (tester) async {
     testWidgets('add text sort', (tester) async {
       await tester.openV020database();
       await tester.openV020database();
       // create a filter
       // create a filter

+ 0 - 15
frontend/appflowy_flutter/integration_test/database_view_test.dart

@@ -11,21 +11,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('database', () {
   group('database', () {
-    const location = 'appflowy';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('create linked view', (tester) async {
     testWidgets('create linked view', (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();

+ 0 - 15
frontend/appflowy_flutter/integration_test/document/cover_image_test.dart

@@ -9,21 +9,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('cover image', () {
   group('cover image', () {
-    const location = 'cover_image';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('document cover tests', (tester) async {
     testWidgets('document cover tests', (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();

+ 0 - 15
frontend/appflowy_flutter/integration_test/document/document_test.dart

@@ -10,21 +10,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('document', () {
   group('document', () {
-    const location = 'appflowy';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('create a new document when launching app in first time',
     testWidgets('create a new document when launching app in first time',
         (tester) async {
         (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();

+ 0 - 11
frontend/appflowy_flutter/integration_test/document/document_with_database_test.dart

@@ -14,17 +14,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('database view in document', () {
   group('database view in document', () {
-    const location = 'database_view';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('insert a referenced grid', (tester) async {
     testWidgets('insert a referenced grid', (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();

+ 0 - 11
frontend/appflowy_flutter/integration_test/document/document_with_inline_page_test.dart

@@ -11,17 +11,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('inline page view in document', () {
   group('inline page view in document', () {
-    const location = 'inline_page';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('insert a inline page - grid', (tester) async {
     testWidgets('insert a inline page - grid', (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();

+ 0 - 11
frontend/appflowy_flutter/integration_test/document/edit_document_test.dart

@@ -13,17 +13,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('edit document', () {
   group('edit document', () {
-    const location = 'appflowy';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('redo & undo', (tester) async {
     testWidgets('redo & undo', (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();

+ 11 - 23
frontend/appflowy_flutter/integration_test/import_files_test.dart

@@ -12,23 +12,8 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('import files', () {
   group('import files', () {
-    const location = 'import_files';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('import multiple markdown files', (tester) async {
     testWidgets('import multiple markdown files', (tester) async {
-      await tester.initializeAppFlowy();
+      final context = await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();
 
 
       // expect to see a readme page
       // expect to see a readme page
@@ -38,18 +23,21 @@ void main() {
       await tester.tapImportButton();
       await tester.tapImportButton();
 
 
       final testFileNames = ['test1.md', 'test2.md'];
       final testFileNames = ['test1.md', 'test2.md'];
-      final fileLocation = await tester.currentFileLocation();
+      final paths = <String>[];
       for (final fileName in testFileNames) {
       for (final fileName in testFileNames) {
         final str = await rootBundle.loadString(
         final str = await rootBundle.loadString(
-          p.join(
-            'assets/test/workspaces/markdowns',
-            fileName,
-          ),
+          'assets/test/workspaces/markdowns/$fileName',
         );
         );
-        File(p.join(fileLocation, fileName)).writeAsStringSync(str);
+        final path = p.join(context.applicationDataDirectory, fileName);
+        paths.add(path);
+        File(path).writeAsStringSync(str);
       }
       }
       // mock get files
       // mock get files
-      await mockPickFilePaths(testFileNames, name: location);
+      await mockPickFilePaths(
+        paths: testFileNames
+            .map((e) => p.join(context.applicationDataDirectory, e))
+            .toList(),
+      );
 
 
       await tester.tapTextAndMarkdownButton();
       await tester.tapTextAndMarkdownButton();
 
 

+ 0 - 10
frontend/appflowy_flutter/integration_test/language_test.dart

@@ -8,16 +8,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('document', () {
   group('document', () {
-    const location = 'appflowy';
-
-    setUpAll(() async {
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets(
     testWidgets(
         'change the language successfully when launching the app for the first time',
         'change the language successfully when launching the app for the first time',
         (tester) async {
         (tester) async {

+ 0 - 11
frontend/appflowy_flutter/integration_test/plugins/outline_block_test.dart

@@ -12,17 +12,6 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('outline block test', () {
   group('outline block test', () {
-    const location = 'outline_test';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('insert an outline block', (tester) async {
     testWidgets('insert an outline block', (tester) async {
       await tester.initializeAppFlowy();
       await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();

+ 12 - 21
frontend/appflowy_flutter/integration_test/share_markdown_test.dart

@@ -3,7 +3,7 @@ import 'dart:io';
 import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
 import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:integration_test/integration_test.dart';
 import 'package:integration_test/integration_test.dart';
-
+import 'package:path/path.dart' as p;
 import 'util/mock/mock_file_picker.dart';
 import 'util/mock/mock_file_picker.dart';
 import 'util/util.dart';
 import 'util/util.dart';
 
 
@@ -11,30 +11,17 @@ void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('share markdown in document page', () {
   group('share markdown in document page', () {
-    const location = 'markdown';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('click the share button in document page', (tester) async {
     testWidgets('click the share button in document page', (tester) async {
-      await tester.initializeAppFlowy();
+      final context = await tester.initializeAppFlowy();
       await tester.tapGoButton();
       await tester.tapGoButton();
 
 
       // expect to see a readme page
       // expect to see a readme page
       tester.expectToSeePageName(readme);
       tester.expectToSeePageName(readme);
 
 
       // mock the file picker
       // mock the file picker
-      final path = await mockSaveFilePath(location, 'test.md');
+      final path = await mockSaveFilePath(
+        p.join(context.applicationDataDirectory, 'test.md'),
+      );
       // click the share button and select markdown
       // click the share button and select markdown
       await tester.tapShareButton();
       await tester.tapShareButton();
       await tester.tapMarkdownButton();
       await tester.tapMarkdownButton();
@@ -52,7 +39,7 @@ void main() {
     testWidgets(
     testWidgets(
       'share the markdown after renaming the document name',
       'share the markdown after renaming the document name',
       (tester) async {
       (tester) async {
-        await tester.initializeAppFlowy();
+        final context = await tester.initializeAppFlowy();
         await tester.tapGoButton();
         await tester.tapGoButton();
 
 
         // expect to see a readme page
         // expect to see a readme page
@@ -65,8 +52,12 @@ void main() {
         final shareButton = find.byType(ShareActionList);
         final shareButton = find.byType(ShareActionList);
         final shareButtonState =
         final shareButtonState =
             tester.state(shareButton) as ShareActionListState;
             tester.state(shareButton) as ShareActionListState;
-        final path =
-            await mockSaveFilePath(location, '${shareButtonState.name}.md');
+        final path = await mockSaveFilePath(
+          p.join(
+            context.applicationDataDirectory,
+            '${shareButtonState.name}.md',
+          ),
+        );
 
 
         // click the share button and select markdown
         // click the share button and select markdown
         await tester.tapShareButton();
         await tester.tapShareButton();

+ 27 - 36
frontend/appflowy_flutter/integration_test/switch_folder_test.dart

@@ -1,48 +1,36 @@
+import 'package:appflowy/startup/startup.dart';
+import 'package:appflowy/startup/tasks/prelude.dart';
 import 'package:appflowy/workspace/application/settings/prelude.dart';
 import 'package:appflowy/workspace/application/settings/prelude.dart';
-import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
-import 'package:flowy_infra/uuid.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:integration_test/integration_test.dart';
 import 'package:integration_test/integration_test.dart';
-
+import 'package:path/path.dart' as p;
 import 'util/mock/mock_file_picker.dart';
 import 'util/mock/mock_file_picker.dart';
 import 'util/util.dart';
 import 'util/util.dart';
-import 'package:path/path.dart' as p;
 
 
 void main() {
 void main() {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
 
   group('customize the folder path', () {
   group('customize the folder path', () {
-    const location = 'appflowy';
-
-    setUp(() async {
-      await TestFolder.cleanTestLocation(location);
-      await TestFolder.setTestLocation(location);
-    });
-
-    tearDown(() async {
-      await TestFolder.cleanTestLocation(location);
-    });
-
-    tearDownAll(() async {
-      await TestFolder.cleanTestLocation(null);
-    });
-
     testWidgets('switch to B from A, then switch to A again', (tester) async {
     testWidgets('switch to B from A, then switch to A again', (tester) async {
-      final userA = uuid();
-      final userB = uuid();
+      const userA = 'UserA';
+      const userB = 'UserB';
 
 
-      await TestFolder.cleanTestLocation(userA);
-      await TestFolder.cleanTestLocation(userB);
-      await TestFolder.setTestLocation(p.join(userA, appFlowyDataFolder));
-
-      await tester.initializeAppFlowy();
+      final initialPath = p.join(userA, appFlowyDataFolder);
+      final context = await tester.initializeAppFlowy(
+        pathExtension: initialPath,
+      );
+      // remove the last extension
+      final rootPath = context.applicationDataDirectory.replaceFirst(
+        initialPath,
+        '',
+      );
 
 
       await tester.tapGoButton();
       await tester.tapGoButton();
       tester.expectToSeeHomePage();
       tester.expectToSeeHomePage();
 
 
       // switch to user B
       // switch to user B
       {
       {
-        // set user name to userA
+        // set user name for userA
         await tester.openSettings();
         await tester.openSettings();
         await tester.openSettingsPage(SettingsPage.user);
         await tester.openSettingsPage(SettingsPage.user);
         await tester.enterUserName(userA);
         await tester.enterUserName(userA);
@@ -51,12 +39,14 @@ void main() {
         await tester.pumpAndSettle();
         await tester.pumpAndSettle();
 
 
         // mock the file_picker result
         // mock the file_picker result
-        await mockGetDirectoryPath(userB);
+        await mockGetDirectoryPath(
+          p.join(rootPath, userB),
+        );
         await tester.tapCustomLocationButton();
         await tester.tapCustomLocationButton();
         await tester.pumpAndSettle();
         await tester.pumpAndSettle();
         tester.expectToSeeHomePage();
         tester.expectToSeeHomePage();
 
 
-        // set user name to userB
+        // set user name for userB
         await tester.openSettings();
         await tester.openSettings();
         await tester.openSettingsPage(SettingsPage.user);
         await tester.openSettingsPage(SettingsPage.user);
         await tester.enterUserName(userB);
         await tester.enterUserName(userB);
@@ -68,7 +58,9 @@ void main() {
         await tester.pumpAndSettle();
         await tester.pumpAndSettle();
 
 
         // mock the file_picker result
         // mock the file_picker result
-        await mockGetDirectoryPath(userA);
+        await mockGetDirectoryPath(
+          p.join(rootPath, userA),
+        );
         await tester.tapCustomLocationButton();
         await tester.tapCustomLocationButton();
 
 
         await tester.pumpAndSettle();
         await tester.pumpAndSettle();
@@ -83,16 +75,15 @@ void main() {
         await tester.pumpAndSettle();
         await tester.pumpAndSettle();
 
 
         // mock the file_picker result
         // mock the file_picker result
-        await mockGetDirectoryPath(userB);
+        await mockGetDirectoryPath(
+          p.join(rootPath, userB),
+        );
         await tester.tapCustomLocationButton();
         await tester.tapCustomLocationButton();
 
 
         await tester.pumpAndSettle();
         await tester.pumpAndSettle();
         tester.expectToSeeHomePage();
         tester.expectToSeeHomePage();
         tester.expectToSeeUserName(userB);
         tester.expectToSeeUserName(userB);
       }
       }
-
-      await TestFolder.cleanTestLocation(userA);
-      await TestFolder.cleanTestLocation(userB);
     });
     });
 
 
     testWidgets('reset to default location', (tester) async {
     testWidgets('reset to default location', (tester) async {
@@ -109,8 +100,8 @@ void main() {
       await tester.restoreLocation();
       await tester.restoreLocation();
 
 
       expect(
       expect(
-        await TestFolder.defaultDevelopmentLocation(),
-        await TestFolder.currentLocation(),
+        await appFlowyApplicationDataDirectory().then((value) => value.path),
+        await getIt<ApplicationDataStorage>().getPath(),
       );
       );
     });
     });
   });
   });

+ 48 - 52
frontend/appflowy_flutter/integration_test/util/base.dart

@@ -1,81 +1,77 @@
 import 'dart:io';
 import 'dart:io';
 
 
-import 'package:appflowy/core/config/kv_keys.dart';
 import 'package:appflowy/startup/entry_point.dart';
 import 'package:appflowy/startup/entry_point.dart';
 import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy/startup/startup.dart';
-import 'package:appflowy/startup/tasks/prelude.dart';
+import 'package:appflowy/workspace/application/settings/prelude.dart';
+import 'package:flowy_infra/uuid.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/gestures.dart';
 import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:path_provider/path_provider.dart';
-import 'package:shared_preferences/shared_preferences.dart';
-
-class TestFolder {
-  /// Location / Path
-
-  /// Set a given AppFlowy data storage location under test environment.
-  ///
-  /// To pass null means clear the location.
-  ///
-  /// The file_picker is a system component and can't be tapped, so using logic instead of tapping.
-  ///
-  static Future<void> setTestLocation(String? name) async {
-    final location = await testLocation(name);
-    SharedPreferences.setMockInitialValues({
-      KVKeys.pathLocation: location.path,
-    });
-    return;
-  }
 
 
-  /// Clean the location.
-  static Future<void> cleanTestLocation(String? name) async {
-    final dir = await testLocation(name);
-    await dir.delete(recursive: true);
-    return;
-  }
+class FlowyTestContext {
+  FlowyTestContext({
+    required this.applicationDataDirectory,
+  });
 
 
-  /// Get current using location.
-  static Future<String> currentLocation() async {
-    final prefs = await SharedPreferences.getInstance();
-    return prefs.getString(KVKeys.pathLocation)!;
-  }
+  final String applicationDataDirectory;
+}
 
 
-  /// Get default location under development environment.
-  static Future<String> defaultDevelopmentLocation() async {
-    final dir = await appFlowyApplicationDataDirectory();
-    return dir.path;
-  }
+extension AppFlowyTestBase on WidgetTester {
+  Future<FlowyTestContext> initializeAppFlowy({
+    // use to append after the application data directory
+    String? pathExtension,
+  }) async {
+    mockHotKeyManagerHandlers();
+    final directory = await mockApplicationDataStorage(
+      pathExtension: pathExtension,
+    );
 
 
-  /// Get default location under test environment.
-  static Future<Directory> testLocation(String? name) async {
-    final dir = await getApplicationDocumentsDirectory();
-    var path = '${dir.path}/flowy_test';
-    if (name != null) {
-      path += '/$name';
-    }
-    return Directory(path).create(recursive: true);
+    WidgetsFlutterBinding.ensureInitialized();
+    await FlowyRunner.run(
+      FlowyApp(),
+      IntegrationMode.integrationTest,
+    );
+
+    await wait(3000);
+    await pumpAndSettle(const Duration(seconds: 2));
+    return FlowyTestContext(
+      applicationDataDirectory: directory,
+    );
   }
   }
-}
 
 
-extension AppFlowyTestBase on WidgetTester {
-  Future<void> initializeAppFlowy() async {
+  void mockHotKeyManagerHandlers() {
     TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
     TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
         .setMockMethodCallHandler(const MethodChannel('hotkey_manager'),
         .setMockMethodCallHandler(const MethodChannel('hotkey_manager'),
             (MethodCall methodCall) async {
             (MethodCall methodCall) async {
       if (methodCall.method == 'unregisterAll') {
       if (methodCall.method == 'unregisterAll') {
         // do nothing
         // do nothing
       }
       }
-
       return;
       return;
     });
     });
+  }
 
 
-    WidgetsFlutterBinding.ensureInitialized();
-    await FlowyRunner.run(FlowyApp(), IntegrationMode.integrationTest);
+  Future<String> mockApplicationDataStorage({
+    // use to append after the application data directory
+    String? pathExtension,
+  }) async {
+    final dir = await getTemporaryDirectory();
 
 
-    await wait(3000);
-    await pumpAndSettle(const Duration(seconds: 2));
+    // Use a random uuid to avoid conflict.
+    String path = '${dir.path}/appflowy_integration_test/${uuid()}';
+    if (pathExtension != null && pathExtension.isNotEmpty) {
+      path = '$path/$pathExtension';
+    }
+    final directory = Directory(path);
+    if (!directory.existsSync()) {
+      await directory.create(recursive: true);
+    }
+
+    MockApplicationDataStorage.initialPath = directory.path;
+
+    return directory.path;
   }
   }
 
 
   Future<void> tapButton(
   Future<void> tapButton(

+ 0 - 5
frontend/appflowy_flutter/integration_test/util/common_operations.dart

@@ -17,11 +17,6 @@ import 'package:flutter_test/flutter_test.dart';
 import 'util.dart';
 import 'util.dart';
 
 
 extension CommonOperations on WidgetTester {
 extension CommonOperations on WidgetTester {
-  /// Get current file location of AppFlowy.
-  Future<String> currentFileLocation() async {
-    return TestFolder.currentLocation();
-  }
-
   /// Tap the GetStart button on the launch page.
   /// Tap the GetStart button on the launch page.
   Future<void> tapGoButton() async {
   Future<void> tapGoButton() async {
     final goButton = find.byType(GoButton);
     final goButton = find.byType(GoButton);

+ 16 - 9
frontend/appflowy_flutter/integration_test/util/database_test_op.dart

@@ -73,7 +73,7 @@ import 'mock/mock_file_picker.dart';
 
 
 extension AppFlowyDatabaseTest on WidgetTester {
 extension AppFlowyDatabaseTest on WidgetTester {
   Future<void> openV020database() async {
   Future<void> openV020database() async {
-    await initializeAppFlowy();
+    final context = await initializeAppFlowy();
     await tapGoButton();
     await tapGoButton();
 
 
     // expect to see a readme page
     // expect to see a readme page
@@ -83,18 +83,25 @@ extension AppFlowyDatabaseTest on WidgetTester {
     await tapImportButton();
     await tapImportButton();
 
 
     final testFileNames = ['v020.afdb'];
     final testFileNames = ['v020.afdb'];
-    final fileLocation = await currentFileLocation();
+    final paths = <String>[];
     for (final fileName in testFileNames) {
     for (final fileName in testFileNames) {
-      final str = await rootBundle.loadString(
-        p.join(
-          'assets/test/workspaces/database',
-          fileName,
-        ),
+      // Don't use the p.join to build the path that used in loadString. It
+      // is not working on windows.
+      final str = await rootBundle
+          .loadString("assets/test/workspaces/database/$fileName");
+
+      // Write the content to the file.
+      final path = p.join(
+        context.applicationDataDirectory,
+        fileName,
       );
       );
-      File(p.join(fileLocation, fileName)).writeAsStringSync(str);
+      paths.add(path);
+      File(path).writeAsStringSync(str);
     }
     }
     // mock get files
     // mock get files
-    await mockPickFilePaths(testFileNames, name: 'import_files');
+    await mockPickFilePaths(
+      paths: paths,
+    );
     await tapDatabaseRawDataButton();
     await tapDatabaseRawDataButton();
     await openPage('v020');
     await openPage('v020');
   }
   }

+ 16 - 21
frontend/appflowy_flutter/integration_test/util/mock/mock_file_picker.dart

@@ -1,10 +1,6 @@
-import 'dart:io';
-
 import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy/util/file_picker/file_picker_service.dart';
 import 'package:appflowy/util/file_picker/file_picker_service.dart';
 import 'package:file_picker/file_picker.dart' as fp;
 import 'package:file_picker/file_picker.dart' as fp;
-import 'package:path/path.dart' as p;
-import '../util.dart';
 
 
 class MockFilePicker implements FilePickerService {
 class MockFilePicker implements FilePickerService {
   MockFilePicker({
   MockFilePicker({
@@ -56,20 +52,21 @@ class MockFilePicker implements FilePickerService {
   }
   }
 }
 }
 
 
-Future<void> mockGetDirectoryPath(String? name) async {
-  final dir = await TestFolder.testLocation(name);
+Future<void> mockGetDirectoryPath(
+  String path,
+) async {
   getIt.unregister<FilePickerService>();
   getIt.unregister<FilePickerService>();
   getIt.registerFactory<FilePickerService>(
   getIt.registerFactory<FilePickerService>(
     () => MockFilePicker(
     () => MockFilePicker(
-      mockPath: dir.path,
+      mockPath: path,
     ),
     ),
   );
   );
   return;
   return;
 }
 }
 
 
-Future<String> mockSaveFilePath(String? name, String fileName) async {
-  final dir = await TestFolder.testLocation(name);
-  final path = p.join(dir.path, fileName);
+Future<String> mockSaveFilePath(
+  String path,
+) async {
   getIt.unregister<FilePickerService>();
   getIt.unregister<FilePickerService>();
   getIt.registerFactory<FilePickerService>(
   getIt.registerFactory<FilePickerService>(
     () => MockFilePicker(
     () => MockFilePicker(
@@ -79,18 +76,16 @@ Future<String> mockSaveFilePath(String? name, String fileName) async {
   return path;
   return path;
 }
 }
 
 
-Future<List<String>> mockPickFilePaths(
-  List<String> fileNames, {
-  String? name,
-  String? customPath,
+Future<List<String>> mockPickFilePaths({
+  required List<String> paths,
 }) async {
 }) async {
-  late final Directory dir;
-  if (customPath != null) {
-    dir = Directory(customPath);
-  } else {
-    dir = await TestFolder.testLocation(name);
-  }
-  final paths = fileNames.map((e) => p.join(dir.path, e)).toList();
+  // late final Directory dir;
+  // if (customPath != null) {
+  //   dir = Directory(customPath);
+  // } else {
+  //   dir = await TestFolder.testLocation(applicationDataPath, name);
+  // }
+  // final paths = fileNames.map((e) => p.join(dir.path, e)).toList();
   getIt.unregister<FilePickerService>();
   getIt.unregister<FilePickerService>();
   getIt.registerFactory<FilePickerService>(
   getIt.registerFactory<FilePickerService>(
     () => MockFilePicker(
     () => MockFilePicker(

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/custom_cover_picker_bloc.dart

@@ -2,7 +2,7 @@ import 'dart:io';
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy/util/file_picker/file_picker_service.dart';
 import 'package:appflowy/util/file_picker/file_picker_service.dart';
-import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
+import 'package:appflowy/workspace/application/settings/prelude.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:file_picker/file_picker.dart' as fp;
 import 'package:file_picker/file_picker.dart' as fp;
 
 

+ 19 - 10
frontend/appflowy_flutter/lib/startup/deps_resolver.dart

@@ -6,6 +6,7 @@ import 'package:appflowy/plugins/database_view/application/field/field_service.d
 import 'package:appflowy/plugins/database_view/application/setting/property_bloc.dart';
 import 'package:appflowy/plugins/database_view/application/setting/property_bloc.dart';
 import 'package:appflowy/plugins/database_view/grid/application/grid_header_bloc.dart';
 import 'package:appflowy/plugins/database_view/grid/application/grid_header_bloc.dart';
 import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
 import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
+import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy/user/application/auth/auth_service.dart';
 import 'package:appflowy/user/application/auth/auth_service.dart';
 import 'package:appflowy/user/application/auth/supabase_auth_service.dart';
 import 'package:appflowy/user/application/auth/supabase_auth_service.dart';
 import 'package:appflowy/user/application/user_listener.dart';
 import 'package:appflowy/user/application/user_listener.dart';
@@ -13,7 +14,6 @@ import 'package:appflowy/user/application/user_service.dart';
 import 'package:appflowy/util/file_picker/file_picker_impl.dart';
 import 'package:appflowy/util/file_picker/file_picker_impl.dart';
 import 'package:appflowy/util/file_picker/file_picker_service.dart';
 import 'package:appflowy/util/file_picker/file_picker_service.dart';
 import 'package:appflowy/plugins/document/application/prelude.dart';
 import 'package:appflowy/plugins/document/application/prelude.dart';
-import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
 import 'package:appflowy/workspace/application/user/prelude.dart';
 import 'package:appflowy/workspace/application/user/prelude.dart';
 import 'package:appflowy/workspace/application/workspace/prelude.dart';
 import 'package:appflowy/workspace/application/workspace/prelude.dart';
 import 'package:appflowy/workspace/application/edit_panel/edit_panel_bloc.dart';
 import 'package:appflowy/workspace/application/edit_panel/edit_panel_bloc.dart';
@@ -32,26 +32,35 @@ import 'package:get_it/get_it.dart';
 import 'package:http/http.dart' as http;
 import 'package:http/http.dart' as http;
 
 
 class DependencyResolver {
 class DependencyResolver {
-  static Future<void> resolve(GetIt getIt) async {
+  static Future<void> resolve(
+    GetIt getIt,
+    IntegrationMode mode,
+  ) async {
     _resolveUserDeps(getIt);
     _resolveUserDeps(getIt);
-
     _resolveHomeDeps(getIt);
     _resolveHomeDeps(getIt);
-
     _resolveFolderDeps(getIt);
     _resolveFolderDeps(getIt);
-
     _resolveDocDeps(getIt);
     _resolveDocDeps(getIt);
-
     _resolveGridDeps(getIt);
     _resolveGridDeps(getIt);
-
-    _resolveCommonService(getIt);
+    _resolveCommonService(getIt, mode);
   }
   }
 }
 }
 
 
-void _resolveCommonService(GetIt getIt) async {
+void _resolveCommonService(
+  GetIt getIt,
+  IntegrationMode mode,
+) async {
   // getIt.registerFactory<KeyValueStorage>(() => RustKeyValue());
   // getIt.registerFactory<KeyValueStorage>(() => RustKeyValue());
   getIt.registerFactory<KeyValueStorage>(() => DartKeyValue());
   getIt.registerFactory<KeyValueStorage>(() => DartKeyValue());
   getIt.registerFactory<FilePickerService>(() => FilePicker());
   getIt.registerFactory<FilePickerService>(() => FilePicker());
-  getIt.registerFactory<ApplicationDataStorage>(() => ApplicationDataStorage());
+  if (mode.isTest) {
+    getIt.registerFactory<ApplicationDataStorage>(
+      () => MockApplicationDataStorage(),
+    );
+  } else {
+    getIt.registerFactory<ApplicationDataStorage>(
+      () => ApplicationDataStorage(),
+    );
+  }
 
 
   getIt.registerFactoryAsync<OpenAIRepository>(
   getIt.registerFactoryAsync<OpenAIRepository>(
     () async {
     () async {

+ 31 - 21
frontend/appflowy_flutter/lib/startup/startup.dart

@@ -1,7 +1,7 @@
 import 'dart:io';
 import 'dart:io';
 
 
 import 'package:appflowy/env/env.dart';
 import 'package:appflowy/env/env.dart';
-import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
+import 'package:appflowy/workspace/application/settings/prelude.dart';
 import 'package:appflowy_backend/appflowy_backend.dart';
 import 'package:appflowy_backend/appflowy_backend.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
@@ -18,8 +18,14 @@ abstract class EntryPoint {
   Widget create(LaunchConfiguration config);
   Widget create(LaunchConfiguration config);
 }
 }
 
 
+class FlowyRunnerContext {
+  final Directory applicationDataDirectory;
+
+  FlowyRunnerContext({required this.applicationDataDirectory});
+}
+
 class FlowyRunner {
 class FlowyRunner {
-  static Future<void> run(
+  static Future<FlowyRunnerContext> run(
     EntryPoint f,
     EntryPoint f,
     IntegrationMode mode, {
     IntegrationMode mode, {
     LaunchConfiguration config = const LaunchConfiguration(
     LaunchConfiguration config = const LaunchConfiguration(
@@ -32,11 +38,10 @@ class FlowyRunner {
     // Specify the env
     // Specify the env
     initGetIt(getIt, mode, f, config);
     initGetIt(getIt, mode, f, config);
 
 
-    final directory = await getIt<ApplicationDataStorage>()
-        .getPath()
-        .then((value) => Directory(value));
-
-    // final directory = await appFlowyDocumentDirectory();
+    final applicationDataDirectory =
+        await getIt<ApplicationDataStorage>().getPath().then(
+              (value) => Directory(value),
+            );
 
 
     // add task
     // add task
     final launcher = getIt<AppLauncher>();
     final launcher = getIt<AppLauncher>();
@@ -49,13 +54,13 @@ class FlowyRunner {
         // init the app window
         // init the app window
         const InitAppWindowTask(),
         const InitAppWindowTask(),
         // Init Rust SDK
         // Init Rust SDK
-        InitRustSDKTask(directory: directory),
+        InitRustSDKTask(directory: applicationDataDirectory),
         // Load Plugins, like document, grid ...
         // Load Plugins, like document, grid ...
         const PluginLoadTask(),
         const PluginLoadTask(),
 
 
         // init the app widget
         // init the app widget
         // ignore in test mode
         // ignore in test mode
-        if (!mode.isTest()) ...[
+        if (!mode.isUnitTest) ...[
           const HotKeyTask(),
           const HotKeyTask(),
           InitSupabaseTask(
           InitSupabaseTask(
             url: Env.supabaseUrl,
             url: Env.supabaseUrl,
@@ -70,6 +75,10 @@ class FlowyRunner {
       ],
       ],
     );
     );
     await launcher.launch(); // execute the tasks
     await launcher.launch(); // execute the tasks
+
+    return FlowyRunnerContext(
+      applicationDataDirectory: applicationDataDirectory,
+    );
   }
   }
 }
 }
 
 
@@ -94,7 +103,7 @@ Future<void> initGetIt(
   );
   );
   getIt.registerSingleton<PluginSandbox>(PluginSandbox());
   getIt.registerSingleton<PluginSandbox>(PluginSandbox());
 
 
-  await DependencyResolver.resolve(getIt);
+  await DependencyResolver.resolve(getIt, env);
 }
 }
 
 
 class LaunchContext {
 class LaunchContext {
@@ -145,23 +154,24 @@ class AppLauncher {
 enum IntegrationMode {
 enum IntegrationMode {
   develop,
   develop,
   release,
   release,
-  test,
-  integrationTest,
-}
+  unitTest,
+  integrationTest;
 
 
-extension IntegrationEnvExt on IntegrationMode {
-  bool isTest() {
-    return this == IntegrationMode.test;
-  }
+  // test mode
+  bool get isTest => isUnitTest || isIntegrationTest;
+  bool get isUnitTest => this == IntegrationMode.unitTest;
+  bool get isIntegrationTest => this == IntegrationMode.integrationTest;
 
 
-  bool isIntegrationTest() {
-    return this == IntegrationMode.integrationTest;
-  }
+  // release mode
+  bool get isRelease => this == IntegrationMode.release;
+
+  // develop mode
+  bool get isDevelop => this == IntegrationMode.develop;
 }
 }
 
 
 IntegrationMode integrationEnv() {
 IntegrationMode integrationEnv() {
   if (Platform.environment.containsKey('FLUTTER_TEST')) {
   if (Platform.environment.containsKey('FLUTTER_TEST')) {
-    return IntegrationMode.test;
+    return IntegrationMode.unitTest;
   }
   }
 
 
   if (kReleaseMode) {
   if (kReleaseMode) {

+ 1 - 0
frontend/appflowy_flutter/lib/startup/tasks/localization.dart

@@ -8,5 +8,6 @@ class InitLocalizationTask extends LaunchTask {
   @override
   @override
   Future<void> initialize(LaunchContext context) async {
   Future<void> initialize(LaunchContext context) async {
     await EasyLocalization.ensureInitialized();
     await EasyLocalization.ensureInitialized();
+    EasyLocalization.logger.enableBuildModes = [];
   }
   }
 }
 }

+ 3 - 3
frontend/appflowy_flutter/lib/startup/tasks/rust_sdk.dart

@@ -38,7 +38,7 @@ AppFlowyEnv getAppFlowyEnv() {
   final collabTableConfig =
   final collabTableConfig =
       CollabTableConfig(enable: true, table_name: Env.supabaseCollabTable);
       CollabTableConfig(enable: true, table_name: Env.supabaseCollabTable);
 
 
-  final supbaseDBConfig = SupabaseDBConfig(
+  final supabaseDBConfig = SupabaseDBConfig(
     url: Env.supabaseUrl,
     url: Env.supabaseUrl,
     key: Env.supabaseKey,
     key: Env.supabaseKey,
     jwt_secret: Env.supabaseJwtSecret,
     jwt_secret: Env.supabaseJwtSecret,
@@ -47,7 +47,7 @@ AppFlowyEnv getAppFlowyEnv() {
 
 
   return AppFlowyEnv(
   return AppFlowyEnv(
     supabase_config: supabaseConfig,
     supabase_config: supabaseConfig,
-    supabase_db_config: supbaseDBConfig,
+    supabase_db_config: supabaseDBConfig,
   );
   );
 }
 }
 
 
@@ -62,7 +62,7 @@ Future<Directory> appFlowyApplicationDataDirectory() async {
     case IntegrationMode.release:
     case IntegrationMode.release:
       final Directory documentsDir = await getApplicationSupportDirectory();
       final Directory documentsDir = await getApplicationSupportDirectory();
       return Directory(path.join(documentsDir.path, 'data')).create();
       return Directory(path.join(documentsDir.path, 'data')).create();
-    case IntegrationMode.test:
+    case IntegrationMode.unitTest:
     case IntegrationMode.integrationTest:
     case IntegrationMode.integrationTest:
       return Directory(path.join(Directory.current.path, '.sandbox'));
       return Directory(path.join(Directory.current.path, '.sandbox'));
   }
   }

+ 1 - 1
frontend/appflowy_flutter/lib/startup/tasks/windows.dart

@@ -27,7 +27,7 @@ class InitAppWindowTask extends LaunchTask with WindowListener {
     windowManager.addListener(this);
     windowManager.addListener(this);
 
 
     Size windowSize = await WindowSizeManager().getSize();
     Size windowSize = await WindowSizeManager().getSize();
-    if (context.env.isIntegrationTest()) {
+    if (context.env.isIntegrationTest) {
       windowSize = const Size(1600, 1200);
       windowSize = const Size(1600, 1200);
     }
     }
 
 

+ 1 - 1
frontend/appflowy_flutter/lib/user/presentation/folder/folder_widget.dart

@@ -1,6 +1,7 @@
 import 'dart:io';
 import 'dart:io';
 
 
 import 'package:appflowy/util/file_picker/file_picker_service.dart';
 import 'package:appflowy/util/file_picker/file_picker_service.dart';
+import 'package:appflowy/workspace/application/settings/prelude.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/size.dart';
@@ -12,7 +13,6 @@ import 'package:google_fonts/google_fonts.dart';
 
 
 import '../../../generated/locale_keys.g.dart';
 import '../../../generated/locale_keys.g.dart';
 import '../../../startup/startup.dart';
 import '../../../startup/startup.dart';
-import '../../../workspace/application/settings/settings_location_cubit.dart';
 import '../../../workspace/presentation/home/toast.dart';
 import '../../../workspace/presentation/home/toast.dart';
 
 
 enum _FolderPage {
 enum _FolderPage {

+ 1 - 1
frontend/appflowy_flutter/lib/workspace/application/appearance.dart

@@ -53,7 +53,7 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
   /// changed. Fallback to [en] locale if [newLocale] is not supported.
   /// changed. Fallback to [en] locale if [newLocale] is not supported.
   void setLocale(BuildContext context, Locale newLocale) {
   void setLocale(BuildContext context, Locale newLocale) {
     if (!context.supportedLocales.contains(newLocale)) {
     if (!context.supportedLocales.contains(newLocale)) {
-      Log.warn("Unsupported locale: $newLocale, Fallback to locale: en");
+      // Log.warn("Unsupported locale: $newLocale, Fallback to locale: en");
       newLocale = const Locale('en');
       newLocale = const Locale('en');
     }
     }
 
 

+ 105 - 0
frontend/appflowy_flutter/lib/workspace/application/settings/application_data_storage.dart

@@ -0,0 +1,105 @@
+import 'dart:io';
+
+import 'package:appflowy/core/config/kv.dart';
+import 'package:appflowy/core/config/kv_keys.dart';
+import 'package:appflowy/startup/startup.dart';
+import 'package:appflowy_backend/log.dart';
+import 'package:flutter/foundation.dart';
+import 'package:path/path.dart' as p;
+
+import '../../../startup/tasks/prelude.dart';
+
+const appFlowyDataFolder = "AppFlowyDataDoNotRename";
+
+class ApplicationDataStorage {
+  ApplicationDataStorage();
+  String? _cachePath;
+
+  /// Set the custom path to store the data.
+  /// If the path is not exists, the path will be created.
+  /// If the path is invalid, the path will be set to the default path.
+  Future<void> setCustomPath(String path) async {
+    if (kIsWeb || Platform.isAndroid || Platform.isIOS) {
+      Log.info('LocalFileStorage is not supported on this platform.');
+      return;
+    }
+
+    if (Platform.isMacOS) {
+      // remove the prefix `/Volumes/*`
+      path = path.replaceFirst(RegExp(r'^/Volumes/[^/]+'), '');
+    } else if (Platform.isWindows) {
+      path = path.replaceAll('/', '\\');
+    }
+
+    // If the path is not ends with `AppFlowyData`, we will append the
+    // `AppFlowyData` to the path. If the path is ends with `AppFlowyData`,
+    // which means the path is the custom path.
+    if (p.basename(path) != appFlowyDataFolder) {
+      path = p.join(path, appFlowyDataFolder);
+    }
+
+    // create the directory if not exists.
+    final directory = Directory(path);
+    if (!directory.existsSync()) {
+      await directory.create(recursive: true);
+    }
+
+    setPath(path);
+  }
+
+  Future<void> setPath(String path) async {
+    if (kIsWeb || Platform.isAndroid || Platform.isIOS) {
+      Log.info('LocalFileStorage is not supported on this platform.');
+      return;
+    }
+
+    await getIt<KeyValueStorage>().set(KVKeys.pathLocation, path);
+    // clear the cache path, and not set the cache path to the new path because the set path may be invalid
+    _cachePath = null;
+  }
+
+  Future<String> getPath() async {
+    if (_cachePath != null) {
+      return _cachePath!;
+    }
+
+    final response = await getIt<KeyValueStorage>().get(KVKeys.pathLocation);
+    String path = await response.fold(
+      (error) async {
+        // return the default path if the path is not set
+        final directory = await appFlowyApplicationDataDirectory();
+        return directory.path;
+      },
+      (path) => path,
+    );
+    _cachePath = path;
+
+    // if the path is not exists means the path is invalid, so we should clear the kv store
+    if (!Directory(path).existsSync()) {
+      await getIt<KeyValueStorage>().clear();
+      final directory = await appFlowyApplicationDataDirectory();
+      path = directory.path;
+    }
+
+    return path;
+  }
+}
+
+class MockApplicationDataStorage extends ApplicationDataStorage {
+  MockApplicationDataStorage();
+
+  // this value will be clear after setup
+  // only for the initial step
+  @visibleForTesting
+  static String? initialPath;
+
+  @override
+  Future<String> getPath() async {
+    final path = initialPath;
+    if (path != null) {
+      initialPath = null;
+      return Future.value(path);
+    }
+    return super.getPath();
+  }
+}

+ 1 - 0
frontend/appflowy_flutter/lib/workspace/application/settings/prelude.dart

@@ -1 +1,2 @@
 export 'settings_dialog_bloc.dart';
 export 'settings_dialog_bloc.dart';
+export 'application_data_storage.dart';

+ 2 - 83
frontend/appflowy_flutter/lib/workspace/application/settings/settings_location_cubit.dart

@@ -1,13 +1,8 @@
-import 'dart:io';
-
-import 'package:appflowy/core/config/kv.dart';
-import 'package:appflowy/core/config/kv_keys.dart';
 import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy/startup/startup.dart';
-import 'package:appflowy_backend/log.dart';
+import 'package:appflowy/workspace/application/settings/application_data_storage.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
-import 'package:path/path.dart' as p;
 
 
 import '../../../startup/tasks/prelude.dart';
 import '../../../startup/tasks/prelude.dart';
 
 
@@ -27,7 +22,7 @@ class SettingsLocationCubit extends Cubit<SettingsLocationState> {
 
 
   Future<void> resetDataStoragePathToApplicationDefault() async {
   Future<void> resetDataStoragePathToApplicationDefault() async {
     final directory = await appFlowyApplicationDataDirectory();
     final directory = await appFlowyApplicationDataDirectory();
-    await getIt<ApplicationDataStorage>()._setPath(directory.path);
+    await getIt<ApplicationDataStorage>().setPath(directory.path);
     emit(SettingsLocationState.didReceivedPath(directory.path));
     emit(SettingsLocationState.didReceivedPath(directory.path));
   }
   }
 
 
@@ -41,79 +36,3 @@ class SettingsLocationCubit extends Cubit<SettingsLocationState> {
     emit(SettingsLocationState.didReceivedPath(path));
     emit(SettingsLocationState.didReceivedPath(path));
   }
   }
 }
 }
-
-const appFlowyDataFolder = "AppFlowyDataDoNotRename";
-
-class ApplicationDataStorage {
-  ApplicationDataStorage();
-  String? _cachePath;
-
-  /// Set the custom path to store the data.
-  /// If the path is not exists, the path will be created.
-  /// If the path is invalid, the path will be set to the default path.
-  Future<void> setCustomPath(String path) async {
-    if (kIsWeb || Platform.isAndroid || Platform.isIOS) {
-      Log.info('LocalFileStorage is not supported on this platform.');
-      return;
-    }
-
-    if (Platform.isMacOS) {
-      // remove the prefix `/Volumes/*`
-      path = path.replaceFirst(RegExp(r'^/Volumes/[^/]+'), '');
-    } else if (Platform.isWindows) {
-      path = path.replaceAll('/', '\\');
-    }
-
-    // If the path is not ends with `AppFlowyData`, we will append the
-    // `AppFlowyData` to the path. If the path is ends with `AppFlowyData`,
-    // which means the path is the custom path.
-    if (p.basename(path) != appFlowyDataFolder) {
-      path = p.join(path, appFlowyDataFolder);
-    }
-
-    // create the directory if not exists.
-    final directory = Directory(path);
-    if (!directory.existsSync()) {
-      await directory.create(recursive: true);
-    }
-
-    _setPath(path);
-  }
-
-  Future<void> _setPath(String path) async {
-    if (kIsWeb || Platform.isAndroid || Platform.isIOS) {
-      Log.info('LocalFileStorage is not supported on this platform.');
-      return;
-    }
-
-    await getIt<KeyValueStorage>().set(KVKeys.pathLocation, path);
-    // clear the cache path, and not set the cache path to the new path because the set path may be invalid
-    _cachePath = null;
-  }
-
-  Future<String> getPath() async {
-    if (_cachePath != null) {
-      return _cachePath!;
-    }
-
-    final response = await getIt<KeyValueStorage>().get(KVKeys.pathLocation);
-    String path = await response.fold(
-      (error) async {
-        // return the default path if the path is not set
-        final directory = await appFlowyApplicationDataDirectory();
-        return directory.path;
-      },
-      (path) => path,
-    );
-    _cachePath = path;
-
-    // if the path is not exists means the path is invalid, so we should clear the kv store
-    if (!Directory(path).existsSync()) {
-      await getIt<KeyValueStorage>().clear();
-      final directory = await appFlowyApplicationDataDirectory();
-      path = directory.path;
-    }
-
-    return path;
-  }
-}

+ 1 - 1
frontend/appflowy_flutter/test/util.dart

@@ -36,7 +36,7 @@ class AppFlowyUnitTest {
 
 
     await FlowyRunner.run(
     await FlowyRunner.run(
       FlowyTestApp(),
       FlowyTestApp(),
-      IntegrationMode.test,
+      IntegrationMode.unitTest,
     );
     );
 
 
     final test = AppFlowyUnitTest();
     final test = AppFlowyUnitTest();