mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-01-19 08:42:48 +08:00
Lexical: Fixed auto-link issue
Added extra test helper to check the editor state directly via string notation access rather than juggling types/objects to access deep properties.
This commit is contained in:
parent
786a434c03
commit
0d1a237f81
|
@ -37,8 +37,6 @@ import {QuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
|
|||
import {DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
|
||||
import {EditorUiContext} from "../../../../ui/framework/core";
|
||||
import {EditorUIManager} from "../../../../ui/framework/manager";
|
||||
import {turtle} from "@codemirror/legacy-modes/mode/turtle";
|
||||
|
||||
|
||||
type TestEnv = {
|
||||
readonly container: HTMLDivElement;
|
||||
|
@ -47,6 +45,9 @@ type TestEnv = {
|
|||
readonly innerHTML: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated - Consider using `createTestContext` instead within the test case.
|
||||
*/
|
||||
export function initializeUnitTest(
|
||||
runTests: (testEnv: TestEnv) => void,
|
||||
editorConfig: CreateEditorArgs = {namespace: 'test', theme: {}},
|
||||
|
@ -795,6 +796,30 @@ export function expectNodeShapeToMatch(editor: LexicalEditor, expected: nodeShap
|
|||
expect(shape.children).toMatchObject(expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expect a given prop within the JSON editor state structure to be the given value.
|
||||
* Uses dot notation for the provided `propPath`. Example:
|
||||
* 0.5.cat => First child, Sixth child, cat property
|
||||
*/
|
||||
export function expectEditorStateJSONPropToEqual(editor: LexicalEditor, propPath: string, expected: any) {
|
||||
let currentItem: any = editor.getEditorState().toJSON().root;
|
||||
let currentPath = [];
|
||||
const pathParts = propPath.split('.');
|
||||
|
||||
for (const part of pathParts) {
|
||||
currentPath.push(part);
|
||||
const childAccess = Number.isInteger(Number(part)) && Array.isArray(currentItem.children);
|
||||
const target = childAccess ? currentItem.children : currentItem;
|
||||
|
||||
if (typeof target[part] === 'undefined') {
|
||||
throw new Error(`Could not resolve editor state at path ${currentPath.join('.')}`)
|
||||
}
|
||||
currentItem = target[part];
|
||||
}
|
||||
|
||||
expect(currentItem).toBe(expected);
|
||||
}
|
||||
|
||||
function formatHtml(s: string): string {
|
||||
return s.replace(/>\s+</g, '><').replace(/\s*\n\s*/g, ' ').trim();
|
||||
}
|
||||
|
|
|
@ -1,91 +1,76 @@
|
|||
import {initializeUnitTest} from "lexical/__tests__/utils";
|
||||
import {SerializedLinkNode} from "@lexical/link";
|
||||
import {
|
||||
createTestContext,
|
||||
dispatchKeydownEventForNode, expectEditorStateJSONPropToEqual,
|
||||
expectNodeShapeToMatch
|
||||
} from "lexical/__tests__/utils";
|
||||
import {
|
||||
$getRoot,
|
||||
ParagraphNode,
|
||||
SerializedParagraphNode,
|
||||
SerializedTextNode,
|
||||
TextNode
|
||||
} from "lexical";
|
||||
import {registerAutoLinks} from "../auto-links";
|
||||
|
||||
describe('Auto-link service tests', () => {
|
||||
initializeUnitTest((testEnv) => {
|
||||
test('space after link in text', async () => {
|
||||
const {editor} = createTestContext();
|
||||
registerAutoLinks(editor);
|
||||
let pNode!: ParagraphNode;
|
||||
|
||||
test('space after link in text', async () => {
|
||||
const {editor} = testEnv;
|
||||
editor.updateAndCommit(() => {
|
||||
pNode = new ParagraphNode();
|
||||
const text = new TextNode('Some https://example.com?test=true text');
|
||||
pNode.append(text);
|
||||
$getRoot().append(pNode);
|
||||
|
||||
registerAutoLinks(editor);
|
||||
let pNode!: ParagraphNode;
|
||||
|
||||
editor.update(() => {
|
||||
pNode = new ParagraphNode();
|
||||
const text = new TextNode('Some https://example.com?test=true text');
|
||||
pNode.append(text);
|
||||
$getRoot().append(pNode);
|
||||
|
||||
text.select(34, 34);
|
||||
});
|
||||
|
||||
editor.commitUpdates();
|
||||
|
||||
const pDomEl = editor.getElementByKey(pNode.getKey());
|
||||
const event = new KeyboardEvent('keydown', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
key: ' ',
|
||||
keyCode: 62,
|
||||
});
|
||||
pDomEl?.dispatchEvent(event);
|
||||
|
||||
editor.commitUpdates();
|
||||
|
||||
const paragraph = editor!.getEditorState().toJSON().root
|
||||
.children[0] as SerializedParagraphNode;
|
||||
expect(paragraph.children[1].type).toBe('link');
|
||||
|
||||
const link = paragraph.children[1] as SerializedLinkNode;
|
||||
expect(link.url).toBe('https://example.com?test=true');
|
||||
const linkText = link.children[0] as SerializedTextNode;
|
||||
expect(linkText.text).toBe('https://example.com?test=true');
|
||||
text.select(34, 34);
|
||||
});
|
||||
|
||||
test('enter after link in text', async () => {
|
||||
const {editor} = testEnv;
|
||||
dispatchKeydownEventForNode(pNode, editor, ' ');
|
||||
|
||||
registerAutoLinks(editor);
|
||||
let pNode!: ParagraphNode;
|
||||
expectEditorStateJSONPropToEqual(editor, '0.1.url', 'https://example.com?test=true');
|
||||
expectEditorStateJSONPropToEqual(editor, '0.1.0.text', 'https://example.com?test=true');
|
||||
});
|
||||
|
||||
editor.update(() => {
|
||||
pNode = new ParagraphNode();
|
||||
const text = new TextNode('Some https://example.com?test=true text');
|
||||
pNode.append(text);
|
||||
$getRoot().append(pNode);
|
||||
test('space after link at end of line', async () => {
|
||||
const {editor} = createTestContext();
|
||||
registerAutoLinks(editor);
|
||||
let pNode!: ParagraphNode;
|
||||
|
||||
text.select(34, 34);
|
||||
});
|
||||
editor.updateAndCommit(() => {
|
||||
pNode = new ParagraphNode();
|
||||
const text = new TextNode('Some https://example.com?test=true');
|
||||
pNode.append(text);
|
||||
$getRoot().append(pNode);
|
||||
|
||||
editor.commitUpdates();
|
||||
|
||||
const pDomEl = editor.getElementByKey(pNode.getKey());
|
||||
const event = new KeyboardEvent('keydown', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
key: 'Enter',
|
||||
keyCode: 66,
|
||||
});
|
||||
pDomEl?.dispatchEvent(event);
|
||||
|
||||
editor.commitUpdates();
|
||||
|
||||
const paragraph = editor!.getEditorState().toJSON().root
|
||||
.children[0] as SerializedParagraphNode;
|
||||
expect(paragraph.children[1].type).toBe('link');
|
||||
|
||||
const link = paragraph.children[1] as SerializedLinkNode;
|
||||
expect(link.url).toBe('https://example.com?test=true');
|
||||
const linkText = link.children[0] as SerializedTextNode;
|
||||
expect(linkText.text).toBe('https://example.com?test=true');
|
||||
text.selectEnd();
|
||||
});
|
||||
|
||||
dispatchKeydownEventForNode(pNode, editor, ' ');
|
||||
|
||||
expectNodeShapeToMatch(editor, [{type: 'paragraph', children: [
|
||||
{text: 'Some '},
|
||||
{type: 'link', children: [{text: 'https://example.com?test=true'}]}
|
||||
]}]);
|
||||
expectEditorStateJSONPropToEqual(editor, '0.1.url', 'https://example.com?test=true');
|
||||
});
|
||||
|
||||
test('enter after link in text', async () => {
|
||||
const {editor} = createTestContext();
|
||||
registerAutoLinks(editor);
|
||||
let pNode!: ParagraphNode;
|
||||
|
||||
editor.updateAndCommit(() => {
|
||||
pNode = new ParagraphNode();
|
||||
const text = new TextNode('Some https://example.com?test=true text');
|
||||
pNode.append(text);
|
||||
$getRoot().append(pNode);
|
||||
|
||||
text.select(34, 34);
|
||||
});
|
||||
|
||||
dispatchKeydownEventForNode(pNode, editor, 'Enter');
|
||||
|
||||
expectEditorStateJSONPropToEqual(editor, '0.1.url', 'https://example.com?test=true');
|
||||
expectEditorStateJSONPropToEqual(editor, '0.1.0.text', 'https://example.com?test=true');
|
||||
});
|
||||
});
|
|
@ -43,7 +43,7 @@ function handlePotentialLinkEvent(node: TextNode, selection: BaseSelection, edit
|
|||
linkNode.append(new TextNode(textSegment));
|
||||
|
||||
const splits = node.splitText(startIndex, cursorPoint);
|
||||
const targetIndex = splits.length === 3 ? 1 : 0;
|
||||
const targetIndex = startIndex > 0 ? 1 : 0;
|
||||
const targetText = splits[targetIndex];
|
||||
if (targetText) {
|
||||
targetText.replace(linkNode);
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
|
||||
## In progress
|
||||
|
||||
Reorg
|
||||
- Merge custom nodes into original nodes
|
||||
- Reduce down to use CommonBlockNode where possible
|
||||
- Remove existing formatType/ElementFormatType references (replaced with alignment).
|
||||
- Remove existing indent references (replaced with inset).
|
||||
//
|
||||
|
||||
## Main Todo
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user