import export working
This commit is contained in:
@@ -21,12 +21,16 @@ import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
|||||||
import 'package:nullability/nullability.dart';
|
import 'package:nullability/nullability.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart';
|
import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/bookmarks/utils/bookmark_html_utils.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/bookmarks/utils/bookmark_json_utils.dart';
|
||||||
|
|
||||||
part 'bookmarks.g.dart';
|
part 'bookmarks.g.dart';
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
class BookmarksRepository extends _$BookmarksRepository {
|
class BookmarksRepository extends _$BookmarksRepository {
|
||||||
final _service = GeckoBookmarksService();
|
final _service = GeckoBookmarksService();
|
||||||
|
late final _jsonUtils = BookmarkJSONUtils(_service);
|
||||||
|
late final _htmlUtils = BookmarkHTMLUtils(_service);
|
||||||
|
|
||||||
Future<void> addBookmark({
|
Future<void> addBookmark({
|
||||||
required String parentGuid,
|
required String parentGuid,
|
||||||
@@ -73,12 +77,31 @@ class BookmarksRepository extends _$BookmarksRepository {
|
|||||||
ref.invalidateSelf();
|
ref.invalidateSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<int> importFromJSON(String jsonString, {bool replace = false}) async {
|
||||||
|
final count = await _jsonUtils.importFromJSON(jsonString, replace: replace);
|
||||||
|
ref.invalidateSelf();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> importFromHTML(String htmlString, {bool replace = false}) async {
|
||||||
|
final count = await _htmlUtils.importFromHTML(htmlString, replace: replace);
|
||||||
|
ref.invalidateSelf();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>?> exportToJson({
|
||||||
|
required BookmarkRoot root,
|
||||||
|
}) async {
|
||||||
|
return await _jsonUtils.exportToJson(root: root);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> exportToHTML({required BookmarkRoot root}) async {
|
||||||
|
return await _htmlUtils.exportToHTML(root: root);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BookmarkItem?> build() async {
|
Future<BookmarkItem?> build() async {
|
||||||
final node = await _service.getTree(
|
final node = await _service.getTree(BookmarkRoot.root.id, recursive: true);
|
||||||
BookmarkRoot.mobile.id,
|
|
||||||
recursive: true,
|
|
||||||
);
|
|
||||||
return node.mapNotNull(BookmarkItem.parseRecursive);
|
return node.mapNotNull(BookmarkItem.parseRecursive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ final class BookmarksRepositoryProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$bookmarksRepositoryHash() =>
|
String _$bookmarksRepositoryHash() =>
|
||||||
r'94d9f0f6c589455b16b0831de4ed1f87dcee704e';
|
r'932441e926afcb1d60dc34b8a8756b7ad8fe0a27';
|
||||||
|
|
||||||
abstract class _$BookmarksRepository extends $AsyncNotifier<BookmarkItem?> {
|
abstract class _$BookmarksRepository extends $AsyncNotifier<BookmarkItem?> {
|
||||||
FutureOr<BookmarkItem?> build();
|
FutureOr<BookmarkItem?> build();
|
||||||
|
|||||||
+2
-2
@@ -48,7 +48,7 @@ class BookmarkEntryEditScreen extends HookConsumerWidget {
|
|||||||
final treeKey = useMemoized(() => GlobalKey<TreeViewState>());
|
final treeKey = useMemoized(() => GlobalKey<TreeViewState>());
|
||||||
|
|
||||||
final folderList = ref.watch(
|
final folderList = ref.watch(
|
||||||
bookmarksProvider<BookmarkFolder>(BookmarkRoot.mobile.id),
|
bookmarksProvider<BookmarkFolder>(BookmarkRoot.root.id),
|
||||||
);
|
);
|
||||||
|
|
||||||
final nameTextController = useTextEditingController(
|
final nameTextController = useTextEditingController(
|
||||||
@@ -242,7 +242,7 @@ class BookmarkEntryEditScreen extends HookConsumerWidget {
|
|||||||
// ignore: unused_result
|
// ignore: unused_result
|
||||||
ref.refresh(
|
ref.refresh(
|
||||||
bookmarksProvider<BookmarkFolder>(
|
bookmarksProvider<BookmarkFolder>(
|
||||||
BookmarkRoot.mobile.id,
|
BookmarkRoot.root.id,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
+125
@@ -18,8 +18,11 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:animated_tree_view/animated_tree_view.dart';
|
import 'package:animated_tree_view/animated_tree_view.dart';
|
||||||
|
import 'package:convert/convert.dart';
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||||
@@ -29,11 +32,13 @@ import 'package:nullability/nullability.dart';
|
|||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart';
|
import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/bookmarks/domain/providers/bookmarks.dart';
|
import 'package:weblibre/features/geckoview/features/bookmarks/domain/providers/bookmarks.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/bookmarks/domain/repositories/bookmarks.dart';
|
||||||
import 'package:weblibre/presentation/hooks/listenable_callback.dart';
|
import 'package:weblibre/presentation/hooks/listenable_callback.dart';
|
||||||
import 'package:weblibre/presentation/hooks/menu_controller.dart';
|
import 'package:weblibre/presentation/hooks/menu_controller.dart';
|
||||||
import 'package:weblibre/presentation/widgets/failure_widget.dart';
|
import 'package:weblibre/presentation/widgets/failure_widget.dart';
|
||||||
import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart';
|
import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart';
|
||||||
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
||||||
|
import 'package:weblibre/utils/ui_helper.dart';
|
||||||
|
|
||||||
class BookmarkListScreen extends HookConsumerWidget {
|
class BookmarkListScreen extends HookConsumerWidget {
|
||||||
final String entryGuid;
|
final String entryGuid;
|
||||||
@@ -101,6 +106,38 @@ class BookmarkListScreen extends HookConsumerWidget {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
SubmenuButton(
|
||||||
|
leadingIcon: const Icon(MdiIcons.import),
|
||||||
|
menuChildren: [
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(MdiIcons.codeJson),
|
||||||
|
child: const Text('JSON'),
|
||||||
|
onPressed: () => _handleImport(context, ref, 'json'),
|
||||||
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(MdiIcons.xml),
|
||||||
|
child: const Text('HTML'),
|
||||||
|
onPressed: () => _handleImport(context, ref, 'html'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: const Text('Import'),
|
||||||
|
),
|
||||||
|
SubmenuButton(
|
||||||
|
leadingIcon: const Icon(MdiIcons.export),
|
||||||
|
menuChildren: [
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(MdiIcons.codeJson),
|
||||||
|
child: const Text('JSON'),
|
||||||
|
onPressed: () => _handleExport(context, ref, 'json'),
|
||||||
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(MdiIcons.xml),
|
||||||
|
child: const Text('HTML'),
|
||||||
|
onPressed: () => _handleExport(context, ref, 'html'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: const Text('Export'),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
builder: (context, controller, child) => IconButton(
|
builder: (context, controller, child) => IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -148,6 +185,7 @@ class BookmarkListScreen extends HookConsumerWidget {
|
|||||||
return TreeView.simple(
|
return TreeView.simple(
|
||||||
key: treeKey,
|
key: treeKey,
|
||||||
tree: root,
|
tree: root,
|
||||||
|
showRootNode: entryGuid != BookmarkRoot.root.id,
|
||||||
onTreeReady: (controller) {
|
onTreeReady: (controller) {
|
||||||
if (textFilterEnabled.value) {
|
if (textFilterEnabled.value) {
|
||||||
controller.expandAllChildren(root, recursive: true);
|
controller.expandAllChildren(root, recursive: true);
|
||||||
@@ -299,4 +337,91 @@ class BookmarkListScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _handleImport(
|
||||||
|
BuildContext context,
|
||||||
|
WidgetRef ref,
|
||||||
|
String format,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
final result = await FilePicker.platform.pickFiles(
|
||||||
|
type: FileType.custom,
|
||||||
|
allowedExtensions: format == 'json' ? ['json'] : ['html', 'htm'],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == null || result.files.isEmpty) return;
|
||||||
|
|
||||||
|
final file = result.files.first;
|
||||||
|
if (file.path == null) {
|
||||||
|
if (context.mounted) {
|
||||||
|
showErrorMessage(context, 'Failed to read file');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final content = await File(file.path!).readAsString();
|
||||||
|
final repository = ref.read(bookmarksRepositoryProvider.notifier);
|
||||||
|
|
||||||
|
final count = format == 'json'
|
||||||
|
? await repository.importFromJSON(content)
|
||||||
|
: await repository.importFromHTML(content);
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
showInfoMessage(context, 'Imported $count bookmarks successfully');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (context.mounted) {
|
||||||
|
showErrorMessage(context, 'Import failed: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _handleExport(
|
||||||
|
BuildContext context,
|
||||||
|
WidgetRef ref,
|
||||||
|
String format,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
// Create the backup content first
|
||||||
|
final repository = ref.read(bookmarksRepositoryProvider.notifier);
|
||||||
|
|
||||||
|
String content;
|
||||||
|
if (format == 'json') {
|
||||||
|
final data = await repository.exportToJson(root: BookmarkRoot.root);
|
||||||
|
if (data == null) {
|
||||||
|
throw Exception('Failed to export bookmarks');
|
||||||
|
}
|
||||||
|
content = const JsonEncoder.withIndent(' ').convert(data);
|
||||||
|
} else {
|
||||||
|
content = await repository.exportToHTML(root: BookmarkRoot.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to bytes for the file picker
|
||||||
|
final bytes = utf8.encode(content);
|
||||||
|
|
||||||
|
// Now show the save dialog with the content ready
|
||||||
|
final dateFormatter = FixedDateTimeFormatter('YYYY-MM-DD_hhmmss');
|
||||||
|
final timestamp = dateFormatter.encode(DateTime.now());
|
||||||
|
final defaultFileName =
|
||||||
|
'bookmarks_$timestamp.${format == 'json' ? 'json' : 'html'}';
|
||||||
|
|
||||||
|
final outputPath = await FilePicker.platform.saveFile(
|
||||||
|
dialogTitle: 'Export Bookmarks',
|
||||||
|
fileName: defaultFileName,
|
||||||
|
type: FileType.custom,
|
||||||
|
allowedExtensions: format == 'json' ? ['json'] : ['html', 'htm'],
|
||||||
|
bytes: bytes,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (outputPath == null) return;
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
showInfoMessage(context, 'Bookmarks exported successfully');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (context.mounted) {
|
||||||
|
showErrorMessage(context, 'Export failed: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
import 'package:html/dom.dart' as dom;
|
import 'package:html/dom.dart' as dom;
|
||||||
import 'package:html/parser.dart' as html_parser;
|
import 'package:html/parser.dart' as html_parser;
|
||||||
|
import 'package:weblibre/core/logger.dart';
|
||||||
|
|
||||||
const _containerNormal = 0;
|
const _containerNormal = 0;
|
||||||
const _containerToolbar = 1;
|
const _containerToolbar = 1;
|
||||||
@@ -426,7 +427,7 @@ class _BookmarkImporter {
|
|||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Failed to import bookmark "$title": $e');
|
logger.e('Failed to import bookmark "$title": $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (type == BookmarkNodeType.folder.index) {
|
} else if (type == BookmarkNodeType.folder.index) {
|
||||||
@@ -436,7 +437,7 @@ class _BookmarkImporter {
|
|||||||
child['guid'] = newGuid;
|
child['guid'] = newGuid;
|
||||||
count += await _insertTree(child);
|
count += await _insertTree(child);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Failed to import folder "$title": $e');
|
logger.e('Failed to import folder "$title": $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
|
import 'package:weblibre/core/logger.dart';
|
||||||
|
|
||||||
class BookmarkJSONUtils {
|
class BookmarkJSONUtils {
|
||||||
final GeckoBookmarksService _service;
|
final GeckoBookmarksService _service;
|
||||||
@@ -24,7 +25,7 @@ class BookmarkJSONUtils {
|
|||||||
|
|
||||||
return await _import(data, replace: replace);
|
return await _import(data, replace: replace);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
print('Failed to import bookmarks: $ex');
|
logger.e('Failed to import bookmarks: $ex');
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,10 +131,10 @@ class BookmarkJSONUtils {
|
|||||||
await _service.addItem(parentGuid, uri, title, i);
|
await _service.addItem(parentGuid, uri, title, i);
|
||||||
count++;
|
count++;
|
||||||
} else {
|
} else {
|
||||||
print('Skipping invalid URL: $url');
|
logger.w('Skipping invalid URL: $url');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Failed to import bookmark "$title": $e');
|
logger.e('Failed to import bookmark "$title": $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (type == BookmarkNodeType.folder) {
|
} else if (type == BookmarkNodeType.folder) {
|
||||||
@@ -145,7 +146,7 @@ class BookmarkJSONUtils {
|
|||||||
// Recursively insert children
|
// Recursively insert children
|
||||||
count += await _insertTree(child, folderIdToGuidMap);
|
count += await _insertTree(child, folderIdToGuidMap);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Failed to import folder "$title": $e');
|
logger.e('Failed to import folder "$title": $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Note: Separators are not supported by the Android API
|
// Note: Separators are not supported by the Android API
|
||||||
@@ -237,13 +238,13 @@ class BookmarkJSONUtils {
|
|||||||
// Skip invalid bookmarks
|
// Skip invalid bookmarks
|
||||||
if (node.type == BookmarkNodeType.item) {
|
if (node.type == BookmarkNodeType.item) {
|
||||||
if (node.url == null || node.url!.isEmpty) {
|
if (node.url == null || node.url!.isEmpty) {
|
||||||
print('Skipping bookmark with invalid URL: ${node.guid}');
|
logger.w('Skipping bookmark with invalid URL: ${node.guid}');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Uri.parse(node.url!);
|
Uri.parse(node.url!);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Skipping bookmark with malformed URL: ${node.url}');
|
logger.w('Skipping bookmark with malformed URL: ${node.url}');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -417,7 +417,7 @@ class ContextualToolbar extends HookConsumerWidget {
|
|||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await BookmarkListRoute(
|
await BookmarkListRoute(
|
||||||
entryGuid: BookmarkRoot.mobile.id,
|
entryGuid: BookmarkRoot.root.id,
|
||||||
).push(context);
|
).push(context);
|
||||||
},
|
},
|
||||||
icon: const Icon(MdiIcons.bookmarkMultiple),
|
icon: const Icon(MdiIcons.bookmarkMultiple),
|
||||||
@@ -806,7 +806,7 @@ class NavigationMenuButton extends HookConsumerWidget {
|
|||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await BookmarkListRoute(
|
await BookmarkListRoute(
|
||||||
entryGuid: BookmarkRoot.mobile.id,
|
entryGuid: BookmarkRoot.root.id,
|
||||||
).push(context);
|
).push(context);
|
||||||
},
|
},
|
||||||
leadingIcon: const Icon(MdiIcons.bookmarkMultiple),
|
leadingIcon: const Icon(MdiIcons.bookmarkMultiple),
|
||||||
|
|||||||
+102
-67
@@ -1,3 +1,5 @@
|
|||||||
|
// ignore_for_file: avoid_redundant_argument_values
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024-2025 Fabian Freund.
|
* Copyright (c) 2024-2025 Fabian Freund.
|
||||||
*
|
*
|
||||||
@@ -41,16 +43,19 @@ void main() {
|
|||||||
group('BookmarkHTMLUtils - Import', () {
|
group('BookmarkHTMLUtils - Import', () {
|
||||||
test('should handle corrupt HTML file with malformed URIs', () async {
|
test('should handle corrupt HTML file with malformed URIs', () async {
|
||||||
// Load the corrupt fixture
|
// Load the corrupt fixture
|
||||||
final fixtureFile =
|
final fixtureFile = File(
|
||||||
File('test/utils/bookmarks/fixtures/bookmarks.corrupt.html');
|
'test/utils/bookmarks/fixtures/bookmarks.corrupt.html',
|
||||||
|
);
|
||||||
final htmlString = await fixtureFile.readAsString();
|
final htmlString = await fixtureFile.readAsString();
|
||||||
|
|
||||||
// Mock the service calls
|
// Mock the service calls
|
||||||
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
||||||
when(mockService.addFolder(any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'generated_guid');
|
mockService.addFolder(any, any, any),
|
||||||
when(mockService.addItem(any, any, any, any))
|
).thenAnswer((_) async => 'generated_guid');
|
||||||
.thenAnswer((_) async => 'generated_guid');
|
when(
|
||||||
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'generated_guid');
|
||||||
|
|
||||||
final count = await utils.importFromHTML(htmlString, replace: true);
|
final count = await utils.importFromHTML(htmlString, replace: true);
|
||||||
|
|
||||||
@@ -60,15 +65,18 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should import from valid HTML file', () async {
|
test('should import from valid HTML file', () async {
|
||||||
final fixtureFile =
|
final fixtureFile = File(
|
||||||
File('test/utils/bookmarks/fixtures/bookmarks.preplaces.html');
|
'test/utils/bookmarks/fixtures/bookmarks.preplaces.html',
|
||||||
|
);
|
||||||
final htmlString = await fixtureFile.readAsString();
|
final htmlString = await fixtureFile.readAsString();
|
||||||
|
|
||||||
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
||||||
when(mockService.addFolder(any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'folder_guid');
|
mockService.addFolder(any, any, any),
|
||||||
when(mockService.addItem(any, any, any, any))
|
).thenAnswer((_) async => 'folder_guid');
|
||||||
.thenAnswer((_) async => 'bookmark_guid');
|
when(
|
||||||
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'bookmark_guid');
|
||||||
|
|
||||||
final count = await utils.importFromHTML(htmlString, replace: true);
|
final count = await utils.importFromHTML(htmlString, replace: true);
|
||||||
|
|
||||||
@@ -105,8 +113,9 @@ void main() {
|
|||||||
</DL>
|
</DL>
|
||||||
''';
|
''';
|
||||||
|
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'guid');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'guid');
|
||||||
|
|
||||||
await utils.importFromHTML(simpleHtml);
|
await utils.importFromHTML(simpleHtml);
|
||||||
|
|
||||||
@@ -123,8 +132,9 @@ void main() {
|
|||||||
</DL>
|
</DL>
|
||||||
''';
|
''';
|
||||||
|
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'guid');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'guid');
|
||||||
|
|
||||||
final count = await utils.importFromHTML(htmlWithSpecialChars);
|
final count = await utils.importFromHTML(htmlWithSpecialChars);
|
||||||
|
|
||||||
@@ -146,8 +156,9 @@ void main() {
|
|||||||
</DL>
|
</DL>
|
||||||
''';
|
''';
|
||||||
|
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'guid');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'guid');
|
||||||
|
|
||||||
final count = await utils.importFromHTML(htmlWithDates);
|
final count = await utils.importFromHTML(htmlWithDates);
|
||||||
|
|
||||||
@@ -171,10 +182,12 @@ void main() {
|
|||||||
</DL>
|
</DL>
|
||||||
''';
|
''';
|
||||||
|
|
||||||
when(mockService.addFolder(any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'folder_guid');
|
mockService.addFolder(any, any, any),
|
||||||
when(mockService.addItem(any, any, any, any))
|
).thenAnswer((_) async => 'folder_guid');
|
||||||
.thenAnswer((_) async => 'bookmark_guid');
|
when(
|
||||||
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'bookmark_guid');
|
||||||
|
|
||||||
final count = await utils.importFromHTML(htmlWithFolders);
|
final count = await utils.importFromHTML(htmlWithFolders);
|
||||||
|
|
||||||
@@ -196,8 +209,9 @@ void main() {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'guid');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'guid');
|
||||||
|
|
||||||
await utils.importFromHTML(htmlWithToolbar, replace: true);
|
await utils.importFromHTML(htmlWithToolbar, replace: true);
|
||||||
|
|
||||||
@@ -222,8 +236,9 @@ void main() {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'guid');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'guid');
|
||||||
|
|
||||||
await utils.importFromHTML(htmlWithUnfiled, replace: true);
|
await utils.importFromHTML(htmlWithUnfiled, replace: true);
|
||||||
|
|
||||||
@@ -245,8 +260,9 @@ void main() {
|
|||||||
</DL>
|
</DL>
|
||||||
''';
|
''';
|
||||||
|
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'guid');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'guid');
|
||||||
|
|
||||||
final count = await utils.importFromHTML(htmlWithSeparator);
|
final count = await utils.importFromHTML(htmlWithSeparator);
|
||||||
|
|
||||||
@@ -265,8 +281,9 @@ void main() {
|
|||||||
</DL>
|
</DL>
|
||||||
''';
|
''';
|
||||||
|
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'guid');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'guid');
|
||||||
|
|
||||||
final count = await utils.importFromHTML(htmlWithoutUrl);
|
final count = await utils.importFromHTML(htmlWithoutUrl);
|
||||||
|
|
||||||
@@ -284,8 +301,9 @@ void main() {
|
|||||||
</DL>
|
</DL>
|
||||||
''';
|
''';
|
||||||
|
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'guid');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'guid');
|
||||||
|
|
||||||
final count = await utils.importFromHTML(htmlWithInvalidUrl);
|
final count = await utils.importFromHTML(htmlWithInvalidUrl);
|
||||||
|
|
||||||
@@ -293,14 +311,17 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should handle single frame HTML', () async {
|
test('should handle single frame HTML', () async {
|
||||||
final fixtureFile =
|
final fixtureFile = File(
|
||||||
File('test/utils/bookmarks/fixtures/bookmarks_html_singleframe.html');
|
'test/utils/bookmarks/fixtures/bookmarks_html_singleframe.html',
|
||||||
|
);
|
||||||
final htmlString = await fixtureFile.readAsString();
|
final htmlString = await fixtureFile.readAsString();
|
||||||
|
|
||||||
when(mockService.addFolder(any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'folder_guid');
|
mockService.addFolder(any, any, any),
|
||||||
when(mockService.addItem(any, any, any, any))
|
).thenAnswer((_) async => 'folder_guid');
|
||||||
.thenAnswer((_) async => 'bookmark_guid');
|
when(
|
||||||
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'bookmark_guid');
|
||||||
|
|
||||||
final count = await utils.importFromHTML(htmlString);
|
final count = await utils.importFromHTML(htmlString);
|
||||||
|
|
||||||
@@ -333,8 +354,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -368,8 +390,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -402,8 +425,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -424,8 +448,9 @@ void main() {
|
|||||||
children: [],
|
children: [],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final html = await utils.exportToHTML(root: BookmarkRoot.toolbar);
|
final html = await utils.exportToHTML(root: BookmarkRoot.toolbar);
|
||||||
|
|
||||||
@@ -447,8 +472,9 @@ void main() {
|
|||||||
children: [],
|
children: [],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final html = await utils.exportToHTML(root: BookmarkRoot.unfiled);
|
final html = await utils.exportToHTML(root: BookmarkRoot.unfiled);
|
||||||
|
|
||||||
@@ -501,8 +527,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -543,8 +570,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -590,8 +618,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -601,9 +630,10 @@ void main() {
|
|||||||
expect(html, contains('</H3>'));
|
expect(html, contains('</H3>'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should throw when tree cannot be fetched', () async {
|
test('should throw when tree cannot be fetched', () {
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => null);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => null);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
() => utils.exportToHTML(root: BookmarkRoot.menu),
|
() => utils.exportToHTML(root: BookmarkRoot.menu),
|
||||||
@@ -624,8 +654,9 @@ void main() {
|
|||||||
children: [],
|
children: [],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -659,8 +690,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -708,8 +740,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
// Export
|
// Export
|
||||||
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
final html = await utils.exportToHTML(root: BookmarkRoot.menu);
|
||||||
@@ -717,10 +750,12 @@ void main() {
|
|||||||
|
|
||||||
// Re-import
|
// Re-import
|
||||||
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
||||||
when(mockService.addFolder(any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'folder1_____');
|
mockService.addFolder(any, any, any),
|
||||||
when(mockService.addItem(any, any, any, any))
|
).thenAnswer((_) async => 'folder1_____');
|
||||||
.thenAnswer((_) async => 'bookmark1___');
|
when(
|
||||||
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'bookmark1___');
|
||||||
|
|
||||||
final count = await utils.importFromHTML(html, replace: true);
|
final count = await utils.importFromHTML(html, replace: true);
|
||||||
|
|
||||||
|
|||||||
+74
-54
@@ -1,3 +1,5 @@
|
|||||||
|
// ignore_for_file: avoid_redundant_argument_values, avoid_dynamic_calls
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024-2025 Fabian Freund.
|
* Copyright (c) 2024-2025 Fabian Freund.
|
||||||
*
|
*
|
||||||
@@ -108,10 +110,7 @@ void main() {
|
|||||||
|
|
||||||
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
||||||
|
|
||||||
await utils.importFromJSON(
|
await utils.importFromJSON(jsonEncode(jsonData), replace: true);
|
||||||
jsonEncode(jsonData),
|
|
||||||
replace: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
verify(mockService.eraseEverything(BookmarkRoot.root)).called(1);
|
verify(mockService.eraseEverything(BookmarkRoot.root)).called(1);
|
||||||
});
|
});
|
||||||
@@ -150,8 +149,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'bookmark1___');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'bookmark1___');
|
||||||
|
|
||||||
final count = await utils.importFromJSON(jsonEncode(jsonData));
|
final count = await utils.importFromJSON(jsonEncode(jsonData));
|
||||||
|
|
||||||
@@ -184,8 +184,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'bookmark1___');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'bookmark1___');
|
||||||
|
|
||||||
final count = await utils.importFromJSON(jsonEncode(jsonData));
|
final count = await utils.importFromJSON(jsonEncode(jsonData));
|
||||||
|
|
||||||
@@ -224,8 +225,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'valid1______');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'valid1______');
|
||||||
|
|
||||||
final count = await utils.importFromJSON(jsonEncode(jsonData));
|
final count = await utils.importFromJSON(jsonEncode(jsonData));
|
||||||
|
|
||||||
@@ -267,10 +269,12 @@ void main() {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
when(mockService.addFolder(any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'folder1_____');
|
mockService.addFolder(any, any, any),
|
||||||
when(mockService.addItem(any, any, any, any))
|
).thenAnswer((_) async => 'folder1_____');
|
||||||
.thenAnswer((_) async => 'bookmark1___');
|
when(
|
||||||
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'bookmark1___');
|
||||||
|
|
||||||
final count = await utils.importFromJSON(jsonEncode(jsonData));
|
final count = await utils.importFromJSON(jsonEncode(jsonData));
|
||||||
|
|
||||||
@@ -299,10 +303,7 @@ void main() {
|
|||||||
'type': 'text/x-moz-place',
|
'type': 'text/x-moz-place',
|
||||||
'uri': 'https://example.com/1',
|
'uri': 'https://example.com/1',
|
||||||
},
|
},
|
||||||
{
|
{'guid': 'separator___', 'type': 'text/x-moz-place-separator'},
|
||||||
'guid': 'separator___',
|
|
||||||
'type': 'text/x-moz-place-separator',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'guid': 'bookmark2___',
|
'guid': 'bookmark2___',
|
||||||
'title': 'Second Bookmark',
|
'title': 'Second Bookmark',
|
||||||
@@ -314,8 +315,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((invocation) async => 'generated_guid');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((invocation) async => 'generated_guid');
|
||||||
|
|
||||||
final count = await utils.importFromJSON(jsonEncode(jsonData));
|
final count = await utils.importFromJSON(jsonEncode(jsonData));
|
||||||
|
|
||||||
@@ -350,10 +352,12 @@ void main() {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
when(mockService.addFolder(any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'folder1_____');
|
mockService.addFolder(any, any, any),
|
||||||
when(mockService.addItem(any, any, any, any))
|
).thenAnswer((_) async => 'folder1_____');
|
||||||
.thenAnswer((_) async => 'shortcut1___');
|
when(
|
||||||
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'shortcut1___');
|
||||||
|
|
||||||
await utils.importFromJSON(jsonEncode(jsonData));
|
await utils.importFromJSON(jsonEncode(jsonData));
|
||||||
|
|
||||||
@@ -384,8 +388,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'shortcut1___');
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'shortcut1___');
|
||||||
|
|
||||||
await utils.importFromJSON(jsonEncode(jsonData));
|
await utils.importFromJSON(jsonEncode(jsonData));
|
||||||
|
|
||||||
@@ -410,10 +415,12 @@ void main() {
|
|||||||
|
|
||||||
// Mock the service calls
|
// Mock the service calls
|
||||||
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
||||||
when(mockService.addFolder(any, any, any))
|
when(
|
||||||
.thenAnswer((invocation) async => 'generated_guid');
|
mockService.addFolder(any, any, any),
|
||||||
when(mockService.addItem(any, any, any, any))
|
).thenAnswer((invocation) async => 'generated_guid');
|
||||||
.thenAnswer((invocation) async => 'generated_guid');
|
when(
|
||||||
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((invocation) async => 'generated_guid');
|
||||||
|
|
||||||
final count = await utils.importFromJSON(jsonString, replace: true);
|
final count = await utils.importFromJSON(jsonString, replace: true);
|
||||||
|
|
||||||
@@ -440,8 +447,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
when(mockService.addItem(any, any, any, any))
|
when(
|
||||||
.thenThrow(Exception('Database error'));
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenThrow(Exception('Database error'));
|
||||||
|
|
||||||
// Should not throw, but should log and continue
|
// Should not throw, but should log and continue
|
||||||
final count = await utils.importFromJSON(jsonEncode(jsonData));
|
final count = await utils.importFromJSON(jsonEncode(jsonData));
|
||||||
@@ -475,8 +483,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final result = await utils.exportToJson(root: BookmarkRoot.menu);
|
final result = await utils.exportToJson(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -529,8 +538,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final result = await utils.exportToJson(root: BookmarkRoot.menu);
|
final result = await utils.exportToJson(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -565,8 +575,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final result = await utils.exportToJson(root: BookmarkRoot.menu);
|
final result = await utils.exportToJson(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -600,8 +611,9 @@ void main() {
|
|||||||
children: [],
|
children: [],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(testCase.$1.id, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(testCase.$1.id, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final result = await utils.exportToJson(root: testCase.$1);
|
final result = await utils.exportToJson(root: testCase.$1);
|
||||||
|
|
||||||
@@ -654,8 +666,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final result = await utils.exportToJson(root: BookmarkRoot.menu);
|
final result = await utils.exportToJson(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -667,9 +680,10 @@ void main() {
|
|||||||
expect(children[2]['index'], equals(2));
|
expect(children[2]['index'], equals(2));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should throw when tree cannot be fetched', () async {
|
test('should throw when tree cannot be fetched', () {
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => null);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => null);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
() => utils.exportToJson(root: BookmarkRoot.menu),
|
() => utils.exportToJson(root: BookmarkRoot.menu),
|
||||||
@@ -701,8 +715,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
final result = await utils.exportToJson(root: BookmarkRoot.menu);
|
final result = await utils.exportToJson(root: BookmarkRoot.menu);
|
||||||
|
|
||||||
@@ -752,8 +767,9 @@ void main() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
when(mockService.getTree(any, recursive: true))
|
when(
|
||||||
.thenAnswer((_) async => mockNode);
|
mockService.getTree(any, recursive: true),
|
||||||
|
).thenAnswer((_) async => mockNode);
|
||||||
|
|
||||||
// Export
|
// Export
|
||||||
final exported = await utils.exportToJson(root: BookmarkRoot.menu);
|
final exported = await utils.exportToJson(root: BookmarkRoot.menu);
|
||||||
@@ -761,12 +777,16 @@ void main() {
|
|||||||
|
|
||||||
// Re-import
|
// Re-import
|
||||||
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
when(mockService.eraseEverything(any)).thenAnswer((_) async {});
|
||||||
when(mockService.addFolder(any, any, any))
|
when(
|
||||||
.thenAnswer((_) async => 'folder1_____');
|
mockService.addFolder(any, any, any),
|
||||||
when(mockService.addItem(any, any, any, any))
|
).thenAnswer((_) async => 'folder1_____');
|
||||||
.thenAnswer((_) async => 'bookmark1___');
|
when(
|
||||||
|
mockService.addItem(any, any, any, any),
|
||||||
|
).thenAnswer((_) async => 'bookmark1___');
|
||||||
|
|
||||||
final jsonString = jsonEncode({'children': [exported]});
|
final jsonString = jsonEncode({
|
||||||
|
'children': [exported],
|
||||||
|
});
|
||||||
final count = await utils.importFromJSON(jsonString, replace: true);
|
final count = await utils.importFromJSON(jsonString, replace: true);
|
||||||
|
|
||||||
expect(count, equals(1)); // One bookmark imported
|
expect(count, equals(1)); // One bookmark imported
|
||||||
|
|||||||
Reference in New Issue
Block a user