Chrome Dev tools recorder + Puppeteer

Recorder tab on DevTools

Creating a recording

  1. Navigate to the page where you want to start the recording.
  2. Open the DevTools by secoundary-clicking on the page and selecting Inspect.
  3. Open the Recorder tab.
  4. Click the Create a new recording button.
  5. Enter a name for your user flow.
  6. Click Start a new recording.
  7. Go through the user journey on your page.
  8. Click End recording

Replay a recording

  • The Replay button simply performs the recorded steps, so you may verify your recording.
  • The Settings let you control the emulated network speed
    • Reducing the network speed to a slower connection is helpful when testing performance and capped speed in more consistent measurements.
  • If a replay isn’t successful, the step where the replay failed is highlighted in the user flow.
  • You can edit individual steps (e.g. choose another element selector).

Measure Performance

  • Captures a DevTools Performance profile while going through the user flow. You may undestand this way which parts of the process are slowing the user down.
    • Hovering over the filmstrip screenshots can give you an idea of what’s going on at any given point.
    • The CPU utilization timeline at the top of the page can point out potential JavaScript bottlenecks.

Export

  • DevTools can export your user journey as a Puppeteer script (e.g. demo_user_flow_recording.js).

Running a Puppeteer script

Basics

Puppeteer is a NodeJS library that lets you control a browser through code.

1
2
3
npm install puppeteer
# run a puppeteer script you exported
node demo_user_flow_recording.js

Exported file overview

  • Each step is wrapped in curly braces, separating the steps and creating a separate scope for variables.
  • waitForSelectors is called with multiple selectors, so if one selector doesn’t work there are others to fall back to, making the script less likely to break and easier to debug when it does
  • waitForSelectors uses Puppeteer’s custom query handlers, so the script looks for an element matching aria/Search rather than CSS selector
  • There’s some code to handle setting the value on non-standard (?) elements
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// launch headless
const browser = await puppeteer.launch();
const page = await browser.newPage();

{
const targetPage = page;
await targetPage.setViewport({"width":1135,"height":338})
}
{
const targetPage = page;
const promises = [];
promises.push(targetPage.waitForNavigation());
await targetPage.goto('https://github.com/');
await Promise.all(promises);
}
{
const targetPage = page;
const element = await waitForSelectors([["aria/Search GitHub"],
["body > div.position-relative.js-header-wrapper > header > div > div.HeaderMenu.HeaderMenu--logged-out.position-fixed.top-0.right-0.bottom-0.height-fit.position-lg-relative.d-lg-flex.flex-justify-between.flex-items-center.flex-auto > div.d-lg-flex.flex-items-center.px-3.px-lg-0.text-center.text-lg-left > div.d-lg-flex.min-width-0.mb-3.mb-lg-0 > div > div > form > label > input.form-control.input-sm.header-search-input.jump-to-field.js-jump-to-field.js-site-search-focus.js-navigation-enable.jump-to-field-active.jump-to-dropdown-visible"]], targetPage);
await element.click({ offset: { x: 74.5, y: 24} });
}
{
const targetPage = page;
const element = await waitForSelectors([["aria/Search GitHub"],
["body > div.position-relative.js-header-wrapper > header > div > div.HeaderMenu.HeaderMenu--logged-out.position-fixed.top-0.right-0.bottom-0.height-fit.position-lg-relative.d-lg-flex.flex-justify-between.flex-items-center.flex-auto > div.d-lg-flex.flex-items-center.px-3.px-lg-0.text-center.text-lg-left > div.d-lg-flex.min-width-0.mb-3.mb-lg-0 > div > div > form > label > input.form-control.input-sm.header-search-input.jump-to-field.js-jump-to-field.js-site-search-focus.js-navigation-enable.jump-to-field-active.jump-to-dropdown-visible"]], targetPage);
const type = await element.evaluate(el => el.type);
if (["textarea","select-one","text","url","tel","search","password","number","email"].includes(type)) {
await element.type('react');
} else {
await element.focus();
await element.evaluate((el, value) => {
el.value = value;
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
}, "react");
}
}

Notes

  • Launch the script on normal chrome
    1
    const browser = await puppeteer.launch({ headless: false });