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

test: implement delete key test for styled text

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

+ 0 - 0
frontend/app_flowy/packages/flowy_editor/coverage/lcov.info


+ 4 - 2
frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/delete_text_handler.dart

@@ -80,11 +80,13 @@ KeyEventResult _handleBackspace(EditorState editorState, RawKeyEvent event) {
 }
 
 KeyEventResult _handleDelete(EditorState editorState, RawKeyEvent event) {
-  final selection = editorState.service.selectionService.currentSelection.value;
+  var selection = editorState.service.selectionService.currentSelection.value;
   if (selection == null) {
     return KeyEventResult.ignored;
   }
-  final nodes = editorState.service.selectionService.currentSelectedNodes;
+  var nodes = editorState.service.selectionService.currentSelectedNodes;
+  nodes = selection.isBackward ? nodes : nodes.reversed.toList(growable: false);
+  selection = selection.isBackward ? selection : selection.reversed;
   // make sure all nodes is [TextNode].
   final textNodes = nodes.whereType<TextNode>().toList();
   if (textNodes.length != nodes.length) {

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

@@ -95,6 +95,15 @@ class EditorWidgetTester {
   }
 }
 
+extension TestString on String {
+  String safeSubString([int start = 0, int? end]) {
+    end ??= length - 1;
+    end = end.clamp(start, length - 1);
+    final sRunes = runes;
+    return String.fromCharCodes(sRunes, start, end);
+  }
+}
+
 extension TestEditorExtension on WidgetTester {
   EditorWidgetTester get editor =>
       EditorWidgetTester(tester: this)..initialize();

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

@@ -58,6 +58,9 @@ extension on LogicalKeyboardKey {
     if (this == LogicalKeyboardKey.backspace) {
       return PhysicalKeyboardKey.backspace;
     }
+    if (this == LogicalKeyboardKey.delete) {
+      return PhysicalKeyboardKey.delete;
+    }
     throw UnimplementedError();
   }
 }

+ 257 - 3
frontend/app_flowy/packages/flowy_editor/test/service/internal_key_event_handlers/delete_text_handler_test.dart

@@ -36,6 +36,87 @@ void main() async {
     });
   });
 
+  // Before
+  //
+  // Welcome to Appflowy 😁
+  // Welcome to Appflowy 😁
+  // Welcome to Appflowy 😁
+  //
+  // After
+  //
+  // Welcome to Appflowy 😁
+  // Welcome t Appflowy 😁
+  // Welcome Appflowy 😁
+  //
+  // Then
+  // Welcome to Appflowy 😁
+  //
+  testWidgets(
+      'Presses backspace key in non-empty document and selection is backward',
+      (tester) async {
+    await _deleteTextByBackspace(tester, true);
+  });
+  testWidgets(
+      'Presses backspace key in non-empty document and selection is forward',
+      (tester) async {
+    await _deleteTextByBackspace(tester, false);
+  });
+
+  // Before
+  //
+  // Welcome to Appflowy 😁
+  // Welcome to Appflowy 😁
+  // Welcome to Appflowy 😁
+  //
+  // After
+  //
+  // Welcome to Appflowy 😁
+  // Welcome t Appflowy 😁
+  // Welcome Appflowy 😁
+  //
+  // Then
+  // Welcome to Appflowy 😁
+  //
+  testWidgets(
+      'Presses delete key in non-empty document and selection is backward',
+      (tester) async {
+    await _deleteTextByDelete(tester, true);
+  });
+  testWidgets(
+      'Presses delete key in non-empty document and selection is forward',
+      (tester) async {
+    await _deleteTextByDelete(tester, false);
+  });
+
+  // Before
+  //
+  // Welcome to Appflowy 😁
+  // Welcome to Appflowy 😁
+  //
+  // After
+  //
+  // Welcome to Appflowy 😁Welcome Appflowy 😁
+  testWidgets(
+      'Presses delete key in non-empty document and selection is at the end of the text',
+      (tester) async {
+    const text = 'Welcome to Appflowy 😁';
+    final editor = tester.editor
+      ..insertTextNode(text)
+      ..insertTextNode(text);
+    await editor.startTesting();
+
+    // delete 'o'
+    await editor.updateSelection(
+      Selection.single(path: [0], startOffset: text.length),
+    );
+    await editor.pressLogicKey(LogicalKeyboardKey.delete);
+
+    expect(editor.documentLength, 1);
+    expect(editor.documentSelection,
+        Selection.single(path: [0], startOffset: text.length));
+    expect((editor.nodeAtPath([0]) as TextNode).toRawString(), text * 2);
+  });
+
   // Before
   //
   // Welcome to Appflowy 😁
@@ -47,12 +128,49 @@ void main() async {
   // Welcome to Appflowy 😁
   // [Style] Welcome to Appflowy 😁Welcome to Appflowy 😁
   //
-  testWidgets('Presses backspace key in styled text', (tester) async {
-    await _deleteStyledText(tester, StyleKey.checkbox);
+  testWidgets('Presses backspace key in styled text (checkbox)',
+      (tester) async {
+    await _deleteStyledTextByBackspace(tester, StyleKey.checkbox);
+  });
+  testWidgets('Presses backspace key in styled text (bulletedList)',
+      (tester) async {
+    await _deleteStyledTextByBackspace(tester, StyleKey.bulletedList);
+  });
+  testWidgets('Presses backspace key in styled text (heading)', (tester) async {
+    await _deleteStyledTextByBackspace(tester, StyleKey.heading);
+  });
+  testWidgets('Presses backspace key in styled text (quote)', (tester) async {
+    await _deleteStyledTextByBackspace(tester, StyleKey.quote);
+  });
+
+  // Before
+  //
+  // Welcome to Appflowy 😁
+  // [Style] Welcome to Appflowy 😁
+  // [Style] Welcome to Appflowy 😁
+  //
+  // After
+  //
+  // Welcome to Appflowy 😁
+  // [Style] Welcome to Appflowy 😁
+  //
+  testWidgets('Presses delete key in styled text (checkbox)', (tester) async {
+    await _deleteStyledTextByDelete(tester, StyleKey.checkbox);
+  });
+  testWidgets('Presses delete key in styled text (bulletedList)',
+      (tester) async {
+    await _deleteStyledTextByDelete(tester, StyleKey.bulletedList);
+  });
+  testWidgets('Presses delete key in styled text (heading)', (tester) async {
+    await _deleteStyledTextByDelete(tester, StyleKey.heading);
+  });
+  testWidgets('Presses delete key in styled text (quote)', (tester) async {
+    await _deleteStyledTextByDelete(tester, StyleKey.quote);
   });
 }
 
-Future<void> _deleteStyledText(WidgetTester tester, String style) async {
+Future<void> _deleteStyledTextByBackspace(
+    WidgetTester tester, String style) async {
   const text = 'Welcome to Appflowy 😁';
   Attributes attributes = {
     StyleKey.subtype: style,
@@ -61,6 +179,8 @@ Future<void> _deleteStyledText(WidgetTester tester, String style) async {
     attributes[StyleKey.checkbox] = true;
   } else if (style == StyleKey.numberList) {
     attributes[StyleKey.number] = 1;
+  } else if (style == StyleKey.heading) {
+    attributes[StyleKey.heading] = StyleKey.h1;
   }
   final editor = tester.editor
     ..insertTextNode(text)
@@ -95,3 +215,137 @@ Future<void> _deleteStyledText(WidgetTester tester, String style) async {
   expect(editor.documentSelection, Selection.single(path: [1], startOffset: 0));
   expect(editor.nodeAtPath([1])?.subtype, null);
 }
+
+Future<void> _deleteStyledTextByDelete(
+    WidgetTester tester, String style) async {
+  const text = 'Welcome to Appflowy 😁';
+  Attributes attributes = {
+    StyleKey.subtype: style,
+  };
+  if (style == StyleKey.checkbox) {
+    attributes[StyleKey.checkbox] = true;
+  } else if (style == StyleKey.numberList) {
+    attributes[StyleKey.number] = 1;
+  } else if (style == StyleKey.heading) {
+    attributes[StyleKey.heading] = StyleKey.h1;
+  }
+  final editor = tester.editor
+    ..insertTextNode(text)
+    ..insertTextNode(text, attributes: attributes)
+    ..insertTextNode(text, attributes: attributes);
+
+  await editor.startTesting();
+  await editor.updateSelection(
+    Selection.single(path: [1], startOffset: 0),
+  );
+  for (var i = 1; i < text.length; i++) {
+    await editor.pressLogicKey(
+      LogicalKeyboardKey.delete,
+    );
+    expect(
+        editor.documentSelection, Selection.single(path: [1], startOffset: 0));
+    expect(editor.nodeAtPath([1])?.subtype, style);
+    expect((editor.nodeAtPath([1]) as TextNode).toRawString(),
+        text.safeSubString(i));
+  }
+
+  await editor.pressLogicKey(
+    LogicalKeyboardKey.delete,
+  );
+  expect(editor.documentLength, 2);
+  expect(editor.documentSelection, Selection.single(path: [1], startOffset: 0));
+  expect(editor.nodeAtPath([1])?.subtype, style);
+  expect((editor.nodeAtPath([1]) as TextNode).toRawString(), text);
+}
+
+Future<void> _deleteTextByBackspace(
+    WidgetTester tester, bool isBackwardSelection) async {
+  const text = 'Welcome to Appflowy 😁';
+  final editor = tester.editor
+    ..insertTextNode(text)
+    ..insertTextNode(text)
+    ..insertTextNode(text);
+  await editor.startTesting();
+
+  // delete 'o'
+  await editor.updateSelection(
+    Selection.single(path: [1], startOffset: 10),
+  );
+  await editor.pressLogicKey(LogicalKeyboardKey.backspace);
+
+  expect(editor.documentLength, 3);
+  expect(editor.documentSelection, Selection.single(path: [1], startOffset: 9));
+  expect((editor.nodeAtPath([1]) as TextNode).toRawString(),
+      'Welcome t Appflowy 😁');
+
+  // delete 'to '
+  await editor.updateSelection(
+    Selection.single(path: [2], startOffset: 8, endOffset: 11),
+  );
+  await editor.pressLogicKey(LogicalKeyboardKey.backspace);
+  expect(editor.documentLength, 3);
+  expect(editor.documentSelection, Selection.single(path: [2], startOffset: 8));
+  expect((editor.nodeAtPath([2]) as TextNode).toRawString(),
+      'Welcome Appflowy 😁');
+
+  // delete 'Appflowy 😁
+  // Welcome t Appflowy 😁
+  // Welcome '
+  final start = Position(path: [0], offset: 11);
+  final end = Position(path: [2], offset: 8);
+  await editor.updateSelection(Selection(
+      start: isBackwardSelection ? start : end,
+      end: isBackwardSelection ? end : start));
+  await editor.pressLogicKey(LogicalKeyboardKey.backspace);
+  expect(editor.documentLength, 1);
+  expect(
+      editor.documentSelection, Selection.single(path: [0], startOffset: 11));
+  expect((editor.nodeAtPath([0]) as TextNode).toRawString(),
+      'Welcome to Appflowy 😁');
+}
+
+Future<void> _deleteTextByDelete(
+    WidgetTester tester, bool isBackwardSelection) async {
+  const text = 'Welcome to Appflowy 😁';
+  final editor = tester.editor
+    ..insertTextNode(text)
+    ..insertTextNode(text)
+    ..insertTextNode(text);
+  await editor.startTesting();
+
+  // delete 'o'
+  await editor.updateSelection(
+    Selection.single(path: [1], startOffset: 9),
+  );
+  await editor.pressLogicKey(LogicalKeyboardKey.delete);
+
+  expect(editor.documentLength, 3);
+  expect(editor.documentSelection, Selection.single(path: [1], startOffset: 9));
+  expect((editor.nodeAtPath([1]) as TextNode).toRawString(),
+      'Welcome t Appflowy 😁');
+
+  // delete 'to '
+  await editor.updateSelection(
+    Selection.single(path: [2], startOffset: 8, endOffset: 11),
+  );
+  await editor.pressLogicKey(LogicalKeyboardKey.delete);
+  expect(editor.documentLength, 3);
+  expect(editor.documentSelection, Selection.single(path: [2], startOffset: 8));
+  expect((editor.nodeAtPath([2]) as TextNode).toRawString(),
+      'Welcome Appflowy 😁');
+
+  // delete 'Appflowy 😁
+  // Welcome t Appflowy 😁
+  // Welcome '
+  final start = Position(path: [0], offset: 11);
+  final end = Position(path: [2], offset: 8);
+  await editor.updateSelection(Selection(
+      start: isBackwardSelection ? start : end,
+      end: isBackwardSelection ? end : start));
+  await editor.pressLogicKey(LogicalKeyboardKey.delete);
+  expect(editor.documentLength, 1);
+  expect(
+      editor.documentSelection, Selection.single(path: [0], startOffset: 11));
+  expect((editor.nodeAtPath([0]) as TextNode).toRawString(),
+      'Welcome to Appflowy 😁');
+}