mirror of
https://github.com/facebook/lexical.git
synced 2025-05-16 22:48:52 +08:00
Add basic selection tests
This commit is contained in:

committed by
acywatson

parent
77cabffa4b
commit
163b1d21e0
@ -1,6 +1,7 @@
|
||||
[ignore]
|
||||
.*/scripts/.*
|
||||
.*/build/.*
|
||||
.*/__tests__/.*
|
||||
.*/dist/.*
|
||||
.*/.tempUserDataDir/.*
|
||||
.*/node_modules/.*
|
||||
|
1
.watchmanconfig
Normal file
1
.watchmanconfig
Normal file
@ -0,0 +1 @@
|
||||
{}
|
5
babel.config.js
Normal file
5
babel.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
presets: ['@babel/preset-env', '@babel/preset-react'],
|
||||
};
|
@ -12,7 +12,9 @@
|
||||
"build": "node scripts/build.js",
|
||||
"build-prod": "node scripts/build.js --prod",
|
||||
"watch": "node scripts/build.js --watch",
|
||||
"flow": "node ./scripts/tasks/flow.js"
|
||||
"flow": "node ./scripts/tasks/flow.js",
|
||||
"test": "jest",
|
||||
"debug-test": "node --inspect-brk node_modules/.bin/jest --runInBand"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.12.1",
|
||||
@ -38,6 +40,7 @@
|
||||
"jest": "26.6.0",
|
||||
"minimist": "^1.2.5",
|
||||
"prettier": "^2.1.2",
|
||||
"react-test-renderer": "^17.0.1",
|
||||
"rollup": "^2.33.1"
|
||||
}
|
||||
}
|
||||
|
219
packages/outline/src/__tests__/OutlineSelection-test.js
Normal file
219
packages/outline/src/__tests__/OutlineSelection-test.js
Normal file
@ -0,0 +1,219 @@
|
||||
let container = null;
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactTestUtils;
|
||||
let Outline;
|
||||
|
||||
function sanitizeHTML(html) {
|
||||
// Remove the special space characters
|
||||
return html.replace(/\uFEFF/g, '');
|
||||
}
|
||||
|
||||
function insertText(text) {
|
||||
return {
|
||||
type: 'insert_text',
|
||||
text,
|
||||
};
|
||||
}
|
||||
|
||||
function deleteBackward() {
|
||||
return {
|
||||
type: 'delete_backward',
|
||||
text: null,
|
||||
};
|
||||
}
|
||||
|
||||
function deleteForward() {
|
||||
return {
|
||||
type: 'delete_forward',
|
||||
text: null,
|
||||
};
|
||||
}
|
||||
|
||||
function setNativeSelection(
|
||||
anchorPath,
|
||||
anchorOffset,
|
||||
focusPath,
|
||||
focusOffset,
|
||||
isCollapsed = false,
|
||||
) {
|
||||
return {
|
||||
type: 'set_native_selection',
|
||||
anchorPath,
|
||||
anchorOffset,
|
||||
focusPath,
|
||||
focusOffset,
|
||||
isCollapsed,
|
||||
};
|
||||
}
|
||||
|
||||
function getNodeFromPath(path, editorElement) {
|
||||
let node = editorElement;
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
node = node.childNodes[path[i]];
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
function updateNativeSelection(
|
||||
editorElement,
|
||||
anchorPath,
|
||||
anchorOffset,
|
||||
focusPath,
|
||||
focusOffset,
|
||||
isCollapsed = false,
|
||||
) {
|
||||
const anchorNode = getNodeFromPath(anchorPath, editorElement);
|
||||
const focusNode = getNodeFromPath(focusPath, editorElement);
|
||||
const domSelection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.collapse(isCollapsed);
|
||||
range.setStart(anchorNode, anchorOffset);
|
||||
range.setEnd(focusNode, focusOffset);
|
||||
domSelection.removeAllRanges();
|
||||
domSelection.addRange(range);
|
||||
}
|
||||
|
||||
function applySelectionInputs(inputs, update, editor) {
|
||||
const editorElement = editor.getEditorElement();
|
||||
inputs.forEach((input) => {
|
||||
update((view) => {
|
||||
const selection = view.getSelection();
|
||||
|
||||
switch (input.type) {
|
||||
case 'insert_text': {
|
||||
selection.insertText(input.text);
|
||||
break;
|
||||
}
|
||||
case 'delete_backward': {
|
||||
selection.deleteBackward();
|
||||
break;
|
||||
}
|
||||
case 'delete_forward': {
|
||||
selection.deleteForward();
|
||||
break;
|
||||
}
|
||||
case 'set_native_selection': {
|
||||
updateNativeSelection(
|
||||
editorElement,
|
||||
input.anchorPath,
|
||||
input.anchorOffset,
|
||||
input.focusPath,
|
||||
input.focusOffset,
|
||||
input.isCollapsed,
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.log('TODO');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('OutlineSelection tests', () => {
|
||||
beforeEach(() => {
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
Outline = require('outline');
|
||||
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
init();
|
||||
});
|
||||
|
||||
let editor = null;
|
||||
|
||||
function init() {
|
||||
const ref = React.createRef();
|
||||
|
||||
function TestBase() {
|
||||
editor = Outline.useOutlineEditor(ref);
|
||||
return <div ref={ref} contentEditable={true} />;
|
||||
}
|
||||
|
||||
ReactTestUtils.act(() => {
|
||||
ReactDOM.render(<TestBase />, container);
|
||||
});
|
||||
ref.current.focus();
|
||||
|
||||
// Insert initial block
|
||||
update((view) => {
|
||||
const paragraph = Outline.createParagraph();
|
||||
const text = Outline.createText();
|
||||
paragraph.append(text);
|
||||
view.getBody().append(paragraph);
|
||||
});
|
||||
|
||||
// Focus first element
|
||||
updateNativeSelection(ref.current, [0, 0, 0], 0, [0, 0, 0], 0);
|
||||
}
|
||||
|
||||
function update(callback) {
|
||||
const viewModel = editor.createViewModel(callback);
|
||||
editor.update(viewModel, true);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
document.body.removeChild(container);
|
||||
container = null;
|
||||
});
|
||||
|
||||
test('Expect initial output to be a block with some text', () => {
|
||||
expect(sanitizeHTML(container.innerHTML)).toBe(
|
||||
'<div contenteditable="true"><p><span data-text="true"><br></span></p></div>',
|
||||
);
|
||||
});
|
||||
|
||||
const suite = [
|
||||
{
|
||||
inputs: [
|
||||
insertText('H'),
|
||||
insertText('e'),
|
||||
insertText('l'),
|
||||
insertText('l'),
|
||||
insertText('o'),
|
||||
],
|
||||
result:
|
||||
'<div contenteditable="true"><p dir="ltr"><span data-text="true">Hello</span></p></div>',
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
insertText('1'),
|
||||
insertText('2'),
|
||||
insertText('3'),
|
||||
deleteBackward(),
|
||||
insertText('4'),
|
||||
insertText('5'),
|
||||
deleteBackward(),
|
||||
insertText('6'),
|
||||
deleteForward(),
|
||||
],
|
||||
result:
|
||||
'<div contenteditable="true"><p><span data-text="true">1246</span></p></div>',
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
insertText('1'),
|
||||
insertText('1'),
|
||||
insertText('2'),
|
||||
insertText('3'),
|
||||
setNativeSelection([0, 0, 0], 0, [0, 0, 0], 0),
|
||||
insertText('a'),
|
||||
insertText('b'),
|
||||
insertText('c'),
|
||||
deleteForward(),
|
||||
],
|
||||
result:
|
||||
'<div contenteditable="true"><p dir="ltr"><span data-text="true">abc123</span></p></div>',
|
||||
},
|
||||
];
|
||||
|
||||
suite.forEach((testUnit, i) => {
|
||||
test('Test case #' + (i + 1), () => {
|
||||
applySelectionInputs(testUnit.inputs, update, editor);
|
||||
expect(sanitizeHTML(container.innerHTML)).toBe(testUnit.result);
|
||||
});
|
||||
});
|
||||
});
|
28
yarn.lock
28
yarn.lock
@ -9018,16 +9018,16 @@ react-error-overlay@^6.0.8:
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.8.tgz#474ed11d04fc6bda3af643447d85e9127ed6b5de"
|
||||
integrity sha512-HvPuUQnLp5H7TouGq3kzBeioJmXms1wHy9EGjz2OURWBp4qZO6AfGEcnxts1D/CbwPLRAgTMPCEgYhA3sEM4vw==
|
||||
|
||||
"react-is@^16.12.0 || ^17.0.0", react-is@^17.0.1:
|
||||
version "17.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
|
||||
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
|
||||
|
||||
react-is@^16.8.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-is@^17.0.1:
|
||||
version "17.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
|
||||
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
|
||||
|
||||
react-refresh@^0.8.3:
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
|
||||
@ -9098,6 +9098,24 @@ react-scripts@4.0.0:
|
||||
optionalDependencies:
|
||||
fsevents "^2.1.3"
|
||||
|
||||
react-shallow-renderer@^16.13.1:
|
||||
version "16.14.1"
|
||||
resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz#bf0d02df8a519a558fd9b8215442efa5c840e124"
|
||||
integrity sha512-rkIMcQi01/+kxiTE9D3fdS959U1g7gs+/rborw++42m1O9FAQiNI/UNRZExVUoAOprn4umcXf+pFRou8i4zuBg==
|
||||
dependencies:
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.12.0 || ^17.0.0"
|
||||
|
||||
react-test-renderer@^17.0.1:
|
||||
version "17.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.1.tgz#3187e636c3063e6ae498aedf21ecf972721574c7"
|
||||
integrity sha512-/dRae3mj6aObwkjCcxZPlxDFh73XZLgvwhhyON2haZGUEhiaY5EjfAdw+d/rQmlcFwdTpMXCSGVk374QbCTlrA==
|
||||
dependencies:
|
||||
object-assign "^4.1.1"
|
||||
react-is "^17.0.1"
|
||||
react-shallow-renderer "^16.13.1"
|
||||
scheduler "^0.20.1"
|
||||
|
||||
react@^17.0.1:
|
||||
version "17.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"
|
||||
|
Reference in New Issue
Block a user