TypeScript · @playwright/test
Help desk with Playwright
Customers raise tickets (priority set by category); agents work a queue with live SLA countdowns and breach states, bulk-assign, reply with canned responses and formatting, add internal notes, and resolve with a required reason.
App under test
🎧 DeskFlow
- URL
- /apps/helpdesk
- Agent
- agent@deskflow.dev
- Password
- Desk@123
- SLA
- Urgent 30m · High 1h · Med 4h · Low 24h
Scenarios to automate
Work top to bottom — each one adds a new technique.
- 1
Derived priority
BeginnerChoosing ticket-category shows which priority the ticket will get (Technical → High).
- 2
Live SLA timers
Intermediatesla-<id> counts down each second; breached tickets have data-breached="true" and red text.
- 3
Queue filters
Beginnerstatus-filter, priority-filter, owner-filter and queue-search combine; assert queue-count.
- 4
Bulk assign
Intermediateselect-all-tickets → bulk-assign-me updates every assignee-<id> cell.
- 5
Toolbar formatting
IntermediateSelect text in reply-text, click format-bold; the sent message renders <strong>.
- 6
Internal notes
Intermediateinternal-note switch → message has data-internal="true" and doesn't count as a first response.
- 7
First response
AdvancedSending a public reply stops the SLA timer (“Responded”) and moves Open → Pending.
- 8
Resolution rule
IntermediateSetting ticket-status to Resolved without resolution-category shows ticket-error.
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 ×4 · CSS ×3 · XPath ×4 · getByTestId ×10
test('raises a ticket and resolves it as an agent', async ({ page }) => {
await page.goto('/apps/helpdesk');
await page.getByLabel('Your name').fill('Grace Lee');
await page.locator('#requester-email').fill('grace@globex.com');
await page.getByRole('combobox', { name: 'Category' }).selectOption('technical');
await page.getByTestId('ticket-subject').fill('Cannot export report to PDF');
await page.getByLabel('Description').fill('The export button spins forever and nothing downloads.');
await page.locator('xpath=//button[normalize-space()="Submit ticket"]').click();
await expect(page.getByTestId('ticket-created')).toContainText('#1041');
// Switch to the agent side
await page.getByTestId('agent-login-link').click();
await page.locator('#agent-email').fill('agent@deskflow.dev');
await page.getByLabel('Password').fill('Desk@123');
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page).toHaveURL(/\/apps\/helpdesk\/queue/);
await expect(page.getByTestId('open-ticket').nth(0)).toContainText('Cannot export report');
await expect(page.getByTestId('sla-1041')).toContainText('00:5');
await page.getByTestId('open-ticket').nth(0).click();
await page.locator('xpath=//button[normalize-space()="Assign to me"]').click();
await expect(page.getByTestId('ticket-toast')).toContainText('Assigned');
await page.getByRole('combobox', { name: 'Insert canned response' }).selectOption('greeting');
await page.getByTestId('send-reply').click();
await expect(page.getByTestId('message').nth(1)).toContainText('thanks for reaching out');
await page.getByRole('switch', { name: 'Internal note' }).click();
await page.getByTestId('reply-text').fill('Reproduced on staging, escalating to dev.');
await page.getByTestId('send-reply').click();
await expect(page.getByTestId('message').nth(2)).toContainText('Reproduced on staging');
await page.locator('#ticket-status').selectOption('Resolved');
await page.locator('xpath=//button[normalize-space()="Update ticket"]').click();
await expect(page.getByTestId('ticket-error')).toBeVisible();
await page.getByLabel('Resolution').selectOption('bug-fixed');
await page.locator('xpath=//button[normalize-space()="Update ticket"]').click();
await expect(page.getByTestId('ticket-status-badge')).toContainText('Resolved');
});