Files
claude-skills-reference/engineering-team/playwright-pro/templates/crud/bulk-operations.md
Alireza Rezvani d33d03da50 feat: add playwright-pro plugin — production-grade Playwright testing toolkit (#254)
Complete Claude Code plugin with:
- 9 skills (/pw:init, generate, review, fix, migrate, coverage, testrail, browserstack, report)
- 3 specialized agents (test-architect, test-debugger, migration-planner)
- 55 test case templates across 11 categories (auth, CRUD, checkout, search, forms, dashboard, settings, onboarding, notifications, API, accessibility)
- TestRail MCP server (TypeScript) — 8 tools for bidirectional sync
- BrowserStack MCP server (TypeScript) — 7 tools for cross-browser testing
- Smart hooks (auto-validate tests, auto-detect Playwright projects)
- 6 curated reference docs (golden rules, locators, assertions, fixtures, pitfalls, flaky tests)
- Leverages Claude Code built-ins (/batch, /debug, Explore subagent)
- Zero-config for core features; TestRail/BrowserStack via env vars
- Both TypeScript and JavaScript support throughout

Co-authored-by: Leo <leo@openclaw.ai>
2026-03-05 13:50:05 +01:00

5.2 KiB

Bulk Operations Template

Tests selecting multiple items and performing bulk delete/update actions.

Prerequisites

  • Authenticated session via {{authStorageStatePath}}
  • At least {{minItemCount}} entities seeded in list
  • App running at {{baseUrl}}

TypeScript

import { test, expect } from '@playwright/test';

test.describe('Bulk Operations', () => {
  test.use({ storageState: '{{authStorageStatePath}}' });

  test.beforeEach(async ({ page }) => {
    await page.goto('{{baseUrl}}/{{entityName}}s');
  });

  // Happy path: select all and bulk delete
  test('selects all and bulk deletes', async ({ page }) => {
    await page.getByRole('checkbox', { name: /select all/i }).check();
    const checkboxes = page.getByRole('row').filter({ hasNot: page.getByRole('columnheader') })
      .getByRole('checkbox');
    await expect(checkboxes.first()).toBeChecked();

    await page.getByRole('button', { name: /bulk delete/i }).click();
    await page.getByRole('dialog').getByRole('button', { name: /confirm/i }).click();
    await expect(page.getByRole('alert')).toContainText(/deleted/i);
    await expect(page.getByRole('row').filter({ hasNot: page.getByRole('columnheader') }))
      .toHaveCount(0);
  });

  // Happy path: select specific rows and bulk update status
  test('updates status of selected rows', async ({ page }) => {
    const rows = page.getByRole('row').filter({ hasNot: page.getByRole('columnheader') });
    await rows.nth(0).getByRole('checkbox').check();
    await rows.nth(1).getByRole('checkbox').check();
    await expect(page.getByText(/2 selected/i)).toBeVisible();

    await page.getByRole('button', { name: /bulk actions/i }).click();
    await page.getByRole('menuitem', { name: /mark as active/i }).click();
    await expect(page.getByRole('alert')).toContainText(/2.*updated/i);
  });

  // Happy path: toolbar appears only when items selected
  test('shows bulk action toolbar only when items are selected', async ({ page }) => {
    await expect(page.getByRole('toolbar', { name: /bulk actions/i })).toBeHidden();
    await page.getByRole('row').nth(1).getByRole('checkbox').check();
    await expect(page.getByRole('toolbar', { name: /bulk actions/i })).toBeVisible();
  });

  // Happy path: deselect all clears toolbar
  test('hides toolbar after deselecting all', async ({ page }) => {
    await page.getByRole('checkbox', { name: /select all/i }).check();
    await page.getByRole('checkbox', { name: /select all/i }).uncheck();
    await expect(page.getByRole('toolbar', { name: /bulk actions/i })).toBeHidden();
  });

  // Error case: bulk delete requires confirmation
  test('requires confirmation before bulk delete', async ({ page }) => {
    await page.getByRole('checkbox', { name: /select all/i }).check();
    await page.getByRole('button', { name: /bulk delete/i }).click();
    await expect(page.getByRole('dialog', { name: /confirm/i })).toBeVisible();
    await page.getByRole('button', { name: /cancel/i }).click();
    const rowCount = await page.getByRole('row').filter({ hasNot: page.getByRole('columnheader') }).count();
    expect(rowCount).toBeGreaterThan(0);
  });

  // Edge case: select all across pages
  test('shows "select all across pages" option when applicable', async ({ page }) => {
    await page.getByRole('checkbox', { name: /select all/i }).check();
    const crossPage = page.getByRole('button', { name: /select all.*across pages/i });
    if (await crossPage.isVisible()) {
      await crossPage.click();
      await expect(page.getByText(/all.*selected/i)).toBeVisible();
    }
  });
});

JavaScript

const { test, expect } = require('@playwright/test');

test.describe('Bulk Operations', () => {
  test.use({ storageState: '{{authStorageStatePath}}' });

  test.beforeEach(async ({ page }) => {
    await page.goto('{{baseUrl}}/{{entityName}}s');
  });

  test('shows bulk action toolbar when items selected', async ({ page }) => {
    await expect(page.getByRole('toolbar', { name: /bulk actions/i })).toBeHidden();
    await page.getByRole('row').nth(1).getByRole('checkbox').check();
    await expect(page.getByRole('toolbar', { name: /bulk actions/i })).toBeVisible();
  });

  test('selects all and bulk deletes', async ({ page }) => {
    await page.getByRole('checkbox', { name: /select all/i }).check();
    await page.getByRole('button', { name: /bulk delete/i }).click();
    await page.getByRole('dialog').getByRole('button', { name: /confirm/i }).click();
    await expect(page.getByRole('alert')).toContainText(/deleted/i);
  });

  test('requires confirmation before bulk delete', async ({ page }) => {
    await page.getByRole('checkbox', { name: /select all/i }).check();
    await page.getByRole('button', { name: /bulk delete/i }).click();
    await expect(page.getByRole('dialog', { name: /confirm/i })).toBeVisible();
  });
});

Variants

Variant Description
Select all + delete All rows selected → confirmed delete → empty list
Partial select + update N rows selected → status updated → success
Toolbar visibility Appears on select, hides on deselect
Deselect all Select all → uncheck → toolbar gone
Confirmation required Bulk delete shows dialog first
Cross-page select Select-all-pages option shown on multi-page lists