Skip to content

Commit d5884ad

Browse files
authored
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
1 parent 95f8b2e commit d5884ad

File tree

16 files changed

+271
-49
lines changed

16 files changed

+271
-49
lines changed

frontend/appflowy_flutter/integration_test/cover_image_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ void main() {
2828
await tester.initializeAppFlowy();
2929

3030
await tester.tapGoButton();
31-
await tester.hoverOnCoverPluginAddButton();
31+
await tester.editor.hoverOnCoverPluginAddButton();
3232

3333
tester.expectToSeePluginAddCoverAndIconButton();
3434
});
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import 'package:appflowy/plugins/database_view/board/presentation/board_page.dart';
2+
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
3+
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart';
4+
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
5+
import 'package:appflowy_editor/appflowy_editor.dart';
6+
import 'package:flowy_infra/uuid.dart';
7+
import 'package:flutter_test/flutter_test.dart';
8+
import 'package:integration_test/integration_test.dart';
9+
10+
import 'util/util.dart';
11+
12+
void main() {
13+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
14+
15+
group('database view in document', () {
16+
const location = 'database_view';
17+
18+
setUp(() async {
19+
await TestFolder.cleanTestLocation(location);
20+
await TestFolder.setTestLocation(location);
21+
});
22+
23+
tearDown(() async {
24+
await TestFolder.cleanTestLocation(null);
25+
});
26+
27+
testWidgets('insert a referenced grid', (tester) async {
28+
await tester.initializeAppFlowy();
29+
await tester.tapGoButton();
30+
31+
await insertReferenceDatabase(tester, ViewLayoutPB.Grid);
32+
33+
// validate the referenced grid is inserted
34+
expect(
35+
find.descendant(
36+
of: find.byType(AppFlowyEditor),
37+
matching: find.byType(GridPage),
38+
),
39+
findsOneWidget,
40+
);
41+
});
42+
43+
testWidgets('insert a referenced board', (tester) async {
44+
await tester.initializeAppFlowy();
45+
await tester.tapGoButton();
46+
47+
await insertReferenceDatabase(tester, ViewLayoutPB.Board);
48+
49+
// validate the referenced board is inserted
50+
expect(
51+
find.descendant(
52+
of: find.byType(AppFlowyEditor),
53+
matching: find.byType(BoardPage),
54+
),
55+
findsOneWidget,
56+
);
57+
});
58+
59+
// testWidgets('insert a referenced calendar', (tester) async {
60+
// await tester.initializeAppFlowy();
61+
// await tester.tapGoButton();
62+
63+
// await insertReferenceDatabase(tester, ViewLayoutPB.Calendar);
64+
65+
// // validate the referenced grid is inserted
66+
// expect(
67+
// find.descendant(
68+
// of: find.byType(AppFlowyEditor),
69+
// matching: find.byType(CalendarPage),
70+
// ),
71+
// findsOneWidget,
72+
// );
73+
// });
74+
});
75+
}
76+
77+
/// Insert a referenced database of [layout] into the document
78+
Future<void> insertReferenceDatabase(
79+
WidgetTester tester,
80+
ViewLayoutPB layout,
81+
) async {
82+
// create a new grid
83+
final id = uuid();
84+
final name = '${layout.name}_$id';
85+
await tester.createNewPageWithName(
86+
layout,
87+
name,
88+
);
89+
// create a new document
90+
await tester.createNewPageWithName(
91+
ViewLayoutPB.Document,
92+
'insert_a_reference_${layout.name}',
93+
);
94+
// tap the first line of the document
95+
await tester.editor.tapLineOfEditorAt(0);
96+
// insert a referenced grid
97+
await tester.editor.showSlashMenu();
98+
await tester.editor.tapSlashMenuItemWithName(
99+
layout.referencedMenuName,
100+
);
101+
102+
final linkToPageMenu = find.byType(LinkToPageMenu);
103+
expect(linkToPageMenu, findsOneWidget);
104+
final referencedDatabase = find.descendant(
105+
of: linkToPageMenu,
106+
matching: find.findTextInFlowyText(name),
107+
);
108+
expect(referencedDatabase, findsOneWidget);
109+
await tester.tapButton(referencedDatabase);
110+
}

frontend/appflowy_flutter/integration_test/runner.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'document_test.dart' as document_test;
55
import 'cover_image_test.dart' as cover_image_test;
66
import 'share_markdown_test.dart' as share_markdown_test;
77
import 'import_files_test.dart' as import_files_test;
8+
import 'document_with_database_test.dart' as document_with_database_test;
89

910
/// The main task runner for all integration tests in AppFlowy.
1011
///
@@ -20,6 +21,7 @@ void main() {
2021
document_test.main();
2122
share_markdown_test.main();
2223
import_files_test.main();
24+
document_with_database_test.main();
2325
// board_test.main();
2426
// empty_document_test.main();
2527
// smart_menu_test.main();

frontend/appflowy_flutter/integration_test/util/base.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,11 @@ extension AppFlowyTestBase on WidgetTester {
126126
return;
127127
}
128128
}
129+
130+
extension AppFlowyFinderTestBase on CommonFinders {
131+
Finder findTextInFlowyText(String text) {
132+
return find.byWidgetPredicate(
133+
(widget) => widget is FlowyText && widget.title == text,
134+
);
135+
}
136+
}

frontend/appflowy_flutter/integration_test/util/common_operations.dart

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import 'package:appflowy/user/presentation/skip_log_in_screen.dart';
77
import 'package:appflowy/workspace/presentation/home/menu/app/header/add_button.dart';
88
import 'package:appflowy/workspace/presentation/home/menu/app/section/item.dart';
99
import 'package:appflowy/workspace/presentation/settings/widgets/settings_language_view.dart';
10-
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
10+
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
1111
import 'package:easy_localization/easy_localization.dart';
1212
import 'package:flowy_infra_ui/widget/buttons/primary_button.dart';
1313
import 'package:flutter/material.dart';
@@ -112,20 +112,32 @@ extension CommonOperations on WidgetTester {
112112
Future<void> hoverOnWidget(
113113
Finder finder, {
114114
Offset? offset,
115+
Future<void> Function()? onHover,
115116
}) async {
116117
try {
117118
final gesture = await createGesture(kind: PointerDeviceKind.mouse);
118119
await gesture.addPointer(location: Offset.zero);
119-
addTearDown(gesture.removePointer);
120120
await pump();
121121
await gesture.moveTo(offset ?? getCenter(finder));
122122
await pumpAndSettle();
123-
} catch (_) {}
123+
await onHover?.call();
124+
await gesture.removePointer();
125+
} catch (err) {
126+
Log.error('hoverOnWidget error: $err');
127+
}
124128
}
125129

126130
/// Hover on the page name.
127-
Future<void> hoverOnPageName(String name) async {
128-
await hoverOnWidget(findPageName(name));
131+
Future<void> hoverOnPageName(
132+
String name, {
133+
Future<void> Function()? onHover,
134+
bool useLast = true,
135+
}) async {
136+
if (useLast) {
137+
await hoverOnWidget(findPageName(name).last, onHover: onHover);
138+
} else {
139+
await hoverOnWidget(findPageName(name).first, onHover: onHover);
140+
}
129141
}
130142

131143
/// Tap the ... button beside the page name.
@@ -137,24 +149,18 @@ extension CommonOperations on WidgetTester {
137149
}
138150

139151
/// Tap the delete page button.
140-
///
141-
/// Must call [tapPageOptionButton] first.
142152
Future<void> tapDeletePageButton() async {
143153
await tapPageOptionButton();
144154
await tapButtonWithName(ViewDisclosureAction.delete.name);
145155
}
146156

147157
/// Tap the rename page button.
148-
///
149-
/// Must call [tapPageOptionButton] first.
150158
Future<void> tapRenamePageButton() async {
151159
await tapPageOptionButton();
152160
await tapButtonWithName(ViewDisclosureAction.rename.name);
153161
}
154162

155163
/// Rename the page.
156-
///
157-
/// Must call [tapPageOptionButton] first.
158164
Future<void> renamePage(String name) async {
159165
await tapRenamePageButton();
160166
await enterText(find.byType(TextFormField), name);
@@ -208,14 +214,50 @@ extension CommonOperations on WidgetTester {
208214
await tapButton(markdownButton);
209215
}
210216

211-
/// Hover on cover plugin button above the document
212-
Future<void> hoverOnCoverPluginAddButton() async {
213-
final editor = find.byWidgetPredicate(
214-
(widget) => widget is AppFlowyEditor,
215-
);
216-
await hoverOnWidget(
217-
editor,
218-
offset: getTopLeft(editor).translate(20, 20),
217+
Future<void> createNewPageWithName(ViewLayoutPB layout, String name) async {
218+
// create a new page
219+
await tapAddButton();
220+
await tapButtonWithName(layout.menuName);
221+
await pumpAndSettle();
222+
223+
// hover on it and change it's name
224+
await hoverOnPageName(
225+
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
226+
onHover: () async {
227+
await renamePage(name);
228+
await pumpAndSettle();
229+
},
219230
);
231+
await pumpAndSettle();
232+
}
233+
}
234+
235+
extension ViewLayoutPBTest on ViewLayoutPB {
236+
String get menuName {
237+
switch (this) {
238+
case ViewLayoutPB.Grid:
239+
return LocaleKeys.grid_menuName.tr();
240+
case ViewLayoutPB.Board:
241+
return LocaleKeys.board_menuName.tr();
242+
case ViewLayoutPB.Document:
243+
return LocaleKeys.document_menuName.tr();
244+
case ViewLayoutPB.Calendar:
245+
return LocaleKeys.calendar_menuName.tr();
246+
default:
247+
throw UnsupportedError('Unsupported layout: $this');
248+
}
249+
}
250+
251+
String get referencedMenuName {
252+
switch (this) {
253+
case ViewLayoutPB.Grid:
254+
return LocaleKeys.document_plugins_referencedGrid.tr();
255+
case ViewLayoutPB.Board:
256+
return LocaleKeys.document_plugins_referencedBoard.tr();
257+
case ViewLayoutPB.Calendar:
258+
return LocaleKeys.document_plugins_referencedCalendar.tr();
259+
default:
260+
throw UnsupportedError('Unsupported layout: $this');
261+
}
220262
}
221263
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
2+
import 'package:flutter_test/flutter_test.dart';
3+
4+
import 'ime.dart';
5+
import 'util.dart';
6+
7+
extension EditorWidgetTester on WidgetTester {
8+
EditorOperations get editor => EditorOperations(this);
9+
}
10+
11+
class EditorOperations {
12+
const EditorOperations(this.tester);
13+
14+
final WidgetTester tester;
15+
16+
/// Tap the line of editor at [index]
17+
Future<void> tapLineOfEditorAt(int index) async {
18+
final textBlocks = find.byType(TextBlockComponentWidget);
19+
await tester.tapAt(tester.getTopRight(textBlocks.at(index)));
20+
}
21+
22+
/// Hover on cover plugin button above the document
23+
Future<void> hoverOnCoverPluginAddButton() async {
24+
final editor = find.byWidgetPredicate(
25+
(widget) => widget is AppFlowyEditor,
26+
);
27+
await tester.hoverOnWidget(
28+
editor,
29+
offset: tester.getTopLeft(editor).translate(20, 20),
30+
);
31+
}
32+
33+
/// trigger the slash command (selection menu)
34+
Future<void> showSlashMenu() async {
35+
await tester.ime.insertCharacter('/');
36+
}
37+
38+
/// Tap the slash menu item with [name]
39+
///
40+
/// Must call [showSlashMenu] first.
41+
Future<void> tapSlashMenuItemWithName(String name) async {
42+
final slashMenuItem = find.text(name, findRichText: true);
43+
await tester.tapButton(slashMenuItem);
44+
}
45+
}

frontend/appflowy_flutter/integration_test/util/util.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export 'common_operations.dart';
33
export 'settings.dart';
44
export 'data.dart';
55
export 'expectation.dart';
6+
export 'editor_test_operations.dart';

frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/row_document.dart

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:appflowy/plugins/database_view/grid/application/row/row_document_bloc.dart';
22
import 'package:appflowy/plugins/document/application/doc_bloc.dart';
33
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
4+
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
45
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
56
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
67
import 'package:flowy_infra_ui/widget/error_page.dart';
@@ -102,11 +103,18 @@ class _RowEditorState extends State<RowEditor> {
102103
return const SizedBox.shrink();
103104
}
104105
return IntrinsicHeight(
105-
child: AppFlowyEditorPage(
106-
shrinkWrap: true,
107-
autoFocus: false,
108-
editorState: editorState,
109-
scrollController: widget.scrollController,
106+
child: Container(
107+
constraints: const BoxConstraints(minHeight: 300),
108+
child: AppFlowyEditorPage(
109+
shrinkWrap: true,
110+
autoFocus: false,
111+
editorState: editorState,
112+
scrollController: widget.scrollController,
113+
styleCustomizer: EditorStyleCustomizer(
114+
context: context,
115+
padding: const EdgeInsets.symmetric(horizontal: 10),
116+
),
117+
),
110118
),
111119
);
112120
},

frontend/appflowy_flutter/lib/plugins/document/document_page.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:appflowy/plugins/document/application/doc_bloc.dart';
55
import 'package:appflowy/plugins/document/presentation/banner.dart';
66
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
77
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
8+
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
89
import 'package:appflowy/plugins/document/presentation/export_page_widget.dart';
910
import 'package:appflowy/startup/startup.dart';
1011
import 'package:appflowy/util/base64_string.dart';
@@ -90,6 +91,10 @@ class _DocumentPageState extends State<DocumentPage> {
9091
Widget _buildEditorPage(BuildContext context, DocumentState state) {
9192
final appflowyEditorPage = AppFlowyEditorPage(
9293
editorState: editorState!,
94+
styleCustomizer: EditorStyleCustomizer(
95+
context: context,
96+
padding: const EdgeInsets.symmetric(horizontal: 50),
97+
),
9398
header: _buildCoverAndIcon(context),
9499
);
95100
return Column(

0 commit comments

Comments
 (0)