@chikimuras/rampe 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +258 -2
  3. package/dist/rampe.mjs +4649 -0
  4. package/package.json +48 -5
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexandre Velia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,259 @@
1
- # Temporary Holding Version
1
+ # @chikimuras/rampe
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Audit automatique d’une page web ou d’un échantillon de pages, avec les résultats rangés par **critère RGAA** (ADR-0011). Le moteur est axe-core 4.14.0, exécuté dans un vrai Chromium par Playwright : les contrastes, la visibilité et les noms accessibles dépendent du rendu.
4
+
5
+ ## Installation
6
+
7
+ Node ≥ 22.18. Sans installation, avec `npx` :
8
+
9
+ ```sh
10
+ npx playwright@1.64.0 install chromium # une fois : le navigateur de l’audit
11
+ npx @chikimuras/rampe audit https://exemple.fr/
12
+ ```
13
+
14
+ Ou installé une fois pour toutes :
15
+
16
+ ```sh
17
+ npm i -g @chikimuras/rampe
18
+ npx playwright@1.64.0 install chromium # une fois
19
+ rampe audit https://exemple.fr/ --json rapport.json --html rapport.html
20
+ rampe --version
21
+ ```
22
+
23
+ La version de Playwright doit être celle de la CLI (1.64.0 aujourd’hui). Si Chromium manque, `rampe` sort en 2 et affiche la commande exacte à lancer. Sous Linux, `--with-deps` installe aussi les bibliothèques système du navigateur.
24
+
25
+ Le paquet publié contient un seul fichier, `dist/rampe.mjs`, où `@rampe/core` est intégré ; `playwright` et `axe-core` sont ses seules dépendances (ADR-0025, nom du paquet : ADR-0026). La commande installée reste `rampe`.
26
+
27
+ ## Utilisation depuis le dépôt
28
+
29
+ Depuis la racine du dépôt, après `pnpm install` :
30
+
31
+ ```sh
32
+ pnpm --filter @chikimuras/rampe exec playwright install chromium # une fois
33
+ pnpm rampe audit https://exemple.fr/
34
+ pnpm rampe audit https://exemple.fr/ --json rapport.json --html rapport.html --timeout 60000
35
+ ```
36
+
37
+ `pnpm rampe` lance `src/cli.ts`, exécuté tel quel par Node ≥ 22.18 (suppression native des types TypeScript) : pas d’étape de build pour le développement. Un chemin `--json` ou `--html` relatif part de la racine du dépôt.
38
+
39
+ | Option | Effet |
40
+ |---|---|
41
+ | `--json <fichier>` | écrit le résultat détaillé en JSON (voir plus bas) ; un fichier existant n’est jamais écrasé, et l’absence du dossier est signalée avant l’audit |
42
+ | `--html <fichier>` | écrit un rapport HTML lisible par un non-expert (voir plus bas) ; mêmes règles que `--json` : jamais d’écrasement, dossier vérifié avant l’audit, ni le fichier d’échantillon ni le fichier `--json` |
43
+ | `--timeout <ms>` | durée maximale de tout l’audit d’une page : chargement, injection et analyse (défaut : 60 000) ; au-delà, la page est fermée et la CLI sort en 2 |
44
+ | `--echantillon <fichier>` | audite les pages d’un fichier d’échantillon au lieu d’une seule URL (voir plus bas) |
45
+ | `--theme clair\|sombre` | préférence de couleurs du navigateur (`prefers-color-scheme`) pendant l’audit (défaut : `clair`) : un défaut de contraste propre au thème sombre n’apparaît qu’avec `sombre` |
46
+ | `--largeur <px>` | largeur de la fenêtre, de 200 à 3 840 px, hauteur 720 (défaut : 1 280) ; `--largeur 375` audite la mise en page mobile |
47
+ | `--strict` | sort aussi en 1 sur un résultat « rattachement incertain » ou « à vérifier » (les bonnes pratiques hors RGAA ne comptent jamais) ; la console dit alors pourquoi |
48
+
49
+ Codes de sortie : **0** aucune non-conformité détectée, **1** au moins une non-conformité détectée (pratique en CI ; avec `--strict`, ou un résultat incertain ou à vérifier), **2** erreur d’usage, page injoignable ou Chromium absent.
50
+
51
+ `--strict` ne change pas ce que Rampe présente comme non-conformité (ADR-0011 §3) : il sert à un site qui veut n’avoir **aucun** signalement, comme celui de Rampe lui-même (ADR-0021). Thème et largeur s’appliquent à chaque page, y compris avec `--echantillon`. Pour couvrir plusieurs thèmes ou largeurs, lancez plusieurs audits.
52
+
53
+ ## Auditer un échantillon de pages (ADR-0012)
54
+
55
+ La méthode officielle (DINUM, [« Évaluation de la conformité », section « Échantillon »](https://accessibilite.numerique.gouv.fr/obligations/evaluation-conformite/)) dit que l’échantillon « porte au moins sur les pages suivantes, lorsqu’elles existent » : accueil, contact, mentions légales, page « accessibilité », plan du site, aide, authentification. S’y ajoutent « au moins une page pertinente pour chaque type de service fourni […] (ex. : rubriques de 1er niveau dans l’arborescence…), y compris la fonctionnalité de recherche », « l’ensemble des pages constituant un processus », des pages au contenu sensiblement différent, au moins un document téléchargeable pertinent le cas échéant, et enfin « des pages sélectionnées au hasard représentant au moins 10 % des pages de l’échantillon ».
56
+
57
+ Le flux de travail :
58
+
59
+ ```sh
60
+ # 1. Rampe propose un échantillon (accueil chargé dans Chromium, liens de même origine)
61
+ rampe echantillon https://exemple.fr/ --sortie echantillon.txt
62
+ # 2. Vous relisez et modifiez echantillon.txt : ajoutez ce que Rampe n’a pas trouvé, retirez ce qui ne convient pas
63
+ # 3. Rampe audite chaque page, une à une, et produit un seul JSON
64
+ rampe audit --echantillon echantillon.txt --json rapport.json
65
+ ```
66
+
67
+ | Option de `rampe echantillon` | Effet |
68
+ |---|---|
69
+ | `--sortie <fichier>` | fichier à écrire (défaut : `echantillon.txt`) ; un fichier existant n’est **jamais écrasé**, puisqu’il a pu être modifié à la main |
70
+ | `--max <n>` | nombre maximal de pages proposées (défaut : 50, minimum 2) ; les pages écartées sont listées « à compléter » |
71
+ | `--graine <n>` | graine du tirage au hasard (entier de 0 à 4 294 967 295) ; sans elle, une graine est tirée et affichée pour rejouer le même échantillon |
72
+ | `--timeout <ms>` | durée maximale du chargement et de l’analyse de l’accueil (défaut : 60 000) |
73
+
74
+ Ce que Rampe propose, avec pour chaque page la **raison** de sa présence :
75
+
76
+ - **les 7 pages types**, repérées par des indices explicables : le texte du lien (sans accents ni majuscules), un segment de son adresse, et sa présence dans l’en-tête (`header`, `role="banner"`) ou le pied de page (`footer`, `role="contentinfo"`). Le texte doit être l’intitulé entier du lien, éventuellement suivi d’un séparateur : « Accessibilité : partiellement conforme » ou « Mentions légales & CGU » sont reconnus, « Contactez-nous pour un devis » dans un paragraphe ne l’est pas. La position seule ne suffit jamais. « Aide à domicile » n’est pas une page d’aide : la méthode vise l’aide à l’utilisation du site, pas une offre. Les intitulés sont surtout français, avec quelques équivalents anglais (*contact us*, *legal notice*, *accessibility*, *sitemap*, *help*, *sign in*…) ;
77
+ - **les rubriques** : liens de premier niveau de la navigation principale (celle de l’en-tête nommée « principal »/*main*, sinon celle qui a le plus de liens). Une navigation de langues, un fil d’Ariane, des liens d’évitement ou une navigation de moins de 3 liens ne comptent pas ; sans navigation principale, les rubriques sont « à compléter ». Les liens vers l’accueil lui-même (ancres, liens d’évitement) sont ignorés ;
78
+ - **la recherche** : formulaire dans `role="search"`, ou avec un champ `type="search"`, ou formulaire GET avec un champ `q`, `recherche`, `search`… ; Rampe ajoute la page de résultats pour le mot « contact ». Si l’adresse des résultats n’est pas déterminable (formulaire POST, recherche en JavaScript), Rampe le signale ;
79
+ - **un tirage au hasard** parmi les autres liens de même origine, d’au moins 10 % de l’échantillon (au moins une page), par un générateur à graine (mulberry32) : même graine et mêmes liens, même tirage.
80
+
81
+ Ce que Rampe ne devine pas est **signalé, jamais inventé** : une page type introuvable est notée « non trouvée automatiquement : à ajouter si elle existe » (Rampe ne conclut jamais qu’elle n’existe pas), l’absence de page « accessibilité » est mise en avant (la déclaration est une obligation légale), et les processus, les pages au contenu particulier et les documents téléchargeables sont toujours listés « à compléter ».
82
+
83
+ Format du fichier : une adresse par ligne ; les lignes vides et celles qui commencent par `#` sont ignorées. Au plus 1 000 adresses et 2 048 caractères par ligne ; une même adresse (sans fragment ni identifiants) n’est auditée qu’une fois. **Tirage au hasard** : si vous ajoutez des pages, ajoutez-en au hasard pour garder au moins 10 % de l’échantillon (1 pour 9). Rampe compte les adresses précédées d’une note `# hasard — …` et avertit, à la relecture, s’il en manque. Rampe écrit en commentaire, au-dessus de chaque adresse, son type et sa raison :
84
+
85
+ ```text
86
+ # contact — lien « Contact », texte reconnu, adresse « /contact » reconnue, dans le pied de page
87
+ https://exemple.fr/contact
88
+ ```
89
+
90
+ Garde-fous : seuls les liens **de même origine** que l’accueil (après redirections) sont retenus, sans fragment, sans doublon, sans fichiers non HTML (PDF, images…), et jamais une adresse dont la visite déclencherait une action (déconnexion, `delete`, suppression, désinscription…). Si l’accueil redirige vers une autre origine, un avertissement le dit (console et fichier) et l’échantillon est construit sur l’origine finale. Si `--max` écarte des pages obligatoires (pages types, recherche), un avertissement en tête dit que l’échantillon n’est pas conforme à la méthode tant qu’elles n’y sont pas ; les pages écartées ne reviennent pas par le tirage au hasard. Les identifiants éventuels de l’adresse (`https://moi:secret@…`) ne sont pas écrits dans le fichier : ajoutez-les à la main si besoin. Le fichier `robots.txt` n’est pas lu : Rampe audite, à faible volume, des pages que vous désignez sur votre propre site.
91
+
92
+ `rampe audit --echantillon <fichier>` vérifie d’abord tout le fichier (http(s) uniquement, toutes les adresses sur la même origine que la première ; sinon erreur avec le numéro de ligne), puis audite les pages **une à une**, dans un seul Chromium, chaque page dans un contexte neuf. `--timeout` s’applique alors à chaque page. Une page qui répond avec un statut HTTP ≥ 400 est en erreur (« HTTP 404 »), pas auditée. La console affiche un bloc par page puis une synthèse (« N pages auditées, M en erreur ») : critères avec une non-conformité détectée sur au moins une page, pages en erreur, et le rappel que les 106 critères demandent une vérification humaine.
93
+
94
+ Codes de sortie avec `--echantillon` : **0** aucune non-conformité détectée, **1** une non-conformité détectée sur au moins une page (avec `--strict` : ou un résultat incertain ou à vérifier), **2** au moins une page en erreur (indiquée en console et dans `error` du JSON ; les autres pages sont quand même auditées) ou fichier invalide.
95
+
96
+ Sous Ubuntu 23.10 ou plus récent, Chromium peut refuser de démarrer (« No usable sandbox! ») : AppArmor y interdit les espaces de noms utilisateur dont son bac à sable a besoin. Rampe ne désactive jamais ce bac à sable ; suivez plutôt la [documentation de Chromium](https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md) (profil AppArmor ciblé, comme dans `.github/workflows/ci.yml`).
97
+
98
+ ## Ce que fait l’audit
99
+
100
+ 1. Seules les adresses `http://` et `https://` sont acceptées. Des identifiants dans l’URL (`https://moi:secret@preprod…`, authentification basique) sont acceptés mais masqués (`***`) dans toutes les sorties : console, erreurs, JSON.
101
+ 2. Chromium headless, bac à sable activé (jamais `--no-sandbox`), charge la page : événement `load`, puis jusqu’à 5 s de réseau calme. Si la page a quitté le web entre-temps (ex. `about:blank`), l’audit s’arrête en erreur ; si elle a fini sur une autre origine que celle demandée (redirection), un avertissement s’affiche et figure dans `warnings` du JSON.
102
+ 3. axe-core est injecté par le protocole de débogage du navigateur, ce qui n’est pas soumis à la CSP de la page (sans la désactiver), puis lancé avec sa configuration par défaut (sans règles expérimentales). Il s’exécute dans un **monde isolé** (ADR-0020) : il partage le DOM de la page, mais pas ses objets JavaScript, si bien qu’une page qui redéfinit `window.axe` ou casse `getAttribute`, `getComputedStyle`, `Array.prototype.filter` ou `JSON.stringify` ne peut pas fausser son audit (c’est testé). La collecte de `rampe echantillon` passe par le même monde isolé. Seul le cadre principal est analysé : le contenu des iframes ne l’est pas encore. axe est lancé avec `iframes: false`, comme dans l’extension : il n’interroge pas les iframes par `postMessage`, sinon une iframe hostile pourrait forger leurs réponses (`frame-tested` reste signalé).
103
+ 4. **Navigation pendant l’analyse** (ADR-0023) : la CLI ne publie jamais un rapport qui risquerait d’être attribué à la mauvaise page. Si, **pendant** l’analyse, la page lance une navigation du cadre principal (lien suivi, `location.href`, redirection par script, téléchargement d’une pièce jointe…), ou change d’adresse par `history.pushState` ou `replaceState`, l’audit s’arrête : « La page a navigué pendant l’analyse : audit impossible. », code 2. Il suffit en général de relancer. Les mêmes actions **avant** l’analyse, au chargement, sont acceptées : l’adresse finale du rapport est alors celle de la page réellement analysée. Un simple changement d’ancre (`#…`) n’interrompt pas l’audit. Des identifiants dans l’URL non plus : ils sont ignorés dans la comparaison des adresses.
104
+ 5. Chaque résultat est classé avec `classifyAxeRule` et `isDirectCheck` de `@rampe/core` (ADR-0008, ADR-0011 §3) :
105
+
106
+ | Résultat axe | Rattachement | `kind` | Présenté comme |
107
+ |---|---|---|---|
108
+ | violation | direct (`rgaa`), ni bonne pratique ni expérimentale | `non-conformite` | non-conformité détectée, avec les tests RGAA cités |
109
+ | violation | direct (`rgaa`), bonne pratique ou expérimentale | `incertain` | sous son critère, « à confirmer par un humain » |
110
+ | violation | indirect (`wcag`) | `incertain` | problème détecté, rattachement RGAA incertain |
111
+ | violation | `hors-rgaa` | `bonne-pratique` | bonne pratique, hors conformité |
112
+ | incomplete | tout niveau | `a-verifier` | à vérifier par un humain |
113
+
114
+ Les règles dépréciées par axe sont exclues. Seule une `non-conformite` fait sortir la CLI en 1 : `region`, bonne pratique d’axe qui cite le test 9.2.1, reste `incertain`.
115
+
116
+ ## Ce que l’outil ne fait pas
117
+
118
+ - **Aucun score ni taux de conformité** (ADR-0002) : un audit automatique ne vérifie directement qu’une minorité des critères. Chaque rapport le rappelle : « Les 106 critères demandent une vérification humaine. 79 d’entre eux n’ont même aucune vérification automatique directe. » (axe-core 4.14.0 ; même décompte que le site, via `criteriaWithDirectRule`).
119
+ - **Aucune certification**, et aucune modification du site audité.
120
+
121
+ ## Rapport HTML (`--html`)
122
+
123
+ Pour la personne qui, dans la PME, reçoit le résultat sans être experte. Il fonctionne avec une page seule comme avec `--echantillon`, seul ou avec `--json`, et il est produit à partir du même JSON v2 (`renderHtmlReport` de `src/rapport.ts`, fonction pure).
124
+
125
+ - **Un seul fichier, autonome** : CSS dans une balise `<style>`, aucune ressource externe, **aucun JavaScript**. Il s’ouvre hors ligne, se joint à un courriel, s’imprime (les blocs repliés s’ouvrent à l’impression).
126
+ - **Accessible** (ADR-0011 §4) : `lang="fr"`, titres hiérarchisés, repères `header`/`main`/`footer`, lien d’évitement, longues listes repliées dans `<details>`, diagnostics d’axe en anglais marqués `lang="en"`, couleurs du site Rampe en clair et en sombre (`prefers-color-scheme`), lisible sur mobile. Un test de bout en bout lance axe-core 4.14.0 sur le rapport lui-même, en clair et en sombre, blocs ouverts : 0 violation attendue.
127
+ - **Échappé** (ADR-0011 §6) : titre, adresses, sélecteurs, extraits HTML, messages d’axe et avertissements passent tous par `escapeHtml` (OWASP *Cross Site Scripting Prevention Cheat Sheet*, « Output Encoding » pour le contexte HTML). Seules les adresses `http(s)` deviennent des liens. Les contrôles de direction bidirectionnelle (U+202A–202E, U+2066–2069) sont retirés, partout dans le rapport comme en console, et chaque donnée est isolée du texte de Rampe (`<bdi>`, `unicode-bidi: isolate`) : un titre piégé ne peut pas retourner le verdict qui le suit. Titre et sélecteur sont bornés à 300 caractères. Une page hostile (titre `<script>`, sélecteur `"><img onerror>`, U+202E…) ne peut pas piéger son rapport : c’est testé.
128
+ - **Honnête** (ADR-0002) : ni score ni taux. Un encadré, en tête, rappelle que ce n’est pas un audit de conformité et combien de critères n’ont aucune vérification automatique directe (même décompte que la console et le site).
129
+
130
+ Structure :
131
+
132
+ ```text
133
+ Rapport d’audit automatique — exemple.fr
134
+ Date · Pages auditées · Pages en erreur · Moteur (axe-core, Chromium)
135
+ [Encadré] Ce rapport n’est pas un audit de conformité.
136
+ Les 106 critères demandent une vérification humaine ; 79 n’ont aucune vérification automatique directe.
137
+ Avertissements (pages en erreur, redirection vers une autre origine…), s’il y en a
138
+ Synthèse
139
+ Non-conformités détectées Critère 1.1 — titre officiel : 2 pages (lien vers le détail)
140
+ Rattachement incertain règle, rattachement à confirmer, nombre de pages
141
+ À vérifier par un humain
142
+ Bonnes pratiques
143
+ Non-conformités, critère par critère
144
+ Critère 1.1 — titre officiel
145
+ Explication Rampe — non normative (intro de la thématique, la même que sur le site)
146
+ Lien vers la fiche du critère sur le site Rampe
147
+ Règle image-alt « Images must have alternative text », test 1.1.1 : N éléments
148
+ [replié] Voir les N éléments concernés par image-alt sur <page> : sélecteur, extrait HTML, diagnostic d’axe (en anglais)
149
+ Autres résultats détail : rattachement incertain, à vérifier, bonnes pratiques ; par règle
150
+ Pages de l’audit statut de chaque page (non-conformités ; points à vérifier) et pages en erreur
151
+ Ce qui reste à faire corriger (s’il y a lieu), audit humain des 106 critères, déclaration d’accessibilité
152
+ si l’organisation y est soumise (lien vers les obligations officielles)
153
+ Pied : version de Rampe ; référentiel RGAA © DINUM, Licence Ouverte 2.0
154
+ ```
155
+
156
+ Une page en erreur n’est jamais comptée comme « sans défaut » : elle est listée à part, avec la raison en clair (délai dépassé, adresse injoignable, HTTP 404…) et le message technique replié. Si aucune page n’a pu être auditée, la synthèse le dit en une phrase.
157
+
158
+ En mode URL unique (`rampe audit <url>`), une page en erreur arrête la CLI en 2 **sans écrire ni JSON ni HTML** : il n’y a rien à rapporter. Avec `--echantillon`, les autres pages sont auditées et les deux fichiers sont écrits.
159
+
160
+ ## Format JSON (`schemaVersion: 2`)
161
+
162
+ ```jsonc
163
+ {
164
+ "schemaVersion": 2,
165
+ "tool": { "name": "@rampe/cli", "version": "0.0.0" },
166
+ "engine": { "axe": "4.14.0", "browser": "156.0.8078.4" },
167
+ "startedAt": "2026-10-15T09:25:01.779Z",
168
+ "pages": [
169
+ {
170
+ "url": "https://exemple.fr/",
171
+ "finalUrl": "https://exemple.fr/", // après redirections
172
+ "title": "Accueil",
173
+ "warnings": [], // ex. redirection vers une autre origine
174
+ "error": null, // message si la page n'a pas pu être auditée (échantillon) ;
175
+ // finalUrl et title sont alors null, results vide
176
+ "results": [
177
+ {
178
+ "ruleId": "image-alt",
179
+ "help": "Images must have alternative text", // description de la règle par axe, en anglais
180
+ "kind": "non-conformite",
181
+ "level": "rgaa", // précision du rattachement : rgaa | wcag | hors-rgaa
182
+ "tests": ["1.1.1"], // tests RGAA cités (niveau rgaa)
183
+ "criteria": ["1.1"],
184
+ "wcag": [], // critères de succès WCAG (niveaux wcag et hors-rgaa)
185
+ "flags": { "bestPractice": false, "experimental": false },
186
+ "impact": "critical",
187
+ "nodes": [{ "target": ["img"], "html": "<img src=…>", "failureSummary": "…" }]
188
+ }
189
+ ]
190
+ }
191
+ ]
192
+ }
193
+ ```
194
+
195
+ `html` est tronqué à 500 caractères, marqué « … (tronqué) », sans couper un caractère en deux. `pages` contient une entrée par page auditée, dans l’ordre du fichier d’échantillon.
196
+
197
+ Version 2 : ajout de `error` par page, et `finalUrl`/`title` à `null` pour une page en erreur. Sans ce changement, un lecteur de la version 1 aurait pris une page en erreur, sans résultats, pour une page sans défaut.
198
+
199
+ ## Reprendre le JSON dans l’audit guidé (ADR-0014)
200
+
201
+ Le fichier `--json` s’importe dans la page `/audit/` du site Rampe, pour ne pas ressaisir ce que la machine a trouvé :
202
+
203
+ ```sh
204
+ rampe echantillon https://exemple.fr/ --sortie echantillon.txt # 1. proposer l’échantillon
205
+ rampe audit --echantillon echantillon.txt --json rapport.json # 2. audit automatique
206
+ # 3. Sur /audit/ : « Importer un rapport automatique (CLI ou extension, JSON) », choisir rapport.json
207
+ ```
208
+
209
+ - Les pages du rapport absentes de l’audit sont ajoutées. Elles sont rapprochées par adresse, sans fragment ni identifiants. Les pages en erreur ne sont pas importées et sont listées dans le bilan.
210
+ - Une **non-conformité** passe le critère à « Non conforme » **seulement si rien n’a encore été saisi** pour ce critère sur la page. L’évaluation est alors marquée « Détecté automatiquement, à confirmer », avec une note générée (« Détecté automatiquement : image-alt (3 éléments) ») et un bouton « Confirmer ». Dès que la personne confirme, change le statut ou la note, l’évaluation devient la sienne.
211
+ - Les autres résultats (rattachement incertain, à vérifier) deviennent des **indices** sous le critère et la page. Ils ne valent ni conformité ni non-conformité. Une non-conformité trouvée sur un critère déjà évalué y figure aussi, sans toucher à l’évaluation ; si la personne l’avait jugé « Conforme », un avertissement l’invite à vérifier.
212
+ - Réimporter un rapport plus récent remplace les indices des pages qu’il contient : un défaut corrigé disparaît des indices.
213
+ - Une évaluation saisie par une personne n’est jamais modifiée. Réimporter le même fichier ne change rien. « Annuler cet import » restaure l’état précédent tant que rien d’autre n’a été modifié.
214
+ - Le fichier vient de l’extérieur de l’application : il est validé strictement, tout ou rien (`parseAuditReport` de `@rampe/core`). Un fichier refusé ne modifie pas l’audit, et le message nomme le premier défaut trouvé.
215
+
216
+ `tool.name` reste `@rampe/cli` : c’est l’identifiant du format, que l’audit guidé vérifie à l’import, pas le nom du paquet npm (`rampe`).
217
+
218
+ Le titre de page est borné à 300 caractères dans le JSON (« … (tronqué) »). Le champ `help` est apparu après la première version du format 2. Il reste facultatif à l’import, donc un fichier plus ancien s’importe aussi, sans les descriptions.
219
+
220
+ ## Tests
221
+
222
+ `pnpm --filter @chikimuras/rampe test` lance les tests unitaires et des tests de bout en bout : la vraie CLI audite les pages de `test/fixtures/` servies par un serveur `node:http` local (défauts connus aux critères 1.1, 3.2, 8.3 et 11.1 ; page propre ; `region` seule, aussi en `--strict` ; défauts de contraste propres au thème sombre et à la fenêtre étroite (`--theme`, `--largeur`) ; boucle infinie ; départ vers `about:blank` ; CSP `script-src 'none'` ; page piège qui falsifie `window.axe` et casse des primitives JavaScript, et accueil piège qui fausse la collecte de l’échantillon ; redirection vers une autre origine avec identifiants dans l’URL). Un mini-site local (`test/fixtures/site-accueil.html` et des pages générées) sert à enchaîner `rampe echantillon` puis `rampe audit --echantillon` : pages types et raisons, page manquante signalée, JSON multi-pages, page en erreur et codes de sortie. Il faut Chromium (commande ci-dessus). Les pages de test sont exclues de Biome, puisqu’elles sont fautives exprès.
223
+
224
+ `pnpm --filter @chikimuras/rampe test:paquet` (hors de `pnpm check`, car il demande npm et l’accès au registre ; lancé par la CI) teste le paquet réel, `test/paquet.e2e.test.ts` : `npm pack` (le build passe par `prepack`), liste exacte de l’archive (`dist/rampe.mjs`, `README.md`, `LICENSE`, `package.json`), puis installation de l’archive dans un dossier vierge et audit de `test/fixtures/defauts.html` par `node_modules/.bin/rampe`, y compris sans Chromium (code 2).
225
+
226
+ ## Publier une version
227
+
228
+ La publication passe par la CI (`.github/workflows/publish.yml`), jamais depuis un poste, sauf la toute première (ci-dessous) :
229
+
230
+ 1. changer `version` dans `packages/cli/package.json` par une PR ;
231
+ 2. une fois la PR fusionnée, poser l’étiquette sur `main` : `git tag v<version> && git push origin v<version>`.
232
+
233
+ Le workflow a deux jobs. `build`, sans droit d’écriture, vérifie que l’étiquette correspond à la version et que le commit est sur `main`, lance `pnpm check` et le test du paquet, puis produit l’archive (`npm pack`, qui construit par `prepack`). `publish`, dans l’environnement GitHub `npm`, publie cette archive telle quelle avec le *Trusted Publishing* de npm (jeton OIDC éphémère, aucun jeton npm stocké) et une attestation de provenance.
234
+
235
+ ### Première publication (manuelle, par Alexandre)
236
+
237
+ npm ne déclare un éditeur de confiance que pour un paquet qui existe déjà. La version 0.1.0 est donc publiée à la main, depuis un clone à jour de `main` :
238
+
239
+ ```sh
240
+ npm login
241
+ pnpm install --frozen-lockfile
242
+ pnpm --filter @chikimuras/rampe exec playwright install chromium
243
+ pnpm check
244
+ pnpm --filter @chikimuras/rampe test:paquet
245
+ cd packages/cli && npm publish --access public # prepack construit dist/rampe.mjs
246
+ ```
247
+
248
+ Pas de `--provenance` ici : l’attestation n’est possible que depuis la CI.
249
+
250
+ Ensuite, déclarer l’éditeur de confiance **juste avant de poser l’étiquette de la version suivante** : une configuration doit être validée par une publication réussie **dans les 2 jours** (documentation npm), sinon elle expire et il faut la supprimer puis la recréer. Sur npmjs.com, page du paquet `@chikimuras/rampe`, *Settings*, section *Trusted Publisher* :
251
+
252
+ - fournisseur : GitHub Actions ;
253
+ - *Organization or user* : `Chikimuras` ;
254
+ - *Repository* : `rampe` ;
255
+ - *Workflow filename* : `publish.yml` ;
256
+ - *Environment name* : `npm` (environnement GitHub du job `publish`) ;
257
+ - autoriser la publication directe (`npm publish`).
258
+
259
+ Puis, dans les 2 jours, publier la version suivante par la CI (PR de version, puis étiquette, voir plus haut). Une fois la configuration validée, npm recommande d’interdire la publication par jeton classique (*Publishing access* : exiger la double authentification et refuser les jetons).