</>

Technology

Playwright

Difficulty

Intermediate

Interview Question

How would you handle a test scenario where you need to verify a dynamic table with sorting and filtering?

Answer

Scenario: Testing Dynamic Tables with Sort and Filter

Test Sorting

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

test('sort products table by price ascending', async ({ page }) => {
  await page.goto('/admin/products');

  // Get initial order
  const prices = await page.locator('td.price').allTextContents();
  const initialPrices = prices.map(p => parseFloat(p.replace('$', '')));

  // Click Price column header to sort
  await page.getByRole('columnheader', { name: 'Price' }).click();

  // Verify sort indicator appears
  await expect(page.getByRole('columnheader', { name: 'Price' })).toHaveAttribute('aria-sort', 'ascending');

  // Wait for table to update
  await page.waitForLoadState('networkidle');

  // Verify prices are in ascending order
  const sortedPrices = await page.locator('td.price').allTextContents();
  const sorted = sortedPrices.map(p => parseFloat(p.replace('$', '')));

  for (let i = 0; i < sorted.length - 1; i++) {
    expect(sorted[i]).toBeLessThanOrEqual(sorted[i + 1]);
  }
});

test('sort by clicking column header twice (desc)', async ({ page }) => {
  await page.goto('/products');

  const nameHeader = page.getByRole('columnheader', { name: 'Name' });
  await nameHeader.click(); // Ascending
  await nameHeader.click(); // Descending

  await expect(nameHeader).toHaveAttribute('aria-sort', 'descending');

  const names = await page.locator('td.product-name').allTextContents();
  const sortedDesc = [...names].sort((a, b) => b.localeCompare(a));
  expect(names).toEqual(sortedDesc);
});

Test Filtering

TypeScript
test('filter products by category', async ({ page }) => {
  await page.goto('/products');

  const initialCount = await page.locator('tr.product-row').count();
  expect(initialCount).toBeGreaterThan(10);

  // Apply category filter
  await page.getByLabel('Category').selectOption('Electronics');

  // Wait for filtered results
  await page.waitForLoadState('networkidle');

  // Verify all visible rows belong to Electronics
  const categories = await page.locator('td.category').allTextContents();
  categories.forEach(cat => expect(cat).toBe('Electronics'));

  // Verify count reduced
  const filteredCount = await page.locator('tr.product-row').count();
  expect(filteredCount).toBeLessThan(initialCount);
  expect(filteredCount).toBeGreaterThan(0);
});

Test Search/Filter Combined

TypeScript
test('search and sort products', async ({ page }) => {
  await page.goto('/products');

  // Apply search
  await page.getByPlaceholder('Search products...').fill('laptop');
  await page.waitForLoadState('networkidle');

  // Verify search results
  const names = await page.locator('td.product-name').allTextContents();
  names.forEach(name => expect(name.toLowerCase()).toContain('laptop'));

  // Now sort the results
  await page.getByRole('columnheader', { name: 'Price' }).click();

  const prices = await page.locator('td.price').allTextContents();
  const numPrices = prices.map(p => parseFloat(p.replace(/[$,]/g, '')));

  for (let i = 0; i < numPrices.length - 1; i++) {
    expect(numPrices[i]).toBeLessThanOrEqual(numPrices[i + 1]);
  }
});

Verify API Params with Sorting

TypeScript
test('table sort sends correct API parameters', async ({ page }) => {
  const apiCalls: URL[] = [];

  await page.route('/api/products*', async route => {
    apiCalls.push(new URL(route.request().url()));
    await route.continue();
  });

  await page.goto('/products');

  // Click to sort by price descending
  await page.getByRole('columnheader', { name: 'Price' }).click(); // asc
  await page.getByRole('columnheader', { name: 'Price' }).click(); // desc

  // Verify the API call includes sort parameters
  const sortCall = apiCalls.find(url => url.searchParams.has('sortBy'));
  expect(sortCall?.searchParams.get('sortBy')).toBe('price');
  expect(sortCall?.searchParams.get('sortDir')).toBe('desc');
});

Test Pagination After Filter

TypeScript
test('pagination resets after applying filter', async ({ page }) => {
  await page.goto('/products?page=3');
  await expect(page.locator('.pagination .current')).toHaveText('3');

  // Apply filter
  await page.getByLabel('In Stock Only').check();

  // Pagination should reset to page 1
  await expect(page.locator('.pagination .current')).toHaveText('1');
});

Follow AutomateQA

Related Topics