fix: unable insert a reference database (#2798)

* fix: unable insert a reference database

* test: add reference database tests

* feat: set min height for document inside database
This commit is contained in:
Lucas.Xu 2023-06-15 16:33:44 +08:00 committed by GitHub
parent 95f8b2e9a4
commit d5884ad2b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 271 additions and 49 deletions

View File

@ -28,7 +28,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.hoverOnCoverPluginAddButton();
await tester.editor.hoverOnCoverPluginAddButton();
tester.expectToSeePluginAddCoverAndIconButton();
});

View File

@ -0,0 +1,110 @@
import 'package:appflowy/plugins/database_view/board/presentation/board_page.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'util/util.dart';
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();
await insertReferenceDatabase(tester, ViewLayoutPB.Grid);
// validate the referenced grid is inserted
expect(
find.descendant(
of: find.byType(AppFlowyEditor),
matching: find.byType(GridPage),
),
findsOneWidget,
);
});
testWidgets('insert a referenced board', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await insertReferenceDatabase(tester, ViewLayoutPB.Board);
// validate the referenced board is inserted
expect(
find.descendant(
of: find.byType(AppFlowyEditor),
matching: find.byType(BoardPage),
),
findsOneWidget,
);
});
// testWidgets('insert a referenced calendar', (tester) async {
// await tester.initializeAppFlowy();
// await tester.tapGoButton();
// await insertReferenceDatabase(tester, ViewLayoutPB.Calendar);
// // validate the referenced grid is inserted
// expect(
// find.descendant(
// of: find.byType(AppFlowyEditor),
// matching: find.byType(CalendarPage),
// ),
// findsOneWidget,
// );
// });
});
}
/// Insert a referenced database of [layout] into the document
Future<void> insertReferenceDatabase(
WidgetTester tester,
ViewLayoutPB layout,
) async {
// create a new grid
final id = uuid();
final name = '${layout.name}_$id';
await tester.createNewPageWithName(
layout,
name,
);
// create a new document
await tester.createNewPageWithName(
ViewLayoutPB.Document,
'insert_a_reference_${layout.name}',
);
// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);
// insert a referenced grid
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName(
layout.referencedMenuName,
);
final linkToPageMenu = find.byType(LinkToPageMenu);
expect(linkToPageMenu, findsOneWidget);
final referencedDatabase = find.descendant(
of: linkToPageMenu,
matching: find.findTextInFlowyText(name),
);
expect(referencedDatabase, findsOneWidget);
await tester.tapButton(referencedDatabase);
}

View File

@ -5,6 +5,7 @@ import 'document_test.dart' as document_test;
import 'cover_image_test.dart' as cover_image_test;
import 'share_markdown_test.dart' as share_markdown_test;
import 'import_files_test.dart' as import_files_test;
import 'document_with_database_test.dart' as document_with_database_test;
/// The main task runner for all integration tests in AppFlowy.
///
@ -20,6 +21,7 @@ void main() {
document_test.main();
share_markdown_test.main();
import_files_test.main();
document_with_database_test.main();
// board_test.main();
// empty_document_test.main();
// smart_menu_test.main();

View File

@ -126,3 +126,11 @@ extension AppFlowyTestBase on WidgetTester {
return;
}
}
extension AppFlowyFinderTestBase on CommonFinders {
Finder findTextInFlowyText(String text) {
return find.byWidgetPredicate(
(widget) => widget is FlowyText && widget.title == text,
);
}
}

View File

@ -7,7 +7,7 @@ import 'package:appflowy/user/presentation/skip_log_in_screen.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/header/add_button.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/section/item.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_language_view.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/widget/buttons/primary_button.dart';
import 'package:flutter/material.dart';
@ -112,20 +112,32 @@ extension CommonOperations on WidgetTester {
Future<void> hoverOnWidget(
Finder finder, {
Offset? offset,
Future<void> Function()? onHover,
}) async {
try {
final gesture = await createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer(location: Offset.zero);
addTearDown(gesture.removePointer);
await pump();
await gesture.moveTo(offset ?? getCenter(finder));
await pumpAndSettle();
} catch (_) {}
await onHover?.call();
await gesture.removePointer();
} catch (err) {
Log.error('hoverOnWidget error: $err');
}
}
/// Hover on the page name.
Future<void> hoverOnPageName(String name) async {
await hoverOnWidget(findPageName(name));
Future<void> hoverOnPageName(
String name, {
Future<void> Function()? onHover,
bool useLast = true,
}) async {
if (useLast) {
await hoverOnWidget(findPageName(name).last, onHover: onHover);
} else {
await hoverOnWidget(findPageName(name).first, onHover: onHover);
}
}
/// Tap the ... button beside the page name.
@ -137,24 +149,18 @@ extension CommonOperations on WidgetTester {
}
/// Tap the delete page button.
///
/// Must call [tapPageOptionButton] first.
Future<void> tapDeletePageButton() async {
await tapPageOptionButton();
await tapButtonWithName(ViewDisclosureAction.delete.name);
}
/// Tap the rename page button.
///
/// Must call [tapPageOptionButton] first.
Future<void> tapRenamePageButton() async {
await tapPageOptionButton();
await tapButtonWithName(ViewDisclosureAction.rename.name);
}
/// Rename the page.
///
/// Must call [tapPageOptionButton] first.
Future<void> renamePage(String name) async {
await tapRenamePageButton();
await enterText(find.byType(TextFormField), name);
@ -208,14 +214,50 @@ extension CommonOperations on WidgetTester {
await tapButton(markdownButton);
}
/// Hover on cover plugin button above the document
Future<void> hoverOnCoverPluginAddButton() async {
final editor = find.byWidgetPredicate(
(widget) => widget is AppFlowyEditor,
);
await hoverOnWidget(
editor,
offset: getTopLeft(editor).translate(20, 20),
Future<void> createNewPageWithName(ViewLayoutPB layout, String name) async {
// create a new page
await tapAddButton();
await tapButtonWithName(layout.menuName);
await pumpAndSettle();
// hover on it and change it's name
await hoverOnPageName(
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
onHover: () async {
await renamePage(name);
await pumpAndSettle();
},
);
await pumpAndSettle();
}
}
extension ViewLayoutPBTest on ViewLayoutPB {
String get menuName {
switch (this) {
case ViewLayoutPB.Grid:
return LocaleKeys.grid_menuName.tr();
case ViewLayoutPB.Board:
return LocaleKeys.board_menuName.tr();
case ViewLayoutPB.Document:
return LocaleKeys.document_menuName.tr();
case ViewLayoutPB.Calendar:
return LocaleKeys.calendar_menuName.tr();
default:
throw UnsupportedError('Unsupported layout: $this');
}
}
String get referencedMenuName {
switch (this) {
case ViewLayoutPB.Grid:
return LocaleKeys.document_plugins_referencedGrid.tr();
case ViewLayoutPB.Board:
return LocaleKeys.document_plugins_referencedBoard.tr();
case ViewLayoutPB.Calendar:
return LocaleKeys.document_plugins_referencedCalendar.tr();
default:
throw UnsupportedError('Unsupported layout: $this');
}
}
}

View File

@ -0,0 +1,45 @@
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:flutter_test/flutter_test.dart';
import 'ime.dart';
import 'util.dart';
extension EditorWidgetTester on WidgetTester {
EditorOperations get editor => EditorOperations(this);
}
class EditorOperations {
const EditorOperations(this.tester);
final WidgetTester tester;
/// Tap the line of editor at [index]
Future<void> tapLineOfEditorAt(int index) async {
final textBlocks = find.byType(TextBlockComponentWidget);
await tester.tapAt(tester.getTopRight(textBlocks.at(index)));
}
/// Hover on cover plugin button above the document
Future<void> hoverOnCoverPluginAddButton() async {
final editor = find.byWidgetPredicate(
(widget) => widget is AppFlowyEditor,
);
await tester.hoverOnWidget(
editor,
offset: tester.getTopLeft(editor).translate(20, 20),
);
}
/// trigger the slash command (selection menu)
Future<void> showSlashMenu() async {
await tester.ime.insertCharacter('/');
}
/// Tap the slash menu item with [name]
///
/// Must call [showSlashMenu] first.
Future<void> tapSlashMenuItemWithName(String name) async {
final slashMenuItem = find.text(name, findRichText: true);
await tester.tapButton(slashMenuItem);
}
}

View File

@ -3,3 +3,4 @@ export 'common_operations.dart';
export 'settings.dart';
export 'data.dart';
export 'expectation.dart';
export 'editor_test_operations.dart';

View File

@ -1,6 +1,7 @@
import 'package:appflowy/plugins/database_view/grid/application/row/row_document_bloc.dart';
import 'package:appflowy/plugins/document/application/doc_bloc.dart';
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
@ -102,11 +103,18 @@ class _RowEditorState extends State<RowEditor> {
return const SizedBox.shrink();
}
return IntrinsicHeight(
child: AppFlowyEditorPage(
shrinkWrap: true,
autoFocus: false,
editorState: editorState,
scrollController: widget.scrollController,
child: Container(
constraints: const BoxConstraints(minHeight: 300),
child: AppFlowyEditorPage(
shrinkWrap: true,
autoFocus: false,
editorState: editorState,
scrollController: widget.scrollController,
styleCustomizer: EditorStyleCustomizer(
context: context,
padding: const EdgeInsets.symmetric(horizontal: 10),
),
),
),
);
},

View File

@ -5,6 +5,7 @@ import 'package:appflowy/plugins/document/application/doc_bloc.dart';
import 'package:appflowy/plugins/document/presentation/banner.dart';
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
import 'package:appflowy/plugins/document/presentation/export_page_widget.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/util/base64_string.dart';
@ -90,6 +91,10 @@ class _DocumentPageState extends State<DocumentPage> {
Widget _buildEditorPage(BuildContext context, DocumentState state) {
final appflowyEditorPage = AppFlowyEditorPage(
editorState: editorState!,
styleCustomizer: EditorStyleCustomizer(
context: context,
padding: const EdgeInsets.symmetric(horizontal: 50),
),
header: _buildCoverAndIcon(context),
);
return Column(

View File

@ -17,6 +17,7 @@ class AppFlowyEditorPage extends StatefulWidget {
this.shrinkWrap = false,
this.scrollController,
this.autoFocus,
required this.styleCustomizer,
});
final Widget? header;
@ -24,6 +25,7 @@ class AppFlowyEditorPage extends StatefulWidget {
final ScrollController? scrollController;
final bool shrinkWrap;
final bool? autoFocus;
final EditorStyleCustomizer styleCustomizer;
@override
State<AppFlowyEditorPage> createState() => _AppFlowyEditorPageState();
@ -91,9 +93,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
style: styleCustomizer.selectionMenuStyleBuilder(),
).handler;
EditorStyleCustomizer get styleCustomizer => EditorStyleCustomizer(
context: context,
);
EditorStyleCustomizer get styleCustomizer => widget.styleCustomizer;
DocumentBloc get documentBloc => context.read<DocumentBloc>();
@override

View File

@ -23,11 +23,13 @@ void showLinkToPageMenu(
final top = alignment == Alignment.bottomLeft ? offset.dy : null;
final bottom = alignment == Alignment.topLeft ? offset.dy : null;
keepEditorFocusNotifier.value += 1;
late OverlayEntry linkToPageMenuEntry;
linkToPageMenuEntry = FullScreenOverlayEntry(
top: top,
bottom: bottom,
left: offset.dx,
dismissCallback: () => keepEditorFocusNotifier.value -= 1,
builder: (context) => Material(
color: Colors.transparent,
child: LinkToPageMenu(

View File

@ -4,7 +4,6 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/cover/change_cover_popover.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/cover/emoji_popover.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/cover/emoji_icon_widget.dart';
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
import 'package:appflowy/workspace/presentation/widgets/emoji_picker/emoji_picker.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide FlowySvg;
import 'package:appflowy_popover/appflowy_popover.dart';
@ -164,8 +163,8 @@ class _AddCoverButtonState extends State<_AddCoverButton> {
height: widget.hasIcon ? 180 : 50.0,
alignment: Alignment.bottomLeft,
width: double.infinity,
padding: EdgeInsets.only(
left: EditorStyleCustomizer.horizontalPadding + 30,
padding: const EdgeInsets.only(
left: 80,
top: 20,
bottom: 5,
),
@ -333,7 +332,7 @@ class _CoverImageState extends State<_CoverImage> {
),
hasIcon
? Positioned(
left: EditorStyleCustomizer.horizontalPadding + 30,
left: 80,
bottom: !hasCover ? 30 : 40,
child: AppFlowyPopover(
offset: const Offset(100, 0),
@ -416,7 +415,7 @@ class _CoverImageState extends State<_CoverImage> {
Widget _buildCoverOverlayButtons(BuildContext context) {
return Positioned(
bottom: 20,
right: EditorStyleCustomizer.horizontalPadding,
right: 50,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [

View File

@ -35,10 +35,12 @@ void showEmojiPickerMenu(
final top = alignment == Alignment.bottomLeft ? offset.dy : null;
final bottom = alignment == Alignment.topLeft ? offset.dy : null;
keepEditorFocusNotifier.value += 1;
final emojiPickerMenuEntry = FullScreenOverlayEntry(
top: top,
bottom: bottom,
left: offset.dx,
dismissCallback: () => keepEditorFocusNotifier.value -= 1,
builder: (context) => Material(
child: Container(
width: 300,

View File

@ -8,12 +8,11 @@ import 'package:google_fonts/google_fonts.dart';
class EditorStyleCustomizer {
EditorStyleCustomizer({
required this.context,
required this.padding,
});
static double get horizontalPadding =>
PlatformExtension.isDesktop ? 50.0 : 10.0;
final BuildContext context;
final EdgeInsets padding;
EditorStyle style() {
if (PlatformExtension.isDesktopOrWeb) {
@ -28,7 +27,7 @@ class EditorStyleCustomizer {
final theme = Theme.of(context);
final fontSize = context.read<DocumentAppearanceCubit>().state.fontSize;
return EditorStyle.desktop(
padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
padding: padding,
backgroundColor: theme.colorScheme.surface,
cursorColor: theme.colorScheme.primary,
textStyleConfiguration: TextStyleConfiguration(
@ -65,7 +64,7 @@ class EditorStyleCustomizer {
final theme = Theme.of(context);
final fontSize = context.read<DocumentAppearanceCubit>().state.fontSize;
return EditorStyle.desktop(
padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
padding: padding,
backgroundColor: theme.colorScheme.surface,
cursorColor: theme.colorScheme.primary,
textStyleConfiguration: TextStyleConfiguration(

View File

@ -52,12 +52,11 @@ packages:
appflowy_editor:
dependency: "direct main"
description:
path: "."
ref: "23bc6d2"
resolved-ref: "23bc6d2f58ab7ab4ff21c507d53753de35094ec0"
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
source: git
version: "1.0.2"
name: appflowy_editor
sha256: "19c2567e23bbd8894243b2e57fa8436e3192c8dcb50c23499b6aea90a674a045"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
appflowy_popover:
dependency: "direct main"
description:

View File

@ -42,11 +42,11 @@ dependencies:
git:
url: https://github.com/AppFlowy-IO/appflowy-board.git
ref: a183c57
# appflowy_editor: ^1.0.2
appflowy_editor:
git:
url: https://github.com/AppFlowy-IO/appflowy-editor.git
ref: 23bc6d2
appflowy_editor: ^1.0.3
# appflowy_editor:
# git:
# url: https://github.com/AppFlowy-IO/appflowy-editor.git
# ref: d2460c9
appflowy_popover:
path: packages/appflowy_popover