Triple cleanup, landed together because they share the same cleanup branch intent and touch non-overlapping trees. 1. 38× tracked .playwright-mcp/*.yml stage-deleted MCP session recordings that had been inadvertently committed. .gitignore already covers .playwright-mcp/ (post-audit J2 block added ind12b901de). Working tree copies removed separately. 2. 19× disabled CI workflows moved to docs/archive/workflows/ Legacy .yml.disabled files in .github/workflows/ were 1676 LOC of dead config (backend-ci, cd, staging-validation, accessibility, chromatic, visual-regression, storybook-audit, contract-testing, zap-dast, container-scan, semgrep, sast, mutation-testing, rust-mutation, load-test-nightly, flaky-report, openapi-lint, commitlint, performance). Preserved in docs/archive/workflows/ for historical reference; `.github/workflows/` now only lists the 5 actually-running pipelines. 3. Orphan code removed (0 consumers confirmed via grep) - veza-backend-api/internal/repository/user_repository.go In-memory UserRepository mock, never imported anywhere. - proto/chat/chat.proto Chat server Rust deleted 2026-02-22 (commit279a10d31); proto file was orphan spec. Chat lives 100% in Go backend now. - veza-common/src/types/chat.rs (Conversation, Message, MessageType, Attachment, Reaction) - veza-common/src/types/websocket.rs (WebSocketMessage, PresenceStatus, CallType — depended on chat::MessageType) - veza-common/src/types/mod.rs updated: removed `pub mod chat;`, `pub mod websocket;`, and their re-exports. Only `veza_common::logging` is consumed by veza-stream-server (verified with `grep -r "veza_common::"`). `cargo check` on veza-common passes post-removal. Refs: AUDIT_REPORT.md §8.2 "Code mort / orphelin" + §9.1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
54 lines
2.1 KiB
TypeScript
54 lines
2.1 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Keyboard Navigation @a11y', () => {
|
|
test('login page: Tab navigates through all interactive elements', async ({ page }) => {
|
|
await page.goto('/login');
|
|
await page.waitForLoadState('networkidle').catch(() => {});
|
|
|
|
// First Tab should focus email input
|
|
await page.keyboard.press('Tab');
|
|
const focused1 = await page.evaluate(() => document.activeElement?.tagName + '.' + document.activeElement?.getAttribute('type'));
|
|
expect(focused1).toContain('INPUT');
|
|
|
|
// Tab through remaining fields
|
|
await page.keyboard.press('Tab'); // password
|
|
await page.keyboard.press('Tab'); // remember me or submit
|
|
await page.keyboard.press('Tab'); // submit or link
|
|
|
|
// Verify we can reach the submit button via keyboard
|
|
const submitReachable = await page.evaluate(() => {
|
|
const btn = document.querySelector('[data-testid="login-submit"]');
|
|
return btn !== null;
|
|
});
|
|
expect(submitReachable).toBeTruthy();
|
|
});
|
|
|
|
test('Escape closes dialogs', async ({ page }) => {
|
|
await page.goto('/login');
|
|
await page.waitForLoadState('networkidle').catch(() => {});
|
|
|
|
// Check for any open dialogs and verify Escape closes them
|
|
const dialog = page.locator('[role="dialog"]');
|
|
if (await dialog.isVisible({ timeout: 2000 }).catch(() => false)) {
|
|
await page.keyboard.press('Escape');
|
|
await expect(dialog).not.toBeVisible({ timeout: 3000 });
|
|
}
|
|
});
|
|
|
|
test('focus-visible styles are present on interactive elements', async ({ page }) => {
|
|
await page.goto('/login');
|
|
await page.waitForLoadState('networkidle').catch(() => {});
|
|
|
|
// Tab to first input to trigger focus-visible
|
|
await page.keyboard.press('Tab');
|
|
|
|
// Check that focus styles are applied (outline or ring)
|
|
const hasFocusStyle = await page.evaluate(() => {
|
|
const el = document.activeElement;
|
|
if (!el) return false;
|
|
const styles = window.getComputedStyle(el);
|
|
return styles.outlineStyle !== 'none' || styles.boxShadow !== 'none';
|
|
});
|
|
expect(hasFocusStyle).toBeTruthy();
|
|
});
|
|
});
|