TypeScript · @playwright/test
Advertising with Playwright
A 5-step campaign wizard — objective, audience with location autocomplete, budget & bidding, creative with character counters and a live preview — then launch and pause/resume.
App under test
📣 AdPulse
- URL
- /apps/ads
- Login
- not required
- Min budget
- $5/day · $100 lifetime
- Review time
- ~3 seconds
Scenarios to automate
Work top to bottom — each one adds a new technique.
- 1
Step validation
Beginnerwizard-next on an empty step shows step-errors and error-* for that step only.
- 2
Autocomplete chips
IntermediateType in location-input, pick location-option-*, then remove a chip with remove-location-*.
- 3
Dependent fields
Intermediatebid-strategy “Cost cap” reveals cost-cap-amount; unticking run-continuously reveals run-duration.
- 4
Character counters
Intermediateheadline-count tracks 0/40 and input stops at 40 characters (maxlength).
- 5
Live preview
Intermediatepreview-headline and preview-cta mirror the creative fields as you type.
- 6
Edit from review
Advancededit-step-2 jumps back to Audience; values are preserved when returning to Review.
- 7
Async status
AdvancedA launched campaign shows “In review”, then flips to “Active” ~3s later.
- 8
Toggle switches
BeginnerFlip toggle-<id> (role=switch) and assert aria-checked and the status badge.
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 ×3 · getByLabel ×3 · getByPlaceholder ×1 · CSS ×4 · XPath ×1 · getByTestId ×14
test('creates and launches a campaign', async ({ page }) => {
await page.goto('/apps/ads');
await page.getByTestId('create-campaign').click();
await expect(page).toHaveURL(/\/apps\/ads\/new/);
await page.getByTestId('objective-traffic').click();
await page.getByLabel('Campaign name').fill('Diwali Sale');
await page.getByTestId('wizard-next').click();
// Audience
await page.getByRole('combobox', { name: 'Min age' }).selectOption('25');
await page.locator('#age-max').selectOption('45');
await page.getByLabel('Locations').fill('Mum');
await page.locator('xpath=//button[normalize-space()="Mumbai, India"]').click();
await page.getByRole('checkbox', { name: 'Online shopping' }).check();
await expect(page.getByTestId('audience-reach')).toContainText('people');
await page.getByTestId('wizard-next').click();
// Budget
await page.getByTestId('budget-type-daily').click();
await page.locator('#budget-amount').fill('50');
await page.getByTestId('wizard-next').click();
// Creative with live preview
await page.getByLabel('Headline').fill('Big Diwali Sale up to 50% off');
await page.locator('#ad-description').fill('Lights, gifts and more at festive prices.');
await page.getByPlaceholder('https://', { exact: true }).fill('https://example.com/diwali');
await page.getByRole('combobox', { name: 'Call to action' }).selectOption('shop-now');
await expect(page.getByTestId('preview-headline')).toContainText('Big Diwali Sale');
await expect(page.getByTestId('preview-cta')).toContainText('Shop now');
await page.getByTestId('wizard-next').click();
await expect(page.getByTestId('review-name')).toContainText('Diwali Sale');
await page.locator('#accept-ad-policy').check();
await page.getByTestId('launch-campaign').click();
await expect(page.getByTestId('campaign-launched')).toBeVisible();
await expect(page.getByTestId('campaigns-table')).toContainText('Diwali Sale');
});