diff --git a/packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx b/packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx index 70a0f13638c..293750ddb07 100644 --- a/packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx +++ b/packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx @@ -33,8 +33,10 @@ import { COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_LOW, createCommand, + EditorState, ElementNode, ParagraphNode, + RootNode, TextNode, } from 'lexical'; import * as React from 'react'; @@ -47,7 +49,7 @@ import { useState, } from 'react'; import {createPortal} from 'react-dom'; -import {createRoot} from 'react-dom/client'; +import {createRoot, Root} from 'react-dom/client'; import * as ReactTestUtils from 'react-dom/test-utils'; import { @@ -60,11 +62,12 @@ import { } from '../utils'; // No idea why we suddenly need to do this, but it fixes the tests // with latest experimental React version. +// @ts-ignore global.IS_REACT_ACT_ENVIRONMENT = true; describe('LexicalEditor tests', () => { let container: HTMLElement; - let reactRoot; + let reactRoot: Root; beforeEach(() => { container = document.createElement('div'); @@ -80,7 +83,10 @@ describe('LexicalEditor tests', () => { jest.restoreAllMocks(); }); - function useLexicalEditor(rootElementRef, onError) { + function useLexicalEditor( + rootElementRef: React.RefObject, + onError?: (error: Error) => void, + ) { const editor = useMemo( () => createTestEditor({ @@ -106,7 +112,7 @@ describe('LexicalEditor tests', () => { return editor; } - let editor: LexicalEditor = null; + let editor: LexicalEditor; function init(onError?: () => void) { const ref = createRef(); @@ -122,7 +128,7 @@ describe('LexicalEditor tests', () => { }); } - async function update(fn) { + async function update(fn: () => void) { editor.update(fn); return Promise.resolve().then(); @@ -294,7 +300,7 @@ describe('LexicalEditor tests', () => { editor.update( () => { log.push('A3'); - $getRoot().getLastDescendant().markDirty(); + $getRoot().getLastDescendant()!.markDirty(); }, { onUpdate: () => { @@ -446,7 +452,7 @@ describe('LexicalEditor tests', () => { await editor.update(() => { const root = $getRoot(); const paragraph = root.getFirstChild(); - paragraph.markDirty(); + paragraph!.markDirty(); }); testParagraphListener(); @@ -551,7 +557,7 @@ describe('LexicalEditor tests', () => { }); await editor.update(() => { - $getRoot().getLastDescendant().remove(); + $getRoot().getLastDescendant()!.remove(); hasBeenRemoved = true; }); @@ -593,8 +599,8 @@ describe('LexicalEditor tests', () => { await editor.update(() => { const root = $getRoot(); - const paragraph = root.getFirstChild(); - const textNode = paragraph.getFirstChild(); + const paragraph = root.getFirstChild() as ParagraphNode; + const textNode = paragraph.getFirstChild() as TextNode; textNode.getWritable(); @@ -610,9 +616,9 @@ describe('LexicalEditor tests', () => { }); describe('transforms on siblings', () => { - let textNodeKeys; - let textTransformCount; - let removeTransform; + let textNodeKeys: string[]; + let textTransformCount: number[]; + let removeTransform: () => void; beforeEach(async () => { init(); @@ -639,7 +645,7 @@ describe('LexicalEditor tests', () => { }); removeTransform = editor.registerNodeTransform(TextNode, (node) => { - textTransformCount[node.__text]++; + textTransformCount[Number(node.__text)]++; }); }); @@ -649,7 +655,7 @@ describe('LexicalEditor tests', () => { it('on remove', async () => { await editor.update(() => { - const textNode1 = $getNodeByKey(textNodeKeys[1]); + const textNode1 = $getNodeByKey(textNodeKeys[1])!; textNode1.remove(); }); expect(textTransformCount).toEqual([2, 1, 2, 1, 1, 1]); @@ -657,8 +663,8 @@ describe('LexicalEditor tests', () => { it('on replace', async () => { await editor.update(() => { - const textNode1 = $getNodeByKey(textNodeKeys[1]); - const textNode4 = $getNodeByKey(textNodeKeys[4]); + const textNode1 = $getNodeByKey(textNodeKeys[1])!; + const textNode4 = $getNodeByKey(textNodeKeys[4])!; textNode4.replace(textNode1); }); expect(textTransformCount).toEqual([2, 2, 2, 2, 1, 2]); @@ -666,8 +672,8 @@ describe('LexicalEditor tests', () => { it('on insertBefore', async () => { await editor.update(() => { - const textNode1 = $getNodeByKey(textNodeKeys[1]); - const textNode4 = $getNodeByKey(textNodeKeys[4]); + const textNode1 = $getNodeByKey(textNodeKeys[1])!; + const textNode4 = $getNodeByKey(textNodeKeys[4])!; textNode4.insertBefore(textNode1); }); expect(textTransformCount).toEqual([2, 2, 2, 2, 2, 1]); @@ -675,8 +681,8 @@ describe('LexicalEditor tests', () => { it('on insertAfter', async () => { await editor.update(() => { - const textNode1 = $getNodeByKey(textNodeKeys[1]); - const textNode4 = $getNodeByKey(textNodeKeys[4]); + const textNode1 = $getNodeByKey(textNodeKeys[1])!; + const textNode4 = $getNodeByKey(textNodeKeys[4])!; textNode4.insertAfter(textNode1); }); expect(textTransformCount).toEqual([2, 2, 2, 1, 2, 2]); @@ -684,7 +690,7 @@ describe('LexicalEditor tests', () => { it('on splitText', async () => { await editor.update(() => { - const textNode1 = $getNodeByKey(textNodeKeys[1]); + const textNode1 = $getNodeByKey(textNodeKeys[1]) as TextNode; textNode1.setTextContent('67'); textNode1.splitText(1); textTransformCount.push(0, 0); @@ -694,7 +700,7 @@ describe('LexicalEditor tests', () => { it('on append', async () => { await editor.update(() => { - const paragraph1 = $getRoot().getFirstChild(); + const paragraph1 = $getRoot().getFirstChild() as ParagraphNode; paragraph1.append($createTextNode('6').toggleUnmergeable()); textTransformCount.push(0); }); @@ -726,7 +732,7 @@ describe('LexicalEditor tests', () => { it('Should be able to update an editor state without a root element', () => { const ref = createRef(); - function TestBase({element}) { + function TestBase({element}: {element: HTMLElement | null}) { editor = useMemo(() => createTestEditor(), []); useEffect(() => { @@ -783,9 +789,9 @@ describe('LexicalEditor tests', () => { editor.update(() => { const root = $getRoot(); root - .getFirstChild() - .getFirstChild() - .getFirstChild() + .getFirstChild()! + .getFirstChild()! + .getFirstChild()! .setTextContent('Foo'); }); @@ -799,13 +805,13 @@ describe('LexicalEditor tests', () => { const rootListener = jest.fn(); const updateListener = jest.fn(); - function TestBase({changeElement}) { + function TestBase({changeElement}: {changeElement: boolean}) { editor = useMemo(() => createTestEditor(), []); useEffect(() => { editor.update(() => { const root = $getRoot(); - const firstChild = root.getFirstChild(); + const firstChild = root.getFirstChild() as ParagraphNode | null; const text = changeElement ? 'Change successful' : 'Not changed'; if (firstChild === null) { @@ -814,7 +820,7 @@ describe('LexicalEditor tests', () => { paragraph.append(textNode); root.append(paragraph); } else { - const textNode = firstChild.getFirstChild(); + const textNode = firstChild.getFirstChild() as TextNode; textNode.setTextContent(text); } }); @@ -828,7 +834,7 @@ describe('LexicalEditor tests', () => { return editor.registerUpdateListener(updateListener); }, []); - const ref = useCallback((node) => { + const ref = useCallback((node: HTMLElement | null) => { editor.setRootElement(node); }, []); @@ -897,7 +903,7 @@ describe('LexicalEditor tests', () => { () => Object.keys(decorators).map((nodeKey) => { const reactDecorator = decorators[nodeKey]; - const element = editor.getElementByKey(nodeKey); + const element = editor.getElementByKey(nodeKey)!; return createPortal(reactDecorator, element); }), @@ -917,7 +923,7 @@ describe('LexicalEditor tests', () => { editor.registerRootListener(listener); }, []); - const ref = useCallback((node) => { + const ref = useCallback((node: HTMLDivElement | null) => { editor.setRootElement(node); }, []); @@ -954,7 +960,7 @@ describe('LexicalEditor tests', () => { it('Should correctly render React component into Lexical node #2', async () => { const listener = jest.fn(); - function Test({divKey}): JSX.Element { + function Test({divKey}: {divKey: number}): JSX.Element { function TestPlugin() { [editor] = useLexicalComposerContext(); @@ -969,6 +975,7 @@ describe('LexicalEditor tests', () => { } @@ -1006,7 +1013,7 @@ describe('LexicalEditor tests', () => { editor.getEditorState().read(() => { const root = $getRoot(); - const paragraph = root.getFirstChild(); + const paragraph = root.getFirstChild()!; expect(root).toEqual({ __cachedText: '', __dir: null, @@ -1039,13 +1046,13 @@ describe('LexicalEditor tests', () => { }); describe('parseEditorState()', () => { - let originalText; - let parsedParagraph; - let parsedRoot; - let parsedText; - let paragraphKey; - let textKey; - let parsedEditorState; + let originalText: TextNode; + let parsedParagraph: ParagraphNode; + let parsedRoot: RootNode; + let parsedText: TextNode; + let paragraphKey: string; + let textKey: string; + let parsedEditorState: EditorState; it('exportJSON API - parses parsed JSON', async () => { await update(() => { @@ -1082,9 +1089,9 @@ describe('LexicalEditor tests', () => { parsedEditorState = editor.parseEditorState(stringifiedEditorState); parsedEditorState.read(() => { parsedRoot = $getRoot(); - parsedParagraph = parsedRoot.getFirstChild(); + parsedParagraph = parsedRoot.getFirstChild() as ParagraphNode; paragraphKey = parsedParagraph.getKey(); - parsedText = parsedParagraph.getFirstChild(); + parsedText = parsedParagraph.getFirstChild() as TextNode; textKey = parsedText.getKey(); }); }); @@ -1160,9 +1167,9 @@ describe('LexicalEditor tests', () => { parsedEditorState = editor.parseEditorState(stringifiedEditorState); parsedEditorState.read(() => { parsedRoot = $getRoot(); - parsedParagraph = parsedRoot.getFirstChild(); + parsedParagraph = parsedRoot.getFirstChild() as ParagraphNode; paragraphKey = parsedParagraph.getKey(); - parsedText = parsedParagraph.getFirstChild(); + parsedText = parsedParagraph.getFirstChild() as TextNode; textKey = parsedText.getKey(); }); }); @@ -1223,8 +1230,8 @@ describe('LexicalEditor tests', () => { describe('$parseSerializedNode()', () => { it('parses serialized nodes', async () => { const expectedTextContent = 'Hello world\n\nHello world'; - let actualTextContent; - let root; + let actualTextContent: string; + let root: RootNode; await update(() => { root = $getRoot(); root.clear(); @@ -1241,7 +1248,7 @@ describe('LexicalEditor tests', () => { root.append(...children); actualTextContent = root.getTextContent(); }); - expect(actualTextContent).toEqual(expectedTextContent); + expect(actualTextContent!).toEqual(expectedTextContent); }); }); @@ -1271,14 +1278,14 @@ describe('LexicalEditor tests', () => { return [elementNode, textNode]; } - let paragraphNodeKey; - let elementNode1Key; - let textNode1Key; - let elementNode2Key; - let textNode2Key; + let paragraphNodeKey: string; + let elementNode1Key: string; + let textNode1Key: string; + let elementNode2Key: string; + let textNode2Key: string; await update(() => { - const paragraph: ParagraphNode = $getRoot().getFirstChild(); + const paragraph = $getRoot().getFirstChild() as ParagraphNode; paragraphNodeKey = paragraph.getKey(); const [elementNode1, textNode1] = createElementNodeWithText('A'); @@ -1293,16 +1300,16 @@ describe('LexicalEditor tests', () => { }); await update(() => { - const elementNode1: ElementNode = $getNodeByKey(elementNode1Key); - const elementNode2: TextNode = $getNodeByKey(elementNode2Key); + const elementNode1 = $getNodeByKey(elementNode1Key) as ElementNode; + const elementNode2 = $getNodeByKey(elementNode2Key) as TextNode; elementNode1.append(elementNode2); }); const keys = [ - paragraphNodeKey, - elementNode1Key, - textNode1Key, - elementNode2Key, - textNode2Key, + paragraphNodeKey!, + elementNode1Key!, + textNode1Key!, + elementNode2Key!, + textNode2Key!, ]; for (let i = 0; i < keys.length; i++) { @@ -1326,11 +1333,11 @@ describe('LexicalEditor tests', () => { return elementNode; } - let elementNode1Key; - let elementNode2Key; + let elementNode1Key: string; + let elementNode2Key: string; await update(() => { - const paragraph: ParagraphNode = $getRoot().getFirstChild(); + const paragraph = $getRoot().getFirstChild() as ParagraphNode; const elementNode1 = createElementNodeWithText('A'); elementNode1Key = elementNode1.getKey(); @@ -1342,8 +1349,8 @@ describe('LexicalEditor tests', () => { }); await update(() => { - const elementNode1 = $getNodeByKey(elementNode1Key); - const elementNode2 = $getNodeByKey(elementNode2Key); + const elementNode1 = $getNodeByKey(elementNode1Key) as TextNode; + const elementNode2 = $getNodeByKey(elementNode2Key) as ElementNode; elementNode2.append(elementNode1); }); @@ -1361,12 +1368,12 @@ describe('LexicalEditor tests', () => { return elementNode; } - let elementNode1Key; - let elementNode2Key; - let elementNode3Key; + let elementNode1Key: string; + let elementNode2Key: string; + let elementNode3Key: string; await update(() => { - const paragraph: ParagraphNode = $getRoot().getFirstChild(); + const paragraph = $getRoot().getFirstChild() as ParagraphNode; const elementNode1 = createElementNodeWithText('A'); elementNode1Key = elementNode1.getKey(); @@ -1381,9 +1388,9 @@ describe('LexicalEditor tests', () => { }); await update(() => { - const elementNode1 = $getNodeByKey(elementNode1Key); - const elementNode2 = $getNodeByKey(elementNode2Key); - const elementNode3: TextNode = $getNodeByKey(elementNode3Key); + const elementNode1 = $getNodeByKey(elementNode1Key) as ElementNode; + const elementNode2 = $getNodeByKey(elementNode2Key) as ElementNode; + const elementNode3 = $getNodeByKey(elementNode3Key) as TextNode; elementNode2.append(elementNode3); elementNode1.append(elementNode3); }); @@ -1519,7 +1526,7 @@ describe('LexicalEditor tests', () => { await editor.update(() => { const root = $getRoot(); - const child = root.getLastDescendant(); + const child = root.getLastDescendant()!; child.insertAfter($createTextNode('bar')); }); @@ -1528,7 +1535,7 @@ describe('LexicalEditor tests', () => { await editor.update(() => { const root = $getRoot(); - const child = root.getLastDescendant(); + const child = root.getLastDescendant()!; child.insertAfter($createLineBreakNode()); }); @@ -1553,7 +1560,7 @@ describe('LexicalEditor tests', () => { const root = $getRoot(); const paragraph = $createParagraphNode(); const paragraph2 = $createParagraphNode(); - root.getLastChild().insertAfter(paragraph); + root.getLastChild()!.insertAfter(paragraph); paragraph.append($createTextNode('bar2')); paragraph2.append($createTextNode('yar2')); paragraph.insertAfter(paragraph2); @@ -1570,8 +1577,8 @@ describe('LexicalEditor tests', () => { const textNodeMutations = jest.fn(); editor.registerMutationListener(ParagraphNode, paragraphNodeMutations); editor.registerMutationListener(TextNode, textNodeMutations); - const paragraphKeys = []; - const textNodeKeys = []; + const paragraphKeys: string[] = []; + const textNodeKeys: string[] = []; // No await intentional (batch with next) editor.update(() => { @@ -1585,7 +1592,7 @@ describe('LexicalEditor tests', () => { }); await editor.update(() => { - const textNode = $getNodeByKey(textNodeKeys[0]); + const textNode = $getNodeByKey(textNodeKeys[0]) as TextNode; const textNode2 = $createTextNode('bar').toggleFormat('bold'); const textNode3 = $createTextNode('xyz').toggleFormat('italic'); textNode.insertAfter(textNode2); @@ -1642,10 +1649,10 @@ describe('LexicalEditor tests', () => { const initialEditorState = editor.getEditorState(); const textNodeMutations = jest.fn(); editor.registerMutationListener(TextNode, textNodeMutations); - const textNodeKeys = []; + const textNodeKeys: string[] = []; await editor.update(() => { - const paragraph = $getRoot().getFirstChild(); + const paragraph = $getRoot().getFirstChild() as ParagraphNode; const textNode1 = $createTextNode('foo'); paragraph.append(textNode1); textNodeKeys.push(textNode1.getKey()); @@ -1660,7 +1667,7 @@ describe('LexicalEditor tests', () => { ); await editor.update(() => { - const paragraph = $getRoot().getFirstChild(); + const paragraph = $getRoot().getFirstChild() as ParagraphNode; const textNode2 = $createTextNode('bar').toggleFormat('bold'); const textNode3 = $createTextNode('xyz').toggleFormat('italic'); paragraph.append(textNode2, textNode3); @@ -1711,7 +1718,7 @@ describe('LexicalEditor tests', () => { const textNodeMutations = jest.fn(); editor.registerMutationListener(TextNode, textNodeMutations); - const textNodeKeys = []; + const textNodeKeys: string[] = []; await editor.update(() => { const root = $getRoot(); @@ -1725,14 +1732,14 @@ describe('LexicalEditor tests', () => { }); await editor.update(() => { - const paragraph = $getRoot().getFirstChild(); + const paragraph = $getRoot().getFirstChild() as ParagraphNode; const textNode3 = $createTextNode('xyz').toggleFormat('bold'); paragraph.append(textNode3); textNodeKeys.push(textNode3.getKey()); }); await editor.update(() => { - const textNode3 = $getNodeByKey(textNodeKeys[2]); + const textNode3 = $getNodeByKey(textNodeKeys[2]) as TextNode; textNode3.toggleFormat('bold'); // Normalize with foobar }); @@ -1759,8 +1766,8 @@ describe('LexicalEditor tests', () => { editor.registerMutationListener(ParagraphNode, paragraphNodeMutations); editor.registerMutationListener(TextNode, textNodeMutations); - const paragraphNodeKeys = []; - const textNodeKeys = []; + const paragraphNodeKeys: string[] = []; + const textNodeKeys: string[] = []; await editor.update(() => { const root = $getRoot(); @@ -1784,13 +1791,15 @@ describe('LexicalEditor tests', () => { // Change first text node's content. await editor.update(() => { - const textNode1 = $getNodeByKey(textNodeKeys[0]); + const textNode1 = $getNodeByKey(textNodeKeys[0]) as TextNode; textNode1.setTextContent('Test'); // Normalize with foobar }); // Append text node to paragraph. await editor.update(() => { - const paragraphNode1 = $getNodeByKey(paragraphNodeKeys[0]); + const paragraphNode1 = $getNodeByKey( + paragraphNodeKeys[0], + ) as ParagraphNode; const textNode1 = $createTextNode('foo'); paragraphNode1.append(textNode1); }); @@ -1808,8 +1817,8 @@ describe('LexicalEditor tests', () => { // Show ParagraphNode was updated when new text node was appended. expect(paragraphNodeMutation2[0].get(paragraphNodeKeys[0])).toBe('updated'); - let tableCellKey; - let tableRowKey; + let tableCellKey: string; + let tableRowKey: string; const tableCellMutations = jest.fn(); const tableRowMutations = jest.fn(); @@ -1834,14 +1843,14 @@ describe('LexicalEditor tests', () => { // Add New Table Cell To Row await editor.update(() => { - const tableRow = $getNodeByKey(tableRowKey); + const tableRow = $getNodeByKey(tableRowKey) as TableRowNode; const tableCell = $createTableCellNode(0); tableRow.append(tableCell); }); // Update Table Cell await editor.update(() => { - const tableCell = $getNodeByKey(tableCellKey); + const tableCell = $getNodeByKey(tableCellKey) as TableCellNode; tableCell.toggleHeaderStyle(1); }); @@ -1849,13 +1858,13 @@ describe('LexicalEditor tests', () => { const tableCellMutation3 = tableCellMutations.mock.calls[2]; // Show table cell is updated when header value changes. - expect(tableCellMutation3[0].get(tableCellKey)).toBe('updated'); + expect(tableCellMutation3[0].get(tableCellKey!)).toBe('updated'); expect(tableRowMutations.mock.calls.length).toBe(2); const tableRowMutation2 = tableRowMutations.mock.calls[1]; // Show row is updated when a new child is added. - expect(tableRowMutation2[0].get(tableRowKey)).toBe('updated'); + expect(tableRowMutation2[0].get(tableRowKey!)).toBe('updated'); }); it('editable listener', () => { diff --git a/packages/lexical/src/__tests__/unit/LexicalEditorState.test.ts b/packages/lexical/src/__tests__/unit/LexicalEditorState.test.ts index 3c8634b0da8..26678f6ca87 100644 --- a/packages/lexical/src/__tests__/unit/LexicalEditorState.test.ts +++ b/packages/lexical/src/__tests__/unit/LexicalEditorState.test.ts @@ -6,10 +6,16 @@ * */ -import {$createParagraphNode, $createTextNode, $getRoot} from 'lexical'; +import { + $createParagraphNode, + $createTextNode, + $getRoot, + ParagraphNode, + TextNode, +} from 'lexical'; import {EditorState} from '../../LexicalEditorState'; -import {$createRootNode} from '../../nodes/LexicalRootNode'; +import {$createRootNode, RootNode} from '../../nodes/LexicalRootNode'; import {initializeUnitTest} from '../utils'; describe('LexicalEditorState tests', () => { @@ -33,14 +39,14 @@ describe('LexicalEditorState tests', () => { $getRoot().append(paragraph); }); - let root = null; - let paragraph = null; - let text = null; + let root!: RootNode; + let paragraph!: ParagraphNode; + let text!: TextNode; editor.getEditorState().read(() => { root = $getRoot(); - paragraph = root.getFirstChild(); - text = paragraph.getFirstChild(); + paragraph = root.getFirstChild()!; + text = paragraph.getFirstChild()!; }); expect(root).toEqual({ @@ -112,7 +118,7 @@ describe('LexicalEditorState tests', () => { // Remove the first node, which should cause a GC for everything await editor.update(() => { - $getRoot().getFirstChild().remove(); + $getRoot().getFirstChild()!.remove(); }); expect(editor.getEditorState()._nodeMap).toEqual( diff --git a/packages/lexical/src/__tests__/unit/LexicalListPlugin.test.tsx b/packages/lexical/src/__tests__/unit/LexicalListPlugin.test.tsx index 398d6582a3c..ce83439276b 100644 --- a/packages/lexical/src/__tests__/unit/LexicalListPlugin.test.tsx +++ b/packages/lexical/src/__tests__/unit/LexicalListPlugin.test.tsx @@ -22,7 +22,7 @@ import { TestComposer, } from 'lexical/src/__tests__/utils'; import * as React from 'react'; -import {createRoot} from 'react-dom/client'; +import {createRoot, Root} from 'react-dom/client'; import * as ReactTestUtils from 'react-dom/test-utils'; import { @@ -31,8 +31,8 @@ import { } from '../../../../lexical-list/src/index'; describe('@lexical/list tests', () => { - let container: HTMLDivElement | null = null; - let reactRoot; + let container: HTMLDivElement; + let reactRoot: Root; beforeEach(() => { container = document.createElement('div'); @@ -41,9 +41,8 @@ describe('@lexical/list tests', () => { }); afterEach(() => { - if (container !== null) { - document.body.removeChild(container); - } + container.remove(); + // @ts-ignore container = null; jest.restoreAllMocks(); @@ -88,7 +87,7 @@ describe('@lexical/list tests', () => { }); expectHtmlToBeEqual( - container?.innerHTML || '', + container.innerHTML, html`
{ }); expectHtmlToBeEqual( - container?.innerHTML || '', + container.innerHTML, html`
{ }); expectHtmlToBeEqual( - container?.innerHTML || '', + container.innerHTML, html`
{ }); expectHtmlToBeEqual( - container?.innerHTML || '', + container.innerHTML, html`
{ }); expectHtmlToBeEqual( - container?.innerHTML || '', + container.innerHTML, html`
{ initializeUnitTest( (testEnv) => { - let paragraphNode; - let textNode; + let paragraphNode: ParagraphNode; + let textNode: TextNode; beforeEach(async () => { const {editor} = testEnv; @@ -108,7 +108,7 @@ describe('LexicalNode tests', () => { test('LexicalNode.isAttached()', async () => { const {editor} = testEnv; - let node; + let node: LexicalNode; await editor.update(() => { node = new LexicalNode('__custom_key__'); @@ -125,7 +125,7 @@ describe('LexicalNode tests', () => { test('LexicalNode.isSelected()', async () => { const {editor} = testEnv; - let node; + let node: LexicalNode; await editor.update(() => { node = new LexicalNode('__custom_key__'); @@ -168,8 +168,8 @@ describe('LexicalNode tests', () => { test('LexicalNode.isSelected(): selected block node range', async () => { const {editor} = testEnv; - let newParagraphNode; - let newTextNode; + let newParagraphNode: ParagraphNode; + let newTextNode: TextNode; await editor.update(() => { expect(paragraphNode.isSelected()).toBe(false); @@ -220,8 +220,8 @@ describe('LexicalNode tests', () => { test('LexicalNode.isSelected(): with custom range selection', async () => { const {editor} = testEnv; - let newParagraphNode; - let newTextNode; + let newParagraphNode: ParagraphNode; + let newTextNode: TextNode; await editor.update(() => { expect(paragraphNode.isSelected()).toBe(false); @@ -341,7 +341,7 @@ describe('LexicalNode tests', () => { test('LexicalNode.getPreviousSibling()', async () => { const {editor} = testEnv; - let barTextNode; + let barTextNode: TextNode; await editor.update(() => { barTextNode = new TextNode('bar'); @@ -365,8 +365,8 @@ describe('LexicalNode tests', () => { test('LexicalNode.getPreviousSiblings()', async () => { const {editor} = testEnv; - let barTextNode; - let bazTextNode; + let barTextNode: TextNode; + let bazTextNode: TextNode; await editor.update(() => { barTextNode = new TextNode('bar'); @@ -404,7 +404,7 @@ describe('LexicalNode tests', () => { test('LexicalNode.getNextSibling()', async () => { const {editor} = testEnv; - let barTextNode; + let barTextNode: TextNode; await editor.update(() => { barTextNode = new TextNode('bar'); @@ -425,8 +425,8 @@ describe('LexicalNode tests', () => { test('LexicalNode.getNextSiblings()', async () => { const {editor} = testEnv; - let barTextNode; - let bazTextNode; + let barTextNode: TextNode; + let bazTextNode: TextNode; await editor.update(() => { barTextNode = new TextNode('bar'); @@ -453,11 +453,11 @@ describe('LexicalNode tests', () => { test('LexicalNode.getCommonAncestor()', async () => { const {editor} = testEnv; - let quxTextNode; - let barParagraphNode; - let barTextNode; - let bazParagraphNode; - let bazTextNode; + let quxTextNode: TextNode; + let barParagraphNode: ParagraphNode; + let barTextNode: TextNode; + let bazParagraphNode: ParagraphNode; + let bazTextNode: TextNode; await editor.update(() => { const rootNode = $getRoot(); @@ -499,8 +499,8 @@ describe('LexicalNode tests', () => { test('LexicalNode.isBefore()', async () => { const {editor} = testEnv; - let barTextNode; - let bazTextNode; + let barTextNode: TextNode; + let bazTextNode: TextNode; await editor.update(() => { barTextNode = new TextNode('bar'); @@ -542,10 +542,10 @@ describe('LexicalNode tests', () => { test('LexicalNode.getNodesBetween()', async () => { const {editor} = testEnv; - let barTextNode; - let bazTextNode; - let newParagraphNode; - let quxTextNode; + let barTextNode: TextNode; + let bazTextNode: TextNode; + let newParagraphNode: ParagraphNode; + let quxTextNode: TextNode; await editor.update(() => { const rootNode = $getRoot(); @@ -590,7 +590,7 @@ describe('LexicalNode tests', () => { test('LexicalNode.isToken()', async () => { const {editor} = testEnv; - let tokenTextNode; + let tokenTextNode: TextNode; await editor.update(() => { tokenTextNode = new TextNode('token').setMode('token'); @@ -602,7 +602,7 @@ describe('LexicalNode tests', () => { ); await editor.getEditorState().read(() => { - expect(textNode.isToken(textNode)).toBe(false); + expect(textNode.isToken()).toBe(false); expect(tokenTextNode.isToken()).toBe(true); }); expect(() => textNode.isToken()).toThrow(); @@ -610,7 +610,7 @@ describe('LexicalNode tests', () => { test('LexicalNode.isSegmented()', async () => { const {editor} = testEnv; - let segmentedTextNode; + let segmentedTextNode: TextNode; await editor.update(() => { segmentedTextNode = new TextNode('segmented').setMode('segmented'); @@ -622,7 +622,7 @@ describe('LexicalNode tests', () => { ); await editor.getEditorState().read(() => { - expect(textNode.isSegmented(textNode)).toBe(false); + expect(textNode.isSegmented()).toBe(false); expect(segmentedTextNode.isSegmented()).toBe(true); }); expect(() => textNode.isSegmented()).toThrow(); @@ -630,7 +630,7 @@ describe('LexicalNode tests', () => { test('LexicalNode.isDirectionless()', async () => { const {editor} = testEnv; - let directionlessTextNode; + let directionlessTextNode: TextNode; await editor.update(() => { directionlessTextNode = new TextNode( @@ -662,9 +662,9 @@ describe('LexicalNode tests', () => { test('LexicalNode.getLatest(): garbage collected node', async () => { const {editor} = testEnv; - let node; - let text; - let block; + let node: LexicalNode; + let text: TextNode; + let block: TestElementNode; await editor.update(() => { node = new LexicalNode(); @@ -762,6 +762,7 @@ describe('LexicalNode tests', () => { const {editor} = testEnv; await editor.getEditorState().read(() => { + // @ts-expect-error expect(() => textNode.replace()).toThrow(); }); expect(() => textNode.remove()).toThrow(); @@ -773,7 +774,7 @@ describe('LexicalNode tests', () => { expect(testEnv.outerHTML).toBe( '

foo

', ); - let barTextNode; + let barTextNode: TextNode; await editor.update(() => { const rootNode = $getRoot(); @@ -895,8 +896,10 @@ describe('LexicalNode tests', () => { const {editor} = testEnv; await editor.getEditorState().read(() => { + // @ts-expect-error expect(() => textNode.insertAfter()).toThrow(); }); + // @ts-expect-error expect(() => textNode.insertAfter()).toThrow(); }); @@ -971,7 +974,12 @@ describe('LexicalNode tests', () => { test('LexicalNode.insertAfter() move blocks around', async () => { const {editor} = testEnv; - let block1, block2, block3, text1, text2, text3; + let block1: ParagraphNode, + block2: ParagraphNode, + block3: ParagraphNode, + text1: TextNode, + text2: TextNode, + text3: TextNode; await editor.update(() => { const root = $getRoot(); @@ -1003,7 +1011,12 @@ describe('LexicalNode tests', () => { test('LexicalNode.insertAfter() move blocks around #2', async () => { const {editor} = testEnv; - let block1, block2, block3, text1, text2, text3; + let block1: ParagraphNode, + block2: ParagraphNode, + block3: ParagraphNode, + text1: TextNode, + text2: TextNode, + text3: TextNode; await editor.update(() => { const root = $getRoot(); @@ -1043,8 +1056,10 @@ describe('LexicalNode tests', () => { const {editor} = testEnv; await editor.getEditorState().read(() => { + // @ts-expect-error expect(() => textNode.insertBefore()).toThrow(); }); + // @ts-expect-error expect(() => textNode.insertBefore()).toThrow(); }); diff --git a/packages/lexical/src/__tests__/unit/LexicalNormalization.test.tsx b/packages/lexical/src/__tests__/unit/LexicalNormalization.test.tsx index 06d3fad8df6..c9227657539 100644 --- a/packages/lexical/src/__tests__/unit/LexicalNormalization.test.tsx +++ b/packages/lexical/src/__tests__/unit/LexicalNormalization.test.tsx @@ -6,7 +6,12 @@ * */ -import {$createParagraphNode, $createTextNode, $getRoot} from 'lexical'; +import { + $createParagraphNode, + $createTextNode, + $getRoot, + RangeSelection, +} from 'lexical'; import {$normalizeSelection} from '../../LexicalNormalization'; import { @@ -19,8 +24,9 @@ describe('LexicalNormalization tests', () => { initializeUnitTest((testEnv) => { describe('$normalizeSelection', () => { for (const reversed of [false, true]) { - const getAnchor = (x) => (reversed ? x.focus : x.anchor); - const getFocus = (x) => (reversed ? x.anchor : x.focus); + const getAnchor = (x: RangeSelection) => + reversed ? x.focus : x.anchor; + const getFocus = (x: RangeSelection) => (reversed ? x.anchor : x.focus); const reversedStr = reversed ? ' (reversed)' : ''; test(`paragraph to text nodes${reversedStr}`, async () => { diff --git a/packages/lexical/src/__tests__/unit/LexicalUtils.test.ts b/packages/lexical/src/__tests__/unit/LexicalUtils.test.ts index e8d870ae59d..abc6ec06924 100644 --- a/packages/lexical/src/__tests__/unit/LexicalUtils.test.ts +++ b/packages/lexical/src/__tests__/unit/LexicalUtils.test.ts @@ -46,6 +46,7 @@ describe('LexicalUtils tests', () => { test('scheduleMicroTask(): promise', async () => { jest.resetModules(); + // @ts-ignore window.queueMicrotask = undefined; let flag = false; @@ -96,7 +97,7 @@ describe('LexicalUtils tests', () => { test('isSelectionWithinEditor()', async () => { const {editor} = testEnv; - let textNode; + let textNode: TextNode; await editor.update(() => { const root = $getRoot(); @@ -107,7 +108,7 @@ describe('LexicalUtils tests', () => { }); await editor.update(() => { - const domSelection = window.getSelection(); + const domSelection = window.getSelection()!; expect( isSelectionWithinEditor( @@ -121,7 +122,7 @@ describe('LexicalUtils tests', () => { }); await editor.update(() => { - const domSelection = window.getSelection(); + const domSelection = window.getSelection()!; expect( isSelectionWithinEditor( @@ -183,8 +184,8 @@ describe('LexicalUtils tests', () => { test('$getNodeByKey', async () => { const {editor} = testEnv; - let paragraphNode; - let textNode; + let paragraphNode: ParagraphNode; + let textNode: TextNode; await editor.update(() => { const rootNode = $getRoot(); @@ -206,7 +207,7 @@ describe('LexicalUtils tests', () => { test('$nodesOfType', async () => { const {editor} = testEnv; - const paragraphKeys = []; + const paragraphKeys: string[] = []; const $paragraphKeys = () => $nodesOfType(ParagraphNode).map((node) => node.getKey()); diff --git a/packages/lexical/src/__tests__/utils/index.tsx b/packages/lexical/src/__tests__/utils/index.tsx index a76e1f5c41d..142ba6fc08d 100644 --- a/packages/lexical/src/__tests__/utils/index.tsx +++ b/packages/lexical/src/__tests__/utils/index.tsx @@ -41,7 +41,7 @@ import { SerializedLexicalNode, SerializedTextNode, } from 'lexical/src'; -import prettier from 'prettier'; +import {format} from 'prettier'; import * as React from 'react'; import {createRef} from 'react'; import {createRoot} from 'react-dom/client'; @@ -51,10 +51,10 @@ import {CreateEditorArgs, LexicalNodeReplacement} from '../../LexicalEditor'; import {resetRandomKey} from '../../LexicalUtils'; type TestEnv = { - container: HTMLDivElement | null; - editor: LexicalEditor | null; - outerHTML: string; - innerHTML: string; + readonly container: HTMLDivElement; + readonly editor: LexicalEditor; + readonly outerHTML: string; + readonly innerHTML: string; }; export function initializeUnitTest( @@ -62,15 +62,37 @@ export function initializeUnitTest( editorConfig: CreateEditorArgs = {namespace: 'test', theme: {}}, plugins?: React.ReactNode, ) { - const testEnv: TestEnv = { - container: null, - editor: null, + const testEnv = { + _container: null as HTMLDivElement | null, + _editor: null as LexicalEditor | null, + get container() { + if (!this._container) { + throw new Error('testEnv.container not initialized.'); + } + return this._container; + }, + set container(container) { + this._container = container; + }, + get editor() { + if (!this._editor) { + throw new Error('testEnv.editor not initialized.'); + } + return this._editor; + }, + set editor(editor) { + this._editor = editor; + }, get innerHTML() { - return this.container.firstChild.innerHTML; + return (this.container.firstChild as HTMLElement).innerHTML; }, get outerHTML() { return this.container.innerHTML; }, + reset() { + this._container = null; + this._editor = null; + }, }; beforeEach(async () => { @@ -80,7 +102,9 @@ export function initializeUnitTest( document.body.appendChild(testEnv.container); const ref = createRef(); - const useLexicalEditor = (rootElementRef) => { + const useLexicalEditor = ( + rootElementRef: React.RefObject, + ) => { const lexicalEditor = React.useMemo(() => { const lexical = createTestEditor(editorConfig); return lexical; @@ -94,14 +118,13 @@ export function initializeUnitTest( }; const Editor = () => { - const editor = useLexicalEditor(ref); - testEnv.editor = editor; + testEnv.editor = useLexicalEditor(ref); const context = createLexicalComposerContext( null, editorConfig?.theme ?? {}, ); return ( - +
{plugins} @@ -109,13 +132,13 @@ export function initializeUnitTest( }; ReactTestUtils.act(() => { - createRoot(testEnv.container as HTMLElement).render(); + createRoot(testEnv.container).render(); }); }); afterEach(() => { - document.body.removeChild(testEnv.container as HTMLElement); - testEnv.container = null; + document.body.removeChild(testEnv.container); + testEnv.reset(); }); runTests(testEnv); @@ -321,7 +344,7 @@ export class TestSegmentedNode extends TextNode { } } -export function $createTestSegmentedNode(text): TestSegmentedNode { +export function $createTestSegmentedNode(text: string): TestSegmentedNode { return new TestSegmentedNode(text).setMode('segmented'); } @@ -429,7 +452,7 @@ export class TestDecoratorNode extends DecoratorNode { } } -function Decorator({text}): JSX.Element { +function Decorator({text}: {text: string}): JSX.Element { return {text}; } @@ -479,7 +502,7 @@ export function TestComposer({ }, ...config, namespace: '', - nodes: DEFAULT_NODES.concat(customNodes), + nodes: DEFAULT_NODES.concat(customNodes || []), }}> {children} @@ -520,7 +543,7 @@ export function createTestHeadlessEditor(): LexicalEditor { }); } -export function $assertRangeSelection(selection): RangeSelection { +export function $assertRangeSelection(selection: unknown): RangeSelection { if (!$isRangeSelection(selection)) { throw new Error(`Expected RangeSelection, got ${selection}`); } @@ -536,28 +559,29 @@ export function invariant(cond?: boolean, message?: string): asserts cond { export class DataTransferMock implements DataTransfer { _data: Map = new Map(); - dropEffect: 'none' | 'copy' | 'link' | 'move'; - effectAllowed: - | 'none' - | 'copy' - | 'copyLink' - | 'copyMove' - | 'link' - | 'linkMove' - | 'move' - | 'all' - | 'uninitialized'; - readonly files: FileList; - readonly items: DataTransferItemList; - readonly types: ReadonlyArray; - clearData(format?: string): void { + get dropEffect(): DataTransfer['dropEffect'] { + throw new Error('Getter not implemented.'); + } + get effectAllowed(): DataTransfer['effectAllowed'] { + throw new Error('Getter not implemented.'); + } + get files(): FileList { + throw new Error('Getter not implemented.'); + } + get items(): DataTransferItemList { + throw new Error('Getter not implemented.'); + } + get types(): ReadonlyArray { + return Array.from(this._data.keys()); + } + clearData(dataType?: string): void { // } - getData(dataType): string { + getData(dataType: string): string { return this._data.get(dataType) || ''; } - setData(format: string, data: string): void { - this._data.set(format, data); + setData(dataType: string, data: string): void { + this._data.set(dataType, data); } setDragImage(image: Element, x: number, y: number): void { // @@ -565,19 +589,45 @@ export class DataTransferMock implements DataTransfer { } export class EventMock implements Event { - bubbles: boolean; - cancelBubble: boolean; - cancelable: boolean; - composed: boolean; - currentTarget: EventTarget | null; - defaultPrevented: boolean; - eventPhase: number; - isTrusted: boolean; - returnValue: boolean; - srcElement: EventTarget | null; - target: EventTarget | null; - timeStamp: number; - type: string; + get bubbles(): boolean { + throw new Error('Getter not implemented.'); + } + get cancelBubble(): boolean { + throw new Error('Gettter not implemented.'); + } + get cancelable(): boolean { + throw new Error('Gettter not implemented.'); + } + get composed(): boolean { + throw new Error('Gettter not implemented.'); + } + get currentTarget(): EventTarget | null { + throw new Error('Gettter not implemented.'); + } + get defaultPrevented(): boolean { + throw new Error('Gettter not implemented.'); + } + get eventPhase(): number { + throw new Error('Gettter not implemented.'); + } + get isTrusted(): boolean { + throw new Error('Gettter not implemented.'); + } + get returnValue(): boolean { + throw new Error('Gettter not implemented.'); + } + get srcElement(): EventTarget | null { + throw new Error('Gettter not implemented.'); + } + get target(): EventTarget | null { + throw new Error('Gettter not implemented.'); + } + get timeStamp(): number { + throw new Error('Gettter not implemented.'); + } + get type(): string { + throw new Error('Gettter not implemented.'); + } composedPath(): EventTarget[] { throw new Error('Method not implemented.'); } @@ -594,27 +644,41 @@ export class EventMock implements Event { stopPropagation(): void { return; } - NONE: 0; - CAPTURING_PHASE: 1; - AT_TARGET: 2; - BUBBLING_PHASE: 3; + NONE = 0 as const; + CAPTURING_PHASE = 1 as const; + AT_TARGET = 2 as const; + BUBBLING_PHASE = 3 as const; preventDefault() { return; } } export class KeyboardEventMock extends EventMock implements KeyboardEvent { - altKey: boolean; - charCode: number; - code: string; - ctrlKey: boolean; - isComposing: boolean; - key: string; - keyCode: number; - location: number; - metaKey: boolean; - repeat: boolean; - shiftKey: boolean; + altKey = false; + get charCode(): number { + throw new Error('Getter not implemented.'); + } + get code(): string { + throw new Error('Getter not implemented.'); + } + ctrlKey = false; + get isComposing(): boolean { + throw new Error('Getter not implemented.'); + } + get key(): string { + throw new Error('Getter not implemented.'); + } + get keyCode(): number { + throw new Error('Getter not implemented.'); + } + get location(): number { + throw new Error('Getter not implemented.'); + } + metaKey = false; + get repeat(): boolean { + throw new Error('Getter not implemented.'); + } + shiftKey = false; constructor(type: void | string) { super(); } @@ -635,13 +699,19 @@ export class KeyboardEventMock extends EventMock implements KeyboardEvent { ): void { throw new Error('Method not implemented.'); } - DOM_KEY_LOCATION_STANDARD: 0; - DOM_KEY_LOCATION_LEFT: 1; - DOM_KEY_LOCATION_RIGHT: 2; - DOM_KEY_LOCATION_NUMPAD: 3; - detail: number; - view: Window | null; - which: number; + DOM_KEY_LOCATION_STANDARD = 0 as const; + DOM_KEY_LOCATION_LEFT = 1 as const; + DOM_KEY_LOCATION_RIGHT = 2 as const; + DOM_KEY_LOCATION_NUMPAD = 3 as const; + get detail(): number { + throw new Error('Getter not implemented.'); + } + get view(): Window | null { + throw new Error('Getter not implemented.'); + } + get which(): number { + throw new Error('Getter not implemented.'); + } initUIEvent( typeArg: string, bubblesArg?: boolean | undefined, @@ -714,5 +784,5 @@ export function expectHtmlToBeEqual(expected: string, actual: string): void { } export function prettifyHtml(s: string): string { - return prettier.format(s.replace(/\n/g, ''), {parser: 'html'}); + return format(s.replace(/\n/g, ''), {parser: 'html'}); } diff --git a/packages/lexical/src/nodes/__tests__/unit/LexicalElementNode.test.tsx b/packages/lexical/src/nodes/__tests__/unit/LexicalElementNode.test.tsx index e4236e0c848..94090d32746 100644 --- a/packages/lexical/src/nodes/__tests__/unit/LexicalElementNode.test.tsx +++ b/packages/lexical/src/nodes/__tests__/unit/LexicalElementNode.test.tsx @@ -12,6 +12,7 @@ import { $getSelection, $isRangeSelection, ElementNode, + LexicalEditor, LexicalNode, TextNode, } from 'lexical'; @@ -26,7 +27,7 @@ import { } from '../../../__tests__/utils'; describe('LexicalElementNode tests', () => { - let container = null; + let container: HTMLElement; beforeEach(async () => { container = document.createElement('div'); @@ -37,16 +38,16 @@ describe('LexicalElementNode tests', () => { afterEach(() => { document.body.removeChild(container); + // @ts-ignore container = null; }); - async function update(fn) { + async function update(fn: () => void) { editor.update(fn); return Promise.resolve().then(); } - function useLexicalEditor(rootElementRef) { - // @ts-ignore + function useLexicalEditor(rootElementRef: React.RefObject) { const editor = React.useMemo(() => createTestEditor(), []); useEffect(() => { @@ -57,7 +58,7 @@ describe('LexicalElementNode tests', () => { return editor; } - let editor = null; + let editor: LexicalEditor; async function init() { const ref = createRef(); @@ -122,7 +123,7 @@ describe('LexicalElementNode tests', () => { test('some children', async () => { await update(() => { - const children = $getRoot().getFirstChild().getChildren(); + const children = $getRoot().getFirstChild()!.getChildren(); expect(children).toHaveLength(3); }); }); @@ -132,7 +133,7 @@ describe('LexicalElementNode tests', () => { test('basic', async () => { await update(() => { const textNodes = $getRoot() - .getFirstChild() + .getFirstChild()! .getAllTextNodes(); expect(textNodes).toHaveLength(3); }); @@ -174,8 +175,8 @@ describe('LexicalElementNode tests', () => { await update(() => { expect( $getRoot() - .getFirstChild() - .getFirstChild() + .getFirstChild()! + .getFirstChild()! .getTextContent(), ).toBe('Foo'); }); @@ -194,8 +195,8 @@ describe('LexicalElementNode tests', () => { await update(() => { expect( $getRoot() - .getFirstChild() - .getLastChild() + .getFirstChild()! + .getLastChild()! .getTextContent(), ).toBe('Baz'); }); @@ -212,7 +213,7 @@ describe('LexicalElementNode tests', () => { describe('getTextContent()', () => { test('basic', async () => { await update(() => { - expect($getRoot().getFirstChild().getTextContent()).toBe('FooBarBaz'); + expect($getRoot().getFirstChild()!.getTextContent()).toBe('FooBarBaz'); }); }); @@ -255,8 +256,8 @@ describe('LexicalElementNode tests', () => { describe('getTextContentSize()', () => { test('basic', async () => { await update(() => { - expect($getRoot().getFirstChild().getTextContentSize()).toBe( - $getRoot().getFirstChild().getTextContent().length, + expect($getRoot().getFirstChild()!.getTextContentSize()).toBe( + $getRoot().getFirstChild()!.getTextContent().length, ); }); }); @@ -274,7 +275,7 @@ describe('LexicalElementNode tests', () => { }); describe('splice', () => { - let block; + let block: ElementNode; beforeEach(async () => { await update(() => { @@ -588,7 +589,7 @@ describe('LexicalElementNode tests', () => { it('Running transforms for inserted nodes, their previous siblings and new siblings', async () => { const transforms = new Set(); - const expectedTransforms = []; + const expectedTransforms: string[] = []; const removeTransform = editor.registerNodeTransform(TextNode, (node) => { transforms.add(node.__key); @@ -610,14 +611,14 @@ describe('LexicalElementNode tests', () => { text1.__key, text2.__key, text3.__key, - block.getChildAtIndex(0).__key, - block.getChildAtIndex(1).__key, + block.getChildAtIndex(0)!.__key, + block.getChildAtIndex(1)!.__key, ); }); await update(() => { block.splice(1, 0, [ - $getRoot().getLastChild().getChildAtIndex(1), + $getRoot().getLastChild()!.getChildAtIndex(1)!, ]); }); diff --git a/packages/lexical/src/nodes/__tests__/unit/LexicalGC.test.tsx b/packages/lexical/src/nodes/__tests__/unit/LexicalGC.test.tsx index ba6dbcf693b..2c7e978a1fc 100644 --- a/packages/lexical/src/nodes/__tests__/unit/LexicalGC.test.tsx +++ b/packages/lexical/src/nodes/__tests__/unit/LexicalGC.test.tsx @@ -70,7 +70,7 @@ describe('LexicalGC tests', () => { const root = $getRoot(); const firstChild = root.getFirstChild(); invariant($isElementNode(firstChild)); - const subchild = firstChild.getChildAtIndex(i); + const subchild = firstChild.getChildAtIndex(i)!; expect(subchild.getTextContent()).toBe(['foo', 'bar', 'zzz'][i]); subchild.remove(); root.clear(); @@ -107,7 +107,7 @@ describe('LexicalGC tests', () => { expect(editor.getEditorState()._nodeMap.size).toBe(7); await editor.update(() => { for (const key of removeKeys) { - const node = $getNodeByKey(String(key)); + const node = $getNodeByKey(String(key))!; node.remove(); } $getRoot().clear(); diff --git a/packages/lexical/src/nodes/__tests__/unit/LexicalParagraphNode.test.ts b/packages/lexical/src/nodes/__tests__/unit/LexicalParagraphNode.test.ts index 6309b86bd75..a5e930401ad 100644 --- a/packages/lexical/src/nodes/__tests__/unit/LexicalParagraphNode.test.ts +++ b/packages/lexical/src/nodes/__tests__/unit/LexicalParagraphNode.test.ts @@ -9,8 +9,10 @@ import { $createParagraphNode, $getRoot, + $getSelection, $isParagraphNode, ParagraphNode, + RangeSelection, } from 'lexical'; import {initializeUnitTest} from '../../../__tests__/utils'; @@ -98,7 +100,7 @@ describe('LexicalParagraphNode tests', () => { test('ParagraphNode.insertNewAfter()', async () => { const {editor} = testEnv; - let paragraphNode; + let paragraphNode: ParagraphNode; await editor.update(() => { const root = $getRoot(); @@ -111,7 +113,10 @@ describe('LexicalParagraphNode tests', () => { ); await editor.update(() => { - const result = paragraphNode.insertNewAfter(); + const result = paragraphNode.insertNewAfter( + $getSelection() as RangeSelection, + false, + ); expect(result).toBeInstanceOf(ParagraphNode); expect(result.getDirection()).toEqual(paragraphNode.getDirection()); diff --git a/packages/lexical/src/nodes/__tests__/unit/LexicalRootNode.test.ts b/packages/lexical/src/nodes/__tests__/unit/LexicalRootNode.test.ts index 787fb5a3ff1..123cb3375d6 100644 --- a/packages/lexical/src/nodes/__tests__/unit/LexicalRootNode.test.ts +++ b/packages/lexical/src/nodes/__tests__/unit/LexicalRootNode.test.ts @@ -14,6 +14,7 @@ import { $isRangeSelection, $isRootNode, ElementNode, + RootNode, TextNode, } from 'lexical'; @@ -27,7 +28,7 @@ import {$createRootNode} from '../../LexicalRootNode'; describe('LexicalRootNode tests', () => { initializeUnitTest((testEnv) => { - let rootNode; + let rootNode: RootNode; function expectRootTextContentToBe(text: string): void { const {editor} = testEnv; @@ -85,17 +86,19 @@ describe('LexicalRootNode tests', () => { }); test('RootNode.clone()', async () => { - const rootNodeClone = rootNode.constructor.clone(); + const rootNodeClone = (rootNode.constructor as typeof RootNode).clone(); expect(rootNodeClone).not.toBe(rootNode); expect(rootNodeClone).toStrictEqual(rootNode); }); test('RootNode.createDOM()', async () => { + // @ts-expect-error expect(() => rootNode.createDOM()).toThrow(); }); test('RootNode.updateDOM()', async () => { + // @ts-expect-error expect(rootNode.updateDOM()).toBe(false); }); @@ -168,7 +171,7 @@ describe('LexicalRootNode tests', () => { await editor.update(() => { const root = $getRoot(); - root.getFirstChild().remove(); + root.getFirstChild()!.remove(); }); await editor.update(() => { @@ -194,7 +197,7 @@ describe('LexicalRootNode tests', () => { expectRootTextContentToBe(''); await editor.update(() => { - const firstParagraph = $getRoot().getFirstChild(); + const firstParagraph = $getRoot().getFirstChild()!; firstParagraph.append($createTextNode('first line')); }); @@ -208,7 +211,7 @@ describe('LexicalRootNode tests', () => { expectRootTextContentToBe('first line\n\n'); await editor.update(() => { - const secondParagraph = $getRoot().getLastChild(); + const secondParagraph = $getRoot().getLastChild()!; secondParagraph.append($createTextNode('second line')); }); @@ -222,15 +225,15 @@ describe('LexicalRootNode tests', () => { expectRootTextContentToBe('first line\n\nsecond line\n\n'); await editor.update(() => { - const thirdParagraph = $getRoot().getLastChild(); + const thirdParagraph = $getRoot().getLastChild()!; thirdParagraph.append($createTextNode('third line')); }); expectRootTextContentToBe('first line\n\nsecond line\n\nthird line'); await editor.update(() => { - const secondParagraph = $getRoot().getChildAtIndex(1); - const secondParagraphText = secondParagraph.getFirstChild(); + const secondParagraph = $getRoot().getChildAtIndex(1)!; + const secondParagraphText = secondParagraph.getFirstChild()!; secondParagraphText.setTextContent('second line!'); }); diff --git a/packages/lexical/src/nodes/__tests__/unit/LexicalTabNode.test.tsx b/packages/lexical/src/nodes/__tests__/unit/LexicalTabNode.test.tsx index 4812c7953c4..0c06273eca1 100644 --- a/packages/lexical/src/nodes/__tests__/unit/LexicalTabNode.test.tsx +++ b/packages/lexical/src/nodes/__tests__/unit/LexicalTabNode.test.tsx @@ -117,7 +117,7 @@ describe('LexicalTabNode tests', () => { registerRichText(editor); registerTabIndentation(editor); await editor.update(() => { - const selection = $getSelection(); + const selection = $getSelection()!; selection.insertText('foo'); $getRoot().selectStart(); }); @@ -168,7 +168,7 @@ describe('LexicalTabNode tests', () => { registerRichText(editor); registerTabIndentation(editor); await editor.update(() => { - $getSelection().insertText('foo'); + $getSelection()!.insertText('foo'); }); await editor.dispatchCommand( KEY_TAB_COMMAND, @@ -184,7 +184,7 @@ describe('LexicalTabNode tests', () => { registerRichText(editor); registerTabIndentation(editor); await editor.update(() => { - $getSelection().insertText('foo'); + $getSelection()!.insertText('foo'); const textNode = $getRoot().getLastDescendant(); invariant($isTextNode(textNode)); textNode.select(1, 1); @@ -203,7 +203,7 @@ describe('LexicalTabNode tests', () => { registerRichText(editor); registerTabIndentation(editor); await editor.update(() => { - $getSelection().insertText('foo'); + $getSelection()!.insertText('foo'); const textNode = $getRoot().getLastDescendant(); invariant($isTextNode(textNode)); textNode.select(1, 2); @@ -222,13 +222,13 @@ describe('LexicalTabNode tests', () => { registerRichText(editor); registerTabIndentation(editor); await editor.update(() => { - $getSelection().insertRawText('hello\tworld'); + $getSelection()!.insertRawText('hello\tworld'); const root = $getRoot(); const firstTextNode = root.getFirstDescendant(); const lastTextNode = root.getLastDescendant(); const selection = $createRangeSelection(); - selection.anchor.set(firstTextNode.getKey(), 'hell'.length, 'text'); - selection.focus.set(lastTextNode.getKey(), 'wo'.length, 'text'); + selection.anchor.set(firstTextNode!.getKey(), 'hell'.length, 'text'); + selection.focus.set(lastTextNode!.getKey(), 'wo'.length, 'text'); $setSelection(selection); }); await editor.dispatchCommand( @@ -247,7 +247,7 @@ describe('LexicalTabNode tests', () => { const tab2 = $createTabNode(); $insertNodes([tab1, tab2]); tab1.select(1, 1); - $getSelection().insertText('f'); + $getSelection()!.insertText('f'); }); expect(testEnv.innerHTML).toBe( '

\tf\t

', diff --git a/packages/lexical/src/nodes/__tests__/unit/LexicalTextNode.test.tsx b/packages/lexical/src/nodes/__tests__/unit/LexicalTextNode.test.tsx index 43a7355d0b5..a90e2de3300 100644 --- a/packages/lexical/src/nodes/__tests__/unit/LexicalTextNode.test.tsx +++ b/packages/lexical/src/nodes/__tests__/unit/LexicalTextNode.test.tsx @@ -15,6 +15,7 @@ import { $isNodeSelection, $isRangeSelection, ElementNode, + LexicalEditor, ParagraphNode, TextFormatType, TextModeType, @@ -61,7 +62,7 @@ const editorConfig = Object.freeze({ }); describe('LexicalTextNode tests', () => { - let container = null; + let container: HTMLElement; beforeEach(async () => { container = document.createElement('div'); @@ -71,15 +72,16 @@ describe('LexicalTextNode tests', () => { }); afterEach(() => { document.body.removeChild(container); + // @ts-ignore container = null; }); - async function update(fn) { + async function update(fn: () => void) { editor.update(fn); return Promise.resolve().then(); } - function useLexicalEditor(rootElementRef) { + function useLexicalEditor(rootElementRef: React.RefObject) { const editor = useMemo(() => createTestEditor(editorConfig), []); useEffect(() => { @@ -91,7 +93,7 @@ describe('LexicalTextNode tests', () => { return editor; } - let editor = null; + let editor: LexicalEditor; async function init() { const ref = createRef(); @@ -141,7 +143,7 @@ describe('LexicalTextNode tests', () => { describe('root.getTextContent()', () => { test('writable nodes', async () => { - let nodeKey; + let nodeKey: string; await update(() => { const textNode = $createTextNode('Text'); @@ -150,7 +152,7 @@ describe('LexicalTextNode tests', () => { expect(textNode.getTextContent()).toBe('Text'); expect(textNode.__text).toBe('Text'); - $getRoot().getFirstChild().append(textNode); + $getRoot().getFirstChild()!.append(textNode); }); expect( @@ -163,7 +165,7 @@ describe('LexicalTextNode tests', () => { // Make sure that the editor content is still set after further reconciliations await update(() => { - $getNodeByKey(nodeKey).markDirty(); + $getNodeByKey(nodeKey)!.markDirty(); }); expect(getEditorStateTextContent(editor.getEditorState())).toBe('Text'); }); @@ -171,14 +173,14 @@ describe('LexicalTextNode tests', () => { test('prepend node', async () => { await update(() => { const textNode = $createTextNode('World').toggleUnmergeable(); - $getRoot().getFirstChild().append(textNode); + $getRoot().getFirstChild()!.append(textNode); }); await update(() => { const textNode = $createTextNode('Hello ').toggleUnmergeable(); const previousTextNode = $getRoot() - .getFirstChild() - .getFirstChild(); + .getFirstChild()! + .getFirstChild()!; previousTextNode.insertBefore(textNode); }); @@ -200,109 +202,69 @@ describe('LexicalTextNode tests', () => { }); describe.each([ - [ - 'bold', - IS_BOLD, - (node) => node.hasFormat('bold'), - (node) => node.toggleFormat('bold'), - ], - [ - 'italic', - IS_ITALIC, - (node) => node.hasFormat('italic'), - (node) => node.toggleFormat('italic'), - ], - [ - 'strikethrough', - IS_STRIKETHROUGH, - (node) => node.hasFormat('strikethrough'), - (node) => node.toggleFormat('strikethrough'), - ], - [ - 'underline', - IS_UNDERLINE, - (node) => node.hasFormat('underline'), - (node) => node.toggleFormat('underline'), - ], - [ - 'code', - IS_CODE, - (node) => node.hasFormat('code'), - (node) => node.toggleFormat('code'), - ], - [ - 'subscript', - IS_SUBSCRIPT, - (node) => node.hasFormat('subscript'), - (node) => node.toggleFormat('subscript'), - ], - [ - 'superscript', - IS_SUPERSCRIPT, - (node) => node.hasFormat('superscript'), - (node) => node.toggleFormat('superscript'), - ], - [ - 'highlight', - IS_HIGHLIGHT, - (node) => node.hasFormat('highlight'), - (node) => node.toggleFormat('highlight'), - ], - ])( - '%s flag', - (formatFlag: TextFormatType, stateFormat, flagPredicate, flagToggle) => { - test(`getFormatFlags(${formatFlag})`, async () => { - await update(() => { - const root = $getRoot(); - const paragraphNode = root.getFirstChild(); - const textNode = paragraphNode.getFirstChild(); - const newFormat = textNode.getFormatFlags(formatFlag, null); + ['bold', IS_BOLD], + ['italic', IS_ITALIC], + ['strikethrough', IS_STRIKETHROUGH], + ['underline', IS_UNDERLINE], + ['code', IS_CODE], + ['subscript', IS_SUBSCRIPT], + ['superscript', IS_SUPERSCRIPT], + ['highlight', IS_HIGHLIGHT], + ] as const)('%s flag', (formatFlag: TextFormatType, stateFormat: number) => { + const flagPredicate = (node: TextNode) => node.hasFormat(formatFlag); + const flagToggle = (node: TextNode) => node.toggleFormat(formatFlag); + + test(`getFormatFlags(${formatFlag})`, async () => { + await update(() => { + const root = $getRoot(); + const paragraphNode = root.getFirstChild()!; + const textNode = paragraphNode.getFirstChild()!; + const newFormat = textNode.getFormatFlags(formatFlag, null); - expect(newFormat).toBe(stateFormat); + expect(newFormat).toBe(stateFormat); - textNode.setFormat(newFormat); - const newFormat2 = textNode.getFormatFlags(formatFlag, null); + textNode.setFormat(newFormat); + const newFormat2 = textNode.getFormatFlags(formatFlag, null); - expect(newFormat2).toBe(0); - }); + expect(newFormat2).toBe(0); }); + }); - test(`predicate for ${formatFlag}`, async () => { - await update(() => { - const root = $getRoot(); - const paragraphNode = root.getFirstChild(); - const textNode = paragraphNode.getFirstChild(); + test(`predicate for ${formatFlag}`, async () => { + await update(() => { + const root = $getRoot(); + const paragraphNode = root.getFirstChild()!; + const textNode = paragraphNode.getFirstChild()!; - textNode.setFormat(stateFormat); + textNode.setFormat(stateFormat); - expect(flagPredicate(textNode)).toBe(true); - }); + expect(flagPredicate(textNode)).toBe(true); }); + }); - test(`toggling for ${formatFlag}`, async () => { - // Toggle method hasn't been implemented for this flag. - if (flagToggle === null) { - return; - } + test(`toggling for ${formatFlag}`, async () => { + // Toggle method hasn't been implemented for this flag. + if (flagToggle === null) { + return; + } - await update(() => { - const root = $getRoot(); - const paragraphNode = root.getFirstChild(); - const textNode = paragraphNode.getFirstChild(); + await update(() => { + const root = $getRoot(); + const paragraphNode = root.getFirstChild()!; + const textNode = paragraphNode.getFirstChild()!; - expect(flagPredicate(textNode)).toBe(false); + expect(flagPredicate(textNode)).toBe(false); - flagToggle(textNode); + flagToggle(textNode); - expect(flagPredicate(textNode)).toBe(true); + expect(flagPredicate(textNode)).toBe(true); - flagToggle(textNode); + flagToggle(textNode); - expect(flagPredicate(textNode)).toBe(false); - }); + expect(flagPredicate(textNode)).toBe(false); }); - }, - ); + }); + }); test('setting subscript clears superscript', async () => { await update(() => { @@ -624,7 +586,7 @@ describe('LexicalTextNode tests', () => { describe('createDOM()', () => { test.each([ - ['no formatting', null, 'My text node', 'My text node'], + ['no formatting', 0, 'My text node', 'My text node'], [ 'bold', IS_BOLD, @@ -717,8 +679,8 @@ describe('LexicalTextNode tests', () => { describe('has parent node', () => { test.each([ - ['no formatting', null, 'My text node', 'My text node'], - ['no formatting + empty string', null, '', ``], + ['no formatting', 0, 'My text node', 'My text node'], + ['no formatting + empty string', 0, '', ``], ])( '%s text format type', async (_type, format, contents, expectedHTML) => { @@ -839,7 +801,7 @@ describe('LexicalTextNode tests', () => { test('mergeWithSibling', async () => { await update(() => { - const paragraph = $getRoot().getFirstChild(); + const paragraph = $getRoot().getFirstChild()!; const textNode1 = $createTextNode('1'); const textNode2 = $createTextNode('2'); const textNode3 = $createTextNode('3'); diff --git a/tsconfig.json b/tsconfig.json index 0593ca0605a..d12fa850af3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -193,7 +193,7 @@ }, "include": ["./libdefs", "./packages"], "exclude": [ - "**/__tests__/**", + "**/lexical-*/src/__tests__/**", "**/dist/**", "**/npm/**", "**/node_modules/**",