Skip to main content

5 Tips for Effective Puppeteer Automation

In our "Complete Guide to Test Chrome Extensions with Puppeteer", we've covered all you need to know to get you up to speed on setting up end-to-end testing for a chrome extension. The fact is that many elements of the article can be brought to traditional front-end applications.

To truly deliver to you the complete knowledge we've acquired, we've decided to write this tiny, complementary blog post, where we'll cover ad-hoc topics, let's call it tips & tricks, to help you work with Puppeteer.

Tips#

We will give you a full set of complete code snippet examples that enhance our Puppeteer workflows.

Clearing an input field#

Surprisingly there's no API to clear an input field. However, there are plenty of workarounds. Our favorite is the following.

async function clear(el) {
await el.click({ clickCount: 3 });
await el.press('Backspace');
}

We're taking advantage of a Chrome feature, whereby three consecutive clicks select the whole text within the targeted input. This is limited to Chrome, but it's certainly a nifty trick to have in your toolbelt.

Wait until a typed value is in sync#

This tip might be an unpopular opinion as it came up purely by experiencing the pain of failing tests because input values are not in sync with the value provided to the Puppeteer primitive element.type. The need for this tool might be because DOM updates are asynchronous tasks, especially when abstracted through UI libraries and frameworks such as React and Vue. This utility function will perform a wait check on the target input element to ensure the typed value is actually in sync with the DOM node.

async function ensureValueIsTyped(page, selector, value) {
const inputEl = await page.$(selector);
// `clear` refers to the above utility in this article
await clear(inputEl);
await inputEl.type(value);
try {
await page.waitForFunction(
({ selector, value }) => {
return document.querySelector(selector)?.value === value;
},
{ timeout: 5000 },
{ selector, value }
);
} catch (error) {
const actual = await page.evaluate(s => document.querySelector(s)?.value, selector);
console.warn(`ensureValueIsTyped failed because value in the DOM is "${actual}" but expected is "${value}" (selector: "${selector}")`);
throw error;
}
}

Sleep#

export async function sleep(ms) {
return await new Promise(resolve => {
setTimeout(() => {
resolve(true);
}, ms);
});
}

With the above code snippet, you can add specific sleep calls in between your test cases. This is particularly useful if you're observing your test run in the heedful mode and you want to observe the state of your web application just for a few seconds and later proceed with your test. Here's a usage example (don't forget the await keyword).

// ...
await sleep(5000);
// ...
tip

It's not a good practice to add random sleeps in your code. Try to use this utility for debugging purposes only.

Screenshots#

Puppeteer allows you to take screenshots of a given page instance. Here's an exciting wrapper function to perform screenshots on a given provided page.

// assumes there's a "tmp" folder where we run the tests (usually root of the project)
async function prtScn(page, path = `./tmp/Screenshot ${new Date().toString()}.png`) {
try {
await page.screenshot({ path, type: 'png', fullPage: true });
} catch (error) {
console.info('Failed to take screenshot but test will proceed...', error);
return Promise.resolve();
}
}

This helper function is handy to name the screenshots for you and abstract your screenshot calls API configurations. In this case, we're using the option fullPage to make sure we capture the entire web application.

info

Notice we return Promise.resolve() when the screenshot fails, we believe there's not reason to break the test run, unless this is somehow part of your test.

Breakpoint#

This is a recommended technique by the chrome team. You can evaluate a page and drop a debugger; statement to create a breakpoint.

async function breakpoint() {
await page.evaluate(() => {
debugger;
});
}

Important: this will only take effect if you pass in devtools: true within the puppeteer.launch options.


And these are our tips. Let us know if you found them helpful by pressing the 👍 feedback button at the end of the page.



If you liked this article, consider sharing (tweeting) it to your followers.



Did you like this article?