mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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 <vedon.fu@gmail.com> Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
parent
2c513be305
commit
f0d5f51703
2
.github/workflows/integration_test.yml
vendored
2
.github/workflows/integration_test.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
|
@ -10,21 +10,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -11,21 +11,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -10,21 +10,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -5,27 +5,11 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.openV020database();
|
||||
|
||||
|
@ -14,21 +14,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -8,21 +8,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -9,21 +9,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -1,66 +1,15 @@
|
||||
import 'dart:io';
|
||||
|
||||
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:integration_test/integration_test.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() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
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
|
||||
final textCells = <String>['A', 'B', 'C', 'D', 'E', '', '', '', '', ''];
|
||||
|
@ -3,27 +3,11 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.openV020database();
|
||||
// create a filter
|
||||
|
@ -11,21 +11,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -9,21 +9,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -10,21 +10,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
|
@ -14,17 +14,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -11,17 +11,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -13,17 +13,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -12,23 +12,8 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
final context = await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// expect to see a readme page
|
||||
@ -38,18 +23,21 @@ void main() {
|
||||
await tester.tapImportButton();
|
||||
|
||||
final testFileNames = ['test1.md', 'test2.md'];
|
||||
final fileLocation = await tester.currentFileLocation();
|
||||
final paths = <String>[];
|
||||
for (final fileName in testFileNames) {
|
||||
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
|
||||
await mockPickFilePaths(testFileNames, name: location);
|
||||
await mockPickFilePaths(
|
||||
paths: testFileNames
|
||||
.map((e) => p.join(context.applicationDataDirectory, e))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
await tester.tapTextAndMarkdownButton();
|
||||
|
||||
|
@ -8,16 +8,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('document', () {
|
||||
const location = 'appflowy';
|
||||
|
||||
setUpAll(() async {
|
||||
await TestFolder.setTestLocation(location);
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
await TestFolder.cleanTestLocation(null);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'change the language successfully when launching the app for the first time',
|
||||
(tester) async {
|
||||
|
@ -12,17 +12,6 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
@ -3,7 +3,7 @@ import 'dart:io';
|
||||
import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
|
||||
import 'package:flutter_test/flutter_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/util.dart';
|
||||
|
||||
@ -11,30 +11,17 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
await tester.initializeAppFlowy();
|
||||
final context = await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// expect to see a readme page
|
||||
tester.expectToSeePageName(readme);
|
||||
|
||||
// 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
|
||||
await tester.tapShareButton();
|
||||
await tester.tapMarkdownButton();
|
||||
@ -52,7 +39,7 @@ void main() {
|
||||
testWidgets(
|
||||
'share the markdown after renaming the document name',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
final context = await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// expect to see a readme page
|
||||
@ -65,8 +52,12 @@ void main() {
|
||||
final shareButton = find.byType(ShareActionList);
|
||||
final shareButtonState =
|
||||
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
|
||||
await tester.tapShareButton();
|
||||
|
@ -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/settings_location_cubit.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flutter_test/flutter_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/util.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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 {
|
||||
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();
|
||||
tester.expectToSeeHomePage();
|
||||
|
||||
// switch to user B
|
||||
{
|
||||
// set user name to userA
|
||||
// set user name for userA
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.enterUserName(userA);
|
||||
@ -51,12 +39,14 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// mock the file_picker result
|
||||
await mockGetDirectoryPath(userB);
|
||||
await mockGetDirectoryPath(
|
||||
p.join(rootPath, userB),
|
||||
);
|
||||
await tester.tapCustomLocationButton();
|
||||
await tester.pumpAndSettle();
|
||||
tester.expectToSeeHomePage();
|
||||
|
||||
// set user name to userB
|
||||
// set user name for userB
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.enterUserName(userB);
|
||||
@ -68,7 +58,9 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// mock the file_picker result
|
||||
await mockGetDirectoryPath(userA);
|
||||
await mockGetDirectoryPath(
|
||||
p.join(rootPath, userA),
|
||||
);
|
||||
await tester.tapCustomLocationButton();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
@ -83,16 +75,15 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// mock the file_picker result
|
||||
await mockGetDirectoryPath(userB);
|
||||
await mockGetDirectoryPath(
|
||||
p.join(rootPath, userB),
|
||||
);
|
||||
await tester.tapCustomLocationButton();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
tester.expectToSeeHomePage();
|
||||
tester.expectToSeeUserName(userB);
|
||||
}
|
||||
|
||||
await TestFolder.cleanTestLocation(userA);
|
||||
await TestFolder.cleanTestLocation(userB);
|
||||
});
|
||||
|
||||
testWidgets('reset to default location', (tester) async {
|
||||
@ -109,8 +100,8 @@ void main() {
|
||||
await tester.restoreLocation();
|
||||
|
||||
expect(
|
||||
await TestFolder.defaultDevelopmentLocation(),
|
||||
await TestFolder.currentLocation(),
|
||||
await appFlowyApplicationDataDirectory().then((value) => value.path),
|
||||
await getIt<ApplicationDataStorage>().getPath(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -1,81 +1,77 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:appflowy/startup/entry_point.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:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class TestFolder {
|
||||
/// Location / Path
|
||||
class FlowyTestContext {
|
||||
FlowyTestContext({
|
||||
required this.applicationDataDirectory,
|
||||
});
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
/// Get current using location.
|
||||
static Future<String> currentLocation() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString(KVKeys.pathLocation)!;
|
||||
}
|
||||
|
||||
/// Get default location under development environment.
|
||||
static Future<String> defaultDevelopmentLocation() async {
|
||||
final dir = await appFlowyApplicationDataDirectory();
|
||||
return dir.path;
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
final String applicationDataDirectory;
|
||||
}
|
||||
|
||||
extension AppFlowyTestBase on WidgetTester {
|
||||
Future<void> initializeAppFlowy() async {
|
||||
Future<FlowyTestContext> initializeAppFlowy({
|
||||
// use to append after the application data directory
|
||||
String? pathExtension,
|
||||
}) async {
|
||||
mockHotKeyManagerHandlers();
|
||||
final directory = await mockApplicationDataStorage(
|
||||
pathExtension: pathExtension,
|
||||
);
|
||||
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await FlowyRunner.run(
|
||||
FlowyApp(),
|
||||
IntegrationMode.integrationTest,
|
||||
);
|
||||
|
||||
await wait(3000);
|
||||
await pumpAndSettle(const Duration(seconds: 2));
|
||||
return FlowyTestContext(
|
||||
applicationDataDirectory: directory,
|
||||
);
|
||||
}
|
||||
|
||||
void mockHotKeyManagerHandlers() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(const MethodChannel('hotkey_manager'),
|
||||
(MethodCall methodCall) async {
|
||||
if (methodCall.method == 'unregisterAll') {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
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(
|
||||
|
@ -17,11 +17,6 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'util.dart';
|
||||
|
||||
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.
|
||||
Future<void> tapGoButton() async {
|
||||
final goButton = find.byType(GoButton);
|
||||
|
@ -73,7 +73,7 @@ import 'mock/mock_file_picker.dart';
|
||||
|
||||
extension AppFlowyDatabaseTest on WidgetTester {
|
||||
Future<void> openV020database() async {
|
||||
await initializeAppFlowy();
|
||||
final context = await initializeAppFlowy();
|
||||
await tapGoButton();
|
||||
|
||||
// expect to see a readme page
|
||||
@ -83,18 +83,25 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapImportButton();
|
||||
|
||||
final testFileNames = ['v020.afdb'];
|
||||
final fileLocation = await currentFileLocation();
|
||||
final paths = <String>[];
|
||||
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
|
||||
await mockPickFilePaths(testFileNames, name: 'import_files');
|
||||
await mockPickFilePaths(
|
||||
paths: paths,
|
||||
);
|
||||
await tapDatabaseRawDataButton();
|
||||
await openPage('v020');
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/util/file_picker/file_picker_service.dart';
|
||||
import 'package:file_picker/file_picker.dart' as fp;
|
||||
import 'package:path/path.dart' as p;
|
||||
import '../util.dart';
|
||||
|
||||
class MockFilePicker implements FilePickerService {
|
||||
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.registerFactory<FilePickerService>(
|
||||
() => MockFilePicker(
|
||||
mockPath: dir.path,
|
||||
mockPath: path,
|
||||
),
|
||||
);
|
||||
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.registerFactory<FilePickerService>(
|
||||
() => MockFilePicker(
|
||||
@ -79,18 +76,16 @@ Future<String> mockSaveFilePath(String? name, String fileName) async {
|
||||
return path;
|
||||
}
|
||||
|
||||
Future<List<String>> mockPickFilePaths(
|
||||
List<String> fileNames, {
|
||||
String? name,
|
||||
String? customPath,
|
||||
Future<List<String>> mockPickFilePaths({
|
||||
required List<String> paths,
|
||||
}) 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.registerFactory<FilePickerService>(
|
||||
() => MockFilePicker(
|
||||
|
@ -2,7 +2,7 @@ import 'dart:io';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.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:file_picker/file_picker.dart' as fp;
|
||||
|
||||
|
@ -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/grid/application/grid_header_bloc.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/supabase_auth_service.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_service.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/workspace/prelude.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;
|
||||
|
||||
class DependencyResolver {
|
||||
static Future<void> resolve(GetIt getIt) async {
|
||||
static Future<void> resolve(
|
||||
GetIt getIt,
|
||||
IntegrationMode mode,
|
||||
) async {
|
||||
_resolveUserDeps(getIt);
|
||||
|
||||
_resolveHomeDeps(getIt);
|
||||
|
||||
_resolveFolderDeps(getIt);
|
||||
|
||||
_resolveDocDeps(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>(() => DartKeyValue());
|
||||
getIt.registerFactory<FilePickerService>(() => FilePicker());
|
||||
getIt.registerFactory<ApplicationDataStorage>(() => ApplicationDataStorage());
|
||||
if (mode.isTest) {
|
||||
getIt.registerFactory<ApplicationDataStorage>(
|
||||
() => MockApplicationDataStorage(),
|
||||
);
|
||||
} else {
|
||||
getIt.registerFactory<ApplicationDataStorage>(
|
||||
() => ApplicationDataStorage(),
|
||||
);
|
||||
}
|
||||
|
||||
getIt.registerFactoryAsync<OpenAIRepository>(
|
||||
() async {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
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:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -18,8 +18,14 @@ abstract class EntryPoint {
|
||||
Widget create(LaunchConfiguration config);
|
||||
}
|
||||
|
||||
class FlowyRunnerContext {
|
||||
final Directory applicationDataDirectory;
|
||||
|
||||
FlowyRunnerContext({required this.applicationDataDirectory});
|
||||
}
|
||||
|
||||
class FlowyRunner {
|
||||
static Future<void> run(
|
||||
static Future<FlowyRunnerContext> run(
|
||||
EntryPoint f,
|
||||
IntegrationMode mode, {
|
||||
LaunchConfiguration config = const LaunchConfiguration(
|
||||
@ -32,11 +38,10 @@ class FlowyRunner {
|
||||
// Specify the env
|
||||
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
|
||||
final launcher = getIt<AppLauncher>();
|
||||
@ -49,13 +54,13 @@ class FlowyRunner {
|
||||
// init the app window
|
||||
const InitAppWindowTask(),
|
||||
// Init Rust SDK
|
||||
InitRustSDKTask(directory: directory),
|
||||
InitRustSDKTask(directory: applicationDataDirectory),
|
||||
// Load Plugins, like document, grid ...
|
||||
const PluginLoadTask(),
|
||||
|
||||
// init the app widget
|
||||
// ignore in test mode
|
||||
if (!mode.isTest()) ...[
|
||||
if (!mode.isUnitTest) ...[
|
||||
const HotKeyTask(),
|
||||
InitSupabaseTask(
|
||||
url: Env.supabaseUrl,
|
||||
@ -70,6 +75,10 @@ class FlowyRunner {
|
||||
],
|
||||
);
|
||||
await launcher.launch(); // execute the tasks
|
||||
|
||||
return FlowyRunnerContext(
|
||||
applicationDataDirectory: applicationDataDirectory,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +103,7 @@ Future<void> initGetIt(
|
||||
);
|
||||
getIt.registerSingleton<PluginSandbox>(PluginSandbox());
|
||||
|
||||
await DependencyResolver.resolve(getIt);
|
||||
await DependencyResolver.resolve(getIt, env);
|
||||
}
|
||||
|
||||
class LaunchContext {
|
||||
@ -145,23 +154,24 @@ class AppLauncher {
|
||||
enum IntegrationMode {
|
||||
develop,
|
||||
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() {
|
||||
if (Platform.environment.containsKey('FLUTTER_TEST')) {
|
||||
return IntegrationMode.test;
|
||||
return IntegrationMode.unitTest;
|
||||
}
|
||||
|
||||
if (kReleaseMode) {
|
||||
|
@ -8,5 +8,6 @@ class InitLocalizationTask extends LaunchTask {
|
||||
@override
|
||||
Future<void> initialize(LaunchContext context) async {
|
||||
await EasyLocalization.ensureInitialized();
|
||||
EasyLocalization.logger.enableBuildModes = [];
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ AppFlowyEnv getAppFlowyEnv() {
|
||||
final collabTableConfig =
|
||||
CollabTableConfig(enable: true, table_name: Env.supabaseCollabTable);
|
||||
|
||||
final supbaseDBConfig = SupabaseDBConfig(
|
||||
final supabaseDBConfig = SupabaseDBConfig(
|
||||
url: Env.supabaseUrl,
|
||||
key: Env.supabaseKey,
|
||||
jwt_secret: Env.supabaseJwtSecret,
|
||||
@ -47,7 +47,7 @@ AppFlowyEnv getAppFlowyEnv() {
|
||||
|
||||
return AppFlowyEnv(
|
||||
supabase_config: supabaseConfig,
|
||||
supabase_db_config: supbaseDBConfig,
|
||||
supabase_db_config: supabaseDBConfig,
|
||||
);
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ Future<Directory> appFlowyApplicationDataDirectory() async {
|
||||
case IntegrationMode.release:
|
||||
final Directory documentsDir = await getApplicationSupportDirectory();
|
||||
return Directory(path.join(documentsDir.path, 'data')).create();
|
||||
case IntegrationMode.test:
|
||||
case IntegrationMode.unitTest:
|
||||
case IntegrationMode.integrationTest:
|
||||
return Directory(path.join(Directory.current.path, '.sandbox'));
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class InitAppWindowTask extends LaunchTask with WindowListener {
|
||||
windowManager.addListener(this);
|
||||
|
||||
Size windowSize = await WindowSizeManager().getSize();
|
||||
if (context.env.isIntegrationTest()) {
|
||||
if (context.env.isIntegrationTest) {
|
||||
windowSize = const Size(1600, 1200);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
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:flowy_infra/image.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 '../../../startup/startup.dart';
|
||||
import '../../../workspace/application/settings/settings_location_cubit.dart';
|
||||
import '../../../workspace/presentation/home/toast.dart';
|
||||
|
||||
enum _FolderPage {
|
||||
|
@ -53,7 +53,7 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
|
||||
/// changed. Fallback to [en] locale if [newLocale] is not supported.
|
||||
void setLocale(BuildContext context, Locale 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');
|
||||
}
|
||||
|
||||
|
@ -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 +1,2 @@
|
||||
export 'settings_dialog_bloc.dart';
|
||||
export 'application_data_storage.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_backend/log.dart';
|
||||
import 'package:appflowy/workspace/application/settings/application_data_storage.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../../../startup/tasks/prelude.dart';
|
||||
|
||||
@ -27,7 +22,7 @@ class SettingsLocationCubit extends Cubit<SettingsLocationState> {
|
||||
|
||||
Future<void> resetDataStoragePathToApplicationDefault() async {
|
||||
final directory = await appFlowyApplicationDataDirectory();
|
||||
await getIt<ApplicationDataStorage>()._setPath(directory.path);
|
||||
await getIt<ApplicationDataStorage>().setPath(directory.path);
|
||||
emit(SettingsLocationState.didReceivedPath(directory.path));
|
||||
}
|
||||
|
||||
@ -41,79 +36,3 @@ class SettingsLocationCubit extends Cubit<SettingsLocationState> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ class AppFlowyUnitTest {
|
||||
|
||||
await FlowyRunner.run(
|
||||
FlowyTestApp(),
|
||||
IntegrationMode.test,
|
||||
IntegrationMode.unitTest,
|
||||
);
|
||||
|
||||
final test = AppFlowyUnitTest();
|
||||
|
Loading…
Reference in New Issue
Block a user