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
In order for your Playwright tests to access and interact with the editor, you'll need to add PlaywrightPlugin
to your editor:
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!');
Imported queries and transforms
You may want to import a query or a transform such as getBlockAbove
or insertNodes
into your Playwright test and use it.
Unfortunately, this is not possible. You can only interact directly with the editor
instance inside the browser context (using evaluate
or evaluateHandle
), and it isn't possible to pass imported functions from Playwright's scope into the browser. This is because neither the editor
object nor JavaScript functions can be adequately serialized.
The best workaround is to interact with the editor in the same way that a user would, without using any imported queries or transforms. This will make your Playwright tests more likely to catch bugs in your application.
If this isn't practical, you can instead call a method on the editor
object inside an evaluate
or evaluateHandle
. (Use evaluateHandle
if you need to return a reference to a DOM node or a JavaScript object from the browser. Use evaluate
if you need to return a serialized copy of a JavaScript object, or if you don't need to return any value.)
Note that while these queries and transforms can't be directly used in Playwright tests, they are available when working with the editor instance in your application code. For more information on how to use these methods in your application, refer to the Editor Methods documentation.
See Playwright's docs for more information about evaluate
and evaluateHandle
.
await editorHandle.evaluate((editor) => {
editor.insertNodes(/* ... */);
});
API
getEditorHandle
Gets a handle to the Plate editor instance.
Parameters
The Playwright page object.
A locator for the editable element. If not provided, it will look for the first [data-slate-editor] on the page.
Returns
A handle to the Plate editor instance.
getNodeByPath
Retrieves a node at the specified path.
Parameters
The Playwright page object.
The handle to the Plate editor instance.
The path to the node.
Returns
A handle to the node at the specified path.
getDOMNodeByPath
Gets the DOM node for a Slate node at the given path.
Parameters
The Playwright page object.
The handle to the Plate editor instance.
The path to the node.
Returns
An ElementHandle for the DOM node corresponding to the Slate node.
clickAtPath
Simulates a click on the node at the specified path.
Parameters
The Playwright page object.
The handle to the Plate editor instance.
The path to the node to click.
getSelection
Retrieves the current editor selection.
Parameters
The Playwright page object.
The handle to the Plate editor instance.
Returns
The current editor selection.
setSelection
Sets the editor selection to the specified range.
Parameters
The Playwright page object.
The handle to the Plate editor instance.
The location to set the selection.
getTypeAtPath
Gets the type of the node at the specified path.
Parameters
The Playwright page object.
The handle to the Plate editor instance.
The path to the node.
Returns
The type of the node at the specified path.
Customizable and extensible.