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

This commit is contained in:
upstream-mirror
2026-09-16 00:29:55 +05:00
commit 240db4e2a7
15309 changed files with 2173406 additions and 0 deletions

View File

@@ -0,0 +1,311 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @accessibility @mfa
import * as TIMEOUTS from '@/fixtures/timeouts';
import {isMac} from '@/utils';
describe('Verify Accessibility Support in different sections in Settings and Profile Dialog', () => {
const accountSettings = {
profile: [
{key: 'name', label: 'Full Name', type: 'text'},
{key: 'username', label: 'Username', type: 'text'},
{key: 'nickname', label: 'Nickname', type: 'text'},
{key: 'position', label: 'Position', type: 'text'},
{key: 'email', label: 'Email', type: 'text'},
{key: 'picture', label: 'Profile Picture', type: 'image'},
],
security: [
{key: 'password', label: 'Password', type: 'text'},
{key: 'mfa', label: 'Multi-factor Authentication', type: 'optional'},
],
};
const settings = {
notifications: [
{key: 'desktopAndMobile', label: 'Desktop and mobile notifications', type: 'radio'},
{key: 'desktopNotificationSound', label: 'Desktop notification sounds', type: 'radio'},
{key: 'email', label: 'Email notifications', type: 'radio'},
{key: 'channelMentionAutoFollow', label: 'Auto-follow threads on channel-wide mentions', type: 'radio'},
{key: 'keywordsAndMentions', label: 'Keywords that trigger notifications', type: 'checkbox'},
{key: 'keywordsAndHighlight', label: 'Keywords that get highlighted (without notifications)', type: 'checkbox'},
{key: 'replyNotifications', label: 'Reply notifications', type: 'radio'},
],
display: [
{key: 'theme', label: 'Theme', type: 'radio'},
{key: 'clock', label: 'Clock Display', type: 'radio'},
{key: 'name_format', label: 'Teammate Name Display', type: 'none'},
{key: 'availabilityStatus', label: 'Show online availability on profile images', type: 'radio'},
{key: 'lastactive', label: 'Share last active time', type: 'radio'},
{key: 'timezone', label: 'Timezone', type: 'none'},
{key: 'collapse', label: 'Default Appearance of Image Previews', type: 'radio'},
{key: 'message_display', label: 'Message Display', type: 'radio'},
{key: 'click_to_reply', label: 'Click to open threads', type: 'radio'},
{key: 'channel_display_mode', label: 'Channel Display', type: 'radio'},
{key: 'one_click_reactions_enabled', label: 'Quick reactions on messages', type: 'radio'},
{key: 'renderEmoticonsAsEmoji', label: 'Render emoticons as emojis', type: 'radio'},
{key: 'languages', label: 'Language', type: 'dropdown'},
],
sidebar: [
{key: 'showUnreadsCategory', label: 'Group unread channels separately', type: 'multiple'},
{key: 'limitVisibleGMsDMs', label: 'Number of direct messages to show', type: 'radio'},
],
advanced: [
{key: 'advancedCtrlSend', label: `Send Messages on ${isMac() ? '⌘+ENTER' : 'CTRL+ENTER'}`, type: 'radio'},
{key: 'wysiwygEditor', label: 'Rich text editing (Beta)', type: 'radio', optional: true},
{key: 'formatting', label: 'Enable Post Formatting', type: 'radio'},
{key: 'joinLeave', label: 'Enable Join/Leave Messages', type: 'radio'},
],
};
let url;
before(() => {
// # Update Configs
cy.apiUpdateConfig({
ServiceSettings: {
EnableMultifactorAuthentication: true,
},
SamlSettings: {
Enable: false,
},
});
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
url = offTopicUrl;
cy.visit(offTopicUrl);
cy.postMessage('hello');
});
});
afterEach(() => {
// # Close the modal
cy.uiClose();
});
it('MM-T1465_1 Verify Label & Tab behavior in section links', () => {
// * Verify aria-label and tab support in section of Account settings modal
cy.uiOpenProfileModal('Profile Settings');
cy.findByRole('tab', {name: 'profile settings'}).should('be.visible').focus().should('be.focused');
['profile settings', 'security'].forEach((text) => {
// * Verify aria-label on each tab and it supports navigating to the next tab with arrow keys
cy.focused().should('have.attr', 'aria-label', text).type('{downarrow}');
});
cy.uiClose();
// * Verify aria-label and tab support in section of Settings modal
cy.uiOpenSettingsModal();
cy.findByRole('tab', {name: 'notifications'}).should('be.visible').focus().should('be.focused');
['notifications', 'display', 'sidebar', 'advanced'].forEach((text) => {
// * Verify aria-label on each tab and it supports navigating to the next tab with arrow keys
cy.focused().should('have.attr', 'aria-label', text).type('{downarrow}');
});
});
it('MM-T1465_2 Verify Accessibility Support in each section in Settings and Profile Dialog', () => {
cy.visit(url);
cy.postMessage('hello');
// # Open profile settings modal
cy.uiOpenProfileModal('Profile Settings');
// * Verify if the focus goes to the individual fields in Profile section
cy.findByRole('tab', {name: 'profile settings'}).click().tab();
verifySettings(accountSettings.profile);
// * Verify if the focus goes to the individual fields in Security section
cy.findByRole('tab', {name: 'security'}).click().tab();
verifySettings(accountSettings.security);
cy.uiClose();
// # Open settings modal
cy.uiOpenSettingsModal();
// * Verify if the focus goes to the individual fields in Notifications section
cy.findByRole('tab', {name: 'notifications'}).click().tab();
cy.focused().should('have.text', 'Learn more about notifications').tab();
verifySettings(settings.notifications);
// // * Verify if the focus goes to the individual fields in Display section
cy.findByRole('tab', {name: 'display'}).click().tab();
verifySettings(settings.display);
// // * Verify if the focus goes to the individual fields in Sidebar section
cy.findByRole('tab', {name: 'sidebar'}).click().tab();
verifySettings(settings.sidebar);
// // * Verify if the focus goes to the individual fields in Advanced section
cy.findByRole('tab', {name: 'advanced'}).click().tab();
verifySettings(settings.advanced);
});
it('MM-T1482 Input fields in Settings and Profile should read labels', () => {
cy.visit(url);
cy.postMessage('hello');
cy.uiOpenProfileModal('Profile Settings');
accountSettings.profile.forEach((section) => {
if (section.type === 'text') {
cy.get(`#${section.key}Edit`).click();
cy.get('.setting-list-item .form-group').each(($el) => {
if ($el.find('input').length) {
cy.wrap($el).find('.control-label').invoke('text').then((label) => {
cy.wrap($el).find('input').should('have.attr', 'aria-label', label);
});
}
});
}
});
});
it('MM-T1485 Language dropdown should read labels', () => {
cy.visit(url);
cy.postMessage('hello');
cy.uiOpenSettingsModal();
cy.get('#displayButton').click();
cy.get('#languagesEdit').click();
cy.findByRole('combobox', {name: 'Dropdown selector to change the interface language'}).should('have.attr', 'aria-autocomplete', 'list').and('have.attr', 'aria-labelledby', 'changeInterfaceLanguageLabel').as('inputEl');
cy.get('#changeInterfaceLanguageLabel').should('be.visible').and('have.text', 'Change interface language');
// # When space key is pressed on dropdown, it should expand and should collapse when esc key is pressed
cy.get('@inputEl').typeWithForce(' ');
cy.findByRole('listbox').should('have.class', 'react-select__menu-list').as('listBox');
cy.get('@inputEl').typeWithForce('{esc}');
cy.get('@listBox').should('not.exist');
// # Press down arrow twice and check aria label
cy.get('@inputEl').typeWithForce(' ');
cy.get('@inputEl').typeWithForce('{downarrow}{downarrow}');
cy.get('#displayLanguage').within(($el) => {
cy.wrap($el).findByRole('log').should('have.attr', 'aria-live', 'assertive').as('ariaEl');
});
cy.get('@ariaEl').within(($el) => {
cy.wrap($el).get('#aria-focused').should('contain', 'option English (Australia) focused');
cy.wrap($el).get('#aria-guidance').should('contain', 'Use Up and Down to choose options, press Enter to select the currently focused option, press Escape to exit the menu, press Tab to select the option and exit the menu.');
});
// # Check if language setting gets changed after user presses space
cy.get('@inputEl').typeWithForce(' ');
cy.get('#displayLanguage').should('contain', 'English (Australia)');
cy.get('@ariaEl').within(($el) => {
cy.wrap($el).get('#aria-selection').should('contain', 'option English (Australia) selected');
});
// # Press down arrow, then up arrow and press space
cy.get('@inputEl').typeWithForce('{downarrow}{downarrow}{downarrow}{uparrow}');
cy.get('@ariaEl').within(($el) => {
cy.wrap($el).get('#aria-focused').should('contain', 'option English (US) focused');
});
cy.get('@inputEl').typeWithForce(' ');
cy.get('#displayLanguage').should('contain', 'English (US)');
cy.get('@ariaEl').within(($el) => {
cy.wrap($el).get('#aria-selection').should('contain', 'option English (US) selected');
});
});
it('MM-T1488 Profile Picture should read labels', () => {
cy.visit(url);
cy.postMessage('hello');
// # Go to Edit Profile picture
cy.uiOpenProfileModal('Profile Settings');
cy.get('#pictureEdit').click();
// * Verify image alt in profile image
cy.get('.profile-img').should('have.attr', 'alt', 'profile image');
cy.get('#profileSettings').then((el) => {
if (el.find('.profile-img__remove').length > 0) {
cy.findByTestId('removeSettingPicture').click();
cy.uiSave();
cy.get('#pictureEdit').click();
}
});
// * Check Labels in different buttons
cy.findByTestId('inputSettingPictureButton').should('have.attr', 'aria-label', 'Select');
cy.uiSaveButton().should('have.attr', 'disabled');
cy.findByTestId('cancelSettingPicture').should('have.attr', 'aria-label', 'Cancel');
// # Upload a pic and save
cy.findByTestId('uploadPicture').attachFile('mattermost-icon.png');
cy.uiSave();
// # Click on Edit Profile Picture
cy.get('#pictureEdit').click();
// * Verify image alt in profile image
cy.get('.profile-img').should('have.attr', 'alt', 'profile image');
// # Option to Remove Profile picture should be present
cy.findByTestId('removeSettingPicture').within(() => {
cy.get('.sr-only').should('have.text', 'Remove Profile Picture');
});
// # Check tab behavior
cy.findByTestId('removeSettingPicture').focus().tab({shift: true}).tab();
cy.findByTestId('removeSettingPicture').should('have.class', 'a11y--active a11y--focused').tab();
cy.findByTestId('inputSettingPictureButton').should('have.class', 'a11y--active a11y--focused').tab();
cy.findByTestId('cancelSettingPicture').should('have.class', 'a11y--active a11y--focused');
// # Remove the profile picture and check the tab behavior
cy.findByTestId('removeSettingPicture').click().wait(TIMEOUTS.HALF_SEC);
cy.findByTestId('inputSettingPictureButton').focus().tab({shift: true}).tab();
cy.findByTestId('inputSettingPictureButton').should('have.class', 'a11y--active a11y--focused').tab();
cy.uiSaveButton().should('be.focused').tab();
cy.findByTestId('cancelSettingPicture').should('have.class', 'a11y--active a11y--focused');
cy.uiSave();
});
it('MM-T1496 Security Settings screen should read labels', () => {
cy.visit(url);
cy.postMessage('hello');
// # Go to Security Settings
cy.uiOpenProfileModal('Profile Settings');
cy.get('#securityButton').click();
// * Check Tab behavior in MFA section
cy.get('#mfaEdit').click();
cy.get('#passwordEdit').focus().tab({shift: true}).tab().tab();
cy.get('.setting-list button.btn').should('have.class', 'a11y--active a11y--focused').tab();
cy.get('#cancelSetting').should('have.class', 'a11y--active a11y--focused');
// * Check Tab behavior in Sign-In Method if its available
cy.get('.user-settings').then((el) => {
if (el.find('#signinEdit').length) {
cy.get('#signinEdit').click();
cy.get('#mfaEdit').focus().tab({shift: true}).tab().tab();
cy.get('.setting-list a.btn').should('have.class', 'a11y--active a11y--focused').tab();
cy.get('#cancelSetting').should('have.class', 'a11y--active a11y--focused');
}
});
});
});
function verifySettings(settings) {
settings.forEach((setting) => {
if (setting.optional) {
cy.get('body').then(($body) => {
if ($body.find(`#${setting.key}Edit`).length === 0) {
return;
}
cy.focused().should('have.id', `${setting.key}Edit`);
cy.findByText(setting.label);
cy.focused().tab();
});
return;
}
cy.focused().should('have.id', `${setting.key}Edit`);
cy.findByText(setting.label);
cy.focused().tab();
});
}

View File

@@ -0,0 +1,84 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @accessibility
describe('Verify Accessibility Support in different Buttons', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
// # Post a message
cy.postMessage('hello');
cy.getLastPostId().then((postId) => {
// # On a message in center channel, click then pin the post to the channel
cy.uiClickPostDropdownMenu(postId, 'Pin to Channel');
// # Click pin icon next to search box
cy.uiGetChannelPinButton().click();
});
});
});
it('MM-T1459 Accessibility Support in RHS expand and close icons', () => {
cy.clickPostCommentIcon();
cy.get('#rhsContainer').
should('be.visible').
within(() => {
// * Verify accessibility support in Sidebar Expand and Shrink icon
cy.get('button.sidebar--right__expand').
should('have.attr', 'aria-label', 'Expand Sidebar Icon');
// * Verify accessibility support in Close icon
cy.get('#rhsCloseButton').
should('have.attr', 'aria-label', 'Close').
within(() => {
cy.get('.icon-close').should('have.attr', 'aria-label', 'Close Sidebar Icon');
});
// # Close the sidebar
cy.get('#rhsCloseButton').click();
});
});
it('MM-T1461 Accessibility Support in different buttons in Channel Header', () => {
// # Ensure the focus is on the Toggle Favorites button
cy.uiGetChannelFavoriteButton().
focus().
tab({shift: true}).
tab();
// * Verify accessibility support in Favorites button
cy.uiGetChannelFavoriteButton().
should('be.focused').
and('have.attr', 'aria-label', 'add to favorites').
click();
// * Verify accessibility support if Channel is added to Favorites
cy.uiGetChannelFavoriteButton().
should('have.attr', 'aria-label', 'remove from favorites');
// # Ensure the focus is on the Toggle Favorites button
cy.uiGetChannelFavoriteButton().
focus().
tab({shift: true}).
tab();
// # Press tab until the focus is on the Pinned messages button
cy.focused().tab().tab().tab();
// * Verify accessibility support in Pinned messages button
cy.uiGetChannelPinButton().
should('be.focused').
and('have.attr', 'aria-label', 'Pinned messages').
tab().tab().tab().tab();
});
});

View File

@@ -0,0 +1,189 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Group: @channels @accessibility
describe('Verify Accessibility Support in Dropdown Menus', () => {
let offTopicUrl;
before(() => {
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
cy.apiLogin(sysadmin);
cy.apiInitSetup().then(({offTopicUrl: url}) => {
offTopicUrl = url;
});
cy.apiCreateTeam('other-team', 'Other Team');
});
});
beforeEach(() => {
// Visit the Off Topic channel
cy.visit(offTopicUrl);
cy.postMessage('hello');
});
it('MM-T1464 Accessibility Support in Channel Menu Dropdown', () => {
// # Press tab from the Channel Favorite button
cy.uiGetChannelFavoriteButton().
focus().
tab({shift: true}).
tab().
tab({shift: true});
// * Verify the aria-label in channel menu button
cy.uiGetChannelHeaderButton().
findByLabelText('channel menu').
should('be.focused').click();
// * Verify the accessibility support in the Channel Dropdown menu
cy.uiGetChannelMenu().
parent().
should('have.attr', 'aria-label', 'channel menu').
and('have.attr', 'role', 'menu');
// * Verify the first option is not selected by default
cy.uiGetChannelMenu().children().eq(0).should('not.be.focused');
// # Press tab
cy.focused().tab();
// * Verify the accessibility support in the Channel Dropdown menu items
const menuItems = [
'View Info',
'Move to...',
'Notification Preferences',
'Mute Channel',
'Add Members',
'Manage Members',
'Edit Channel Header',
'Edit Channel Purpose',
'Rename Channel',
'Convert to Private Channel',
'Archive Channel',
'Leave Channel',
];
menuItems.forEach((item) => {
// * Verify that the menu item is focused
cy.uiGetChannelMenu().findByText(item).parent().should('be.focused');
// # Press tab for next item
cy.focused().tab();
});
// * Verify if menu is closed when we press Escape
cy.get('body').typeWithForce('{esc}');
cy.uiGetChannelMenu({exist: false});
});
it('MM-T1476 Accessibility Support in Team Menu Dropdown', () => {
// # Open team menu
cy.uiGetLHSHeader().click();
// * Verify the accessibility support in the Main Menu Dropdown
cy.findByRole('menu').
should('exist').
and('have.attr', 'aria-label', 'team menu').
and('have.class', 'a11y__popup');
// * Verify the first option is not selected by default
cy.uiGetLHSTeamMenu().find('.MenuItem').
children().eq(0).
should('not.be.focused').
focus();
// * Verify the accessibility support in the Main Menu Dropdown items
const menuItems = [
{id: 'invitePeople', label: 'Invite People dialog'},
{id: 'teamSettings', label: 'Team Settings dialog'},
{id: 'manageMembers', label: 'Manage Members dialog'},
{id: 'joinTeam', text: 'Join another team'},
{id: 'leaveTeam', label: 'Leave Team dialog'},
{id: 'createTeam', text: 'Create a team'},
];
menuItems.forEach((item) => {
// * Verify that the menu item is focused
if (item.label) {
cy.focused().should('have.attr', 'aria-label', item.label);
} else {
cy.focused().should('have.text', item.text);
}
// # Press tab for next item
cy.focused().tab();
});
cy.uiGetLHSTeamMenu().find('.MenuItem').each((el) => {
cy.wrap(el).should('have.attr', 'role', 'menuitem');
});
// * Verify if menu is closed when we press Escape
cy.get('body').typeWithForce('{esc}');
cy.uiGetLHSTeamMenu().should('not.exist');
});
it('MM-T1477 Accessibility Support in Status Dropdown - KNOWN ISSUE: MM-45716', () => {
// # Focus to set status button
cy.uiGetSetStatusButton().focus().tab({shift: true}).tab();
// * Verify the aria-label in status menu button
cy.uiGetSetStatusButton().
should('be.focused').
click();
// * Verify the accessibility support in the Status Dropdown
cy.uiGetStatusMenuContainer().
should('have.attr', 'aria-label', 'set status').
and('have.class', 'a11y__popup').
and('have.attr', 'role', 'menu');
// * Verify the first option is not selected by default
cy.uiGetStatusMenuContainer().find('.dropdown-menu').children().eq(0).should('not.be.focused');
// # Press tab
cy.focused().tab();
// * Verify first focus is on menu header which is the profile image
cy.uiGetStatusMenuContainer().within(() => {
cy.findByAltText('user profile image').should('be.focused');
});
// # Press tab
cy.focused().tab();
// * Verify the accessibility support in the Status Dropdown menu items
const menuItems = [
{id: 'status-menu-custom-status', label: 'Set a Custom Status dialog'},
{id: 'status-menu-online', label: 'online'},
{id: 'status-menu-away', label: 'away'},
{id: 'status-menu-dnd_menuitem', label: 'do not disturb. disables all notifications'},
{id: 'status-menu-offline', label: 'offline'},
{id: 'accountSettings', label: 'Profile dialog'},
{id: 'logout', label: 'Log Out'},
];
menuItems.forEach((item) => {
// * Verify that the menu item is focused
cy.uiGetStatusMenuContainer().find(`#${item.id}`).
should('be.visible').
findAllByLabelText(item.label).first().
should('be.focused');
// # Press tab for next item
cy.focused().tab();
});
// * Verify if menu is closed when we press Escape
cy.get('body').typeWithForce('{esc}');
cy.uiGetStatusMenuContainer({exist: false});
});
});

View File

@@ -0,0 +1,85 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @accessibility
// * Verify the accessibility support in the different images
describe('Verify Accessibility Support in Different Images', () => {
let otherUser;
before(() => {
cy.apiInitSetup().then(({team, offTopicUrl, user}) => {
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
otherUser = user1;
return cy.apiAddUserToTeam(team.id, otherUser.id);
}).then(() => {
cy.apiLogin(user);
// Visit the Off Topic channel
cy.visit(offTopicUrl);
});
});
});
it('MM-T1508 Accessibility support in different images', () => {
// * Verify image alt in profile image
cy.get('#userAccountMenuButton').within(() => {
cy.findByAltText('user profile image').should('be.visible');
});
// # Upload an image in the post
cy.get('#fileUploadInput').attachFile('small-image.png');
cy.postMessage('Image upload');
// * Verify accessibility in images uploaded in a post
cy.getLastPostId().then((postId) => {
cy.get(`#${postId}_message`).within(() => {
cy.get('img').should('be.visible').should('have.attr', 'aria-label', 'file thumbnail small-image.png');
});
});
// # Post a message as a different user
cy.getCurrentChannelId().then((channelId) => {
const message = `hello from ${otherUser.username}: ${Date.now()}`;
cy.postMessageAs({sender: otherUser, message, channelId});
});
// # Open profile popover
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within(() => {
cy.get('.status-wrapper').click();
});
// * Verify image alt in profile popover
cy.get('.user-profile-popover').within(() => {
cy.get('.Avatar').should('have.attr', 'alt', `${otherUser.username} profile image`);
});
});
// # Close the profile popover
cy.get('body').click();
// # Open Settings > Display > Themes
cy.uiOpenSettingsModal('Display').within(() => {
cy.get('#displayButton').click();
cy.get('#displaySettingsTitle').should('exist');
cy.get('#themeTitle').scrollIntoView().should('be.visible');
cy.get('#themeEdit').click();
// * Verify image alt in Theme Images
cy.get('#displaySettings').within(() => {
cy.get('.appearance-section>div').children().each(($el) => {
cy.wrap($el).get('#denim-theme-icon').should('have.text', 'Denim theme icon');
});
});
});
});
});

View File

@@ -0,0 +1,166 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Group: @channels @accessibility
import {getRandomId} from '@/utils';
function postMessages(testChannel, otherUser, count) {
for (let index = 0; index < count; index++) {
// # Post Message as Current user
const message = `hello from current user: ${getRandomId()}`;
cy.postMessage(message);
const otherMessage = `hello from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message: otherMessage, channelId: testChannel.id});
}
}
describe('Verify Accessibility keyboard usability across different regions in the app', () => {
const count = 5;
let testUser;
let otherUser;
let testChannel;
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
testUser = user;
testChannel = channel;
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(team.id, otherUser.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, otherUser.id).then(() => {
// # Login as test user, visit test channel and post few messages
cy.apiLogin(user);
cy.visit(`/${team.name}/channels/${testChannel.name}`);
});
});
});
});
});
it('MM-T1513_2 Verify Keyboard support in Search Results', () => {
// # Post few messages
postMessages(testChannel, otherUser, count);
// # Search for a term
cy.uiGetSearchContainer().click();
cy.uiGetSearchBox().typeWithForce('hello').typeWithForce('{enter}');
// # Change the focus to search results
cy.get('#searchContainer').within(() => {
cy.get('button.sidebar--right__expand').focus().tab({shift: true}).tab();
// # focus the messages and files tabs
cy.focused().tab().tab();
});
// # verify the tabs buttons files or messages are accesible via left and right keys
cy.get('body').type('{rightarrow}').wait(100);
cy.get('#filesTab').should('have.class', 'a11y--active a11y--focused');
cy.get('body').type('{leftarrow}').wait(100);
cy.get('#messagesTab').should('have.class', 'a11y--active a11y--focused');
// # move focus to the team selector
cy.focused().tab();
// * Ensures the focus item has test-id of searchTeamsSelectorMenuButton
cy.get('#searchTeamsSelectorMenuButton').should('have.class', 'a11y--active a11y--focused');
// # move focus to search results items
cy.focused().tab();
cy.get('body').type('{downarrow}{uparrow}');
// # Use down arrow keys and verify if results are highlighted sequentially
for (let index = 0; index < count; index++) {
cy.get('.files-or-messages-panel').children('.search-item__container').eq(index).then(($el) => {
// * Verify search result is highlighted
cy.get($el).find('.post').should('have.class', 'a11y--active a11y--focused');
cy.get('body').type('{downarrow}');
});
}
// # Use up arrow keys and verify if results are highlighted sequentially
for (let index = count; index > 0; index--) {
cy.get('.files-or-messages-panel').children('.search-item__container').eq(index).then(($el) => {
// * Verify search result is highlighted
cy.get($el).find('.post').should('have.class', 'a11y--active a11y--focused');
cy.get('body').type('{uparrow}');
});
}
});
it('MM-T1513_1 Verify Keyboard support in RHS', () => {
// # Post Message as Current user
const message = `hello from ${testUser.username}: ${getRandomId()}`;
cy.postMessage(message);
// # Post few replies on RHS
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
cy.get('#rhsContainer').should('be.visible');
for (let index = 0; index < count; index++) {
const replyMessage = `A reply ${getRandomId()}`;
cy.postMessageReplyInRHS(replyMessage);
const messageFromOther = `reply from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message: messageFromOther, channelId: testChannel.id, rootId: postId});
}
});
// * Verify that the highlight order is in the reverse direction in RHS
cy.get('#rhsContainer .post-right__content').should('have.attr', 'data-a11y-order-reversed', 'true').and('have.attr', 'data-a11y-focus-child', 'true');
// # Change the focus to the last post
cy.get('#rhsContainer').within(() => {
cy.uiGetReplyTextBox().focus().tab({shift: true});
});
cy.get('body').type('{uparrow}{downarrow}');
// # Use up arrow keys and verify if results are highlighted sequentially
const total = (count * 2) + 1; // # total number of expected posts in RHS
let row = total - 1; // # the row index which should be focused
for (let index = count; index > 0; index--) {
cy.get('#rhsContainer .post-right-comments-container .post').eq(row).then(($el) => {
// * Verify search result is highlighted
cy.get($el).should('have.class', 'a11y--active a11y--focused');
cy.get('body').type('{uparrow}');
});
row--;
}
// # Use down arrow keys and verify if posts are highlighted sequentially
for (let index = count; index > 0; index--) {
cy.get('#rhsContainer .post-right-comments-container .post').eq(row).then(($el) => {
// * Verify search result is highlighted
cy.get($el).should('have.class', 'a11y--active a11y--focused');
cy.get('body').type('{downarrow}');
});
row++;
}
});
it('MM-T1499 Verify Screen reader should not switch to virtual cursor mode', () => {
// # Open RHS
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
// * Verify Screen reader should not switch to virtual cursor mode. This is handled by adding a role=application attribute
const regions = ['#sidebar-left', '#rhsContainer .post-right__content', '#advancedTextEditorCell'];
regions.forEach((region) => {
cy.get(region).should('have.attr', 'role', 'application');
});
cy.get('.search__form').should('have.attr', 'role', 'search');
});
});
});

View File

@@ -0,0 +1,141 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @accessibility
import {getRandomId} from '@/utils';
function postMessages(testChannel, otherUser, count) {
for (let index = 0; index < count; index++) {
// # Post Message as Current user
const message = `hello from current user: ${getRandomId()}`;
cy.postMessage(message);
const otherMessage = `hello from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message: otherMessage, channelId: testChannel.id});
}
}
function verifyNavSupport(element, label, tabOrder) {
cy.get(element).
should('have.attr', 'aria-label', label).
and('have.attr', 'data-a11y-sort-order', tabOrder).
and('have.class', 'a11y__region a11y--active');
}
describe('Verify Quick Navigation support across different regions in the app', () => {
let otherUser;
let testChannel;
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
testChannel = channel;
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(team.id, otherUser.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, otherUser.id).then(() => {
// # Login as test user, visit test channel and post few messages
cy.apiLogin(user);
cy.visit(`/${team.name}/channels/${testChannel.name}`);
// # Post few messages
postMessages(testChannel, otherUser, 5);
});
});
});
});
});
it('MM-T1460_1 Verify Navigation Support in Post List & Post Input', () => {
// # Shift focus to the last post
cy.uiGetPostTextBox().focus().tab({shift: true}).tab({shift: true});
cy.get('body').type('{uparrow}{downarrow}');
// * Verify post region reads out correctly
verifyNavSupport('#virtualizedPostListContent > div', 'message list main region', '1');
// # Shift focus to the post input
cy.uiGetPostTextBox().focus().tab().tab({shift: true});
// * Verify post input region reads out correctly
verifyNavSupport('#advancedTextEditorCell', 'message input complementary region', '2');
});
it('MM-T1460_3 Verify Navigation Support in RHS Post List & RHS Post Input', () => {
// # Open RHS and reply
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
const replyMessage = 'A reply to an older post';
cy.postMessageReplyInRHS(replyMessage);
});
// * Verify post message in RHS
cy.uiGetRHS().within(() => {
// # Shift the focus to the last post
cy.uiGetReplyTextBox().focus().tab({shift: true}).type('{uparrow}');
// * Verify post region on RHS reads out correctly
verifyNavSupport('.post-right__content', 'message details complementary region', '3');
// # Shift the focus to the RHS input
cy.uiGetReplyTextBox().focus().tab().tab({shift: true});
// * Verify post input on RHS reads out correctly
cy.get('#advancedTextEditorCell').
should('have.attr', 'aria-label', 'message input complementary region').
and('have.attr', 'data-a11y-sort-order', '2').
and('have.class', 'a11y__region');
cy.uiGetReplyTextBox().
should('have.class', 'a11y--active a11y--focused');
});
});
it('MM-T1460_5 Verify Navigation Support in LHS Sidebar', () => {
// # Change the focus to Main Menu button
cy.uiGetLHSAddChannelButton().focus().tab({shift: true}).tab();
// # Change the focus to the LHS sidebar
cy.focused().tab();
// * Verify nav support in LHS channel navigator
verifyNavSupport('#lhsNavigator', 'channel navigator region', '6');
// # Change the focus to the LHS sidebar
cy.focused().tab().tab().tab().tab();
// * Verify nav support in LHS sidebar
verifyNavSupport('#sidebar-left', 'channel sidebar region', '7');
});
it('MM-T1460_6 Verify Navigation Support in Channel Header', () => {
// # Change the focus to Main Menu button
cy.get('#toggleFavorite').focus().tab({shift: true}).tab();
// * Verify nav support in LHS sidebar header
verifyNavSupport('#channel-header', 'channel header region', '8');
});
it('MM-T1460_8 Verify Navigation Support in Search Results', () => {
// # Search for some text
cy.uiGetSearchContainer().click();
cy.uiGetSearchBox().should('be.visible').type('hello {enter}');
// # Change the focus to search results
cy.get('#searchContainer').within(() => {
cy.get('button.sidebar--right__expand').focus().tab({shift: true}).tab();
cy.focused().tab().tab().tab().tab();
});
cy.get('body').type('{downarrow}{uparrow}');
// * Verify nav support in Search Results Container
verifyNavSupport('#search-items-container', 'Search Results complementary region', '3');
});
});

View File

@@ -0,0 +1,86 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @accessibility
let previousEmoji = 'grinning';
function verifyArrowKeysEmojiNavigation(arrowKey, count) {
for (let index = 0; index < count; index++) {
cy.get('body').type(arrowKey);
cy.get('.emoji-picker__preview-name').invoke('text').then((selectedEmoji) => {
expect(selectedEmoji).not.equal(previousEmoji);
previousEmoji = selectedEmoji;
});
}
}
describe('Verify Accessibility Support in Popovers', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
// # Post a message
cy.postMessage(`hello from test user: ${Date.now()}`);
});
});
it('MM-T1489 Accessibility Support in Emoji Popover on click of Emoji Reaction button', () => {
cy.getLastPostId().then((postId) => {
// # Open the Emoji Popover
cy.clickPostReactionIcon(postId);
// * Verify accessibility support in Emoji Search input
cy.get('#emojiPickerSearch').should('have.attr', 'aria-label', 'Search for an emoji');
// # Focus on the first emoji Category
cy.get('#emojiPickerCategories').children().eq(0).focus().tab({shift: true}).tab();
const categories = [
{ariaLabel: 'Smileys & Emotion', header: 'Smileys & Emotion'},
{ariaLabel: 'People & Body', header: 'People & Body'},
{ariaLabel: 'Animals & Nature', header: 'Animals & Nature'},
{ariaLabel: 'Food & Drink', header: 'Food & Drink'},
{ariaLabel: 'Travel & Places', header: 'Travel & Places'},
{ariaLabel: 'Activities', header: 'Activities'},
{ariaLabel: 'Objects', header: 'Objects'},
{ariaLabel: 'Symbols', header: 'Symbols'},
{ariaLabel: 'Flags', header: 'Flags'},
];
// * Verify if emoji Categories gets the focus when tab is pressed
cy.get('#emojiPickerCategories').children('.emoji-picker__category').each(($el, index) => {
// * Verify each category
if (index < categories.length) {
// * Verify accessibility support in emoji category
cy.get($el).should('have.class', 'a11y--active a11y--focused').should('have.attr', 'aria-label', categories[index].ariaLabel);
// * Verify if corresponding section is displayed when emoji category has focus and clicked
cy.get($el).trigger('click').tab();
// * Verify if corresponding section is displayed
cy.findByText(categories[index].header).should('be.visible');
// * Verify emoji navigation using arrow keys
verifyArrowKeysEmojiNavigation('{rightarrow}', 3);
verifyArrowKeysEmojiNavigation('{leftarrow}', 2);
// # Press tab
cy.get($el).focus().tab();
}
});
// # Close the Emoji Popover
cy.get('body').click();
});
});
});

View File

@@ -0,0 +1,343 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @accessibility
import * as TIMEOUTS from '@/fixtures/timeouts';
import {getRandomId} from '@/utils';
describe('Verify Accessibility Support in Post', () => {
let testUser;
let otherUser;
let testTeam;
let testChannel;
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
testUser = user;
testTeam = team;
testChannel = channel;
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, otherUser.id);
});
});
});
});
beforeEach(() => {
// # Login as test user and visit the test channel
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.get('#postListContent', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
});
// To be addressed in MM-70414
it.skip('MM-T1479 Verify Reader reads out the post correctly on Center Channel', () => {
const {lastMessage} = postMessages(testChannel, otherUser, 1);
performActionsToLastPost();
// # Shift focus to the last post
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true});
cy.get('body').type('{uparrow}{downarrow}');
// * Verify post message in Center Channel
cy.getLastPostId().then((postId) => {
// * Verify reader reads out the post correctly
verifyPostLabel(`#post_${postId}`, otherUser.username, `wrote, ${lastMessage}, 2 reactions, message is saved and pinned`);
});
});
it('MM-T1480 Verify Reader reads out the post correctly on RHS', () => {
const {lastMessage} = postMessages(testChannel, otherUser, 1);
performActionsToLastPost();
// # Post a reply on RHS
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
cy.get('#rhsContainer').should('be.visible');
const replyMessage = 'A reply to an older post';
cy.postMessageReplyInRHS(replyMessage);
// * Verify post message in RHS
cy.get('#rhsContainer').within(() => {
// # Shift the focus to the last post
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true}).type('{uparrow}');
// * Verify reader reads out the post correctly
verifyPostLabel(`#rhsPost_${postId}`, otherUser.username, `wrote, ${lastMessage}, 2 reactions, message is saved and pinned`);
});
// * Verify reply message in RHS
cy.getLastPostId().then((replyId) => {
cy.get('#rhsContainer').within(() => {
// # Shift the focus to the last reply message
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true}).type('{uparrow}{downarrow}');
// * Verify reader reads out the post correctly
verifyPostLabel(`#rhsPost_${replyId}`, testUser.username, `replied, ${replyMessage}`);
});
});
});
});
it('MM-T1486_1 Verify different Post Focus on Center Channel', () => {
postMessages(testChannel, otherUser, 5);
// # Shift focus to the last post
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true}).type('{uparrow}');
// * Verify if focus changes to different posts when we use up arrows
for (let index = 1; index < 5; index++) {
cy.getNthPostId(-index - 1).then((postId) => {
cy.get(`#post_${postId}`).should('be.focused');
cy.get('body').type('{uparrow}');
});
}
// * Verify if focus changes to different posts when we use down arrows
for (let index = 5; index > 0; index--) {
cy.getNthPostId(-index - 1).then((postId) => {
cy.get(`#post_${postId}`).should('be.focused');
cy.get('body').type('{downarrow}');
});
}
});
it('MM-T1486_2 Verify different Post Focus on RHS', () => {
// # Post Message as Current user
const message = `hello from current user: ${getRandomId()}`;
cy.postMessage(message);
// # Post few replies on RHS
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
cy.get('#rhsContainer').should('be.visible');
for (let index = 0; index < 3; index++) {
const replyMessage = `A reply ${getRandomId()}`;
cy.postMessageReplyInRHS(replyMessage);
const otherMessage = `reply from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message: otherMessage, channelId: testChannel.id, rootId: postId});
}
});
cy.get('#rhsContainer').within(() => {
// # Shift focus to the last post
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true}).type('{uparrow}');
});
// * Verify if focus changes to different posts when we use up arrows
for (let index = 1; index < 5; index++) {
cy.getNthPostId(-index - 1).then((postId) => {
cy.get(`#rhsPost_${postId}`).should('be.focused');
cy.get('body').type('{uparrow}');
});
}
// * Verify if focus changes to different posts when we use down arrows
for (let index = 5; index > 1; index--) {
cy.getNthPostId(-index - 1).then((postId) => {
cy.get(`#rhsPost_${postId}`).should('be.focused');
cy.get('body').type('{downarrow}');
});
}
});
it('MM-T1486_3 Verify Tab support on Post on Center Channel', () => {
postMessages(testChannel, otherUser, 1);
// # Shift focus to the last post
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true}).tab({shift: true}).tab({shift: true});
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within(() => {
// * Verify focus is on profile image
cy.get('button.status-wrapper').first().should('be.focused');
cy.focused().tab();
// * Verify focus is on the username
cy.get('button.user-popover').should('be.focused');
cy.focused().tab();
// * Verify focus is on the time
cy.get(`#CENTER_time_${postId}`).should('be.focused');
cy.focused().tab();
for (let i = 0; i < 3; i++) {
// * Verify focus is on the reactions button
cy.get(`#recent_reaction_${i}`).should('have.class', 'emoticon--post-menu').and('have.attr', 'aria-label');
cy.focused().tab();
}
// * Verify focus is on the reactions button
cy.get(`#CENTER_reaction_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'Add Reaction');
cy.focused().tab();
// * Verify focus is on the save post button
cy.get(`#CENTER_flagIcon_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'save message');
cy.focused().tab();
// * Verify focus is on the actions button
cy.get(`#CENTER_actions_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'actions');
cy.focused().tab();
// * Verify focus is on the comment button
cy.get(`#CENTER_commentIcon_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'reply');
cy.focused().tab();
// * Verify focus is on the more button
cy.get(`#CENTER_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'more');
cy.focused().tab();
});
});
});
it('MM-T1486_4 Verify Tab support on Post on RHS', () => {
// # Post Message as Current user
const message = `hello from current user: ${getRandomId()}`;
cy.postMessage(message);
// # Post few replies on RHS
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
cy.get('#rhsContainer').should('be.visible');
const replyMessage = `A reply ${getRandomId()}`;
cy.postMessageReplyInRHS(replyMessage);
const otherMessage = `reply from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message: otherMessage, channelId: testChannel.id, rootId: postId});
});
cy.get('#rhsContainer').within(() => {
// # Shift focus to the last post
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true});
});
// * Verify reverse tab on RHS
cy.getLastPostId().then((postId) => {
cy.get(`#rhsPost_${postId}`).within(() => {
// * Verify focus is on the time
cy.get(`#RHS_COMMENT_time_${postId}`).should('be.focused');
cy.focused().tab({shift: true});
// * Verify focus is on the username
cy.get('button.user-popover').should('be.focused');
cy.focused().tab().tab();
// * Verify focus is on most recent action
cy.get('#recent_reaction_0').should('have.class', 'emoticon--post-menu').and('have.attr', 'aria-label');
cy.focused().tab();
// * Verify focus is on the reactions button
cy.get(`#RHS_COMMENT_reaction_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'Add Reaction');
cy.focused().tab();
// * Verify focus is on the save icon
cy.get(`#RHS_COMMENT_flagIcon_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'save message');
cy.focused().tab();
// * Verify focus is on the actions button
cy.get(`#RHS_COMMENT_actions_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'actions');
cy.focused().tab();
// * Verify focus is on the more button
cy.get(`#RHS_COMMENT_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'more');
cy.focused().tab({shift: true});
});
});
});
it('MM-T1462 Verify incoming messages are read', () => {
// # Make channel as read by switching back and forth to testChannel
cy.uiGetLhsSection('CHANNELS').findByText('Off-Topic').click();
cy.get('#postListContent', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
cy.uiGetLhsSection('CHANNELS').findByText(testChannel.display_name).click();
cy.get('#postListContent', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
// # Submit a post as another user
const message = `verify incoming message from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message, channelId: testChannel.id});
cy.uiWaitUntilMessagePostedIncludes(message);
// # Get the element which stores the incoming messages
cy.get('#postListContent').within(() => {
cy.get('.sr-only').should('have.attr', 'aria-live', 'polite').as('incomingMessage');
});
// * Verify incoming message is read
cy.get('@incomingMessage').invoke('text').then((text) => {
expect(text).contain(message);
});
});
});
function postMessages(testChannel, otherUser, count) {
let lastMessage;
for (let index = 0; index < count; index++) {
// # Post Message as Current user
const message = `hello from current user: ${getRandomId()}`;
cy.postMessage(message);
lastMessage = `hello from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message: lastMessage, channelId: testChannel.id});
}
return {lastMessage};
}
function performActionsToLastPost() {
// # Take some actions on the last post
cy.getLastPostId().then((postId) => {
// # Add grinning reaction
cy.clickPostReactionIcon(postId);
cy.findAllByTestId('grinning').first().trigger('mouseover', {force: true});
cy.get('.sprite-preview').should('be.visible');
cy.get('.emoji-picker__preview').should('be.visible').and('have.text', ':grinning:');
cy.findAllByTestId('grinning').first().click({force: true});
cy.get(`#postReaction-${postId}-grinning`).should('be.visible');
// # Add smile reaction
cy.clickPostReactionIcon(postId);
cy.findAllByTestId('smile').first().trigger('mouseover', {force: true});
cy.get('.sprite-preview').should('be.visible');
cy.get('.emoji-picker__preview').should('be.visible').and('have.text', ':smile:');
cy.findAllByTestId('smile').first().click({force: true});
cy.get(`#postReaction-${postId}-smile`).should('be.visible');
// # Save the post
cy.clickPostSaveIcon(postId);
// # Pin the post
cy.clickPostDotMenu(postId);
cy.get(`#pin_post_${postId}`).click();
cy.clickPostDotMenu(postId);
cy.get('body').type('{esc}');
});
}
function verifyPostLabel(elementId, username, labelSuffix) {
// # Shift focus to the last post
cy.get(elementId).as('lastPost').should('be.focused');
// * Verify reader reads out the post correctly
cy.get('@lastPost').then((el) => {
// # Get the post time
cy.wrap(el).find('time.post__time').invoke('attr', 'datetime').then((time) => {
const parsedTime = Cypress.dayjs(time);
const expectedLabel = `At ${parsedTime.format('h:mm A dddd, MMMM D')}, ${username} ${labelSuffix}`;
cy.wrap(el).should('have.attr', 'aria-label', expectedLabel);
});
});
}

View File

@@ -0,0 +1,105 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Group: @channels @accessibility @smoke
import * as TIMEOUTS from '@/fixtures/timeouts';
describe('Verify Accessibility Support in Channel Sidebar Navigation', () => {
let testUser;
let testTeam;
let testChannel;
let offTopicUrl;
before(() => {
let otherUser;
let otherChannel;
cy.apiInitSetup({promoteNewUserAsAdmin: true}).then(({team, channel, user, offTopicUrl: url}) => {
testUser = user;
testTeam = team;
testChannel = channel;
offTopicUrl = url;
cy.apiCreateUser().then(({user: regularUser}) => {
cy.apiAddUserToTeam(testTeam.id, regularUser.id);
});
return cy.apiCreateChannel(testTeam.id, 'test', 'Test');
}).then(({channel}) => {
otherChannel = channel;
return cy.apiAddUserToChannel(otherChannel.id, testUser.id);
}).then(() => {
return cy.apiCreateUser({prefix: 'other'});
}).then(({user}) => {
otherUser = user;
return cy.apiAddUserToTeam(testTeam.id, otherUser.id);
}).then(() => {
return cy.apiAddUserToChannel(testChannel.id, otherUser.id);
}).then(() => {
return cy.apiAddUserToChannel(otherChannel.id, otherUser.id);
}).then(() => {
// # Post messages to have unread messages to test user
for (let index = 0; index < 5; index++) {
cy.postMessageAs({sender: otherUser, message: 'This is an old message', channelId: otherChannel.id});
}
// # Login as test user and visit the off-topic channel
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
});
});
it('MM-T1472 Verify Tab Support in Direct Messages section', () => {
const usersPrefixes = ['a', 'c', 'd'];
usersPrefixes.forEach((prefix) => {
//# Create users with prefixes in alphabetical order
cy.apiCreateUser({prefix}).then(({user: newUser}) => {
cy.apiCreateDirectChannel([testUser.id, newUser.id]).then(({channel}) => {
// # Post message in The DM channel
cy.postMessageAs({sender: newUser, message: 'test', channelId: channel.id});
});
});
});
// # Trigger DM with a user
cy.uiAddDirectMessage().click();
searchAndSelectUser('user');
searchAndSelectUser('user');
cy.uiGetButton('Go').click();
cy.wait(TIMEOUTS.TWO_SEC);
// # Press tab to the Create DM button and verify if the Plus button has focus
cy.uiAddDirectMessage().
tab({shift: true}).tab().
should('be.focused').
tab({shift: true}).tab({shift: true});
cy.focused().parent().parent().next().find('.SidebarChannel').each((el, i) => {
if (i === 0) {
cy.focused().findByText('DIRECT MESSAGES');
cy.focused().tab().tab().tab();
}
// * Verify if focus changes to different channels in Direct Messages section
cy.wrap(el).find('.SidebarLink').should('be.focused');
cy.focused().tab().tab().tab();
});
});
});
function searchAndSelectUser(text) {
cy.findByRole('textbox', {name: 'Search for people'}).
typeWithForce(text).
wait(TIMEOUTS.ONE_SEC);
cy.get('.more-modal__row.clickable').
first().
click();
}

View File

@@ -0,0 +1,255 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @accessibility @smoke
import * as TIMEOUTS from '@/fixtures/timeouts';
describe('Verify Accessibility Support in Channel Sidebar Navigation', () => {
let testUser: Cypress.UserProfile;
let testTeam: Cypress.Team;
let testChannel: Cypress.Channel;
let offTopicUrl: string;
before(() => {
let otherUser: Cypress.UserProfile;
let otherChannel: Cypress.Channel;
cy.apiInitSetup({promoteNewUserAsAdmin: true}).then(({team, channel, user, offTopicUrl: url}) => {
testUser = user;
testTeam = team;
testChannel = channel;
offTopicUrl = url;
return cy.apiCreateChannel(testTeam.id, 'test', 'Test');
}).then(({channel}) => {
otherChannel = channel;
return cy.apiAddUserToChannel(otherChannel.id, testUser.id);
}).then(() => {
return cy.apiCreateUser({prefix: 'other'});
}).then(({user}) => {
otherUser = user;
return cy.apiAddUserToTeam(testTeam.id, otherUser.id);
}).then(() => {
return cy.apiAddUserToChannel(testChannel.id, otherUser.id);
}).then(() => {
return cy.apiAddUserToChannel(otherChannel.id, otherUser.id);
}).then(() => {
// # Post messages to have unread messages to test user
for (let index = 0; index < 5; index++) {
cy.postMessageAs({sender: otherUser, message: 'This is an old message', channelId: otherChannel.id});
}
});
});
beforeEach(() => {
// # Login as test user and visit the off-topic channel
cy.apiLogin(testUser);
(cy as any).apiSaveSidebarSettingPreference();
cy.visit(offTopicUrl);
cy.get('#postListContent').should('be.visible');
});
it('MM-T1470 Verify Tab Support in Channels section', () => {
// # Create some Public Channels
Cypress._.times(2, () => {
cy.apiCreateChannel(testTeam.id, 'public', 'public');
});
// # Wait for few seconds
cy.wait(TIMEOUTS.ONE_SEC);
// # Press tab to the Add Public Channel button
cy.uiGetLHSAddChannelButton().focus().tab().tab({shift: true});
// * Verify if the Plus button has focus
cy.uiGetLHSAddChannelButton().should('be.focused');
cy.focused().tab();
// * Verify if the Plus button has focus
cy.findByRole('button', {name: 'Find Channels'}).should('be.focused');
cy.focused().tab();
// * Verify if focus changes to different channels in Unread section
cy.get('.SidebarChannel.unread').each((el) => {
cy.wrap(el).find('.unread-title').should('be.focused');
cy.focused().tab().tab();
});
cy.focused().parent().next().find('.SidebarChannel').each((el, i) => {
if (i === 0) {
cy.focused().findByText('CHANNELS');
cy.focused().tab().tab();
}
// * Verify if focus changes to different channels in Channels section
cy.wrap(el).find('.SidebarLink').should('be.focused');
cy.focused().tab().tab();
});
});
it('MM-T1473 Verify Tab Support in Unreads section', () => {
// # Press tab from the Main Menu button
cy.uiGetLHSAddChannelButton().focus().tab().tab();
// * Verify if focus changes to different channels in Unread section
cy.get('.SidebarChannel.unread').each((el) => {
cy.wrap(el).find('.unread-title').should('be.focused');
cy.focused().tab().tab();
});
});
it('MM-T1474 Verify Tab Support in Favorites section', () => {
// # Mark few channels as Favorites
markAsFavorite('off-topic');
markAsFavorite(testChannel.name);
// # Press tab from the add channel button down to all unread channels
cy.uiGetLHSAddChannelButton().focus().tab().tab();
cy.get('.SidebarChannel.unread').each(() => {
cy.focused().tab().tab();
});
// * Verify if focus changes to different channels in Favorite Channels section
cy.focused().tab().tab().parent().next().find('.SidebarChannel').each((el, i) => {
if (i === 0) {
cy.focused().findByText('FAVORITES');
cy.focused().tab().tab();
}
cy.wrap(el).find('.SidebarLink').should('be.focused');
cy.focused().tab().tab();
});
});
it('MM-T1475 Verify Up & Down Arrow support in Channel Sidebar', () => {
cy.apiCreateChannel(testTeam.id, 'public', 'Public', 'O');
cy.apiCreateChannel(testTeam.id, 'private', 'Private', 'P');
// # Mark few channels as Favorites
markAsFavorite('off-topic');
markAsFavorite(testChannel.name);
// # Press tab from the Main Menu button
cy.uiGetLHSAddChannelButton().focus().tab().tab().tab();
// # Press Down Arrow and then Up Arrow
cy.get('body').type('{downarrow}{uparrow}');
// * Verify if Unread Channels section has focus
cy.uiGetLhsSection('UNREADS').should('be.focused');
// # Press Down Arrow and check the focus
cy.get('body').type('{downarrow}');
// * Verify if Favorite Channels section has focus
cy.uiGetLhsSection('FAVORITES').should('be.focused');
// # Press Down Arrow and check the focus
cy.get('body').type('{downarrow}');
// * Verify if Public Channels section has focus
cy.uiGetLhsSection('CHANNELS').should('be.focused');
// # Press Down Arrow and check the focus
cy.get('body').type('{downarrow}');
// * Verify if Direct Messages section has focus
cy.uiGetLhsSection('DIRECT MESSAGES').should('be.focused');
});
});
describe('Accessibility tests for RHS getting focus after buttons actions', () => {
let testUser;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
// # Login as the test user and visit the town-square channel
cy.apiLogin(testUser);
cy.visit(`/${team.name}/channels/town-square`);
cy.get('#postListContent').should('be.visible');
});
});
beforeEach(() => {
// # Close the RHS
cy.uiCloseRHS();
cy.get('#sidebar-right').should('not.exist');
});
afterEach(() => {
// # Close the RHS
cy.uiCloseRHS();
cy.get('#sidebar-right').should('not.exist');
});
it('Focus should be on RHS when opening Recent Mentions', () => {
// # Enter key press on the Recent Mentions button
cy.findByRole('button', {name: /Recent mentions/i}).focus().realPress('Enter');
// * Verify RHS is open
cy.get('#sidebar-right').should('be.visible');
// * Check that the RHS container is focused
cy.findByLabelText('Expand Sidebar Icon').should('be.focused');
});
it('Focus should be on RHS when opening Saved Messages', () => {
// # Enter key press on the Saved Messages button
cy.findByRole('button', {name: /Saved messages/i}).focus().realPress('Enter');
// * Verify RHS is open
cy.get('#sidebar-right').should('be.visible');
// * Check that the RHS container is focused
cy.findByLabelText('Expand Sidebar Icon').should('be.focused');
});
it('Focus should be on RHS when opening Members', () => {
cy.get('#channelHeaderInfo').should('exist');
// # Enter key press on the Members button
cy.get('#member_rhs').should('be.visible').focus().realPress('Enter');
// * Verify RHS is open
cy.get('#sidebar-right').should('be.visible');
// * Check that the RHS container is focused
cy.get('.sidebar-right-container').find('#rhsCloseButton').should('be.focused');
});
it('Focus should be on RHS when opening Channel files', () => {
// # Enter key press on the Channel files button
cy.findByRole('button', {name: /Channel files/i}).focus().realPress('Enter');
// * Verify RHS is open
cy.get('#sidebar-right').should('be.visible');
// * Check that the RHS container is focused
cy.findByLabelText('Expand Sidebar Icon').should('be.focused');
});
});
function markAsFavorite(channelName: string) {
// # Visit the channel
cy.get(`#sidebarItem_${channelName}`).click();
cy.get('#postListContent').should('be.visible');
// # Remove from Favorites if already set
cy.get('#channelHeaderInfo').then((el) => {
if (el.find('#toggleFavorite.active').length) {
cy.get('#toggleFavorite').click();
}
});
// # mark it as Favorite
cy.get('#toggleFavorite').click();
}