Download sample data files
Drop these into ./data/ in your Playwright project. The XLSX file generates client-side via SheetJS so you always get the latest rows.
Eleven patterns covering Faker, inline arrays, JSON, CSV, XLSX, hooks, and a clean Page Object Model
with a shared UtilElementLocator helper. Every snippet targets TTA practice pages
(https://app.thetestingacademy.com/playwright/multiple_element_filter.html for login,
https://app.thetestingacademy.com/playwright/tables/practice.html for register/QA-profile).
No third-party URLs.
Drop these into ./data/ in your Playwright project. The XLSX file generates client-side via SheetJS so you always get the latest rows.
One test, one fresh user generated at call time. Install: npm i -D @faker-js/faker.
import { test, expect } from '@playwright/test';
import { faker } from '@faker-js/faker';
test(`Register single user with Faker`, async ({ page }) => {
const firstName = faker.person.firstName();
const lastName = faker.person.lastName();
const email = faker.internet.email({ firstName: 'Auto' });
const telephone = faker.phone.number({ style: 'national' });
const password = faker.internet.password({ length: 20, memorable: true, pattern: /[A-Z]/, prefix: 'Auto ' });
await page.goto('https://app.thetestingacademy.com/playwright/tables/practice.html');
await page.getByRole('textbox', { name: 'First Name' }).fill(firstName);
await page.getByRole('textbox', { name: 'Last Name' }).fill(lastName);
await page.getByRole('textbox', { name: 'Email' }).fill(email);
await page.getByRole('textbox', { name: 'Phone' }).fill(telephone);
await page.getByRole('textbox', { name: 'Password' }).first().fill(password);
await page.getByRole('button', { name: 'Save profile' }).click();
await expect(page.locator('#submission-output')).toContainText(firstName);
});
generateUser() utilityFactor the fake-data generation out so any test can grab a fresh user with one call.
import { test, expect } from '@playwright/test';
import { faker } from '@faker-js/faker';
function generateUser() {
return {
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
email: faker.internet.email({ firstName: 'Auto' }),
telephone: faker.phone.number({ style: 'national' }),
password: faker.internet.password({ length: 20, memorable: true, pattern: /[A-Z]/, prefix: 'Auto ' }),
};
}
test(`Register single user via generateUser()`, async ({ page }) => {
const user = generateUser();
await page.goto('https://app.thetestingacademy.com/playwright/tables/practice.html');
await page.getByRole('textbox', { name: 'First Name' }).fill(user.firstName);
await page.getByRole('textbox', { name: 'Last Name' }).fill(user.lastName);
await page.getByRole('textbox', { name: 'Email' }).fill(user.email);
await page.getByRole('textbox', { name: 'Password' }).first().fill(user.password);
await page.getByRole('button', { name: 'Save profile' }).click();
await expect(page.locator('#submission-output')).toContainText(user.firstName);
});
Generate N independent tests with Faker data. Each iteration becomes its own Playwright test, so failures isolate cleanly and run in parallel.
import { test, expect } from '@playwright/test';
import { faker } from '@faker-js/faker';
const userCount = 8;
for (let i = 1; i <= userCount; i++) {
test(`Register new user# ${i}`, async ({ page }) => {
const user = {
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
email: faker.internet.email({ firstName: 'Auto' }),
telephone: faker.phone.number({ style: 'national' }),
password: faker.internet.password({ length: 20, memorable: true, pattern: /[A-Z]/, prefix: 'Auto ' }),
};
await page.goto('https://app.thetestingacademy.com/playwright/tables/practice.html');
await page.getByRole('textbox', { name: 'First Name' }).fill(user.firstName);
await page.getByRole('textbox', { name: 'Last Name' }).fill(user.lastName);
await page.getByRole('textbox', { name: 'Email' }).fill(user.email);
await page.getByRole('textbox', { name: 'Password' }).first().fill(user.password);
await page.getByRole('button', { name: 'Save profile' }).click();
await expect(page.locator('#submission-output')).toContainText(user.firstName);
});
}
Sometimes you need real-looking emails. Faker the name, then craft the email from a curated domain list. One iteration per domain.
import { test, expect } from '@playwright/test';
import { faker } from '@faker-js/faker';
const totalUserCount = 5;
const emailDomains = ['gmail.com', 'yahoo.com', 'outlook.com', 'tta.dev', 'icloud.com'];
for (let i = 1; i <= totalUserCount; i++) {
test(`Register user# ${i} (${emailDomains[i-1]})`, async ({ page }) => {
const firstName = faker.person.firstName();
const lastName = faker.person.lastName();
const email = `${firstName.toLowerCase()}.${lastName.toLowerCase()}@${emailDomains[i-1]}`;
const password = faker.internet.password({ length: 20, memorable: true, pattern: /[A-Z]/, prefix: 'Auto ' });
await page.goto('https://app.thetestingacademy.com/playwright/tables/practice.html');
await page.getByRole('textbox', { name: 'First Name' }).fill(firstName);
await page.getByRole('textbox', { name: 'Last Name' }).fill(lastName);
await page.getByRole('textbox', { name: 'Email' }).fill(email);
await page.getByRole('textbox', { name: 'Password' }).first().fill(password);
await page.getByRole('button', { name: 'Save profile' }).click();
await expect(page.locator('#submission-output')).toContainText(email);
});
}
Smallest DDT footprint. Hard-coded array, one test per row. Switch the SUT here to the login page to keep it varied.
import { test, expect } from '@playwright/test';
const loginData = [
{ username: '[email protected]', password: 'test123' },
{ username: '[email protected]', password: 'test123' },
];
for (const data of loginData) {
test(`login test for ${data.username}`, async ({ page }) => {
await page.goto('https://app.thetestingacademy.com/playwright/multiple_element_filter.html');
await page.getByRole('textbox', { name: 'Username' }).fill(data.username);
await page.getByRole('textbox', { name: 'Password' }).fill(data.password);
await page.getByRole('button', { name: 'Login' }).click();
await expect(page).toHaveURL(/multiple_element_filter/);
});
}
Read once at module load, parse with the built-in JSON.parse. Use a TS type so each row is statically typed. Download register.json above.
import { test, expect } from '@playwright/test';
import fs from 'fs';
type RegData = {
firstName: string;
lastName: string;
telephone: string;
password: string;
subscribeNewsletter: string;
};
const registerData: RegData[] = JSON.parse(
fs.readFileSync('./data/register.json', 'utf-8')
);
for (const user of registerData) {
test(`Register from JSON: ${user.firstName}`, async ({ page }) => {
await page.goto('https://app.thetestingacademy.com/playwright/tables/practice.html');
await page.getByRole('textbox', { name: 'First Name' }).fill(user.firstName);
await page.getByRole('textbox', { name: 'Last Name' }).fill(user.lastName);
await page.getByRole('textbox', { name: 'Email' }).fill(getRandomEmail());
await page.getByRole('textbox', { name: 'Phone' }).fill(user.telephone);
await page.getByRole('textbox', { name: 'Password' }).first().fill(user.password);
if (user.subscribeNewsletter === 'Yes') {
await page.getByRole('radio', { name: 'Yes' }).click();
} else {
await page.getByRole('radio', { name: 'No' }).click();
}
await page.getByRole('button', { name: 'Save profile' }).click();
await expect(page.locator('#submission-output')).toContainText(user.firstName);
});
}
function getRandomEmail(): string {
const randomValue = Math.random().toString(36).substring(2, 9);
return `auto_${randomValue}@tta.dev`;
}
csv-parse/syncInstall: npm i -D csv-parse. Read the file synchronously at module load, parse with columns: true so each row becomes an object keyed by header. Download register.csv above.
import { test, expect } from '@playwright/test';
import fs from 'fs';
import { parse } from 'csv-parse/sync';
type RegData = {
firstName: string; lastName: string;
telephone: string; password: string;
subscribeNewsletter: string;
};
const fileContent = fs.readFileSync('./data/register.csv', 'utf-8');
const registerData: RegData[] = parse(fileContent, {
columns: true,
skip_empty_lines: true,
});
for (const user of registerData) {
test(`Register from CSV: ${user.firstName}`, async ({ page }) => {
await page.goto('https://app.thetestingacademy.com/playwright/tables/practice.html');
await page.getByRole('textbox', { name: 'First Name' }).fill(user.firstName);
await page.getByRole('textbox', { name: 'Last Name' }).fill(user.lastName);
await page.getByRole('textbox', { name: 'Email' }).fill(getRandomEmail());
await page.getByRole('textbox', { name: 'Phone' }).fill(user.telephone);
await page.getByRole('textbox', { name: 'Password' }).first().fill(user.password);
if (user.subscribeNewsletter === 'Yes') {
await page.getByRole('radio', { name: 'Yes' }).click();
} else {
await page.getByRole('radio', { name: 'No' }).click();
}
await page.getByRole('button', { name: 'Save profile' }).click();
await expect(page.locator('#submission-output')).toContainText(user.firstName);
});
}
function getRandomEmail(): string {
const randomValue = Math.random().toString(36).substring(2, 9);
return `auto_${randomValue}@tta.dev`;
}
xlsxInstall: npm i -D xlsx. Read workbook, pick a named sheet, convert to JSON. Hit the users.xlsx button above to download a generated workbook with the same shape.
import { test, expect } from '@playwright/test';
import XLSX from 'xlsx';
const workbook = XLSX.readFile('./data/users.xlsx');
const sheet = workbook.Sheets['register'];
const users = XLSX.utils.sheet_to_json(sheet); // excel -> JSON
for (const [index, user] of users.entries()) {
const { firstName, lastName, phone, password } = user as any;
test(`Register from XLSX #${index+1}: ${firstName}`, async ({ page }) => {
await page.goto('https://app.thetestingacademy.com/playwright/tables/practice.html');
await page.getByRole('textbox', { name: 'First Name' }).fill(firstName);
await page.getByRole('textbox', { name: 'Last Name' }).fill(lastName);
await page.getByRole('textbox', { name: 'Email' }).fill(getRandomEmail());
await page.getByRole('textbox', { name: 'Phone' }).fill(phone);
await page.getByRole('textbox', { name: 'Password' }).first().fill(password);
await page.getByRole('button', { name: 'Save profile' }).click();
await expect(page.locator('#submission-output')).toContainText(firstName);
});
}
function getRandomEmail(): string {
const randomValue = Math.random().toString(36).substring(2, 9);
return `auto_${randomValue}@tta.dev`;
}
beforeAll / beforeEach / afterEach / afterAllOrder: beforeAll -> (beforeEach -> test body -> afterEach) per test -> afterAll. Use them for shared setup (server up, logged-in session) and cleanup (logout, close browser).
import { test, expect } from '@playwright/test';
test.beforeAll(async () => {
console.log('beforeAll -- server is up and running!');
});
test.beforeEach(async () => {
console.log('beforeEach -- user is logged in!');
});
test('Home Page Test', async () => {
console.log('home page test');
});
test('Search Product Test', async () => {
console.log('search product test');
});
test('Cart Test', async () => {
console.log('cart test');
});
test.afterEach(async () => {
console.log('afterEach -- logout!');
});
test.afterAll(async () => {
console.log('afterAll -- close browser!');
});
Read CSV, hand each row to the page-object. Test stays declarative, no page. calls leak out of the POM.
import { expect, test } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
import { RegisterPage } from '../pages/RegisterPage';
import fs from 'fs';
import { parse } from 'csv-parse/sync';
type RegData = {
firstName: string; lastName: string;
telephone: string; password: string;
subscribeNewsletter: string;
};
const fileContent = fs.readFileSync('./data/register.csv', 'utf-8');
const registerData: RegData[] = parse(fileContent, {
columns: true, skip_empty_lines: true,
});
for (const user of registerData) {
test(`@register verify user is able to register ${user.firstName}`, async ({ page, baseURL }) => {
const loginPage = new LoginPage(page);
await loginPage.goToLoginPage(baseURL);
const registerPage: RegisterPage = await loginPage.navigateToRegisterPage();
const isUserRegistered: boolean = await registerPage.registerUser(
user.firstName,
user.lastName,
getRandomEmail(),
user.telephone,
user.password,
user.subscribeNewsletter,
);
expect(isUserRegistered).toBeTruthy();
});
}
function getRandomEmail(): string {
const randomValue = Math.random().toString(36).substring(2, 9);
return `auto_${randomValue}@tta.dev`;
}
UtilElementLocator)Each page-object exposes locators (private) and actions (public). All low-level interactions go through a shared UtilElementLocator helper so timeouts, logging and waits live in one place.
import { Locator, Page } from '@playwright/test';
import { UtilElementLocator } from '../utils/UtilElementLocator';
import { HomePage } from '../pages/HomePage';
import { RegisterPage } from '../pages/RegisterPage';
export class LoginPage {
// 1. page locators / object repository
private readonly page: Page;
private readonly eleUtil: UtilElementLocator;
private readonly emailId: Locator;
private readonly password: Locator;
private readonly loginBtn: string;
private readonly warningMsg: Locator;
private readonly registerLink: Locator;
// 2. constructor
constructor(page: Page) {
this.page = page;
this.eleUtil = new UtilElementLocator(page);
this.emailId = page.getByRole('textbox', { name: 'Username' });
this.password = page.getByRole('textbox', { name: 'Password' });
this.loginBtn = 'button[type="submit"]';
this.warningMsg = page.locator('.alert.alert-danger');
this.registerLink = page.getByText('Register', { exact: true });
}
// 3. page actions / methods
async goToLoginPage(baseURL: string | undefined) {
await this.page.goto(baseURL + '/playwright/multiple_element_filter.html');
}
async doLogin(email: string, password: string): Promise<HomePage> {
await this.eleUtil.fill(this.emailId, email);
await this.eleUtil.fill(this.password, password);
await this.eleUtil.click(this.loginBtn, { force: true, timeout: 5000 });
return new HomePage(this.page);
}
async getInvalidLoginMessage(): Promise<string | null> {
const errorMsg = await this.eleUtil.getText(this.warningMsg);
console.log('invalid login warning: ' + errorMsg);
return errorMsg;
}
async navigateToRegisterPage(): Promise<RegisterPage> {
await this.eleUtil.click(this.registerLink, { force: true }, 1);
return new RegisterPage(this.page);
}
}
import { Locator, Page } from '@playwright/test';
import { UtilElementLocator } from '../utils/UtilElementLocator';
import { LoginPage } from '../pages/LoginPage';
import { ResultsPage } from '../pages/ResultsPage';
export class HomePage {
readonly page: Page;
private readonly eleUtil: UtilElementLocator;
private readonly loginLink: Locator;
private readonly logoutLink: Locator;
private readonly search: Locator;
private readonly searchIcon: Locator;
constructor(page: Page) {
this.page = page;
this.eleUtil = new UtilElementLocator(page);
this.logoutLink = page.getByRole('link', { name: 'Logout' });
this.loginLink = page.getByRole('link', { name: 'Login' });
this.search = page.getByRole('textbox', { name: 'Search' });
this.searchIcon = page.locator('#search button');
}
async isUserLoggedIn(): Promise<boolean> {
return await this.eleUtil.isVisible(this.logoutLink, 0);
}
async logout(): Promise<LoginPage> {
await this.eleUtil.click(this.logoutLink, { timeout: 5000 }, 1);
await this.eleUtil.click(this.loginLink, { timeout: 5000 }, 1);
return new LoginPage(this.page);
}
async doSearch(searchKey: string): Promise<ResultsPage> {
console.log(`search key: ${searchKey}`);
await this.eleUtil.fill(this.search, searchKey);
await this.eleUtil.click(this.searchIcon);
return new ResultsPage(this.page);
}
}
import { expect, Locator, Page } from '@playwright/test';
import { UtilElementLocator } from '../utils/UtilElementLocator';
export class RegisterPage {
private readonly page: Page;
private readonly eleUtil: UtilElementLocator;
private readonly firstNameInput: Locator;
private readonly lastNameInput: Locator;
private readonly emailInput: Locator;
private readonly telephoneInput: Locator;
private readonly passwordInput: Locator;
private readonly confirmPasswordInput: Locator;
private readonly newsletterYesRadio: Locator;
private readonly newsletterNoRadio: Locator;
private readonly agreeCheckbox: Locator;
private readonly continueButton: Locator;
private readonly successMsg: Locator;
constructor(page: Page) {
this.page = page;
this.eleUtil = new UtilElementLocator(page);
this.firstNameInput = page.getByRole('textbox', { name: 'First Name' });
this.lastNameInput = page.getByRole('textbox', { name: 'Last Name' });
this.emailInput = page.getByRole('textbox', { name: 'Email' });
this.telephoneInput = page.getByRole('textbox', { name: 'Phone' });
this.passwordInput = page.getByRole('textbox', { name: 'Password' }).first();
this.confirmPasswordInput = page.getByRole('textbox', { name: 'Password Confirm' });
this.newsletterYesRadio = page.getByRole('radio', { name: 'Yes' });
this.newsletterNoRadio = page.getByRole('radio', { name: 'No' });
this.agreeCheckbox = page.locator('[name="agree"]');
this.continueButton = page.getByRole('button', { name: 'Save profile' });
this.successMsg = page.locator('#submission-output');
}
async registerUser(
firstName: string,
lastName: string,
email: string,
telephone: string,
password: string,
subscribeNewsletter: string,
): Promise<boolean> {
await this.eleUtil.fill(this.firstNameInput, firstName);
await this.eleUtil.fill(this.lastNameInput, lastName);
await this.eleUtil.fill(this.emailInput, email);
await this.eleUtil.fill(this.telephoneInput, telephone);
await this.eleUtil.fill(this.passwordInput, password);
await this.eleUtil.fill(this.confirmPasswordInput, password);
if (subscribeNewsletter === 'Yes') {
await this.eleUtil.click(this.newsletterYesRadio);
} else {
await this.eleUtil.click(this.newsletterNoRadio);
}
await this.eleUtil.click(this.agreeCheckbox);
await this.eleUtil.click(this.continueButton);
await expect(this.successMsg).toContainText(firstName);
return true;
}
}
import { Locator, Page } from '@playwright/test';
import { UtilElementLocator } from '../utils/UtilElementLocator';
export class ProductInfoPage {
private readonly page: Page;
private readonly eleUtil: UtilElementLocator;
private readonly header: Locator;
private readonly imageCount: Locator;
private readonly productMetaData: Locator;
private readonly productPriceData: Locator;
private readonly productMap = new Map<string, string | number | null>();
constructor(page: Page) {
this.page = page;
this.eleUtil = new UtilElementLocator(page);
this.header = page.locator('h1');
this.imageCount = page.locator('div#content img');
this.productMetaData = page.locator("(//div[@id='content']//ul[@class='list-unstyled'])[1]/li");
this.productPriceData = page.locator("(//div[@id='content']//ul[@class='list-unstyled'])[2]/li");
}
async getProductHeader(): Promise<string> {
const header = await this.eleUtil.getInnerText(this.header);
return header.trim();
}
async getProductDetails(): Promise<Map<string, string | number | null>> {
this.productMap.set('header', await this.getProductHeader());
this.productMap.set('imagecount', await this.imageCount.count());
await this.getProductMetaData();
await this.getProductPricingData();
return this.productMap;
}
private async getProductMetaData() {
const rows = await this.productMetaData.allInnerTexts();
for (const meta of rows) {
const [k, v] = meta.split(':');
this.productMap.set(k.trim(), v.trim());
}
}
private async getProductPricingData() {
const rows = await this.productPriceData.allInnerTexts();
this.productMap.set('price', rows[0].trim());
this.productMap.set('extaxprice', rows[1].split(':')[1].trim());
}
}
import { Locator, Page } from '@playwright/test';
import { UtilElementLocator } from '../utils/UtilElementLocator';
import { ProductInfoPage } from '../pages/ProductInfoPage';
export class ResultsPage {
private readonly page: Page;
private readonly eleUtil: UtilElementLocator;
private readonly results: Locator;
constructor(page: Page) {
this.page = page;
this.eleUtil = new UtilElementLocator(page);
this.results = page.locator('.product-thumb');
}
async getSearchResultsCount(): Promise<number> {
await this.page.waitForTimeout(2000);
return await this.results.count();
}
async selectProduct(productName: string): Promise<ProductInfoPage> {
await this.eleUtil.click(this.page.getByRole('link', { name: productName }));
return new ProductInfoPage(this.page);
}
}
For the UtilElementLocator source (string-or-Locator Flex type, click / fill / type / getText / waitFor / select methods), see the Test modifiers page.