From 93a2c3281ad896a84fc2a3fd6a52708a765a7dd9 Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Fri, 28 Jul 2023 11:17:11 -0400 Subject: [PATCH] MM-53813: Fix(accessibility): on channels page (#24122) * fix(accessibility): on channels page * fix lint and update dynamic-virtualized-list * fix snapshot and update per feedback * fix e2e tests * fix test --------- Co-authored-by: Mattermost Build --- e2e-tests/cypress/.eslintrc.json | 6 +++ .../accessibility_keyboard_usability_spec.js | 3 +- .../accessibility/accessibility_post_spec.js | 4 +- .../channel_sidebar/dm_category_spec.ts | 41 +++++++++---------- .../guest_identification_spec.ts | 2 +- .../guest_invitation_ui_spec.ts | 8 ++-- .../enterprise/guest_accounts/helpers.ts | 2 +- .../member_invitation_ui_spec.ts | 6 +-- .../enterprise/ldap_group/invite_bot_spec.ts | 2 +- .../interactive_dialog/full_dialog_spec.js | 4 +- .../slack_parsing_message_button_spec.js | 12 +++++- ..._l_does_not_change_focus_to_msgbox_spec.js | 2 +- ...validate_pending_email_invitations_spec.js | 2 +- .../closed_team_invite_by_email_spec.js | 2 +- .../channels/team_settings/helpers.js | 2 +- .../team_settings/invite_members_spec.js | 2 +- .../invite_user_to_closed_team_spec.js | 2 +- .../channels/team_settings/teams_spec.js | 2 +- .../playbooks/channels/broadcast_spec.js | 6 +-- e2e-tests/cypress/tests/support/ui/team.js | 2 +- e2e-tests/playwright/support/test_fixture.ts | 24 ++++------- .../channels/intro_channel.spec.ts | 26 ++++++++++++ .../tests/accessibility/common/login.spec.ts | 2 +- webapp/channels/package.json | 2 +- .../searchable_channel_list.test.tsx.snap | 2 +- .../channel_header.test.tsx.snap | 16 -------- .../channel_header/channel_header.tsx | 1 - .../invitation_modal/invite_view.tsx | 2 +- .../src/components/post/post_component.tsx | 1 - .../post_edit_history.test.tsx.snap | 1 - .../post_priority_picker_overlay.tsx | 12 +++--- .../post_list_virtualized.tsx | 3 -- .../post_message_view.test.tsx.snap | 5 --- .../post_message_view/post_message_view.tsx | 1 - .../__snapshots__/search_bar.test.tsx.snap | 10 ++--- .../src/components/search_bar/search_bar.tsx | 2 +- .../components/searchable_channel_list.tsx | 2 +- .../add_channels_cta_button.test.tsx.snap | 8 ++-- .../invite_members_button.test.tsx.snap | 16 ++++---- .../sidebar/add_channels_cta_button.tsx | 4 +- .../sidebar/invite_members_button.tsx | 8 ++-- .../sidebar/sidebar_category_header.tsx | 10 ++++- webapp/channels/src/i18n/en.json | 1 - .../__snapshots__/post_type.test.jsx.snap | 1 - .../src/sass/layout/_sidebar-left.scss | 4 +- webapp/package-lock.json | 14 +++---- 46 files changed, 149 insertions(+), 141 deletions(-) create mode 100644 e2e-tests/playwright/tests/accessibility/channels/intro_channel.spec.ts diff --git a/e2e-tests/cypress/.eslintrc.json b/e2e-tests/cypress/.eslintrc.json index 2d84a2ee5c..feffdb5cb4 100644 --- a/e2e-tests/cypress/.eslintrc.json +++ b/e2e-tests/cypress/.eslintrc.json @@ -15,6 +15,12 @@ "env": { "cypress/globals": true }, + "settings": { + "react": { + "pragma": "React", + "version": "detect" + } + }, "rules": { "header/header": [ 2, diff --git a/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_keyboard_usability_spec.js b/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_keyboard_usability_spec.js index 1c3052edf4..9aa70288e9 100644 --- a/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_keyboard_usability_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_keyboard_usability_spec.js @@ -137,10 +137,11 @@ describe('Verify Accessibility keyboard usability across different regions in th 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', '.search__form', '#advancedTextEditorCell']; + 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'); cy.get(`#post_${postId}`).children('.post__content').eq(0).should('have.attr', 'role', 'application'); cy.get(`#rhsPost_${postId}`).children('.post__content').eq(0).should('have.attr', 'role', 'application'); }); diff --git a/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_post_spec.js b/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_post_spec.js index 3ecf92ca94..b3cf28dc06 100644 --- a/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_post_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/accessibility/accessibility_post_spec.js @@ -201,7 +201,7 @@ describe('Verify Accessibility Support in Post', () => { cy.focused().tab(); // * Verify focus is on the post text - cy.get(`#postMessageText_${postId}`).should('be.focused').and('have.attr', 'aria-readonly', 'true'); + cy.get(`#postMessageText_${postId}`).should('be.focused'); }); }); }); @@ -231,7 +231,7 @@ describe('Verify Accessibility Support in Post', () => { cy.getLastPostId().then((postId) => { cy.get(`#rhsPost_${postId}`).within(() => { // * Verify focus is on the post text - cy.get(`#rhsPostMessageText_${postId}`).should('be.focused').and('have.attr', 'aria-readonly', 'true'); + cy.get(`#rhsPostMessageText_${postId}`).should('be.focused'); cy.focused().tab({shift: true}); // * Verify focus is on the more button diff --git a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/dm_category_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/dm_category_spec.ts index 6b52fca362..12cf407685 100644 --- a/e2e-tests/cypress/tests/integration/channels/channel_sidebar/dm_category_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/channel_sidebar/dm_category_spec.ts @@ -24,6 +24,20 @@ describe('MM-T3156 DM category', () => { cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true}).then(({team, user}) => { testUser = user; cy.visit(`/${team.name}/channels/town-square`); + + const usersPrefixes = ['a', 'c', 'd', 'j', 'p', 'u', 'x', 'z']; + 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}); + + // add usernames in array for reference + usernames.push(newUser.username); + }); + }); + }); }); }); @@ -36,20 +50,6 @@ describe('MM-T3156 DM category', () => { }); it('MM-T3156_2 should order DMs based on recent interactions', () => { - const usersPrefixes = ['a', 'c', 'd', 'j', 'p', 'u', 'x', 'z']; - 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}); - - // add usernames in array for reference - usernames.push(newUser.username); - }); - }); - }); - // get DM category group cy.findByLabelText('DIRECT MESSAGES').parents('.SidebarChannelGroup').within(() => { const usernamesReversed = [...usernames].reverse(); @@ -68,15 +68,14 @@ describe('MM-T3156 DM category', () => { // # Change sorting to be alphabetical cy.findByText('Sort').trigger('mouseover'); - cy.findByText('Alphabetically').click(); + cy.findByText('Alphabetically').click().wait(TIMEOUTS.ONE_SEC); - cy.findByLabelText('DIRECT MESSAGES').should('be.visible'). - parents('.SidebarChannelGroup').within(() => { - cy.get('.NavGroupContent').children().each(($el, index) => { - // * Verify that the usernames are in alphabetical order - cy.wrap($el).findByText(usernames[index]).should('be.visible'); - }); + cy.findByLabelText('DIRECT MESSAGES').parents('.SidebarChannelGroup').within(() => { + cy.get('.NavGroupContent').children().each(($el, index) => { + // * Verify that the usernames are in alphabetical order + cy.wrap($el).findByText(usernames[index]).should('be.visible'); }); + }); }); it('MM-T3156_4 should not be able to rearrange DMs', () => { diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_identification_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_identification_spec.ts index ed4cf9dd24..2057a66a55 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_identification_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_identification_spec.ts @@ -119,7 +119,7 @@ describe('Guest Accounts', () => { children().should('have.length', 1). eq(0).should('contain', testChannel.name).click(); - cy.get('#inviteGuestButton').scrollIntoView().click(); + cy.findByTestId('inviteButton').scrollIntoView().click(); cy.findByTestId('confirm-done').should('be.visible').click(); // # Get invitation link. diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_invitation_ui_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_invitation_ui_spec.ts index 396869cd8e..a1acc16f2e 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_invitation_ui_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/guest_invitation_ui_spec.ts @@ -65,7 +65,7 @@ describe('Guest Account - Guest User Invitation Flow', () => { }); // * Verify Invite Guests button is disabled by default - cy.get('#inviteGuestButton').scrollIntoView().should('be.visible').and('be.disabled'); + cy.findByTestId('inviteButton').scrollIntoView().should('be.visible').and('be.disabled'); // * Verify Invite People field const email = `temp-${getRandomId()}@mattermost.com`; @@ -141,7 +141,7 @@ describe('Guest Account - Guest User Invitation Flow', () => { invitePeople(email, 1, email, 'Town Square', false); // * Verify Invite Guests button is disabled - cy.get('#inviteGuestButton').should('be.disabled'); + cy.findByTestId('inviteButton').should('be.disabled'); }); it('MM-T4449 Invite Guest via Email containing upper case letters', () => { @@ -177,7 +177,7 @@ describe('Guest Account - Guest User Invitation Flow', () => { children().should('have.length', 1).eq(0).should('contain', newUser.username).click(); // # Click Invite Members - cy.get('#inviteMembersButton').scrollIntoView().click(); + cy.findByTestId('inviteButton').scrollIntoView().click(); // * Verify the content and error message in the Invitation Modal cy.findByTestId('invitationModal').within(() => { @@ -218,7 +218,7 @@ describe('Guest Account - Guest User Invitation Flow', () => { cy.get('.users-emails-input__menu').children().should('have.length', 1).eq(0).should('contain', email).click(); // # Click Invite Guests Button - cy.get('#inviteGuestButton').scrollIntoView().click(); + cy.findByTestId('inviteButton').scrollIntoView().click(); // * Verify invite more button is present cy.findByTestId('invite-more').should('be.visible'); diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/helpers.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/helpers.ts index ad1c41ab85..b4bb5a2674 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/helpers.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/helpers.ts @@ -38,7 +38,7 @@ export function invitePeople(typeText: string, resultsCount: number, verifyText: if (clickInvite) { // # Click Invite Guests Button - cy.get('#inviteGuestButton').scrollIntoView().click(); + cy.findByTestId('inviteButton').scrollIntoView().click(); } } diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/member_invitation_ui_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/member_invitation_ui_spec.ts index 5c21131d04..3b64ec9396 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/member_invitation_ui_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/guest_accounts/member_invitation_ui_spec.ts @@ -69,7 +69,7 @@ describe('Guest Account - Member Invitation Flow', () => { cy.get('@clipboard').its('contents').should('eq', `${baseUrl}/signup_user_complete/?id=${testTeam.invite_id}`); - cy.get('#inviteMembersButton').scrollIntoView().should('be.visible').and('be.disabled'); + cy.findByTestId('inviteButton').scrollIntoView().should('be.visible').and('be.disabled'); cy.get('.users-emails-input__control').should('be.visible').within(() => { // * Verify the input placeholder text cy.get('.users-emails-input__placeholder').should('have.text', 'Enter a name or email address'); @@ -224,7 +224,7 @@ describe('Guest Account - Member Invitation Flow', () => { }); // # Click Invite Members - cy.get('#inviteMembersButton').scrollIntoView().click(); + cy.findByTestId('inviteButton').scrollIntoView().click(); // * Verify the content and message in the Invitation Modal cy.findByTestId('invitationModal').within(() => { @@ -267,7 +267,7 @@ function invitePeople(typeText, resultsCount, verifyText, clickInvite = true) { // # Click Invite Members if (clickInvite) { - cy.get('#inviteMembersButton').scrollIntoView().click(); + cy.findByTestId('inviteButton').scrollIntoView().click(); } } diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/ldap_group/invite_bot_spec.ts b/e2e-tests/cypress/tests/integration/channels/enterprise/ldap_group/invite_bot_spec.ts index ab11433cfd..8bffb1172c 100644 --- a/e2e-tests/cypress/tests/integration/channels/enterprise/ldap_group/invite_bot_spec.ts +++ b/e2e-tests/cypress/tests/integration/channels/enterprise/ldap_group/invite_bot_spec.ts @@ -66,7 +66,7 @@ describe('Group Synced Team - Bot invitation flow', () => { click(); // # Invite the bot - cy.get('#inviteMembersButton').click(); + cy.findByTestId('inviteButton').click(); // * Ensure that the response message was not an error cy.get('.InviteResultRow').find('.reason').should('not.contain', 'Error'); diff --git a/e2e-tests/cypress/tests/integration/channels/interactive_dialog/full_dialog_spec.js b/e2e-tests/cypress/tests/integration/channels/interactive_dialog/full_dialog_spec.js index e5a1e32309..b58f44f74d 100644 --- a/e2e-tests/cypress/tests/integration/channels/interactive_dialog/full_dialog_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/interactive_dialog/full_dialog_spec.js @@ -18,7 +18,7 @@ const webhookUtils = require('../../../../utils/webhook_utils'); describe('Interactive Dialog', () => { const inputTypes = { - realname: 'input', + realname: 'text', someemail: 'email', somenumber: 'number', somepassword: 'password', @@ -101,7 +101,7 @@ describe('Interactive Dialog', () => { cy.wrap($elForm).find('#suggestionList').scrollIntoView().should('be.visible'); // # Click field label to close any opened drop-downs - cy.wrap($elForm).find('label.control-label').scrollIntoView().click(); + cy.wrap($elForm).find('label.control-label').scrollIntoView().click({force: true}); } else if (element.name === 'someradiooptions') { cy.wrap($elForm).find('input').should('be.visible').and('have.length', optionsLength[element.name]); diff --git a/e2e-tests/cypress/tests/integration/channels/interactive_menu/slack_parsing_message_button_spec.js b/e2e-tests/cypress/tests/integration/channels/interactive_menu/slack_parsing_message_button_spec.js index 799d032ea3..5085ab6a21 100644 --- a/e2e-tests/cypress/tests/integration/channels/interactive_menu/slack_parsing_message_button_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/interactive_menu/slack_parsing_message_button_spec.js @@ -44,21 +44,29 @@ describe('Interactive Menu', () => { // # Post an incoming webhook cy.postIncomingWebhook({url: incomingWebhook.url, data: payload, waitFor: 'attachment-pretext'}); + cy.waitUntil(() => cy.findAllByTestId('postContent').then((el) => { + if (el.length > 0) { + return el[1].innerText.includes(payload.attachments[0].actions[0].name); + } + return false; + })); // # Click on "Skip Parsing" button - cy.findByText('Skip Parsing').should('be.visible').click({force: true}); + cy.findByText(payload.attachments[0].actions[0].name).should('be.visible').click({force: true}); cy.wait(TIMEOUTS.HALF_SEC); // * Verify that it renders original "spoiler" text + cy.uiWaitUntilMessagePostedIncludes('a < a | b > a'); cy.getLastPostId().then((postId) => { cy.get(`#postMessageText_${postId}`).should('have.html', '

a < a | b > a

'); }); // # Click on "Do Parsing" button - cy.findByText('Do Parsing').should('be.visible').click({force: true}); + cy.findByText(payload.attachments[0].actions[1].name).should('be.visible').click({force: true}); cy.wait(TIMEOUTS.HALF_SEC); // * Verify that it renders markdown with link + cy.uiWaitUntilMessagePostedIncludes('a b a'); cy.getLastPostId().then((postId) => { cy.get(`#postMessageText_${postId}`).should('have.html', '

a b a

'); }); diff --git a/e2e-tests/cypress/tests/integration/channels/keyboard_shortcuts/ctrl_cmd_shift_l_does_not_change_focus_to_msgbox_spec.js b/e2e-tests/cypress/tests/integration/channels/keyboard_shortcuts/ctrl_cmd_shift_l_does_not_change_focus_to_msgbox_spec.js index 1b785f3fb1..fc1afe51db 100644 --- a/e2e-tests/cypress/tests/integration/channels/keyboard_shortcuts/ctrl_cmd_shift_l_does_not_change_focus_to_msgbox_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/keyboard_shortcuts/ctrl_cmd_shift_l_does_not_change_focus_to_msgbox_spec.js @@ -32,7 +32,7 @@ describe('Keyboard Shortcuts', () => { cy.uiClose(); // # Open invite members full-page screen - cy.get('#introTextInvite').click(); + cy.findByLabelText('Invite Users').click(); // # Press ctrl/cmd+shift+l cy.get('body').cmdOrCtrlShortcut('{shift+l}'); diff --git a/e2e-tests/cypress/tests/integration/channels/onboarding/invalidate_pending_email_invitations_spec.js b/e2e-tests/cypress/tests/integration/channels/onboarding/invalidate_pending_email_invitations_spec.js index 46cf7b224e..29206155ac 100644 --- a/e2e-tests/cypress/tests/integration/channels/onboarding/invalidate_pending_email_invitations_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/onboarding/invalidate_pending_email_invitations_spec.js @@ -103,6 +103,6 @@ describe('Onboarding', () => { cy.findByRole('textbox', {name: 'Add or Invite People'}). typeWithForce(email).wait(TIMEOUTS.HALF_SEC). typeWithForce('{enter}'); - cy.get('#inviteMembersButton').click(); + cy.findByTestId('inviteButton').click(); } }); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/closed_team_invite_by_email_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/closed_team_invite_by_email_spec.js index ddaa36945d..2e1c148b05 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/closed_team_invite_by_email_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/closed_team_invite_by_email_spec.js @@ -83,7 +83,7 @@ describe('Team Settings', () => { } cy.findByRole('textbox', {name: 'Add or Invite People'}).type(email, {force: true}).wait(TIMEOUTS.HALF_SEC).type('{enter}', {force: true}); - cy.get('#inviteMembersButton').click(); + cy.findByTestId('inviteButton').click(); // # Wait for a while to ensure that email notification is sent and logout from sysadmin account cy.wait(TIMEOUTS.FIVE_SEC); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/helpers.js b/e2e-tests/cypress/tests/integration/channels/team_settings/helpers.js index 7cef607d78..8af3462cfe 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/helpers.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/helpers.js @@ -40,7 +40,7 @@ export const inviteUserByEmail = (email) => { typeWithForce(email). wait(TIMEOUTS.HALF_SEC). typeWithForce('{enter}'); - cy.get('#inviteMembersButton').click(); + cy.findByTestId('inviteButton').click(); // # Wait for a while to ensure that email notification is sent cy.wait(TIMEOUTS.TWO_SEC); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/invite_members_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/invite_members_spec.js index 29d3072971..9f4571d7f1 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/invite_members_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/invite_members_spec.js @@ -172,7 +172,7 @@ function inviteUser(user) { cy.get('.users-emails-input__menu').children().eq(0).should('contain', user.username).click(); // # Click Invite Members - cy.get('#inviteMembersButton').scrollIntoView().click(); + cy.findByTestId('inviteButton').scrollIntoView().click(); } function inviteUserToTeamAsMember(testUser, testTeam, user) { diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/invite_user_to_closed_team_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/invite_user_to_closed_team_spec.js index cff33f6517..19cbc1ceff 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/invite_user_to_closed_team_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/invite_user_to_closed_team_spec.js @@ -82,6 +82,6 @@ describe('Team Settings', () => { typeWithForce(email). wait(TIMEOUTS.HALF_SEC). typeWithForce('{enter}'); - cy.get('#inviteMembersButton').click(); + cy.findByTestId('inviteButton').click(); } }); diff --git a/e2e-tests/cypress/tests/integration/channels/team_settings/teams_spec.js b/e2e-tests/cypress/tests/integration/channels/team_settings/teams_spec.js index 211ecaa40a..02d53bcc52 100644 --- a/e2e-tests/cypress/tests/integration/channels/team_settings/teams_spec.js +++ b/e2e-tests/cypress/tests/integration/channels/team_settings/teams_spec.js @@ -93,7 +93,7 @@ describe('Teams Suite', () => { click(); // # Click "Invite Members" button, then "Done" button - cy.get('#inviteMembersButton').click(); + cy.findByTestId('inviteButton').click(); cy.findByTestId('confirm-done').click(); // * As sysadmin, verify system message posts in Town Square and Off-Topic diff --git a/e2e-tests/cypress/tests/integration/playbooks/channels/broadcast_spec.js b/e2e-tests/cypress/tests/integration/playbooks/channels/broadcast_spec.js index bdb21248a0..066ab67342 100644 --- a/e2e-tests/cypress/tests/integration/playbooks/channels/broadcast_spec.js +++ b/e2e-tests/cypress/tests/integration/playbooks/channels/broadcast_spec.js @@ -325,10 +325,10 @@ const verifyInitialAndStatusPostInBroadcast = (testTeam, channelName, runName, i should('exist'). within(() => { // * Thread should have two posts - cy.findAllByRole('listitem').should('have.length', 2); + cy.findAllByTestId('postContent').should('have.length', 2); // * The first should be announcement - cy.findAllByRole('listitem').eq(0).contains(initialMessage); + cy.findAllByTestId('postContent').eq(0).contains(initialMessage); // * Latest post should be update cy.get(`#rhsPost_${lastPostId}`).contains( @@ -354,7 +354,7 @@ const deleteLatestPostRoot = (testTeam, channelName) => { cy.get('#rhsContainer'). should('exist'). within(() => { - cy.findAllByRole('listitem').eq(0).then((root) => { + cy.findAllByTestId('postContent').eq(0).parent().then((root) => { const rootId = root.attr('id').slice(8); // # Click root's post dot menu. diff --git a/e2e-tests/cypress/tests/support/ui/team.js b/e2e-tests/cypress/tests/support/ui/team.js index bac28ae4d1..3354972f3c 100644 --- a/e2e-tests/cypress/tests/support/ui/team.js +++ b/e2e-tests/cypress/tests/support/ui/team.js @@ -14,7 +14,7 @@ Cypress.Commands.add('uiInviteMemberToCurrentTeam', (username) => { cy.get('.users-emails-input__control input').typeWithForce(username).as('input'); cy.get('.users-emails-input__option ').contains(`@${username}`); cy.get('@input').typeWithForce('{enter}'); - cy.get('#inviteMembersButton').click(); + cy.findByTestId('inviteButton').click(); // * Verify user invited to team cy.get('.invitation-modal-confirm--sent .InviteResultRow'). diff --git a/e2e-tests/playwright/support/test_fixture.ts b/e2e-tests/playwright/support/test_fixture.ts index 5cfb75815f..7d963933d8 100644 --- a/e2e-tests/playwright/support/test_fixture.ts +++ b/e2e-tests/playwright/support/test_fixture.ts @@ -19,8 +19,9 @@ type ExtendedFixtures = { }; export const test = base.extend({ - axe: async ({page}, use) => { - const ab = new AxeBuilderExtended(page); + // eslint-disable-next-line no-empty-pattern + axe: async ({}, use) => { + const ab = new AxeBuilderExtended(); await use(ab); }, pw: async ({browser, viewport}, use) => { @@ -93,24 +94,15 @@ class PlaywrightExtended { } class AxeBuilderExtended { - /** - * Each page should have its own Axe Builder to specifically list known issues - * which are to be excluded from being scanned until issues are fixed. - * Excluded element should have a corresponding ticket. - */ - - // '/login' - readonly loginPage: () => AxeBuilder; + readonly builder: (page: Page, disableRules?: string[]) => AxeBuilder; // See https://github.com/dequelabs/axe-core/blob/master/doc/API.md#axe-core-tags readonly tags: string[] = ['wcag2a', 'wcag2aa']; - // See https://github.com/dequelabs/axe-core/blob/master/doc/rule-descriptions.md#wcag-20-level-a--aa-rules - readonly disabledRules: string[] = []; - - constructor(page: Page) { - this.loginPage = () => { - return new AxeBuilder({page}).withTags(this.tags).disableRules(this.disabledRules); + constructor() { + // See https://github.com/dequelabs/axe-core/blob/master/doc/rule-descriptions.md#wcag-20-level-a--aa-rules + this.builder = (page: Page, disableRules?: string[]) => { + return new AxeBuilder({page}).withTags(this.tags).disableRules(disableRules || []); }; } diff --git a/e2e-tests/playwright/tests/accessibility/channels/intro_channel.spec.ts b/e2e-tests/playwright/tests/accessibility/channels/intro_channel.spec.ts new file mode 100644 index 0000000000..0dfac7cdaf --- /dev/null +++ b/e2e-tests/playwright/tests/accessibility/channels/intro_channel.spec.ts @@ -0,0 +1,26 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect, test} from '@e2e-support/test_fixture'; + +test('Intro to channel', async ({pw, pages, axe}) => { + // Create and sign in a new user + const {user} = await pw.initSetup(); + + // Log in a user in new browser context + const {page} = await pw.testBrowser.login(user); + + // Visit a default channel page + const channelsPage = new pages.ChannelsPage(page); + await channelsPage.goto(); + await channelsPage.toBeVisible(); + await channelsPage.postMessage('hello'); + await channelsPage.sendMessage(); + + // # Analyze the page + // Disable 'color-contrast' to be addressed by MM-53814 + const accessibilityScanResults = await axe.builder(page, ['color-contrast']).analyze(); + + // * Should have no violation + expect(accessibilityScanResults.violations).toHaveLength(0); +}); diff --git a/e2e-tests/playwright/tests/accessibility/common/login.spec.ts b/e2e-tests/playwright/tests/accessibility/common/login.spec.ts index 6957077ad1..2618783ed1 100644 --- a/e2e-tests/playwright/tests/accessibility/common/login.spec.ts +++ b/e2e-tests/playwright/tests/accessibility/common/login.spec.ts @@ -12,7 +12,7 @@ test('/login', async ({pw, pages, page, axe}) => { await loginPage.toBeVisible(); // # Analyze the page - const accessibilityScanResults = await axe.loginPage().analyze(); + const accessibilityScanResults = await axe.builder(loginPage.page).analyze(); // * Should have no violation expect(accessibilityScanResults.violations).toHaveLength(0); diff --git a/webapp/channels/package.json b/webapp/channels/package.json index 63901dfea6..65c7b4a6a3 100644 --- a/webapp/channels/package.json +++ b/webapp/channels/package.json @@ -32,7 +32,7 @@ "crypto-browserify": "3.12.0", "css-vars-ponyfill": "2.4.8", "date-fns": "2.29.3", - "dynamic-virtualized-list": "github:mattermost/dynamic-virtualized-list#1c330717d6778315a40e70137ace38247c6a1d0f", + "dynamic-virtualized-list": "github:mattermost/dynamic-virtualized-list#3fe918b41de4cb08dbf43f1207bb58827b38e833", "emoji-regex": "10.2.1", "exif2css": "1.3.0", "fast-deep-equal": "3.1.3", diff --git a/webapp/channels/src/components/__snapshots__/searchable_channel_list.test.tsx.snap b/webapp/channels/src/components/__snapshots__/searchable_channel_list.test.tsx.snap index 1118d16bf2..9b0dfcab83 100644 --- a/webapp/channels/src/components/__snapshots__/searchable_channel_list.test.tsx.snap +++ b/webapp/channels/src/components/__snapshots__/searchable_channel_list.test.tsx.snap @@ -65,7 +65,7 @@ exports[`components/SearchableChannelList should match init snapshot 1`] = `
@@ -380,7 +379,6 @@ exports[`components/ChannelHeader should match snapshot with no last active disp @@ -676,7 +674,6 @@ exports[`components/ChannelHeader should render active channel files 1`] = ` @@ -978,7 +975,6 @@ exports[`components/ChannelHeader should render active flagged posts 1`] = ` @@ -1280,7 +1276,6 @@ exports[`components/ChannelHeader should render active mentions posts 1`] = ` @@ -1582,7 +1577,6 @@ exports[`components/ChannelHeader should render active pinned posts 1`] = ` @@ -1887,7 +1881,6 @@ exports[`components/ChannelHeader should render archived view 1`] = ` @@ -2155,7 +2148,6 @@ exports[`components/ChannelHeader should render correct menu when muted 1`] = ` @@ -2489,7 +2481,6 @@ exports[`components/ChannelHeader should render not active channel files 1`] = ` @@ -2799,7 +2790,6 @@ exports[`components/ChannelHeader should render properly when custom status is e @@ -3123,7 +3113,6 @@ exports[`components/ChannelHeader should render properly when custom status is s @@ -3459,7 +3448,6 @@ exports[`components/ChannelHeader should render properly when empty 1`] = ` @@ -3761,7 +3749,6 @@ exports[`components/ChannelHeader should render properly when populated 1`] = ` @@ -4063,7 +4050,6 @@ exports[`components/ChannelHeader should render properly when populated with cha @@ -4396,7 +4382,6 @@ exports[`components/ChannelHeader should render shared view 1`] = ` @@ -4700,7 +4685,6 @@ exports[`components/ChannelHeader should render the pinned icon with the pinned diff --git a/webapp/channels/src/components/channel_header/channel_header.tsx b/webapp/channels/src/components/channel_header/channel_header.tsx index 11582e64cb..44144444d7 100644 --- a/webapp/channels/src/components/channel_header/channel_header.tsx +++ b/webapp/channels/src/components/channel_header/channel_header.tsx @@ -785,7 +785,6 @@ class ChannelHeader extends React.PureComponent {
diff --git a/webapp/channels/src/components/invitation_modal/invite_view.tsx b/webapp/channels/src/components/invitation_modal/invite_view.tsx index 298171db63..43bc0e6408 100644 --- a/webapp/channels/src/components/invitation_modal/invite_view.tsx +++ b/webapp/channels/src/components/invitation_modal/invite_view.tsx @@ -267,7 +267,7 @@ export default function InviteView(props: Props) { disabled={!isInviteValid} onClick={props.invite} className={'btn btn-primary'} - id={props.inviteType === InviteType.MEMBER ? 'inviteMembersButton' : 'inviteGuestButton'} + data-testid={'inviteButton'} > { {(isSearchResultItem || (props.location !== Locations.CENTER && (props.isPinnedPosts || props.isFlaggedPosts))) && }
- ), + message: messagePriority, }); const handleClose = useCallback(() => { @@ -104,6 +101,7 @@ function PostPriorityPickerOverlay({ className={classNames({control: true, active: pickerOpen})} disabled={disabled} type='button' + aria-label={messagePriority} {...getPickerReferenceProps()} > { return (
{ )}
{ maxHeight={maxHeight} >
= (props: Props): JSX.Element => className='search-form__container' >
diff --git a/webapp/channels/src/components/sidebar/__snapshots__/add_channels_cta_button.test.tsx.snap b/webapp/channels/src/components/sidebar/__snapshots__/add_channels_cta_button.test.tsx.snap index 981ae5f06a..52530b0f03 100644 --- a/webapp/channels/src/components/sidebar/__snapshots__/add_channels_cta_button.test.tsx.snap +++ b/webapp/channels/src/components/sidebar/__snapshots__/add_channels_cta_button.test.tsx.snap @@ -13,7 +13,7 @@ exports[`components/new_channel_modal should match snapshot 1`] = ` id="addChannelsCta" onClick={[Function]} > -
  • Add Channels -
  • +
    -
  • Add Channels -
  • +
    `; diff --git a/webapp/channels/src/components/sidebar/__snapshots__/invite_members_button.test.tsx.snap b/webapp/channels/src/components/sidebar/__snapshots__/invite_members_button.test.tsx.snap index 43eaebeb46..f62174b987 100644 --- a/webapp/channels/src/components/sidebar/__snapshots__/invite_members_button.test.tsx.snap +++ b/webapp/channels/src/components/sidebar/__snapshots__/invite_members_button.test.tsx.snap @@ -38,7 +38,7 @@ exports[`components/sidebar/invite_members_button should match snapshot 1`] = ` teamId="team_id2sss" > @@ -58,7 +58,7 @@ exports[`components/sidebar/invite_members_button should match snapshot 1`] = ` "openModal": [Function], } } - ariaLabel="Invite Users" + ariaLabel="Invite Members" className="intro-links color--link cursor--pointer" dialogType={ Object { @@ -68,17 +68,17 @@ exports[`components/sidebar/invite_members_button should match snapshot 1`] = ` "type": [Function], } } - id="introTextInvite" + id="inviteMembersButton" modalId="invitation" onClick={[Function]} >
    diff --git a/webapp/channels/src/components/sidebar/add_channels_cta_button.tsx b/webapp/channels/src/components/sidebar/add_channels_cta_button.tsx index 2152522d68..4ca53f6e66 100644 --- a/webapp/channels/src/components/sidebar/add_channels_cta_button.tsx +++ b/webapp/channels/src/components/sidebar/add_channels_cta_button.tsx @@ -117,14 +117,14 @@ const AddChannelsCtaButton = (): JSX.Element | null => { aria-label={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.dropdownAriaLabel', defaultMessage: 'Add Channel Dropdown'})} onClick={handleClick} > -
  • {intl.formatMessage({id: 'sidebar_left.addChannelsCta', defaultMessage: 'Add Channels'})} -
  • +
    ); }; diff --git a/webapp/channels/src/components/sidebar/invite_members_button.tsx b/webapp/channels/src/components/sidebar/invite_members_button.tsx index ce36ae3dfe..a256f4692e 100644 --- a/webapp/channels/src/components/sidebar/invite_members_button.tsx +++ b/webapp/channels/src/components/sidebar/invite_members_button.tsx @@ -43,14 +43,14 @@ const InviteMembersButton = (props: Props): JSX.Element | null => { permissions={[Permissions.ADD_USER_TO_TEAM, Permissions.INVITE_GUEST]} > -
  • @@ -59,7 +59,7 @@ const InviteMembersButton = (props: Props): JSX.Element | null => { id={'sidebar_left.inviteMembers'} defaultMessage='Invite Members' /> -
  • +
    ); diff --git a/webapp/channels/src/components/sidebar/sidebar_category_header.tsx b/webapp/channels/src/components/sidebar/sidebar_category_header.tsx index 405f150ec5..0ff774288c 100644 --- a/webapp/channels/src/components/sidebar/sidebar_category_header.tsx +++ b/webapp/channels/src/components/sidebar/sidebar_category_header.tsx @@ -40,6 +40,14 @@ type Props = StaticProps & { } export const SidebarCategoryHeader = React.forwardRef((props: Props, ref?: React.Ref) => { + const {dragHandleProps} = props; + + // (Accessibility) Ensures interactive controls are not nested as they are not always announced + // by screen readers or can cause focus problems for assistive technologies. + if (dragHandleProps && dragHandleProps.role) { + Reflect.deleteProperty(dragHandleProps, 'role'); + } + return (
    {wrapEmojis(props.displayName)}
    diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json index db067ec0e7..0c3de9ef52 100644 --- a/webapp/channels/src/i18n/en.json +++ b/webapp/channels/src/i18n/en.json @@ -4788,7 +4788,6 @@ "sidebar_left.channel_navigator.goForwardLabel": "Forward", "sidebar_left.channel_navigator.jumpTo": "Find channel", "sidebar_left.inviteMembers": "Invite Members", - "sidebar_left.inviteUsers": "Invite Users", "sidebar_left.sidebar_category_menu.createCategory": "Create New Category", "sidebar_left.sidebar_category_menu.deleteCategory": "Delete Category", "sidebar_left.sidebar_category_menu.dropdownAriaLabel": "Edit category menu", diff --git a/webapp/channels/src/plugins/test/__snapshots__/post_type.test.jsx.snap b/webapp/channels/src/plugins/test/__snapshots__/post_type.test.jsx.snap index d2bebba9b6..33c73c076d 100644 --- a/webapp/channels/src/plugins/test/__snapshots__/post_type.test.jsx.snap +++ b/webapp/channels/src/plugins/test/__snapshots__/post_type.test.jsx.snap @@ -71,7 +71,6 @@ exports[`plugins/PostMessageView should match snapshot with no extended post typ text="this is some text" >