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:
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {Page} from '@playwright/test';
|
||||
import type {UserProfile} from '@mattermost/types/users';
|
||||
|
||||
import type {ChannelsPage} from '@mattermost/playwright-lib';
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
test('MM-63451 should be able to navigate the account settings menu with the keyboard after opening it with the mouse', async ({
|
||||
pw,
|
||||
}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on the account menu button
|
||||
await channelsPage.globalHeader.accountMenuButton.click();
|
||||
|
||||
await testMenuWithKeyboard(user, page, channelsPage);
|
||||
});
|
||||
|
||||
test('MM-63451 should be able to navigate the account settings menu with the keyboard after opening it with the keyboard', async ({
|
||||
pw,
|
||||
}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Focus the account menu button
|
||||
await channelsPage.globalHeader.accountMenuButton.focus();
|
||||
await expect(channelsPage.globalHeader.accountMenuButton).toBeFocused();
|
||||
await page.keyboard.press('Space');
|
||||
|
||||
await testMenuWithKeyboard(user, page, channelsPage);
|
||||
});
|
||||
|
||||
async function testMenuWithKeyboard(user: UserProfile, page: Page, channelsPage: ChannelsPage) {
|
||||
// * Should start focused on the first menu item
|
||||
await expect(page.getByRole('menuitem', {name: '@' + user.username})).toBeFocused();
|
||||
|
||||
// * Should be able to scroll down through the menu with the keyboard
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: 'Set custom status'})).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: 'Online'})).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: 'Away'})).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: 'Do not disturb Disables all notifications'})).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: 'Offline'})).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: 'Profile'})).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: 'Log Out'})).toBeFocused();
|
||||
|
||||
// * Should be able to scroll back up through the menu with the keyboard
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(page.getByRole('menuitem', {name: 'Profile'})).toBeFocused();
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(page.getByRole('menuitem', {name: 'Offline'})).toBeFocused();
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(page.getByRole('menuitem', {name: 'Do not disturb Disables all notifications'})).toBeFocused();
|
||||
|
||||
// * Should be able to move into the submenu by pressing the right arrow
|
||||
await page.keyboard.press('ArrowRight');
|
||||
await expect(page.getByRole('menuitem', {name: "Don't clear"})).toBeFocused();
|
||||
|
||||
// * Should be able to scroll through the submenu with the keyboard
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: '30 mins'})).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: '1 hour'})).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: '2 hours'})).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: 'Tomorrow'})).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: 'Choose date and time'})).toBeFocused();
|
||||
|
||||
// * Should wrap around when you reach the end
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('menuitem', {name: "Don't clear"})).toBeFocused();
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(page.getByRole('menuitem', {name: 'Choose date and time'})).toBeFocused();
|
||||
|
||||
// * Should be able to close the submenu by pressing the left arrow
|
||||
await page.keyboard.press('ArrowLeft');
|
||||
await expect(page.getByRole('menuitem', {name: 'Do not disturb Disables all notifications'})).toBeFocused();
|
||||
|
||||
// * Should be able to close the menu by pressing escape
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(page.getByRole('menuitem')).toHaveCount(0);
|
||||
|
||||
// * Should be focused back on the menu button
|
||||
await expect(channelsPage.globalHeader.accountMenuButton).toBeFocused();
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
test('Settings sidebar should be keyboard accessible', async ({axe, pw}) => {
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in as a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Initialize Axe
|
||||
const ab = axe.builder(page).disableRules([
|
||||
'color-contrast',
|
||||
|
||||
// Known issue: These fail due to the way we've grouped plugin setting tabs together in the LHS
|
||||
'aria-required-children',
|
||||
'aria-required-parent',
|
||||
]);
|
||||
|
||||
// # Visit default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Open settings modal
|
||||
const settingsModal = await channelsPage.globalHeader.openSettings();
|
||||
|
||||
// * The settings modal should have no accessibility violations
|
||||
const accessibilityScanResults = await ab.analyze();
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
|
||||
// # Focus the sidebar
|
||||
await settingsModal.container.focus();
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
// * The Notifications tab should start focused
|
||||
await expect(page.getByRole('tab', {name: 'Notifications'})).toBeFocused();
|
||||
await expect(page.getByText('Desktop and mobile notifications')).toBeVisible();
|
||||
|
||||
// * Pressing the down arrow should focus and show the Display tab
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('tab', {name: 'Display'})).toBeFocused();
|
||||
await expect(page.getByText('Theme', {exact: true})).toBeVisible();
|
||||
|
||||
// * Pressing the down arrow should focus and show the Sidebar tab
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('tab', {name: 'Sidebar'})).toBeFocused();
|
||||
await expect(page.getByText('Group unread channels separately')).toBeVisible();
|
||||
|
||||
// * Pressing the down arrow should focus and show the Advanced tab
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(page.getByRole('tab', {name: 'Advanced'})).toBeFocused();
|
||||
await expect(page.getByText('Enable Post Formatting')).toBeVisible();
|
||||
|
||||
// * Pressing the up arrow should go back through the tabs
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(page.getByRole('tab', {name: 'Sidebar'})).toBeFocused();
|
||||
await expect(page.getByText('Group unread channels separately')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(page.getByRole('tab', {name: 'Display'})).toBeFocused();
|
||||
await expect(page.getByText('Theme', {exact: true})).toBeVisible();
|
||||
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await expect(page.getByRole('tab', {name: 'Notifications'})).toBeFocused();
|
||||
await expect(page.getByText('Desktop and mobile notifications')).toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,181 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify accessibility support in Add people to Channel Dialog screen
|
||||
*/
|
||||
test(
|
||||
'MM-T1468 Accessibility Support in Add people to Channel Dialog screen',
|
||||
{tag: ['@accessibility', '@add_people_channel']},
|
||||
async ({pw}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, adminUser, adminClient} = await pw.initSetup();
|
||||
|
||||
// # Turn off email invitations, which replace the no-results message asserted below with an
|
||||
// # invite-to-team prompt
|
||||
await adminClient.patchConfig({ServiceSettings: {EnableEmailInvitations: false}});
|
||||
|
||||
// # Create a channel in the team
|
||||
const channel = await adminClient.createChannel(
|
||||
pw.random.channel({
|
||||
teamId: team.id,
|
||||
displayName: 'Test Channel',
|
||||
name: 'test-channel',
|
||||
}),
|
||||
);
|
||||
|
||||
// # Create additional users and add to team
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const newUser = await adminClient.createUser(await pw.random.user(), '', '');
|
||||
await adminClient.addToTeam(team.id, newUser.id);
|
||||
}
|
||||
|
||||
// # Log in as admin
|
||||
const {page, channelsPage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit the test channel
|
||||
await channelsPage.goto(team.name, channel.name);
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Open channel menu and click Members
|
||||
await channelsPage.centerView.header.openChannelMenu();
|
||||
const membersMenuItem = page.locator('#channelMembers');
|
||||
await membersMenuItem.click();
|
||||
|
||||
// # Click the Add people button
|
||||
const membersRhs = page.locator('#rhsContainer');
|
||||
await expect(membersRhs).toBeVisible();
|
||||
const addMembers = membersRhs.locator('button.add-members');
|
||||
await expect(addMembers).toBeEnabled();
|
||||
await addMembers.click();
|
||||
|
||||
// * Verify the Add people dialog is visible
|
||||
const dialog = page.getByRole('dialog').first();
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
// * Verify the heading with channel name
|
||||
const modalName = `Add people to ${channel.display_name}`;
|
||||
await expect(dialog.getByRole('heading', {name: modalName})).toBeVisible();
|
||||
await pw.wait(pw.duration.one_sec);
|
||||
|
||||
// * Verify the search input has proper accessibility attributes
|
||||
const searchInput = dialog.getByLabel('Search for people or groups');
|
||||
await expect(searchInput).toBeVisible();
|
||||
await expect(searchInput).toHaveAttribute('aria-autocomplete', 'list');
|
||||
|
||||
// # Search for a text and navigate with arrow keys
|
||||
await pw.wait(pw.duration.half_sec);
|
||||
await searchInput.fill('u');
|
||||
await pw.wait(pw.duration.half_sec);
|
||||
|
||||
// # Navigate down through the list
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowUp');
|
||||
|
||||
// * Verify the selected row has the correct class
|
||||
const selectedRow = dialog.locator('#multiSelectList').locator('.more-modal__row--selected');
|
||||
await expect(selectedRow).toBeVisible();
|
||||
|
||||
// * Verify image alt is displayed for user profile
|
||||
const avatar = selectedRow.locator('img.Avatar');
|
||||
await expect(avatar).toHaveAttribute('alt', 'user profile image');
|
||||
|
||||
// * Verify screen reader live region exists and has proper attributes
|
||||
const srOnlyRegion = dialog.locator('.filtered-user-list div.sr-only:not([role="status"])');
|
||||
await expect(srOnlyRegion).toHaveAttribute('aria-live', 'polite');
|
||||
await expect(srOnlyRegion).toHaveAttribute('aria-atomic', 'true');
|
||||
|
||||
// # Search for an invalid text
|
||||
await searchInput.fill('somethingwhichdoesnotexist');
|
||||
|
||||
// * Check if the no results message is displayed with proper accessibility
|
||||
const noResultsMessage = dialog.getByText(/No matches found|No results found matching/i);
|
||||
await expect(noResultsMessage).toBeVisible();
|
||||
const noResultsWrapper = dialog.locator('.multi-select__wrapper');
|
||||
await expect(noResultsWrapper).toHaveAttribute('aria-live', 'polite');
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Add people to Channel dialog passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Add people to Channel dialog',
|
||||
{tag: ['@accessibility', '@add_people_channel', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, adminUser, adminClient} = await pw.initSetup();
|
||||
|
||||
// # Create a channel in the team
|
||||
const channel = await adminClient.createChannel(
|
||||
pw.random.channel({
|
||||
teamId: team.id,
|
||||
displayName: 'Test Channel',
|
||||
name: 'test-channel',
|
||||
}),
|
||||
);
|
||||
|
||||
// # Create additional users and add to team
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const newUser = await adminClient.createUser(await pw.random.user(), '', '');
|
||||
await adminClient.addToTeam(team.id, newUser.id);
|
||||
}
|
||||
|
||||
// # Log in as admin
|
||||
const {page, channelsPage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit the test channel
|
||||
await channelsPage.goto(team.name, channel.name);
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Open channel menu and click Members
|
||||
await channelsPage.centerView.header.openChannelMenu();
|
||||
const membersMenuItem = page.locator('#channelMembers');
|
||||
await membersMenuItem.click();
|
||||
|
||||
// # Click the Add people button
|
||||
const membersRhs = page.locator('#rhsContainer');
|
||||
await expect(membersRhs).toBeVisible();
|
||||
const addMembers = membersRhs.locator('button.add-members');
|
||||
await expect(addMembers).toBeEnabled();
|
||||
await addMembers.click();
|
||||
|
||||
// * Verify the Add people dialog is visible
|
||||
const dialog = page.getByRole('dialog').first();
|
||||
await expect(dialog).toBeVisible();
|
||||
await pw.wait(pw.duration.one_sec);
|
||||
|
||||
// * Verify aria snapshot of Add people to Channel dialog
|
||||
await expect(dialog).toMatchAriaSnapshot(`
|
||||
- dialog "Add people to Test Channel":
|
||||
- document:
|
||||
- heading "Add people to Test Channel" [level=1]
|
||||
- button "Close"
|
||||
- log
|
||||
- text: Search for people or groups
|
||||
- combobox "Search for people or groups"
|
||||
- button "Cancel"
|
||||
- button "Add"
|
||||
`);
|
||||
|
||||
// * Analyze the Add people dialog for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.include('[role="dialog"]')
|
||||
.analyze();
|
||||
|
||||
// * Should have no violations
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify accessibility support in Browse Channels Dialog screen
|
||||
*/
|
||||
test(
|
||||
'MM-T1467 Accessibility Support in Browse Channels Dialog screen',
|
||||
{tag: ['@accessibility', '@browse_channels']},
|
||||
async ({pw}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, user, adminClient} = await pw.initSetup();
|
||||
|
||||
// # Create two channels with purposes for testing aria-label
|
||||
const channel1 = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: 'accessibility-' + Date.now(),
|
||||
display_name: 'Accessibility',
|
||||
type: 'O',
|
||||
purpose: 'some purpose',
|
||||
});
|
||||
|
||||
const channel2 = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: 'z-accessibility-' + Date.now(),
|
||||
display_name: 'Z Accessibility',
|
||||
type: 'O',
|
||||
purpose: 'other purpose',
|
||||
});
|
||||
|
||||
// # Log in as regular user
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit town-square channel
|
||||
await channelsPage.goto(team.name, 'town-square');
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Open the Browse Channels modal
|
||||
const dialog = await channelsPage.openBrowseChannelsModal();
|
||||
|
||||
// * Verify the search input exists
|
||||
const searchInput = dialog.searchInput;
|
||||
await expect(searchInput).toBeVisible();
|
||||
|
||||
// # Wait for channel list to load
|
||||
await dialog.toBeDoneLoading();
|
||||
|
||||
// # Hide already joined channels
|
||||
const hideJoinedCheckbox = dialog.hideJoinedCheckbox;
|
||||
await hideJoinedCheckbox.click();
|
||||
|
||||
// # Focus on Create Channel button and tab through elements
|
||||
// Tab order: Close → Search → Channel type filter → Hide Archived → Hide Joined → first channel
|
||||
const createChannelButton = dialog.createNewChannelButton;
|
||||
await createChannelButton.focus();
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
// * Verify channel name is highlighted and has proper aria-label
|
||||
await dialog.toHaveChannelAsNthResult(channel1.name, 0);
|
||||
const channel1AriaLabel = `${channel1.display_name.toLowerCase()}, ${channel1.purpose.toLowerCase()}`;
|
||||
const channel1Item = dialog.container.getByLabel(channel1AriaLabel);
|
||||
await expect(channel1Item).toBeVisible();
|
||||
await expect(channel1Item).toBeFocused();
|
||||
|
||||
// # Press Tab again to move to next channel
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
// * Verify focus moved to next channel
|
||||
await dialog.toHaveChannelAsNthResult(channel2.name, 1);
|
||||
const channel2AriaLabel = `${channel2.display_name.toLowerCase()}, ${channel2.purpose.toLowerCase()}`;
|
||||
const channel2Item = dialog.container.getByLabel(channel2AriaLabel);
|
||||
await expect(channel2Item).toBeFocused();
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Browse Channels dialog passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Browse Channels dialog',
|
||||
{tag: ['@accessibility', '@browse_channels', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, user} = await pw.initSetup();
|
||||
|
||||
// # Log in as regular user
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit town-square channel
|
||||
await channelsPage.goto(team.name, 'town-square');
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Open the Browse Channels modal
|
||||
const dialog = await channelsPage.openBrowseChannelsModal();
|
||||
|
||||
// * Verify aria snapshot of Browse Channels dialog
|
||||
await expect(dialog.container).toMatchAriaSnapshot(`
|
||||
- dialog "Browse Channels":
|
||||
- document:
|
||||
- heading "Browse Channels" [level=1]
|
||||
- button "Create New Channel"
|
||||
- button "Close"
|
||||
- textbox "Search Channels"
|
||||
- /text: \\d+ Results/
|
||||
- /status: \\d+ Results/
|
||||
- status: Channel type filter set to All
|
||||
- button "Channel type filter"
|
||||
- checkbox "Hide archived channels" [checked]: Hide Archived
|
||||
- checkbox "Hide joined channels": Hide Joined
|
||||
- search
|
||||
`);
|
||||
|
||||
// * Analyze the Browse Channels dialog for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.include('[role="dialog"]')
|
||||
.analyze();
|
||||
|
||||
// * Should have no violations
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify accessibility support in Direct Messages Dialog screen
|
||||
*/
|
||||
test(
|
||||
'MM-T1466 Accessibility Support in Direct Messages Dialog screen',
|
||||
{tag: ['@accessibility', '@direct_messages']},
|
||||
async ({pw}) => {
|
||||
// # Skip test if no license for Guest Accounts
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup with admin and another user
|
||||
const {team, adminUser, adminClient} = await pw.initSetup();
|
||||
|
||||
// # Create additional user and add to team
|
||||
const user2 = await adminClient.createUser(await pw.random.user(), '', '');
|
||||
await adminClient.addToTeam(team.id, user2.id);
|
||||
|
||||
// # Log in as admin
|
||||
const {page, channelsPage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit town-square channel
|
||||
await channelsPage.goto(team.name, 'town-square');
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on the Write a direct message button to open the Direct Messages dialog
|
||||
const writeDirectMessageButton = page.getByRole('button', {name: 'Write a direct message'});
|
||||
await writeDirectMessageButton.click();
|
||||
|
||||
// * Verify the Direct Messages dialog is visible
|
||||
const dialog = page.getByRole('dialog', {name: 'Direct Messages'});
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
// * Verify the heading
|
||||
await expect(dialog.getByRole('heading', {name: 'Direct Messages'})).toBeVisible();
|
||||
|
||||
// * Verify the search input has proper accessibility attributes
|
||||
const searchInput = dialog.getByLabel('Search for people');
|
||||
await expect(searchInput).toBeVisible();
|
||||
await expect(searchInput).toHaveAttribute('aria-autocomplete', 'list');
|
||||
|
||||
// # Search for a text and navigate with arrow keys
|
||||
await searchInput.fill('s');
|
||||
await pw.wait(pw.duration.half_sec);
|
||||
|
||||
// # Navigate down through the list
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowUp');
|
||||
|
||||
// * Verify the selected row has the correct class
|
||||
const selectedRow = dialog.locator('#multiSelectList').locator('.more-modal__row--selected');
|
||||
await expect(selectedRow).toBeVisible();
|
||||
|
||||
// * Verify image alt is displayed for user profile
|
||||
const avatar = selectedRow.locator('img.Avatar');
|
||||
await expect(avatar).toHaveAttribute('alt', 'user profile image');
|
||||
|
||||
// * Verify screen reader live region exists and has proper attributes
|
||||
const srOnlyRegion = dialog.locator('.filtered-user-list div.sr-only:not([role="status"])');
|
||||
await expect(srOnlyRegion).toHaveAttribute('aria-live', 'polite');
|
||||
await expect(srOnlyRegion).toHaveAttribute('aria-atomic', 'true');
|
||||
|
||||
// # Search for an invalid text
|
||||
const invalidSearchTerm = 'somethingwhichdoesnotexist';
|
||||
await searchInput.clear();
|
||||
await searchInput.fill(invalidSearchTerm);
|
||||
await pw.wait(pw.duration.half_sec);
|
||||
|
||||
// * Check if the no results message is displayed with proper accessibility
|
||||
const noResultsWrapper = dialog.locator('.multi-select__wrapper');
|
||||
await expect(noResultsWrapper).toHaveAttribute('aria-live', 'polite');
|
||||
await expect(noResultsWrapper).toContainText(`No results found matching ${invalidSearchTerm}`);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Direct Messages dialog passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Direct Messages dialog',
|
||||
{tag: ['@accessibility', '@direct_messages', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, user} = await pw.initSetup();
|
||||
|
||||
// # Log in as admin
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit town-square channel
|
||||
await channelsPage.goto(team.name, 'town-square');
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on the Write a direct message button to open the Direct Messages dialog
|
||||
const writeDirectMessageButton = page.getByRole('button', {name: 'Write a direct message'});
|
||||
await writeDirectMessageButton.click();
|
||||
|
||||
// * Verify the Direct Messages dialog is visible
|
||||
const dialog = page.getByRole('dialog', {name: 'Direct Messages'});
|
||||
await expect(dialog).toBeVisible();
|
||||
await pw.wait(pw.duration.one_sec);
|
||||
|
||||
// * Verify aria snapshot of Direct Messages dialog (key structural elements only)
|
||||
await expect(dialog).toMatchAriaSnapshot(`
|
||||
- dialog "Direct Messages":
|
||||
- document:
|
||||
- heading "Direct Messages" [level=1]
|
||||
- button "Close"
|
||||
- application:
|
||||
- log
|
||||
- text: Search for people
|
||||
- combobox "Search for people"
|
||||
- button "Go"
|
||||
`);
|
||||
|
||||
// * Analyze the Direct Messages dialog for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page)
|
||||
.include('[role="dialog"]')
|
||||
// TODO: Address scrollable-region-focusable violation in the Direct Messages dialog
|
||||
// The multiSelectList and sr-only status elements need to be keyboard accessible
|
||||
.disableRules(['color-contrast', 'scrollable-region-focusable'])
|
||||
.analyze();
|
||||
|
||||
// * Should have no violations
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,171 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
test.fixme('Base channel accessibility', async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
await channelsPage.postMessage('hello');
|
||||
|
||||
// # Analyze the page
|
||||
// Disable 'color-contrast' to be addressed by MM-53814
|
||||
const accessibilityScanResults = await axe.builder(page, {disableColorContrast: true}).analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('Post actions tab support', async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user, adminClient} = await pw.initSetup();
|
||||
const config = await adminClient.getConfig();
|
||||
const license = await adminClient.getClientLicenseOld();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
await channelsPage.postMessage('hello');
|
||||
|
||||
const post = await channelsPage.getLastPost();
|
||||
await post.hover();
|
||||
await post.postMenu.toBeVisible();
|
||||
|
||||
// # Open the dot menu
|
||||
await post.postMenu.dotMenuButton.press('Enter');
|
||||
|
||||
// * Dot menu should be visible and have focused
|
||||
await channelsPage.postDotMenu.toBeVisible();
|
||||
await expect(channelsPage.postDotMenu.replyMenuItem).toBeFocused();
|
||||
|
||||
// # Analyze the page
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.include('.MuiList-root.MuiList-padding')
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
|
||||
// * Should move focus to Forward after arrow down
|
||||
await channelsPage.postDotMenu.replyMenuItem.press('ArrowDown');
|
||||
await expect(channelsPage.postDotMenu.forwardMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to Follow message after arrow down
|
||||
await channelsPage.postDotMenu.forwardMenuItem.press('ArrowDown');
|
||||
await expect(channelsPage.postDotMenu.followMessageMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to Mark as Unread after arrow down
|
||||
await channelsPage.postDotMenu.followMessageMenuItem.press('ArrowDown');
|
||||
await expect(channelsPage.postDotMenu.markAsUnreadMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to Save after arrow down
|
||||
await channelsPage.postDotMenu.markAsUnreadMenuItem.press('ArrowDown');
|
||||
await expect(channelsPage.postDotMenu.saveMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to Remind after arrow down
|
||||
await channelsPage.postDotMenu.saveMenuItem.press('ArrowDown');
|
||||
await expect(channelsPage.postDotMenu.remindMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to Pin to Channel after arrow down
|
||||
await channelsPage.postDotMenu.remindMenuItem.press('ArrowDown');
|
||||
await expect(channelsPage.postDotMenu.pinToChannelMenuItem).toBeFocused();
|
||||
|
||||
if (config.FeatureFlags.MoveThreadsEnabled && license.IsLicensed === 'true') {
|
||||
// * Should move focus to Move Thread after arrow down
|
||||
await channelsPage.postDotMenu.pinToChannelMenuItem.press('ArrowDown');
|
||||
await expect(channelsPage.postDotMenu.moveThreadMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to Copy Text after arrow down
|
||||
await channelsPage.postDotMenu.moveThreadMenuItem.press('ArrowDown');
|
||||
await expect(channelsPage.postDotMenu.copyTextMenuItem).toBeFocused();
|
||||
} else {
|
||||
// * Should move focus to Copy Text after arrow down
|
||||
await channelsPage.postDotMenu.pinToChannelMenuItem.press('ArrowDown');
|
||||
await expect(channelsPage.postDotMenu.copyTextMenuItem).toBeFocused();
|
||||
}
|
||||
|
||||
// * Should move focus to Copy Link after arrow down
|
||||
await channelsPage.postDotMenu.copyTextMenuItem.press('ArrowDown');
|
||||
await expect(channelsPage.postDotMenu.copyLinkMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to Edit after arrow down
|
||||
await channelsPage.postDotMenu.copyLinkMenuItem.press('ArrowDown');
|
||||
await expect(channelsPage.postDotMenu.editMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to Delete after arrow down
|
||||
// "Quarantine for Review" is inserted between Edit and Delete when content flagging is on.
|
||||
await channelsPage.postDotMenu.editMenuItem.press('ArrowDown');
|
||||
if (config.ContentFlaggingSettings?.EnableContentFlagging) {
|
||||
const quarantineMenuItem = page
|
||||
.getByRole('menu', {name: 'Post extra options'})
|
||||
.getByRole('menuitem')
|
||||
.filter({hasText: 'Quarantine for Review'});
|
||||
await expect(quarantineMenuItem).toBeFocused();
|
||||
await page.keyboard.press('ArrowDown');
|
||||
}
|
||||
await expect(channelsPage.postDotMenu.deleteMenuItem).toBeFocused();
|
||||
|
||||
// * Then, should move focus back to Reply after arrow down
|
||||
await channelsPage.postDotMenu.deleteMenuItem.press('ArrowDown');
|
||||
await expect(channelsPage.postDotMenu.replyMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to Delete after arrow uo
|
||||
await channelsPage.postDotMenu.container.press('ArrowUp');
|
||||
expect(await channelsPage.postDotMenu.deleteMenuItem).toBeFocused();
|
||||
|
||||
// # Set focus to Remind
|
||||
await channelsPage.postDotMenu.remindMenuItem.focus();
|
||||
await expect(channelsPage.postDotMenu.remindMenuItem).toBeFocused();
|
||||
|
||||
// * Reminder menu should still be hidden
|
||||
await expect(channelsPage.postReminderMenu.container).toBeHidden();
|
||||
|
||||
// # Press arrow right
|
||||
await channelsPage.postDotMenu.remindMenuItem.press('ArrowRight');
|
||||
|
||||
// * Reminder menu should be visible
|
||||
expect(channelsPage.postReminderMenu.container).toBeVisible();
|
||||
|
||||
// * Should have focus on 30 mins after submenu opens
|
||||
expect(await channelsPage.postReminderMenu.thirtyMinsMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to 1 hour after arrow down
|
||||
await channelsPage.postReminderMenu.thirtyMinsMenuItem.press('ArrowDown');
|
||||
expect(await channelsPage.postReminderMenu.oneHourMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to 2 hours after arrow down
|
||||
await channelsPage.postReminderMenu.oneHourMenuItem.press('ArrowDown');
|
||||
expect(await channelsPage.postReminderMenu.twoHoursMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to Tomorrow after arrow down
|
||||
await channelsPage.postReminderMenu.twoHoursMenuItem.press('ArrowDown');
|
||||
expect(await channelsPage.postReminderMenu.tomorrowMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to Custom after arrow down
|
||||
await channelsPage.postReminderMenu.tomorrowMenuItem.press('ArrowDown');
|
||||
expect(await channelsPage.postReminderMenu.customMenuItem).toBeFocused();
|
||||
|
||||
// * Then, should move focus back to 30 mins after arrow down
|
||||
await channelsPage.postReminderMenu.customMenuItem.press('ArrowDown');
|
||||
expect(await channelsPage.postReminderMenu.thirtyMinsMenuItem).toBeFocused();
|
||||
|
||||
// * Should hide Reminder menu and focus to Remind menu after arrow left
|
||||
await channelsPage.postReminderMenu.container.press('ArrowLeft');
|
||||
await expect(channelsPage.postReminderMenu.container).toBeHidden();
|
||||
await expect(channelsPage.postDotMenu.remindMenuItem).toBeFocused();
|
||||
|
||||
// * Should hide Dot menu of Escape
|
||||
await channelsPage.postDotMenu.container.press('Escape');
|
||||
await expect(channelsPage.postDotMenu.container).toBeHidden();
|
||||
});
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify accessibility support in Invite People Flow
|
||||
*/
|
||||
test(
|
||||
'MM-T1515 Verify Accessibility Support in Invite People Flow',
|
||||
{tag: ['@accessibility', '@invite_people']},
|
||||
async ({pw}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, adminUser} = await pw.initSetup();
|
||||
|
||||
// # Log in as admin
|
||||
const {page, channelsPage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit town-square channel
|
||||
await channelsPage.goto(team.name, 'town-square');
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Open team menu and click Invite people
|
||||
await channelsPage.sidebarLeft.teamMenuButton.click();
|
||||
await channelsPage.teamMenu.toBeVisible();
|
||||
|
||||
const invitePeopleMenuItem = page.locator("#sidebarTeamMenu li:has-text('Invite people')");
|
||||
await invitePeopleMenuItem.click();
|
||||
|
||||
// * Verify the Invite People modal has proper accessibility attributes
|
||||
const inviteModal = page.getByTestId('invitationModal');
|
||||
await expect(inviteModal).toBeVisible();
|
||||
await expect(inviteModal).toHaveAttribute('aria-modal', 'true');
|
||||
await expect(inviteModal).toHaveAttribute('aria-labelledby', 'invitation_modal_title');
|
||||
await expect(inviteModal).toHaveAttribute('role', 'dialog');
|
||||
|
||||
// * Verify the modal title is visible and contains correct text
|
||||
const modalTitle = page.locator('#invitation_modal_title');
|
||||
await expect(modalTitle).toBeVisible();
|
||||
await expect(modalTitle).toContainText('Invite people to');
|
||||
|
||||
// # Get the close button and verify accessibility
|
||||
const closeButton = inviteModal.locator('button.icon-close');
|
||||
await expect(closeButton).toHaveAttribute('aria-label', 'Close');
|
||||
|
||||
// # Focus on close button and verify tab navigation works
|
||||
await closeButton.focus();
|
||||
await page.keyboard.press('Shift+Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
// * Verify focus returns to close button
|
||||
await expect(closeButton).toBeFocused();
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Invite People dialog passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Invite People dialog',
|
||||
{tag: ['@accessibility', '@invite_people', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, user, adminClient} = await pw.initSetup();
|
||||
|
||||
// # Turn off email invitations, which change the invite input's placeholder from
|
||||
// # "Add members" to "Enter a name or email address" in the snapshot below
|
||||
await adminClient.patchConfig({ServiceSettings: {EnableEmailInvitations: false}});
|
||||
|
||||
// # Log in as user
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit town-square channel
|
||||
await channelsPage.goto(team.name, 'town-square');
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Open team menu and click Invite people
|
||||
await channelsPage.sidebarLeft.teamMenuButton.click();
|
||||
await channelsPage.teamMenu.toBeVisible();
|
||||
|
||||
const invitePeopleMenuItem = page.locator("#sidebarTeamMenu li:has-text('Invite people')");
|
||||
await invitePeopleMenuItem.click();
|
||||
|
||||
// * Verify the Invite People modal is visible
|
||||
const inviteModal = page.getByTestId('invitationModal');
|
||||
await expect(inviteModal).toBeVisible();
|
||||
await pw.wait(pw.duration.one_sec);
|
||||
|
||||
// * Verify aria snapshot of Invite People dialog (key structural elements only)
|
||||
await expect(inviteModal).toMatchAriaSnapshot(`
|
||||
- dialog:
|
||||
- document:
|
||||
- heading [level=1]
|
||||
- button
|
||||
- text: "To:"
|
||||
- log
|
||||
- text: Add members
|
||||
- combobox "Invite People"
|
||||
- listbox
|
||||
- button
|
||||
- button "Invite" [disabled]
|
||||
`);
|
||||
|
||||
// * Analyze the Invite People dialog for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.include('[data-testid="invitationModal"]')
|
||||
.analyze();
|
||||
|
||||
// * Should have no violations
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,437 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify keyboard navigation through interactive elements in Advanced settings
|
||||
*/
|
||||
test(
|
||||
'navigate on keyboard tab between interactive elements',
|
||||
{tag: ['@accessibility', '@settings', '@advanced_settings']},
|
||||
async ({pw}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user, adminClient} = await pw.initSetup();
|
||||
const config = await adminClient.getConfig();
|
||||
const wysiwygEnabled =
|
||||
config.FeatureFlags?.WysiwygEditor === true || config.FeatureFlags?.WysiwygEditor === 'true';
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
const advancedSettings = settingsModal.advancedSettings;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible and focus should be on the modal
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.container);
|
||||
|
||||
// # Press Tab and verify focus is on Close button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.closeButton);
|
||||
|
||||
// # Click on Advanced tab to open it
|
||||
await settingsModal.advancedTab.click();
|
||||
|
||||
// * Advanced Settings panel should be visible
|
||||
await expect(advancedSettings.container).toBeVisible();
|
||||
|
||||
// # Press Tab to move focus to Send Messages on CTRL+ENTER button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(advancedSettings.ctrlEnterEditButton);
|
||||
|
||||
if (wysiwygEnabled) {
|
||||
// # Press Tab to move focus to Rich text editing button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(advancedSettings.wysiwygEditorEditButton);
|
||||
}
|
||||
|
||||
// # Press Tab to move focus to Enable Post Formatting button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(advancedSettings.postFormattingEditButton);
|
||||
|
||||
// # Press Tab to move focus to Enable Join/Leave Messages button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(advancedSettings.joinLeaveEditButton);
|
||||
|
||||
// # Press Tab to move focus to Scroll position button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(advancedSettings.scrollPositionEditButton);
|
||||
|
||||
// # Press Tab to move focus to Allow message drafts to sync button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(advancedSettings.syncDraftsEditButton);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Advanced settings panel passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Advanced settings panel',
|
||||
{tag: ['@accessibility', '@settings', '@advanced_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user, adminClient} = await pw.initSetup();
|
||||
|
||||
const config = await adminClient.getConfig();
|
||||
const wysiwygEnabled =
|
||||
config.FeatureFlags?.WysiwygEditor === true || config.FeatureFlags?.WysiwygEditor === 'true';
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Open Advanced tab
|
||||
await settingsModal.advancedTab.click();
|
||||
|
||||
// * Advanced Settings panel should be visible
|
||||
await expect(settingsModal.advancedSettings.container).toBeVisible();
|
||||
|
||||
const wysiwygSnapshot = wysiwygEnabled
|
||||
? `
|
||||
- heading "Rich text editing (Beta)" [level=4]
|
||||
- button "Rich text editing (Beta) Edit"
|
||||
- text: /(Markdown editing [(]default[)]|Rich text editing.*)/`
|
||||
: '';
|
||||
|
||||
// * Verify aria snapshot of Advanced settings tab
|
||||
await expect(settingsModal.advancedSettings.container).toMatchAriaSnapshot(`
|
||||
- tabpanel "advanced":
|
||||
- heading "Advanced Settings" [level=3]
|
||||
- heading /Send Messages on (CTRL|⌘)\\+ENTER/ [level=4]
|
||||
- button /Send Messages on (CTRL|⌘)\\+ENTER Edit/
|
||||
- text: /(On for all messages|On only for code blocks starting with \`\`\`|Off)/${wysiwygSnapshot}
|
||||
- heading "Enable Post Formatting" [level=4]
|
||||
- button "Enable Post Formatting Edit"
|
||||
- text: /(On|Off)/
|
||||
- heading "Enable Join/Leave Messages" [level=4]
|
||||
- button "Enable Join/Leave Messages Edit"
|
||||
- text: /(On|Off)/
|
||||
- heading "Scroll position when viewing an unread channel" [level=4]
|
||||
- button "Scroll position when viewing an unread channel Edit"
|
||||
- text: /(Start me where I left off|Start me at the newest message)/
|
||||
- heading "Allow message drafts to sync with the server" [level=4]
|
||||
- button "Allow message drafts to sync with the server Edit"
|
||||
- text: /(On|Off)/
|
||||
`);
|
||||
|
||||
// * Analyze the Advanced settings panel for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.include(settingsModal.advancedSettings.id)
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Send Messages on CTRL+ENTER section passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Send Messages on CTRL+ENTER section',
|
||||
{tag: ['@accessibility', '@settings', '@advanced_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Open Advanced tab
|
||||
await settingsModal.advancedTab.click();
|
||||
|
||||
const advancedSettings = settingsModal.advancedSettings;
|
||||
|
||||
// # Click Edit on Send Messages section
|
||||
await advancedSettings.ctrlEnterEditButton.click();
|
||||
|
||||
// * Verify aria snapshot of Send Messages section when expanded
|
||||
await advancedSettings.expandedSection.waitFor();
|
||||
await expect(advancedSettings.expandedSection).toMatchAriaSnapshot(`
|
||||
- heading /Send Messages on (CTRL|⌘)\\+ENTER/ [level=4]
|
||||
- group /Send Messages on (CTRL|⌘)\\+ENTER/:
|
||||
- text: /Send Messages on (CTRL|⌘)\\+ENTER/
|
||||
- radio "On for all messages"
|
||||
- text: On for all messages
|
||||
- radio /(On only for code blocks starting with \`\`\`|On only for code blocks starting with \\\`\\\`\\\`)/ [checked]
|
||||
- text: /(On only for code blocks starting with \`\`\`|On only for code blocks starting with \\\`\\\`\\\`)/
|
||||
- radio "Off"
|
||||
- text: /Off When enabled, (CTRL|⌘) \\+ ENTER will send the message and ENTER inserts a new line\\./
|
||||
- separator
|
||||
- alert
|
||||
- button "Save"
|
||||
- button "Cancel"
|
||||
`);
|
||||
|
||||
// # Analyze the expanded section for accessibility issues
|
||||
const accessibilityScanResults = await axe.builder(page).include(advancedSettings.expandedSectionId).analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Enable Post Formatting section passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Enable Post Formatting section',
|
||||
{tag: ['@accessibility', '@settings', '@advanced_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Open Advanced tab
|
||||
await settingsModal.advancedTab.click();
|
||||
|
||||
const advancedSettings = settingsModal.advancedSettings;
|
||||
|
||||
// # Click Edit on Enable Post Formatting section
|
||||
await advancedSettings.postFormattingEditButton.click();
|
||||
|
||||
// * Verify aria snapshot of Enable Post Formatting section when expanded
|
||||
await advancedSettings.expandedSection.waitFor();
|
||||
await expect(advancedSettings.expandedSection).toMatchAriaSnapshot(`
|
||||
- heading "Enable Post Formatting" [level=4]
|
||||
- group "Enable Post Formatting":
|
||||
- radio /(On|Off)/ [checked]
|
||||
- text: /(On|Off)/
|
||||
- separator
|
||||
- alert
|
||||
- button "Save"
|
||||
- button "Cancel"
|
||||
`);
|
||||
|
||||
// # Analyze the expanded section for accessibility issues
|
||||
const accessibilityScanResults = await axe.builder(page).include(advancedSettings.expandedSectionId).analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Enable Join/Leave Messages section passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Enable Join/Leave Messages section',
|
||||
{tag: ['@accessibility', '@settings', '@advanced_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Open Advanced tab
|
||||
await settingsModal.advancedTab.click();
|
||||
|
||||
const advancedSettings = settingsModal.advancedSettings;
|
||||
|
||||
// # Click Edit on Enable Join/Leave Messages section
|
||||
await advancedSettings.joinLeaveEditButton.click();
|
||||
|
||||
// * Verify aria snapshot of Enable Join/Leave Messages section when expanded
|
||||
await advancedSettings.expandedSection.waitFor();
|
||||
await expect(advancedSettings.expandedSection).toMatchAriaSnapshot(`
|
||||
- heading "Enable Join/Leave Messages" [level=4]
|
||||
- group "Enable Join/Leave Messages":
|
||||
- text: Enable Join/Leave Messages
|
||||
- radio "On" [checked]
|
||||
- text: "On"
|
||||
- radio "Off"
|
||||
- text: /Off When "On", System Messages saying a user has joined or left a channel will be visible\\./
|
||||
- separator
|
||||
- alert
|
||||
- button "Save"
|
||||
- button "Cancel"
|
||||
`);
|
||||
|
||||
// # Analyze the expanded section for accessibility issues
|
||||
const accessibilityScanResults = await axe.builder(page).include(advancedSettings.expandedSectionId).analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Scroll position section passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Scroll position section',
|
||||
{tag: ['@accessibility', '@settings', '@advanced_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Open Advanced tab
|
||||
await settingsModal.advancedTab.click();
|
||||
|
||||
const advancedSettings = settingsModal.advancedSettings;
|
||||
|
||||
// # Click Edit on Scroll position section
|
||||
await advancedSettings.scrollPositionEditButton.click();
|
||||
|
||||
// * Verify aria snapshot of Scroll position section when expanded
|
||||
await advancedSettings.expandedSection.waitFor();
|
||||
await expect(advancedSettings.expandedSection).toMatchAriaSnapshot(`
|
||||
- heading "Scroll position when viewing an unread channel" [level=4]
|
||||
- group "Scroll position when viewing an unread channel":
|
||||
- text: Scroll position when viewing an unread channel
|
||||
- radio "Start me where I left off" [checked]
|
||||
- text: Start me where I left off
|
||||
- radio "Start me at the newest message"
|
||||
- text: /Start me at the newest message Choose your scroll position when you view an unread channel\\./
|
||||
- separator
|
||||
- alert
|
||||
- button "Save"
|
||||
- button "Cancel"
|
||||
`);
|
||||
|
||||
// # Analyze the expanded section for accessibility issues
|
||||
const accessibilityScanResults = await axe.builder(page).include(advancedSettings.expandedSectionId).analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify message drafts sync section passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Allow message drafts to sync section',
|
||||
{tag: ['@accessibility', '@settings', '@advanced_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Open Advanced tab
|
||||
await settingsModal.advancedTab.click();
|
||||
|
||||
const advancedSettings = settingsModal.advancedSettings;
|
||||
|
||||
// # Click Edit on Allow message drafts to sync section
|
||||
await advancedSettings.syncDraftsEditButton.click();
|
||||
|
||||
// * Verify aria snapshot of Allow message drafts to sync section when expanded
|
||||
await advancedSettings.expandedSection.waitFor();
|
||||
await expect(advancedSettings.expandedSection).toMatchAriaSnapshot(`
|
||||
- heading "Allow message drafts to sync with the server" [level=4]
|
||||
- group "Allow message drafts to sync with the server":
|
||||
- text: Allow message drafts to sync with the server
|
||||
- radio "On" [checked]
|
||||
- text: "On"
|
||||
- radio "Off"
|
||||
- text: /Off When enabled, message drafts are synced with the server/
|
||||
- separator
|
||||
- alert
|
||||
- button "Save"
|
||||
- button "Cancel"
|
||||
`);
|
||||
|
||||
// # Analyze the expanded section for accessibility issues
|
||||
const accessibilityScanResults = await axe.builder(page).include(advancedSettings.expandedSectionId).analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,385 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
test(
|
||||
'navigate on keyboard tab between interactive elements',
|
||||
{tag: ['@accessibility', '@settings', '@notification_settings']},
|
||||
async ({pw}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
const notificationsSettings = settingsModal.notificationsSettings;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible and focus should be on the modal
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.container);
|
||||
|
||||
// # Press Tab and verify focus is on Close button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.closeButton);
|
||||
|
||||
// # Press Tab to move focus to Notifications tab
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.notificationsTab);
|
||||
|
||||
// # Press Tab to move focus to Learn more link
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(notificationsSettings.learnMoreText);
|
||||
|
||||
// # Press Tab to move focus to Desktop and mobile notifications button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(notificationsSettings.desktopAndMobileEditButton);
|
||||
|
||||
// # Press Tab to move focus to Desktop notification sounds button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(notificationsSettings.desktopNotificationSoundEditButton);
|
||||
|
||||
// # Press Tab to move focus to Email notifications button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(notificationsSettings.emailEditButton);
|
||||
|
||||
// # Press Tab to move focus to Auto-follow threads on channel-wide mentions button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(notificationsSettings.channelMentionAutoFollowEditButton);
|
||||
|
||||
// # Press Tab to move focus to Keywords that trigger notifications button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(notificationsSettings.keywordsTriggerNotificationsEditButton);
|
||||
|
||||
// # Press Tab to move focus to Keywords that get highlighted (without notifications) button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(notificationsSettings.keywordsGetHighlightedEditButton);
|
||||
|
||||
// # Press Tab to move focus to Send a test notification button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(notificationsSettings.testNotificationButton);
|
||||
|
||||
// # Press Tab to move focus to Troubleshooting docs button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(notificationsSettings.troubleshootingDocsButton);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Notifications settings panel',
|
||||
{tag: ['@accessibility', '@settings', '@notification_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user, adminClient} = await pw.initSetup();
|
||||
const clientConfig = await adminClient.getClientConfig();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// * Verify aria snapshot of Notifications settings tab
|
||||
await expect(settingsModal.notificationsSettings.container).toMatchAriaSnapshot(`
|
||||
- tabpanel "notifications":
|
||||
- heading "Notifications" [level=3]
|
||||
- link "Learn more about notifications":
|
||||
- /url: https://mattermost.com/pl/about-notifications?utm_source=mattermost&utm_medium=in-product&utm_content=user_settings_notifications&uid=${user.id}&sid=${clientConfig.DiagnosticId}&edition=enterprise&server_version=${clientConfig.Version}
|
||||
- img
|
||||
- heading "Desktop and mobile notifications Permission required" [level=4]:
|
||||
- img
|
||||
- button "Desktop and mobile notifications Permission required Edit"
|
||||
- text: Mentions, direct messages, and group messages
|
||||
- heading "Desktop notification sounds" [level=4]
|
||||
- button "Desktop notification sounds Edit"
|
||||
- text: "\\"Bing\\" for messages"
|
||||
- heading "Email notifications" [level=4]
|
||||
- button "Email notifications Edit"
|
||||
- heading "Auto-follow threads on channel-wide mentions" [level=4]
|
||||
- button "Auto-follow threads on channel-wide mentions Edit"
|
||||
- text: "On"
|
||||
- heading "Keywords that trigger notifications" [level=4]
|
||||
- button "Keywords that trigger notifications Edit"
|
||||
- text: "\\"@${user.username}\\", \\"@channel\\", \\"@all\\", \\"@here\\""
|
||||
- heading "Keywords that get highlighted (without notifications)" [level=4]
|
||||
- button "Keywords that get highlighted (without notifications) Edit"
|
||||
- heading "Troubleshooting notifications" [level=4]
|
||||
- paragraph: Not receiving notifications? Start by sending a test notification to all your devices to check if they’re working as expected. If issues persist, explore ways to solve them with troubleshooting steps.
|
||||
- button "Send a test notification"
|
||||
- button "Troubleshooting docs "
|
||||
`);
|
||||
|
||||
// * Analyze the Notifications settings panel for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.include(settingsModal.notificationsSettings.id)
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Desktop and mobile notifications section',
|
||||
{tag: ['@accessibility', '@settings', '@notification_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user, adminClient} = await pw.initSetup();
|
||||
|
||||
// # Send push notifications, without which the mobile settings in this section are replaced
|
||||
// # by a "not enabled by your system administrator" message
|
||||
await adminClient.patchConfig({EmailSettings: {SendPushNotifications: true}});
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
const notificationsSettings = settingsModal.notificationsSettings;
|
||||
|
||||
// # Click Edit on Desktop and mobile notifications section
|
||||
await notificationsSettings.desktopAndMobileEditButton.click();
|
||||
|
||||
// * Verify aria snapshot of Desktop and mobile notifications section when expanded
|
||||
await notificationsSettings.expandedSection.waitFor();
|
||||
await expect(notificationsSettings.expandedSection).toMatchAriaSnapshot({
|
||||
name: 'desktop_and_mobile_section.yml',
|
||||
});
|
||||
|
||||
// # Analyze the expanded section for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page)
|
||||
.include(notificationsSettings.expandedSectionId)
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Desktop notification sounds section',
|
||||
{tag: ['@accessibility', '@settings', '@notification_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
const notificationsSettings = settingsModal.notificationsSettings;
|
||||
|
||||
// # Click Edit on Desktop notification sounds section
|
||||
await notificationsSettings.desktopNotificationSoundEditButton.click();
|
||||
|
||||
// * Verify aria snapshot of Desktop notification sounds section when expanded
|
||||
await notificationsSettings.expandedSection.waitFor();
|
||||
await expect(notificationsSettings.expandedSection).toMatchAriaSnapshot({
|
||||
name: 'desktop_notification_sounds_section.yml',
|
||||
});
|
||||
|
||||
// # Analyze the expanded section for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page)
|
||||
.include(notificationsSettings.expandedSectionId)
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Email notifications section',
|
||||
{tag: ['@accessibility', '@settings', '@notification_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
const notificationsSettings = settingsModal.notificationsSettings;
|
||||
|
||||
// # Click Edit on Email notifications section
|
||||
await notificationsSettings.emailEditButton.click();
|
||||
|
||||
// * Verify aria snapshot of Email notifications section when expanded
|
||||
await notificationsSettings.expandedSection.waitFor();
|
||||
await expect(notificationsSettings.expandedSection).toMatchAriaSnapshot({
|
||||
name: 'email_notifications_section.yml',
|
||||
});
|
||||
|
||||
// # Analyze the expanded section for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page)
|
||||
.include(notificationsSettings.expandedSectionId)
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Keywords that trigger notifications section',
|
||||
{tag: ['@accessibility', '@settings', '@notification_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
const notificationsSettings = settingsModal.notificationsSettings;
|
||||
|
||||
// # Click Edit on Keywords that trigger notifications section
|
||||
await notificationsSettings.keywordsTriggerNotificationsEditButton.click();
|
||||
|
||||
// * Verify aria snapshot of Keywords that trigger notifications section when expanded
|
||||
await notificationsSettings.expandedSection.waitFor();
|
||||
await expect(notificationsSettings.expandedSection).toMatchAriaSnapshot(`
|
||||
- heading "Keywords that trigger notifications" [level=4]
|
||||
- group "Keywords that trigger notifications":
|
||||
- checkbox "Your case-sensitive first name \\"${user.first_name}\\""
|
||||
- text: Your case-sensitive first name "${user.first_name}"
|
||||
- checkbox "Your non case-sensitive username \\"${user.username}\\""
|
||||
- text: Your non case-sensitive username "${user.username}"
|
||||
- checkbox "Channel-wide mentions \\"@channel\\", \\"@all\\", \\"@here\\"" [checked]
|
||||
- text: Channel-wide mentions "@channel", "@all", "@here"
|
||||
- checkbox "Other non case-sensitive words, press Tab or use commas to separate keywords:"
|
||||
- text: "Other non case-sensitive words, press Tab or use commas to separate keywords:"
|
||||
- log
|
||||
- combobox "Keywords that trigger notifications"
|
||||
- text: Notifications are triggered when someone sends a message that includes your username ("@${user.username}") or any of the options selected above.
|
||||
- separator
|
||||
- alert
|
||||
- button "Save"
|
||||
- button "Cancel"
|
||||
`);
|
||||
|
||||
// # Analyze the expanded section for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page)
|
||||
.include(notificationsSettings.expandedSectionId)
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Keywords that get highlighted (without notifications) section',
|
||||
{tag: ['@accessibility', '@settings', '@notification_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
const notificationsSettings = settingsModal.notificationsSettings;
|
||||
|
||||
// # Click Edit on Keywords that get highlighted (without notifications) section
|
||||
await notificationsSettings.keywordsGetHighlightedEditButton.click();
|
||||
|
||||
// * Verify aria snapshot of Keywords that get highlighted (without notifications) section when expanded
|
||||
await notificationsSettings.expandedSection.waitFor();
|
||||
await expect(notificationsSettings.expandedSection).toMatchAriaSnapshot({
|
||||
name: 'keywords_that_get_highlighted_section.yml',
|
||||
});
|
||||
|
||||
// # Analyze the expanded section for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page)
|
||||
.include(notificationsSettings.expandedSectionId)
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
- heading "Desktop and mobile notifications" [level=4]
|
||||
- text:
|
||||
- heading "Browser notification permission was denied" [level=4]
|
||||
- paragraph: You're missing important message and call notifications from Mattermost. To start receiving notifications, please enable notifications for Mattermost in your browser settings.
|
||||
- button "How to enable notifications"
|
||||
- group "Send notifications for:":
|
||||
- radio "All new messages"
|
||||
- radio "Mentions, direct messages, and group messages" [checked]
|
||||
- text: Mentions, direct messages, and group messages
|
||||
- radio "Nothing"
|
||||
- checkbox "Notify me about replies to threads I'm following" [checked]
|
||||
- text: Notify me about replies to threads I'm following
|
||||
- separator
|
||||
- checkbox "Use different settings for my mobile devices"
|
||||
- text: "Use different settings for my mobile devices Trigger mobile notifications when I am:"
|
||||
- log
|
||||
- text: Online, away, or offline
|
||||
- combobox "Trigger mobile notifications when I am:"
|
||||
- separator
|
||||
- alert
|
||||
- button "Save"
|
||||
- button "Cancel"
|
||||
@@ -0,0 +1,10 @@
|
||||
- heading "Desktop notification sounds" [level=4]
|
||||
- checkbox "Message notification sound" [checked]
|
||||
- text: Message notification sound
|
||||
- log
|
||||
- text: Bing
|
||||
- combobox "Message notification sound"
|
||||
- separator
|
||||
- alert
|
||||
- button "Save"
|
||||
- button "Cancel"
|
||||
@@ -0,0 +1,16 @@
|
||||
- heading "Email notifications" [level=4]
|
||||
- group:
|
||||
- group:
|
||||
- radio "On" [checked]
|
||||
- text: "On"
|
||||
- radio "Off"
|
||||
- text: "Off"
|
||||
- text: When enabled, email notifications are sent for mentions and direct messages when you are offline or away for more than 5 minutes.
|
||||
- separator
|
||||
- group:
|
||||
- checkbox "Notify me about replies to threads I’m following" [checked]
|
||||
- text: Notify me about replies to threads I’m following
|
||||
- separator
|
||||
- alert
|
||||
- button "Save"
|
||||
- button "Cancel"
|
||||
@@ -0,0 +1,9 @@
|
||||
- heading "Keywords that get highlighted (without notifications)" [level=4]
|
||||
- text: "Enter non case-sensitive keywords, press Tab or use commas to separate them: option , selected.Select is focused ,type to refine list, press Down to open the menu, press left to focus selected values"
|
||||
- log
|
||||
- combobox
|
||||
- text: These keywords will be shown to you with a highlight when anyone sends a message that includes them.
|
||||
- separator
|
||||
- alert
|
||||
- button "Save"
|
||||
- button "Cancel"
|
||||
@@ -0,0 +1,307 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify that focus is properly managed when opening and closing settings modal using keyboard interactions
|
||||
*/
|
||||
test(
|
||||
'manages focus when opening and closing settings modal with keyboard',
|
||||
{tag: ['@accessibility', '@settings']},
|
||||
async ({pw}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible and focus should be on the modal
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.container);
|
||||
|
||||
// # Press Tab and verify focus is on Close button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.closeButton);
|
||||
|
||||
// # Press Enter and verify Settings modal is closed and focus is back on Settings button
|
||||
await page.keyboard.press('Enter');
|
||||
await expect(settingsModal.container).not.toBeVisible();
|
||||
await pw.toBeFocusedWithFocusVisible(globalHeader.settingsButton);
|
||||
|
||||
// # Open Settings modal again
|
||||
await page.keyboard.press('Enter');
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Press Escape and verify Settings modal is closed and focus is back on Settings button
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(settingsModal.container).not.toBeVisible();
|
||||
await pw.toBeFocusedWithFocusVisible(globalHeader.settingsButton);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify that users can navigate between settings tabs using arrow keys
|
||||
*/
|
||||
test('navigates between settings tabs using arrow keys', {tag: ['@accessibility', '@settings']}, async ({pw}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await channelsPage.globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible and focus should be on the modal
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.container);
|
||||
|
||||
// # Press Tab twice and verify focus is on Notifications tab and Notifications Settings panel is visible
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(settingsModal.notificationsTab).toBeVisible();
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.notificationsTab);
|
||||
await expect(settingsModal.notificationsSettings.container).toBeVisible();
|
||||
|
||||
// # Press arrow down key and verify focus is on Display tab and Display Settings panel is visible
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.displayTab);
|
||||
await expect(settingsModal.displaySettings.container).toBeVisible();
|
||||
|
||||
// # Press arrow down key and verify focus is on Sidebar tab and Sidebar Settings panel is visible
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(settingsModal.sidebarTab).toBeVisible();
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.sidebarTab);
|
||||
await expect(settingsModal.sidebarSettings.container).toBeVisible();
|
||||
|
||||
// # Press Tab and verify focus is on Advanced tab and Advanced Settings panel is visible
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.advancedTab);
|
||||
await expect(settingsModal.advancedSettings.container).toBeVisible();
|
||||
|
||||
// # Press arrow down through the tabs plugins add after Advanced, Calls among them
|
||||
const builtInTabs = 4;
|
||||
const lastTab = settingsModal.tabs.last();
|
||||
for (let i = 0; i < (await settingsModal.tabs.count()) - builtInTabs; i++) {
|
||||
await page.keyboard.press('ArrowDown');
|
||||
}
|
||||
await pw.toBeFocusedWithFocusVisible(lastTab);
|
||||
|
||||
// # Press arrow down key and verify focus is back on Notifications tab and Notifications Settings panel is visible
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await expect(settingsModal.notificationsTab).toBeVisible();
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.notificationsTab);
|
||||
await expect(settingsModal.notificationsSettings.container).toBeVisible();
|
||||
|
||||
// # Press arrow up key and verify focus wraps to the last tab
|
||||
await page.keyboard.press('ArrowUp');
|
||||
await pw.toBeFocusedWithFocusVisible(lastTab);
|
||||
});
|
||||
|
||||
/**
|
||||
* @objective Verify that notifications settings panel meets WCAG accessibility standards
|
||||
*/
|
||||
test(
|
||||
'passes accessibility scan on notifications settings panel',
|
||||
{tag: ['@accessibility', '@settings']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await channelsPage.globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal on notifications panel should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
await expect(settingsModal.notificationsSettings.container).toBeVisible();
|
||||
|
||||
// * Analyze the Settings modal with notifications panel
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.disableRules([
|
||||
'color-contrast',
|
||||
|
||||
// Known issue: These fail due to the way setting tabs are grouped.
|
||||
'aria-required-children',
|
||||
'aria-required-parent',
|
||||
])
|
||||
.include(settingsModal.getContainerId())
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify that display settings panel meets WCAG accessibility standards
|
||||
*/
|
||||
test(
|
||||
'passes accessibility scan on display settings panel',
|
||||
{tag: ['@accessibility', '@settings']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await channelsPage.globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings dialog should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Open Display tab
|
||||
await settingsModal.openDisplayTab();
|
||||
|
||||
// * Display Settings panel should be visible
|
||||
await expect(settingsModal.displaySettings.container).toBeVisible();
|
||||
|
||||
// * Analyze the Settings modal with display panel
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.disableRules([
|
||||
'color-contrast',
|
||||
|
||||
// Known issue: These fail due to the way setting tabs are grouped.
|
||||
'aria-required-children',
|
||||
'aria-required-parent',
|
||||
])
|
||||
.include(settingsModal.getContainerId())
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify that sidebar settings panel meets WCAG accessibility standards
|
||||
*/
|
||||
test(
|
||||
'passes accessibility scan on sidebar settings panel',
|
||||
{tag: ['@accessibility', '@settings']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await channelsPage.globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings dialog should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Open Sidebar tab
|
||||
await settingsModal.openSidebarTab();
|
||||
|
||||
// * Sidebar Settings panel should be visible
|
||||
await expect(settingsModal.sidebarSettings.container).toBeVisible();
|
||||
|
||||
// * Analyze the Settings modal with sidebar panel
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.disableRules([
|
||||
'color-contrast',
|
||||
|
||||
// Known issue: These fail due to the way setting tabs are grouped.
|
||||
'aria-required-children',
|
||||
'aria-required-parent',
|
||||
])
|
||||
.include(settingsModal.getContainerId())
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify that advanced settings panel meets WCAG accessibility standards
|
||||
*/
|
||||
test(
|
||||
'passes accessibility scan on advanced settings panel',
|
||||
{tag: ['@accessibility', '@settings']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await channelsPage.globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings dialog should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Open Advanced tab
|
||||
await settingsModal.openAdvancedTab();
|
||||
|
||||
// * Advanced Settings panel should be visible
|
||||
await expect(settingsModal.advancedSettings.container).toBeVisible();
|
||||
|
||||
// * Analyze the Settings modal with advanced panel
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.disableRules([
|
||||
'color-contrast',
|
||||
|
||||
// Known issue: These fail due to the way setting tabs are grouped.
|
||||
'aria-required-children',
|
||||
'aria-required-parent',
|
||||
])
|
||||
.include(settingsModal.getContainerId())
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,210 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify keyboard navigation works correctly through all interactive elements in the Sidebar settings panel
|
||||
*/
|
||||
test(
|
||||
'navigate on keyboard tab between interactive elements',
|
||||
{tag: ['@accessibility', '@settings', '@sidebar_settings']},
|
||||
async ({pw}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
const sidebarSettings = settingsModal.sidebarSettings;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible and focus should be on the modal
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.container);
|
||||
|
||||
// # Press Tab and verify focus is on Close button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.closeButton);
|
||||
|
||||
// # Press Tab to move focus to Notifications tab
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.notificationsTab);
|
||||
|
||||
// # Press ArrowDown to navigate to Display tab
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.displayTab);
|
||||
|
||||
// # Press ArrowDown to navigate to Sidebar tab
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await pw.toBeFocusedWithFocusVisible(settingsModal.sidebarTab);
|
||||
|
||||
// # Press Tab to move focus to Group unread channels separately button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(sidebarSettings.groupUnreadEditButton);
|
||||
|
||||
// # Press Tab to move focus to Number of direct messages to show button
|
||||
await page.keyboard.press('Tab');
|
||||
await pw.toBeFocusedWithFocusVisible(sidebarSettings.limitVisibleDMsEditButton);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify the Sidebar settings panel passes accessibility scan and matches aria snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Sidebar settings panel',
|
||||
{tag: ['@accessibility', '@settings', '@sidebar_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Open Sidebar tab
|
||||
await settingsModal.openSidebarTab();
|
||||
|
||||
// * Verify aria snapshot of Sidebar settings tab
|
||||
await expect(settingsModal.sidebarSettings.container).toMatchAriaSnapshot(`
|
||||
- tabpanel "sidebar":
|
||||
- heading "Sidebar Settings" [level=3]
|
||||
- heading "Group unread channels separately" [level=4]
|
||||
- button "Group unread channels separately Edit": Edit
|
||||
- text: "Off"
|
||||
- heading "Number of direct messages to show" [level=4]
|
||||
- button "Number of direct messages to show Edit": Edit
|
||||
- text: "40"
|
||||
`);
|
||||
|
||||
// * Analyze the Sidebar settings panel for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.include(settingsModal.sidebarSettings.id)
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify the Group unread channels separately section passes accessibility scan when expanded
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Group unread channels separately section',
|
||||
{tag: ['@accessibility', '@settings', '@sidebar_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Open Sidebar tab
|
||||
await settingsModal.openSidebarTab();
|
||||
|
||||
const sidebarSettings = settingsModal.sidebarSettings;
|
||||
|
||||
// # Click Edit on Group unread channels separately section
|
||||
await sidebarSettings.groupUnreadEditButton.click();
|
||||
|
||||
// * Verify aria snapshot of Group unread channels separately section when expanded
|
||||
await sidebarSettings.expandedSection.waitFor();
|
||||
await expect(sidebarSettings.expandedSection).toMatchAriaSnapshot({
|
||||
name: 'group_unread_channels_section.yml',
|
||||
});
|
||||
|
||||
// # Analyze the expanded section for accessibility issues
|
||||
const accessibilityScanResults = await axe.builder(page).include(sidebarSettings.expandedSectionId).analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify the Number of direct messages to show section passes accessibility scan when expanded
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Number of direct messages to show section',
|
||||
{tag: ['@accessibility', '@settings', '@sidebar_settings', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const globalHeader = channelsPage.globalHeader;
|
||||
const settingsModal = channelsPage.settingsModal;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus to Settings button and press Enter
|
||||
await globalHeader.settingsButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Settings modal should be visible
|
||||
await expect(settingsModal.container).toBeVisible();
|
||||
|
||||
// # Open Sidebar tab
|
||||
await settingsModal.openSidebarTab();
|
||||
|
||||
const sidebarSettings = settingsModal.sidebarSettings;
|
||||
|
||||
// # Click Edit on Number of direct messages to show section
|
||||
await sidebarSettings.limitVisibleDMsEditButton.click();
|
||||
|
||||
// * Verify aria snapshot of Number of direct messages to show section when expanded
|
||||
await sidebarSettings.expandedSection.waitFor();
|
||||
await expect(sidebarSettings.expandedSection).toMatchAriaSnapshot({
|
||||
name: 'number_of_direct_messages_section.yml',
|
||||
});
|
||||
|
||||
// # Analyze the expanded section for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page)
|
||||
.include(sidebarSettings.expandedSectionId)
|
||||
.exclude('#react-select-2-input') // Exclude react-select input that has a known accessibility issue
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,9 @@
|
||||
- heading "Group unread channels separately" [level=4]
|
||||
- group "Group unread channels separately":
|
||||
- radio "On"
|
||||
- radio "Off" [checked]
|
||||
- text: Off When enabled, all unread channels and direct messages will be grouped together in the sidebar.
|
||||
- separator
|
||||
- alert
|
||||
- button "Save"
|
||||
- button "Cancel"
|
||||
@@ -0,0 +1,10 @@
|
||||
- heading "Number of direct messages to show" [level=4]
|
||||
- group "Number of direct messages to show":
|
||||
- text: /Number of direct messages to show option \d+, selected\.Select is focused , press Down to open the menu,/
|
||||
- log
|
||||
- combobox
|
||||
- text: You can also change these settings in the direct messages sidebar menu.
|
||||
- separator
|
||||
- alert
|
||||
- button "Save"
|
||||
- button "Cancel"
|
||||
@@ -0,0 +1,326 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify arrow key navigation through all menu items in the team menu
|
||||
*/
|
||||
test(
|
||||
'navigate using arrow keys between menu items',
|
||||
{tag: ['@accessibility', '@team_menu', '@keyboard_navigation']},
|
||||
async ({pw}) => {
|
||||
// # Initialize setup and make the user a team admin to see all menu items
|
||||
const {user, team, adminClient} = await pw.initSetup();
|
||||
await adminClient.updateTeamMemberSchemeRoles(team.id, user.id, true, true);
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const sidebarLeft = channelsPage.sidebarLeft;
|
||||
const teamMenu = channelsPage.teamMenu;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus on team menu button and press Enter to open the menu
|
||||
await sidebarLeft.teamMenuButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
await pw.logFocusedElement(page);
|
||||
|
||||
// * Team menu should be visible and focus should be on first menu item - "Invite people"
|
||||
await teamMenu.toBeVisible();
|
||||
await pw.toBeFocusedWithFocusVisible(teamMenu.invitePeople);
|
||||
|
||||
// # Press ArrowDown to move focus to "Team settings" menu item
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await pw.toBeFocusedWithFocusVisible(teamMenu.teamSettings);
|
||||
|
||||
// # Press ArrowDown to move focus to "Manage members" menu item
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await pw.toBeFocusedWithFocusVisible(teamMenu.manageMembers);
|
||||
|
||||
// # Press ArrowDown to move focus to "Leave team" menu item
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await pw.toBeFocusedWithFocusVisible(teamMenu.leaveTeam);
|
||||
|
||||
// # Press ArrowDown to move focus to "Create a team" menu item
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await pw.toBeFocusedWithFocusVisible(teamMenu.createTeam);
|
||||
|
||||
// # Press ArrowDown to move focus to "Learn about teams" menu item
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await pw.toBeFocusedWithFocusVisible(teamMenu.learnAboutTeams);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Tab key navigation escapes menu and moves to next focusable element
|
||||
*/
|
||||
test(
|
||||
'navigate using Tab key to escape menu',
|
||||
{tag: ['@accessibility', '@team_menu', '@keyboard_navigation']},
|
||||
async ({pw}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const sidebarLeft = channelsPage.sidebarLeft;
|
||||
const teamMenu = channelsPage.teamMenu;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Set focus on team menu button and press Enter to open the menu
|
||||
await sidebarLeft.teamMenuButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Team menu should be visible and focus should be on first menu item
|
||||
await teamMenu.toBeVisible();
|
||||
await pw.toBeFocusedWithFocusVisible(teamMenu.invitePeople);
|
||||
|
||||
// # Press Tab to exit menu and move to next focusable element outside menu
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
// * Team menu should be closed and focus should move to next focusable element
|
||||
await expect(teamMenu.container).not.toBeVisible();
|
||||
|
||||
// * Focus should be on the next focusable element - "Browse or create channels" button
|
||||
await expect(sidebarLeft.teamMenuButton).not.toBeFocused();
|
||||
await expect(sidebarLeft.browseOrCreateChannelButton).toBeFocused();
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify menu items can be activated using Enter and Space keys
|
||||
*/
|
||||
test(
|
||||
'activate menu items using Enter and Space keys',
|
||||
{tag: ['@accessibility', '@team_menu', '@keyboard_navigation']},
|
||||
async ({pw}) => {
|
||||
// # Initialize setup and make the user a team admin to see all menu items
|
||||
const {user, team, adminClient} = await pw.initSetup();
|
||||
await adminClient.updateTeamMemberSchemeRoles(team.id, user.id, true, true);
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const sidebarLeft = channelsPage.sidebarLeft;
|
||||
const teamMenu = channelsPage.teamMenu;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on team menu button to open the menu
|
||||
await sidebarLeft.teamMenuButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Team menu should be visible
|
||||
await teamMenu.toBeVisible();
|
||||
|
||||
// # Set focus on "Team settings" menu item and press Enter
|
||||
await teamMenu.teamSettings.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// * Team settings modal should open (wait for navigation or modal)
|
||||
await channelsPage.teamSettingsModal.toBeVisible();
|
||||
|
||||
// # Close the modal using Escape key
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
await expect(channelsPage.teamSettingsModal.container).not.toBeVisible();
|
||||
|
||||
// # Reopen team menu
|
||||
await sidebarLeft.teamMenuButton.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
await teamMenu.toBeVisible();
|
||||
|
||||
// # Set focus on "Learn about teams" menu item and press Space
|
||||
await teamMenu.invitePeople.focus();
|
||||
await page.keyboard.press(' ');
|
||||
|
||||
// * Should navigate to learn about teams documentation or open modal
|
||||
const invitePeopleModal = await channelsPage.getInvitePeopleModal(team.display_name);
|
||||
await invitePeopleModal.toBeVisible();
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Escape key closes the team menu
|
||||
*/
|
||||
test('close menu using Escape key', {tag: ['@accessibility', '@team_menu', '@keyboard_navigation']}, async ({pw}) => {
|
||||
// # Create and sign in a new user
|
||||
const {user} = await pw.initSetup();
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const sidebarLeft = channelsPage.sidebarLeft;
|
||||
const teamMenu = channelsPage.teamMenu;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on team menu button to open the menu
|
||||
await sidebarLeft.teamMenuButton.click();
|
||||
|
||||
// * Team menu should be visible
|
||||
await teamMenu.toBeVisible();
|
||||
|
||||
// # Press Escape key
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
// * Team menu should be closed
|
||||
await expect(teamMenu.container).not.toBeVisible();
|
||||
|
||||
// * Focus should return to the team menu button
|
||||
await pw.toBeFocusedWithFocusVisible(sidebarLeft.teamMenuButton);
|
||||
});
|
||||
|
||||
/**
|
||||
* @objective Verify team menu accessibility compliance and aria-snapshot structure
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of team menu',
|
||||
{tag: ['@accessibility', '@team_menu', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Initialize setup and make the user a team admin to see all menu items
|
||||
const {user, team, adminClient} = await pw.initSetup();
|
||||
await adminClient.updateTeamMemberSchemeRoles(team.id, user.id, true, true);
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
const sidebarLeft = channelsPage.sidebarLeft;
|
||||
const teamMenu = channelsPage.teamMenu;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on team menu button to open the menu
|
||||
await sidebarLeft.teamMenuButton.click();
|
||||
|
||||
// * Team menu should be visible
|
||||
await teamMenu.toBeVisible();
|
||||
|
||||
// * Verify aria snapshot of team menu structure
|
||||
await expect(teamMenu.container).toMatchAriaSnapshot();
|
||||
|
||||
// # Analyze the team menu for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.include(teamMenu.getContainerId())
|
||||
.analyze();
|
||||
|
||||
// * Should have no violations
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify menu items have proper ARIA attributes for screen readers
|
||||
*/
|
||||
test(
|
||||
'verify ARIA attributes and screen reader support',
|
||||
{tag: ['@accessibility', '@team_menu', '@screen_reader']},
|
||||
async ({pw}) => {
|
||||
// # Initialize setup and make the user a team admin to see all menu items
|
||||
const {user, team, adminClient} = await pw.initSetup();
|
||||
await adminClient.updateTeamMemberSchemeRoles(team.id, user.id, true, true);
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {channelsPage} = await pw.testBrowser.login(user);
|
||||
const sidebarLeft = channelsPage.sidebarLeft;
|
||||
const teamMenu = channelsPage.teamMenu;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on team menu button to open the menu
|
||||
await sidebarLeft.teamMenuButton.click();
|
||||
|
||||
// * Team menu should be visible
|
||||
await teamMenu.toBeVisible();
|
||||
|
||||
// * Verify menu container has correct role
|
||||
await expect(teamMenu.container).toHaveAttribute('role', 'menu');
|
||||
|
||||
// * Verify menu items have correct roles and attributes
|
||||
await expect(teamMenu.invitePeople).toHaveAttribute('role', 'menuitem');
|
||||
await expect(teamMenu.invitePeople).toHaveAttribute('aria-haspopup', 'dialog');
|
||||
|
||||
await expect(teamMenu.teamSettings).toHaveAttribute('role', 'menuitem');
|
||||
await expect(teamMenu.teamSettings).toHaveAttribute('aria-haspopup', 'dialog');
|
||||
|
||||
await expect(teamMenu.manageMembers).toHaveAttribute('role', 'menuitem');
|
||||
await expect(teamMenu.manageMembers).toHaveAttribute('aria-haspopup', 'dialog');
|
||||
|
||||
await expect(teamMenu.leaveTeam).toHaveAttribute('role', 'menuitem');
|
||||
await expect(teamMenu.leaveTeam).toHaveAttribute('aria-haspopup', 'dialog');
|
||||
|
||||
await expect(teamMenu.createTeam).toHaveAttribute('role', 'menuitem');
|
||||
|
||||
await expect(teamMenu.learnAboutTeams).toHaveAttribute('role', 'menuitem');
|
||||
|
||||
// * Verify menu items are focusable
|
||||
await expect(teamMenu.invitePeople).toHaveAttribute('tabindex', '0');
|
||||
|
||||
// * Verify menu items have accessible names
|
||||
await expect(teamMenu.invitePeople).toHaveAccessibleName('Invite people Add or invite people to the team');
|
||||
await expect(teamMenu.teamSettings).toHaveAccessibleName('Team settings');
|
||||
await expect(teamMenu.manageMembers).toHaveAccessibleName('Manage members');
|
||||
await expect(teamMenu.leaveTeam).toHaveAccessibleName('Leave team');
|
||||
await expect(teamMenu.createTeam).toHaveAccessibleName('Create a team');
|
||||
await expect(teamMenu.learnAboutTeams).toHaveAccessibleName('Learn about teams');
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify menu items with icons have proper visual indicators and alt text
|
||||
*/
|
||||
test(
|
||||
'verify visual indicators and icon accessibility',
|
||||
{tag: ['@accessibility', '@team_menu', '@visual_indicators']},
|
||||
async ({pw}) => {
|
||||
// # Initialize setup and make the user a team admin to see all menu items
|
||||
const {user, team, adminClient} = await pw.initSetup();
|
||||
await adminClient.updateTeamMemberSchemeRoles(team.id, user.id, true, true);
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {channelsPage} = await pw.testBrowser.login(user);
|
||||
const sidebarLeft = channelsPage.sidebarLeft;
|
||||
const teamMenu = channelsPage.teamMenu;
|
||||
|
||||
// # Visit a default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on team menu button to open the menu
|
||||
await sidebarLeft.teamMenuButton.click();
|
||||
|
||||
// * Team menu should be visible
|
||||
await teamMenu.toBeVisible();
|
||||
|
||||
// * Verify icons have aria-hidden attribute (decorative)
|
||||
const menuItems = [
|
||||
teamMenu.invitePeople,
|
||||
teamMenu.teamSettings,
|
||||
teamMenu.manageMembers,
|
||||
teamMenu.leaveTeam,
|
||||
teamMenu.createTeam,
|
||||
teamMenu.learnAboutTeams,
|
||||
];
|
||||
|
||||
for (const menuItem of menuItems) {
|
||||
// * Each menu item should contain an SVG icon with aria-hidden
|
||||
const icon = menuItem.locator('svg').first();
|
||||
await expect(icon).toHaveAttribute('aria-hidden', 'true');
|
||||
|
||||
// * Menu item should have visible focus indicator on focus
|
||||
await menuItem.focus();
|
||||
|
||||
// * Verify the menu item is focused and has focus-visible styles
|
||||
await pw.toBeFocusedWithFocusVisible(menuItem);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,9 @@
|
||||
- menu:
|
||||
- menuitem "Invite people Add or invite people to the team"
|
||||
- menuitem "Team settings"
|
||||
- menuitem "Manage members"
|
||||
- menuitem "Leave team"
|
||||
- separator
|
||||
- menuitem "Create a team"
|
||||
- separator
|
||||
- menuitem "Learn about teams"
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
test('Theme settings should be keyboard accessible', async ({axe, pw}) => {
|
||||
const {user} = await pw.initSetup();
|
||||
|
||||
// # Log in as a user in new browser context
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Initialize Axe
|
||||
const ab = axe.builder(page).disableRules([
|
||||
'color-contrast',
|
||||
|
||||
// Known issue: These fail due to the way we've grouped plugin setting tabs together in the LHS
|
||||
'aria-required-children',
|
||||
'aria-required-parent',
|
||||
]);
|
||||
|
||||
// # Visit default channel page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Open settings modal
|
||||
const settingsModal = await channelsPage.globalHeader.openSettings();
|
||||
|
||||
// * The settings modal should have no accessibility violations
|
||||
let accessibilityScanResults = await ab.analyze();
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
|
||||
// # Open display tab
|
||||
await settingsModal.container.focus();
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
|
||||
// * The display tab should be open
|
||||
const {displaySettings} = settingsModal;
|
||||
await displaySettings.toBeVisible();
|
||||
|
||||
// * The display tab should have no accessibility violations
|
||||
accessibilityScanResults = await ab.analyze();
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
|
||||
// # Open the theme section
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Space');
|
||||
|
||||
// * The theme section should be open
|
||||
await displaySettings.verifySectionIsExpanded('theme');
|
||||
|
||||
// * The Premade Themes option should be focused
|
||||
await expect(page.getByLabel('Premade Themes')).toBeFocused();
|
||||
|
||||
// * The theme section for premade themes should have no accessibility violations
|
||||
accessibilityScanResults = await ab.analyze();
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
|
||||
// * Should be able to tab through the options
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByRole('button', {name: 'Denim'})).toBeFocused();
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByRole('button', {name: 'Sapphire'})).toBeFocused();
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByRole('button', {name: 'Quartz'})).toBeFocused();
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByRole('button', {name: 'Indigo'})).toBeFocused();
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByRole('button', {name: 'Onyx'})).toBeFocused();
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
// Note: There's no "Apply to all your teams" option because this user is only on one team
|
||||
await expect(page.getByRole('link', {name: 'See other themes'})).toBeFocused();
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByRole('button', {name: 'Save'})).toBeFocused();
|
||||
|
||||
// # Go back to the premade/custom option
|
||||
await page.getByLabel('Premade Themes').focus();
|
||||
|
||||
// # Switch to the custom theme section
|
||||
await page.keyboard.press('ArrowDown');
|
||||
|
||||
// * The Custom Theme option should be focused
|
||||
await expect(page.getByLabel('Custom Theme')).toBeFocused();
|
||||
|
||||
// * Check the ARIA of the Sidebar Styles section
|
||||
await page.keyboard.press('Tab');
|
||||
const sidebarStyles = page.getByRole('button', {name: 'Sidebar Styles'});
|
||||
await expect(sidebarStyles).toBeFocused();
|
||||
await expect(sidebarStyles).toHaveAttribute('aria-expanded', 'false');
|
||||
|
||||
// * Check that we can tab over the collapsed section to the next section and then back again
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByRole('button', {name: 'Center Channel Styles'})).toBeFocused();
|
||||
|
||||
await page.keyboard.press('Shift+Tab');
|
||||
await expect(sidebarStyles).toBeFocused();
|
||||
|
||||
// * Check that we can expand the section
|
||||
await page.keyboard.press('Enter');
|
||||
await expect(sidebarStyles).toHaveAttribute('aria-expanded', 'true');
|
||||
|
||||
// # Wait for the expanding animation to be open
|
||||
await expect(page.getByLabel('Sidebar Styles')).toHaveCSS('overflow-y', 'visible');
|
||||
|
||||
// * Check that we can tab through color pickers
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByLabel('Sidebar BG', {exact: true})).toBeFocused();
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByLabel('Sidebar Text', {exact: true})).toBeFocused();
|
||||
await page.keyboard.press('Tab');
|
||||
await expect(page.getByLabel('Sidebar Header BG')).toBeFocused();
|
||||
|
||||
// * The theme section for custom themes should have no accessibility violations
|
||||
accessibilityScanResults = await ab.analyze();
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
test.beforeEach(async ({pw}) => {
|
||||
const {adminClient} = await pw.getAdminClient();
|
||||
await adminClient.patchConfig({
|
||||
TeamSettings: {EnableOpenServer: true},
|
||||
SamlSettings: {Enable: false},
|
||||
LdapSettings: {Enable: false},
|
||||
});
|
||||
});
|
||||
|
||||
test('/login accessibility quick check', async ({pw, axe}) => {
|
||||
// Set up the page not to redirect to the landing page
|
||||
await pw.hasSeenLandingPage();
|
||||
|
||||
// # Go to login page
|
||||
await pw.loginPage.goto();
|
||||
await pw.loginPage.toBeVisible();
|
||||
|
||||
// # Analyze the page
|
||||
const accessibilityScanResults = await axe.builder(pw.loginPage.page).analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('/login accessibility tab support', async ({pw}) => {
|
||||
// Set up the page not to redirect to the landing page
|
||||
await pw.hasSeenLandingPage();
|
||||
|
||||
// # Go to login page
|
||||
await pw.loginPage.goto();
|
||||
await pw.loginPage.toBeVisible();
|
||||
|
||||
// * Should have focused at login input on page load
|
||||
expect(await pw.loginPage.loginInput).toBeFocused();
|
||||
|
||||
// * Should move focus to password input after tab
|
||||
await pw.loginPage.loginInput.press('Tab');
|
||||
expect(await pw.loginPage.passwordInput).toBeFocused();
|
||||
|
||||
// * Should move focus to password toggle button after tab
|
||||
await pw.loginPage.passwordInput.press('Tab');
|
||||
expect(await pw.loginPage.passwordToggleButton).toBeFocused();
|
||||
|
||||
// * Should move focus to forgot password link after tab
|
||||
await pw.loginPage.passwordToggleButton.press('Tab');
|
||||
expect(await pw.loginPage.forgotPasswordLink).toBeFocused();
|
||||
|
||||
// * Should move focus to forgot password link after tab
|
||||
await pw.loginPage.forgotPasswordLink.press('Tab');
|
||||
expect(await pw.loginPage.signInButton).toBeFocused();
|
||||
|
||||
// * Should move focus to about link after tab
|
||||
await pw.loginPage.signInButton.press('Tab');
|
||||
expect(await pw.loginPage.footer.aboutLink).toBeFocused();
|
||||
|
||||
// * Should move focus to privacy policy link after tab
|
||||
await pw.loginPage.footer.aboutLink.press('Tab');
|
||||
expect(await pw.loginPage.footer.privacyPolicyLink).toBeFocused();
|
||||
|
||||
// * Should move focus to terms link after tab
|
||||
await pw.loginPage.footer.privacyPolicyLink.press('Tab');
|
||||
expect(await pw.loginPage.footer.termsLink).toBeFocused();
|
||||
|
||||
// * Should move focus to help link after tab
|
||||
await pw.loginPage.footer.termsLink.press('Tab');
|
||||
expect(await pw.loginPage.footer.helpLink).toBeFocused();
|
||||
|
||||
// # Move focus to login input
|
||||
await pw.loginPage.loginInput.focus();
|
||||
expect(await pw.loginPage.loginInput).toBeFocused();
|
||||
|
||||
// * Should move focus to login body after shift+tab
|
||||
await pw.loginPage.loginInput.press('Shift+Tab');
|
||||
expect(await pw.loginPage.createAccountLink).toBeFocused();
|
||||
|
||||
// * Should move focus to login body after tab
|
||||
await pw.loginPage.createAccountLink.press('Shift+Tab');
|
||||
expect(await pw.loginPage.header.logo).toBeFocused();
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
test('/reset_password accessibility quick check', async ({pw, axe}) => {
|
||||
// Set up the page not to redirect to the landing page
|
||||
await pw.hasSeenLandingPage();
|
||||
|
||||
// # Go to reset password page
|
||||
await pw.resetPasswordPage.goto();
|
||||
await pw.resetPasswordPage.toBeVisible();
|
||||
|
||||
// # Analyze the page
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(pw.resetPasswordPage.page, {disableColorContrast: true})
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('/reset_password accessibility tab support', async ({pw}) => {
|
||||
// Set up the page not to redirect to the landing page
|
||||
await pw.hasSeenLandingPage();
|
||||
|
||||
// # Go to reset password page
|
||||
await pw.resetPasswordPage.goto();
|
||||
await pw.resetPasswordPage.toBeVisible();
|
||||
|
||||
// * Should have focused at email input on page load
|
||||
expect(await pw.resetPasswordPage.emailInput).toBeFocused();
|
||||
|
||||
// * Should move focus to reset button after tab
|
||||
await pw.resetPasswordPage.emailInput.press('Tab');
|
||||
expect(await pw.resetPasswordPage.resetButton).toBeFocused();
|
||||
|
||||
// * Should move focus to about link after tab
|
||||
await pw.resetPasswordPage.resetButton.press('Tab');
|
||||
expect(await pw.resetPasswordPage.footer.aboutLink).toBeFocused();
|
||||
|
||||
// * Should move focus to privacy policy link after tab
|
||||
await pw.resetPasswordPage.footer.aboutLink.press('Tab');
|
||||
expect(await pw.resetPasswordPage.footer.privacyPolicyLink).toBeFocused();
|
||||
|
||||
// * Should move focus to terms link after tab
|
||||
await pw.resetPasswordPage.footer.privacyPolicyLink.press('Tab');
|
||||
expect(await pw.resetPasswordPage.footer.termsLink).toBeFocused();
|
||||
|
||||
// * Should move focus to help link after tab
|
||||
await pw.resetPasswordPage.footer.termsLink.press('Tab');
|
||||
expect(await pw.resetPasswordPage.footer.helpLink).toBeFocused();
|
||||
|
||||
// # Move focus to email input
|
||||
await pw.resetPasswordPage.emailInput.focus();
|
||||
expect(await pw.resetPasswordPage.emailInput).toBeFocused();
|
||||
|
||||
// * Should move focus to back button after shift+tab
|
||||
await pw.resetPasswordPage.emailInput.press('Shift+Tab');
|
||||
expect(await pw.resetPasswordPage.header.backButton).toBeFocused();
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
test('/signup_user_complete accessibility quick check', async ({pw, axe}) => {
|
||||
// Set up the page not to redirect to the landing page
|
||||
await pw.hasSeenLandingPage();
|
||||
|
||||
// # Go to reset password page
|
||||
await pw.signupPage.goto();
|
||||
await pw.signupPage.toBeVisible();
|
||||
|
||||
// # Analyze the page
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(pw.signupPage.page, {disableColorContrast: true, disableLinkInTextBlock: true})
|
||||
.analyze();
|
||||
|
||||
// * Should have no violation
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('/signup_user_complete accessibility tab support', async ({pw}, testInfo) => {
|
||||
// Set up the page not to redirect to the landing page
|
||||
await pw.hasSeenLandingPage();
|
||||
|
||||
// # Go to reset password page
|
||||
await pw.signupPage.goto();
|
||||
await pw.signupPage.toBeVisible();
|
||||
|
||||
// * Should have focused at email input on page load
|
||||
expect(await pw.signupPage.emailInput).toBeFocused();
|
||||
|
||||
// * Should move focus to username input after tab
|
||||
await pw.signupPage.emailInput.press('Tab');
|
||||
expect(await pw.signupPage.usernameInput).toBeFocused();
|
||||
|
||||
// * Should move focus to password input after tab
|
||||
await pw.signupPage.usernameInput.press('Tab');
|
||||
expect(await pw.signupPage.passwordInput).toBeFocused();
|
||||
|
||||
// * Should move focus to password toggle button after tab
|
||||
await pw.signupPage.passwordInput.press('Tab');
|
||||
expect(await pw.signupPage.passwordToggleButton).toBeFocused();
|
||||
|
||||
// * Should move focus to terms and privacy checkbox after tab
|
||||
await pw.signupPage.passwordToggleButton.press('Tab');
|
||||
expect(await pw.signupPage.termsAndPrivacyCheckBox).toBeFocused();
|
||||
|
||||
// * Should move focus to acceptable use policy link after tab
|
||||
await pw.signupPage.termsAndPrivacyCheckBox.press('Tab');
|
||||
expect(await pw.signupPage.termsAndPrivacyAcceptableUsePolicyLink).toBeFocused();
|
||||
|
||||
// * Should move focus to privacy policy link after tab
|
||||
await pw.signupPage.termsAndPrivacyAcceptableUsePolicyLink.press('Tab');
|
||||
expect(await pw.signupPage.termsAndPrivacyPrivacyPolicyLink).toBeFocused();
|
||||
|
||||
// * Should move focus to about link after tab (skips disabled create account button)
|
||||
await pw.signupPage.termsAndPrivacyPrivacyPolicyLink.press('Tab');
|
||||
expect(await pw.signupPage.footer.aboutLink).toBeFocused();
|
||||
|
||||
// * Should move focus to privacy policy link after tab
|
||||
await pw.signupPage.footer.aboutLink.press('Tab');
|
||||
expect(await pw.signupPage.footer.privacyPolicyLink).toBeFocused();
|
||||
|
||||
// * Should move focus to terms link after tab
|
||||
await pw.signupPage.footer.privacyPolicyLink.press('Tab');
|
||||
expect(await pw.signupPage.footer.termsLink).toBeFocused();
|
||||
|
||||
// * Should move focus to help link after tab
|
||||
await pw.signupPage.footer.termsLink.press('Tab');
|
||||
expect(await pw.signupPage.footer.helpLink).toBeFocused();
|
||||
|
||||
// # Move focus to email input
|
||||
await pw.signupPage.emailInput.focus();
|
||||
expect(await pw.signupPage.emailInput).toBeFocused();
|
||||
|
||||
if (testInfo.project.name === 'ipad') {
|
||||
// * Should move focus to header back button after shift+tab
|
||||
await pw.signupPage.bodyCard.press('Shift+Tab');
|
||||
expect(await pw.signupPage.header.backButton).toBeFocused();
|
||||
|
||||
// * Should move focus to log in link after shift+tab
|
||||
await pw.signupPage.header.backButton.press('Shift+Tab');
|
||||
expect(await pw.signupPage.loginLink).toBeFocused();
|
||||
|
||||
// * Should move focus to header logo after shift+tab
|
||||
await pw.signupPage.loginLink.press('Shift+Tab');
|
||||
expect(await pw.signupPage.header.logo).toBeFocused();
|
||||
} else {
|
||||
// * Should move focus to log in link after shift+tab
|
||||
await pw.signupPage.bodyCard.press('Shift+Tab');
|
||||
expect(await pw.signupPage.loginLink).toBeFocused();
|
||||
|
||||
// * Should move focus to header back button after shift+tab
|
||||
await pw.signupPage.loginLink.press('Shift+Tab');
|
||||
expect(await pw.signupPage.header.backButton).toBeFocused();
|
||||
|
||||
// * Should move focus to header logo after shift+tab
|
||||
await pw.signupPage.header.backButton.press('Shift+Tab');
|
||||
expect(await pw.signupPage.header.logo).toBeFocused();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user