mirror of
https://github.com/mattermost/mattermost.git
synced 2026-02-18 18:18:23 -05:00
(test): fix flaky and migrate to playwright (#35156)
This commit is contained in:
parent
51e6431275
commit
e499decea0
8 changed files with 630 additions and 291 deletions
|
|
@ -1,222 +0,0 @@
|
|||
// 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 @enterprise @accessibility
|
||||
|
||||
import {Channel} from '@mattermost/types/channels';
|
||||
import {Team} from '@mattermost/types/teams';
|
||||
import {UserProfile} from '@mattermost/types/users';
|
||||
|
||||
import * as TIMEOUTS from '../../../../fixtures/timeouts';
|
||||
|
||||
describe('Verify Accessibility Support in Modals & Dialogs', () => {
|
||||
let testTeam: Team;
|
||||
let testChannel: Channel;
|
||||
let testUser: UserProfile;
|
||||
let selectedRowText: string;
|
||||
|
||||
before(() => {
|
||||
// * Check if server has license for Guest Accounts
|
||||
cy.apiRequireLicenseForFeature('GuestAccounts');
|
||||
|
||||
cy.apiInitSetup({userPrefix: 'user000a'}).then(({team, channel, user}) => {
|
||||
testTeam = team;
|
||||
testChannel = channel;
|
||||
testUser = user;
|
||||
|
||||
cy.apiCreateUser({prefix: 'user000b'}).then(({user: newUser}) => {
|
||||
cy.apiAddUserToTeam(testTeam.id, newUser.id).then(() => {
|
||||
cy.apiAddUserToChannel(testChannel.id, newUser.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// # Login as sysadmin and visit the town-square
|
||||
cy.apiAdminLogin();
|
||||
cy.visit(`/${testTeam.name}/channels/town-square`);
|
||||
});
|
||||
|
||||
it('MM-T1466 Accessibility Support in Direct Messages Dialog screen', () => {
|
||||
// * Verify the aria-label in create direct message button
|
||||
cy.uiAddDirectMessage().click();
|
||||
|
||||
// * Verify the accessibility support in Direct Messages Dialog
|
||||
cy.findAllByRole('dialog', {name: 'Direct Messages'}).eq(0).within(() => {
|
||||
cy.findByRole('heading', {name: 'Direct Messages'});
|
||||
|
||||
// * Verify the accessibility support in search input
|
||||
cy.findByLabelText('Search for people').
|
||||
should('have.attr', 'aria-autocomplete', 'list');
|
||||
|
||||
// # Search for a text and then check up and down arrow
|
||||
cy.findByLabelText('Search for people').
|
||||
typeWithForce('s').
|
||||
wait(TIMEOUTS.HALF_SEC).
|
||||
typeWithForce('{downarrow}{downarrow}{downarrow}{uparrow}');
|
||||
cy.get('#multiSelectList').children().eq(2).should('have.class', 'more-modal__row--selected').within(() => {
|
||||
cy.get('.more-modal__name').invoke('text').then((user) => {
|
||||
selectedRowText = user.split(' - ')[0].replace('@', '');
|
||||
});
|
||||
|
||||
// * Verify image alt is displayed
|
||||
cy.get('img.Avatar').should('have.attr', 'alt', 'user profile image');
|
||||
});
|
||||
|
||||
// * Verify if the reader is able to read out the selected row
|
||||
cy.get('.filtered-user-list div.sr-only:not([role="status"])').
|
||||
should('have.attr', 'aria-live', 'polite').
|
||||
and('have.attr', 'aria-atomic', 'true').
|
||||
invoke('text').then((text) => {
|
||||
expect(text).equal(selectedRowText);
|
||||
});
|
||||
|
||||
// # Search for an invalid text
|
||||
const additionalSearchTerm = 'somethingwhichdoesnotexist';
|
||||
cy.findByLabelText('Search for people').clear().
|
||||
typeWithForce(additionalSearchTerm).
|
||||
wait(TIMEOUTS.HALF_SEC);
|
||||
|
||||
// * Check if reader can read no results
|
||||
cy.get('.multi-select__wrapper').should('have.attr', 'aria-live', 'polite').and('have.text', `No results found matching ${additionalSearchTerm}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T1467 Accessibility Support in Browse Channels Dialog screen', () => {
|
||||
function getChannelAriaLabel(channel) {
|
||||
return channel.display_name.toLowerCase() + ', ' + channel.purpose.toLowerCase();
|
||||
}
|
||||
|
||||
// # Create atleast 2 channels
|
||||
let otherChannel;
|
||||
cy.apiCreateChannel(testTeam.id, 'z_accessibility', 'Z Accessibility', 'O', 'other purpose').then(({channel}) => {
|
||||
otherChannel = channel;
|
||||
});
|
||||
cy.apiCreateChannel(testTeam.id, 'accessibility', 'Accessibility', 'O', 'some purpose').then(({channel}) => {
|
||||
cy.apiLogin(testUser).then(() => {
|
||||
cy.reload();
|
||||
|
||||
// * Verify the aria-label in more public channels button
|
||||
cy.uiBrowseOrCreateChannel('Browse channels');
|
||||
|
||||
// * Verify the accessibility support in More Channels Dialog
|
||||
cy.findByRole('dialog', {name: 'Browse Channels'}).within(() => {
|
||||
cy.findByRole('heading', {name: 'Browse Channels'});
|
||||
|
||||
// * Verify the accessibility support in search input
|
||||
cy.findByPlaceholderText('Search channels');
|
||||
|
||||
cy.get('#moreChannelsList').should('be.visible').then((el) => {
|
||||
return el[0].children.length === 2;
|
||||
});
|
||||
|
||||
// # Hide already joined channels
|
||||
cy.findByText('Hide Joined').click();
|
||||
|
||||
// # Focus on the Create Channel button and TAB five time
|
||||
cy.get('#createNewChannelButton').focus().tab().tab().tab().tab().tab();
|
||||
|
||||
// * Verify channel name is highlighted and reader reads the channel name and channel description
|
||||
cy.get('#moreChannelsList').within(() => {
|
||||
const selectedChannel = getChannelAriaLabel(channel);
|
||||
cy.findByLabelText(selectedChannel).should('be.visible').should('be.focused');
|
||||
});
|
||||
|
||||
// * Press Tab again and verify if focus changes to next row
|
||||
cy.focused().tab();
|
||||
cy.findByLabelText(getChannelAriaLabel(otherChannel)).should('be.focused');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T1468 Accessibility Support in Add people to Channel Dialog screen', () => {
|
||||
// # Add atleast 5 users
|
||||
for (let i = 0; i < 5; i++) {
|
||||
cy.apiCreateUser().then(({user}) => {
|
||||
cy.apiAddUserToTeam(testTeam.id, user.id);
|
||||
});
|
||||
}
|
||||
|
||||
// # Visit the test channel, and wait for the page to fully load
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
|
||||
// # Open Add Members Dialog
|
||||
cy.uiOpenChannelMenu('Members');
|
||||
cy.uiGetButton('Add').click();
|
||||
|
||||
// * Verify the accessibility support in Add people Dialog
|
||||
cy.findAllByRole('dialog').eq(0).within(() => {
|
||||
const modalName = `Add people to ${testChannel.display_name}`;
|
||||
cy.findByRole('heading', {name: modalName});
|
||||
cy.wait(TIMEOUTS.ONE_SEC);
|
||||
|
||||
// * Verify the accessibility support in search input
|
||||
cy.findByLabelText('Search for people or groups').
|
||||
should('have.attr', 'aria-autocomplete', 'list');
|
||||
|
||||
// # Search for a text and then check up and down arrow
|
||||
cy.findByLabelText('Search for people or groups').
|
||||
wait(TIMEOUTS.HALF_SEC).
|
||||
typeWithForce('u').
|
||||
wait(TIMEOUTS.HALF_SEC).
|
||||
typeWithForce('{downarrow}{downarrow}{downarrow}{downarrow}{uparrow}');
|
||||
cy.get('#multiSelectList').
|
||||
children().eq(2).
|
||||
should('have.class', 'more-modal__row--selected').
|
||||
within(() => {
|
||||
cy.get('.more-modal__name').invoke('text').then((user) => {
|
||||
selectedRowText = user.split(' - ')[0].replace('@', '');
|
||||
});
|
||||
|
||||
// * Verify image alt is displayed
|
||||
cy.get('img.Avatar').should('have.attr', 'alt', 'user profile image');
|
||||
});
|
||||
|
||||
// * Verify if the reader is able to read out the selected row
|
||||
cy.get('.filtered-user-list div.sr-only:not([role="status"])').
|
||||
should('have.attr', 'aria-live', 'polite').
|
||||
and('have.attr', 'aria-atomic', 'true').
|
||||
invoke('text').then((text) => {
|
||||
// Check that the readout starts with the selected user since it may be followed by
|
||||
// "Already in Channel" depending on which user was selected
|
||||
expect(text).to.match(new RegExp(`^${selectedRowText}\\b`));
|
||||
});
|
||||
|
||||
// # Search for an invalid text and check if reader can read no results
|
||||
cy.findByLabelText('Search for people or groups').
|
||||
typeWithForce('somethingwhichdoesnotexist').
|
||||
wait(TIMEOUTS.HALF_SEC);
|
||||
|
||||
// * Check if reader can read no results
|
||||
cy.get('.custom-no-options-message').
|
||||
should('be.visible').
|
||||
and('contain', 'No matches found - Invite them to the team');
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T1515 Verify Accessibility Support in Invite People Flow', () => {
|
||||
// # Open Invite People
|
||||
cy.uiGetLHSHeader().click();
|
||||
cy.get("#sidebarTeamMenu li:contains('Invite people')").should('be.visible').click();
|
||||
|
||||
// * Verify accessibility support in Invite People Dialog
|
||||
cy.findByTestId('invitationModal').should('have.attr', 'aria-modal', 'true').and('have.attr', 'aria-labelledby', 'invitation_modal_title').and('have.attr', 'role', 'dialog');
|
||||
cy.get('#invitation_modal_title').should('be.visible').and('contain.text', 'Invite people to');
|
||||
|
||||
// # Press tab
|
||||
cy.get('button.icon-close').focus().tab({shift: true}).tab();
|
||||
|
||||
// * Verify tab focuses on close button
|
||||
cy.get('button.icon-close').should('have.attr', 'aria-label', 'Close').and('be.focused');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
// 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 @files_and_attachments
|
||||
|
||||
import * as TIMEOUTS from '../../../fixtures/timeouts';
|
||||
|
||||
describe('Edit Message with Attachment', () => {
|
||||
before(() => {
|
||||
// # Enable Link Previews
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
EnableLinkPreviews: true,
|
||||
},
|
||||
});
|
||||
|
||||
// # Create new team and new user and visit off-topic channel
|
||||
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
|
||||
cy.visit(offTopicUrl);
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T2268 - Edit Message with Attachment', () => {
|
||||
// # Upload file
|
||||
cy.get('#fileUploadInput').attachFile('mattermost-icon.png');
|
||||
|
||||
// # Wait for file to upload
|
||||
cy.wait(TIMEOUTS.TWO_SEC);
|
||||
|
||||
// # Post message
|
||||
cy.postMessage('Test');
|
||||
|
||||
cy.getLastPost().within(() => {
|
||||
// * Posted message should be correct
|
||||
cy.get('.post-message__text').should('contain.text', 'Test');
|
||||
|
||||
// * Attachment should exist
|
||||
cy.get('.file-view--single').should('exist');
|
||||
|
||||
// * Edited indicator should not exist
|
||||
cy.get('.post-edited__indicator').should('not.exist');
|
||||
});
|
||||
|
||||
// # Open the edit dialog
|
||||
cy.uiGetPostTextBox().type('{uparrow}');
|
||||
|
||||
// # Add some more text and save
|
||||
cy.get('#edit_textbox').type(' with some edit');
|
||||
cy.get('#edit_textbox').type('{enter}').wait(TIMEOUTS.HALF_SEC);
|
||||
|
||||
cy.getLastPost().within(() => {
|
||||
// * New text should show
|
||||
cy.get('.post-message__text').should('contain.text', 'Test with some edit');
|
||||
|
||||
// * Attachment should still exist
|
||||
cy.get('.file-view--single').should('exist');
|
||||
|
||||
// * Edited indicator should exist
|
||||
cy.get('.post-edited__indicator').should('exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify accessibility support in Add people to Channel Dialog screen
|
||||
*/
|
||||
test(
|
||||
'MM-T1468 Accessibility Support in Add people to Channel Dialog screen',
|
||||
{tag: ['@accessibility', '@add_people_channel']},
|
||||
async ({pw}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, adminUser, adminClient} = await pw.initSetup();
|
||||
|
||||
// # Create a channel in the team
|
||||
const channel = await adminClient.createChannel(
|
||||
pw.random.channel({
|
||||
teamId: team.id,
|
||||
displayName: 'Test Channel',
|
||||
name: 'test-channel',
|
||||
}),
|
||||
);
|
||||
|
||||
// # Create additional users and add to team
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const newUser = await adminClient.createUser(await pw.random.user(), '', '');
|
||||
await adminClient.addToTeam(team.id, newUser.id);
|
||||
}
|
||||
|
||||
// # Log in as admin
|
||||
const {page, channelsPage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit the test channel
|
||||
await channelsPage.goto(team.name, channel.name);
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Open channel menu and click Members
|
||||
await channelsPage.centerView.header.openChannelMenu();
|
||||
const membersMenuItem = page.locator('#channelMembers');
|
||||
await membersMenuItem.click();
|
||||
|
||||
// # Click the Add people button
|
||||
const addButton = page.getByRole('button', {name: 'Add people'});
|
||||
await addButton.click();
|
||||
|
||||
// * Verify the Add people dialog is visible
|
||||
const dialog = page.getByRole('dialog').first();
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
// * Verify the heading with channel name
|
||||
const modalName = `Add people to ${channel.display_name}`;
|
||||
await expect(dialog.getByRole('heading', {name: modalName})).toBeVisible();
|
||||
await pw.wait(pw.duration.one_sec);
|
||||
|
||||
// * Verify the search input has proper accessibility attributes
|
||||
const searchInput = dialog.getByLabel('Search for people or groups');
|
||||
await expect(searchInput).toBeVisible();
|
||||
await expect(searchInput).toHaveAttribute('aria-autocomplete', 'list');
|
||||
|
||||
// # Search for a text and navigate with arrow keys
|
||||
await pw.wait(pw.duration.half_sec);
|
||||
await searchInput.fill('u');
|
||||
await pw.wait(pw.duration.half_sec);
|
||||
|
||||
// # Navigate down through the list
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowUp');
|
||||
|
||||
// * Verify the selected row has the correct class
|
||||
const selectedRow = dialog.locator('#multiSelectList').locator('.more-modal__row--selected');
|
||||
await expect(selectedRow).toBeVisible();
|
||||
|
||||
// * Verify image alt is displayed for user profile
|
||||
const avatar = selectedRow.locator('img.Avatar');
|
||||
await expect(avatar).toHaveAttribute('alt', 'user profile image');
|
||||
|
||||
// * Verify screen reader live region exists and has proper attributes
|
||||
const srOnlyRegion = dialog.locator('.filtered-user-list div.sr-only:not([role="status"])');
|
||||
await expect(srOnlyRegion).toHaveAttribute('aria-live', 'polite');
|
||||
await expect(srOnlyRegion).toHaveAttribute('aria-atomic', 'true');
|
||||
|
||||
// # Search for an invalid text
|
||||
await searchInput.fill('somethingwhichdoesnotexist');
|
||||
await pw.wait(pw.duration.half_sec);
|
||||
|
||||
// * Check if the no results message is displayed with proper accessibility
|
||||
const noResultsWrapper = dialog.locator('.multi-select__wrapper');
|
||||
await expect(noResultsWrapper).toHaveAttribute('aria-live', 'polite');
|
||||
const noResultsMessage = dialog.locator('.no-channel-message .primary-message');
|
||||
await expect(noResultsMessage).toBeVisible();
|
||||
await expect(noResultsMessage).toContainText('No results found matching');
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Add people to Channel dialog passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Add people to Channel dialog',
|
||||
{tag: ['@accessibility', '@add_people_channel', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, adminUser, adminClient} = await pw.initSetup();
|
||||
|
||||
// # Create a channel in the team
|
||||
const channel = await adminClient.createChannel(
|
||||
pw.random.channel({
|
||||
teamId: team.id,
|
||||
displayName: 'Test Channel',
|
||||
name: 'test-channel',
|
||||
}),
|
||||
);
|
||||
|
||||
// # Create additional users and add to team
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const newUser = await adminClient.createUser(await pw.random.user(), '', '');
|
||||
await adminClient.addToTeam(team.id, newUser.id);
|
||||
}
|
||||
|
||||
// # Log in as admin
|
||||
const {page, channelsPage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit the test channel
|
||||
await channelsPage.goto(team.name, channel.name);
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Open channel menu and click Members
|
||||
await channelsPage.centerView.header.openChannelMenu();
|
||||
const membersMenuItem = page.locator('#channelMembers');
|
||||
await membersMenuItem.click();
|
||||
|
||||
// # Click the Add people button
|
||||
const addButton = page.getByRole('button', {name: 'Add people'});
|
||||
await addButton.click();
|
||||
|
||||
// * Verify the Add people dialog is visible
|
||||
const dialog = page.getByRole('dialog').first();
|
||||
await expect(dialog).toBeVisible();
|
||||
await pw.wait(pw.duration.one_sec);
|
||||
|
||||
// * Verify aria snapshot of Add people to Channel dialog
|
||||
await expect(dialog).toMatchAriaSnapshot(`
|
||||
- dialog "Add people to Test Channel":
|
||||
- document:
|
||||
- heading "Add people to Test Channel" [level=1]
|
||||
- button "Close"
|
||||
- log
|
||||
- text: Search for people or groups
|
||||
- combobox "Search for people or groups"
|
||||
- button "Cancel"
|
||||
- button "Add"
|
||||
`);
|
||||
|
||||
// * Analyze the Add people dialog for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.include('[role="dialog"]')
|
||||
.analyze();
|
||||
|
||||
// * Should have no violations
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify accessibility support in Browse Channels Dialog screen
|
||||
*/
|
||||
test(
|
||||
'MM-T1467 Accessibility Support in Browse Channels Dialog screen',
|
||||
{tag: ['@accessibility', '@browse_channels']},
|
||||
async ({pw}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, user, adminClient} = await pw.initSetup();
|
||||
|
||||
// # Create two channels with purposes for testing aria-label
|
||||
const channel1 = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: 'accessibility-' + Date.now(),
|
||||
display_name: 'Accessibility',
|
||||
type: 'O',
|
||||
purpose: 'some purpose',
|
||||
});
|
||||
|
||||
const channel2 = await adminClient.createChannel({
|
||||
team_id: team.id,
|
||||
name: 'z-accessibility-' + Date.now(),
|
||||
display_name: 'Z Accessibility',
|
||||
type: 'O',
|
||||
purpose: 'other purpose',
|
||||
});
|
||||
|
||||
// # Log in as regular user
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit town-square channel
|
||||
await channelsPage.goto(team.name, 'town-square');
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on Browse or Create Channel button and then Browse Channels
|
||||
await channelsPage.sidebarLeft.browseOrCreateChannelButton.click();
|
||||
const browseChannelsMenuItem = page.locator('#browseChannelsMenuItem');
|
||||
await browseChannelsMenuItem.click();
|
||||
|
||||
// * Verify the Browse Channels dialog is visible
|
||||
const dialog = page.getByRole('dialog', {name: 'Browse Channels'});
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
// * Verify the heading
|
||||
await expect(dialog.getByRole('heading', {name: 'Browse Channels'})).toBeVisible();
|
||||
|
||||
// * Verify the search input exists
|
||||
const searchInput = dialog.getByPlaceholder('Search channels');
|
||||
await expect(searchInput).toBeVisible();
|
||||
|
||||
// # Wait for channel list to load
|
||||
const channelList = dialog.locator('#moreChannelsList');
|
||||
await expect(channelList).toBeVisible();
|
||||
|
||||
// # Hide already joined channels
|
||||
const hideJoinedCheckbox = dialog.getByText('Hide Joined');
|
||||
await hideJoinedCheckbox.click();
|
||||
|
||||
// # Focus on Create Channel button and tab through elements
|
||||
const createChannelButton = dialog.locator('#createNewChannelButton');
|
||||
await createChannelButton.focus();
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
// * Verify channel name is highlighted and has proper aria-label
|
||||
const channel1AriaLabel = `${channel1.display_name.toLowerCase()}, ${channel1.purpose.toLowerCase()}`;
|
||||
const channel1Item = dialog.getByLabel(channel1AriaLabel);
|
||||
await expect(channel1Item).toBeVisible();
|
||||
await expect(channel1Item).toBeFocused();
|
||||
|
||||
// # Press Tab again to move to next channel
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
// * Verify focus moved to next channel
|
||||
const channel2AriaLabel = `${channel2.display_name.toLowerCase()}, ${channel2.purpose.toLowerCase()}`;
|
||||
const channel2Item = dialog.getByLabel(channel2AriaLabel);
|
||||
await expect(channel2Item).toBeFocused();
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Browse Channels dialog passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Browse Channels dialog',
|
||||
{tag: ['@accessibility', '@browse_channels', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, user} = await pw.initSetup();
|
||||
|
||||
// # Log in as regular user
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit town-square channel
|
||||
await channelsPage.goto(team.name, 'town-square');
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on Browse or Create Channel button and then Browse Channels
|
||||
await channelsPage.sidebarLeft.browseOrCreateChannelButton.click();
|
||||
const browseChannelsMenuItem = page.locator('#browseChannelsMenuItem');
|
||||
await browseChannelsMenuItem.click();
|
||||
|
||||
// * Verify the Browse Channels dialog is visible
|
||||
const dialog = page.getByRole('dialog', {name: 'Browse Channels'});
|
||||
await expect(dialog).toBeVisible();
|
||||
await pw.wait(pw.duration.one_sec);
|
||||
|
||||
// * Verify aria snapshot of Browse Channels dialog
|
||||
await expect(dialog).toMatchAriaSnapshot(`
|
||||
- dialog "Browse Channels":
|
||||
- document:
|
||||
- heading "Browse Channels" [level=1]
|
||||
- button "Create New Channel"
|
||||
- button "Close"
|
||||
- textbox "Search Channels"
|
||||
- /text: \\d+ Results/
|
||||
- /status: \\d+ Results/
|
||||
- status: Channel type filter set to All
|
||||
- button "Channel type filter"
|
||||
- checkbox "Hide joined channels": Hide Joined
|
||||
- search
|
||||
`);
|
||||
|
||||
// * Analyze the Browse Channels dialog for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.include('[role="dialog"]')
|
||||
.analyze();
|
||||
|
||||
// * Should have no violations
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify accessibility support in Direct Messages Dialog screen
|
||||
*/
|
||||
test(
|
||||
'MM-T1466 Accessibility Support in Direct Messages Dialog screen',
|
||||
{tag: ['@accessibility', '@direct_messages']},
|
||||
async ({pw}) => {
|
||||
// # Skip test if no license for Guest Accounts
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup with admin and another user
|
||||
const {team, adminUser, adminClient} = await pw.initSetup();
|
||||
|
||||
// # Create additional user and add to team
|
||||
const user2 = await adminClient.createUser(await pw.random.user(), '', '');
|
||||
await adminClient.addToTeam(team.id, user2.id);
|
||||
|
||||
// # Log in as admin
|
||||
const {page, channelsPage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit town-square channel
|
||||
await channelsPage.goto(team.name, 'town-square');
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on the Write a direct message button to open the Direct Messages dialog
|
||||
const writeDirectMessageButton = page.getByRole('button', {name: 'Write a direct message'});
|
||||
await writeDirectMessageButton.click();
|
||||
|
||||
// * Verify the Direct Messages dialog is visible
|
||||
const dialog = page.getByRole('dialog', {name: 'Direct Messages'});
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
// * Verify the heading
|
||||
await expect(dialog.getByRole('heading', {name: 'Direct Messages'})).toBeVisible();
|
||||
|
||||
// * Verify the search input has proper accessibility attributes
|
||||
const searchInput = dialog.getByLabel('Search for people');
|
||||
await expect(searchInput).toBeVisible();
|
||||
await expect(searchInput).toHaveAttribute('aria-autocomplete', 'list');
|
||||
|
||||
// # Search for a text and navigate with arrow keys
|
||||
await searchInput.fill('s');
|
||||
await pw.wait(pw.duration.half_sec);
|
||||
|
||||
// # Navigate down through the list
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowUp');
|
||||
|
||||
// * Verify the selected row has the correct class
|
||||
const selectedRow = dialog.locator('#multiSelectList').locator('.more-modal__row--selected');
|
||||
await expect(selectedRow).toBeVisible();
|
||||
|
||||
// * Verify image alt is displayed for user profile
|
||||
const avatar = selectedRow.locator('img.Avatar');
|
||||
await expect(avatar).toHaveAttribute('alt', 'user profile image');
|
||||
|
||||
// * Verify screen reader live region exists and has proper attributes
|
||||
const srOnlyRegion = dialog.locator('.filtered-user-list div.sr-only:not([role="status"])');
|
||||
await expect(srOnlyRegion).toHaveAttribute('aria-live', 'polite');
|
||||
await expect(srOnlyRegion).toHaveAttribute('aria-atomic', 'true');
|
||||
|
||||
// # Search for an invalid text
|
||||
const invalidSearchTerm = 'somethingwhichdoesnotexist';
|
||||
await searchInput.clear();
|
||||
await searchInput.fill(invalidSearchTerm);
|
||||
await pw.wait(pw.duration.half_sec);
|
||||
|
||||
// * Check if the no results message is displayed with proper accessibility
|
||||
const noResultsWrapper = dialog.locator('.multi-select__wrapper');
|
||||
await expect(noResultsWrapper).toHaveAttribute('aria-live', 'polite');
|
||||
await expect(noResultsWrapper).toContainText(`No results found matching ${invalidSearchTerm}`);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Direct Messages dialog passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Direct Messages dialog',
|
||||
{tag: ['@accessibility', '@direct_messages', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, user} = await pw.initSetup();
|
||||
|
||||
// # Log in as admin
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit town-square channel
|
||||
await channelsPage.goto(team.name, 'town-square');
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on the Write a direct message button to open the Direct Messages dialog
|
||||
const writeDirectMessageButton = page.getByRole('button', {name: 'Write a direct message'});
|
||||
await writeDirectMessageButton.click();
|
||||
|
||||
// * Verify the Direct Messages dialog is visible
|
||||
const dialog = page.getByRole('dialog', {name: 'Direct Messages'});
|
||||
await expect(dialog).toBeVisible();
|
||||
await pw.wait(pw.duration.one_sec);
|
||||
|
||||
// * Verify aria snapshot of Direct Messages dialog (key structural elements only)
|
||||
await expect(dialog).toMatchAriaSnapshot(`
|
||||
- dialog "Direct Messages":
|
||||
- document:
|
||||
- heading "Direct Messages" [level=1]
|
||||
- button "Close"
|
||||
- application:
|
||||
- log
|
||||
- text: Search for people
|
||||
- combobox "Search for people"
|
||||
- button "Go"
|
||||
`);
|
||||
|
||||
// * Analyze the Direct Messages dialog for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.include('[role="dialog"]')
|
||||
// TODO: Address scrollable-region-focusable violation in the Direct Messages dialog
|
||||
// The multiSelectList and sr-only status elements need to be keyboard accessible
|
||||
.disableRules(['scrollable-region-focusable'])
|
||||
.analyze();
|
||||
|
||||
// * Should have no violations
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify accessibility support in Invite People Flow
|
||||
*/
|
||||
test(
|
||||
'MM-T1515 Verify Accessibility Support in Invite People Flow',
|
||||
{tag: ['@accessibility', '@invite_people']},
|
||||
async ({pw}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, adminUser} = await pw.initSetup();
|
||||
|
||||
// # Log in as admin
|
||||
const {page, channelsPage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit town-square channel
|
||||
await channelsPage.goto(team.name, 'town-square');
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Open team menu and click Invite people
|
||||
await channelsPage.sidebarLeft.teamMenuButton.click();
|
||||
await channelsPage.teamMenu.toBeVisible();
|
||||
|
||||
const invitePeopleMenuItem = page.locator("#sidebarTeamMenu li:has-text('Invite people')");
|
||||
await invitePeopleMenuItem.click();
|
||||
|
||||
// * Verify the Invite People modal has proper accessibility attributes
|
||||
const inviteModal = page.getByTestId('invitationModal');
|
||||
await expect(inviteModal).toBeVisible();
|
||||
await expect(inviteModal).toHaveAttribute('aria-modal', 'true');
|
||||
await expect(inviteModal).toHaveAttribute('aria-labelledby', 'invitation_modal_title');
|
||||
await expect(inviteModal).toHaveAttribute('role', 'dialog');
|
||||
|
||||
// * Verify the modal title is visible and contains correct text
|
||||
const modalTitle = page.locator('#invitation_modal_title');
|
||||
await expect(modalTitle).toBeVisible();
|
||||
await expect(modalTitle).toContainText('Invite people to');
|
||||
|
||||
// # Get the close button and verify accessibility
|
||||
const closeButton = inviteModal.locator('button.icon-close');
|
||||
await expect(closeButton).toHaveAttribute('aria-label', 'Close');
|
||||
|
||||
// # Focus on close button and verify tab navigation works
|
||||
await closeButton.focus();
|
||||
await page.keyboard.press('Shift+Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
// * Verify focus returns to close button
|
||||
await expect(closeButton).toBeFocused();
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @objective Verify Invite People dialog passes accessibility scan and matches aria-snapshot
|
||||
*/
|
||||
test(
|
||||
'accessibility scan and aria-snapshot of Invite People dialog',
|
||||
{tag: ['@accessibility', '@invite_people', '@snapshots']},
|
||||
async ({pw, axe}) => {
|
||||
// # Skip test if no license
|
||||
await pw.skipIfNoLicense();
|
||||
|
||||
// # Initialize setup
|
||||
const {team, user} = await pw.initSetup();
|
||||
|
||||
// # Log in as user
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit town-square channel
|
||||
await channelsPage.goto(team.name, 'town-square');
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Open team menu and click Invite people
|
||||
await channelsPage.sidebarLeft.teamMenuButton.click();
|
||||
await channelsPage.teamMenu.toBeVisible();
|
||||
|
||||
const invitePeopleMenuItem = page.locator("#sidebarTeamMenu li:has-text('Invite people')");
|
||||
await invitePeopleMenuItem.click();
|
||||
|
||||
// * Verify the Invite People modal is visible
|
||||
const inviteModal = page.getByTestId('invitationModal');
|
||||
await expect(inviteModal).toBeVisible();
|
||||
await pw.wait(pw.duration.one_sec);
|
||||
|
||||
// * Verify aria snapshot of Invite People dialog (key structural elements only)
|
||||
await expect(inviteModal).toMatchAriaSnapshot(`
|
||||
- dialog:
|
||||
- document:
|
||||
- heading [level=1]
|
||||
- button
|
||||
- text: "To:"
|
||||
- log
|
||||
- text: Add members
|
||||
- combobox "Invite People"
|
||||
- listbox
|
||||
- button
|
||||
- button "Invite" [disabled]
|
||||
`);
|
||||
|
||||
// * Analyze the Invite People dialog for accessibility issues
|
||||
const accessibilityScanResults = await axe
|
||||
.builder(page, {disableColorContrast: true})
|
||||
.include('[data-testid="invitationModal"]')
|
||||
.analyze();
|
||||
|
||||
// * Should have no violations
|
||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||
},
|
||||
);
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify that users can edit a message that has an attachment,
|
||||
* the attachment is preserved after edit, and the edited indicator appears.
|
||||
*/
|
||||
test('MM-T2268 Edit Message with Attachment', async ({pw}) => {
|
||||
// # Initialize user and login
|
||||
const {user} = await pw.initSetup();
|
||||
const {channelsPage, page} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Navigate to channels page
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Post a message with an attachment
|
||||
await channelsPage.postMessage('Test', ['mattermost.png']);
|
||||
|
||||
// # Get the last post and verify content
|
||||
const post = await channelsPage.getLastPost();
|
||||
await post.toBeVisible();
|
||||
|
||||
// * Verify the posted message is correct
|
||||
await post.toContainText('Test');
|
||||
|
||||
// * Verify attachment exists (image thumbnail)
|
||||
const attachment = post.container.getByLabel(/file thumbnail/i);
|
||||
await expect(attachment).toBeVisible();
|
||||
|
||||
// * Verify edited indicator does not exist initially
|
||||
const postId = await post.getId();
|
||||
await expect(channelsPage.centerView.editedPostIcon(postId)).not.toBeVisible();
|
||||
|
||||
// # Focus on the post textbox and press Up arrow to open edit dialog
|
||||
await channelsPage.centerView.postCreate.input.focus();
|
||||
await page.keyboard.press('ArrowUp');
|
||||
|
||||
// # Verify edit mode is active and add more text
|
||||
await channelsPage.centerView.postEdit.toBeVisible();
|
||||
await channelsPage.centerView.postEdit.input.fill('Test with some edit');
|
||||
await channelsPage.centerView.postEdit.sendMessage();
|
||||
|
||||
// # Get the updated post
|
||||
const updatedPost = await channelsPage.getLastPost();
|
||||
await updatedPost.toBeVisible();
|
||||
|
||||
// * Verify the new text shows
|
||||
await updatedPost.toContainText('Test with some edit');
|
||||
|
||||
// * Verify attachment still exists (image thumbnail)
|
||||
const updatedAttachment = updatedPost.container.getByLabel(/file thumbnail/i);
|
||||
await expect(updatedAttachment).toBeVisible();
|
||||
|
||||
// * Verify edited indicator now exists
|
||||
await expect(channelsPage.centerView.editedPostIcon(postId)).toBeVisible();
|
||||
});
|
||||
Loading…
Reference in a new issue