Skip to content

Instantly share code, notes, and snippets.

@minhphong306
Created September 6, 2025 12:14
Show Gist options
  • Save minhphong306/0906109bd7ea8a7b072173c5136dc761 to your computer and use it in GitHub Desktop.
Save minhphong306/0906109bd7ea8a7b072173c5136dc761 to your computer and use it in GitHub Desktop.
import { test, expect, Page } from '@playwright/test';
import { faker } from '@faker-js/faker';
import path from 'path';
test.describe('Lesson 02 - Ex 01', async () => {
const baseUrl = 'https://material.playwrightvn.com/';
const registerData = {
username: faker.person.fullName(),
email: faker.internet.email(),
gender: faker.helpers.arrayElement(['Male', 'Female']),
hobbies: ['traveling'],
interests: [
{ value: 'technology' },
{ value: 'science' },
{ value: 'music' },
],
country: 'usa',
dateOfBirth: faker.date.birthdate().toISOString().split('T')[0],
biography: faker.lorem.sentence(),
ratingUsOutOfTen: faker.number.int({ min: 1, max: 10 }).toString(),
favoriteColor: faker.color.rgb(),
newsletter: faker.datatype.boolean(0.75),
enableFeature: faker.datatype.boolean(0.75),
ratingStarOutOfFive: faker.number.float({ min: 0, max: 4.9, fractionDigits: 1 }),
customDate: faker.date.birthdate().toISOString().split('T')[0]
};
const shoppingCartData = {
product1: {
id: 1,
name: 'Product 1',
price: 10.00,
quantity: 2,
},
product2: {
id: 2,
name: 'Product 2',
price: 20.00,
quantity: 3,
},
product3: {
id: 3,
name: 'Product 3',
price: 30.00,
quantity: 1,
}
}
test.beforeEach(async ({ page }) => {
await test.step(`Step 01: Navigate to the page: ${baseUrl}`, async () => {
await page.goto(baseUrl);
});
});
test('Test 01 - Register Page', async ({ page }) => {
await test.step('Step 02: Click to "Bài học 1: Register Page" then verify "User Registration" title display', async () => {
await page.getByRole('link', { name: /Bài học 1: Register Page/ }).click();
const userRegistrationHeading = page.getByRole('heading', { name: 'User Registration' });
await expect(userRegistrationHeading).toBeVisible();
});
await test.step('Step 03: Fill register form then verify returned data', async () => {
await page.getByLabel('Username').fill(registerData.username);
await page.getByRole('textbox', { name: 'Email' }).fill(registerData.email);
await page.getByRole('radio', { name: `${registerData.gender}`, exact: true }).check();
for (const hobby of registerData.hobbies) {
await page.getByLabel(hobby, { exact: false }).check();
}
await page.getByRole('listbox', { name: 'Interests' }).selectOption(registerData.interests);
await page.getByRole('combobox', { name: 'Country' }).selectOption(
{ value: `${registerData.country}` }
);
await page.getByLabel('Date of Birth').fill(registerData.dateOfBirth);
await page.getByRole('button', { name: 'Profile Picture' }).setInputFiles(
path.join(process.cwd(), 'tests', 'student-submissions', '01-nghia', 'lesson-02', 'mcp.jpeg')
);
await page.getByLabel('Biography').fill(registerData.biography);
await page.locator('#rating').fill(registerData.ratingUsOutOfTen);
await page.locator('input[type="color"]').fill(registerData.favoriteColor);
await page.getByText('Hover over me').hover({ timeout: 1_000 });
expect(page.locator('.tooltiptext', { hasText: 'Subscribe to our newsletter for updates' })).toBeVisible();
if (registerData.newsletter) {
await page.locator('#newsletter').check();
}
if (registerData.enableFeature) {
await page.locator('#toggleOption + span').check();
}
const box = await page.locator('#starRating').boundingBox();
if (box) {
const x = box.x + (registerData.ratingStarOutOfFive / 5) * box.width;
const y = box.y + box.height / 2;
await page.mouse.click(x, y);
} else {
console.error('Could not get bounding box for #starRating');
}
await page.locator('#customDate').evaluate((element, value) => element.setAttribute('value', value), registerData.customDate);
await page.getByRole('button', { name: 'Register' }).click();
await expect(page.locator('#userTable tbody tr:first-child td:nth-child(2)')).toHaveText(registerData.username);
await expect(page.locator('#userTable tbody tr:first-child td:nth-child(3)')).toHaveText(registerData.email);
const infoCell = page.locator('#userTable tbody tr:first-child td:nth-child(4)');
await expect(infoCell).toContainText(`Gender: ${registerData.gender}`, { ignoreCase: true });
await expect(infoCell).toContainText(`Hobbies: ${registerData.hobbies.join(', ')}`);
await expect(infoCell).toContainText(`Country: ${registerData.country}`);
await expect(infoCell).toContainText(`Date of Birth: ${registerData.dateOfBirth}`);
await expect(infoCell).toContainText(`Biography: ${registerData.biography}`);
await expect(infoCell).toContainText(`Rating: ${registerData.ratingUsOutOfTen}`);
await expect(infoCell).toContainText(`Favorite Color: ${registerData.favoriteColor}`);
await expect(infoCell).toContainText(`Newsletter: ${registerData.newsletter ? 'Yes' : 'No'}`);
await expect(infoCell).toContainText(`Enable Feature: ${registerData.enableFeature ? 'Yes' : 'No'}`);
await expect(infoCell).toContainText(`Star Rating: ${Number(registerData.ratingStarOutOfFive.toFixed(1))}⭐`);
await expect(infoCell).toContainText(`Custom Date: ${registerData.customDate}`);
});
});
test('Test 02 - Product Page', async ({ page }) => {
await test.step('Step 02: Click to "Bài học 2: Product page" then verify "Simple E-commerce" title display', async () => {
await page.getByRole('link', { name: /Bài học 2: Product page/ }).click();
const simpleEcommerceHeading = page.getByRole('heading', { name: 'Simple E-commerce' });
await expect(simpleEcommerceHeading).toBeVisible();
});
await test.step('Step 03: Purchase products', async () => {
for (const product of Object.values(shoppingCartData)) {
for (let i = 0; i < product.quantity; i++) {
//cách khác: await page.locator('.product-info').filter({ hasText: `${product.name}` }).getByRole('button', { name: 'Add to Cart' }).click();
await page.locator(`button[data-product-id="${product.id}"]`).click();
}
}
});
await test.step('Step 04: Verify shopping cart data', async () => {
for (const product of Object.values(shoppingCartData)) {
await verifyProductInCart(page, product.name, product.price, product.quantity);
}
});
await test.step('Step 05: Verify total price', async () => {
const totalPrice = Object.values(shoppingCartData).reduce((total, product) => total + product.price * product.quantity, 0);
await expect(page.locator('tfoot .total-price')).toHaveText(`$${totalPrice.toFixed(2)}`);
});
});
test('Test 03 - Todo page', async ({ page }) => {
page.on('dialog', dialog => dialog.accept());
const maxTodoCount = 100;
// If maxTodoCount is odd, expectedEvenTodoCount = (maxTodoCount - 1) / 2
// e.g.: maxTodoCount = 13 => expectedEvenTodoCount = (13 - 1) / 2 = 6
const expectedEvenTodoCount = (maxTodoCount & 1) === 1 ? (maxTodoCount + 1) / 2 : maxTodoCount / 2;
await test.step('Step 02: Click to "Bài học 3: Todo page" then verify "To-Do List" title display', async () => {
await page.getByRole('link', { name: /Bài học 3: Todo page/ }).click();
const todoListHeading = page.getByRole('heading', { name: 'To-Do List' });
await expect(todoListHeading).toBeVisible();
});
await test.step(`Step 03: Add ${maxTodoCount} todos then verify there are ${maxTodoCount} todos`, async () => {
for (let i = 1; i <= maxTodoCount; i++) {
await page.locator('#new-task').fill(`todo ${i}`);
await page.locator('#add-task').click();
}
await expect(page.locator('#task-list li')).toHaveCount(maxTodoCount);
});
await test.step(`Step 04: Remove all todo with odd index then verify there are ${expectedEvenTodoCount} todos`, async () => {
for (let i = 1; i <= maxTodoCount; i += 2) {
await page.locator(`#task-list li button#todo-${i}-delete`).click();
}
await expect(page.locator('#task-list li')).toHaveCount(expectedEvenTodoCount);
});
});
test('Test 04 - Personal notes', async ({ page }) => {
const maxNoteCount = 10;
await test.step('Step 02: Click to "Bài học 4: Personal notes" then verify "Personal Notes" title display', async () => {
await page.getByRole('link', { name: /Bài học 4: Personal notes/ }).click();
const personalNotesHeading = page.getByRole('heading', { name: 'Personal Notes' });
await expect(personalNotesHeading).toBeVisible();
});
await test.step(`Step 03: Add ${maxNoteCount} notes then verify there are ${maxNoteCount} notes`, async () => {
for (let i = 1; i <= maxNoteCount; i++) {
await page.locator('#note-title').fill(`title ${i}`);
await page.locator('#note-content').fill(faker.lorem.sentence());
await page.locator('#add-note').click();
}
await expect(page.locator('#notes-list li')).toHaveCount(maxNoteCount);
});
await test.step('Step 04: Search the added note', async () => {
const searchValue = `title ${faker.number.int({ min: 1, max: maxNoteCount })}`;
await page.locator('#search').fill(searchValue);
await expect(page.locator('#notes-list li', { hasText: searchValue }).first()).toBeVisible();
});
});
async function verifyProductInCart(page: Page, productName: string, expectedPrice: number, expectedQuantity: number) {
const productRow = page.locator('tbody#cart-items tr').filter({ hasText: productName });
await expect(productRow.locator('td:nth-child(1)')).toHaveText(productName);
await expect(productRow.locator('td:nth-child(2)')).toHaveText(`$${expectedPrice.toFixed(2)}`);
await expect(productRow.locator('td:nth-child(3)')).toHaveText(expectedQuantity.toString());
await expect(productRow.locator('td:nth-child(4)')).toHaveText(`$${(expectedPrice * expectedQuantity).toFixed(2)}`);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment