From 10fe077e3f24ac4d8a6cdbd1c98a310a6bb698db Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 28 Aug 2024 11:37:04 -0400 Subject: [PATCH 01/25] ADd and implement cypress-axe --- pnpm-lock.yaml | 24 +++ services/cypress/cypress.config.ts | 11 +- .../accessiblility.spec.ts | 141 +++++++++++------- services/cypress/package.json | 6 +- services/cypress/support/e2e.ts | 1 + services/cypress/tsconfig.json | 2 +- 6 files changed, 125 insertions(+), 60 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e913f3eb75..d47d63894d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -723,12 +723,18 @@ importers: '@testing-library/cypress': specifier: ^10.0.1 version: 10.0.1(cypress@13.13.2) + axe-core: + specifier: ^4.10.0 + version: 4.10.0 c8: specifier: ^10.1.2 version: 10.1.2 cypress: specifier: ^13.13.2 version: 13.13.2 + cypress-axe: + specifier: ^1.5.0 + version: 1.5.0(axe-core@4.10.0)(cypress@13.13.2) husky: specifier: ^9.0.11 version: 9.0.11 @@ -6253,6 +6259,10 @@ packages: aws4@1.11.0: resolution: {integrity: sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==} + axe-core@4.10.0: + resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==} + engines: {node: '>=4'} + axe-core@4.2.4: resolution: {integrity: sha512-9AiDKFKUCWEQm1Kj4lcq7KFavLqSXdf2m/zJo+NVh4VXlW5iwXRJ6alkKmipCyYorsRnqsICH9XLubP1jBF+Og==} engines: {node: '>=4'} @@ -7120,6 +7130,13 @@ packages: engines: {node: '>= 10'} hasBin: true + cypress-axe@1.5.0: + resolution: {integrity: sha512-Hy/owCjfj+25KMsecvDgo4fC/781ccL+e8p+UUYoadGVM2ogZF9XIKbiM6KI8Y3cEaSreymdD6ZzccbI2bY0lQ==} + engines: {node: '>=10'} + peerDependencies: + axe-core: ^3 || ^4 + cypress: ^10 || ^11 || ^12 || ^13 + cypress-file-upload@5.0.8: resolution: {integrity: sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==} engines: {node: '>=8.2.1'} @@ -22253,6 +22270,8 @@ snapshots: aws4@1.11.0: {} + axe-core@4.10.0: {} + axe-core@4.2.4: {} axe-core@4.7.0: {} @@ -23401,6 +23420,11 @@ snapshots: dependencies: minimist: 1.2.8 + cypress-axe@1.5.0(axe-core@4.10.0)(cypress@13.13.2): + dependencies: + axe-core: 4.10.0 + cypress: 13.13.2 + cypress-file-upload@5.0.8(cypress@13.13.2): dependencies: cypress: 13.13.2 diff --git a/services/cypress/cypress.config.ts b/services/cypress/cypress.config.ts index ec00c676e0..7c63ac50de 100644 --- a/services/cypress/cypress.config.ts +++ b/services/cypress/cypress.config.ts @@ -55,7 +55,6 @@ module.exports = defineConfig({ // Reads graphql schema and converts it to gql for apollo client. on('task', { - pa11y: pa11y(), readGraphQLSchema() { const gqlSchema = fs.readFileSync( path.resolve(__dirname, './gen/schema.graphql'), @@ -63,6 +62,16 @@ module.exports = defineConfig({ ) return gql(`${gqlSchema}`) }, + log(message) { + console.log(message) + + return null + }, + table(message) { + console.table(message) + + return null + } }) return newConfig }, diff --git a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts index f151ea0eec..b8fbc425f1 100644 --- a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts +++ b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts @@ -1,3 +1,25 @@ +// Define at the top of the spec file or just import it +function terminalLog(violations: Record[]) { + console.log(violations) + cy.task( + 'log', + `${violations.length} accessibility violation${ + violations.length === 1 ? '' : 's' + } ${violations.length === 1 ? 'was' : 'were'} detected` + ) + // pluck specific keys to keep the table readable + const violationData: Record[] = violations.map( + ({ id, impact, description, nodes }) => ({ + id, + impact, + description, + nodes: nodes.length + }) + ) + + cy.task('table', violationData) +} + describe('state user in state submission form', () => { beforeEach(() => { cy.stubFeatureFlags() @@ -6,68 +28,75 @@ describe('state user in state submission form', () => { // SKIPPED because we currently have a bug that looks related to https://github.com/mfrachet/cypress-audit/issues/136#issuecomment-1311236777 // this started when we upgraded node and serverless and started default to ipv6 which prevented tests // current this keeps pa11y from running - it.skip('can fill out contract only submission with no a11y errors', () => { + it('can fill out contract only submission with no a11y errors', () => { // goal of this test is to check every single form page and navigation (going backwards, forwards or save as draft with new info) cy.logInAsStateUser() + cy.injectAxe() // Start a base contract only submissions cy.startNewContractOnlySubmissionWithBaseContract() + cy.checkA11y(undefined, undefined, terminalLog) + // cy.findByRole('heading', { + // level: 2, + // name: /Contract details/, + // timeout: 10_000, + // }) + // // cy.checkA11y() + // + // // check contract details a11y + // // cy.pa11y({ + // // hideElements: '.usa-step-indicator', + // // actions: ['wait for element #form-guidance to be visible'], + // // threshold: 6, + // // hideElements: '.usa-step-indicator', + // // }) + // cy.deprecatedNavigateV1Form('BACK') + // // check submissionType a11y + // // cy.pa11y({ + // // hideElements: '.usa-step-indicator', + // // actions: ['wait for element #form-guidance to be visible'], + // // threshold: 6, + // // hideElements: '.usa-step-indicator', + // // }) + // // cy.checkA11y() + // + // cy.deprecatedNavigateV1Form('CONTINUE') + // // cy.checkA11y() + // cy.deprecatedNavigateV1Form('CONTINUE') + // // cy.checkA11y() + // + // cy.findByRole('heading', { level: 2, name: /Contacts/ }) + // // cy.checkA11y() + // cy.fillOutStateContact() + // + // // check contacts a11y + // // cy.pa11y({ + // // hideElements: '.usa-step-indicator', + // // actions: ['wait for element #form-guidance to be visible'], + // // threshold: 6, + // // hideElements: '.usa-step-indicator', + // // }) + // + // cy.deprecatedNavigateV1Form('SAVE_DRAFT') + // cy.findByRole('heading', { level: 1, name: /Submissions dashboard/ }) + // //cy.checkA11y() + // + // + // cy.deprecatedNavigateV1Form('CONTINUE') + // // skip documents page - that will be deleted soon + // cy.deprecatedNavigateV1Form('CONTINUE') + // + // // Check that we end up on Review and Submit + // cy.findByRole('heading', { level: 2, name: /Review and submit/ }) - cy.findByRole('heading', { - level: 2, - name: /Contract details/, - timeout: 10_000, - }) - - // check contract details a11y - cy.pa11y({ - hideElements: '.usa-step-indicator', - actions: ['wait for element #form-guidance to be visible'], - threshold: 6, - hideElements: '.usa-step-indicator', - }) - cy.deprecatedNavigateV1Form('BACK') - // check submissionType a11y - cy.pa11y({ - hideElements: '.usa-step-indicator', - actions: ['wait for element #form-guidance to be visible'], - threshold: 6, - hideElements: '.usa-step-indicator', - }) - - cy.deprecatedNavigateV1Form('CONTINUE') - cy.deprecatedNavigateV1Form('CONTINUE') - - cy.findByRole('heading', { level: 2, name: /Contacts/ }) - cy.fillOutStateContact() - - // check contacts a11y - cy.pa11y({ - hideElements: '.usa-step-indicator', - actions: ['wait for element #form-guidance to be visible'], - threshold: 6, - hideElements: '.usa-step-indicator', - }) - - cy.deprecatedNavigateV1Form('SAVE_DRAFT') - cy.findByRole('heading', { level: 1, name: /Submissions dashboard/ }) - - - cy.deprecatedNavigateV1Form('CONTINUE') - // skip documents page - that will be deleted soon - cy.deprecatedNavigateV1Form('CONTINUE') - - // Check that we end up on Review and Submit - cy.findByRole('heading', { level: 2, name: /Review and submit/ }) - - // check review and submit a11y - cy.pa11y({ - hideElements: '.usa-step-indicator', - actions: ['wait for element #form-guidance to be visible'], - threshold: 6, - hideElements: '.usa-step-indicator', - }) - }) + // check review and submit a11y + // cy.pa11y({ + // hideElements: '.usa-step-indicator', + // actions: ['wait for element #form-guidance to be visible'], + // threshold: 6, + // hideElements: '.usa-step-indicator', + // }) }) +}) diff --git a/services/cypress/package.json b/services/cypress/package.json index 698d97ef1d..9567b9b747 100644 --- a/services/cypress/package.json +++ b/services/cypress/package.json @@ -43,18 +43,20 @@ "@cypress-audit/pa11y": "^1.3.0", "@cypress/code-coverage": "^3.10.0", "@testing-library/cypress": "^10.0.1", + "axe-core": "^4.10.0", "c8": "^10.1.2", "cypress": "^13.13.2", + "cypress-axe": "^1.5.0", "husky": "^9.0.11", "lint-staged": "^15.2.2", "prettier": "^3.3.3" }, "dependencies": { "@apollo/client": "3.8.8", - "cypress-file-upload": "^5.0.8", - "cypress-pipe": "^2.0.0", "aws-amplify": "^5.0.10", "axios": "^1.7.4", + "cypress-file-upload": "^5.0.8", + "cypress-pipe": "^2.0.0", "graphql": "^16.9.0", "protobufjs": "^7.3.0" } diff --git a/services/cypress/support/e2e.ts b/services/cypress/support/e2e.ts index 3cd559d4c7..321230abbd 100644 --- a/services/cypress/support/e2e.ts +++ b/services/cypress/support/e2e.ts @@ -1,4 +1,5 @@ import '@cypress/code-coverage/support' +import 'cypress-axe' Cypress.on('uncaught:exception', (err, runnable) => { // returning false here prevents Cypress from diff --git a/services/cypress/tsconfig.json b/services/cypress/tsconfig.json index 16c6f54b0d..4db3fa5dfc 100644 --- a/services/cypress/tsconfig.json +++ b/services/cypress/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "target": "ES2017", "lib": ["ES2017", "dom"], - "types": ["cypress", "@testing-library/cypress", "cypress-file-upload", "node"] + "types": ["cypress", "cypress-axe", "@testing-library/cypress", "cypress-file-upload", "node"] }, "include": ["**/*.ts"] } From 36ceb712fe827d7466dd78d3b3c440d982855788 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 28 Aug 2024 22:55:47 -0400 Subject: [PATCH 02/25] Add accessibility commands --- .../cypress/support/accessibilityCommands.ts | 32 +++++++++++++++++++ services/cypress/support/index.ts | 6 ++++ 2 files changed, 38 insertions(+) create mode 100644 services/cypress/support/accessibilityCommands.ts diff --git a/services/cypress/support/accessibilityCommands.ts b/services/cypress/support/accessibilityCommands.ts new file mode 100644 index 0000000000..1d2553d904 --- /dev/null +++ b/services/cypress/support/accessibilityCommands.ts @@ -0,0 +1,32 @@ +function terminalLog(violations: Record[]) { + console.log(violations) + cy.task( + 'log', + `${violations.length} accessibility violation${ + violations.length === 1 ? '' : 's' + } ${violations.length === 1 ? 'was' : 'were'} detected` + ) + // pluck specific keys to keep the table readable + const violationData: Record[] = violations.map( + ({ id, impact, description, nodes }) => ({ + id, + impact, + description, + nodes: nodes.length + }) + ) + + cy.task('table', violationData) +} + +Cypress.Commands.add( + 'checkA11yWithSection508', + () => { + cy.checkA11y('', { + runOnly: { + type: 'tag', + values: ['section508'] + }, + }, terminalLog) + } +) diff --git a/services/cypress/support/index.ts b/services/cypress/support/index.ts index 57fecc8be4..49d5dfb530 100644 --- a/services/cypress/support/index.ts +++ b/services/cypress/support/index.ts @@ -26,6 +26,7 @@ import { FeatureFlagSettings, } from '../../app-web/src/common-code/featureFlags' import './apiCommands' +import './accessibilityCommands' import { Contract, HealthPlanPackage } from '../gen/gqlClient'; import { CMSUserType, DivisionType } from '../utils/apollo-test-utils'; import { StateUserType } from 'app-api/src/domain-models'; @@ -105,6 +106,7 @@ declare global { division: DivisionType }): void + // Direct API commands apiCreateAndSubmitContractOnlySubmission(stateUser: StateUserType): Cypress.Chainable apiCreateAndSubmitContractWithRates(stateUser: StateUserType): Cypress.Chainable apiDeprecatedCreateSubmitHPP(stateUser: StateUserType, formData?: Partial): Cypress.Chainable @@ -112,7 +114,11 @@ declare global { apiAssignDivisionToCMSUser(cmsUser: CMSUserType, division: DivisionType): Cypress.Chainable apiCreateAndSubmitContractWithRates(stateUser: StateUserType): Cypress.Chainable + // GraphQL intercept commands interceptGraphQL(): void + + // Accessibility Commands + checkA11yWithSection508(): void } } } From ca341072a60490554e63ff7a815618fc2bbf5cee Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 28 Aug 2024 22:56:12 -0400 Subject: [PATCH 03/25] Fix launch darkly intercepts for local deployment. --- .../cypress/support/launchDarklyCommands.ts | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/services/cypress/support/launchDarklyCommands.ts b/services/cypress/support/launchDarklyCommands.ts index 4c2cce6c57..f0dc718627 100644 --- a/services/cypress/support/launchDarklyCommands.ts +++ b/services/cypress/support/launchDarklyCommands.ts @@ -41,10 +41,14 @@ Cypress.Commands.add( JSON.stringify(featureFlagObject) ) + const clientSDKMatchers = Cypress.env('AUTH_MODE') === 'LOCAL' ? + { method: 'GET', pathname: /^\/ld-clientsdk(\/.*)?$/ } : + { method: 'GET', hostname: /\.*clientsdk\.launchdarkly\.us/ } + // Intercepts LD request and returns with our own feature flags and values. return cy .intercept( - { method: 'GET', hostname: /\.*clientsdk\.launchdarkly\.us/ }, + clientSDKMatchers, { body: featureFlagObject } ) .as('LDApp') @@ -54,24 +58,32 @@ Cypress.Commands.add( // Intercepting feature flag api calls and returns some response. This should stop the app from calling making requests to LD. Cypress.Commands.add('stubFeatureFlags', () => { // ignore api calls to events endpoint + const eventMatchers = Cypress.env('AUTH_MODE') === 'LOCAL' ? + { method: 'POST', pathname: /^\/ld-events(\/.*)?$/ } : + { method: 'POST', hostname: /\.*events\.launchdarkly\.us/ } + cy.intercept( - { method: 'POST', hostname: /\.*events\.launchdarkly\.us/ }, + eventMatchers, // { body: {} } (req) => { req.on('response', (res) => { - res.setDelay(60000) + res.setDelay(15000) }) req.reply({ body: {} }) } ).as('LDEvents') + const clientStreamMatchers = Cypress.env('AUTH_MODE') === 'LOCAL' ? + { method: 'GET', pathname: /^\/ld-clientstream(\/.*)?$/ } : + { method: 'GET', hostname: /\.*clientstream\.launchdarkly\.us/ } + // turn off push updates from LaunchDarkly (EventSource) cy.intercept( - { method: 'GET', hostname: /\.*clientstream\.launchdarkly\.us/ }, + clientStreamMatchers, // access the request handler and stub a response (req) => { req.on('response', (res) => { - res.setDelay(60000) + res.setDelay(15000) }) req.reply('data: no streaming feature flag data here\n\n', { 'content-type': 'text/event-stream; charset=utf-8', From 41333e99d287ee173ef497663004a432b4041ee1 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 28 Aug 2024 22:56:51 -0400 Subject: [PATCH 04/25] Add two accessibility tests. --- .../accessiblility.spec.ts | 186 ++++++++++-------- 1 file changed, 99 insertions(+), 87 deletions(-) diff --git a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts index b8fbc425f1..062ffe1800 100644 --- a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts +++ b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts @@ -1,102 +1,114 @@ -// Define at the top of the spec file or just import it -function terminalLog(violations: Record[]) { - console.log(violations) - cy.task( - 'log', - `${violations.length} accessibility violation${ - violations.length === 1 ? '' : 's' - } ${violations.length === 1 ? 'was' : 'were'} detected` - ) - // pluck specific keys to keep the table readable - const violationData: Record[] = violations.map( - ({ id, impact, description, nodes }) => ({ - id, - impact, - description, - nodes: nodes.length - }) - ) - - cy.task('table', violationData) -} +import { stateUser } from '../../../utils/apollo-test-utils'; describe('state user in state submission form', () => { beforeEach(() => { cy.stubFeatureFlags() cy.interceptGraphQL() }) - // SKIPPED because we currently have a bug that looks related to https://github.com/mfrachet/cypress-audit/issues/136#issuecomment-1311236777 - // this started when we upgraded node and serverless and started default to ipv6 which prevented tests - // current this keeps pa11y from running - it('can fill out contract only submission with no a11y errors', () => { + it('has not a11y errors with submission form and and form erros', () => { // goal of this test is to check every single form page and navigation (going backwards, forwards or save as draft with new info) - + cy.interceptFeatureFlags({'438-attestation': true}) cy.logInAsStateUser() + + // Inject the axe run-time cy.injectAxe() // Start a base contract only submissions - cy.startNewContractOnlySubmissionWithBaseContract() - cy.checkA11y(undefined, undefined, terminalLog) + cy.findByTestId('state-dashboard-page').should('exist') + cy.findByRole('link', { name: 'Start new submission' }).click() + + // Check accessibility on Submission type page + cy.findByRole('heading', { level: 1, name: /New submission/ }) + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithSection508() + + cy.fillOutContractActionAndRateCertification() + cy.deprecatedNavigateV1Form('CONTINUE_FROM_START_NEW') + + cy.findByRole('heading', { level: 2, name: /Contract details/ }) + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithSection508() + + cy.location().then((fullUrl) => { + const submissionURL = fullUrl.toString().replace( + 'edit/contract-details', + '' + ) - // cy.findByRole('heading', { - // level: 2, - // name: /Contract details/, - // timeout: 10_000, - // }) - // // cy.checkA11y() - // - // // check contract details a11y - // // cy.pa11y({ - // // hideElements: '.usa-step-indicator', - // // actions: ['wait for element #form-guidance to be visible'], - // // threshold: 6, - // // hideElements: '.usa-step-indicator', - // // }) - // cy.deprecatedNavigateV1Form('BACK') - // // check submissionType a11y - // // cy.pa11y({ - // // hideElements: '.usa-step-indicator', - // // actions: ['wait for element #form-guidance to be visible'], - // // threshold: 6, - // // hideElements: '.usa-step-indicator', - // // }) - // // cy.checkA11y() - // - // cy.deprecatedNavigateV1Form('CONTINUE') - // // cy.checkA11y() - // cy.deprecatedNavigateV1Form('CONTINUE') - // // cy.checkA11y() - // - // cy.findByRole('heading', { level: 2, name: /Contacts/ }) - // // cy.checkA11y() - // cy.fillOutStateContact() - // - // // check contacts a11y - // // cy.pa11y({ - // // hideElements: '.usa-step-indicator', - // // actions: ['wait for element #form-guidance to be visible'], - // // threshold: 6, - // // hideElements: '.usa-step-indicator', - // // }) - // - // cy.deprecatedNavigateV1Form('SAVE_DRAFT') - // cy.findByRole('heading', { level: 1, name: /Submissions dashboard/ }) - // //cy.checkA11y() - // - // - // cy.deprecatedNavigateV1Form('CONTINUE') - // // skip documents page - that will be deleted soon - // cy.deprecatedNavigateV1Form('CONTINUE') - // - // // Check that we end up on Review and Submit - // cy.findByRole('heading', { level: 2, name: /Review and submit/ }) + // Check accessibility on rate details page + cy.navigateFormByDirectLink(`${submissionURL}edit/rate-details`) + cy.findByRole('radiogroup', { + name: /Was this rate certification included with another submission?/, + }) + .should('exist') + .within(() => { + cy.findByText('No, this rate certification was not included with any other submissions').click() + }) + cy.injectAxe() + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithSection508() - // check review and submit a11y - // cy.pa11y({ - // hideElements: '.usa-step-indicator', - // actions: ['wait for element #form-guidance to be visible'], - // threshold: 6, - // hideElements: '.usa-step-indicator', - // }) + //Check accessibility on contacts page + cy.navigateFormByDirectLink(`${submissionURL}edit/contacts`) + cy.findByRole('heading', { level: 2, name: /Contacts/ }) + cy.injectAxe() + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithSection508() + + //Check accessibility on documents page + cy.navigateFormByDirectLink(`${submissionURL}edit/documents`) + cy.findByRole('heading', { level: 2, name: /Supporting documents/ }) + cy.injectAxe() + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithSection508() + + //Check accessibility on review and submit page + cy.navigateFormByDirectLink(`${submissionURL}edit/review-and-submit`) + cy.findByRole('heading', { level: 2, name: /Review and submit/ }) + cy.injectAxe() + cy.checkA11yWithSection508() + + //Check the dashboard + cy.navigateContractRatesForm('SAVE_DRAFT', false) + cy.checkA11yWithSection508() + }) + }) + + it('has no errors on CMS dashboard', () => { + cy.apiCreateAndSubmitContractWithRates(stateUser()).then(() => { + cy.logInAsCMSUser() + cy.injectAxe() + //check submissions tab + cy.checkA11yWithSection508() + + //check rate reviews tab + cy.findByRole('tab', { name: 'Rate reviews' }).should('exist').click() + cy.checkA11yWithSection508() + }) }) }) From 1014ee03668fa5928bceca43dd44ca5afe12f812 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 28 Aug 2024 23:39:24 -0400 Subject: [PATCH 05/25] Reverting bad merge From eebe3ccd6690f3e27902695ff358d19e1cfda6a9 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 28 Aug 2024 11:37:04 -0400 Subject: [PATCH 06/25] ADd and implement cypress-axe --- pnpm-lock.yaml | 24 +++ services/cypress/cypress.config.ts | 11 +- .../accessiblility.spec.ts | 141 +++++++++++------- services/cypress/package.json | 2 + services/cypress/support/e2e.ts | 1 + services/cypress/tsconfig.json | 2 +- 6 files changed, 123 insertions(+), 58 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2a5a617c8..5eed759821 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -723,12 +723,18 @@ importers: '@testing-library/cypress': specifier: ^10.0.1 version: 10.0.1(cypress@13.13.2) + axe-core: + specifier: ^4.10.0 + version: 4.10.0 c8: specifier: ^10.1.2 version: 10.1.2 cypress: specifier: ^13.13.2 version: 13.13.2 + cypress-axe: + specifier: ^1.5.0 + version: 1.5.0(axe-core@4.10.0)(cypress@13.13.2) husky: specifier: ^9.1.5 version: 9.1.5 @@ -6296,6 +6302,10 @@ packages: aws4@1.11.0: resolution: {integrity: sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==} + axe-core@4.10.0: + resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==} + engines: {node: '>=4'} + axe-core@4.2.4: resolution: {integrity: sha512-9AiDKFKUCWEQm1Kj4lcq7KFavLqSXdf2m/zJo+NVh4VXlW5iwXRJ6alkKmipCyYorsRnqsICH9XLubP1jBF+Og==} engines: {node: '>=4'} @@ -7163,6 +7173,13 @@ packages: engines: {node: '>= 10'} hasBin: true + cypress-axe@1.5.0: + resolution: {integrity: sha512-Hy/owCjfj+25KMsecvDgo4fC/781ccL+e8p+UUYoadGVM2ogZF9XIKbiM6KI8Y3cEaSreymdD6ZzccbI2bY0lQ==} + engines: {node: '>=10'} + peerDependencies: + axe-core: ^3 || ^4 + cypress: ^10 || ^11 || ^12 || ^13 + cypress-file-upload@5.0.8: resolution: {integrity: sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==} engines: {node: '>=8.2.1'} @@ -22375,6 +22392,8 @@ snapshots: aws4@1.11.0: {} + axe-core@4.10.0: {} + axe-core@4.2.4: {} axe-core@4.7.0: {} @@ -23523,6 +23542,11 @@ snapshots: dependencies: minimist: 1.2.8 + cypress-axe@1.5.0(axe-core@4.10.0)(cypress@13.13.2): + dependencies: + axe-core: 4.10.0 + cypress: 13.13.2 + cypress-file-upload@5.0.8(cypress@13.13.2): dependencies: cypress: 13.13.2 diff --git a/services/cypress/cypress.config.ts b/services/cypress/cypress.config.ts index ec00c676e0..7c63ac50de 100644 --- a/services/cypress/cypress.config.ts +++ b/services/cypress/cypress.config.ts @@ -55,7 +55,6 @@ module.exports = defineConfig({ // Reads graphql schema and converts it to gql for apollo client. on('task', { - pa11y: pa11y(), readGraphQLSchema() { const gqlSchema = fs.readFileSync( path.resolve(__dirname, './gen/schema.graphql'), @@ -63,6 +62,16 @@ module.exports = defineConfig({ ) return gql(`${gqlSchema}`) }, + log(message) { + console.log(message) + + return null + }, + table(message) { + console.table(message) + + return null + } }) return newConfig }, diff --git a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts index f151ea0eec..b8fbc425f1 100644 --- a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts +++ b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts @@ -1,3 +1,25 @@ +// Define at the top of the spec file or just import it +function terminalLog(violations: Record[]) { + console.log(violations) + cy.task( + 'log', + `${violations.length} accessibility violation${ + violations.length === 1 ? '' : 's' + } ${violations.length === 1 ? 'was' : 'were'} detected` + ) + // pluck specific keys to keep the table readable + const violationData: Record[] = violations.map( + ({ id, impact, description, nodes }) => ({ + id, + impact, + description, + nodes: nodes.length + }) + ) + + cy.task('table', violationData) +} + describe('state user in state submission form', () => { beforeEach(() => { cy.stubFeatureFlags() @@ -6,68 +28,75 @@ describe('state user in state submission form', () => { // SKIPPED because we currently have a bug that looks related to https://github.com/mfrachet/cypress-audit/issues/136#issuecomment-1311236777 // this started when we upgraded node and serverless and started default to ipv6 which prevented tests // current this keeps pa11y from running - it.skip('can fill out contract only submission with no a11y errors', () => { + it('can fill out contract only submission with no a11y errors', () => { // goal of this test is to check every single form page and navigation (going backwards, forwards or save as draft with new info) cy.logInAsStateUser() + cy.injectAxe() // Start a base contract only submissions cy.startNewContractOnlySubmissionWithBaseContract() + cy.checkA11y(undefined, undefined, terminalLog) + // cy.findByRole('heading', { + // level: 2, + // name: /Contract details/, + // timeout: 10_000, + // }) + // // cy.checkA11y() + // + // // check contract details a11y + // // cy.pa11y({ + // // hideElements: '.usa-step-indicator', + // // actions: ['wait for element #form-guidance to be visible'], + // // threshold: 6, + // // hideElements: '.usa-step-indicator', + // // }) + // cy.deprecatedNavigateV1Form('BACK') + // // check submissionType a11y + // // cy.pa11y({ + // // hideElements: '.usa-step-indicator', + // // actions: ['wait for element #form-guidance to be visible'], + // // threshold: 6, + // // hideElements: '.usa-step-indicator', + // // }) + // // cy.checkA11y() + // + // cy.deprecatedNavigateV1Form('CONTINUE') + // // cy.checkA11y() + // cy.deprecatedNavigateV1Form('CONTINUE') + // // cy.checkA11y() + // + // cy.findByRole('heading', { level: 2, name: /Contacts/ }) + // // cy.checkA11y() + // cy.fillOutStateContact() + // + // // check contacts a11y + // // cy.pa11y({ + // // hideElements: '.usa-step-indicator', + // // actions: ['wait for element #form-guidance to be visible'], + // // threshold: 6, + // // hideElements: '.usa-step-indicator', + // // }) + // + // cy.deprecatedNavigateV1Form('SAVE_DRAFT') + // cy.findByRole('heading', { level: 1, name: /Submissions dashboard/ }) + // //cy.checkA11y() + // + // + // cy.deprecatedNavigateV1Form('CONTINUE') + // // skip documents page - that will be deleted soon + // cy.deprecatedNavigateV1Form('CONTINUE') + // + // // Check that we end up on Review and Submit + // cy.findByRole('heading', { level: 2, name: /Review and submit/ }) - cy.findByRole('heading', { - level: 2, - name: /Contract details/, - timeout: 10_000, - }) - - // check contract details a11y - cy.pa11y({ - hideElements: '.usa-step-indicator', - actions: ['wait for element #form-guidance to be visible'], - threshold: 6, - hideElements: '.usa-step-indicator', - }) - cy.deprecatedNavigateV1Form('BACK') - // check submissionType a11y - cy.pa11y({ - hideElements: '.usa-step-indicator', - actions: ['wait for element #form-guidance to be visible'], - threshold: 6, - hideElements: '.usa-step-indicator', - }) - - cy.deprecatedNavigateV1Form('CONTINUE') - cy.deprecatedNavigateV1Form('CONTINUE') - - cy.findByRole('heading', { level: 2, name: /Contacts/ }) - cy.fillOutStateContact() - - // check contacts a11y - cy.pa11y({ - hideElements: '.usa-step-indicator', - actions: ['wait for element #form-guidance to be visible'], - threshold: 6, - hideElements: '.usa-step-indicator', - }) - - cy.deprecatedNavigateV1Form('SAVE_DRAFT') - cy.findByRole('heading', { level: 1, name: /Submissions dashboard/ }) - - - cy.deprecatedNavigateV1Form('CONTINUE') - // skip documents page - that will be deleted soon - cy.deprecatedNavigateV1Form('CONTINUE') - - // Check that we end up on Review and Submit - cy.findByRole('heading', { level: 2, name: /Review and submit/ }) - - // check review and submit a11y - cy.pa11y({ - hideElements: '.usa-step-indicator', - actions: ['wait for element #form-guidance to be visible'], - threshold: 6, - hideElements: '.usa-step-indicator', - }) - }) + // check review and submit a11y + // cy.pa11y({ + // hideElements: '.usa-step-indicator', + // actions: ['wait for element #form-guidance to be visible'], + // threshold: 6, + // hideElements: '.usa-step-indicator', + // }) }) +}) diff --git a/services/cypress/package.json b/services/cypress/package.json index 38f0f842bb..1b5a5a64c0 100644 --- a/services/cypress/package.json +++ b/services/cypress/package.json @@ -43,8 +43,10 @@ "@cypress-audit/pa11y": "^1.3.0", "@cypress/code-coverage": "^3.10.0", "@testing-library/cypress": "^10.0.1", + "axe-core": "^4.10.0", "c8": "^10.1.2", "cypress": "^13.13.2", + "cypress-axe": "^1.5.0", "husky": "^9.1.5", "lint-staged": "^15.2.2", "prettier": "^3.3.3" diff --git a/services/cypress/support/e2e.ts b/services/cypress/support/e2e.ts index 3cd559d4c7..321230abbd 100644 --- a/services/cypress/support/e2e.ts +++ b/services/cypress/support/e2e.ts @@ -1,4 +1,5 @@ import '@cypress/code-coverage/support' +import 'cypress-axe' Cypress.on('uncaught:exception', (err, runnable) => { // returning false here prevents Cypress from diff --git a/services/cypress/tsconfig.json b/services/cypress/tsconfig.json index 16c6f54b0d..4db3fa5dfc 100644 --- a/services/cypress/tsconfig.json +++ b/services/cypress/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "target": "ES2017", "lib": ["ES2017", "dom"], - "types": ["cypress", "@testing-library/cypress", "cypress-file-upload", "node"] + "types": ["cypress", "cypress-axe", "@testing-library/cypress", "cypress-file-upload", "node"] }, "include": ["**/*.ts"] } From 3f9a1e2ceedeab40e615bb55c2119f8dfdc46c0f Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 28 Aug 2024 22:55:47 -0400 Subject: [PATCH 07/25] Add accessibility commands --- .../cypress/support/accessibilityCommands.ts | 32 +++++++++++++++++++ services/cypress/support/index.ts | 6 ++++ 2 files changed, 38 insertions(+) create mode 100644 services/cypress/support/accessibilityCommands.ts diff --git a/services/cypress/support/accessibilityCommands.ts b/services/cypress/support/accessibilityCommands.ts new file mode 100644 index 0000000000..1d2553d904 --- /dev/null +++ b/services/cypress/support/accessibilityCommands.ts @@ -0,0 +1,32 @@ +function terminalLog(violations: Record[]) { + console.log(violations) + cy.task( + 'log', + `${violations.length} accessibility violation${ + violations.length === 1 ? '' : 's' + } ${violations.length === 1 ? 'was' : 'were'} detected` + ) + // pluck specific keys to keep the table readable + const violationData: Record[] = violations.map( + ({ id, impact, description, nodes }) => ({ + id, + impact, + description, + nodes: nodes.length + }) + ) + + cy.task('table', violationData) +} + +Cypress.Commands.add( + 'checkA11yWithSection508', + () => { + cy.checkA11y('', { + runOnly: { + type: 'tag', + values: ['section508'] + }, + }, terminalLog) + } +) diff --git a/services/cypress/support/index.ts b/services/cypress/support/index.ts index 5ce7b5799a..34ce51c0ae 100644 --- a/services/cypress/support/index.ts +++ b/services/cypress/support/index.ts @@ -26,6 +26,7 @@ import { FeatureFlagSettings, } from '../../app-web/src/common-code/featureFlags' import './apiCommands' +import './accessibilityCommands' import { Contract, HealthPlanPackage } from '../gen/gqlClient'; import { CMSUserType, DivisionType } from '../utils/apollo-test-utils'; import { StateUserType } from '../../app-api/src/domain-models'; @@ -110,6 +111,7 @@ declare global { division: DivisionType }): void + // Direct API commands apiCreateAndSubmitContractOnlySubmission(stateUser: StateUserType): Cypress.Chainable apiCreateAndSubmitContractWithRates(stateUser: StateUserType): Cypress.Chainable apiDeprecatedCreateSubmitHPP(stateUser: StateUserType, formData?: Partial): Cypress.Chainable @@ -117,7 +119,11 @@ declare global { apiAssignDivisionToCMSUser(cmsUser: CMSUserType, division: DivisionType): Cypress.Chainable apiCreateAndSubmitContractWithRates(stateUser: StateUserType): Cypress.Chainable + // GraphQL intercept commands interceptGraphQL(): void + + // Accessibility Commands + checkA11yWithSection508(): void } } } From 64f664fe7973d099dfa38a51ded47d7a92b3ecf3 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 28 Aug 2024 22:56:12 -0400 Subject: [PATCH 08/25] Fix launch darkly intercepts for local deployment. --- .../cypress/support/launchDarklyCommands.ts | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/services/cypress/support/launchDarklyCommands.ts b/services/cypress/support/launchDarklyCommands.ts index 4c2cce6c57..f0dc718627 100644 --- a/services/cypress/support/launchDarklyCommands.ts +++ b/services/cypress/support/launchDarklyCommands.ts @@ -41,10 +41,14 @@ Cypress.Commands.add( JSON.stringify(featureFlagObject) ) + const clientSDKMatchers = Cypress.env('AUTH_MODE') === 'LOCAL' ? + { method: 'GET', pathname: /^\/ld-clientsdk(\/.*)?$/ } : + { method: 'GET', hostname: /\.*clientsdk\.launchdarkly\.us/ } + // Intercepts LD request and returns with our own feature flags and values. return cy .intercept( - { method: 'GET', hostname: /\.*clientsdk\.launchdarkly\.us/ }, + clientSDKMatchers, { body: featureFlagObject } ) .as('LDApp') @@ -54,24 +58,32 @@ Cypress.Commands.add( // Intercepting feature flag api calls and returns some response. This should stop the app from calling making requests to LD. Cypress.Commands.add('stubFeatureFlags', () => { // ignore api calls to events endpoint + const eventMatchers = Cypress.env('AUTH_MODE') === 'LOCAL' ? + { method: 'POST', pathname: /^\/ld-events(\/.*)?$/ } : + { method: 'POST', hostname: /\.*events\.launchdarkly\.us/ } + cy.intercept( - { method: 'POST', hostname: /\.*events\.launchdarkly\.us/ }, + eventMatchers, // { body: {} } (req) => { req.on('response', (res) => { - res.setDelay(60000) + res.setDelay(15000) }) req.reply({ body: {} }) } ).as('LDEvents') + const clientStreamMatchers = Cypress.env('AUTH_MODE') === 'LOCAL' ? + { method: 'GET', pathname: /^\/ld-clientstream(\/.*)?$/ } : + { method: 'GET', hostname: /\.*clientstream\.launchdarkly\.us/ } + // turn off push updates from LaunchDarkly (EventSource) cy.intercept( - { method: 'GET', hostname: /\.*clientstream\.launchdarkly\.us/ }, + clientStreamMatchers, // access the request handler and stub a response (req) => { req.on('response', (res) => { - res.setDelay(60000) + res.setDelay(15000) }) req.reply('data: no streaming feature flag data here\n\n', { 'content-type': 'text/event-stream; charset=utf-8', From 2167a6c731165bf947658d70f75341d0a378de61 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 28 Aug 2024 22:56:51 -0400 Subject: [PATCH 09/25] Add two accessibility tests. --- .../accessiblility.spec.ts | 186 ++++++++++-------- 1 file changed, 99 insertions(+), 87 deletions(-) diff --git a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts index b8fbc425f1..062ffe1800 100644 --- a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts +++ b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts @@ -1,102 +1,114 @@ -// Define at the top of the spec file or just import it -function terminalLog(violations: Record[]) { - console.log(violations) - cy.task( - 'log', - `${violations.length} accessibility violation${ - violations.length === 1 ? '' : 's' - } ${violations.length === 1 ? 'was' : 'were'} detected` - ) - // pluck specific keys to keep the table readable - const violationData: Record[] = violations.map( - ({ id, impact, description, nodes }) => ({ - id, - impact, - description, - nodes: nodes.length - }) - ) - - cy.task('table', violationData) -} +import { stateUser } from '../../../utils/apollo-test-utils'; describe('state user in state submission form', () => { beforeEach(() => { cy.stubFeatureFlags() cy.interceptGraphQL() }) - // SKIPPED because we currently have a bug that looks related to https://github.com/mfrachet/cypress-audit/issues/136#issuecomment-1311236777 - // this started when we upgraded node and serverless and started default to ipv6 which prevented tests - // current this keeps pa11y from running - it('can fill out contract only submission with no a11y errors', () => { + it('has not a11y errors with submission form and and form erros', () => { // goal of this test is to check every single form page and navigation (going backwards, forwards or save as draft with new info) - + cy.interceptFeatureFlags({'438-attestation': true}) cy.logInAsStateUser() + + // Inject the axe run-time cy.injectAxe() // Start a base contract only submissions - cy.startNewContractOnlySubmissionWithBaseContract() - cy.checkA11y(undefined, undefined, terminalLog) + cy.findByTestId('state-dashboard-page').should('exist') + cy.findByRole('link', { name: 'Start new submission' }).click() + + // Check accessibility on Submission type page + cy.findByRole('heading', { level: 1, name: /New submission/ }) + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithSection508() + + cy.fillOutContractActionAndRateCertification() + cy.deprecatedNavigateV1Form('CONTINUE_FROM_START_NEW') + + cy.findByRole('heading', { level: 2, name: /Contract details/ }) + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithSection508() + + cy.location().then((fullUrl) => { + const submissionURL = fullUrl.toString().replace( + 'edit/contract-details', + '' + ) - // cy.findByRole('heading', { - // level: 2, - // name: /Contract details/, - // timeout: 10_000, - // }) - // // cy.checkA11y() - // - // // check contract details a11y - // // cy.pa11y({ - // // hideElements: '.usa-step-indicator', - // // actions: ['wait for element #form-guidance to be visible'], - // // threshold: 6, - // // hideElements: '.usa-step-indicator', - // // }) - // cy.deprecatedNavigateV1Form('BACK') - // // check submissionType a11y - // // cy.pa11y({ - // // hideElements: '.usa-step-indicator', - // // actions: ['wait for element #form-guidance to be visible'], - // // threshold: 6, - // // hideElements: '.usa-step-indicator', - // // }) - // // cy.checkA11y() - // - // cy.deprecatedNavigateV1Form('CONTINUE') - // // cy.checkA11y() - // cy.deprecatedNavigateV1Form('CONTINUE') - // // cy.checkA11y() - // - // cy.findByRole('heading', { level: 2, name: /Contacts/ }) - // // cy.checkA11y() - // cy.fillOutStateContact() - // - // // check contacts a11y - // // cy.pa11y({ - // // hideElements: '.usa-step-indicator', - // // actions: ['wait for element #form-guidance to be visible'], - // // threshold: 6, - // // hideElements: '.usa-step-indicator', - // // }) - // - // cy.deprecatedNavigateV1Form('SAVE_DRAFT') - // cy.findByRole('heading', { level: 1, name: /Submissions dashboard/ }) - // //cy.checkA11y() - // - // - // cy.deprecatedNavigateV1Form('CONTINUE') - // // skip documents page - that will be deleted soon - // cy.deprecatedNavigateV1Form('CONTINUE') - // - // // Check that we end up on Review and Submit - // cy.findByRole('heading', { level: 2, name: /Review and submit/ }) + // Check accessibility on rate details page + cy.navigateFormByDirectLink(`${submissionURL}edit/rate-details`) + cy.findByRole('radiogroup', { + name: /Was this rate certification included with another submission?/, + }) + .should('exist') + .within(() => { + cy.findByText('No, this rate certification was not included with any other submissions').click() + }) + cy.injectAxe() + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithSection508() - // check review and submit a11y - // cy.pa11y({ - // hideElements: '.usa-step-indicator', - // actions: ['wait for element #form-guidance to be visible'], - // threshold: 6, - // hideElements: '.usa-step-indicator', - // }) + //Check accessibility on contacts page + cy.navigateFormByDirectLink(`${submissionURL}edit/contacts`) + cy.findByRole('heading', { level: 2, name: /Contacts/ }) + cy.injectAxe() + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithSection508() + + //Check accessibility on documents page + cy.navigateFormByDirectLink(`${submissionURL}edit/documents`) + cy.findByRole('heading', { level: 2, name: /Supporting documents/ }) + cy.injectAxe() + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithSection508() + + //Check accessibility on review and submit page + cy.navigateFormByDirectLink(`${submissionURL}edit/review-and-submit`) + cy.findByRole('heading', { level: 2, name: /Review and submit/ }) + cy.injectAxe() + cy.checkA11yWithSection508() + + //Check the dashboard + cy.navigateContractRatesForm('SAVE_DRAFT', false) + cy.checkA11yWithSection508() + }) + }) + + it('has no errors on CMS dashboard', () => { + cy.apiCreateAndSubmitContractWithRates(stateUser()).then(() => { + cy.logInAsCMSUser() + cy.injectAxe() + //check submissions tab + cy.checkA11yWithSection508() + + //check rate reviews tab + cy.findByRole('tab', { name: 'Rate reviews' }).should('exist').click() + cy.checkA11yWithSection508() + }) }) }) From 0bc8e796c1318325a253bcab0b4d95ba4f9e2786 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 28 Aug 2024 23:39:24 -0400 Subject: [PATCH 10/25] Reverting bad merge From 7861efd560f0e6e16408e769d9147d0daefcc272 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 28 Aug 2024 23:47:49 -0400 Subject: [PATCH 11/25] lock file --- pnpm-lock.yaml | 63 ++++++++------------------------------------------ 1 file changed, 9 insertions(+), 54 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5eed759821..e833c5cdd1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -736,7 +736,7 @@ importers: specifier: ^1.5.0 version: 1.5.0(axe-core@4.10.0)(cypress@13.13.2) husky: - specifier: ^9.1.5 + specifier: ^9.0.11 version: 9.1.5 lint-staged: specifier: ^15.2.2 @@ -13757,10 +13757,10 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -14070,10 +14070,10 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -14155,7 +14155,7 @@ snapshots: '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -14776,7 +14776,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -15074,7 +15074,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -15352,24 +15352,6 @@ snapshots: '@aws-sdk/types': 3.6.1 tslib: 1.14.1 - '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': - dependencies: - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/credential-provider-env': 3.609.0 - '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/client-sts': 3.637.0 @@ -15432,25 +15414,6 @@ snapshots: '@aws-sdk/types': 3.6.1 tslib: 1.14.1 - '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': - dependencies: - '@aws-sdk/credential-provider-env': 3.609.0 - '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - '@aws-sdk/client-sts' - - aws-crt - '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/credential-provider-env': 3.609.0 @@ -15562,14 +15525,6 @@ snapshots: '@aws-sdk/types': 3.186.0 tslib: 2.6.3 - '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0)': - dependencies: - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/client-sts': 3.637.0 @@ -16162,7 +16117,7 @@ snapshots: '@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: - '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 From cfae62b126c45fabc062342b91c47ee7e2a71bb4 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 28 Aug 2024 23:59:19 -0400 Subject: [PATCH 12/25] Reverting bad merge again From 585d269c1f16dac8917a4edeebd913e60f3dd5ad Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 29 Aug 2024 00:07:02 -0400 Subject: [PATCH 13/25] Fix lock file. --- pnpm-lock.yaml | 2 +- services/cypress/package.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e833c5cdd1..0e772878a8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -736,7 +736,7 @@ importers: specifier: ^1.5.0 version: 1.5.0(axe-core@4.10.0)(cypress@13.13.2) husky: - specifier: ^9.0.11 + specifier: ^9.1.5 version: 9.1.5 lint-staged: specifier: ^15.2.2 diff --git a/services/cypress/package.json b/services/cypress/package.json index 9567b9b747..1b5a5a64c0 100644 --- a/services/cypress/package.json +++ b/services/cypress/package.json @@ -47,16 +47,16 @@ "c8": "^10.1.2", "cypress": "^13.13.2", "cypress-axe": "^1.5.0", - "husky": "^9.0.11", + "husky": "^9.1.5", "lint-staged": "^15.2.2", "prettier": "^3.3.3" }, "dependencies": { "@apollo/client": "3.8.8", - "aws-amplify": "^5.0.10", - "axios": "^1.7.4", "cypress-file-upload": "^5.0.8", "cypress-pipe": "^2.0.0", + "aws-amplify": "^5.0.10", + "axios": "^1.7.4", "graphql": "^16.9.0", "protobufjs": "^7.3.0" } From f6ff9a98f210ddfd77e0358923c128025433ac59 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 29 Aug 2024 00:12:54 -0400 Subject: [PATCH 14/25] Fix lock file. --- pnpm-lock.yaml | 61 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e772878a8..5eed759821 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13757,10 +13757,10 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -14070,10 +14070,10 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -14155,7 +14155,7 @@ snapshots: '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -14776,7 +14776,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -15074,7 +15074,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -15352,6 +15352,24 @@ snapshots: '@aws-sdk/types': 3.6.1 tslib: 1.14.1 + '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': + dependencies: + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/credential-provider-env': 3.609.0 + '@aws-sdk/credential-provider-http': 3.609.0 + '@aws-sdk/credential-provider-process': 3.609.0 + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/types': 3.609.0 + '@smithy/credential-provider-imds': 3.2.0 + '@smithy/property-provider': 3.1.3 + '@smithy/shared-ini-file-loader': 3.1.4 + '@smithy/types': 3.3.0 + tslib: 2.6.3 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/client-sts': 3.637.0 @@ -15414,6 +15432,25 @@ snapshots: '@aws-sdk/types': 3.6.1 tslib: 1.14.1 + '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': + dependencies: + '@aws-sdk/credential-provider-env': 3.609.0 + '@aws-sdk/credential-provider-http': 3.609.0 + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-process': 3.609.0 + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/types': 3.609.0 + '@smithy/credential-provider-imds': 3.2.0 + '@smithy/property-provider': 3.1.3 + '@smithy/shared-ini-file-loader': 3.1.4 + '@smithy/types': 3.3.0 + tslib: 2.6.3 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - '@aws-sdk/client-sts' + - aws-crt + '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/credential-provider-env': 3.609.0 @@ -15525,6 +15562,14 @@ snapshots: '@aws-sdk/types': 3.186.0 tslib: 2.6.3 + '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0)': + dependencies: + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/types': 3.609.0 + '@smithy/property-provider': 3.1.3 + '@smithy/types': 3.3.0 + tslib: 2.6.3 + '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/client-sts': 3.637.0 @@ -16117,7 +16162,7 @@ snapshots: '@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: - '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 From 58fdd780bf083268a99582a1a57aab2ede96e791 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 29 Aug 2024 00:20:24 -0400 Subject: [PATCH 15/25] Remove pa11y. --- services/cypress/cypress.config.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/services/cypress/cypress.config.ts b/services/cypress/cypress.config.ts index 7c63ac50de..ad7d2585bb 100644 --- a/services/cypress/cypress.config.ts +++ b/services/cypress/cypress.config.ts @@ -1,6 +1,5 @@ const { defineConfig } = require('cypress') const { gql } = require('@apollo/client') -const { pa11y, prepareAudit } = require('@cypress-audit/pa11y') const fs = require('fs') const path = require('path') const createBundler = require('@bahmutov/cypress-esbuild-preprocessor') @@ -49,9 +48,6 @@ module.exports = defineConfig({ process.env.COGNITO_IDENTITY_POOL_ID newConfig.env.COGNITO_USER_POOL_WEB_CLIENT_ID = process.env.COGNITO_USER_POOL_WEB_CLIENT_ID - on('before:browser:launch', (browser, launchOptions) => { - prepareAudit(launchOptions) - }) // Reads graphql schema and converts it to gql for apollo client. on('task', { From 14c2fe626bcf86c58326c14bd850447b1501128f Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 29 Aug 2024 00:21:03 -0400 Subject: [PATCH 16/25] Add axe-core and cypress-axe to root package.json --- package.json | 2 ++ pnpm-lock.yaml | 75 ++++++++++---------------------------------------- 2 files changed, 17 insertions(+), 60 deletions(-) diff --git a/package.json b/package.json index 688da72bab..cb6af428ab 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,8 @@ "devDependencies": { "c8": "^10.1.2", "cypress": "^13.13.2", + "axe-core": "^4.10.0", + "cypress-axe": "^1.5.0", "danger": "^11.2.6", "husky": "^9.1.5", "lint-staged": "^15.2.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5eed759821..838f12c693 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,18 @@ importers: .: devDependencies: + axe-core: + specifier: ^4.10.0 + version: 4.10.0 c8: specifier: ^10.1.2 version: 10.1.2 cypress: specifier: ^13.13.2 version: 13.13.2 + cypress-axe: + specifier: ^1.5.0 + version: 1.5.0(axe-core@4.10.0)(cypress@13.13.2) danger: specifier: ^11.2.6 version: 11.3.1(encoding@0.1.13) @@ -6314,10 +6320,6 @@ packages: resolution: {integrity: sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==} engines: {node: '>=4'} - axe-core@4.8.1: - resolution: {integrity: sha512-9l850jDDPnKq48nbad8SiEelCv4OrUWrKab/cPj0GScVg6cb6NbCCt/Ulk26QEq5jP9NnGr04Bit1BHyV6r5CQ==} - engines: {node: '>=4'} - axios@0.26.0: resolution: {integrity: sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==} @@ -13757,10 +13759,10 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -14070,10 +14072,10 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -14155,7 +14157,7 @@ snapshots: '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -14776,7 +14778,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -15074,7 +15076,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -15352,24 +15354,6 @@ snapshots: '@aws-sdk/types': 3.6.1 tslib: 1.14.1 - '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': - dependencies: - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/credential-provider-env': 3.609.0 - '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/client-sts': 3.637.0 @@ -15432,25 +15416,6 @@ snapshots: '@aws-sdk/types': 3.6.1 tslib: 1.14.1 - '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': - dependencies: - '@aws-sdk/credential-provider-env': 3.609.0 - '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - '@aws-sdk/client-sts' - - aws-crt - '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/credential-provider-env': 3.609.0 @@ -15562,14 +15527,6 @@ snapshots: '@aws-sdk/types': 3.186.0 tslib: 2.6.3 - '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0)': - dependencies: - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/client-sts': 3.637.0 @@ -16162,7 +16119,7 @@ snapshots: '@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: - '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 @@ -20758,7 +20715,7 @@ snapshots: '@storybook/addon-a11y@8.2.9(storybook@8.2.9(@babel/preset-env@7.24.8(@babel/core@7.24.7)))': dependencies: '@storybook/addon-highlight': 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.8(@babel/core@7.24.7))) - axe-core: 4.8.1 + axe-core: 4.10.0 storybook: 8.2.9(@babel/preset-env@7.24.8(@babel/core@7.24.7)) '@storybook/addon-actions@8.2.9(storybook@8.2.9(@babel/preset-env@7.24.8(@babel/core@7.24.7)))': @@ -22398,8 +22355,6 @@ snapshots: axe-core@4.7.0: {} - axe-core@4.8.1: {} - axios@0.26.0: dependencies: follow-redirects: 1.15.6 From edd896057e01114dd32a6876d5fad8b2087cb6f7 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 29 Aug 2024 12:49:26 -0400 Subject: [PATCH 17/25] Cleanup --- services/cypress/package.json | 3 +-- services/cypress/support/loginCommands.ts | 2 -- services/cypress/support/questionResponseCommands.ts | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/services/cypress/package.json b/services/cypress/package.json index 1b5a5a64c0..19bf728ebf 100644 --- a/services/cypress/package.json +++ b/services/cypress/package.json @@ -57,7 +57,6 @@ "cypress-pipe": "^2.0.0", "aws-amplify": "^5.0.10", "axios": "^1.7.4", - "graphql": "^16.9.0", - "protobufjs": "^7.3.0" + "graphql": "^16.9.0" } } diff --git a/services/cypress/support/loginCommands.ts b/services/cypress/support/loginCommands.ts index 12429d32e8..94c12ef753 100644 --- a/services/cypress/support/loginCommands.ts +++ b/services/cypress/support/loginCommands.ts @@ -1,5 +1,3 @@ -import { aliasQuery } from '../utils/graphql-test-utils' - Cypress.Commands.add('logInAsStateUser', () => { // Set up gql intercept for requests on app load diff --git a/services/cypress/support/questionResponseCommands.ts b/services/cypress/support/questionResponseCommands.ts index e25f16ffcb..18dcbeef66 100644 --- a/services/cypress/support/questionResponseCommands.ts +++ b/services/cypress/support/questionResponseCommands.ts @@ -1,5 +1,3 @@ -import {aliasMutation, aliasQuery} from '../utils/graphql-test-utils'; - Cypress.Commands.add( 'addQuestion', ({ documentPath }: { documentPath: string }) => { From 833c0ae5a401bceab1ac297823b1345cbd8d1df6 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 29 Aug 2024 12:49:58 -0400 Subject: [PATCH 18/25] Use WCAG 2.2 Level AA --- .../accessiblility.spec.ts | 22 +++++++++---------- .../cypress/support/accessibilityCommands.ts | 4 ++-- services/cypress/support/index.ts | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts index 062ffe1800..979e148785 100644 --- a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts +++ b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts @@ -6,8 +6,8 @@ describe('state user in state submission form', () => { cy.interceptGraphQL() }) it('has not a11y errors with submission form and and form erros', () => { - // goal of this test is to check every single form page and navigation (going backwards, forwards or save as draft with new info) - cy.interceptFeatureFlags({'438-attestation': true}) + // 438-attestation still needs to go through design, there is an a11y violation for links and spacing + cy.interceptFeatureFlags({'438-attestation': false}) cy.logInAsStateUser() // Inject the axe run-time @@ -25,7 +25,7 @@ describe('state user in state submission form', () => { cy.findByRole('button', { name: 'Continue', }).safeClick() - cy.checkA11yWithSection508() + cy.checkA11yWithWcag22aa() cy.fillOutContractActionAndRateCertification() cy.deprecatedNavigateV1Form('CONTINUE_FROM_START_NEW') @@ -37,7 +37,7 @@ describe('state user in state submission form', () => { cy.findByRole('button', { name: 'Continue', }).safeClick() - cy.checkA11yWithSection508() + cy.checkA11yWithWcag22aa() cy.location().then((fullUrl) => { const submissionURL = fullUrl.toString().replace( @@ -61,7 +61,7 @@ describe('state user in state submission form', () => { cy.findByRole('button', { name: 'Continue', }).safeClick() - cy.checkA11yWithSection508() + cy.checkA11yWithWcag22aa() //Check accessibility on contacts page cy.navigateFormByDirectLink(`${submissionURL}edit/contacts`) @@ -73,7 +73,7 @@ describe('state user in state submission form', () => { cy.findByRole('button', { name: 'Continue', }).safeClick() - cy.checkA11yWithSection508() + cy.checkA11yWithWcag22aa() //Check accessibility on documents page cy.navigateFormByDirectLink(`${submissionURL}edit/documents`) @@ -85,17 +85,17 @@ describe('state user in state submission form', () => { cy.findByRole('button', { name: 'Continue', }).safeClick() - cy.checkA11yWithSection508() + cy.checkA11yWithWcag22aa() //Check accessibility on review and submit page cy.navigateFormByDirectLink(`${submissionURL}edit/review-and-submit`) cy.findByRole('heading', { level: 2, name: /Review and submit/ }) cy.injectAxe() - cy.checkA11yWithSection508() + cy.checkA11yWithWcag22aa() //Check the dashboard cy.navigateContractRatesForm('SAVE_DRAFT', false) - cy.checkA11yWithSection508() + cy.checkA11yWithWcag22aa() }) }) @@ -104,11 +104,11 @@ describe('state user in state submission form', () => { cy.logInAsCMSUser() cy.injectAxe() //check submissions tab - cy.checkA11yWithSection508() + cy.checkA11yWithWcag22aa() //check rate reviews tab cy.findByRole('tab', { name: 'Rate reviews' }).should('exist').click() - cy.checkA11yWithSection508() + cy.checkA11yWithWcag22aa() }) }) }) diff --git a/services/cypress/support/accessibilityCommands.ts b/services/cypress/support/accessibilityCommands.ts index 1d2553d904..d5d6add1c9 100644 --- a/services/cypress/support/accessibilityCommands.ts +++ b/services/cypress/support/accessibilityCommands.ts @@ -20,12 +20,12 @@ function terminalLog(violations: Record[]) { } Cypress.Commands.add( - 'checkA11yWithSection508', + 'checkA11yWithWcag22aa', () => { cy.checkA11y('', { runOnly: { type: 'tag', - values: ['section508'] + values: ['wcag22aa'] }, }, terminalLog) } diff --git a/services/cypress/support/index.ts b/services/cypress/support/index.ts index 34ce51c0ae..d303605185 100644 --- a/services/cypress/support/index.ts +++ b/services/cypress/support/index.ts @@ -123,7 +123,7 @@ declare global { interceptGraphQL(): void // Accessibility Commands - checkA11yWithSection508(): void + checkA11yWithWcag22aa(): void } } } From e60db8f59bb348198e1bea0296021e651ee4167d Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 29 Aug 2024 12:51:30 -0400 Subject: [PATCH 19/25] Lock file --- pnpm-lock.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 838f12c693..7d59c95aa2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -713,9 +713,6 @@ importers: graphql: specifier: ^16.9.0 version: 16.9.0 - protobufjs: - specifier: ^7.3.0 - version: 7.3.2 devDependencies: '@bahmutov/cypress-esbuild-preprocessor': specifier: ^2.2.0 From 3402246752f5e215f8aa1deee5958ee653012a1e Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 29 Aug 2024 16:24:33 -0400 Subject: [PATCH 20/25] Add documentation --- .../005-frontend-a11y-toolset.md | 22 +++- services/cypress/README.md | 100 +++++++++++++++++- 2 files changed, 119 insertions(+), 3 deletions(-) diff --git a/docs/architectural-decision-records/005-frontend-a11y-toolset.md b/docs/architectural-decision-records/005-frontend-a11y-toolset.md index 40035553a9..864f4037ea 100644 --- a/docs/architectural-decision-records/005-frontend-a11y-toolset.md +++ b/docs/architectural-decision-records/005-frontend-a11y-toolset.md @@ -1,6 +1,6 @@ # Define Frontend Toolset for Accessibility Standards -Decide on a toolset to help ensure MCRRS frontend development follows accessibility standards (specifically WCAG AA guidelines). +Decide on a toolset to help ensure MCRRS frontend development follows accessibility standards (specifically WCAG 2.2 AA guidelines). Tooling is not a replacement for engineers/designers with strong expertise in HTML or manual testing with assistive technology. Thus, the toolset will focus on the types of accessibility issues where tooling is useful. This includes validating HTML attributes, checking basic page structure, ensuring media content has descriptive tags, ensuring that buttons are used appropriately, and checking for basic color contrast. @@ -19,7 +19,7 @@ Together these tools include linting, testing, and auditing loaded web pages for [eslint jsx-a11y][eslint-a11y] Provides React-friendly code linting which allows developers to find accessibility issues during local development. -[pa11y][pa11y] and [pa11y-ci][pa11y-ci] - Accessibility test runner for end to end testing in the command line or Node.js. End to end testing can catch concerns that are not noticeable in linting or unit tests since tests run on browser. +[cypress-axe][cypress-axe] and [axe-core][axe-copre] - Accessibility test runner for end to end testing in the command line or Node.js. End to end testing can catch concerns that are not noticeable in linting or unit tests since tests run on browser. Our previous choice [pa11y][pa11y] seems to not be maintained anymore and no longer works with our end to end testing. [jest-axe][jest-axe] - This tool provides additional jest matchers, like `.toHaveNoViolations`, to use in unit tests. This may be redundant when end to end accessibility testing is present. @@ -43,12 +43,27 @@ Together these tools include linting, testing, and auditing loaded web pages for - `+` Allows the team to get a Lighthouse report alongside every PR. - `-` Seemed too robust for this stage of the project (pilot only), provides a lot of information about performance that is not relevant to us now. May be useful later on. +#### [cypress-axe][cypress-axe] and [axe-core][axe-core] +- `+` `axe-core` is a well known and maintained a11y testing engine +- `+` `axe-core` has great documentation +- `+` `cypress-axe` can run tests with specific a11y standards like `WCAG 2.2 AA`. +- `+` `cypress-axe` is a Cypress plugin for axe-core and most of the configuration for this plugin follows `axe-core` which has great documentation. +- `-` `cypress-axe` itself has sparse documentation +- `-` `cypress-axe` is very minimal in features and if we wanted specific things like reporting, we would have to implement that ourselves. + +#### [Cypress Accessibility][cypress-accessibility] +- `+` Built in accessibility testing by Cypress. +- `-` Still in early access and not much is known about implementation or documentation. +- `-` As of now it looks like it would cost money in addition to what we already pay for Cypress. +- `-` To get access we have to sign up for early access as a trial. + #### [pa11y][pa11y] and [pa11y-ci][pa11y-ci] - `+` pa11y-ci has clear patterns for use in ci - `+` Unlike other options explored, includes ‘actions’ to be used within a test to interact with the page under test. - `+` Has significant documentation. - `-` Could slow development processes if its set too strict. However, many examples of how to optimize configuration as needed in documentation and tutorial +- `-` No longer works with our end to end testing tool Cypress #### [eslint-jsx-a11y][eslint-a11y] & [addon-a11y][storybook-addon-a11y] @@ -63,3 +78,6 @@ Together these tools include linting, testing, and auditing loaded web pages for [axe-core-cli]: https://www.npmjs.com/package/@axe-core/cli [cypress-audit]: https://github.com/mfrachet/cypress-audit [lighthouse-ci]: https://github.com/GoogleChrome/lighthouse-ci +[cypress-axe]: https://github.com/component-driven/cypress-axe +[axe-core]: https://github.com/dequelabs/axe-core +[cypress-accessibility]: https://www.cypress.io/blog/introducing-cypress-accessibility diff --git a/services/cypress/README.md b/services/cypress/README.md index 1c81928c5e..fb45d787a0 100644 --- a/services/cypress/README.md +++ b/services/cypress/README.md @@ -68,6 +68,105 @@ CI deployment is congifured in `.github/workflows/deploy.yml`. LaunchDarkly integration docs can be found in [launch-darkly-testing-approach.md](../../docs/technical-design/launch-darkly-testing-approach.md#feature-flag-cypress-testing) +### Accessibility Testing + +We are using the `cypress-axe` plugin to run our a11y tests. `cypress-axe` uses the `axe-core` testing engine. + +Our implementation is almost exactly like the [documentation](https://github.com/component-driven/cypress-axe) for `cypress-axe`. The only difference is our custom Cypress command `cy.checkA11yWithWcag22aa()` which is configured to only use `WCAG 2.2 AA` standard for tests. This follows our to follow our ADR [frontend-a11y-toolset](/docs/architectural-decision-records/005-frontend-a11y-toolset.md). + +To run axe inject the `axe-core` runtime using `cy.injectAxe()` after `cy.visit()`. Then you can call the custom command `cy.checkA11yWithWcag22aa()` to check a11y against the page. Each time you use `cy.visit()` you will need to inject the runtime again. + +The `checkA11yWithWcag22aa` command configures `cy.checkA11y()` with the WCAG 2.2 AA standard we use for the MC-Review app. In the `cy.checkA11y()` there are more options we can configure the check with. The options are specified in the [axe-core documentation](https://www.deque.com/axe/core-documentation/api-documentation). +```typescript +Cypress.Commands.add( + 'checkA11yWithWcag22aa', + () => { + cy.checkA11y('', { + runOnly: { + type: 'tag', + values: ['wcag22aa'] + }, + }, terminalLog) + } +) +``` + +In the example below, `cy.logInAsStateUser()` calls `cy.visit()` so we can inject the runtime after and then run the a11y checks. +Using the app to navigate allows us to check a11y without injecting the runtime again because it is still available + +```typescript + it('has not a11y errors with submission form and and form erros', () => { + // 438-attestation still needs to go through design, there is an a11y violation for links and spacing + cy.interceptFeatureFlags({'438-attestation': false}) + cy.logInAsStateUser() + + // Inject the axe run-time + cy.injectAxe() + + // Start a base contract only submissions + cy.findByTestId('state-dashboard-page').should('exist') + cy.findByRole('link', { name: 'Start new submission' }).click() + + // Check accessibility on Submission type page + cy.findByRole('heading', { level: 1, name: /New submission/ }) + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithWcag22aa() + + cy.fillOutContractActionAndRateCertification() + cy.deprecatedNavigateV1Form('CONTINUE_FROM_START_NEW') + + cy.findByRole('heading', { level: 2, name: /Contract details/ }) + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithWcag22aa() + ... +``` + +In the next portion of the same test, we are then navigating by direct links using `cy.visit()` wrapped by our custom command `navigateFormByDirectLink()`. Now that we called `cy.visit()` the last injection of the runtime is unavailable and will need to be re-injected. + +```typescript + ... + // Check accessibility on rate details page + cy.navigateFormByDirectLink(`${submissionURL}edit/rate-details`) + cy.findByRole('radiogroup', { + name: /Was this rate certification included with another submission?/, + }) + .should('exist') + .within(() => { + cy.findByText('No, this rate certification was not included with any other submissions').click() + }) + cy.injectAxe() + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithWcag22aa() + + //Check accessibility on contacts page + cy.navigateFormByDirectLink(`${submissionURL}edit/contacts`) + cy.findByRole('heading', { level: 2, name: /Contacts/ }) + cy.injectAxe() + cy.findByRole('button', { + name: 'Continue', + }).should('not.have.attr', 'aria-disabled') + cy.findByRole('button', { + name: 'Continue', + }).safeClick() + cy.checkA11yWithWcag22aa() + ... +``` + ### Direct API Requests The reason for making direct API request through Cypress was to speed up testing by quickly making a new submission to test against. For example, the `questionResponse.spec` test only tests Q&A features but needed a submission in order to get to Q&A, so a new submission was needed and without API request Cypress had to manually go through the state submission form. Now we make a new submission using direct API request which is much faster. @@ -82,7 +181,6 @@ We mimicked the same setup in the application in Cypress to make graphql request - In `cypress.config` under `setupNodeEvents` there is a task, `readGraphQLSchema`, that can be called in Cypress to load our `GraphQL` schema and convert it to `gql`. ```ts on('task', { - pa11y: pa11y(), readGraphQLSchema() { const gqlSchema = fs.readFileSync( path.resolve(__dirname, './gen/schema.graphql'), From 09dea908a1a153d9aa25213e84d3d2be0985db10 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 29 Aug 2024 17:16:08 -0400 Subject: [PATCH 21/25] Rename tests --- .../stateWorkflow/stateSubmissionForm/accessiblility.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts index 979e148785..21eac963c3 100644 --- a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts +++ b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts @@ -5,7 +5,7 @@ describe('state user in state submission form', () => { cy.stubFeatureFlags() cy.interceptGraphQL() }) - it('has not a11y errors with submission form and and form erros', () => { + it('has no a11y violations on submission form with form input errors', () => { // 438-attestation still needs to go through design, there is an a11y violation for links and spacing cy.interceptFeatureFlags({'438-attestation': false}) cy.logInAsStateUser() @@ -99,7 +99,7 @@ describe('state user in state submission form', () => { }) }) - it('has no errors on CMS dashboard', () => { + it('has no a11y violations on CMS dashboards', () => { cy.apiCreateAndSubmitContractWithRates(stateUser()).then(() => { cy.logInAsCMSUser() cy.injectAxe() From 6fe51575cab1f37cad8cc22e1be5d0951cb67174 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Fri, 30 Aug 2024 15:29:58 -0400 Subject: [PATCH 22/25] Update ADR --- .../005-frontend-a11y-toolset.md | 24 ++------ .../028-updated-frontend-a11y-toolset.md | 60 +++++++++++++++++++ 2 files changed, 64 insertions(+), 20 deletions(-) create mode 100644 docs/architectural-decision-records/028-updated-frontend-a11y-toolset.md diff --git a/docs/architectural-decision-records/005-frontend-a11y-toolset.md b/docs/architectural-decision-records/005-frontend-a11y-toolset.md index 864f4037ea..d3e55271fa 100644 --- a/docs/architectural-decision-records/005-frontend-a11y-toolset.md +++ b/docs/architectural-decision-records/005-frontend-a11y-toolset.md @@ -1,6 +1,8 @@ # Define Frontend Toolset for Accessibility Standards -Decide on a toolset to help ensure MCRRS frontend development follows accessibility standards (specifically WCAG 2.2 AA guidelines). +## This ADR has been superseded by [028-updated-frontend-a11y-toolset](028-updated-frontend-a11y-toolset.md) + +Decide on a toolset to help ensure MCRRS frontend development follows accessibility standards (specifically WCAG AA guidelines). Tooling is not a replacement for engineers/designers with strong expertise in HTML or manual testing with assistive technology. Thus, the toolset will focus on the types of accessibility issues where tooling is useful. This includes validating HTML attributes, checking basic page structure, ensuring media content has descriptive tags, ensuring that buttons are used appropriately, and checking for basic color contrast. @@ -19,7 +21,7 @@ Together these tools include linting, testing, and auditing loaded web pages for [eslint jsx-a11y][eslint-a11y] Provides React-friendly code linting which allows developers to find accessibility issues during local development. -[cypress-axe][cypress-axe] and [axe-core][axe-copre] - Accessibility test runner for end to end testing in the command line or Node.js. End to end testing can catch concerns that are not noticeable in linting or unit tests since tests run on browser. Our previous choice [pa11y][pa11y] seems to not be maintained anymore and no longer works with our end to end testing. +[pa11y][pa11y] and [pa11y-ci][pa11y-ci] - Accessibility test runner for end to end testing in the command line or Node.js. End to end testing can catch concerns that are not noticeable in linting or unit tests since tests run on browser. [jest-axe][jest-axe] - This tool provides additional jest matchers, like `.toHaveNoViolations`, to use in unit tests. This may be redundant when end to end accessibility testing is present. @@ -43,27 +45,12 @@ Together these tools include linting, testing, and auditing loaded web pages for - `+` Allows the team to get a Lighthouse report alongside every PR. - `-` Seemed too robust for this stage of the project (pilot only), provides a lot of information about performance that is not relevant to us now. May be useful later on. -#### [cypress-axe][cypress-axe] and [axe-core][axe-core] -- `+` `axe-core` is a well known and maintained a11y testing engine -- `+` `axe-core` has great documentation -- `+` `cypress-axe` can run tests with specific a11y standards like `WCAG 2.2 AA`. -- `+` `cypress-axe` is a Cypress plugin for axe-core and most of the configuration for this plugin follows `axe-core` which has great documentation. -- `-` `cypress-axe` itself has sparse documentation -- `-` `cypress-axe` is very minimal in features and if we wanted specific things like reporting, we would have to implement that ourselves. - -#### [Cypress Accessibility][cypress-accessibility] -- `+` Built in accessibility testing by Cypress. -- `-` Still in early access and not much is known about implementation or documentation. -- `-` As of now it looks like it would cost money in addition to what we already pay for Cypress. -- `-` To get access we have to sign up for early access as a trial. - #### [pa11y][pa11y] and [pa11y-ci][pa11y-ci] - `+` pa11y-ci has clear patterns for use in ci - `+` Unlike other options explored, includes ‘actions’ to be used within a test to interact with the page under test. - `+` Has significant documentation. - `-` Could slow development processes if its set too strict. However, many examples of how to optimize configuration as needed in documentation and tutorial -- `-` No longer works with our end to end testing tool Cypress #### [eslint-jsx-a11y][eslint-a11y] & [addon-a11y][storybook-addon-a11y] @@ -78,6 +65,3 @@ Together these tools include linting, testing, and auditing loaded web pages for [axe-core-cli]: https://www.npmjs.com/package/@axe-core/cli [cypress-audit]: https://github.com/mfrachet/cypress-audit [lighthouse-ci]: https://github.com/GoogleChrome/lighthouse-ci -[cypress-axe]: https://github.com/component-driven/cypress-axe -[axe-core]: https://github.com/dequelabs/axe-core -[cypress-accessibility]: https://www.cypress.io/blog/introducing-cypress-accessibility diff --git a/docs/architectural-decision-records/028-updated-frontend-a11y-toolset.md b/docs/architectural-decision-records/028-updated-frontend-a11y-toolset.md new file mode 100644 index 0000000000..01d39ab389 --- /dev/null +++ b/docs/architectural-decision-records/028-updated-frontend-a11y-toolset.md @@ -0,0 +1,60 @@ +# Updated Frontend Toolset for Accessibility Standards + +Decide on a toolset to help ensure MCRRS frontend development follows accessibility standards (specifically WCAG 2.2 AA guidelines). + +Tooling is not a replacement for engineers/designers with strong expertise in HTML or manual testing with assistive technology. Thus, the toolset will focus on the types of accessibility issues where tooling is useful. This includes validating HTML attributes, checking basic page structure, ensuring media content has descriptive tags, ensuring that buttons are used appropriately, and checking for basic color contrast. + +### Considerations + +- Must check React component design for accessibility standards +- Must validate raw HTML markup for accessibility standards +- Must include end to end testing for accessibility standards rather than only unit tests +- Preference for easy options to run in CI + +### Decision Changes + +Updated toolset for accessibility testing and supersedes [005-frontend-a11y-toolset](005-frontend-a11y-toolset.md) + +[storybook addon-a11y][storybook-addon-a11y] - Provides integration with Storybook GUI which allows the team to surface a11y concerns to non-developer stakeholders. This helps make a11y issues visible beyond the code editor or command line. + +[eslint jsx-a11y][eslint-a11y] Provides React-friendly code linting which allows developers to find accessibility issues during local development. + +[cypress-axe][cypress-axe] and [axe-core][axe-core] - Accessibility test runner for end to end testing in the command line or Node.js. End to end testing can catch concerns that are not noticeable in linting or unit tests since tests run on browser. Our previous choice [pa11y][pa11y] and [pa11y-ci][pa11y-ci] seems to not be maintained anymore and no longer works with Cypress end to end testing. + +[jest-axe][jest-axe] - We are no longer using this tool and has been removed `package.json` + +### Pro/Cons + +#### [@axe-core/react][axe-core-react], [@axe-core/cli][axe-core-cli] + +- `+` This react plugin outputs accessibility warnings in Chrome Devtools when the app is running. The cli plugin surfaces a command for running in CI. +- `+` Base standard for most accessibility tools. Customizable for different levels of accessibility compliance. +- `-` Documentation. Couldn’t get it set up quickly, they are in progress of moving their docs and consolidating their npm modules. Minimal documentation and example on best use in CI. +- `-` Heard from other teams that have struggled customization - when used with standard flags it is more strict than needed. + +#### [cypress-axe][cypress-axe] and [axe-core][axe-core] +- `+` `axe-core` is a well known and maintained a11y testing engine +- `+` `axe-core` has great documentation +- `+` `cypress-axe` can run tests with specific a11y standards like `WCAG 2.2 AA`. +- `+` `cypress-axe` is a Cypress plugin for axe-core and most of the configuration for this plugin follows `axe-core` which has great documentation. +- `-` `cypress-axe` itself has sparse documentation +- `-` `cypress-axe` is very minimal in features and if we wanted specific things like reporting, we would have to implement that ourselves. + +#### [Cypress Accessibility][cypress-accessibility] +- `+` Built in accessibility testing by Cypress. +- `-` Still in early access and not much is known about implementation or documentation. +- `-` As of now it looks like it would cost money in addition to what we already pay for Cypress. +- `-` To get access we have to sign up for early access as a trial. + +[storybook-addon-a11y]: https://storybook.js.org/addons/@storybook/addon-a11y +[eslint-a11y]: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y +[pa11y]: https://github.com/pa11y/pa11y +[pa11y-ci]: https://github.com/pa11y/pa11y-ci +[jest-axe]: https://github.com/nickcolley/jest-axe +[axe-core-react]: https://www.npmjs.com/package/@axe-core/react +[axe-core-cli]: https://www.npmjs.com/package/@axe-core/cli +[cypress-audit]: https://github.com/mfrachet/cypress-audit +[lighthouse-ci]: https://github.com/GoogleChrome/lighthouse-ci +[cypress-axe]: https://github.com/component-driven/cypress-axe +[axe-core]: https://github.com/dequelabs/axe-core +[cypress-accessibility]: https://www.cypress.io/blog/introducing-cypress-accessibility From ea421f5e142ffceff160f382fa6b0bc944387b48 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Fri, 30 Aug 2024 15:30:34 -0400 Subject: [PATCH 23/25] Replace depreciated command --- .../stateWorkflow/stateSubmissionForm/accessiblility.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts index 21eac963c3..fa41f1d2ef 100644 --- a/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts +++ b/services/cypress/integration/stateWorkflow/stateSubmissionForm/accessiblility.spec.ts @@ -5,7 +5,7 @@ describe('state user in state submission form', () => { cy.stubFeatureFlags() cy.interceptGraphQL() }) - it('has no a11y violations on submission form with form input errors', () => { + it.only('has no a11y violations on submission form with form input errors', () => { // 438-attestation still needs to go through design, there is an a11y violation for links and spacing cy.interceptFeatureFlags({'438-attestation': false}) cy.logInAsStateUser() @@ -28,7 +28,7 @@ describe('state user in state submission form', () => { cy.checkA11yWithWcag22aa() cy.fillOutContractActionAndRateCertification() - cy.deprecatedNavigateV1Form('CONTINUE_FROM_START_NEW') + cy.navigateContractForm('CONTINUE_FROM_START_NEW') cy.findByRole('heading', { level: 2, name: /Contract details/ }) cy.findByRole('button', { From eb4b34d38e0ab7ab6cc39f2b0ceb687c7b873d25 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Fri, 30 Aug 2024 15:30:49 -0400 Subject: [PATCH 24/25] Skipping violations and made tickets to address them. --- services/cypress/support/accessibilityCommands.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/services/cypress/support/accessibilityCommands.ts b/services/cypress/support/accessibilityCommands.ts index d5d6add1c9..b875a260f2 100644 --- a/services/cypress/support/accessibilityCommands.ts +++ b/services/cypress/support/accessibilityCommands.ts @@ -1,3 +1,5 @@ +import {getRules} from 'axe-core' + function terminalLog(violations: Record[]) { console.log(violations) cy.task( @@ -24,9 +26,18 @@ Cypress.Commands.add( () => { cy.checkA11y('', { runOnly: { - type: 'tag', - values: ['wcag22aa'] + type: 'tags', + values: ['wcag2a','wcag2aa', 'wcag21a', 'wcag21aa','wcag22aa'] }, + rules: { + // Rule skipped. It can be removed from config when https://jiraent.cms.gov/browse/MCR-4421 has been + // completed + 'aria-allowed-attr': { enabled: false }, + // Both of these rules are skipped. They can be removed from config when + // https://jiraent.cms.gov/browse/MCR-4420 has been completed + 'dlitem': { enabled: false }, + 'definition-list': { enabled: false } + } }, terminalLog) } ) From 321d164302b7b6ffd123d51a0390001507b4c0b4 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Fri, 30 Aug 2024 15:37:01 -0400 Subject: [PATCH 25/25] Update doc --- services/cypress/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/cypress/README.md b/services/cypress/README.md index fb45d787a0..71c1994349 100644 --- a/services/cypress/README.md +++ b/services/cypress/README.md @@ -77,6 +77,8 @@ Our implementation is almost exactly like the [documentation](https://github.com To run axe inject the `axe-core` runtime using `cy.injectAxe()` after `cy.visit()`. Then you can call the custom command `cy.checkA11yWithWcag22aa()` to check a11y against the page. Each time you use `cy.visit()` you will need to inject the runtime again. The `checkA11yWithWcag22aa` command configures `cy.checkA11y()` with the WCAG 2.2 AA standard we use for the MC-Review app. In the `cy.checkA11y()` there are more options we can configure the check with. The options are specified in the [axe-core documentation](https://www.deque.com/axe/core-documentation/api-documentation). + +In the example below, you will notice that there are more than the WCAG 2.2 AA standard being used in the test. This is because the rules for each standard are not cumulative. So in order to test all rules, all the standards must be included. If we only include `'wcag22aa'` then only the specific rules added in for that standard will be tested. ```typescript Cypress.Commands.add( 'checkA11yWithWcag22aa', @@ -84,7 +86,7 @@ Cypress.Commands.add( cy.checkA11y('', { runOnly: { type: 'tag', - values: ['wcag22aa'] + values: ['wcag2a','wcag2aa', 'wcag21a', 'wcag21aa','wcag22aa'] }, }, terminalLog) }