slate-test-utils
Version:
> 📣 Love Slate and looking for your next gig? Sirona Medical is [hiring](https://sironamedical.com/about-us/careers/)!
198 lines (197 loc) • 8.13 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildTestHarness = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("@testing-library/react");
const react_2 = require("@testing-library/react");
const editorQueries = require("./editorQueries");
const is_hotkey_1 = require("is-hotkey");
const utils_1 = require("./utils");
const slate_1 = require("slate");
const ensureSlateValid_1 = require("./ensureSlateValid");
/**
* A test harness for the RichTextEditor that adds custom queries to assert on, lots
* of simulated actions, and a custom rerender in case you want to assert on the DOM.
* In most cases, you'll want to assert directly on the editor state to check that the editor
* selection and other pieces of the editor are working as intended.
*/
const buildTestHarness = (Component) => async ({ debug = false, strict = false, editor, componentProps = {}, testID = 'slate-content-editable', }) => {
const proppies = {
editor,
initialValue: editor.children,
...componentProps,
};
if (strict) {
(0, ensureSlateValid_1.ensureSlateStateValid)(editor);
}
const options = (0, react_1.render)((0, jsx_runtime_1.jsx)(Component, Object.assign({ initialValue: editor.children }, proppies), void 0), {
queries: { ...react_1.queries, ...editorQueries },
// TODO: Rest of options...
});
// @ts-ignore
await (0, react_2.act)(async () => options);
const element = options.getByTestId(testID);
/**
* Manually add this because JSDom doesn't implement this and Slate checks for it
* internally before doing stuff.
*
* https://github.com/jsdom/jsdom/issues/1670
*/
// @ts-ignore
element.isContentEditable = true;
/**
* Slate React uses beforeinput events in order to prevent the default behavior within contenteditables
* and apply certain operations to the Slate state based on the event type. We emulate all of the operations
* that Slate applies in order to integration test the editor within a JSDom environment with React Testing
* Library.
*
* Reference events we emulate:
* https://github.com/ianstormtaylor/slate/blob/a5f4170162cefd1c9458544402bb8f2266e05ead/packages/slate-react/src/components/editable.tsx#L289
*/
/**
* Emulates typing content into Slate.
*
* @param {string} value
*/
const type = async (value) => (0, react_2.act)(async () => {
(0, react_2.fireEvent)(element, new InputEvent('beforeinput', {
inputType: 'insertText',
data: value,
}));
});
const typeSpace = async () => type(' ');
const paste = async (payload, options = {}) => (0, react_2.act)(async () => {
var _a;
const types = (_a = options === null || options === void 0 ? void 0 : options.types) !== null && _a !== void 0 ? _a : ['text/html'];
const event = new window.Event('paste', {
bubbles: true,
cancelable: true,
composed: true,
});
// @ts-ignore Typescript doesn't expect clipboardData on Event type
event.clipboardData = {
types,
getData() {
return payload;
},
};
(0, react_2.fireEvent)(element, event);
});
/**
* Deletes forward one character from the current Slate selection.
*/
const deleteForward = async () => (0, react_2.act)(async () => {
(0, react_2.fireEvent)(element, new InputEvent('beforeinput', { inputType: 'deleteContentForward' }));
});
/**
* Deletes backward one character from the current Slate selection.
*/
const deleteBackward = async () => (0, react_2.act)(async () => {
(0, react_2.fireEvent)(element, new InputEvent('beforeinput', { inputType: 'deleteContentBackward' }));
});
/**
* Deletes the entire soft line in Slate backwards and forwards from current Slate selection.
*/
const deleteEntireSoftline = async () => (0, react_2.act)(async () => {
(0, react_2.fireEvent)(element, new InputEvent('beforeinput', { inputType: 'deleteEntireSoftLine' }));
});
/**
* Deletes the entire block content backwards from current Slate selection.
*/
const deleteHardLineBackward = async () => (0, react_2.act)(async () => {
(0, react_2.fireEvent)(element, new InputEvent('beforeinput', {
inputType: 'deleteHardLineBackward',
}));
});
/**
* Deletes the entire block content backwards from current Slate selection.
*/
const deleteSoftLineBackward = async () => (0, react_2.act)(async () => {
(0, react_2.fireEvent)(element, new InputEvent('beforeinput', {
inputType: 'deleteSoftLineBackward',
}));
});
/**
* Deletes the entire block content forwards from current Slate selection.
*/
const deleteHardLineForward = async () => (0, react_2.act)(async () => {
(0, react_2.fireEvent)(element, new InputEvent('beforeinput', { inputType: 'deleteHardLineForward' }));
});
/**
* Deletes the entire block content forwards from current Slate selection.
*/
const deleteSoftLineForward = async () => (0, react_2.act)(async () => {
(0, react_2.fireEvent)(element, new InputEvent('beforeinput', { inputType: 'deleteSoftLineForward' }));
});
/**
* Deletes a word backwards from Slate's selection
*/
const deleteWordBackward = async () => (0, react_2.act)(async () => {
(0, react_2.fireEvent)(element, new InputEvent('beforeinput', { inputType: 'deleteWordBackward' }));
});
/**
* Deletes a word forward from Slate's selection
*/
const deleteWordForward = async () => (0, react_2.act)(async () => {
(0, react_2.fireEvent)(element, new InputEvent('beforeinput', { inputType: 'deleteWordForward' }));
});
/**
* Inserts a line break at the current selection. Simulates pressing 'Enter' in a contenteditable with Slate.
*/
const pressEnter = async () => (0, react_2.act)(async () => {
await triggerKeyboardEvent('Enter');
(0, react_2.fireEvent)(element, new InputEvent('beforeinput', { inputType: 'insertParagraph' }));
});
/**
* Simulates the user pressing a key down. This is commonly used for testing hotkeys.
*/
const triggerKeyboardEvent = async (hotkey) => (0, react_2.act)(async () => {
const eventProps = (0, is_hotkey_1.parseHotkey)(hotkey);
const values = hotkey.split('+');
(0, react_2.fireEvent)(element, new window.KeyboardEvent('keydown', {
key: values[values.length - 1],
code: `${eventProps.which}`,
keyCode: eventProps.which,
bubbles: true,
...eventProps,
}));
});
const undo = async () => editor.undo();
const redo = async () => editor.redo();
// Keyboard shortcut wouldn't work within JSDOM so we emulate it
const selectAll = async () => slate_1.Transforms.select(editor, []);
if (debug) {
const { apply } = editor;
editor.apply = (args) => {
// eslint-disable-next-line no-console
console.log('OPERATION APPLIED', JSON.stringify(args, null, 2));
return apply(args);
};
}
return [
editor,
{
type,
deleteForward,
deleteBackward,
deleteEntireSoftline,
deleteHardLineBackward,
deleteSoftLineBackward,
deleteHardLineForward,
deleteSoftLineForward,
deleteWordBackward,
deleteWordForward,
triggerKeyboardEvent,
paste,
pressEnter,
typeSpace,
undo,
redo,
selectAll,
isApple: utils_1.isApple,
rerender: () => options.rerender((0, jsx_runtime_1.jsx)(Component, Object.assign({}, proppies), void 0)),
},
options,
];
};
exports.buildTestHarness = buildTestHarness;