PracticeQAAutomation logoPracticeQAAutomation
Pw

TypeScript · @playwright/test

Project management with Playwright

A sprint Kanban you can drag across (or use the move menu), a create-issue modal with label chips, issue pages with sub-task progress and comments, an SVG burndown chart, and a backlog with bulk move to sprint.

App under test

🗂️ TaskBoard

URL
/apps/projects
Sprint
Sprint 14 · day 6 of 10
Issue keys
PA-1 … (next PA-9)
Login
not required

Scenarios to automate

Work top to bottom — each one adds a new technique.

  1. 1

    Assignee avatars filter

    Beginner

    filter-assignee-* toggles (aria-pressed) and only-mine narrow the board; assert board-count.

  2. 2

    Create-issue validation

    Intermediate

    save-task with blanks shows task-errors and error-*; a short title is rejected.

  3. 3

    Chips with Enter

    Intermediate

    Type in label-input and press Enter (or add-label); remove with remove-label-*.

  4. 4

    Drag across columns

    Advanced

    Drag a task-card onto col-review (HTML5 DnD); or use move-task-<key>. col-count-* updates.

  5. 5

    Sub-task progress

    Beginner

    Add sub-tasks, tick subtask-0 and assert subtask-progress “1/2”.

  6. 6

    SVG chart data

    Advanced

    burndown-point circles carry data-day/data-points; the last point equals burndown-remaining.

  7. 7

    Bulk move

    Intermediate

    Tick select-<key> in the backlog and move-to-sprint; the issues appear on the board.

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.

Language
taskboard.spec.ts
import { test, expect } from '@playwright/test';

// playwright.config.ts → use: { baseURL: 'https://www.practiceqaautomation.com' }
// Locators in this suite: getByRole ×5 · getByLabel ×3 · getByPlaceholder ×3 · CSS ×1 · XPath ×4 · getByTestId ×13
test('creates an issue and finishes it in the sprint', async ({ page }) => {
  await page.goto('/apps/projects');
  await page.getByTestId('create-task').click();
  await page.getByLabel('Summary').fill('Add login rate limiting');
  await page.getByRole('combobox', { name: 'Type' }).selectOption('story');
  await page.locator('#task-priority').selectOption('high');
  await page.getByRole('combobox', { name: 'Assignee' }).selectOption('amir');
  await page.getByLabel('Story points').selectOption('5');
  await page.getByPlaceholder('Type and press Enter', { exact: true }).fill('security');
  await page.locator('xpath=//button[normalize-space()="Add"]').click();
  await expect(page.getByTestId('label-chip')).toContainText('security');
  await page.locator('xpath=//button[normalize-space()="Create"]').click();
  await expect(page.getByTestId('board-toast')).toContainText('PA-9 created');
  await expect(page.getByTestId('col-todo')).toContainText('Add login rate limiting');
  await page.getByRole('link', { name: 'Add login rate limiting' }).click();
  await page.getByPlaceholder('Add a sub-task', { exact: true }).fill('Write tests');
  await page.locator('xpath=//button[normalize-space()="Add"]').click();
  await page.getByPlaceholder('Add a sub-task', { exact: true }).fill('Implement limiter');
  await page.locator('xpath=//button[normalize-space()="Add"]').click();
  await page.getByLabel('Write tests').check();
  await expect(page.getByTestId('subtask-progress')).toContainText('1/2');
  await page.getByTestId('back-to-board').click();
  await page.getByTestId('move-task-PA-9').selectOption('done');
  await expect(page.getByTestId('col-done')).toContainText('Add login rate limiting');
  await page.getByRole('link', { name: 'Sprint report' }).click();
  await expect(page.getByTestId('burndown-total')).toContainText('26 pts');
  await expect(page.getByTestId('burndown-remaining')).toContainText('18 pts');
  await page.getByRole('link', { name: 'Backlog' }).click();
  await page.getByTestId('select-PA-6').check();
  await page.getByTestId('move-to-sprint').click();
  await expect(page.getByTestId('backlog-count')).toContainText('2 issues');
});

Practicing another tool?