Files
claude-skills-reference/engineering-team/playwright-pro/templates/search/sorting.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.1 KiB

Search Sorting Template

Tests sorting results by name, date, and price.

Prerequisites

  • Search results for {{searchQuery}} with multiple items
  • App running at {{baseUrl}}

TypeScript

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

test.describe('Search Sorting', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('{{baseUrl}}/search?q={{searchQuery}}');
  });

  // Happy path: sort by name A-Z
  test('sorts results alphabetically A-Z', async ({ page }) => {
    await page.getByRole('combobox', { name: /sort by/i }).selectOption('name_asc');
    await expect(page).toHaveURL(/sort=name_asc/);
    const names = page.getByTestId('result-name');
    const first = await names.first().textContent();
    const second = await names.nth(1).textContent();
    expect(first!.localeCompare(second!)).toBeLessThanOrEqual(0);
  });

  // Happy path: sort by name Z-A
  test('sorts results alphabetically Z-A', async ({ page }) => {
    await page.getByRole('combobox', { name: /sort by/i }).selectOption('name_desc');
    const names = page.getByTestId('result-name');
    const first = await names.first().textContent();
    const second = await names.nth(1).textContent();
    expect(first!.localeCompare(second!)).toBeGreaterThanOrEqual(0);
  });

  // Happy path: sort by date newest
  test('sorts results by newest date first', async ({ page }) => {
    await page.getByRole('combobox', { name: /sort by/i }).selectOption('date_desc');
    await expect(page).toHaveURL(/sort=date_desc/);
    const dates = page.getByTestId('result-date');
    const firstDate = new Date(await dates.first().getAttribute('datetime') ?? '');
    const secondDate = new Date(await dates.nth(1).getAttribute('datetime') ?? '');
    expect(firstDate.getTime()).toBeGreaterThanOrEqual(secondDate.getTime());
  });

  // Happy path: sort by price low-high
  test('sorts by price low to high', async ({ page }) => {
    await page.getByRole('combobox', { name: /sort by/i }).selectOption('price_asc');
    const prices = page.getByTestId('result-price');
    const firstText = await prices.first().textContent() ?? '';
    const secondText = await prices.nth(1).textContent() ?? '';
    const first = parseFloat(firstText.replace(/[^0-9.]/g, ''));
    const second = parseFloat(secondText.replace(/[^0-9.]/g, ''));
    expect(first).toBeLessThanOrEqual(second);
  });

  // Happy path: sort by price high-low
  test('sorts by price high to low', async ({ page }) => {
    await page.getByRole('combobox', { name: /sort by/i }).selectOption('price_desc');
    const prices = page.getByTestId('result-price');
    const firstText = await prices.first().textContent() ?? '';
    const secondText = await prices.nth(1).textContent() ?? '';
    const first = parseFloat(firstText.replace(/[^0-9.]/g, ''));
    const second = parseFloat(secondText.replace(/[^0-9.]/g, ''));
    expect(first).toBeGreaterThanOrEqual(second);
  });

  // Happy path: sort persists with filters
  test('sort selection persists when filter applied', async ({ page }) => {
    await page.getByRole('combobox', { name: /sort by/i }).selectOption('price_asc');
    await page.getByRole('checkbox', { name: '{{filterCategory}}' }).check();
    await expect(page).toHaveURL(/sort=price_asc/);
    await expect(page.getByRole('combobox', { name: /sort by/i })).toHaveValue('price_asc');
  });

  // Edge case: default sort is relevance
  test('default sort is relevance', async ({ page }) => {
    await expect(page.getByRole('combobox', { name: /sort by/i })).toHaveValue('relevance');
  });
});

JavaScript

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

test.describe('Search Sorting', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('{{baseUrl}}/search?q={{searchQuery}}');
  });

  test('sorts alphabetically A-Z', async ({ page }) => {
    await page.getByRole('combobox', { name: /sort by/i }).selectOption('name_asc');
    await expect(page).toHaveURL(/sort=name_asc/);
    const names = page.getByTestId('result-name');
    const first = await names.first().textContent();
    const second = await names.nth(1).textContent();
    expect(first.localeCompare(second)).toBeLessThanOrEqual(0);
  });

  test('sorts by price low to high', async ({ page }) => {
    await page.getByRole('combobox', { name: /sort by/i }).selectOption('price_asc');
    const prices = page.getByTestId('result-price');
    const a = parseFloat((await prices.first().textContent()).replace(/[^0-9.]/g, ''));
    const b = parseFloat((await prices.nth(1).textContent()).replace(/[^0-9.]/g, ''));
    expect(a).toBeLessThanOrEqual(b);
  });

  test('default sort is relevance', async ({ page }) => {
    await expect(page.getByRole('combobox', { name: /sort by/i })).toHaveValue('relevance');
  });
});

Variants

Variant Description
Name A-Z First result ≤ second alphabetically
Name Z-A First result ≥ second alphabetically
Date newest Dates in descending order
Price low-high Prices in ascending order
Price high-low Prices in descending order
Sort + filter Sort param persists when filter applied
Default sort Relevance selected by default