Skip to content

Commit

Permalink
fix(accessibility): tab support at login, reset and signup pages, but…
Browse files Browse the repository at this point in the history
…tons at ATE and app bar (#24214) (#24239)

Automatic Merge
  • Loading branch information
mattermost-build committed Aug 10, 2023
1 parent 581ce5b commit 096c9ea
Show file tree
Hide file tree
Showing 26 changed files with 441 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ describe('Signup Email page', () => {

it('should match elements, back button', () => {
// * Check elements in the header with back button
cy.get('#back_button').should('be.visible');
cy.get('#back_button').should('contain', 'Back');
cy.get('#back_button_icon').should('be.visible');
cy.get('#back_button_icon').should('have.attr', 'title', 'Back Icon');
cy.findByTestId('back_button').
should('be.visible').
and('have.text', 'Back');
cy.get('#back_button_icon').
should('be.visible').
and('have.attr', 'title', 'Back Icon');
});

it('should match elements, body', () => {
Expand Down Expand Up @@ -94,20 +96,20 @@ describe('Signup Email page', () => {
// * Check elements in the footer
cy.get('.hfroute-footer').scrollIntoView().should('be.visible').within(() => {
// * Check if about footer link is present
cy.findByText('About').should('exist').
and('have.attr', 'href', config.SupportSettings.AboutLink || ABOUT_LINK);
cy.findByText('About').should('be.visible').
should('have.attr', 'href').and('match', new RegExp(`${config.SupportSettings.AboutLink || ABOUT_LINK}/*`));

// * Check if privacy footer link is present
cy.findByText('Privacy Policy').should('exist').
and('have.attr', 'href', config.SupportSettings.PrivacyPolicyLink || PRIVACY_POLICY_LINK);
cy.findByText('Privacy Policy').should('be.visible').
should('have.attr', 'href').and('match', new RegExp(`${config.SupportSettings.PrivacyPolicyLink || PRIVACY_POLICY_LINK}/*`));

// * Check if terms footer link is present
cy.findByText('Terms').should('exist').
and('have.attr', 'href', config.SupportSettings.TermsOfServiceLink || TERMS_OF_SERVICE_LINK);
cy.findByText('Terms').should('be.visible').
should('have.attr', 'href').and('match', new RegExp(`${config.SupportSettings.TermsOfServiceLink || TERMS_OF_SERVICE_LINK}/*`));

// * Check if help footer link is present
cy.findByText('Help').should('exist').
and('have.attr', 'href', config.SupportSettings.HelpLink || HELP_LINK);
cy.findByText('Help').should('be.visible').
should('have.attr', 'href').and('match', new RegExp(`${config.SupportSettings.HelpLink || HELP_LINK}/*`));

const todaysDate = new Date();
const currentYear = todaysDate.getFullYear();
Expand Down
27 changes: 23 additions & 4 deletions e2e-tests/playwright/support/test_fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ type ExtendedFixtures = {
pages: typeof pages;
};

type AxeBuilderOptions = {
disableColorContrast?: boolean;
disableLinkInTextBlock?: boolean;
};

export const test = base.extend<ExtendedFixtures>({
// eslint-disable-next-line no-empty-pattern
axe: async ({}, use) => {
Expand Down Expand Up @@ -94,15 +99,29 @@ class PlaywrightExtended {
}

class AxeBuilderExtended {
readonly builder: (page: Page, disableRules?: string[]) => AxeBuilder;
readonly builder: (page: Page, options?: AxeBuilderOptions) => AxeBuilder;

// See https://github.com/dequelabs/axe-core/blob/master/doc/API.md#axe-core-tags
readonly tags: string[] = ['wcag2a', 'wcag2aa'];

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 || []);
this.builder = (page: Page, options: AxeBuilderOptions = {}) => {
// See https://github.com/dequelabs/axe-core/blob/master/doc/rule-descriptions.md#wcag-20-level-a--aa-rules
const disabledRules: string[] = [];

if (options.disableColorContrast) {
// Disabled in pages due to impact to overall theme of Mattermost.
// Option: make use of custom theme to improve color contrast.
disabledRules.push('color-contrast');
}

if (options.disableLinkInTextBlock) {
// Disabled in pages due to impact to overall theme of Mattermost.
// Option: make use of custom theme to improve color contrast.
disabledRules.push('link-in-text-block');
}

return new AxeBuilder({page}).withTags(this.tags).disableRules(disabledRules);
};
}

Expand Down
30 changes: 30 additions & 0 deletions e2e-tests/playwright/support/ui/components/footer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {expect, Locator} from '@playwright/test';

export default class Footer {
readonly container: Locator;

readonly copyright;
readonly aboutLink;
readonly privacyPolicyLink;
readonly termsLink;
readonly helpLink;

constructor(container: Locator) {
this.container = container;

this.copyright = container.locator('.footer-copyright');
this.aboutLink = container.locator('text=About');
this.privacyPolicyLink = container.locator('text=Privacy Policy');
this.termsLink = container.locator('text=Terms');
this.helpLink = container.locator('text=Help');
}

async toBeVisible() {
await expect(this.copyright).toBeVisible();
}
}

export {Footer};
4 changes: 4 additions & 0 deletions e2e-tests/playwright/support/ui/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {ChannelsPost} from './channels/post';
import {ChannelsSidebarLeft} from './channels/sidebar_left';
import {ChannelsSidebarRight} from './channels/sidebar_right';
import {FindChannelsModal} from './channels/find_channels_modal';
import {Footer} from './footer';
import {GlobalHeader} from './global_header';
import {MainHeader} from './main_header';
import {PostDotMenu} from './channels/post_dot_menu';
import {DeletePostModal} from './channels/delete_post_modal';
import {PostMenu} from './channels/post_menu';
Expand All @@ -26,7 +28,9 @@ const components = {
ChannelsSidebarLeft,
ChannelsSidebarRight,
FindChannelsModal,
Footer,
GlobalHeader,
MainHeader,
PostDotMenu,
DeletePostModal,
PostMenu,
Expand Down
24 changes: 24 additions & 0 deletions e2e-tests/playwright/support/ui/components/main_header.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {expect, Locator} from '@playwright/test';

export default class MainHeader {
readonly container: Locator;

readonly logo;
readonly backButton;

constructor(container: Locator) {
this.container = container;

this.logo = container.locator('.header-logo-link');
this.backButton = container.getByTestId('back_button');
}

async toBeVisible() {
await expect(this.container).toBeVisible();
}
}

export {MainHeader};
2 changes: 2 additions & 0 deletions e2e-tests/playwright/support/ui/pages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {BoardsViewPage} from './boards_view';
import {ChannelsPage} from './channels';
import {LandingLoginPage} from './landing_login';
import {LoginPage} from './login';
import {ResetPasswordPage} from './reset_password';
import {SignupPage} from './signup';

const pages = {
Expand All @@ -14,6 +15,7 @@ const pages = {
ChannelsPage,
LandingLoginPage,
LoginPage,
ResetPasswordPage,
SignupPage,
};

Expand Down
12 changes: 11 additions & 1 deletion e2e-tests/playwright/support/ui/pages/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {expect, Page} from '@playwright/test';
import {AdminConfig} from '@mattermost/types/config';
import {UserProfile} from '@mattermost/types/users';

import {components} from '@e2e-support/ui/components';

export default class LoginPage {
readonly adminConfig: AdminConfig;

Expand All @@ -17,13 +19,17 @@ export default class LoginPage {
readonly loginInput;
readonly loginPlaceholder;
readonly passwordInput;
readonly passwordToggleButton;
readonly signInButton;
readonly createAccountLink;
readonly forgotPasswordLink;
readonly userErrorLabel;
readonly fieldWithError;
readonly formContainer;

readonly header;
readonly footer;

constructor(page: Page, adminConfig: AdminConfig) {
this.page = page;
this.adminConfig = adminConfig;
Expand All @@ -34,16 +40,20 @@ export default class LoginPage {

this.title = page.locator('h1:has-text("Log in to your account")');
this.subtitle = page.locator('text=Collaborate with your team in real-time');
this.bodyCard = page.locator('.login-body-card');
this.bodyCard = page.locator('.login-body-card-content');
this.loginInput = page.locator('#input_loginId');
this.loginPlaceholder = page.locator(`[placeholder="${loginInputPlaceholder}"]`);
this.passwordInput = page.locator('#input_password-input');
this.passwordToggleButton = page.getByRole('button', {name: 'Show or hide password'});
this.signInButton = page.locator('button:has-text("Log in")');
this.createAccountLink = page.locator("text=Don't have an account?");
this.forgotPasswordLink = page.locator('text=Forgot your password?');
this.userErrorLabel = page.locator('text=Please enter your email or username');
this.fieldWithError = page.locator('.with-error');
this.formContainer = page.locator('.signup-team__container');

this.header = new components.MainHeader(page.locator('.hfroute-header'));
this.footer = new components.Footer(page.locator('.hfroute-footer'));
}

async toBeVisible() {
Expand Down
51 changes: 51 additions & 0 deletions e2e-tests/playwright/support/ui/pages/reset_password.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {expect, Page} from '@playwright/test';

import {components} from '@e2e-support/ui/components';

export default class ResetPasswordPage {
readonly page: Page;

readonly title;
readonly subtitle;
readonly emailInput;
readonly resetButton;
readonly formContainer;

readonly header;
readonly footer;

constructor(page: Page) {
this.page = page;

this.title = page.locator('h1:has-text("Password Reset")');
this.subtitle = page.locator('text=To reset your password, enter the email address you used to sign up');
this.emailInput = page.locator(`[placeholder="Email"]`);
this.resetButton = page.locator('#passwordResetButton');
this.formContainer = page.locator('.signup-team__container');

this.header = new components.MainHeader(page.locator('.signup-header'));
this.footer = new components.Footer(page.locator('#footer_section'));
}

async toBeVisible() {
await this.page.waitForLoadState('networkidle');
await expect(this.title).toBeVisible();
await expect(this.subtitle).toBeVisible();
await expect(this.emailInput).toBeVisible();
await expect(this.resetButton).toBeVisible();
}

async goto() {
await this.page.goto('/reset_password');
}

async reset(email: string) {
await this.emailInput.fill(email);
await Promise.all([this.page.waitForNavigation(), this.resetButton.click()]);
}
}

export {ResetPasswordPage};
29 changes: 26 additions & 3 deletions e2e-tests/playwright/support/ui/pages/signup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import {expect, Page} from '@playwright/test';

import {duration, wait} from '@e2e-support/util';
import {components} from '@e2e-support/ui/components';

export default class SignupPage {
readonly page: Page;
Expand All @@ -14,28 +15,50 @@ export default class SignupPage {
readonly emailInput;
readonly usernameInput;
readonly passwordInput;
readonly passwordToggleButton;
readonly newsLetterCheckBox;
readonly newsLetterPrivacyPolicyLink;
readonly newsLetterUnsubscribeLink;
readonly agreementTermsOfUseLink;
readonly agreementPrivacyPolicyLink;
readonly createAccountButton;
readonly loginLink;
readonly emailError;
readonly usernameError;
readonly passwordError;

readonly header;
readonly footer;

constructor(page: Page) {
this.page = page;

this.title = page.locator('h1:has-text("Let’s get started")');
this.subtitle = page.locator('text=Create your Mattermost account to start collaborating with your team');
this.bodyCard = page.locator('.signup-body-card');
this.bodyCard = page.locator('.signup-body-card-content');
this.loginLink = page.locator('text=Log in');
this.emailInput = page.locator('#input_email');
this.usernameInput = page.locator('#input_name');
this.passwordInput = page.locator('#input_password-input');
this.passwordToggleButton = page.getByRole('button', {name: 'Show or hide password'});
this.createAccountButton = page.locator('button:has-text("Create Account")');
this.loginLink = page.locator('text=Click here to sign in.');
this.emailError = page.locator('text=Please enter a valid email address');
this.usernameError = page.locator(
'text=Usernames have to begin with a lowercase letter and be 3-22 characters long. You can use lowercase letters, numbers, periods, dashes, and underscores.'
);
this.passwordError = page.locator('text=Must be 5-64 characters long.');

const newsletterBlock = page.locator('.check-input');
this.newsLetterCheckBox = newsletterBlock.getByRole('checkbox', {name: 'newsletter checkbox'});
this.newsLetterPrivacyPolicyLink = newsletterBlock.locator('text=Privacy Policy');
this.newsLetterUnsubscribeLink = newsletterBlock.locator('text=unsubscribe');

const agreementBlock = page.locator('.signup-body-card-agreement');
this.agreementTermsOfUseLink = agreementBlock.locator('text=Terms of Use');
this.agreementPrivacyPolicyLink = agreementBlock.locator('text=Privacy Policy');

this.header = new components.MainHeader(page.locator('.hfroute-header'));
this.footer = new components.Footer(page.locator('.hfroute-footer'));
}

async toBeVisible() {
Expand All @@ -49,7 +72,7 @@ export default class SignupPage {
}

async goto() {
await this.page.goto('/signup_email');
await this.page.goto('/signup_user_complete');
}

async create(user: {email: string; username: string; password: string}, waitForRedirect = true) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ test('Intro to channel', async ({pw, pages, axe}) => {

// # Analyze the page
// Disable 'color-contrast' to be addressed by MM-53814
const accessibilityScanResults = await axe.builder(page, ['color-contrast']).analyze();
const accessibilityScanResults = await axe.builder(page, {disableColorContrast: true}).analyze();

// * Should have no violation
expect(accessibilityScanResults.violations).toHaveLength(0);
Expand Down

0 comments on commit 096c9ea

Please sign in to comment.