test: migrate the markdown_syntax_to_styled_text
This commit is contained in:
parent
3020ce0c4c
commit
4a32d0a460
7 changed files with 279 additions and 256 deletions
|
|
@ -0,0 +1,22 @@
|
||||||
|
import 'package:appflowy_editor/src/editor/editor_component/service/shortcuts/character_shortcut_event.dart';
|
||||||
|
import 'package:appflowy_editor/src/editor/editor_component/service/shortcuts/character_shortcut_events/format_by_wrapping_with_double_character/handle_format_by_wrapping_with_double_character.dart';
|
||||||
|
|
||||||
|
const _tile = '~';
|
||||||
|
|
||||||
|
/// format the text surrounded by double asterisks to bold
|
||||||
|
///
|
||||||
|
/// - support
|
||||||
|
/// - desktop
|
||||||
|
/// - mobile
|
||||||
|
/// - web
|
||||||
|
///
|
||||||
|
final CharacterShortcutEvent formatDoubleTilesToStrikethrough =
|
||||||
|
CharacterShortcutEvent(
|
||||||
|
key: 'format the text surrounded by double asterisks to bold',
|
||||||
|
character: _tile,
|
||||||
|
handler: (editorState) async => handleFormatByWrappingWithDoubleCharacter(
|
||||||
|
editorState: editorState,
|
||||||
|
character: _tile,
|
||||||
|
formatStyle: DoubleCharacterFormatStyle.strikethrough,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
// If we have more in the future, we should add them in this enum and update the [style] variable in [handleDoubleCharactersFormat].
|
// If we have more in the future, we should add them in this enum and update the [style] variable in [handleDoubleCharactersFormat].
|
||||||
enum DoubleCharacterFormatStyle {
|
enum DoubleCharacterFormatStyle {
|
||||||
bold,
|
bold,
|
||||||
|
strikethrough,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handleFormatByWrappingWithDoubleCharacter({
|
bool handleFormatByWrappingWithDoubleCharacter({
|
||||||
|
|
@ -79,6 +80,9 @@ bool handleFormatByWrappingWithDoubleCharacter({
|
||||||
case DoubleCharacterFormatStyle.bold:
|
case DoubleCharacterFormatStyle.bold:
|
||||||
style = 'bold';
|
style = 'bold';
|
||||||
break;
|
break;
|
||||||
|
case DoubleCharacterFormatStyle.strikethrough:
|
||||||
|
style = 'strikethrough';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
style = '';
|
style = '';
|
||||||
assert(false, 'Invalid format style');
|
assert(false, 'Invalid format style');
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:appflowy_editor/src/editor/editor_component/service/shortcuts/character_shortcut_events/format_by_wrapping_with_double_character/format_bold.dart';
|
import 'package:appflowy_editor/src/editor/editor_component/service/shortcuts/character_shortcut_events/format_by_wrapping_with_double_character/format_bold.dart';
|
||||||
|
import 'package:appflowy_editor/src/editor/editor_component/service/shortcuts/character_shortcut_events/format_by_wrapping_with_double_character/format_strikethrough.dart';
|
||||||
import 'package:appflowy_editor/src/editor/editor_component/service/shortcuts/character_shortcut_events/format_by_wrapping_with_single_character/format_code.dart';
|
import 'package:appflowy_editor/src/editor/editor_component/service/shortcuts/character_shortcut_events/format_by_wrapping_with_single_character/format_code.dart';
|
||||||
import 'package:appflowy_editor/src/editor/editor_component/service/shortcuts/character_shortcut_events/format_by_wrapping_with_single_character/format_italic.dart';
|
import 'package:appflowy_editor/src/editor/editor_component/service/shortcuts/character_shortcut_events/format_by_wrapping_with_single_character/format_italic.dart';
|
||||||
import 'package:appflowy_editor/src/editor/editor_component/service/shortcuts/character_shortcut_events/format_by_wrapping_with_single_character/format_strikethrough.dart';
|
import 'package:appflowy_editor/src/editor/editor_component/service/shortcuts/character_shortcut_events/format_by_wrapping_with_single_character/format_strikethrough.dart';
|
||||||
|
|
@ -15,15 +16,20 @@ import 'package:appflowy_editor/src/editor/editor_component/service/shortcuts/ch
|
||||||
// 4. tilde to strikethrough -> ~abc~
|
// 4. tilde to strikethrough -> ~abc~
|
||||||
|
|
||||||
final List<CharacterShortcutEvent> markdownSyntaxShortcutEvents = [
|
final List<CharacterShortcutEvent> markdownSyntaxShortcutEvents = [
|
||||||
// format code, 'code'
|
// format code, `code`
|
||||||
formatBackquoteToCode,
|
formatBackquoteToCode,
|
||||||
|
|
||||||
// format italic, _italic_ or *italic*
|
// format italic,
|
||||||
|
// _italic_
|
||||||
|
// *italic*
|
||||||
formatUnderscoreToItalic,
|
formatUnderscoreToItalic,
|
||||||
formatAsteriskToItalic,
|
formatAsteriskToItalic,
|
||||||
|
|
||||||
//format strikethrough, ~strikethrough~
|
// format strikethrough,
|
||||||
|
// ~strikethrough~
|
||||||
|
// ~~strikethrough~~
|
||||||
formatTildeToStrikethrough,
|
formatTildeToStrikethrough,
|
||||||
|
formatDoubleTilesToStrikethrough,
|
||||||
|
|
||||||
// format bold, **bold** or __bold__
|
// format bold, **bold** or __bold__
|
||||||
formatDoubleAsterisksToBold,
|
formatDoubleAsterisksToBold,
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,8 @@ class _FlowyRichTextState extends State<FlowyRichText> with SelectableMixin {
|
||||||
_textKey.currentContext?.findRenderObject() as RenderParagraph;
|
_textKey.currentContext?.findRenderObject() as RenderParagraph;
|
||||||
|
|
||||||
RenderParagraph? get _placeholderRenderParagraph =>
|
RenderParagraph? get _placeholderRenderParagraph =>
|
||||||
_placeholderTextKey.currentContext?.findRenderObject() as RenderParagraph;
|
_placeholderTextKey.currentContext?.findRenderObject()
|
||||||
|
as RenderParagraph?;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant FlowyRichText oldWidget) {
|
void didUpdateWidget(covariant FlowyRichText oldWidget) {
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,10 @@ class TestableEditor {
|
||||||
final keyToCharacterMap = {
|
final keyToCharacterMap = {
|
||||||
LogicalKeyboardKey.space: ' ',
|
LogicalKeyboardKey.space: ' ',
|
||||||
LogicalKeyboardKey.enter: '\n',
|
LogicalKeyboardKey.enter: '\n',
|
||||||
|
LogicalKeyboardKey.backquote: '`',
|
||||||
|
LogicalKeyboardKey.tilde: '~',
|
||||||
|
LogicalKeyboardKey.asterisk: '*',
|
||||||
|
LogicalKeyboardKey.underscore: '_',
|
||||||
};
|
};
|
||||||
Future<void> pressLogicKey({
|
Future<void> pressLogicKey({
|
||||||
String? character,
|
String? character,
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ void main() async {
|
||||||
LogicalKeyboardKey.keyB,
|
LogicalKeyboardKey.keyB,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Presses Command + I to update text style', (tester) async {
|
testWidgets('Presses Command + I to update text style', (tester) async {
|
||||||
await _testUpdateTextStyleByCommandX(
|
await _testUpdateTextStyleByCommandX(
|
||||||
tester,
|
tester,
|
||||||
|
|
@ -30,6 +31,7 @@ void main() async {
|
||||||
LogicalKeyboardKey.keyI,
|
LogicalKeyboardKey.keyI,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Presses Command + U to update text style', (tester) async {
|
testWidgets('Presses Command + U to update text style', (tester) async {
|
||||||
await _testUpdateTextStyleByCommandX(
|
await _testUpdateTextStyleByCommandX(
|
||||||
tester,
|
tester,
|
||||||
|
|
@ -38,6 +40,7 @@ void main() async {
|
||||||
LogicalKeyboardKey.keyU,
|
LogicalKeyboardKey.keyU,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Presses Command + Shift + S to update text style',
|
testWidgets('Presses Command + Shift + S to update text style',
|
||||||
(tester) async {
|
(tester) async {
|
||||||
await _testUpdateTextStyleByCommandX(
|
await _testUpdateTextStyleByCommandX(
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,56 @@
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import '../../infra/test_editor.dart';
|
|
||||||
|
import '../../new/infra/testable_editor.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
|
bool allInSelection(
|
||||||
|
Node? node,
|
||||||
|
Selection selection,
|
||||||
|
String key,
|
||||||
|
) {
|
||||||
|
return node?.allSatisfyInSelection(
|
||||||
|
selection,
|
||||||
|
(delta) => delta.whereType<TextInsert>().every(
|
||||||
|
(element) => element.attributes?[key] == true,
|
||||||
|
),
|
||||||
|
) ??
|
||||||
|
false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allCodeInSelection(Node? node, Selection selection) => allInSelection(
|
||||||
|
node,
|
||||||
|
selection,
|
||||||
|
'code',
|
||||||
|
);
|
||||||
|
|
||||||
|
bool allStrikethroughInSelection(Node? node, Selection selection) =>
|
||||||
|
allInSelection(
|
||||||
|
node,
|
||||||
|
selection,
|
||||||
|
'strikethrough',
|
||||||
|
);
|
||||||
|
|
||||||
|
bool allBoldInSelection(Node? node, Selection selection) => allInSelection(
|
||||||
|
node,
|
||||||
|
selection,
|
||||||
|
'bold',
|
||||||
|
);
|
||||||
|
|
||||||
|
bool allItalicInSelection(Node? node, Selection selection) => allInSelection(
|
||||||
|
node,
|
||||||
|
selection,
|
||||||
|
'bold',
|
||||||
|
);
|
||||||
|
|
||||||
setUpAll(() {
|
setUpAll(() {
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
});
|
});
|
||||||
|
|
||||||
group('markdown_syntax_to_styled_text.dart', () {
|
group('markdown_syntax_to_styled_text.dart', () {
|
||||||
group('convert single backquote to code', () {
|
|
||||||
Future<void> insertBackquote(
|
Future<void> insertBackquote(
|
||||||
EditorWidgetTester editor, {
|
TestableEditor editor, {
|
||||||
int repeat = 1,
|
int repeat = 1,
|
||||||
}) async {
|
}) async {
|
||||||
for (var i = 0; i < repeat; i++) {
|
for (var i = 0; i < repeat; i++) {
|
||||||
|
|
@ -21,489 +60,433 @@ void main() async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
group('convert single backquote to code', () {
|
||||||
testWidgets('`AppFlowy` to code AppFlowy', (tester) async {
|
testWidgets('`AppFlowy` to code AppFlowy', (tester) async {
|
||||||
const text = '`AppFlowy';
|
const text = '`AppFlowy';
|
||||||
final editor = tester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [0], startOffset: 0),
|
Selection.single(path: [0], startOffset: 0),
|
||||||
);
|
);
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
|
||||||
for (var i = 0; i < text.length; i++) {
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
await insertBackquote(editor);
|
await insertBackquote(editor);
|
||||||
final allCode = textNode.allSatisfyCodeInSelection(
|
final node = editor.nodeAtPath([0]);
|
||||||
|
|
||||||
|
final allCode = allCodeInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: text.length - 1,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(allCode, true);
|
expect(allCode, true);
|
||||||
expect(textNode.toPlainText(), 'AppFlowy');
|
expect(node?.delta?.toPlainText(), 'AppFlowy');
|
||||||
|
await editor.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('App`Flowy` to code AppFlowy', (tester) async {
|
testWidgets('App`Flowy` to code AppFlowy', (tester) async {
|
||||||
const text = 'App`Flowy';
|
const text = 'App`Flowy';
|
||||||
final editor = tester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [0], startOffset: 0),
|
Selection.single(path: [0], startOffset: 0),
|
||||||
);
|
);
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
|
||||||
for (var i = 0; i < text.length; i++) {
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
await insertBackquote(editor);
|
await insertBackquote(editor);
|
||||||
final allCode = textNode.allSatisfyCodeInSelection(
|
final node = editor.nodeAtPath([0]);
|
||||||
|
|
||||||
|
final allCode = allCodeInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 3,
|
startOffset: 3,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: text.length - 1,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(allCode, true);
|
expect(allCode, true);
|
||||||
expect(textNode.toPlainText(), 'AppFlowy');
|
expect(node?.delta?.toPlainText(), 'AppFlowy');
|
||||||
|
await editor.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('`` nothing changes', (tester) async {
|
testWidgets('`` nothing changes', (tester) async {
|
||||||
const text = '`';
|
const text = '`';
|
||||||
final editor = tester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [0], startOffset: 0),
|
Selection.single(path: [0], startOffset: 0),
|
||||||
);
|
);
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
for (var i = 0; i < text.length; i++) {
|
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
await insertBackquote(editor);
|
await insertBackquote(editor);
|
||||||
final allCode = textNode.allSatisfyCodeInSelection(
|
final node = editor.nodeAtPath([0]);
|
||||||
|
final allCode = allCodeInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(allCode, false);
|
expect(allCode, false);
|
||||||
expect(textNode.toPlainText(), text);
|
expect(node.delta?.toPlainText(), '``');
|
||||||
|
await editor.dispose();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('convert double backquote to code', () {
|
group('convert double backquote to code', () {
|
||||||
Future<void> insertBackquote(
|
|
||||||
EditorWidgetTester editor, {
|
|
||||||
int repeat = 1,
|
|
||||||
}) async {
|
|
||||||
for (var i = 0; i < repeat; i++) {
|
|
||||||
await editor.pressLogicKey(
|
|
||||||
key: LogicalKeyboardKey.backquote,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testWidgets('```AppFlowy`` to code `AppFlowy', (tester) async {
|
|
||||||
const text = '```AppFlowy`';
|
|
||||||
final editor = tester.editor..insertTextNode('');
|
|
||||||
await editor.startTesting();
|
|
||||||
await editor.updateSelection(
|
|
||||||
Selection.single(path: [0], startOffset: 0),
|
|
||||||
);
|
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
|
||||||
for (var i = 0; i < text.length; i++) {
|
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
await insertBackquote(editor);
|
|
||||||
final allCode = textNode.allSatisfyCodeInSelection(
|
|
||||||
Selection.single(
|
|
||||||
path: [0],
|
|
||||||
startOffset: 1,
|
|
||||||
endOffset: textNode.toPlainText().length,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(allCode, true);
|
|
||||||
expect(textNode.toPlainText(), '`AppFlowy');
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('```` nothing changes', (tester) async {
|
testWidgets('```` nothing changes', (tester) async {
|
||||||
const text = '```';
|
const text = '```';
|
||||||
final editor = tester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [0], startOffset: 0),
|
Selection.single(path: [0], startOffset: 0),
|
||||||
);
|
);
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
for (var i = 0; i < text.length; i++) {
|
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
await insertBackquote(editor);
|
await insertBackquote(editor);
|
||||||
final allCode = textNode.allSatisfyCodeInSelection(
|
final node = editor.nodeAtPath([0]);
|
||||||
|
final allCode = allCodeInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(allCode, false);
|
expect(allCode, false);
|
||||||
expect(textNode.toPlainText(), text);
|
expect(node.delta?.toPlainText(), '````');
|
||||||
|
await editor.dispose();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('convert double tilde to strikethrough', () {
|
group('convert double tilde to strikethrough', () {
|
||||||
Future<void> insertTilde(
|
Future<void> insertTilde(
|
||||||
EditorWidgetTester editor, {
|
TestableEditor editor, {
|
||||||
int repeat = 1,
|
int repeat = 1,
|
||||||
}) async {
|
}) async {
|
||||||
for (var i = 0; i < repeat; i++) {
|
for (var i = 0; i < repeat; i++) {
|
||||||
await editor.pressLogicKey(character: '~');
|
await editor.pressLogicKey(key: LogicalKeyboardKey.tilde);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testWidgets('~~AppFlowy~~ to strikethrough AppFlowy', (tester) async {
|
testWidgets('~~AppFlowy~~ to strikethrough AppFlowy', (tester) async {
|
||||||
const text = '~~AppFlowy~';
|
const text = '~~AppFlowy~';
|
||||||
final editor = tester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [0], startOffset: 0),
|
Selection.single(path: [0], startOffset: 0),
|
||||||
);
|
);
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
for (var i = 0; i < text.length; i++) {
|
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
await insertTilde(editor);
|
await insertTilde(editor);
|
||||||
final allStrikethrough = textNode.allSatisfyStrikethroughInSelection(
|
final node = editor.nodeAtPath([0]);
|
||||||
|
final result = allStrikethroughInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allStrikethrough, true);
|
|
||||||
expect(textNode.toPlainText(), 'AppFlowy');
|
expect(result, true);
|
||||||
|
expect(node.delta?.toPlainText(), 'AppFlowy');
|
||||||
|
await editor.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('App~~Flowy~~ to strikethrough AppFlowy', (tester) async {
|
testWidgets('App~~Flowy~~ to strikethrough AppFlowy', (tester) async {
|
||||||
const text = 'App~~Flowy~';
|
const text = 'App~~Flowy~';
|
||||||
final editor = tester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [0], startOffset: 0),
|
Selection.single(path: [0], startOffset: 0),
|
||||||
);
|
);
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
for (var i = 0; i < text.length; i++) {
|
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
await insertTilde(editor);
|
await insertTilde(editor);
|
||||||
final allStrikethrough = textNode.allSatisfyStrikethroughInSelection(
|
final node = editor.nodeAtPath([0]);
|
||||||
|
final result = allStrikethroughInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 3,
|
startOffset: 3,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allStrikethrough, true);
|
|
||||||
expect(textNode.toPlainText(), 'AppFlowy');
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('~~~AppFlowy~~ to bold ~AppFlowy', (tester) async {
|
expect(result, true);
|
||||||
const text = '~~~AppFlowy~';
|
expect(node.delta?.toPlainText(), 'AppFlowy');
|
||||||
final editor = tester.editor..insertTextNode('');
|
await editor.dispose();
|
||||||
await editor.startTesting();
|
|
||||||
await editor.updateSelection(
|
|
||||||
Selection.single(path: [0], startOffset: 0),
|
|
||||||
);
|
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
|
||||||
for (var i = 0; i < text.length; i++) {
|
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
await insertTilde(editor);
|
|
||||||
final allStrikethrough = textNode.allSatisfyStrikethroughInSelection(
|
|
||||||
Selection.single(
|
|
||||||
path: [0],
|
|
||||||
startOffset: 1,
|
|
||||||
endOffset: textNode.toPlainText().length,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(allStrikethrough, true);
|
|
||||||
expect(textNode.toPlainText(), '~AppFlowy');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('~~~~ nothing changes', (tester) async {
|
testWidgets('~~~~ nothing changes', (tester) async {
|
||||||
const text = '~~~';
|
const text = '~~~';
|
||||||
final editor = tester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [0], startOffset: 0),
|
Selection.single(path: [0], startOffset: 0),
|
||||||
);
|
);
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
for (var i = 0; i < text.length; i++) {
|
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
await insertTilde(editor);
|
await insertTilde(editor);
|
||||||
final allStrikethrough = textNode.allSatisfyStrikethroughInSelection(
|
final node = editor.nodeAtPath([0]);
|
||||||
|
final result = allStrikethroughInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allStrikethrough, false);
|
|
||||||
expect(textNode.toPlainText(), text);
|
expect(result, false);
|
||||||
|
expect(node.delta?.toPlainText(), '~~~~');
|
||||||
|
await editor.dispose();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('convert double asterisk to bold', () {
|
group('convert double asterisk to bold', () {
|
||||||
Future<void> insertAsterisk(
|
Future<void> insertAsterisk(
|
||||||
EditorWidgetTester editor, {
|
TestableEditor editor, {
|
||||||
int repeat = 1,
|
int repeat = 1,
|
||||||
}) async {
|
}) async {
|
||||||
for (var i = 0; i < repeat; i++) {
|
for (var i = 0; i < repeat; i++) {
|
||||||
await editor.pressLogicKey(character: '*');
|
await editor.pressLogicKey(key: LogicalKeyboardKey.asterisk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'**AppFlowy** to bold AppFlowy',
|
'**AppFlowy** to bold AppFlowy',
|
||||||
((widgetTester) async {
|
(tester) async {
|
||||||
const text = '**AppFlowy*';
|
const text = '**AppFlowy*';
|
||||||
final editor = widgetTester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
|
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor
|
await editor.updateSelection(
|
||||||
.updateSelection(Selection.single(path: [0], startOffset: 0));
|
Selection.single(path: [0], startOffset: 0),
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
);
|
||||||
for (var i = 0; i < text.length; i++) {
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
await insertAsterisk(editor);
|
await insertAsterisk(editor);
|
||||||
|
final node = editor.nodeAtPath([0]);
|
||||||
final allBold = textNode.allSatisfyBoldInSelection(
|
final result = allBoldInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(allBold, true);
|
expect(result, true);
|
||||||
expect(textNode.toPlainText(), 'AppFlowy');
|
expect(node.delta?.toPlainText(), 'AppFlowy');
|
||||||
}),
|
await editor.dispose();
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'App**Flowy** to bold AppFlowy',
|
'App**Flowy** to bold AppFlowy',
|
||||||
((widgetTester) async {
|
((tester) async {
|
||||||
const text = 'App**Flowy*';
|
const text = 'App**Flowy*';
|
||||||
final editor = widgetTester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
|
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor
|
await editor.updateSelection(
|
||||||
.updateSelection(Selection.single(path: [0], startOffset: 0));
|
Selection.single(path: [0], startOffset: 0),
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
);
|
||||||
for (var i = 0; i < text.length; i++) {
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
await insertAsterisk(editor);
|
await insertAsterisk(editor);
|
||||||
|
final node = editor.nodeAtPath([0]);
|
||||||
final allBold = textNode.allSatisfyBoldInSelection(
|
final result = allBoldInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 3,
|
startOffset: 3,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(allBold, true);
|
expect(result, true);
|
||||||
expect(textNode.toPlainText(), 'AppFlowy');
|
expect(node.delta?.toPlainText(), 'AppFlowy');
|
||||||
|
await editor.dispose();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'***AppFlowy** to bold *AppFlowy',
|
'***AppFlowy** to bold *AppFlowy',
|
||||||
((widgetTester) async {
|
((tester) async {
|
||||||
const text = '***AppFlowy*';
|
const text = '***AppFlowy*';
|
||||||
final editor = widgetTester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
|
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor
|
await editor.updateSelection(
|
||||||
.updateSelection(Selection.single(path: [0], startOffset: 0));
|
Selection.single(path: [0], startOffset: 0),
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
);
|
||||||
for (var i = 0; i < text.length; i++) {
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
await insertAsterisk(editor);
|
await insertAsterisk(editor);
|
||||||
|
final node = editor.nodeAtPath([0]);
|
||||||
final allBold = textNode.allSatisfyBoldInSelection(
|
final result = allBoldInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 1,
|
startOffset: 1,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(allBold, true);
|
expect(result, true);
|
||||||
expect(textNode.toPlainText(), '*AppFlowy');
|
expect(node.delta?.toPlainText(), '*AppFlowy');
|
||||||
|
await editor.dispose();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'**** nothing changes',
|
'**** nothing changes',
|
||||||
((widgetTester) async {
|
((tester) async {
|
||||||
const text = '***';
|
const text = '***';
|
||||||
final editor = widgetTester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
|
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor
|
await editor.updateSelection(
|
||||||
.updateSelection(Selection.single(path: [0], startOffset: 0));
|
Selection.single(path: [0], startOffset: 0),
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
);
|
||||||
for (var i = 0; i < text.length; i++) {
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
await insertAsterisk(editor);
|
await insertAsterisk(editor);
|
||||||
|
final node = editor.nodeAtPath([0]);
|
||||||
final allBold = textNode.allSatisfyBoldInSelection(
|
final result = allBoldInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 1,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(allBold, false);
|
expect(result, false);
|
||||||
expect(textNode.toPlainText(), text);
|
expect(node.delta?.toPlainText(), '****');
|
||||||
|
await editor.dispose();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
group('convert double underscore to bold', () {
|
group('convert double underscore to bold', () {
|
||||||
Future<void> insertUnderscore(
|
Future<void> insertUnderscore(
|
||||||
EditorWidgetTester editor, {
|
TestableEditor editor, {
|
||||||
int repeat = 1,
|
int repeat = 1,
|
||||||
}) async {
|
}) async {
|
||||||
for (var i = 0; i < repeat; i++) {
|
for (var i = 0; i < repeat; i++) {
|
||||||
await editor.pressLogicKey(character: '_');
|
await editor.pressLogicKey(key: LogicalKeyboardKey.underscore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'__AppFlowy__ to bold AppFlowy',
|
'__AppFlowy__ to bold AppFlowy',
|
||||||
((widgetTester) async {
|
((tester) async {
|
||||||
const text = '__AppFlowy_';
|
const text = '__AppFlowy_';
|
||||||
final editor = widgetTester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
|
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor
|
await editor.updateSelection(
|
||||||
.updateSelection(Selection.single(path: [0], startOffset: 0));
|
Selection.single(path: [0], startOffset: 0),
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
);
|
||||||
for (var i = 0; i < text.length; i++) {
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
await insertUnderscore(editor);
|
await insertUnderscore(editor);
|
||||||
|
final node = editor.nodeAtPath([0]);
|
||||||
|
|
||||||
final allBold = textNode.allSatisfyBoldInSelection(
|
final result = allItalicInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(allBold, true);
|
expect(result, true);
|
||||||
expect(textNode.toPlainText(), 'AppFlowy');
|
expect(node.delta!.toPlainText(), 'AppFlowy');
|
||||||
|
await editor.dispose();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'App__Flowy__ to bold AppFlowy',
|
'App__Flowy__ to bold AppFlowy',
|
||||||
((widgetTester) async {
|
((tester) async {
|
||||||
const text = 'App__Flowy_';
|
const text = 'App__Flowy_';
|
||||||
final editor = widgetTester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
|
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor
|
await editor.updateSelection(
|
||||||
.updateSelection(Selection.single(path: [0], startOffset: 0));
|
Selection.single(path: [0], startOffset: 0),
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
);
|
||||||
for (var i = 0; i < text.length; i++) {
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
await insertUnderscore(editor);
|
await insertUnderscore(editor);
|
||||||
|
final node = editor.nodeAtPath([0]);
|
||||||
|
|
||||||
final allBold = textNode.allSatisfyBoldInSelection(
|
final result = allItalicInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 3,
|
startOffset: 3,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(allBold, true);
|
expect(result, true);
|
||||||
expect(textNode.toPlainText(), 'AppFlowy');
|
expect(node.delta!.toPlainText(), 'AppFlowy');
|
||||||
|
await editor.dispose();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'__*AppFlowy__ to bold *AppFlowy',
|
'__*AppFlowy__ to bold *AppFlowy',
|
||||||
((widgetTester) async {
|
((tester) async {
|
||||||
const text = '__*AppFlowy_';
|
const text = '__*AppFlowy_';
|
||||||
final editor = widgetTester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
|
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor
|
await editor.updateSelection(
|
||||||
.updateSelection(Selection.single(path: [0], startOffset: 0));
|
Selection.single(path: [0], startOffset: 0),
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
);
|
||||||
for (var i = 0; i < text.length; i++) {
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
await insertUnderscore(editor);
|
await insertUnderscore(editor);
|
||||||
|
final node = editor.nodeAtPath([0]);
|
||||||
|
|
||||||
final allBold = textNode.allSatisfyBoldInSelection(
|
final result = allItalicInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 1,
|
startOffset: 1,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(allBold, true);
|
expect(result, true);
|
||||||
expect(textNode.toPlainText(), '*AppFlowy');
|
expect(node.delta!.toPlainText(), '*AppFlowy');
|
||||||
|
await editor.dispose();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'____ nothing changes',
|
'____ nothing changes',
|
||||||
((widgetTester) async {
|
((tester) async {
|
||||||
const text = '___';
|
const text = '___';
|
||||||
final editor = widgetTester.editor..insertTextNode('');
|
final editor = tester.editor..addEmptyParagraph();
|
||||||
|
|
||||||
await editor.startTesting();
|
await editor.startTesting();
|
||||||
await editor
|
await editor.updateSelection(
|
||||||
.updateSelection(Selection.single(path: [0], startOffset: 0));
|
Selection.single(path: [0], startOffset: 0),
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
);
|
||||||
for (var i = 0; i < text.length; i++) {
|
await editor.editorState.insertTextAtCurrentSelection(text);
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
await insertUnderscore(editor);
|
await insertUnderscore(editor);
|
||||||
|
final node = editor.nodeAtPath([0]);
|
||||||
|
|
||||||
final allBold = textNode.allSatisfyBoldInSelection(
|
final result = allItalicInSelection(
|
||||||
|
node,
|
||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 1,
|
||||||
endOffset: textNode.toPlainText().length,
|
endOffset: node!.delta!.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(allBold, false);
|
expect(result, false);
|
||||||
expect(textNode.toPlainText(), text);
|
expect(node.delta!.toPlainText(), '____');
|
||||||
|
await editor.dispose();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue