snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
223
e2e-tests/playwright/docs/accessibility/interaction_testing.md
Normal file
223
e2e-tests/playwright/docs/accessibility/interaction_testing.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# Accessibility Interaction Testing Best Practices
|
||||
|
||||
## Overview
|
||||
|
||||
This guide covers **automated accessibility testing through user interaction simulation** using Playwright. These techniques test real user experiences with assistive technologies by automating keyboard navigation, focus management, and screen reader workflows.
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
### **Keyboard Navigation Testing**
|
||||
|
||||
- **Arrow Key Navigation** - Menu and list traversal
|
||||
- **Tab Navigation** - Focus order validation
|
||||
- **Enter/Space Activation** - Button and link interaction
|
||||
- **Escape Key Handling** - Modal and menu dismissal
|
||||
- **Letter Key Shortcuts** - Quick navigation patterns
|
||||
|
||||
### **Focus Management Validation**
|
||||
|
||||
- **Focus Visibility** - `:focus-visible` state verification
|
||||
- **Focus Trapping** - Modal and dialog containment
|
||||
- **Focus Restoration** - Return focus after interactions
|
||||
- **Focus Order** - Logical tab sequence validation
|
||||
|
||||
### **State and Behavior Testing**
|
||||
|
||||
- **ARIA State Changes** - `aria-expanded`, `aria-selected` verification
|
||||
- **Dynamic Content** - Live region announcements
|
||||
- **Error Handling** - Validation message accessibility
|
||||
- **Loading States** - Progress indication accessibility
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
### **1. Keyboard Navigation Testing**
|
||||
|
||||
```typescript
|
||||
test('keyboard navigation through menu', async ({page}) => {
|
||||
// Open menu with keyboard
|
||||
await page.getByRole('button', {name: 'Menu'}).focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// Navigate through menu items
|
||||
await expect(page.getByRole('menuitem').first()).toBeFocused();
|
||||
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem').nth(1)).toBeFocused();
|
||||
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem').nth(2)).toBeFocused();
|
||||
|
||||
// Test wrapping behavior
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(page.getByRole('menuitem').nth(1)).toBeFocused();
|
||||
|
||||
// Close menu with Escape
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(page.getByRole('menuitem')).toHaveCount(0);
|
||||
|
||||
// Verify focus restoration
|
||||
await expect(page.getByRole('button', {name: 'Menu'})).toBeFocused();
|
||||
});
|
||||
```
|
||||
|
||||
### **2. Focus Trapping in Modals**
|
||||
|
||||
```typescript
|
||||
test('modal focus trapping', async ({page}) => {
|
||||
// Open modal
|
||||
await page.getByRole('button', {name: 'Open modal'}).click();
|
||||
const modal = page.getByRole('dialog');
|
||||
|
||||
// Verify initial focus
|
||||
await expect(modal).toBeFocused();
|
||||
|
||||
// Find all focusable elements in modal
|
||||
const focusableElements = modal.locator('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
||||
const count = await focusableElements.count();
|
||||
|
||||
// Tab through all elements
|
||||
for (let i = 0; i < count; i++) {
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(focusableElements.nth(i)).toBeFocused();
|
||||
}
|
||||
|
||||
// Verify focus wraps back to first element
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(modal).toBeFocused(); // Or first focusable element
|
||||
|
||||
// Test reverse tabbing
|
||||
await page.keyboard.press('Shift+Tab');
|
||||
await expect(focusableElements.last()).toBeFocused();
|
||||
});
|
||||
```
|
||||
|
||||
### **3. ARIA State Management Testing**
|
||||
|
||||
```typescript
|
||||
test('expandable section ARIA states', async ({page}) => {
|
||||
const expandButton = page.getByRole('button', {name: 'Expand section', expanded: false});
|
||||
const expandableContent = page.getByRole('region', {name: 'Expandable content'});
|
||||
|
||||
// Test initial collapsed state
|
||||
await expect(expandButton).toHaveAttribute('aria-expanded', 'false');
|
||||
await expect(expandableContent).toBeHidden();
|
||||
|
||||
// Expand with keyboard
|
||||
await expandButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// Verify expanded state
|
||||
await expect(expandButton).toHaveAttribute('aria-expanded', 'true');
|
||||
await expect(expandableContent).toBeVisible();
|
||||
|
||||
// Verify focus moves to content
|
||||
await expect(expandableContent).toBeFocused();
|
||||
|
||||
// Collapse with keyboard
|
||||
await expandButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// Verify collapsed state
|
||||
await expect(expandButton).toHaveAttribute('aria-expanded', 'false');
|
||||
await expect(expandableContent).toBeHidden();
|
||||
});
|
||||
```
|
||||
|
||||
### **4. Form Validation Accessibility**
|
||||
|
||||
```typescript
|
||||
test('form validation accessibility', async ({page}) => {
|
||||
const form = page.getByRole('form');
|
||||
const requiredField = form.getByRole('textbox', {name: 'Email'});
|
||||
const submitButton = form.getByRole('button', {name: 'Submit'});
|
||||
|
||||
// Submit empty form to trigger validation
|
||||
await submitButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// Verify error state accessibility
|
||||
await expect(requiredField).toHaveAttribute('aria-invalid', 'true');
|
||||
await expect(page.getByRole('alert')).toBeVisible();
|
||||
|
||||
// Verify focus moves to invalid field
|
||||
await expect(requiredField).toBeFocused();
|
||||
|
||||
// Test error message association
|
||||
const errorId = await page.getByRole('alert').getAttribute('id');
|
||||
await expect(requiredField).toHaveAttribute('aria-describedby', errorId);
|
||||
|
||||
// Fix validation error
|
||||
await requiredField.fill('valid@example.com');
|
||||
await submitButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// Verify error cleared
|
||||
await expect(requiredField).toHaveAttribute('aria-invalid', 'false');
|
||||
await expect(page.getByRole('alert')).toBeHidden();
|
||||
});
|
||||
```
|
||||
|
||||
### **5. Live Region Announcements**
|
||||
|
||||
```typescript
|
||||
test('dynamic content announcements', async ({page}) => {
|
||||
const liveRegion = page.getByRole('status');
|
||||
const triggerButton = page.getByRole('button', {name: 'Load content'});
|
||||
|
||||
// Verify live region setup
|
||||
await expect(liveRegion).toHaveAttribute('aria-live', 'polite');
|
||||
|
||||
// Trigger content update
|
||||
await triggerButton.click();
|
||||
|
||||
// Wait for content to appear in live region
|
||||
await expect(liveRegion).toContainText('Loading complete');
|
||||
|
||||
// Test urgent announcements
|
||||
const urgentRegion = page.getByRole('alert');
|
||||
await page.getByRole('button', {name: 'Urgent action'}).click();
|
||||
await expect(urgentRegion).toContainText('Critical update');
|
||||
});
|
||||
```
|
||||
|
||||
### **6. Focus Visibility Testing**
|
||||
|
||||
```typescript
|
||||
test('focus indicators visibility', async ({page}) => {
|
||||
const button = page.getByRole('button', {name: 'Save'});
|
||||
|
||||
// Tab to button (should show focus indicator)
|
||||
await page.keyboard.press('Tab');
|
||||
await toBeFocusedWithFocusVisible(button);
|
||||
});
|
||||
```
|
||||
|
||||
### **7. Complex Navigation Patterns**
|
||||
|
||||
```typescript
|
||||
test('hierarchical menu navigation', async ({page}) => {
|
||||
await page.getByRole('button', {name: 'Menu'}).focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// Navigate to submenu trigger
|
||||
const submenuTrigger = page.getByRole('menuitem', {name: /has submenu/});
|
||||
await submenuTrigger.focus();
|
||||
|
||||
// Open submenu with right arrow
|
||||
await page.keyboard.press('ArrowRight');
|
||||
|
||||
// Verify submenu opened and focused
|
||||
const submenu = page.getByRole('menu').nth(1);
|
||||
await expect(submenu).toBeVisible();
|
||||
await expect(submenu.getByRole('menuitem').first()).toBeFocused();
|
||||
|
||||
// Navigate in submenu
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(submenu.getByRole('menuitem').nth(1)).toBeFocused();
|
||||
|
||||
// Return to parent menu with left arrow
|
||||
await page.keyboard.press('ArrowLeft');
|
||||
await expect(submenu).toBeHidden();
|
||||
await expect(submenuTrigger).toBeFocused();
|
||||
});
|
||||
```
|
||||
Reference in New Issue
Block a user