diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text_style.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text_style.dart index 5140e70daf..a1fbfdbb57 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text_style.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text_style.dart @@ -49,6 +49,7 @@ class StyleKey { StyleKey.strikethrough, StyleKey.backgroundColor, StyleKey.href, + StyleKey.code, ]; static List globalStyleKeys = [ @@ -58,7 +59,6 @@ class StyleKey { StyleKey.bulletedList, StyleKey.numberList, StyleKey.quote, - StyleKey.code, ]; } diff --git a/frontend/app_flowy/packages/appflowy_editor/test/infra/test_editor.dart b/frontend/app_flowy/packages/appflowy_editor/test/infra/test_editor.dart index e3a5a7d0c5..7754785fd3 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/infra/test_editor.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/infra/test_editor.dart @@ -28,6 +28,9 @@ class EditorWidgetTester { home: Scaffold( body: AppFlowyEditor( editorState: _editorState, + editorStyle: const EditorStyle( + padding: EdgeInsets.symmetric(vertical: 30), + ), ), ), ), diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart new file mode 100644 index 0000000000..441cc6743b --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart @@ -0,0 +1,331 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; +import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; +import 'package:appflowy_editor/src/render/toolbar/toolbar_item_widget.dart'; +import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart'; +import 'package:flutter_test/flutter_test.dart'; +import '../../infra/test_editor.dart'; + +void main() async { + setUpAll(() { + TestWidgetsFlutterBinding.ensureInitialized(); + }); + + const singleLineText = "One Line Of Text"; + + group('toolbar, heading', (() { + testWidgets('Select Text, Click toolbar and set style for h1 heading', + (tester) async { + final editor = tester.editor..insertTextNode(singleLineText); + await editor.startTesting(); + + final h1 = Selection( + start: Position(path: [0], offset: 0), + end: Position(path: [0], offset: singleLineText.length)); + + await editor.updateSelection(h1); + + expect(find.byType(ToolbarWidget), findsOneWidget); + + final h1Button = find.byWidgetPredicate((widget) { + if (widget is ToolbarItemWidget) { + return widget.item.id == 'appflowy.toolbar.h1'; + } + return false; + }); + + expect(h1Button, findsOneWidget); + await tester.tap(h1Button); + await tester.pumpAndSettle(); + + final node = editor.nodeAtPath([0]) as TextNode; + expect(node.attributes.heading, 'h1'); + }); + + testWidgets('Select Text, Click toolbar and set style for h2 heading', + (tester) async { + final editor = tester.editor..insertTextNode(singleLineText); + await editor.startTesting(); + + final h2 = Selection( + start: Position(path: [0], offset: 0), + end: Position(path: [0], offset: singleLineText.length)); + + await editor.updateSelection(h2); + expect(find.byType(ToolbarWidget), findsOneWidget); + + final h2Button = find.byWidgetPredicate((widget) { + if (widget is ToolbarItemWidget) { + return widget.item.id == 'appflowy.toolbar.h2'; + } + return false; + }); + expect(h2Button, findsOneWidget); + await tester.tap(h2Button); + await tester.pumpAndSettle(); + final node = editor.nodeAtPath([0]) as TextNode; + expect(node.attributes.heading, 'h2'); + }); + + testWidgets('Select Text, Click toolbar and set style for h3 heading', + (tester) async { + final editor = tester.editor..insertTextNode(singleLineText); + await editor.startTesting(); + + final h3 = Selection( + start: Position(path: [0], offset: 0), + end: Position(path: [0], offset: singleLineText.length)); + + await editor.updateSelection(h3); + expect(find.byType(ToolbarWidget), findsOneWidget); + + final h3Button = find.byWidgetPredicate((widget) { + if (widget is ToolbarItemWidget) { + return widget.item.id == 'appflowy.toolbar.h3'; + } + return false; + }); + expect(h3Button, findsOneWidget); + await tester.tap(h3Button); + await tester.pumpAndSettle(); + final node = editor.nodeAtPath([0]) as TextNode; + expect(node.attributes.heading, 'h3'); + }); + })); + + group('toolbar, underline', (() { + testWidgets('Select text, click toolbar and set style for underline', + (tester) async { + final editor = tester.editor..insertTextNode(singleLineText); + await editor.startTesting(); + + final underline = Selection( + start: Position(path: [0], offset: 0), + end: Position(path: [0], offset: singleLineText.length)); + + await editor.updateSelection(underline); + expect(find.byType(ToolbarWidget), findsOneWidget); + final underlineButton = find.byWidgetPredicate((widget) { + if (widget is ToolbarItemWidget) { + return widget.item.id == 'appflowy.toolbar.underline'; + } + return false; + }); + + expect(underlineButton, findsOneWidget); + await tester.tap(underlineButton); + await tester.pumpAndSettle(); + final node = editor.nodeAtPath([0]) as TextNode; + // expect(node.attributes.underline, true); + expect(node.allSatisfyUnderlineInSelection(underline), true); + }); + })); + + group('toolbar, bold', (() { + testWidgets('Select Text, Click Toolbar and set style for bold', + (tester) async { + final editor = tester.editor..insertTextNode(singleLineText); + await editor.startTesting(); + + final bold = Selection( + start: Position(path: [0], offset: 0), + end: Position(path: [0], offset: singleLineText.length)); + + await editor.updateSelection(bold); + expect(find.byType(ToolbarWidget), findsOneWidget); + final boldButton = find.byWidgetPredicate((widget) { + if (widget is ToolbarItemWidget) { + return widget.item.id == 'appflowy.toolbar.bold'; + } + return false; + }); + + expect(boldButton, findsOneWidget); + await tester.tap(boldButton); + await tester.pumpAndSettle(); + final node = editor.nodeAtPath([0]) as TextNode; + expect(node.allSatisfyBoldInSelection(bold), true); + }); + })); + + group('toolbar, italic', (() { + testWidgets('Select Text, Click Toolbar and set style for italic', + (tester) async { + final editor = tester.editor..insertTextNode(singleLineText); + await editor.startTesting(); + + final italic = Selection( + start: Position(path: [0], offset: 0), + end: Position(path: [0], offset: singleLineText.length)); + + await editor.updateSelection(italic); + expect(find.byType(ToolbarWidget), findsOneWidget); + final italicButton = find.byWidgetPredicate((widget) { + if (widget is ToolbarItemWidget) { + return widget.item.id == 'appflowy.toolbar.italic'; + } + return false; + }); + + expect(italicButton, findsOneWidget); + await tester.tap(italicButton); + await tester.pumpAndSettle(); + final node = editor.nodeAtPath([0]) as TextNode; + expect(node.allSatisfyItalicInSelection(italic), true); + }); + })); + + group('toolbar, strikethrough', (() { + testWidgets('Select Text, Click Toolbar and set style for strikethrough', + (tester) async { + final editor = tester.editor..insertTextNode(singleLineText); + await editor.startTesting(); + + final strikeThrough = Selection( + start: Position(path: [0], offset: 0), + end: Position(path: [0], offset: singleLineText.length)); + + await editor.updateSelection(strikeThrough); + + expect(find.byType(ToolbarWidget), findsOneWidget); + final strikeThroughButton = find.byWidgetPredicate((widget) { + if (widget is ToolbarItemWidget) { + return widget.item.id == 'appflowy.toolbar.strikethrough'; + } + return false; + }); + + expect(strikeThroughButton, findsOneWidget); + await tester.tap(strikeThroughButton); + await tester.pumpAndSettle(); + final node = editor.nodeAtPath([0]) as TextNode; + expect(node.allSatisfyStrikethroughInSelection(strikeThrough), true); + }); + })); + + group('toolbar, code', (() { + testWidgets('Select Text, Click Toolbar and set style for code', + (tester) async { + final editor = tester.editor..insertTextNode(singleLineText); + await editor.startTesting(); + + final code = Selection( + start: Position(path: [0], offset: 0), + end: Position(path: [0], offset: singleLineText.length)); + + await editor.updateSelection(code); + expect(find.byType(ToolbarWidget), findsOneWidget); + final codeButton = find.byWidgetPredicate((widget) { + if (widget is ToolbarItemWidget) { + return widget.item.id == 'appflowy.toolbar.code'; + } + return false; + }); + + expect(codeButton, findsOneWidget); + await tester.tap(codeButton); + await tester.pumpAndSettle(); + final node = editor.nodeAtPath([0]) as TextNode; + expect( + node.allSatisfyInSelection( + code, + StyleKey.code, + (value) { + return value == true; + }, + ), + true, + ); + }); + })); + + group('toolbar, quote', (() { + testWidgets('Select Text, Click Toolbar and set style for quote', + (tester) async { + final editor = tester.editor..insertTextNode(singleLineText); + await editor.startTesting(); + + final quote = Selection( + start: Position(path: [0], offset: 0), + end: Position(path: [0], offset: singleLineText.length)); + + await editor.updateSelection(quote); + expect(find.byType(ToolbarWidget), findsOneWidget); + final quoteButton = find.byWidgetPredicate((widget) { + if (widget is ToolbarItemWidget) { + return widget.item.id == 'appflowy.toolbar.quote'; + } + return false; + }); + expect(quoteButton, findsOneWidget); + await tester.tap(quoteButton); + await tester.pumpAndSettle(); + final node = editor.nodeAtPath([0]) as TextNode; + expect(node.subtype, 'quote'); + }); + })); + + group('toolbar, bullet list', (() { + testWidgets('Select Text, Click Toolbar and set style for bullet', + (tester) async { + final editor = tester.editor..insertTextNode(singleLineText); + await editor.startTesting(); + + final bulletList = Selection( + start: Position(path: [0], offset: 0), + end: Position(path: [0], offset: singleLineText.length)); + + await editor.updateSelection(bulletList); + expect(find.byType(ToolbarWidget), findsOneWidget); + final bulletListButton = find.byWidgetPredicate((widget) { + if (widget is ToolbarItemWidget) { + return widget.item.id == 'appflowy.toolbar.bulleted_list'; + } + return false; + }); + + expect(bulletListButton, findsOneWidget); + await tester.tap(bulletListButton); + await tester.pumpAndSettle(); + final node = editor.nodeAtPath([0]) as TextNode; + expect(node.subtype, 'bulleted-list'); + }); + })); + + group('toolbar, highlight', (() { + testWidgets('Select Text, Click Toolbar and set style for highlighted text', + (tester) async { + // FIXME: Use a const value instead of the magic string. + const blue = '0x6000BCF0'; + final editor = tester.editor..insertTextNode(singleLineText); + await editor.startTesting(); + + final node = editor.nodeAtPath([0]) as TextNode; + final selection = Selection( + start: Position(path: [0], offset: 0), + end: Position(path: [0], offset: singleLineText.length)); + + await editor.updateSelection(selection); + expect(find.byType(ToolbarWidget), findsOneWidget); + final highlightButton = find.byWidgetPredicate((widget) { + if (widget is ToolbarItemWidget) { + return widget.item.id == 'appflowy.toolbar.highlight'; + } + return false; + }); + expect(highlightButton, findsOneWidget); + await tester.tap(highlightButton); + await tester.pumpAndSettle(); + expect( + node.allSatisfyInSelection( + selection, + StyleKey.backgroundColor, + (value) { + return value == blue; + }, + ), + true, + ); + }); + })); +} diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/page_up_down_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/page_up_down_handler_test.dart index eb221ddf2c..465d198e8e 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/page_up_down_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/page_up_down_handler_test.dart @@ -40,7 +40,11 @@ void main() async { await editor.pressLogicKey( LogicalKeyboardKey.pageDown, ); - currentOffsetY += onePageHeight!; + if (i == page) { + currentOffsetY = scrollService.maxScrollExtent; + } else { + currentOffsetY += onePageHeight!; + } final dy = scrollService.dy; expect(dy, currentOffsetY); } @@ -58,7 +62,11 @@ void main() async { await editor.pressLogicKey( LogicalKeyboardKey.pageUp, ); - currentOffsetY -= onePageHeight!; + if (i == 1) { + currentOffsetY = scrollService.minScrollExtent; + } else { + currentOffsetY -= onePageHeight!; + } final dy = editor.editorState.service.scrollService?.dy; expect(dy, currentOffsetY); } diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart index 23759e449c..060e730af5 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart @@ -83,6 +83,8 @@ void main() async { key = 'highlight'; } else if (styleKey == StyleKey.href) { key = 'link'; + } else { + continue; } final itemWidget = _itemWidgetForId(tester, 'appflowy.toolbar.$key'); expect(itemWidget.isHighlight, expectedValue);