Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✅ Add automatic A11Y testing #864

Open
wants to merge 19 commits into
base: preprod
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- 'main'
- 'NGC-1684'
pull_request:
branches:
- 'main'
Expand All @@ -30,7 +31,7 @@ jobs:
strategy:
fail-fast: false
matrix:
containers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
containers: [1, 2, 3]
name: Testing e2e in worker ${{ matrix.containers }}
if: github.event.pull_request.draft == false
steps:
Expand Down
89 changes: 89 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { addMatchImageSnapshotPlugin } from '@simonsmith/cypress-image-snapshot/plugin'
import { spawn } from 'child_process'
import { defineConfig } from 'cypress'
import dotenv from 'dotenv'
import { Client } from 'pg'

dotenv.config()

Expand All @@ -21,6 +23,93 @@ export default defineConfig({
baseUrl: process.env.CYPRESS_baseUrl ?? 'http://localhost:3000',
setupNodeEvents(on) {
addMatchImageSnapshotPlugin(on)
on('task', {
getVerificationCodeFromScalingo: async () => {
let tunnelProcess = null
try {
// Set up SSH tunnel to Scalingo
const scalingoApp = process.env.SCALINGO_APP_NAME
const scalingoPostgresqlUrl = process.env.SCALINGO_POSTGRESQL_URL

if (!scalingoApp || !scalingoPostgresqlUrl) {
throw new Error(
'Missing required environment variables: SCALINGO_APP_NAME or SCALINGO_POSTGRESQL_URL'
)
}

// Use spawn instead of execSync to run the tunnel in background
tunnelProcess = spawn(
'scalingo',
['--app', scalingoApp, 'db-tunnel', scalingoPostgresqlUrl],
{
detached: true,
stdio: ['ignore', 'pipe', 'pipe'],
}
)

// Add event listeners to capture output and errors
tunnelProcess.stdout.on('data', (data) => {
console.log(`Tunnel stdout: ${data}`)
})

tunnelProcess.stderr.on('data', (data) => {
console.error(`Tunnel stderr: ${data}`)
})

// Wait longer for the tunnel to establish
console.log('Waiting for SSH tunnel to establish...')
await new Promise((resolve) => setTimeout(resolve, 8000))
console.log('SSH tunnel setup attempted')

// Extract connection parameters from tunnelOutput if needed
// For now using hardcoded values
const client = new Client({
host: 'localhost',
port: 10000,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
connectionTimeoutMillis: 10000, // Add timeout
})

await client.connect()
console.log('Database client connected')

// Query to get the verification code
const result = await client.query(
`
SELECT code FROM ngc."VerificationCode"
WHERE email = '${process.env.VERIFICATION_CODE_EMAIL}'
ORDER BY "createdAt" DESC
LIMIT 1
`
)

await client.end()
console.log('Database connection closed')

// Return the result
if (result.rows.length > 0) {
return result.rows[0].code
} else {
return null
}
} catch (error) {
console.error('Error getting verification code:', error)
return `Error: ${error.message}`
} finally {
// Always clean up the tunnel process
if (tunnelProcess && tunnelProcess.pid) {
try {
process.kill(-tunnelProcess.pid, 'SIGTERM')
console.log('Tunnel process terminated')
} catch (killError) {
console.error('Error terminating tunnel process:', killError)
}
}
}
},
})
},
experimentalRunAllSpecs: true,
specPattern: 'cypress/e2e/**/*.cy.js',
Expand Down
26 changes: 26 additions & 0 deletions cypress/constants/elements-ids.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,29 @@ export const SAVE_MODAL_EMAIL_INPUT = 'save-modal-email-input'
export const SAVE_MODAL_SUBMIT_BUTTON = 'save-modal-submit-button'
export const FIN_EMAIL_INPUT = 'fin-email-input'
export const FIN_EMAIL_SUBMIT_BUTTON = 'fin-email-submit-button'
export const ORGANISATION_CONNEXION_EMAIL_INPUT =
'organisation-connexion-email-input'
export const ORGANISATION_CONNEXION_SUBMIT_BUTTON =
'organisation-connexion-submit-button'
export const ORGANISATION_CONNEXION_VERIFICATION_CODE_INPUT =
'organisation-connexion-verification-code-input'
export const ORGANISATION_NAME_INPUT = 'organisation-name-input'
export const ORGANISATION_TYPE_SELECT = 'organisation-type-select'
export const ORGANISATION_ADMINISTRATOR_FIRST_NAME_INPUT =
'organisation-administrator-first-name-input'
export const ORGANISATION_ADMINISTRATOR_LAST_NAME_INPUT =
'organisation-administrator-last-name-input'
export const CREATE_ORGANISATION_BUTTON = 'create-organisation-button'
export const POLL_NAME_INPUT = 'poll-name-input'
export const POLL_EXPECTED_NUMBER_OF_PARTICIPANTS_INPUT =
'poll-expected-number-of-participants-input'
export const POLL_DEFAULT_ADDITIONAL_QUESTIONS_POSTAL_CODE_TOGGLE =
'poll-default-additional-questions-postal-code-toggle'
export const POLL_CREATE_BUTTON = 'poll-create-button'
export const POLL_CARD_SEE_DETAILS_BUTTON = 'poll-card-see-details-button'
export const POLL_ADMIN_SECTION_SEE_PARAMETERS_BUTTON =
'poll-admin-section-see-parameters-button'
export const ORGANISATION_PAGE_SEE_PARAMETERS_BUTTON =
'organisation-page-see-parameters-button'
export const ORGANISATION_ADMINISTRATOR_POSITION_INPUT =
'organisation-administrator-position-input'
40 changes: 40 additions & 0 deletions cypress/e2e/integration/accessibility/actions.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'cypress-axe'
import { recursivelyFillSimulation } from '../../../helpers/simulation/recursivelyFillSimulation'
import { setupSimulation } from '../../../helpers/simulation/setupSimulation'

Cypress.on('uncaught:exception', (err) => {
// Ignore uncaught exception failures for now
return false
})

describe('Accessibility Tests', () => {
it('Should return no accessibility violations on /actions', () => {
cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false })

// Actions when user hasn't completed the simulation
cy.visit('/actions')

cy.wait(2000)

cy.injectAxe()

cy.checkA11y()

// Actions when user has completed the simulation
cy.visit('/')

setupSimulation()

recursivelyFillSimulation()

cy.wait(4000)

cy.visit('/actions')

cy.wait(2000)

cy.injectAxe()

cy.checkA11y()
})
})
82 changes: 82 additions & 0 deletions cypress/e2e/integration/accessibility/groups.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'cypress-axe'
import { clickSkipTutorialButton } from '../../../helpers/elements/buttons'
import { clickNextStepGroupCreation } from '../../../helpers/groups/clickNextStepGroupCreation'
import { clickValidateGroupCreation } from '../../../helpers/groups/clickValidateGroupCreation'
import { fillGroupCreationFirstStep } from '../../../helpers/groups/fillGroupCreationFirstStep'
import { fillGroupNameEmoji } from '../../../helpers/groups/fillGroupNameEmoji'
import { recursivelyFillSimulation } from '../../../helpers/simulation/recursivelyFillSimulation'
import { skipRiddle } from '../../../helpers/simulation/skipRiddle'
Cypress.on('uncaught:exception', (err) => {
// Ignore uncaught exception failures for now
return false
})

describe('Accessibility Tests', () => {
it('Should return no accessibility violations on /classements', () => {
cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false })

cy.visit('/classements')

cy.wait(8000)

cy.injectAxe()

// Run accessibility checks
cy.checkA11y()
})

it('Should return no accessibility violations on the /amis userflow', () => {
cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false })

cy.visit('/amis/creer/vos-informations')

// Run accessibility checks on first step
cy.injectAxe()

cy.checkA11y()

fillGroupCreationFirstStep()

clickNextStepGroupCreation()

cy.wait(2000)

// Run accessibility checks on second step
cy.injectAxe()

cy.checkA11y()

fillGroupNameEmoji()

clickValidateGroupCreation()

cy.wait(2000)

clickSkipTutorialButton()

recursivelyFillSimulation()

cy.wait(4000)

skipRiddle()

cy.wait(4000)

// Run accessibility checks on group results page
cy.injectAxe()

cy.checkA11y()

cy.wait(2000)

cy.clearLocalStorage()
cy.reload()

cy.wait(7000)

// Run accessibility checks on group results page
cy.injectAxe()

cy.checkA11y()
})
})
Loading
Loading