I want a custom Playwright command for page.goto
called page.gotoDOM
that automatically passes the waitUntil: 'domcontentloaded'
parameter so I don’t have to code it in every test.
I have followed the instructions for fixtures and tried various combinations without success.
My code so far:
// @ts-check
const base = require('@playwright/test');
const { test, expect } = require('@playwright/test');
const describe = test.describe;
const testDOM = base.test.extend({
pageDOM: async ({ host, page }, use) => {
await page.goto(host, { waitUntil: 'domcontentloaded'});
await use(page);
},
})
describe('Google', () => {
test('Search box', async ({ page }) => {
await page.goto('https://google.com/', { waitUntil: 'domcontentloaded'});
await expect(page.getByRole('combobox', { name: 'Search' })).toBeVisible();
});
testDOM('Search box using testDOM', async ({ page }) => {
await testDOM('https://google.com/'); // <-- Now I don't need to pass the waitUntil parameter
await expect(page.getByRole('combobox', { name: 'Search' })).toBeVisible();
});
})
Right now I want to get it working in one file, ultimately available for all files once that is done.
Currently the code above is complaining that testDOM requires more arguments but so far my attempts to correct that have failed.
2
Answers
Here is a solution for just using a plain function:
helper file
test file
but I'd rather use a solution that can be used in multiple file without separate imports so as part of the test infrastructure would be nice
There are many ways to do helper functions in Playwright. See Playwright: how to create a static utility method? which is highly relevant here.
For this case, I’d keep it simple and just inline
page.goto(url, {waitUntil: "domcontentloaded"})
everywhere that uses that option. This really isn’t much code and it seems premature to abstract it away–it’s clear to anyone familiar with Playwright what it does on sight, isn’t that verbose, and likely won’t require much maintenance.But if
"domcontentloaded"
is too long to look at over and over, you could make the config object a global:Note that Playwright has an even faster predicate that’s similar to
"domcontentloaded"
called"commit"
, which is shorter to type and can be inlined easily.Also note that
goto
generally shouldn’t be needed much–I usually use one per test file in atest.beforeEach
block. If you’re usinggoto
over and over at the beginning of each test, then you can avoid the repetition easily withbeforeEach
. Most navigations in test cases should be initiated by a user action like a click, notgoto
.Assuming none of the above work, another option is to write a plain, boring JS function that accepts
page
as the first argument. This is a straightforward and unclever approach and seems like a reasonable solution for this case.util.js:
foo.test.js:
This lets you pass in an optional options argument like
{timeout: 20_000}
as the last argument, just as in normal Playwright. When writing abstractions, I like to try to stick to the design of the library I’m using so it’s less surprising, so I would not want a signature likeawait gotoDOM({ page, url: 'https://google.com' })
as shown in this other answer, especially if I need to pass an options object through my wrapper into Puppeteer’s API.If you don’t like the
page
argument, you can use a closure pattern, discussed at length in the linked answer:util.js
foo.test.js:
If you only have one helper, this isn’t that exciting, but it can be useful once you have a handful of them since you generally only use one page per test.
By the way, the
base.test.extend
pattern in OP’s attempt seems best for POMs that you want to inject into each test block, but is overkill for a simple helper function like this.