VALIDATION LAB•BUTTONS
Forms
Buttons
Exercise pointer-first button semantics through clear, user-facing feedback messages: primary click, double-click isolation, secondary-click context menu, hover-then-confirm, disabled guards, and loading/busy paths.
Scenario
Readable feedback teaches real pointer semantics faster than raw metrics, while stable `data-testid` hooks keep automation specs maintainable.
Interaction variants · mouse / pointer
Each variant writes a readable message to the feedback panel below.
Interaction feedback
variant: idle
Interact with a button variant to see feedback.
Live widget · interact freely
Manual test checklist
- 1Click — feedback panel reads "You clicked the button."
- 2Double-click — only the double-click message appears (single clicks stay silent on that card)
- 3Right-click — feedback shows "Right-click context action detected."
- 4Hover — confirm pill appears, click it for "Hover interaction confirmed."
- 5Disabled — control stays inert, helper text explains the disabled state
- 6Loading — spinner appears, duplicate clicks blocked, completion message shown
Expected result
Each interaction shows a clear user-facing feedback message in the Interaction feedback panel. Disabled controls remain inactive. Loading controls enter a busy state, block duplicate clicks, and show completion after the delay.
Automation challenge
Assert `btn-feedback-message` after each interaction. Right-click `btn-variant-context` with `{ button: 'right' }`. Double-click `btn-variant-dblclick` and confirm only the double-click copy appears. Click `btn-variant-disabled-wrapper` and assert `btn-variant-disabled` stays disabled with `blocked=0` on `btn-count-disabled`. Start `btn-variant-loading`, spam-click while busy, and assert the flow completes once with "Loading completed successfully."
Stable selectors
- Variant: Click
[data-testid="btn-variant-click"] - Variant: Double click
[data-testid="btn-variant-dblclick"] - Variant: Right click
[data-testid="btn-variant-context"] - Variant: Hover
[data-testid="btn-variant-hover"] - Hover confirm pill
[data-testid="btn-hover-confirm"] - Variant: Disabled
[data-testid="btn-variant-disabled"] - Disabled wrapper
[data-testid="btn-variant-disabled-wrapper"] - Variant: Loading
[data-testid="btn-variant-loading"] - Feedback panel
[data-testid="btn-feedback-panel"] - Feedback message
[data-testid="btn-feedback-message"] - Per-card status: click
[data-testid="btn-status-click"] - Per-card status: dblclick
[data-testid="btn-status-dblclick"] - Per-card status: context
[data-testid="btn-status-context"] - Per-card status: hover
[data-testid="btn-status-hover"] - Per-card status: disabled
[data-testid="btn-status-disabled"] - Per-card status: loading
[data-testid="btn-status-loading"] - Disabled helper line
[data-testid="btn-count-disabled"]
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. Target interactive controls by stable id when each variant is unique.
1234
await page.goto('https://lab.hakdogan.com/practice/buttons');
await page.locator('#btn-variant-click').click();
await page.locator('#btn-variant-context').click({ button: 'right' });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
1234567891011121314151617181920212223242526272829303132333435363738394041424344
import { test, expect } from '@playwright/test';
test('button QA lab — message-based feedback', async ({ page }) => {
await page.goto('https://lab.hakdogan.com/practice/buttons');
// Initial state — feedback panel announces the prompt.
await expect(page.getByTestId('btn-feedback-message'))
.toHaveText('Interact with a button variant to see feedback.');
// Click — readable feedback message.
await page.getByTestId('btn-variant-click').click();
await expect(page.getByTestId('btn-feedback-message'))
.toHaveText('You clicked the button.');
await expect(page.getByTestId('btn-status-click'))
.toHaveText('You clicked the button.');
// Double-click — only the dblclick message appears.
await page.getByTestId('btn-variant-dblclick').dblclick();
await expect(page.getByTestId('btn-feedback-message'))
.toHaveText('You double-clicked the button.');
// Right-click — context message.
await page.getByTestId('btn-variant-context').click({ button: 'right' });
await expect(page.getByTestId('btn-feedback-message'))
.toHaveText('Right-click context action detected.');
// Hover then confirm — two-step interaction.
await page.getByTestId('btn-variant-hover').hover();
await page.getByTestId('btn-hover-confirm').click();
await expect(page.getByTestId('btn-feedback-message'))
.toHaveText('Hover interaction confirmed.');
// Disabled — never increments and stays inert.
await expect(page.getByTestId('btn-variant-disabled')).toBeDisabled();
await expect(page.getByTestId('btn-count-disabled')).toContainText('blocked=0');
// Loading — second click during busy phase is ignored, completion message follows.
const loading = page.getByTestId('btn-variant-loading');
await loading.click();
await expect(page.getByTestId('btn-feedback-message')).toHaveText('Loading…');
await expect(loading).toBeDisabled();
await expect(page.getByTestId('btn-feedback-message'))
.toHaveText('Loading completed successfully.', { timeout: 5_000 });
});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.
Mixed: fill() for stable fields, type() / press() where keyboard semantics matter.
buttons.spec.ts- Click primary button
- Double-click isolation target
- Right-click context variant
- Hover-enter tracking
- Verify counters vs audit targets
- Pass