PracticeQAAutomation logoPracticeQAAutomation
Pw

TypeScript · @playwright/test

ERP with Playwright

A sortable, paginated inventory grid, purchase orders with dynamic line items and GST totals, and a two-role approval workflow with an audit trail.

App under test

🏭 CoreERP

URL
/apps/erp
Buyer
buyer / Erp@123
Manager
manager / Erp@123
Tax
GST 18%

Scenarios to automate

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

  1. 1

    Role-based access

    Beginner

    As buyer, open /apps/erp/approvals and assert access-denied; the manager sees the queue.

  2. 2

    Grid sorting

    Intermediate

    Click sort-qty twice and verify qty-* values are descending; aria-sort reflects the state.

  3. 3

    Filters & pagination

    Intermediate

    Combine inventory-search, warehouse-filter, low-stock-only and page-size; walk page-next.

  4. 4

    Reorder shortcut

    Intermediate

    reorder-<sku> opens a new PO with that item pre-filled on line 0.

  5. 5

    Dynamic line items

    Advanced

    Add 3 lines, remove the middle one, and check the remaining line-*-total and po-total.

  6. 6

    PO validation

    Intermediate

    Submitting with no vendor, no lines, qty 0 or duplicate items shows po-errors and error-*.

  7. 7

    Two-user workflow

    Advanced

    Buyer submits → logout → manager approves; po-status and po-timeline update.

  8. 8

    Rejection with comment

    Advanced

    reject-po requires a 10+ char reject-comment; it appears as po-comment in the timeline.

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
coreerp.spec.ts
import { test, expect } from '@playwright/test';

// playwright.config.ts → use: { baseURL: 'https://www.practiceqaautomation.com' }
// Locators in this suite: getByRole ×4 · getByLabel ×3 · CSS ×3 · XPath ×4 · getByTestId ×7
test('raises and approves a purchase order', async ({ page }) => {
  await page.goto('/apps/erp');

  // Buyer raises the purchase order
  await page.getByLabel('Username').fill('buyer');
  await page.locator('#erp-password').fill('Erp@123');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await expect(page).toHaveURL(/\/apps\/erp\/dashboard/);
  await page.locator('xpath=//a[normalize-space()="Purchase orders"]').click();
  await page.getByTestId('new-po').click();
  await page.getByRole('combobox', { name: 'Vendor' }).selectOption('v2');
  await page.getByTestId('po-delivery-14').click();
  await page.getByTestId('add-line').click();
  await page.getByLabel('Item').selectOption('SKU-1004');
  await page.locator('#line-0-qty').fill('25');
  await expect(page.getByTestId('line-0-total')).toContainText('8,400.00');
  await expect(page.getByTestId('po-total')).toContainText('9,912.00');
  await page.locator('xpath=//button[normalize-space()="Submit for approval"]').click();
  await expect(page).toHaveURL(/\/apps\/erp\/po\//);
  await expect(page.getByTestId('po-status')).toContainText('Pending approval');
  await page.getByRole('button', { name: 'Log out' }).click();

  // Manager approves it
  await page.getByLabel('Username').fill('manager');
  await page.locator('#erp-password').fill('Erp@123');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await expect(page).toHaveURL(/\/apps\/erp\/dashboard/);
  await page.locator('xpath=//a[normalize-space()="Approvals"]').click();
  await page.getByTestId('approval-review').nth(0).click();
  await page.locator('xpath=//button[normalize-space()="Approve"]').click();
  await expect(page.getByTestId('po-status')).toContainText('Approved');
});

Practicing another tool?