Skip to content

Latest commit

 

History

History
209 lines (150 loc) · 6.32 KB

writing-basic-user-flows.md

File metadata and controls

209 lines (150 loc) · 6.32 KB

Writing basic user flows

Selector suggestions

When writing tests it is good practice to decouple your selector needed for the test from the actual code used to style your application. A good way to do this is using the data attribute and attribute selectors e.g. [data-test]="lcp-img".

The following selectors are suggested to align with the official user flow recorder feature:

  • data-testid
  • data-test
  • data-qa
  • data-cy
  • data-test-id
  • data-qa-id
  • data-testing

User flow measurement modes

Icon Mode Measure Performance Accessibility Best Practices SEO PWA
user-flow_navigation-icon Navigation Page load 100% / 30 100% / 30 100% / 30 100% / 30 ✔ / 7
user-flow_timespan-icon Timespan User Interaction 10 / 10 7 / 7
user-flow_snapshot-icon Snapshot Current page state 4 / 4 16 / 16 5 / 5 9 / 9

user-flow--example

Setup and navigation

Example measure - Order Coffee

In the following steps we will implement the a user flow measurement for the following interactions:

  • Navigate to page
  • Select coffee
  • Checkout order
  • Submit order
  1. Setup the .user-flowrc.json config file

Run npx @push-based/user-flow init in the console and accept the default value for every question.

This results in the following file:

./.user-flowrc.json

{
    "collect": {
        // URL to analyze
        "url": "https://coffee-cart.netlify.app/",
        // Path to user flows from root directory
        "ufPath": "./user-flows"
    },
    "persist": {
        // Output path for the reports from root directory
        "outPath": "./measures"
    }
}
  1. Create a basic-navigation.uf.mts file.

./user-flows/basic-navigation.uf.mts

import {
  UserFlowInteractionsFn,
  UserFlowContext,
  UserFlowProvider
} from '@push-based/user-flow';

// Your custom interactions with the page 
const interactions: UserFlowInteractionsFn = async (ctx: UserFlowContext): Promise<any> => {
  const { page, flow, browser, collectOptions } = ctx;
  const { url } = collectOptions;

  await flow.navigate(url, {
    name: 'Navigate to coffee cart',
  });

  // Select coffee

  // Checkout order

  // Submit order

};

export default {
  flowOptions: { name: "Order Coffee" },
  interactions,
} satisfies UserFlowProvider;
  1. Run CLI You can directly run the CLI command. The typescript files will get resolved and compiled live.

npx @push-based/user-flow --url=https://localhost:4200

Optionally you can pass params to overwrite the values form .user-flowrc.mts

npx @push-based/user-flow --ufPath=./user-flows --outPath=./user-flows-reports --url=https://localhost:4200

🤓 DX Tip:
For a faster development process you can use the --dryRun option to skip measurement and perform the interactions only
This is a multitude faster e.g. 3s vs 53s for a simple 2 step flow with navigation

Using the timespan measurement mode

Timespan measures are limited in the number of audits, but give us the opportunity to measure runtime changes in the page.

Let's start by filling in our interaction logic:

  1. Copy and paste the new lines into your flow.

./user-flows/basic-navigation.uf.mts

const interactions: UserFlowInteractionsFn = async (ctx: UserFlowContext): Promise<any> => {
  // ... 
  
  // Select coffee
  const cappuccinoItem = '[data-test=Cappucino]';
  await page.waitForSelector(cappuccinoItem);
  await page.click(cappuccinoItem);
  
  // Checkout order
  const checkoutBtn = '[data-test=checkout]';
  await page.waitForSelector(checkoutBtn);
  await page.click(checkoutBtn);

  const nameInputSelector = '#name';
  await page.waitForSelector(nameInputSelector);
  await page.type(nameInputSelector, 'nina');

  const emailInputSelector = '#email';
  await page.waitForSelector(emailInputSelector);
  await page.type(emailInputSelector, '[email protected]');
  
  // Submit order
  const submitBtn = '#submit-payment';
  await page.click(submitBtn);
  await page.waitForSelector(submitBtn);
  const successMsg = '.snackbar.success';
  await page.waitForSelector(successMsg);
  
};

// ...
  1. Wrap the sections interesting for a timespan measure with await flow.startTimespan({ name: 'Select coffee' }); and await flow.stopTimespan();.

./user-flows/basic-navigation.uf.mts

const interactions: UserFlowInteractionsFn = async (ctx: UserFlowContext): Promise<any> => {
  // ...
  
  await flow.navigate(url, {
    name: 'Navigate to coffee cart',
  });

  await flow.startTimespan({ name: 'Select coffee' });
  // Select coffee
  // ...
  await flow.endTimespan();

  await flow.startTimespan({ name: 'Checkout order' });
  // Checkout order
  // ...
  await flow.endTimespan();

  await flow.startTimespan({ name: 'Submit order' });
  // Submit order
  // ...
  await flow.endTimespan();
};

// ...

Use snapshot measures

  1. Wrap the sections interesting for a snapshot measure with await flow.snapshot({ name: 'step name' });.

./user-flows/basic-navigation.uf.mts

// Your custom interactions with the page 
const interactions: UserFlowInteractionsFn = async (ctx: UserFlowContext): Promise<any> => {

  // Select coffee
  //  endTimespan here ...
  
  await flow.snapshot({ name: 'Coffee selected' });

  // Checkout order
  //  endTimespan here ...
  
  await flow.snapshot({ name: 'Order checked out' });

  // Submit order
  //  endTimespan here ...
  
  await flow.snapshot({ name: 'Order submitted' });
};

// ...

made with ❤ by push-based.io