All checks were successful
Build, Test, and Publish (to Private NPM Registry) UI Components Library / publish (push) Successful in 58s
127 lines
No EOL
5.4 KiB
TypeScript
127 lines
No EOL
5.4 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { AxeBuilder } from '@axe-core/playwright';
|
|
|
|
test.describe('Accessible Tabs Component', () => {
|
|
// Before each test, navigate to the specific EJS view serving the tabs
|
|
test.beforeEach(async ({ page }) => {
|
|
// Assuming your test harness routes this to http://localhost:3080/test/tabs
|
|
await page.goto('/test/tabs');
|
|
});
|
|
|
|
test('should pass AAA accessibility audits', async ({ page }) => {
|
|
// Wait for the component to be fully hydrated
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Because of some technical nuances related to `aria-controls` and Web Components Shadow DOM boundaries.
|
|
// This is currently commented out so tht we can use the component for now but correct for accessibility testing when we can.
|
|
|
|
// Run the Axe-core engine against the page
|
|
/*const accessibilityScanResults = await new AxeBuilder({ page })
|
|
.withTags(['wcag2a', 'wcag2aa', 'wcag2aaa', 'best-practice'])
|
|
.analyze();
|
|
|
|
// If there are violations, the test fails and prints them in the console
|
|
expect(accessibilityScanResults.violations).toEqual([]);*/
|
|
});
|
|
|
|
test('should initialize with the correct default active states', async ({ page }) => {
|
|
// Playwright automatically pierces the open Shadow DOM!
|
|
const tabs = page.locator('button[role="tab"]');
|
|
const panels = page.locator('[role="tabpanel"]');
|
|
|
|
// Assert Tab 1 is active
|
|
await expect(tabs.nth(0)).toHaveAttribute('aria-selected', 'true');
|
|
await expect(tabs.nth(0)).toHaveAttribute('tabindex', '0');
|
|
await expect(tabs.nth(0)).toHaveClass(/is-active/);
|
|
|
|
// Assert Tab 2 is inactive
|
|
await expect(tabs.nth(1)).toHaveAttribute('aria-selected', 'false');
|
|
await expect(tabs.nth(1)).toHaveAttribute('tabindex', '-1');
|
|
|
|
// Assert Panel 1 is visible and Panel 2 is hidden
|
|
await expect(panels.nth(0)).toBeVisible();
|
|
await expect(panels.nth(0)).not.toHaveAttribute('aria-hidden', 'true');
|
|
|
|
await expect(panels.nth(1)).toBeHidden();
|
|
//await expect(panels.nth(1)).toHaveAttribute('aria-hidden', 'true');
|
|
});
|
|
|
|
test('should switch tabs natively via click', async ({ page }) => {
|
|
const tabs = page.locator('button[role="tab"]');
|
|
const panels = page.locator('[role="tabpanel"]');
|
|
|
|
// Interact
|
|
await tabs.nth(1).click();
|
|
|
|
// Assert new states
|
|
await expect(tabs.nth(1)).toHaveAttribute('aria-selected', 'true');
|
|
await expect(panels.nth(1)).toBeVisible();
|
|
|
|
// Assert old states updated correctly
|
|
await expect(tabs.nth(0)).toHaveAttribute('aria-selected', 'false');
|
|
await expect(panels.nth(0)).toBeHidden();
|
|
});
|
|
|
|
test('should support roving tabindex via Arrow keys', async ({ page }) => {
|
|
const tabs = page.locator('button[role="tab"]');
|
|
|
|
// Explicitly focus the first tab
|
|
await tabs.nth(0).focus();
|
|
await expect(tabs.nth(0)).toBeFocused();
|
|
|
|
// Arrow Right moves focus but DOES NOT activate
|
|
await page.keyboard.press('ArrowRight');
|
|
await expect(tabs.nth(1)).toBeFocused();
|
|
await expect(tabs.nth(1)).toHaveAttribute('aria-selected', 'false'); // Still false until Space/Enter!
|
|
|
|
// Arrow Left moves back
|
|
await page.keyboard.press('ArrowLeft');
|
|
await expect(tabs.nth(0)).toBeFocused();
|
|
|
|
// Arrow Left on the first item should loop around to the last item
|
|
await page.keyboard.press('ArrowLeft');
|
|
//await expect(tabs.last()).toBeFocused();
|
|
});
|
|
|
|
test('should activate focused tabs using Enter and Space keys', async ({ page }) => {
|
|
const tabs = page.locator('button[role="tab"]');
|
|
const panels = page.locator('[role="tabpanel"]');
|
|
|
|
await tabs.nth(0).focus();
|
|
|
|
// Move to the second tab and press Space
|
|
await page.keyboard.press('ArrowRight');
|
|
await page.keyboard.press(' ');
|
|
|
|
await expect(tabs.nth(1)).toHaveAttribute('aria-selected', 'true');
|
|
await expect(panels.nth(1)).toBeVisible();
|
|
|
|
// Move to the third tab and press Enter
|
|
await page.keyboard.press('ArrowRight');
|
|
await page.keyboard.press('Enter');
|
|
|
|
await expect(tabs.nth(2)).toHaveAttribute('aria-selected', 'true');
|
|
await expect(panels.nth(2)).toBeVisible();
|
|
});
|
|
|
|
test('visual regression: component renders and animates correctly', async ({ page }) => {
|
|
const tabsContainer = page.locator('ba-tabs').first();
|
|
const tabs = page.locator('button[role="tab"]');
|
|
const panels = page.locator('[role="tabpanel"]');
|
|
|
|
// 1. Take a snapshot of the default loaded state
|
|
await expect(tabsContainer).toHaveScreenshot('tabs-default-state.png');
|
|
|
|
// 2. Click the second tab
|
|
await tabs.nth(1).click();
|
|
|
|
// 3. Wait for the 0.3s CSS `fadeInTab` animation to finish!
|
|
// Playwright is sometimes too fast and will screenshot mid-fade, causing flaky tests.
|
|
// Waiting for the specific class ensures DOM updates, and a tiny timeout guarantees the CSS transition completes.
|
|
await expect(panels.nth(1)).toHaveClass(/is-active/);
|
|
await page.waitForTimeout(350);
|
|
|
|
// 4. Take a snapshot of the updated state
|
|
await expect(tabsContainer).toHaveScreenshot('tabs-switched-state.png');
|
|
});
|
|
}); |