A collection of helpful tricks for Playwright tests
Playwright does not add a newline at the end of files created with non-image snapshots - the text snapshots created with expect().toMatchSnapshot()
- as discussed in microsoft/playwright#33416
.
This means that the following code will create a file snapshot.txt
with the content abc
, without any newline at the end:
import { test, expect } from '@playwright/test';
test('example test', () => {
expect('abc').toMatchSnapshot('snapshot.txt');
});
Snapshot files without newlines at the ends are problematic because commonly-used software like the GitHub "Edit in Place" feature and other common editor configurations will silently add a newline in the edge case of editing a snapshot file, which will cause the snapshot test to fail in a confusing way.
Also, POSIX and *nix tools assume newlines at the end of files, so Playwright text snapshots will not play nice with those.
Unless the Playwright team reverses their "working as intended" decision and adds a fix to make text snapshots interoperable Create interoperable, this needs to be worked around.
The current workaround to create robust text snapshots with Playwright is to manually adding a newline at the end of the string passed to expect()
:
import { test, expect } from '@playwright/test';
test('example test', () => {
expect(
'abc' +
// Make Playwright snapshot file interoperable
// - https://github.com/microsoft/playwright/issues/33416#issuecomment-2456363012
'\n',
).toMatchSnapshot('snapshot.txt');
});
Scroll to all visible lazy-loaded images and wait for successful loading of image:
const lazyImages = await page.locator('img[loading="lazy"]:visible').all();
for (const lazyImage of lazyImages) {
await lazyImage.scrollIntoViewIfNeeded();
await expect(lazyImage).not.toHaveJSProperty('naturalWidth', 0);
}
One workaround for this is to assert the length of the .all()
array (if you know it) to wait for it to stabilize:
const lazyImagesLocator = page.locator('img[loading="lazy"]:visible');
// Assert on length to wait for image visibility to stabilize
// after client-side JavaScript hides some images
// https://github.com/microsoft/playwright/issues/31737#issuecomment-2233775909
await expect(lazyImagesLocator).toHaveCount(13);
const lazyImages = await lazyImagesLocator.all();
for (const lazyImage of lazyImages) {
await lazyImage.scrollIntoViewIfNeeded();
await expect(lazyImage).not.toHaveJSProperty('naturalWidth', 0);
}
Playwright does not (as of June 2024) have support for visual comparison testing with PDFs.
There are many issues asking for this feature, but the current position of the Playwright team is that PDF.js should be used instead, to render the PDF to a canvas.
It's not clear how the Playwright team suggests to do this, but one way is to navigate to about:blank
, use page.setContent()
to add a PDF.js viewer to the page, which accepts a URL, and then use expect(page).toHaveScreenshot()
:
// HTML template string no-op for VS Code highlighting / formatting
function html(strings: TemplateStringsArray, ...values: unknown[]) {
return strings.reduce((result, string, i) => {
return result + string + (values[i] ?? '');
}, '');
}
test('PDF has screenshot', async ({ page }) => {
// Go to page without Content-Security-Policy header, to avoid CSP
// prevention of script loading from https://mozilla.github.io
await page.goto('about:blank');
await page.setContent(html`
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<canvas></canvas>
<script src="https://mozilla.github.io/pdf.js/build/pdf.mjs" type="module"></script>
<script type="module">
pdfjsLib.GlobalWorkerOptions.workerSrc =
'https://mozilla.github.io/pdf.js/build/pdf.worker.mjs';
try {
const pdf = await pdfjsLib.getDocument(
'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf',
).promise;
const page = await pdf.getPage(1);
const viewport = page.getViewport({ scale: 1.5 });
const canvas = document.querySelector('canvas');
canvas.height = viewport.height;
canvas.width = viewport.width;
await page.render({
canvasContext: canvas.getContext('2d'),
viewport,
}).promise;
} catch (error) {
console.error('Error loading PDF:', error);
}
</script>
</body>
</html>
`);
await page.waitForTimeout(1000);
await expect(page).toHaveScreenshot({ fullPage: true });
});
Test that <img>
elements have a src
attribute that is reachable and responds with image data:
const img = page.locator('img');
await expect(img).not.toHaveJSProperty('naturalWidth', 0);