Playwright
// Navigate with keyboard
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
// Get focused element
const focused = await page.evaluate(
() => document.activeElement?.tagName
);
// Check focus visibility with CSS
const button = page.getByTestId('button-1');
await button.focus();
const styles = await button.evaluate(el => {
const computed = getComputedStyle(el);
return {
outline: computed.outline,
boxShadow: computed.boxShadow
};
});
// Verify focus indicator exists
expect(styles.outline).not.toBe('none');axe-core Rules
import AxeBuilder from '@axe-core/playwright';
// Note: axe-core can't fully test
// focus visibility (visual check needed)
// But can detect outline: none in CSS
const results = await new AxeBuilder({ page })
.withRules(['focus-visible'])
.analyze();
// Manual check: tab through all elements
const focusable = await page.$$(`
a[href], button, input, select, textarea,
[tabindex]:not([tabindex="-1"])
`);
for (const el of focusable) {
await el.focus();
// Screenshot for visual verification
await page.screenshot({
path: `focus-screenshot.png`
});
}