From 0abc6561e917ec31d80ac878cfa378f2fd95b0c3 Mon Sep 17 00:00:00 2001 From: Benjamin Arias Date: Tue, 4 Mar 2025 17:23:05 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20getVerificationCodeFromScalin?= =?UTF-8?q?go?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cypress.config.js | 88 ++++++ cypress/constants/elements-ids.js | 6 + .../accessibility/dynamic-pages.cy.js | 277 +++++++++++------- package.json | 2 + .../_components/emailSection/EmailForm.tsx | 6 +- .../VerificationCodeInput.tsx | 1 + .../layout/header/HeaderDesktop.tsx | 6 +- src/design-system/layout/Breadcrumbs.tsx | 9 +- yarn.lock | 47 ++- 9 files changed, 328 insertions(+), 114 deletions(-) diff --git a/cypress.config.js b/cypress.config.js index c403e8c42..0169ca7df 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -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() @@ -21,6 +23,92 @@ 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 = 'team@nosgestesclimat.fr' + 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', diff --git a/cypress/constants/elements-ids.js b/cypress/constants/elements-ids.js index 00fc40583..1d5cd4f4e 100644 --- a/cypress/constants/elements-ids.js +++ b/cypress/constants/elements-ids.js @@ -21,3 +21,9 @@ 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' diff --git a/cypress/e2e/integration/accessibility/dynamic-pages.cy.js b/cypress/e2e/integration/accessibility/dynamic-pages.cy.js index 9c566784d..0de6a765d 100644 --- a/cypress/e2e/integration/accessibility/dynamic-pages.cy.js +++ b/cypress/e2e/integration/accessibility/dynamic-pages.cy.js @@ -1,6 +1,9 @@ import 'cypress-axe' -import { recursivelyFillSimulation } from '../../../helpers/simulation/recursivelyFillSimulation' -import { setupSimulation } from '../../../helpers/simulation/setupSimulation' +import { + ORGANISATION_CONNEXION_EMAIL_INPUT, + ORGANISATION_CONNEXION_SUBMIT_BUTTON, + ORGANISATION_CONNEXION_VERIFICATION_CODE_INPUT, +} from '../../../constants/elements-ids' Cypress.on('uncaught:exception', (err) => { // Ignore uncaught exception failures for now @@ -8,189 +11,243 @@ Cypress.on('uncaught:exception', (err) => { }) describe('Accessibility Tests', () => { - it('Should have no accessibility violations on /simulateur/bilan', () => { - cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) + // it('Should have no accessibility violations on /simulateur/bilan', () => { + // cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) - cy.visit('/tutoriel') + // cy.visit('/tutoriel') - clickSkipTutorialButton() + // clickSkipTutorialButton() - // Wait for the page to load completely - cy.wait(2000) - cy.injectAxe() + // // Wait for the page to load completely + // cy.wait(2000) + // cy.injectAxe() - // Run accessibility checks - cy.checkA11y() - }) + // // Run accessibility checks + // cy.checkA11y() + // }) - it('Should have no accessibility violations on /fin', () => { - cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) + // it('Should have no accessibility violations on /fin', () => { + // cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) - cy.visit('/') + // cy.visit('/') - setupSimulation() + // setupSimulation() - recursivelyFillSimulation() + // recursivelyFillSimulation() - cy.visit('/fin') + // cy.visit('/fin') - cy.wait(2000) - cy.injectAxe() + // cy.wait(2000) + // cy.injectAxe() - // Run accessibility checks - cy.checkA11y() - }) + // // Run accessibility checks + // cy.checkA11y() + // }) - it('Should have no accessibility violations on /classements', () => { - cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) + // it('Should have no accessibility violations on /classements', () => { + // cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) - cy.visit('/classements') + // cy.visit('/classements') - cy.wait(8000) + // cy.wait(8000) - cy.injectAxe() + // cy.injectAxe() - // Run accessibility checks - cy.checkA11y() - }) + // // Run accessibility checks + // cy.checkA11y() + // }) - it('Should have no accessibility violations on the /amis userflow', () => { - cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) + // it('Should have no accessibility violations on the /amis userflow', () => { + // cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) - cy.visit('/amis/creer/vos-informations') + // cy.visit('/amis/creer/vos-informations') - // Run accessibility checks on first step - cy.injectAxe() + // // Run accessibility checks on first step + // cy.injectAxe() - cy.checkA11y() + // cy.checkA11y() - fillGroupCreationFirstStep() + // fillGroupCreationFirstStep() - clickNextStepGroupCreation() + // clickNextStepGroupCreation() - cy.wait(2000) + // cy.wait(2000) - // Run accessibility checks on second step - cy.injectAxe() + // // Run accessibility checks on second step + // cy.injectAxe() - cy.checkA11y() + // cy.checkA11y() - fillGroupNameEmoji() + // fillGroupNameEmoji() - clickValidateGroupCreation() + // clickValidateGroupCreation() - cy.wait(2000) + // cy.wait(2000) - clickSkipTutorialButton() + // clickSkipTutorialButton() - recursivelyFillSimulation() + // recursivelyFillSimulation() - cy.wait(4000) + // cy.wait(4000) - skipRiddle() + // skipRiddle() - cy.wait(4000) + // cy.wait(4000) - // Run accessibility checks on group results page - cy.injectAxe() + // // Run accessibility checks on group results page + // cy.injectAxe() - cy.checkA11y() + // cy.checkA11y() - cy.wait(2000) + // cy.wait(2000) - cy.clearLocalStorage() - cy.reload() + // cy.clearLocalStorage() + // cy.reload() - cy.wait(7000) + // cy.wait(7000) - // Run accessibility checks on group results page - cy.injectAxe() + // // Run accessibility checks on group results page + // cy.injectAxe() - cy.checkA11y() - }) + // cy.checkA11y() + // }) - it('Should have no accessibility violations on /actions', () => { - cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) + // it('Should have no accessibility violations on /actions', () => { + // cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) - // Actions when user hasn't completed the simulation - cy.visit('/actions') + // // Actions when user hasn't completed the simulation + // cy.visit('/actions') - cy.wait(2000) + // cy.wait(2000) - cy.injectAxe() + // cy.injectAxe() - cy.checkA11y() + // cy.checkA11y() - // Actions when user has completed the simulation - cy.visit('/') + // // Actions when user has completed the simulation + // cy.visit('/') - setupSimulation() + // setupSimulation() - recursivelyFillSimulation() + // recursivelyFillSimulation() - cy.wait(4000) + // cy.wait(4000) - cy.visit('/actions') + // cy.visit('/actions') - cy.wait(2000) + // cy.wait(2000) - cy.injectAxe() + // cy.injectAxe() - cy.checkA11y() - }) - it('should have no accessibility violations on the /organisations userflow', () => { - cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) + // cy.checkA11y() + // }) + // it('should have no accessibility violations on the /organisations userflow', () => { + // cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) - cy.visit('/organisations') + // cy.visit('/organisations') - cy.wait(2000) + // cy.wait(2000) - cy.injectAxe() + // cy.injectAxe() - cy.checkA11y() + // cy.checkA11y() - cy.visit('/organisations/connexion') + // cy.visit('/organisations/connexion') - cy.wait(2000) + // cy.wait(2000) - cy.injectAxe() + // cy.injectAxe() - cy.checkA11y() + // cy.checkA11y() - cy.visit('/organisations/creer') // ! This shouldn't be accessible without being logged in + // cy.visit('/organisations/creer') // ! This shouldn't be accessible without being logged in - cy.wait(2000) + // cy.wait(2000) - cy.injectAxe() + // cy.injectAxe() - cy.checkA11y() - }) + // cy.checkA11y() + // }) - it('should have no accessibility violations on /profil', () => { - cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) + // it('should have no accessibility violations on /profil', () => { + // cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) - // With no simulation completed - cy.visit('/profil') + // // With no simulation completed + // cy.visit('/profil') - cy.wait(2000) + // cy.wait(2000) - cy.injectAxe() + // cy.injectAxe() - cy.checkA11y() + // cy.checkA11y() - // With simulation completed - cy.visit('/') + // // With simulation completed + // cy.visit('/') - setupSimulation() + // setupSimulation() - recursivelyFillSimulation() + // recursivelyFillSimulation() - cy.visit('/profil') + // cy.visit('/profil') - cy.wait(2000) + // cy.wait(2000) - cy.injectAxe() + // cy.injectAxe() - cy.checkA11y() - }) + // cy.checkA11y() + // }) + + // Run this test locally only + ;(Cypress.env('WITH_DB') ? it : it.skip)( + 'should have no accessibility violations on /organisations/:orgaSlug', + () => { + // cy.intercept({ resourceType: /xhr|fetch|uncaught/ }, { log: false }) + + cy.visit('/organisations') + + // cy.wait(2000) + + // // cy.injectAxe() + + // // cy.checkA11y() + + cy.visit('/organisations/connexion') + + // cy.wait(2000) + + // // cy.injectAxe() + + // // cy.checkA11y() + + // Fill the form + cy.get(`[data-cypress-id="${ORGANISATION_CONNEXION_EMAIL_INPUT}"]`).type( + 'team@nosgestesclimat.fr' + ) + + cy.get( + `[data-cypress-id="${ORGANISATION_CONNEXION_SUBMIT_BUTTON}"]` + ).click() + + // cy.wait(2000) + + // cy.injectAxe() + + // cy.checkA11y() + + // Get verification code from database + cy.task('getVerificationCodeFromScalingo').then((verificationCode) => { + cy.log(`Retrieved verification code: ${verificationCode}`) + + cy.get( + `[data-cypress-id="${ORGANISATION_CONNEXION_VERIFICATION_CODE_INPUT}"]` + ).type(verificationCode) + + cy.wait(2000) + + // cy.injectAxe() + + // cy.checkA11y() + }) + } + ) }) diff --git a/package.json b/package.json index 5207dd93f..38e62bebb 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "lint": "prettier --check \"**/*.{js,ts,md,json}\" && next lint", "lint:types": "tsc --noEmit", "e2e": "cypress open --e2e", + "e2e:with-db": "WITH_DB=true cypress open --e2e --config baseUrl=https://preprod.nosgestesclimat.fr", "e2e:generate": "node cypress/scripts/generateSpecsFromPersonas.js", "ui:parse": "node scripts/i18n/generate-ui.js", "ui:check": "yarn run ui:parse && node scripts/i18n/check-ui.js", @@ -137,6 +138,7 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "json-stable-stringify": "^1.1.0", + "pg": "^8.13.3", "postcss-loader": "^8.1.0", "prettier": "^3.1.0", "prettier-plugin-organize-imports": "^3.2.4", diff --git a/src/app/(simulation)/(large-layout)/organisations/connexion/_components/emailSection/EmailForm.tsx b/src/app/(simulation)/(large-layout)/organisations/connexion/_components/emailSection/EmailForm.tsx index 102a43300..09318891f 100644 --- a/src/app/(simulation)/(large-layout)/organisations/connexion/_components/emailSection/EmailForm.tsx +++ b/src/app/(simulation)/(large-layout)/organisations/connexion/_components/emailSection/EmailForm.tsx @@ -72,6 +72,7 @@ export default function EmailForm() { Votre adresse e-mail} placeholder="jeanmarc@nosgestesclimat.fr" @@ -87,7 +88,10 @@ export default function EmailForm() { {inputError &&

{inputError}

} - diff --git a/src/app/(simulation)/(large-layout)/organisations/connexion/_components/emailSection/verificationForm/VerificationCodeInput.tsx b/src/app/(simulation)/(large-layout)/organisations/connexion/_components/emailSection/verificationForm/VerificationCodeInput.tsx index eb5897915..e1a71b81a 100644 --- a/src/app/(simulation)/(large-layout)/organisations/connexion/_components/emailSection/verificationForm/VerificationCodeInput.tsx +++ b/src/app/(simulation)/(large-layout)/organisations/connexion/_components/emailSection/verificationForm/VerificationCodeInput.tsx @@ -21,6 +21,7 @@ export default function VerificationCodeInput({ <> -