appflowy-editor/test/new/command/text_commands_test.dart

429 lines
13 KiB
Dart
Raw Normal View History

import 'dart:async';
import 'dart:io';
2023-04-27 11:51:11 +08:00
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/services.dart';
2023-04-27 11:51:11 +08:00
import 'package:flutter_test/flutter_test.dart';
import '../infra/testable_editor.dart';
2023-04-27 11:51:11 +08:00
import '../util/util.dart';
void main() async {
2023-04-29 14:36:24 +08:00
group('formatDelta', () {
const text = 'Welcome to AppFlowy Editor 🔥!';
// Welcome |to AppFlowy Editor 🔥!
test('format delta in collapsed selection', () async {
final document = Document.blank().addParagraph(
initialText: text,
);
final editorState = EditorState(document: document);
// Welcome |to AppFlowy Editor 🔥!
const welcome = 'Welcome ';
final selection = Selection.collapsed(
Position(path: [0], offset: welcome.length),
);
editorState.selection = selection;
final before = editorState.getNodeAtPath([0]);
await editorState.formatDelta(selection, {
'bold': true,
});
final after = editorState.getNodeAtPath([0]);
expect(before?.toJson(), after?.toJson());
expect(editorState.selection, selection);
});
// Before
// Welcome to |AppFlowy| Editor 🔥!
// After
// Welcome to <bold>AppFlowy</bold> Editor 🔥!
test('format delta in single selection', () async {
final document = Document.blank().addParagraph(
initialText: text,
);
final editorState = EditorState(document: document);
// Welcome |to AppFlowy Editor 🔥!
const welcomeTo = 'Welcome to ';
const appFlowy = 'AppFlowy';
final selection = Selection.single(
path: [0],
startOffset: welcomeTo.length,
endOffset: welcomeTo.length + appFlowy.length,
);
editorState.selection = selection;
await editorState.formatDelta(selection, {
'bold': true,
});
final after = editorState.getNodeAtPath([0]);
final result = after?.allSatisfyInSelection(selection, (delta) {
final textInserts = delta.whereType<TextInsert>();
2023-04-29 14:36:24 +08:00
return textInserts
.every((element) => element.attributes?['bold'] == true);
});
expect(result, true);
expect(editorState.selection, selection);
});
// Welcome to |AppFlowy Editor 🔥!
// Welcome to |AppFlowy Editor 🔥!
// After
// Welcome to <bold>AppFlowy Editor 🔥!</bold>
// <bold>Welcome to </bold>AppFlowy Editor 🔥!
test('format delta in not single selection', () async {
final document = Document.blank().addParagraph(
initialText: text,
);
final editorState = EditorState(document: document);
// Welcome |to AppFlowy Editor 🔥!
const welcomeTo = 'Welcome to ';
final selection = Selection(
start: Position(path: [0], offset: welcomeTo.length),
end: Position(path: [1], offset: welcomeTo.length),
);
editorState.selection = selection;
await editorState.formatDelta(selection, {
'bold': true,
});
final after = editorState.getNodesInSelection(selection);
final result = after.allSatisfyInSelection(selection, (delta) {
final textInserts = delta.whereType<TextInsert>();
2023-04-29 14:36:24 +08:00
return textInserts
.every((element) => element.attributes?['bold'] == true);
});
expect(result, true);
expect(editorState.selection, selection);
});
});
group('insertNewLine', () {
const text = 'Welcome to AppFlowy Editor 🔥!';
2023-04-27 11:51:11 +08:00
// Before
// Welcome |to AppFlowy Editor 🔥!
// After
// Welcome
// |AppFlowy Editor 🔥!
test('insert new line at the node which doesn\'t contains children',
() async {
final document = Document.blank().addParagraph(
initialText: text,
);
final editorState = EditorState(document: document);
2023-04-27 11:51:11 +08:00
// Welcome |to AppFlowy Editor 🔥!
const welcome = 'Welcome ';
final selection = Selection.collapsed(
Position(path: [0], offset: welcome.length),
);
editorState.selection = selection;
unawaited(editorState.insertNewLine());
2023-04-27 11:51:11 +08:00
expect(editorState.getNodeAtPath([0])?.delta?.toPlainText(), welcome);
expect(
editorState.getNodeAtPath([1])?.delta?.toPlainText(),
text.substring(welcome.length),
);
});
2023-04-27 11:51:11 +08:00
// Before
// Welcome |to AppFlowy Editor 🔥!
// Welcome to AppFlowy Editor 🔥!
// After
// Welcome |
// AppFlowy Editor 🔥!
// Welcome to AppFlowy Editor 🔥!
test('insert new line at the node which contains children', () async {
final document = Document.blank().addParagraph(
initialText: text,
decorator: (index, node) {
node.addParagraph(
initialText: text,
);
},
);
final editorState = EditorState(document: document);
2023-04-27 11:51:11 +08:00
// 0. Welcome |to AppFlowy Editor 🔥!
const welcome = 'Welcome ';
final selection = Selection.collapsed(
Position(path: [0], offset: welcome.length),
);
editorState.selection = selection;
unawaited(editorState.insertNewLine());
2023-04-27 11:51:11 +08:00
expect(editorState.getNodeAtPath([0])?.delta?.toPlainText(), welcome);
expect(editorState.getNodeAtPath([0, 0]), null);
expect(
editorState.getNodeAtPath([1])?.delta?.toPlainText(),
text.substring(welcome.length),
);
expect(editorState.getNodeAtPath([1, 0])?.delta?.toPlainText(), text);
2023-04-27 11:51:11 +08:00
});
test('insert new line preserve direction', () async {
final document = Document.blank().addParagraph(
initialText: text,
decorator: (index, node) => node.updateAttributes(
{ParagraphBlockKeys.textDirection: blockComponentTextDirectionRTL},
),
);
final editorState = EditorState(document: document);
final selection = Selection.collapsed(
Position(path: [0], offset: text.length),
);
editorState.selection = selection;
await editorState.insertNewLine();
final textDirection = editorState
.getNodeAtPath([1])?.attributes[ParagraphBlockKeys.textDirection];
expect(
textDirection,
blockComponentTextDirectionRTL,
);
});
2023-04-27 11:51:11 +08:00
});
2023-04-30 15:01:09 +08:00
group('insertText', () {
const text = 'Welcome to AppFlowy Editor 🔥!';
/// Before
/// |
/// Welcome to AppFlowy Editor 🔥!
///
/// After
/// Hello|
/// Welcome to AppFlowy Editor 🔥!
test('insertText', () async {
final document = Document.blank()
.addParagraph(
initialText: '',
)
.addParagraph(
initialText: text,
decorator: (index, node) {
node.addParagraph(
initialText: text,
);
},
);
final editorState = EditorState(document: document);
const hello = 'Hello';
await editorState.insertText(0, hello, path: [0]);
expect(editorState.getNodeAtPath([0])?.delta?.toPlainText(), hello);
});
test('insertTextAtCurrentSelection', () async {
final document = Document.blank()
.addParagraph(
initialText: '',
)
.addParagraph(
initialText: text,
decorator: (index, node) {
node.addParagraph(
initialText: text,
);
},
);
final selection = Selection.collapsed(
Position(path: [0]),
2023-04-30 15:01:09 +08:00
);
final editorState = EditorState(document: document);
editorState.selection = selection;
const hello = 'Hello';
await editorState.insertTextAtCurrentSelection(hello);
expect(editorState.getNodeAtPath([0])?.delta?.toPlainText(), hello);
expect(
editorState.selection,
Selection.collapsed(
Position(path: [0], offset: hello.length),
),
);
});
});
2023-04-30 15:32:29 +08:00
group('getNodesInSelection', () {
const text = 'Welcome to AppFlowy Editor 🔥!';
// Welcome| to AppFlowy Editor 🔥!
test('get nodes in collapsed selection', () async {
final document = Document.blank().addParagraph(
initialText: text,
);
// Welcome| to AppFlowy Editor 🔥!
final selection = Selection.collapsed(
Position(path: [0], offset: 4),
2023-04-30 15:32:29 +08:00
);
final editorState = EditorState(document: document);
editorState.selection = selection;
final texts = editorState.getTextInSelection(selection);
expect(texts, []);
});
// Welcome to |AppFlowy| Editor 🔥!
test('get nodes in single selection', () async {
final document = Document.blank().addParagraph(
initialText: text,
);
// Welcome to |AppFlowy| Editor 🔥!
final selection = Selection.single(
path: [0],
startOffset: 'Welcome to '.length,
endOffset: 'Welcome to AppFlowy'.length,
);
final editorState = EditorState(document: document);
editorState.selection = selection;
final texts = editorState.getTextInSelection(selection);
expect(texts, ['AppFlowy']);
});
// Wel|come
// To
// App|Flowy
test('get nodes in multi selection', () async {
final document = Document.blank()
.addParagraph(
initialText: 'Welcome',
)
.addParagraph(
initialText: 'To',
)
.addParagraph(
initialText: 'AppFlowy',
);
// Wel|come
// To
// App|Flowy
final selection = Selection(
start: Position(path: [0], offset: 3),
end: Position(path: [2], offset: 3),
);
final editorState = EditorState(document: document);
editorState.selection = selection;
final texts = editorState.getTextInSelection(selection);
expect(texts, ['come', 'To', 'App']);
});
});
group('toggle style', () {
testWidgets('toggle the style if the previous character isn\'t formatted',
(tester) async {
const text = '';
final editor = tester.editor..addParagraph(initialText: text);
await editor.startTesting();
await editor.updateSelection(
Selection.single(path: [0], startOffset: text.length),
);
// toggle bold, italic, underline
final keys = [
LogicalKeyboardKey.keyB,
LogicalKeyboardKey.keyI,
LogicalKeyboardKey.keyU,
];
for (final key in keys) {
await editor.pressKey(
key: key,
isControlPressed: !Platform.isMacOS,
isMetaPressed: Platform.isMacOS,
);
}
await editor.ime.insertText('Hello');
final delta1 = editor.nodeAtPath([0])!.delta!;
expect(delta1.toJson(), [
{
"insert": "Hello",
"attributes": {"bold": true, "italic": true, "underline": true},
}
]);
// cancel the toggled style
for (final key in keys) {
await editor.pressKey(
key: key,
isControlPressed: !Platform.isMacOS,
isMetaPressed: Platform.isMacOS,
);
}
await editor.ime.insertText('World');
final delta2 = editor.nodeAtPath([0])!.delta!;
expect(delta2.toJson(), [
{
"insert": "Hello",
"attributes": {"bold": true, "italic": true, "underline": true},
},
{
"insert": "World",
"attributes": {"bold": false, "italic": false, "underline": false},
},
]);
expect(editor.editorState.toggledStyle, isEmpty);
await editor.dispose();
});
testWidgets('toggle twice to reset the toggled style', (tester) async {
const text = '';
final editor = tester.editor..addParagraph(initialText: text);
await editor.startTesting();
await editor.updateSelection(
Selection.single(path: [0], startOffset: text.length),
);
// toggle bold, italic, underline
final keys = [
LogicalKeyboardKey.keyB,
LogicalKeyboardKey.keyI,
LogicalKeyboardKey.keyU,
];
for (final key in keys) {
await editor.pressKey(
key: key,
isControlPressed: !Platform.isMacOS,
isMetaPressed: Platform.isMacOS,
);
}
// reset
for (final key in keys) {
await editor.pressKey(
key: key,
isControlPressed: !Platform.isMacOS,
isMetaPressed: Platform.isMacOS,
);
}
await editor.ime.insertText('Hello');
final delta1 = editor.nodeAtPath([0])!.delta!;
expect(delta1.toJson(), [
{
"insert": "Hello",
"attributes": {"bold": false, "italic": false, "underline": false},
}
]);
expect(editor.editorState.toggledStyle, isEmpty);
await editor.dispose();
});
});
2023-04-27 11:51:11 +08:00
}