TypeScript · @playwright/test
Legal with Playwright
Filter lawyers by practice area, city and language and book a consultation; then generate a rental agreement through a wizard with a live document preview, cross-field rules, typed e-signatures and a stamp-duty calculation.
App under test
⚖️ LexDesk
- URL
- /apps/legal
- Deposit cap
- 6 × monthly rent
- Stamp duty
- 0.25% (min ₹100)
- Consult days
- closed Sundays
Scenarios to automate
Work top to bottom — each one adds a new technique.
- 1
Directory filters
Beginnerarea-*, lawyer-city, lawyer-language and lawyer-sort change lawyer-count and order.
- 2
Booking modal validation
Intermediateconfirm-consult without mode/day/time or with a short description shows consult-error.
- 3
Live preview
IntermediateTyping in the wizard updates preview-* fields immediately; blanks show underscores.
- 4
Cross-field rules
AdvancedDeposit above 6 months' rent and landlord = tenant are rejected with specific error-* messages.
- 5
Exact-match signatures
Intermediatelandlord-signature / tenant-signature must match the names exactly (case and spaces).
- 6
Stamp duty maths
Intermediate₹25,000 × 11 months + ₹1,50,000 deposit → stamp-duty ₹1,063.
- 7
Wizard navigation
Beginnerdoc-back keeps entered values; the stepper marks completed steps.
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 ×6 · XPath ×2 · getByTestId ×13
test('books a lawyer and signs a rental agreement', async ({ page }) => {
await page.goto('/apps/legal');
await page.getByRole('button', { name: 'Property' }).click();
await expect(page.getByTestId('lawyer-count')).toContainText('2 lawyers');
await page.locator('#lawyer-sort').selectOption('fee');
await page.getByTestId('book-l4').click();
await page.locator('xpath=//button[normalize-space()="Video"]').click();
await page.locator('[data-testid="consult-day"]:not([disabled])').first().click();
await page.getByLabel('Time').selectOption('12:00');
await page.getByLabel('Your matter').fill('Need a review of my rental agreement before signing.');
await page.getByTestId('confirm-consult').click();
await expect(page.getByTestId('consult-booked')).toContainText('LX-');
// Generate a rental agreement
await page.getByRole('link', { name: 'Create agreement' }).click();
await page.locator('#landlord-name').fill('Meera Kulkarni');
await page.getByLabel('Tenant (licensee) full name').fill('Amir Khan');
await expect(page.getByTestId('preview-tenant')).toContainText('Amir Khan');
await page.getByTestId('doc-next').click();
await page.locator('#property-address').fill('Flat 12B, Palm Residency, Baner');
await page.getByRole('combobox', { name: 'City' }).selectOption('Pune');
await page.getByTestId('doc-next').click();
await page.locator('#monthly-rent').fill('25000');
await page.locator('#security-deposit').fill('150000');
await page.getByRole('combobox', { name: 'Term' }).selectOption('11');
await page.getByTestId('agreement-start').nth(0).click();
await expect(page.getByTestId('preview-rent')).toContainText('₹25,000');
await expect(page.getByTestId('duty-estimate')).toContainText('₹1,063');
await page.getByTestId('doc-next').click();
await page.getByLabel('Landlord signature (Meera Kulkarni)').fill('Meera Kulkarni');
await page.getByLabel('Tenant signature (Amir Khan)').fill('Amir Khan');
await page.locator('#esign-consent').check();
await page.locator('xpath=//button[normalize-space()="Sign agreement"]').click();
await expect(page.getByTestId('agreement-id')).toContainText('AGR-');
await expect(page.getByTestId('stamp-duty')).toContainText('₹1,063');
await expect(page.getByTestId('preview-signed')).toContainText('Signed electronically');
});