Forráskód Böngészése

test: implement simple redo/undo test for no-styled text

Lucas.Xu 2 éve
szülő
commit
e926c89548

+ 11 - 23
frontend/app_flowy/packages/flowy_editor/lib/src/render/selection/toolbar_widget.dart

@@ -28,10 +28,12 @@ List<String> defaultListToolbarEventNames = [
   'H1',
   'H2',
   'H3',
-  // 'B-List',
-  // 'N-List',
 ];
 
+mixin ToolBarMixin<T extends StatefulWidget> on State<T> {
+  void hide();
+}
+
 class ToolbarWidget extends StatefulWidget {
   const ToolbarWidget({
     Key? key,
@@ -50,7 +52,7 @@ class ToolbarWidget extends StatefulWidget {
   State<ToolbarWidget> createState() => _ToolbarWidgetState();
 }
 
-class _ToolbarWidgetState extends State<ToolbarWidget> {
+class _ToolbarWidgetState extends State<ToolbarWidget> with ToolBarMixin {
   final GlobalKey _listToolbarKey = GlobalKey();
 
   final toolbarHeight = 32.0;
@@ -63,21 +65,6 @@ class _ToolbarWidgetState extends State<ToolbarWidget> {
 
   OverlayEntry? _listToolbarOverlay;
 
-  @override
-  void initState() {
-    super.initState();
-
-    widget.editorState.service.selectionService.currentSelection
-        .addListener(_onSelectionChange);
-  }
-
-  @override
-  void dispose() {
-    widget.editorState.service.selectionService.currentSelection
-        .removeListener(_onSelectionChange);
-    super.dispose();
-  }
-
   @override
   Widget build(BuildContext context) {
     return Positioned(
@@ -92,6 +79,12 @@ class _ToolbarWidgetState extends State<ToolbarWidget> {
     );
   }
 
+  @override
+  void hide() {
+    _listToolbarOverlay?.remove();
+    _listToolbarOverlay = null;
+  }
+
   Widget _buildToolbar(BuildContext context) {
     return Material(
       borderRadius: BorderRadius.circular(cornerRadius),
@@ -212,9 +205,4 @@ class _ToolbarWidgetState extends State<ToolbarWidget> {
     }
     assert(false, 'Could not find the event handler for $eventName');
   }
-
-  void _onSelectionChange() {
-    _listToolbarOverlay?.remove();
-    _listToolbarOverlay = null;
-  }
 }

+ 13 - 1
frontend/app_flowy/packages/flowy_editor/lib/src/service/toolbar_service.dart

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 
 import 'package:flowy_editor/flowy_editor.dart';
 import 'package:flowy_editor/src/render/selection/toolbar_widget.dart';
+import 'package:flowy_editor/src/extensions/object_extensions.dart';
 
 abstract class FlowyToolbarService {
   /// Show the toolbar widget beside the offset.
@@ -28,12 +29,15 @@ class FlowyToolbar extends StatefulWidget {
 class _FlowyToolbarState extends State<FlowyToolbar>
     implements FlowyToolbarService {
   OverlayEntry? _toolbarOverlay;
+  final _toolbarWidgetKey = GlobalKey(debugLabel: '_toolbar_widget');
 
   @override
   void showInOffset(Offset offset, LayerLink layerLink) {
-    _toolbarOverlay?.remove();
+    hide();
+
     _toolbarOverlay = OverlayEntry(
       builder: (context) => ToolbarWidget(
+        key: _toolbarWidgetKey,
         editorState: widget.editorState,
         layerLink: layerLink,
         offset: offset.translate(0, -37.0),
@@ -45,6 +49,7 @@ class _FlowyToolbarState extends State<FlowyToolbar>
 
   @override
   void hide() {
+    _toolbarWidgetKey.currentState?.unwrapOrNull<ToolBarMixin>()?.hide();
     _toolbarOverlay?.remove();
     _toolbarOverlay = null;
   }
@@ -55,4 +60,11 @@ class _FlowyToolbarState extends State<FlowyToolbar>
       child: widget.child,
     );
   }
+
+  @override
+  void dispose() {
+    hide();
+
+    super.dispose();
+  }
 }

+ 14 - 2
frontend/app_flowy/packages/flowy_editor/test/infra/test_editor.dart

@@ -72,8 +72,20 @@ class EditorWidgetTester {
     await tester.pumpAndSettle();
   }
 
-  Future<void> pressLogicKey(LogicalKeyboardKey key) async {
-    final testRawKeyEventData = TestRawKeyEventData(logicalKey: key).toKeyEvent;
+  Future<void> pressLogicKey(
+    LogicalKeyboardKey key, {
+    bool isControlPressed = false,
+    bool isShiftPressed = false,
+    bool isAltPressed = false,
+    bool isMetaPressed = false,
+  }) async {
+    final testRawKeyEventData = TestRawKeyEventData(
+      logicalKey: key,
+      isControlPressed: isControlPressed,
+      isShiftPressed: isShiftPressed,
+      isAltPressed: isAltPressed,
+      isMetaPressed: isMetaPressed,
+    ).toKeyEvent;
     _editorState.service.keyboardService!.onKey(testRawKeyEventData);
     await tester.pumpAndSettle();
   }

+ 29 - 2
frontend/app_flowy/packages/flowy_editor/test/infra/test_raw_key_event.dart

@@ -1,7 +1,25 @@
 import 'package:flutter/services.dart';
 
 class TestRawKeyEvent extends RawKeyDownEvent {
-  const TestRawKeyEvent({required super.data});
+  const TestRawKeyEvent({
+    required super.data,
+    this.isControlPressed = false,
+    this.isShiftPressed = false,
+    this.isAltPressed = false,
+    this.isMetaPressed = false,
+  });
+
+  @override
+  final bool isControlPressed;
+
+  @override
+  final bool isShiftPressed;
+
+  @override
+  final bool isAltPressed;
+
+  @override
+  final bool isMetaPressed;
 }
 
 class TestRawKeyEventData extends RawKeyEventData {
@@ -46,7 +64,13 @@ class TestRawKeyEventData extends RawKeyEventData {
   String get keyLabel => throw UnimplementedError();
 
   RawKeyEvent get toKeyEvent {
-    return TestRawKeyEvent(data: this);
+    return TestRawKeyEvent(
+      data: this,
+      isAltPressed: isAltPressed,
+      isControlPressed: isControlPressed,
+      isMetaPressed: isMetaPressed,
+      isShiftPressed: isShiftPressed,
+    );
   }
 }
 
@@ -67,6 +91,9 @@ extension on LogicalKeyboardKey {
     if (this == LogicalKeyboardKey.pageUp) {
       return PhysicalKeyboardKey.pageUp;
     }
+    if (this == LogicalKeyboardKey.keyZ) {
+      return PhysicalKeyboardKey.keyZ;
+    }
     throw UnimplementedError();
   }
 }

+ 60 - 0
frontend/app_flowy/packages/flowy_editor/test/service/internal_key_event_handlers/redo_undo_handler_test.dart

@@ -0,0 +1,60 @@
+import 'package:flowy_editor/flowy_editor.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('redo_undo_handler_test.dart', () {
+    // TODO: need to test more cases.
+    testWidgets('Redo, Undo for backspace key, and selection is downward',
+        (tester) async {
+      await _testBackspaceUndoRedo(tester, true);
+    });
+
+    testWidgets('Redo, Undo for backspace key, and selection is forward',
+        (tester) async {
+      await _testBackspaceUndoRedo(tester, false);
+    });
+  });
+}
+
+Future<void> _testBackspaceUndoRedo(
+    WidgetTester tester, bool isDownwardSelection) async {
+  const text = 'Welcome to Appflowy 😁';
+  final editor = tester.editor
+    ..insertTextNode(text)
+    ..insertTextNode(text)
+    ..insertTextNode(text);
+  await editor.startTesting();
+
+  final start = Position(path: [0], offset: text.length);
+  final end = Position(path: [1], offset: text.length);
+  final selection = Selection(
+    start: isDownwardSelection ? start : end,
+    end: isDownwardSelection ? end : start,
+  );
+  await editor.updateSelection(selection);
+  await editor.pressLogicKey(LogicalKeyboardKey.backspace);
+  expect(editor.documentLength, 2);
+
+  await editor.pressLogicKey(
+    LogicalKeyboardKey.keyZ,
+    isMetaPressed: true,
+  );
+
+  expect(editor.documentLength, 3);
+  expect((editor.nodeAtPath([1]) as TextNode).toRawString(), text);
+  expect(editor.documentSelection, selection);
+
+  await editor.pressLogicKey(
+    LogicalKeyboardKey.keyZ,
+    isMetaPressed: true,
+    isShiftPressed: true,
+  );
+
+  expect(editor.documentLength, 2);
+}