TypeScript · @playwright/test
Real estate with Playwright
Buy/Rent search with BHK chips, budget ranges and type filters, grid/list views, a shortlist with side-by-side compare, a photo gallery, an EMI calculator and site-visit booking.
App under test
🏠 HomeNest
- URL
- /apps/realestate
- Login
- not required
- Cities
- Pune · Mumbai · Bengaluru
- EMI
- standard reducing-balance formula
Scenarios to automate
Work top to bottom — each one adds a new technique.
- 1
Buy vs Rent
Beginnermode-rent swaps the budget options (₹/month) and the listings; price labels end in /mo.
- 2
Combined filters
Intermediatebhk-* chips + budget-min/max + type-* + furnishing; a min above max shows budget-error.
- 3
Sorting & views
Intermediateestate-sort by price; view-list/view-grid changes data-view on property-list.
- 4
Shortlist state
Beginnershortlist-<id> toggles aria-pressed and the shortlist-count badge; it persists across reloads.
- 5
Compare limit
IntermediateTick compare-* on 2–3 shortlisted homes for compare-table; a 4th shows compare-limit.
- 6
Gallery carousel
Beginnergallery-next/prev wrap around; thumb-* jumps; gallery-counter shows “n / 4”.
- 7
EMI maths
AdvancedChange emi-down, emi-rate, emi-tenure and verify emi-loan and emi-result against the formula.
- 8
Masked phone & visits
Intermediatecontact-agent masks the number until reveal-phone; book-visit validates, then list/cancel it under My visits.
Sample Playwright suite
A working starting point that finds elements every way Playwright can — by role, label, text, id, name, CSS, XPath and data-testid — so you practice them all. Pick your language, switch to data-testid only to compare, or try your own in the Locator Playground.
import { test, expect } from '@playwright/test';
// playwright.config.ts → use: { baseURL: 'https://www.practiceqaautomation.com' }
// Locators in this suite: getByRole ×4 · getByLabel ×5 · CSS ×4 · XPath ×1 · getByTestId ×9
test('shortlists a flat and books a site visit', async ({ page }) => {
await page.goto('/apps/realestate');
await page.getByRole('tab', { name: 'buy' }).click();
await page.getByLabel('City').selectOption('pune');
await page.getByTestId('bhk-2').click();
await page.locator('#budget-max').selectOption('10000000');
await page.getByRole('checkbox', { name: 'apartment' }).check();
await page.locator('#estate-sort').selectOption('price-asc');
await expect(page.getByTestId('property-count')).toContainText('2 properties');
await page.getByTestId('shortlist-p2').click();
await expect(page.getByTestId('shortlist-count')).toContainText('1');
await page.getByTestId('view-property-p2').click();
await expect(page.getByTestId('property-title')).toContainText('2 BHK Apartment in Baner');
await page.getByRole('button', { name: 'Next photo' }).click();
await expect(page.getByTestId('gallery-counter')).toContainText('2 / 4');
// EMI for ₹85 L with 20% down, 8.5% for 20 years
await page.getByLabel('Down payment').selectOption('20');
await page.getByLabel('Interest rate').selectOption('8.5');
await page.locator('#emi-tenure').selectOption('20');
await expect(page.getByTestId('emi-result')).toContainText('59,012');
await page.getByTestId('visit-day').nth(0).click();
await page.getByRole('combobox', { name: 'Time' }).selectOption('11:00');
await page.getByLabel('Your name').fill('Amir Khan');
await page.locator('#visitor-phone').fill('9876543210');
await page.getByLabel('Email').fill('amir@example.com');
await page.locator('xpath=//button[normalize-space()="Book visit"]').click();
await expect(page.getByTestId('visit-confirmed')).toContainText('VIS-');
});