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
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
- 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"
}
}
- 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;
- 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
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:
- 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);
};
// ...
- Wrap the sections interesting for a timespan measure with
await flow.startTimespan({ name: 'Select coffee' });
andawait 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();
};
// ...
- 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