Ajoute `cypress-axe` (#2414)

* feat: Ajoute cypress-axe + ajoute le check sur chaque page

* fix: Ajouter label par défault th

* feat: Modifie checkA11Y

* fix: heading hierarchy

* fix: Retire lien en double encapsulé

* fix: Problème de hiérarchie de titres

* chore: Màj publicodes

* fix: Retire le check A11Y sur iframe-pamc, corrige hiérarchie titre
pull/2419/head
Benjamin Arias 2022-12-08 16:29:53 +01:00 committed by GitHub
parent 6012e82437
commit 1a756c41df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 165 additions and 56 deletions

View File

@ -13,7 +13,7 @@ GITHUB_API_SECRET=
### Variables utilisées par l'app ###
VITE_ALGOLIA_APP_ID=
VITE_ALGOLIA_SEARCH_KEY=
VITE_ALGOLIA_INDEX_PREFIX=monentreprise-{env}-
VITE_ALGOLIA_INDEX_PREFIX=monentreprise-master-
VITE_AT_INTERNET_SITE_ID=
VITE_FR_BASE_URL="http://localhost:3000/mon-entreprise"

View File

@ -1,4 +1,4 @@
import { fr } from '../../support/utils'
import { checkA11Y, fr } from '../../support/utils'
describe('Page covid-19', { testIsolation: 'off' }, function () {
if (!fr) {
@ -35,5 +35,7 @@ describe('Page covid-19', { testIsolation: 'off' }, function () {
cy.get('[data-test-id=comparaison-total]').contains(
/Soit [\d]{1} % du coût habituel/
)
checkA11Y()
})
})

View File

@ -1,4 +1,4 @@
import { fr } from '../../support/utils'
import { checkA11Y, fr } from '../../support/utils'
const writeFixtures = Cypress.env('record_http') !== undefined
@ -176,5 +176,7 @@ describe(`Formulaire demande mobilité (${
cy.focused().type('Plougastel')
cy.contains('Générer la demande').click()
cy.contains('Télécharger le fichier').click()
checkA11Y()
})
})

View File

@ -1,3 +1,5 @@
import { checkA11Y } from '../../support/utils'
const searchInputPath = '[data-test-id=company-search-input]'
const searchResultsPath = '[data-test-id=company-search-results]'
const currentCompanyPath = '[data-test-id=currently-selected-company]'
@ -7,6 +9,8 @@ const FIXTURES_FOLDER = 'cypress/fixtures/landing'
describe('Landing page', function () {
it('should not crash', function () {
cy.visit('/')
checkA11Y()
})
it('should display logo', function () {

View File

@ -1,4 +1,4 @@
import { fr } from '../../support/utils'
import { checkA11Y, fr } from '../../support/utils'
describe('Recherche globales', function () {
if (!fr) {
@ -28,5 +28,7 @@ describe('Recherche globales', function () {
.next()
.find('li')
.should('have.length.of.at.least', 1)
checkA11Y()
})
})

View File

@ -1,4 +1,4 @@
import { fr } from '../../support/utils'
import { checkA11Y, fr } from '../../support/utils'
describe('Secondary pages', function () {
if (!fr) {
@ -8,11 +8,15 @@ describe('Secondary pages', function () {
it("page stats doesn't crash", function () {
cy.visit('/stats')
cy.contains('Statistiques détaillées')
checkA11Y()
})
it('navigate in the news section', function () {
cy.visit('/nouveautés')
cy.contains('←').click()
cy.url({ decode: true }).should('match', /\/nouveautés\/[^/]*$/)
checkA11Y()
})
})

View File

@ -1,4 +1,4 @@
import { fr } from '../../support/utils'
import { checkA11Y, fr } from '../../support/utils'
describe('Simulateur auto-entrepreneur', { testIsolation: 'off' }, function () {
if (!fr) {
@ -73,5 +73,7 @@ describe('Simulateur auto-entrepreneur', { testIsolation: 'off' }, function () {
expect(activitéMixtes[0]).to.be.equal(activitéMixtes[2])
expect(activitéMixtes[1]).to.be.equal(activitéMixtes[0] * 2)
})
checkA11Y()
})
})

View File

@ -1,3 +1,5 @@
import { checkA11Y } from '../../support/utils'
const inputSelector = 'div[id="simulator-legend"] input'
const fr = Cypress.env('language') === 'fr'
@ -11,5 +13,7 @@ describe('Simulateur dividendes', function () {
cy.get(inputSelector).first().type('{selectall}5000')
cy.contains(/[cC]otisations\s+17,2\s*%/)
cy.contains(/[Ii]mpôt\s+12,8\s*%/)
checkA11Y()
})
})

View File

@ -1,4 +1,4 @@
import { fr } from '../../support/utils'
import { checkA11Y, fr } from '../../support/utils'
describe('Simulateur salarié : part time contract', function () {
if (!fr) {
@ -50,5 +50,7 @@ describe('Simulateur salarié : part time contract', function () {
.replace(/[\s,.€]/g, '')
expect(parseInt(val)).to.be.below(1000)
})
checkA11Y()
})
})

View File

@ -16,6 +16,7 @@
// Import commands.js using ES2015 syntax:
import 'cypress-plugin-tab'
import './commands'
import 'cypress-axe'
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@ -1,3 +1,5 @@
import { checkA11Y } from './utils'
const inputSelector =
'div[id="simulator-legend"] input[inputmode="numeric"]:not([id="entreprisecharges"])'
const chargeInputSelector = 'input[id="entreprisecharges"]'
@ -69,6 +71,7 @@ export const runSimulateurTest = (simulateur) => {
.first()
.invoke('val')
.should('match', /2[\s,]000/)
checkA11Y()
})
})
}

View File

@ -1,2 +1,38 @@
export const fr = Cypress.env('language') === 'fr'
export const baseUrl = Cypress.config('baseUrl')
type ViolationType = {
description: string
}
export const checkA11Y = () => {
cy.injectAxe()
cy.configureAxe({
rules: [
{
id: 'color-contrast',
enabled: false,
},
],
})
cy.checkA11y(
null,
null,
(violations: ViolationType[]) => {
if (violations.length > 0) {
cy.log(
`${violations.length} erreur${
violations.length > 1 ? 's' : ''
} d'accessibilité sur cette page :`
)
violations.forEach((violation, index) => {
cy.log(`Violation ${index + 1} : ${violation.description}`)
})
cy.log(
'Pour y remédier, lancer la commande yarn start:axe-debugging en local.'
)
}
},
null
)
}

View File

@ -3,7 +3,7 @@
"target": "es5",
"module": "ESNext",
"lib": ["es5", "dom"],
"types": ["cypress", "cypress-plugin-tab/src", "node"]
"types": ["cypress", "cypress-plugin-tab/src", "node", "cypress-axe"]
},
"include": ["**/*.ts", "../cypress.config.ts"]
}

View File

@ -51,6 +51,7 @@
"@atomik-color/component": "^1.0.16",
"@axe-core/react": "^4.5.2",
"@internationalized/number": "^3.1.1",
"@publicodes/api": "^1.0.0-beta.63",
"@react-aria/accordion": "^3.0.0-alpha.13",
"@react-aria/button": "^3.6.3",
"@react-aria/checkbox": "^3.7.0",
@ -80,8 +81,8 @@
"isbot": "^3.6.5",
"markdown-to-jsx": "^7.1.7",
"modele-social": "workspace:^",
"publicodes": "^1.0.0-beta.62",
"publicodes-react": "^1.0.0-beta.62",
"publicodes": "^1.0.0-beta.63",
"publicodes-react": "^1.0.0-beta.63",
"react": "^18.2.0",
"react-colorful": "^5.6.1",
"react-dom": "^18.2.0",
@ -129,6 +130,7 @@
"@vitejs/plugin-legacy": "^2.3.1",
"@vitejs/plugin-react": "^2.2.0",
"cypress": "https://cdn.cypress.io/beta/npm/12.0.0/linux-arm64/release/12.0.0-e8898d2f6b65a11f36ceb2dd32ce7f1d87b103bc/cypress.tgz",
"cypress-axe": "^1.1.0",
"cypress-plugin-tab": "^1.0.5",
"cypress-wait-until": "^1.7.2",
"dotenv": "^16.0.3",

View File

@ -114,10 +114,10 @@ const App = () => {
return (
<StyledLayout isEmbedded={isEmbedded}>
{!isEmbedded && <Header />}
<a href={`${fullURL}#footer`} className="skip-link print-hidden">
{t('Passer le contenu')}
</a>
<main role="main" id="main">
<a href={`${fullURL}#footer`} className="skip-link print-hidden">
{t('Passer le contenu')}
</a>
<Container>
<ErrorBoundary fallback={CatchOffline}>
<Routes>

View File

@ -1,6 +1,7 @@
import { Evaluation } from 'publicodes'
import { useContext } from 'react'
import { Trans } from 'react-i18next'
import styled from 'styled-components'
import Warning from '@/components/ui/WarningBlock'
import { Link } from '@/design-system/typography/link'
@ -28,7 +29,7 @@ export default function SimulateurWarning({
<Ul>
{simulateur === 'auto-entrepreneur' && (
<>
<Li>
<StyledLi>
<Trans i18nKey="simulateurs.warning.auto-entrepreneur">
Les auto-entrepreneurs bénéficient dun régime très simplifié
avec un taux forfaitaire pour le calcul des cotisations et
@ -39,8 +40,8 @@ export default function SimulateurWarning({
revenu net est donc le chiffre daffaires moins toutes les
charges engagées pour lentreprise.
</Trans>
</Li>
<Li>
</StyledLi>
<StyledLi>
<Trans i18nKey="simulateurs.warning.cfe">
Le simulateur n'intègre pas la cotisation foncière des
entreprise (CFE) qui est dûe dès la deuxième année d'exercice.
@ -53,30 +54,30 @@ export default function SimulateurWarning({
Plus d'infos.
</Link>
</Trans>
</Li>
</StyledLi>
</>
)}
{simulateur !== 'artiste-auteur' && (
<Li>
<StyledLi>
<Trans i18nKey="simulateurs.warning.urssaf">
Les calculs sont indicatifs. Ils ne se substituent pas aux
décomptes réels de lUrssaf, de ladministration fiscale ou de
toute autre organisme.
</Trans>
</Li>
</StyledLi>
)}
{simulateur === 'profession-libérale' && (
<Trans i18nKey="simulateurs.warning.profession-libérale">
<Li>
<StyledLi>
Ce simulateur est à destination des professions libérales en BNC.
Il ne prend pas en compte les sociétés d'exercice libéral.
</Li>
</StyledLi>
</Trans>
)}
{simulateur === 'artiste-auteur' && (
<>
<Li>
<StyledLi>
<Trans i18nKey="simulateurs.warning.artiste-auteur.1">
Cette estimation est proposée à titre indicatif. Elle est faite
à partir des éléments réglementaires applicables et des éléments
@ -84,33 +85,39 @@ export default function SimulateurWarning({
l'ensemble de votre situation. Le montant réel de vos
cotisations peut donc être différent.
</Trans>
</Li>
<Li>
</StyledLi>
<StyledLi>
<Trans i18nKey="simulateurs.warning.artiste-auteur.2">
Ce simulateur permet d'estimer le montant de vos cotisations à
partir de votre revenu projeté
</Trans>
</Li>
</StyledLi>
</>
)}
{['indépendant', 'profession-libérale'].includes(simulateur) && (
<Li>
<StyledLi>
<Trans i18nKey="simulateurs.warning.année-courante">
Le montant calculé correspond aux cotisations de lannée{' '}
{{ year }} (pour un revenu {{ year }}).
</Trans>
</Li>
</StyledLi>
)}
{['profession-libérale'].includes(simulateur) && (
<Li>
<StyledLi>
<Trans i18nKey="simulateurs.warning.cotisations-ordinales">
Pour les professions réglementées, le simulateur ne calcule pas le
montant des cotisations à l'ordre. Elles doivent être ajoutées
manuellement dans la case « charges de fonctionnement ».
</Trans>
</Li>
</StyledLi>
)}
</Ul>
</Warning>
)
}
const StyledLi = styled(Li)`
&::before {
color: ${({ theme }) => theme.colors.bases.tertiary[800]} !important;
}
`

View File

@ -38,10 +38,10 @@ const SimulateurCardHit = ({
to={{ pathname: path }}
state={{ fromSimulateurs: true }}
title={
<h4>
<h3>
<Highlight hit={hit} attribute="title" />{' '}
{tooltip && <InfoBulle>{tooltip}</InfoBulle>}
</h4>
</h3>
}
/>
)

View File

@ -176,12 +176,10 @@ function DroitsRetraite() {
<Ul>
<Li>
Retraite de base :{' '}
<RuleLink dottedName="protection sociale . retraite . trimestres">
<Value
expression="protection sociale . retraite . trimestres"
displayedUnit={t('trimestres acquis')}
/>
</RuleLink>
<Value
expression="protection sociale . retraite . trimestres"
displayedUnit={t('trimestres acquis')}
/>
</Li>
<WhenApplicable dottedName="protection sociale . retraite . CNAVPL">
<Li>
@ -206,13 +204,11 @@ function DroitsRetraite() {
<Li>
Points de retraite complémentaire acquis :{' '}
<WhenApplicable dottedName="protection sociale . retraite . complémentaire . RCI . points acquis">
<RuleLink dottedName="protection sociale . retraite . complémentaire . RCI . points acquis">
<Value
expression="protection sociale . retraite . complémentaire . RCI . points acquis"
displayedUnit=""
/>{' '}
points acquis
</RuleLink>
<Value
expression="protection sociale . retraite . complémentaire . RCI . points acquis"
displayedUnit=""
/>{' '}
points acquis
</WhenApplicable>
<WhenNotApplicable dottedName="protection sociale . retraite . complémentaire . RCI . points acquis">
<Strong>non connue</Strong>

View File

@ -37,10 +37,11 @@ export default function Landing() {
ogImage="/logo-share.png"
/>
<Header />
<a href={`${fullURL}#footer`} className="skip-link print-hidden">
{t('Passer le contenu')}
</a>
<main role="main" id="main">
<a href={`${fullURL}#footer`} className="skip-link print-hidden">
{t('Passer le contenu')}
</a>
<Container>
<PageHeader
titre={

View File

@ -155,6 +155,8 @@ function ComparaisonTable({ rows: [head, ...body] }: ComparaisonTableProps) {
columns.length - 1
)
const { t } = useTranslation()
const captionText = (
<Trans i18nKey="chomagePartiel.tableCaption">
Tableau indiquant le salaire net et le coût pour l'employeur avec ou sans
@ -204,7 +206,7 @@ function ComparaisonTable({ rows: [head, ...body] }: ComparaisonTableProps) {
<tr>
{head.map((label, i) => (
<th key={i} scope="col">
{label}
{label || t('Type')}
</th>
))}
</tr>

View File

@ -158,7 +158,7 @@ notification signature:
valeur: oui
type: groupe
description: |
#### Ce document doit être signé ✍️
### Ce document doit être signé ✍️
Nous vous invitons à utiliser un écran tactile pour le compléter (téléphone, tablette, etc.). Sinon, vous devrez limprimer, le signer et le scanner avant envoi par mail.

View File

@ -147,7 +147,7 @@ function FormulairePublicodes() {
}) => {
const meta = getMeta<{ affichage?: string }>(rawNode, {})
const headerLevel = Math.min(dottedName.split(' . ').length + 1, 6)
const headerLevel = Math.min(dottedName.split(' . ').length + 1, 4)
const HeaderComponent = headings.fromLevel(headerLevel)
return (

View File

@ -4754,6 +4754,21 @@ __metadata:
languageName: node
linkType: hard
"@publicodes/api@npm:^1.0.0-beta.63":
version: 1.0.0-beta.63
resolution: "@publicodes/api@npm:1.0.0-beta.63"
dependencies:
"@koa/cors": ^3.3.0
"@koa/router": ^10.1.1
koa: ^2.13.4
koa-body: ^5.0.0
openapi-validator-middleware: ^3.2.6
peerDependencies:
publicodes: ^1.0.0-beta.47
checksum: 53d0cf908e787e6299224547dd5b345fb24dbf092625410b245c9bb2e66cd7084bfb2fd5f215eb98796c4200f65395f88003c5c13eaccd4cb0bcf37eb8061183
languageName: node
linkType: hard
"@react-aria/accordion@npm:^3.0.0-alpha.13":
version: 3.0.0-nightly.3598
resolution: "@react-aria/accordion@npm:3.0.0-nightly.3598"
@ -12881,6 +12896,16 @@ __metadata:
languageName: node
linkType: hard
"cypress-axe@npm:^1.1.0":
version: 1.1.0
resolution: "cypress-axe@npm:1.1.0"
peerDependencies:
axe-core: ^3 || ^4
cypress: ^10 || ^11
checksum: a86b4ff0d35eab82ac5cfd40bbe4077aaf5e7f075b40a49efa949e2c40c60d0f78c1b43d65f98e255c1675d8492b674933da12e11ff71b1f670bd6cfa03b363c
languageName: node
linkType: hard
"cypress-plugin-tab@npm:^1.0.5":
version: 1.0.5
resolution: "cypress-plugin-tab@npm:1.0.5"
@ -23768,16 +23793,16 @@ __metadata:
languageName: node
linkType: hard
"publicodes-react@npm:^1.0.0-beta.62":
version: 1.0.0-beta.62
resolution: "publicodes-react@npm:1.0.0-beta.62"
"publicodes-react@npm:^1.0.0-beta.63":
version: 1.0.0-beta.63
resolution: "publicodes-react@npm:1.0.0-beta.63"
dependencies:
styled-components: ^5.1.0
peerDependencies:
publicodes: ^1.0.0-beta.40
react: ^17 || ^18
react-dom: ^17 || ^18
checksum: 71e73059a9509b7741b6c308bbd8a740cc9daf0c2afba45d6c2db9f3bde735840d94c31cd38a730fce3aee3c5d5d590d9a9d8b84907ee6cb44123deca9ece2e6
checksum: c3078deed199de42eb5a65cecc5db12696e93a4ae5c082e38c3edea959be734e0d574acc240ebb7da9ba35d7126b3991d1d6daf41b1647aac7736a06bafe8701
languageName: node
linkType: hard
@ -23793,6 +23818,18 @@ __metadata:
languageName: node
linkType: hard
"publicodes@npm:^1.0.0-beta.63":
version: 1.0.0-beta.63
resolution: "publicodes@npm:1.0.0-beta.63"
dependencies:
moo: ^0.5.1
nearley: ^2.19.2
peerDependencies:
"@types/mocha": ^9.0.0
checksum: 7aff3e841735582737b0d89bb763c49c4bc8ddd5806ff5c7c32fa79d7b49d2b2ba29ad86a3eaa28c25bc28007ba49f068856cefe6e5430d405e0f51f0637e163
languageName: node
linkType: hard
"pump@npm:^1.0.0":
version: 1.0.3
resolution: "pump@npm:1.0.3"
@ -25943,6 +25980,7 @@ __metadata:
"@atomik-color/component": ^1.0.16
"@axe-core/react": ^4.5.2
"@internationalized/number": ^3.1.1
"@publicodes/api": ^1.0.0-beta.63
"@react-aria/accordion": ^3.0.0-alpha.13
"@react-aria/button": ^3.6.3
"@react-aria/checkbox": ^3.7.0
@ -25992,6 +26030,7 @@ __metadata:
"@vitejs/plugin-react": ^2.2.0
algoliasearch: ^4.14.2
cypress: "https://cdn.cypress.io/beta/npm/12.0.0/linux-arm64/release/12.0.0-e8898d2f6b65a11f36ceb2dd32ce7f1d87b103bc/cypress.tgz"
cypress-axe: ^1.1.0
cypress-plugin-tab: ^1.0.5
cypress-wait-until: ^1.7.2
dotenv: ^16.0.3
@ -26003,8 +26042,8 @@ __metadata:
markdown-to-jsx: ^7.1.7
modele-social: "workspace:^"
netlify-cli: ^12.2.8
publicodes: ^1.0.0-beta.62
publicodes-react: ^1.0.0-beta.62
publicodes: ^1.0.0-beta.63
publicodes-react: ^1.0.0-beta.63
react: ^18.2.0
react-colorful: ^5.6.1
react-dom: ^18.2.0