VALIDATION LAB•TABS
Navigation
Tabs
Switch between three tabs, verify only the active panel renders, and confirm the URL hash updates.
Scenario
Tabbed UIs (settings, profile, ticket detail) leak bugs around: lazy-mounted vs eagerly-mounted panels, URL hash sync for deep-links, and aria-selected state for screen readers.
Overview
Headline metrics — pass rate, MTTR, open defects. Updated every minute.
Live widget · interact freely
Manual test checklist
- 1Click each tab — the previous panel unmounts (or hides), the new one mounts
- 2Reload on `#logs` — Logs tab is selected without a flash of Overview
- 3Tab + ←/→ — keyboard navigation works between tabs
- 4Each tab has `role='tab'` and `aria-selected='true'` when active
- 5Confirm the panel exposes `role='tabpanel'`
Expected result
Clicking 'Logs' shows the Logs panel and writes `#logs` to the URL; reloading lands you back on Logs.
Automation challenge
Click `tab-logs`, assert the URL contains `#logs` AND the panel is visible. Reload on `https://…/practice/tabs#logs` — assert Logs is still active without an extra click.
Stable selectors
- Overview tab
[data-testid="tab-overview"] - Logs tab
[data-testid="tab-logs"] - Settings tab
[data-testid="tab-settings"] - Active panel
[data-testid="tab-panel-active"]
Locator strategy
Three levels from simple IDs to scoped Playwright locators. IDs and names are easy to learn but are not always the best long-term choice when labels change or components repeat.
Use simple IDs and names to understand how locating elements works.
123
await page.goto('https://lab.hakdogan.com/practice/tabs');
await page.locator('#tab-logs').click();Avoid as primary strategies
XPath (unless there is no alternative), long CSS chains, Tailwind-style utility class selectors, generated or unstable IDs, and volatile framework internals break when layout, styling, or DOM structure shifts.
12
await page.locator('.w-full.rounded-xl.border.bg-blue-500').fill('demo@example.com');
await page.locator('//div[2]/form/div[1]/input').fill('demo@example.com');Reference Playwright spec
123456789
import { test, expect } from '@playwright/test';
test('tab switch syncs URL hash', async ({ page }) => {
await page.goto('https://lab.hakdogan.com/practice/tabs');
await page.getByTestId('tab-logs').click();
await expect(page).toHaveURL(/#logs$/);
await expect(page.getByTestId('tab-panel-active')).toContainText('Logs');
});Headed Test Playback
Simulated headed-browser flow — no real browser is launched.
Automation-style playback (Playwright-shaped logs). No real browser; no commands run on your machine.
tabs.spec.ts- Interacting with tabs widget
- Awaiting deterministic render
- Asserting expected state
- Cleaning up context
- Pass