Playwright Testing
Learn how to write Playwright tests that integrate with Plate.
Playwright enables end-to-end testing in headless browsers. This guide covers integrating Playwright with Plate using @udecode/plate-playwright
.
Setup
Install Dependencies
Follow Playwright's guide to install Playwright in your app and ensure that you can write basic end-to-end tests.
npm install @udecode/plate-playwright playwright
Add PlaywrightPlugin
Add PlaywrightPlugin
to your editor to enable test utilities:
const editor = createPlateEditor({
plugins: [
// other plugins...
PlaywrightPlugin.configure({ enabled: process.env.NODE_ENV !== 'production' }),
]
})
This exposes various utilities on window.platePlaywrightAdapter
.
Get Editor Handle
What is an editor handle?
Most Playwright test code runs in a non-browser environment. Interacting with a Plate editor requires running JavaScript inside the browser context using Playwright's evaluate
and evaluateHandle
APIs.
A handle references a JavaScript object within the browser. The editor handle refers to the editor
instance of your Plate editor (JSHandle<PlateEditor>
).
In your Playwright test, get the editor handle before interacting with Plate:
const editorHandle = await getEditorHandle(page);
For multiple editors, specify the editable element:
const editable = getEditable(page.getByTestId('my-editor-container'));
const editorHandle = await getEditorHandle(page, editable);
The locator must match exactly one [data-slate-editor]
element.
Start Writing Tests
With the editorHandle
, you can now write Playwright tests for your editor.
Examples
Get a node handle by its path
Use getNodeByPath
to get a handle referencing the node at a specific path. To make assertions about the value of the node, convert it to JSON using .jsonValue()
.
const nodeHandle = await getNodeByPath(page, editorHandle, [0]);
expect(await nodeHandle.jsonValue()).toBe({
type: 'p',
children: [{ text: 'My paragraph' }],
});
Get the type of a node
const firstNodeType = await getTypeAtPath(page, editorHandle, [0]);
expect(firstNodeType).toBe('h1');
Get the DOM node for a node
Often in Playwright, you'll want to reference a specific DOM element in order to make assertions about its state or perform operations involving it.
getDOMNodeByPath
returns an ElementHandle for the DOM node corresponding to the Slate node at a given path.
const firstNodeEl = await getDOMNodeByPath(page, elementHandle, [0]);
await firstNodeEl.hover();
Click a node
await clickAtPath(page, elementHandle, [0]);
Get the selection
const selection = await getSelection(page, editorHandle);
expect(selection).toBe({
anchor: { path: [0, 0], offset: 0 },
focus: { path: [0, 0], offset: 7 },
});
Select a point or range
In order to type at a specific point in the editor, you'll need to select that point using setSelection
.
If you select a single point (consisting of a path
and an offset
), the cursor will be placed at that point. If you select a range (consisting of an anchor
and a focus
), that range will be selected. If you select a path, the entire node at that path will be selected.
Make sure you focus the editor before setting the selection. Focusing the editor using editable.focus()
may not work correctly in WebKit, so the best way of doing this is with clickAtPath
.
// Click the first paragraph to focus the editor
await clickAtPath(page, editorHandle, [0]);
await setSelection(page, editorHandle, {
path: [0, 0],
offset: 2,
});
await page.keyboard.type('Hello world!');
Working with Editor Methods
You can't directly import queries or transforms into Playwright tests since they run outside the browser context. Instead:
- Interact with the editor like a user would (preferred)
- Call methods on the editor object inside
evaluate
/evaluateHandle
:
await editorHandle.evaluate((editor) => {
editor.tf.insertNodes(/* ... */);
});
See Playwright's docs for more about evaluate
and evaluateHandle
.