浏览代码

test: implement white_space_handler test

Lucas.Xu 2 年之前
父节点
当前提交
a7681f86e5

+ 7 - 0
frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart

@@ -9,6 +9,13 @@ import 'package:flowy_editor/src/operation/transaction_builder.dart';
 import 'package:flowy_editor/src/render/rich_text/rich_text_style.dart';
 import 'package:flowy_editor/src/service/keyboard_service.dart';
 
+@visibleForTesting
+List<String> get checkboxListSymbols => _checkboxListSymbols;
+@visibleForTesting
+List<String> get unCheckboxListSymbols => _unCheckboxListSymbols;
+@visibleForTesting
+List<String> get bulletedListSymbols => _bulletedListSymbols;
+
 const _bulletedListSymbols = ['*', '-'];
 const _checkboxListSymbols = ['[x]', '-[x]'];
 const _unCheckboxListSymbols = ['[]', '-[]'];

+ 8 - 4
frontend/app_flowy/packages/flowy_editor/lib/src/service/service.dart

@@ -1,7 +1,4 @@
-import 'package:flowy_editor/src/service/keyboard_service.dart';
-import 'package:flowy_editor/src/service/render_plugin_service.dart';
-import 'package:flowy_editor/src/service/scroll_service.dart';
-import 'package:flowy_editor/src/service/selection_service.dart';
+import 'package:flowy_editor/flowy_editor.dart';
 import 'package:flowy_editor/src/service/toolbar_service.dart';
 import 'package:flutter/material.dart';
 
@@ -26,6 +23,13 @@ class FlowyService {
 
   // input service
   final inputServiceKey = GlobalKey(debugLabel: 'flowy_input_service');
+  FlowyInputService? get inputService {
+    if (inputServiceKey.currentState != null &&
+        inputServiceKey.currentState is FlowyInputService) {
+      return inputServiceKey.currentState! as FlowyInputService;
+    }
+    return null;
+  }
 
   // render plugin service
   late FlowyRenderPlugin renderPluginService;

+ 25 - 0
frontend/app_flowy/packages/flowy_editor/test/infra/test_editor.dart

@@ -70,6 +70,31 @@ class EditorWidgetTester {
       _editorState.service.selectionService.updateSelection(selection);
     }
     await tester.pumpAndSettle();
+
+    expect(_editorState.service.selectionService.currentSelection.value,
+        selection);
+  }
+
+  Future<void> insertText(TextNode textNode, String text, int offset,
+      {Selection? selection}) async {
+    await apply([
+      TextEditingDeltaInsertion(
+        oldText: textNode.toRawString(),
+        textInserted: text,
+        insertionOffset: offset,
+        selection: selection != null
+            ? TextSelection(
+                baseOffset: selection.start.offset,
+                extentOffset: selection.end.offset)
+            : TextSelection.collapsed(offset: offset),
+        composing: TextRange.empty,
+      )
+    ]);
+  }
+
+  Future<void> apply(List<TextEditingDelta> deltas) async {
+    _editorState.service.inputService?.apply(deltas);
+    await tester.pumpAndSettle();
   }
 
   Future<void> pressLogicKey(

+ 3 - 0
frontend/app_flowy/packages/flowy_editor/test/infra/test_raw_key_event.dart

@@ -79,6 +79,9 @@ extension on LogicalKeyboardKey {
     if (this == LogicalKeyboardKey.enter) {
       return PhysicalKeyboardKey.enter;
     }
+    if (this == LogicalKeyboardKey.space) {
+      return PhysicalKeyboardKey.space;
+    }
     if (this == LogicalKeyboardKey.backspace) {
       return PhysicalKeyboardKey.backspace;
     }

+ 178 - 0
frontend/app_flowy/packages/flowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart

@@ -0,0 +1,178 @@
+import 'package:flowy_editor/flowy_editor.dart';
+import 'package:flowy_editor/src/render/rich_text/rich_text_style.dart';
+import 'package:flowy_editor/src/service/internal_key_event_handlers/whitespace_handler.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import '../../infra/test_editor.dart';
+
+void main() async {
+  setUpAll(() {
+    TestWidgetsFlutterBinding.ensureInitialized();
+  });
+
+  group('white_space_handler.dart', () {
+    // Before
+    //
+    // Welcome to Appflowy 😁
+    // Welcome to Appflowy 😁
+    // Welcome to Appflowy 😁
+    // Welcome to Appflowy 😁
+    // Welcome to Appflowy 😁
+    // Welcome to Appflowy 😁
+    //
+    // After
+    // [h1]Welcome to Appflowy 😁
+    // [h2]Welcome to Appflowy 😁
+    // [h3]Welcome to Appflowy 😁
+    // [h4]Welcome to Appflowy 😁
+    // [h5]Welcome to Appflowy 😁
+    // [h6]Welcome to Appflowy 😁
+    //
+    testWidgets('Presses whitespace key after #*', (tester) async {
+      const maxSignCount = 6;
+      const text = 'Welcome to Appflowy 😁';
+      final editor = tester.editor;
+      for (var i = 1; i <= maxSignCount; i++) {
+        editor.insertTextNode('${'#' * i}$text');
+      }
+      await editor.startTesting();
+
+      for (var i = 1; i <= maxSignCount; i++) {
+        await editor.updateSelection(
+          Selection.single(path: [i - 1], startOffset: i),
+        );
+        await editor.pressLogicKey(LogicalKeyboardKey.space);
+
+        final textNode = (editor.nodeAtPath([i - 1]) as TextNode);
+
+        expect(textNode.subtype, StyleKey.heading);
+        // StyleKey.h1 ~ StyleKey.h6
+        expect(textNode.attributes.heading, 'h$i');
+      }
+    });
+
+    // Before
+    //
+    // Welcome to Appflowy 😁
+    // Welcome to Appflowy 😁
+    // Welcome to Appflowy 😁
+    // Welcome to Appflowy 😁
+    // Welcome to Appflowy 😁
+    // Welcome to Appflowy 😁
+    //
+    // After
+    // [h1]##Welcome to Appflowy 😁
+    // [h2]##Welcome to Appflowy 😁
+    // [h3]##Welcome to Appflowy 😁
+    // [h4]##Welcome to Appflowy 😁
+    // [h5]##Welcome to Appflowy 😁
+    // [h6]##Welcome to Appflowy 😁
+    //
+    testWidgets('Presses whitespace key inside #*', (tester) async {
+      const maxSignCount = 6;
+      const text = 'Welcome to Appflowy 😁';
+      final editor = tester.editor;
+      for (var i = 1; i <= maxSignCount; i++) {
+        editor.insertTextNode('${'###' * i}$text');
+      }
+      await editor.startTesting();
+
+      for (var i = 1; i <= maxSignCount; i++) {
+        await editor.updateSelection(
+          Selection.single(path: [i - 1], startOffset: i),
+        );
+        await editor.pressLogicKey(LogicalKeyboardKey.space);
+
+        final textNode = (editor.nodeAtPath([i - 1]) as TextNode);
+
+        expect(textNode.subtype, StyleKey.heading);
+        // StyleKey.h1 ~ StyleKey.h6
+        expect(textNode.attributes.heading, 'h$i');
+        expect(textNode.toRawString().startsWith('##'), true);
+      }
+    });
+
+    // Before
+    //
+    // Welcome to Appflowy 😁
+    //
+    // After
+    // [h1 ~ h6]##Welcome to Appflowy 😁
+    //
+    testWidgets('Presses whitespace key in heading styled text',
+        (tester) async {
+      const text = 'Welcome to Appflowy 😁';
+      final editor = tester.editor..insertTextNode(text);
+
+      await editor.startTesting();
+
+      const maxSignCount = 6;
+      for (var i = 1; i <= maxSignCount; i++) {
+        await editor.updateSelection(
+          Selection.single(path: [0], startOffset: 0),
+        );
+
+        final textNode = (editor.nodeAtPath([0]) as TextNode);
+
+        await editor.insertText(textNode, '#' * i, 0);
+        await editor.pressLogicKey(LogicalKeyboardKey.space);
+
+        expect(textNode.subtype, StyleKey.heading);
+        // StyleKey.h2 ~ StyleKey.h6
+        expect(textNode.attributes.heading, 'h$i');
+      }
+    });
+
+    testWidgets('Presses whitespace key after (un)checkbox symbols',
+        (tester) async {
+      const text = 'Welcome to Appflowy 😁';
+      final editor = tester.editor..insertTextNode(text);
+      await editor.startTesting();
+
+      final textNode = editor.nodeAtPath([0]) as TextNode;
+      for (final symbol in unCheckboxListSymbols) {
+        await editor.updateSelection(
+          Selection.single(path: [0], startOffset: 0),
+        );
+        await editor.insertText(textNode, symbol, 0);
+        await editor.pressLogicKey(LogicalKeyboardKey.space);
+        expect(textNode.subtype, StyleKey.checkbox);
+        expect(textNode.attributes.check, false);
+      }
+    });
+
+    testWidgets('Presses whitespace key after checkbox symbols',
+        (tester) async {
+      const text = 'Welcome to Appflowy 😁';
+      final editor = tester.editor..insertTextNode(text);
+      await editor.startTesting();
+
+      final textNode = editor.nodeAtPath([0]) as TextNode;
+      for (final symbol in checkboxListSymbols) {
+        await editor.updateSelection(
+          Selection.single(path: [0], startOffset: 0),
+        );
+        await editor.insertText(textNode, symbol, 0);
+        await editor.pressLogicKey(LogicalKeyboardKey.space);
+        expect(textNode.subtype, StyleKey.checkbox);
+        expect(textNode.attributes.check, true);
+      }
+    });
+
+    testWidgets('Presses whitespace key after bulleted list', (tester) async {
+      const text = 'Welcome to Appflowy 😁';
+      final editor = tester.editor..insertTextNode(text);
+      await editor.startTesting();
+
+      final textNode = editor.nodeAtPath([0]) as TextNode;
+      for (final symbol in bulletedListSymbols) {
+        await editor.updateSelection(
+          Selection.single(path: [0], startOffset: 0),
+        );
+        await editor.insertText(textNode, symbol, 0);
+        await editor.pressLogicKey(LogicalKeyboardKey.space);
+        expect(textNode.subtype, StyleKey.bulletedList);
+      }
+    });
+  });
+}