mjolnir-qa 0.4.0 → 0.5.2
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.
- package/CHANGELOG.md +1199 -28
- package/README.ar.md +667 -0
- package/README.bn.md +678 -0
- package/README.br.md +706 -0
- package/README.bs.md +690 -0
- package/README.da.md +698 -0
- package/README.de.md +711 -0
- package/README.es.md +709 -0
- package/README.fr.md +714 -0
- package/README.gr.md +705 -0
- package/README.he.md +664 -0
- package/README.it.md +709 -0
- package/README.ja.md +692 -0
- package/README.ko.md +681 -0
- package/README.md +425 -152
- package/README.no.md +696 -0
- package/README.pl.md +699 -0
- package/README.ru.md +701 -0
- package/README.th.md +672 -0
- package/README.tr.md +699 -0
- package/README.uk.md +693 -0
- package/README.vi.md +680 -0
- package/README.zh.md +652 -0
- package/README.zht.md +652 -0
- package/dist/cli.d.mts +469 -22
- package/dist/cli.mjs +9780 -5230
- package/package.json +25 -7
package/README.fr.md
ADDED
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/readme/logo.png" alt="Mjölnir — Verification Trust Engine" width="800" />
|
|
4
|
+
|
|
5
|
+
### Vos tests vous mentent. Nous le prouvons.
|
|
6
|
+
|
|
7
|
+
**Verification Trust Engine pour la QA.** Mjölnir audite les suites de
|
|
8
|
+
tests et les pipelines CI, rapporte un score de fiabilité et montre
|
|
9
|
+
exactement où la confiance se brise.
|
|
10
|
+
|
|
11
|
+
[](https://www.npmjs.com/package/mjolnir-qa)
|
|
12
|
+
[](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
|
|
13
|
+
[](LICENSE)
|
|
14
|
+
[](https://nodejs.org)
|
|
15
|
+
|
|
16
|
+
[English](README.md) | [简体中文](README.zh.md) | [繁體中文](README.zht.md) | [한국어](README.ko.md) | [Deutsch](README.de.md) | [Español](README.es.md) | Français | [Italiano](README.it.md) | [Dansk](README.da.md) | [日本語](README.ja.md) | [Polski](README.pl.md) | [Русский](README.ru.md) | [Norsk](README.no.md) | [Português (Brasil)](README.br.md) | [ไทย](README.th.md) | [Türkçe](README.tr.md) | [Українська](README.uk.md) | [বাংলা](README.bn.md) | [Ελληνικά](README.gr.md) | [Tiếng Việt](README.vi.md) | [עברית](README.he.md) | [العربية](README.ar.md) | [Bosanski](README.bs.md)
|
|
17
|
+
|
|
18
|
+
> 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-04.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx mjolnir-qa@latest
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Vos tests sont-ils dignes de confiance ?**
|
|
25
|
+
|
|
26
|
+
[Le voir en action](#-le-voir-en-action) ·
|
|
27
|
+
[Démarrage rapide](#-démarrage-rapide) ·
|
|
28
|
+
[Ce qu'il vérifie](#-ce-que-mjölnir-vérifie) ·
|
|
29
|
+
[Scoring](#comment-le-score-fonctionne) ·
|
|
30
|
+
[CI](#-intégration-ci) · [Configuration](#configuration) ·
|
|
31
|
+
[Documentation](#-documentation)
|
|
32
|
+
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 🎬 Le voir en action
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<img src="assets/readme/demo.svg" alt="Le rapport --verbose complet de Mjölnir sur un dépôt de démo : WORTHINESS 75/100 NEEDS WORK, une répartition des diagnostics par catégorie, une liste FIX THIS FIRST, et chaque constat avec son ID de règle et son numéro de ligne à travers CI, Playwright, l'hygiène des tests et les règles Python" width="900" />
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
<sub>La sortie complète de `npx mjolnir-qa ./examples/demo-repo --verbose`,
|
|
44
|
+
rendue par le vrai reporter — rien de rogné. Régénérée par
|
|
45
|
+
`npm run docs:demo` ;
|
|
46
|
+
[`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
|
|
47
|
+
fait échouer la CI si elle dérive de ce que l'outil imprime.</sub>
|
|
48
|
+
|
|
49
|
+
**Ce qui vient de se passer :**
|
|
50
|
+
|
|
51
|
+
1. Mjölnir a découvert les specs Playwright, sa configuration, le
|
|
52
|
+
workflow CI et un fichier de test Python — quatre langages/formats,
|
|
53
|
+
une seule passe.
|
|
54
|
+
2. Il a trouvé des preuves qui affaiblissent la confiance dans la suite
|
|
55
|
+
— un `continue-on-error` qui masque un job, un `|| true` qui avale un
|
|
56
|
+
code de sortie, des sleeps en dur, un sélecteur fragile, des URLs de
|
|
57
|
+
staging codées en dur, une attente `networkidle`.
|
|
58
|
+
3. Il en a fait un constat concret avec un ID de règle, un emplacement
|
|
59
|
+
et un correctif — et un score unique sur lequel gate une PR.
|
|
60
|
+
|
|
61
|
+
### Un constat de près
|
|
62
|
+
|
|
63
|
+
Exécutez `mjolnir explain QA-CI-001` sur le premier constat ci-dessus
|
|
64
|
+
et vous obtenez :
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
▚▞ QA-CI-001 — continue-on-error masks a failing verification gate
|
|
68
|
+
|
|
69
|
+
Severity: error
|
|
70
|
+
Confidence: high
|
|
71
|
+
Evidence: E2
|
|
72
|
+
Measured FP: not yet measured — this rule ships on assumption (see docs/FP-AUDIT.md)
|
|
73
|
+
|
|
74
|
+
WHAT WAS FOUND (real detector output, not a mockup)
|
|
75
|
+
Job `security-scan` runs a verification gate under `continue-on-error: true`.
|
|
76
|
+
|
|
77
|
+
WHY IT MATTERS
|
|
78
|
+
This job can fail every day and CI will still show green. The checkmark
|
|
79
|
+
on this workflow cannot be trusted.
|
|
80
|
+
|
|
81
|
+
HOW TO FIX
|
|
82
|
+
Remove continue-on-error, or scope it to individual non-blocking steps only.
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
C'est l'unité de valeur : pas une broutille de style, mais un endroit
|
|
86
|
+
où votre CI affiche quelque chose comme réussi alors que ça ne l'est
|
|
87
|
+
pas.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## ⚡ Démarrage rapide
|
|
92
|
+
|
|
93
|
+
Exécutez-le sur un dépôt pour un rapport complet et un score de
|
|
94
|
+
fiabilité :
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npx mjolnir-qa@latest
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**En CI, le produit tient en une commande.** Il ne scanne que ce que la
|
|
101
|
+
branche a touché et sort avec un code non nul sur de nouveaux
|
|
102
|
+
problèmes :
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npx mjolnir-qa@latest --scope changed
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Déposez ça dans un check de PR — `mjolnir ci install` écrit le workflow —
|
|
109
|
+
et c'est fini. Tout le reste est optionnel.
|
|
110
|
+
|
|
111
|
+
| Commande | Ce qu'elle fait |
|
|
112
|
+
| ----------------------------------- | ---------------------------------------------------------------------- |
|
|
113
|
+
| `mjolnir` | Scan complet du dépôt + score de fiabilité |
|
|
114
|
+
| `mjolnir --scope changed` | Uniquement ce que votre branche a introduit — la forme CI |
|
|
115
|
+
| `mjolnir ci install` | Génère le workflow de PR consultatif |
|
|
116
|
+
| `mjolnir explain QA-CI-001` | Quoi / pourquoi / correctif + taux de FP mesuré d'une règle |
|
|
117
|
+
| `mjolnir rules --unmeasured` | Les règles qui tournent sur hypothèse, pas sur mesure |
|
|
118
|
+
| `mjolnir --json` / `--format sarif` | Lisible par machine / GitHub Code Scanning |
|
|
119
|
+
| `mjolnir --strict` | Exécute aussi les règles du tier quarantaine (risque de FP plus élevé) |
|
|
120
|
+
|
|
121
|
+
<details>
|
|
122
|
+
<summary><strong>Quand quelque chose est instable</strong></summary>
|
|
123
|
+
|
|
124
|
+
| Commande | Ce qu'elle fait |
|
|
125
|
+
| ----------------------------------- | -------------------------------------------------------------- |
|
|
126
|
+
| `mjolnir forensics ./test-results/` | Vraies données d'exécution → verdicts `TRUE-FLAKE`, `FLAKY.md` |
|
|
127
|
+
| `mjolnir triage ./test-results/` | Proposition de quarantaine issue de l'historique d'exécution |
|
|
128
|
+
| `mjolnir pw-report ./test-results/` | Synthèse de run Playwright — retries / flakes / plus lents |
|
|
129
|
+
| `mjolnir doctor:playwright` | Scan profond Playwright uniquement + Selector Health Score |
|
|
130
|
+
|
|
131
|
+
</details>
|
|
132
|
+
|
|
133
|
+
<details>
|
|
134
|
+
<summary><strong>Occasionnel / rapports</strong></summary>
|
|
135
|
+
|
|
136
|
+
| Commande | Ce qu'elle fait |
|
|
137
|
+
| ------------------------------- | ----------------------------------------------------------------- |
|
|
138
|
+
| `mjolnir fix --dry-run` / `fix` | Auto-corrections sûres avec preuve |
|
|
139
|
+
| `mjolnir baseline` / `diff` | Instantané des constats, puis rapport des seuls nouveaux/aggravés |
|
|
140
|
+
| `mjolnir impact --since <ref>` | Ce qui a changé depuis un commit antérieur |
|
|
141
|
+
| `mjolnir debt` | Registre de dette de test avec un modèle de coût |
|
|
142
|
+
| `mjolnir handover` | Carte d'onboarding de la suite pour un nouveau QA |
|
|
143
|
+
| `mjolnir stats` | Compteurs locaux de tous les correctifs vus |
|
|
144
|
+
| `mjolnir badge` | JSON d'endpoint shields.io + snippet |
|
|
145
|
+
| `mjolnir rules --md` | Catalogue complet des règles (JSON ou Markdown) |
|
|
146
|
+
| `mjolnir doctor` | Auto-audit de la propre base de règles de Mjölnir |
|
|
147
|
+
| `mjolnir create-rule <ID>` | Scafholde une nouvelle règle + ses fixtures |
|
|
148
|
+
| `mjolnir --format mermaid` | Diagramme d'architecture de test pour un commentaire de PR |
|
|
149
|
+
|
|
150
|
+
</details>
|
|
151
|
+
|
|
152
|
+
Installez-le globalement plutôt qu'en `npx` si vous préférez :
|
|
153
|
+
`npm i -g mjolnir-qa`. Requiert Node.js ≥ 22.18. Fonctionne sous
|
|
154
|
+
Windows, macOS et Linux.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 👥 À qui ça s'adresse ?
|
|
159
|
+
|
|
160
|
+
- **QA / SDET** propriétaires d'une suite e2e ou d'intégration qui ont
|
|
161
|
+
besoin de la preuve que la suite mérite vraiment la coche verte
|
|
162
|
+
qu'elle produit.
|
|
163
|
+
- **Équipes Plateforme / DevEx** responsables de l'intégrité CI et des
|
|
164
|
+
release gates — celles et ceux pour qui un `continue-on-error` ne
|
|
165
|
+
doit jamais repeindre en vert une pipeline rouge en silence.
|
|
166
|
+
- **Mainteneurs OSS** qui veulent un gate de vérification bon marché,
|
|
167
|
+
toujours actif, qui tourne en local et en CI sans aucun appel réseau.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 🔨 Ce que Mjölnir vérifie
|
|
172
|
+
|
|
173
|
+
| | |
|
|
174
|
+
| --- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
175
|
+
| ⚖️ | **Score de fiabilité** — un chiffre, une table de déductions transparente, aucune boîte noire |
|
|
176
|
+
| 🎭 | **Selector Health Score** — note vos locators Playwright, pas seulement votre taux de réussite |
|
|
177
|
+
| 🔬 | **Forensique d'exécution** — lit de vraies données de run Playwright/JUnit pour détecter `TRUE-FLAKE`, pas seulement des suppositions statiques |
|
|
178
|
+
| 🚨 | **Règles d'intégrité CI** — attrape `continue-on-error`, `\|\| true` et autres astuces à faux vert |
|
|
179
|
+
| 🐍 | **Les quatre bindings Playwright** — TypeScript, Python, Java, C#/.NET — plus pytest, JUnit/TestNG et workflows CI |
|
|
180
|
+
| 🔒 | **Local-first** — zéro appel réseau pendant le scan, zéro télémétrie, s'exécute en secondes |
|
|
181
|
+
|
|
182
|
+
### Les règles
|
|
183
|
+
|
|
184
|
+
Chaque règle est livrée avec des fixtures must-fire **et** must-not-fire.
|
|
185
|
+
Une règle qui se déclenche sur sa propre fixture négative ne peut pas
|
|
186
|
+
sortir — c'est le pare-feu anti faux positifs.
|
|
187
|
+
|
|
188
|
+
<details>
|
|
189
|
+
<summary><strong>Hygiène des tests</strong></summary>
|
|
190
|
+
|
|
191
|
+
| ID | Règle | Severity |
|
|
192
|
+
| ----------- | ----------------------------------------------------- | -------- |
|
|
193
|
+
| QA-TEST-001 | Test focalisé commité (`.only`, `fit`) | error |
|
|
194
|
+
| QA-TEST-002 | Test sauté sans justification | error |
|
|
195
|
+
| QA-TEST-002 | Test sauté avec justification tracée | warning |
|
|
196
|
+
| QA-TEST-003 | Test sans assertion | error |
|
|
197
|
+
| QA-TEST-004 | Sleep en dur (`waitForTimeout`, `sleep()`, `delay()`) | warning |
|
|
198
|
+
| QA-TEST-006 | Abus de retry masquant l'instabilité | warning |
|
|
199
|
+
| QA-TEST-010 | Corps de test vide | error |
|
|
200
|
+
|
|
201
|
+
</details>
|
|
202
|
+
|
|
203
|
+
<details>
|
|
204
|
+
<summary><strong>Qualité des tests</strong></summary>
|
|
205
|
+
|
|
206
|
+
| ID | Règle | Severity |
|
|
207
|
+
| ------------ | --------------------------------- | -------- |
|
|
208
|
+
| QA-TQUAL-001 | Vérification par mocks uniquement | info |
|
|
209
|
+
| QA-TQUAL-002 | Assertion tautologique | error |
|
|
210
|
+
| QA-TQUAL-009 | Assertion de promise non awaitée | error |
|
|
211
|
+
| QA-TQUAL-011 | Tests commentés | warning |
|
|
212
|
+
|
|
213
|
+
</details>
|
|
214
|
+
|
|
215
|
+
<details>
|
|
216
|
+
<summary><strong>Playwright 🎭</strong></summary>
|
|
217
|
+
|
|
218
|
+
| ID | Règle | Severity |
|
|
219
|
+
| --------- | ------------------------------------------------ | -------- |
|
|
220
|
+
| QA-PW-002 | Assertion de locator non awaitée | error |
|
|
221
|
+
| QA-PW-003 | `page.pause()` / `test.only()` commités | error |
|
|
222
|
+
| QA-PW-004 | Sélecteurs CSS/XPath fragiles | warning |
|
|
223
|
+
| QA-PW-005 | Logique métier dans `page.evaluate()` | info |
|
|
224
|
+
| QA-PW-114 | Element handles historiques (`page.$`) | info |
|
|
225
|
+
| QA-PW-118 | Attentes `networkidle` (instables de conception) | info |
|
|
226
|
+
| QA-PW-123 | URLs d'environnement codées en dur | warning |
|
|
227
|
+
|
|
228
|
+
</details>
|
|
229
|
+
|
|
230
|
+
<details>
|
|
231
|
+
<summary><strong>Intégrité CI</strong></summary>
|
|
232
|
+
|
|
233
|
+
| ID | Règle | Severity |
|
|
234
|
+
| --------- | -------------------------------------------------------------------- | -------- |
|
|
235
|
+
| QA-CI-001 | `continue-on-error` masque les échecs | error |
|
|
236
|
+
| QA-CI-002 | `\|\| true` avale les codes de sortie | error |
|
|
237
|
+
| QA-CI-005 | Rapport consommé mais jamais généré | error |
|
|
238
|
+
| QA-CI-007 | Wrappers de retry autour des tests | warning |
|
|
239
|
+
| QA-CI-008 | Step toujours réussi masquant les échecs | error |
|
|
240
|
+
| QA-CI-009 | Code de sortie du test non propagé (`\|` sans pipefail, chaînes `;`) | error |
|
|
241
|
+
| QA-CI-010 | Tests sautés là où ils doivent bloquer (gardes skip-on-PR) | error |
|
|
242
|
+
|
|
243
|
+
</details>
|
|
244
|
+
|
|
245
|
+
<details>
|
|
246
|
+
<summary><strong>Python / pytest 🐍</strong></summary>
|
|
247
|
+
|
|
248
|
+
| ID | Règle | Severity |
|
|
249
|
+
| --------- | ----------------------------------------- | -------- |
|
|
250
|
+
| QA-PY-002 | Test sauté (`skip`, `xfail` non strict) | warning |
|
|
251
|
+
| QA-PY-003 | Fonction de test sans assertion | error |
|
|
252
|
+
| QA-PY-005 | `time.sleep()` dans les tests | warning |
|
|
253
|
+
| QA-PY-006 | Corps de test vide (`pass`) | info |
|
|
254
|
+
| QA-PY-010 | Dépendance au hasard/au temps sans freeze | info |
|
|
255
|
+
| QA-PY-012 | Assertion tautologique | error |
|
|
256
|
+
|
|
257
|
+
20 règles Python au total (QA-PY-001…012 hygiène pytest + QA-PY-101…108 Playwright-Python).
|
|
258
|
+
|
|
259
|
+
</details>
|
|
260
|
+
|
|
261
|
+
<details>
|
|
262
|
+
<summary><strong>Java / JUnit · TestNG ☕</strong></summary>
|
|
263
|
+
|
|
264
|
+
| ID | Règle | Severity |
|
|
265
|
+
| --------- | --------------------------------------------- | -------- |
|
|
266
|
+
| QA-JV-101 | Test désactivé (`@Disabled`) | warning |
|
|
267
|
+
| QA-JV-102 | Sleep en dur (`Thread.sleep()`) | warning |
|
|
268
|
+
| QA-JV-103 | Méthode de test sans assertion | error |
|
|
269
|
+
| QA-JV-105 | Sleep en dur Playwright `waitForTimeout()` | warning |
|
|
270
|
+
| QA-JV-106 | Sélecteur fragile au lieu d'un role locator | warning |
|
|
271
|
+
| QA-JV-108 | URL d'environnement codée en dur dans le test | info |
|
|
272
|
+
| QA-JV-111 | Mock blanket `page.route("**")` | info |
|
|
273
|
+
|
|
274
|
+
</details>
|
|
275
|
+
|
|
276
|
+
<details>
|
|
277
|
+
<summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
|
|
278
|
+
|
|
279
|
+
| ID | Règle | Severity |
|
|
280
|
+
| --------- | --------------------------------------------- | -------- |
|
|
281
|
+
| QA-CS-101 | Test sauté (`[Ignore]`, `[Fact(Skip=)]`) | warning |
|
|
282
|
+
| QA-CS-102 | Sleep en dur (`Thread.Sleep` / `Task.Delay`) | warning |
|
|
283
|
+
| QA-CS-103 | Méthode de test sans assertion | error |
|
|
284
|
+
| QA-CS-105 | Sleep en dur `WaitForTimeoutAsync()` | warning |
|
|
285
|
+
| QA-CS-106 | Sélecteur fragile au lieu d'un role locator | warning |
|
|
286
|
+
| QA-CS-108 | URL d'environnement codée en dur dans le test | info |
|
|
287
|
+
| QA-CS-111 | Mock blanket `page.RouteAsync("**")` | info |
|
|
288
|
+
|
|
289
|
+
</details>
|
|
290
|
+
|
|
291
|
+
> Le catalogue live complet — chaque règle avec son tier, sa
|
|
292
|
+
> confidence, son risque de faux positif et sa disponibilité d'autofix —
|
|
293
|
+
> est généré depuis le registre :
|
|
294
|
+
>
|
|
295
|
+
> ```bash
|
|
296
|
+
> mjolnir rules --md
|
|
297
|
+
> ```
|
|
298
|
+
>
|
|
299
|
+
> Les pages par règle vivent sous [`docs/rules/`](docs/rules/).
|
|
300
|
+
|
|
301
|
+
### Quelle part est mesurée
|
|
302
|
+
|
|
303
|
+
**74 des 99 règles portent un taux de faux positifs mesuré sur du vrai
|
|
304
|
+
code OSS** (≥ 10 constats classés à la main chacun ; voir
|
|
305
|
+
[docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Les 19 autres sortent sur
|
|
306
|
+
l'estimation de l'auteur. Chaque pied de scan vous dit combien des
|
|
307
|
+
règles _déclenchées_ sont mesurées ; `mjolnir rules --unmeasured` liste
|
|
308
|
+
celles qui ne le sont pas ; la page `mjolnir explain` de chaque règle
|
|
309
|
+
énonce son statut. Nous publions le taux même quand il est laid —
|
|
310
|
+
QA-CS-103 s'audite à 95 % et est mis en quarantaine pour ça. Faire
|
|
311
|
+
grandir ce 78 est le travail continu du projet.
|
|
312
|
+
|
|
313
|
+
### Tiers de règles et maturité par langage
|
|
314
|
+
|
|
315
|
+
Chaque règle est `core`, `extended` ou `quarantine`, attribué d'après
|
|
316
|
+
son taux de faux positifs **mesuré** :
|
|
317
|
+
|
|
318
|
+
| Tier | Signification | Scan par défaut | `--strict` |
|
|
319
|
+
| ------------ | ------------------------------------------------ | :-------------: | :--------: |
|
|
320
|
+
| `core` | ≤ 10 % de FP mesuré | ✅ | ✅ |
|
|
321
|
+
| `extended` | ≤ 30 % de FP mesuré | ✅ | ✅ |
|
|
322
|
+
| `quarantine` | au-dessus de 30 %, ou pas encore mesuré (n < 10) | ❌ | ✅ |
|
|
323
|
+
|
|
324
|
+
| Langage | Adaptateur | Couverture aujourd'hui |
|
|
325
|
+
| --------------- | --------------- | ---------------------------------------------------------- |
|
|
326
|
+
| TypeScript / JS | AST compilateur | la plus large, la plus mesurée — surtout `core`/`extended` |
|
|
327
|
+
| Python / pytest | Couche regex | large, auditée sur corpus — surtout `core`/`extended` |
|
|
328
|
+
| Java | Couche regex | plus récent — surtout `extended`/`quarantine` |
|
|
329
|
+
| C# / .NET | Couche regex | plus récent — surtout `extended`/`quarantine` |
|
|
330
|
+
|
|
331
|
+
TypeScript et Python ont la couverture mesurée la plus large. Java et
|
|
332
|
+
C# sont livrés, documentés, et restent hors du chiffre vedette tant
|
|
333
|
+
qu'une vraie suite consommatrice (pas les propres tests d'une
|
|
334
|
+
bibliothèque de binding) n'a pas été auditée.
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Comment le score fonctionne
|
|
339
|
+
|
|
340
|
+
<p align="center">
|
|
341
|
+
<img src="assets/readme/terminal-hero.svg" alt="Sortie terminal de Mjölnir — WORTHINESS 75/100 NEEDS WORK, une répartition des diagnostics par catégorie et une liste FIX THIS FIRST" width="820" />
|
|
342
|
+
</p>
|
|
343
|
+
|
|
344
|
+
<sub>Régénérée par `npm run docs:hero` ;
|
|
345
|
+
[`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
|
|
346
|
+
fait échouer la CI si elle dérive de ce que le reporter imprime
|
|
347
|
+
réellement.</sub>
|
|
348
|
+
|
|
349
|
+
Le score est transparent : **error −8, warning −3, info −1**, puis
|
|
350
|
+
normalisé par l'exposition de la suite (déductions par déclaration de
|
|
351
|
+
test). Les déductions pondérées par la preuve signifient que les
|
|
352
|
+
signaux faibles coûtent moins cher. Le terminal affiche les mêmes
|
|
353
|
+
chiffres actualisés que ceux du score — pas de boîte noire. Méthode
|
|
354
|
+
complète : [docs/SCORING.md](docs/SCORING.md).
|
|
355
|
+
|
|
356
|
+
**Verdicts**
|
|
357
|
+
|
|
358
|
+
| Score | Verdict |
|
|
359
|
+
| ------- | ---------------- |
|
|
360
|
+
| ≥ 80 | ✓ **WORTHY** |
|
|
361
|
+
| 50 – 79 | ⚠ **NEEDS WORK** |
|
|
362
|
+
| < 50 | ✖ **UNWORTHY** |
|
|
363
|
+
|
|
364
|
+
**Niveaux de preuve** — chaque constat en porte un ; il fixe le poids
|
|
365
|
+
du constat dans le score :
|
|
366
|
+
|
|
367
|
+
| Niveau | Signification | Impact sur le score | Exemple |
|
|
368
|
+
| ------ | ------------------- | --------------------- | --------------------------------------------------------- |
|
|
369
|
+
| E2 | Défaut déterministe | Déduction pleine | `.only` commité — structurellement prouvable |
|
|
370
|
+
| E1 | Motif heuristique | Déduction moitié | `sleep()` détecté par regex — signal fort, pas une preuve |
|
|
371
|
+
| E0 | Observation | Zéro (info seulement) | Rapporté mais ne gate jamais la CI ni ne déduit |
|
|
372
|
+
|
|
373
|
+
La plupart des règles sont **E1**. Le slogan « we prove it » renvoie à
|
|
374
|
+
ce système : les constats E2 sont une preuve structurelle ; les
|
|
375
|
+
constats E1 sont des avertissements correctement positionnés, pas des
|
|
376
|
+
preuves formelles.
|
|
377
|
+
|
|
378
|
+
Un dépôt vide obtient `null`, jamais un faux 100 — voir
|
|
379
|
+
[Modèle de confiance](#modèle-de-confiance).
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## 🎭 Selector Health Score
|
|
384
|
+
|
|
385
|
+
La métrique vedette pour les suites Playwright — la résilience de vos
|
|
386
|
+
locators :
|
|
387
|
+
|
|
388
|
+
```text
|
|
389
|
+
▚▞ SELECTOR HEALTH — e2e/checkout.spec.ts
|
|
390
|
+
|
|
391
|
+
[█████████████████░░░] 83 / 100
|
|
392
|
+
role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Les locators basés sur les rôles obtiennent la note maximale. Les
|
|
396
|
+
chaînes de classes CSS et le XPath effondrent le score — ils cassent à
|
|
397
|
+
chaque refonte du DOM sans vous dire quel comportement a régressé.
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## 🔬 Preuves d'exécution
|
|
402
|
+
|
|
403
|
+
La détection statique d'instabilité, c'est deviner. Mjölnir lit de
|
|
404
|
+
**vraies données d'exécution** — rapports JSON Playwright et XML JUnit
|
|
405
|
+
de n'importe quel runner :
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
mjolnir forensics ./test-results/
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
```text
|
|
412
|
+
▚▞ FLAKINESS LEADERBOARD
|
|
413
|
+
|
|
414
|
+
3 tests · 1 failed · 1 flaky · 1 retried
|
|
415
|
+
|
|
416
|
+
TRUE-FLAKE completes checkout with saved card (e2e/checkout.spec.ts)
|
|
417
|
+
████████████████████ 6.0s · 2 attempts
|
|
418
|
+
FAILING declines an expired card (e2e/checkout.spec.ts)
|
|
419
|
+
████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Un test qui ne passe qu'à partir de la tentative ≥ 2 n'est pas un test
|
|
423
|
+
qui passe — c'est un test chanceux. Il est marqué `TRUE-FLAKE` quel que
|
|
424
|
+
soit le vert final de la coche.
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## ⚡ Mjölnir n'est pas un linter de plus
|
|
429
|
+
|
|
430
|
+
Les linters vous disent si le code suit des règles. Mjölnir vous dit si
|
|
431
|
+
votre vérification peut être crue.
|
|
432
|
+
|
|
433
|
+
| | ESLint / SonarQube | Outils de coverage | Revue manuelle | **Mjölnir** |
|
|
434
|
+
| ------------------------------------------------------------- | :----------------: | :----------------: | :------------: | :---------: |
|
|
435
|
+
| Intégrité des workflows CI (`continue-on-error`, `\|\| true`) | ❌ | ❌ | rarement | ✅ |
|
|
436
|
+
| Multi-langage (TS, Python, Java, C#) depuis un seul outil | ❌ | ❌ | ❌ | ✅ |
|
|
437
|
+
| Note la résilience des locators Playwright (Selector Health) | ❌ | ❌ | rarement | ✅ |
|
|
438
|
+
| Signale les tests sans vraies assertions | ✅ (plugin)\* | ❌ | parfois | ✅ |
|
|
439
|
+
| Détecte les sleeps en dur (`waitForTimeout`, `time.sleep`) | ✅ (plugin)\* | ❌ | parfois | ✅ |
|
|
440
|
+
| Tourne en secondes, zéro appel réseau pendant le scan | ✅ | ✅ | — | ✅ |
|
|
441
|
+
|
|
442
|
+
\*`eslint-plugin-jest` (`expect-expect`) et `eslint-plugin-playwright`
|
|
443
|
+
(`expect-expect`, `no-wait-for-timeout`) couvrent cela pour leurs
|
|
444
|
+
frameworks respectifs.
|
|
445
|
+
|
|
446
|
+
**L'analyse d'exécution** est une catégorie à part du linting
|
|
447
|
+
statique :
|
|
448
|
+
|
|
449
|
+
| | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
|
|
450
|
+
| ----------------------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
|
|
451
|
+
| Lit de vraies données de run pour des verdicts `TRUE-FLAKE` | partiel\* | partiel (tag) | ✅ |
|
|
452
|
+
| Rapport de triage d'instabilité depuis l'historique | ❌ | ✅ | ✅ |
|
|
453
|
+
| S'intègre au score de fiabilité statique | ❌ | ❌ | ✅ |
|
|
454
|
+
|
|
455
|
+
\*Playwright suit les retries en interne mais ne produit pas de rapport
|
|
456
|
+
d'instabilité autonome avec des étiquettes de verdict.
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## 🤖 Pourquoi pas simplement une revue de code par IA ?
|
|
461
|
+
|
|
462
|
+
Un problème différent, une autre couche. Une revue IA peut repérer un
|
|
463
|
+
changement de test suspect dans un diff ; elle ne prouve pas que le
|
|
464
|
+
système de vérification dans son ensemble est digne de confiance — et
|
|
465
|
+
elle ne voit que le diff que vous lui montrez.
|
|
466
|
+
|
|
467
|
+
| | Revue de code IA (Copilot, etc.) | **Mjölnir** |
|
|
468
|
+
| ------------------------------------------- | :------------------------------------: | :-------------------------------: |
|
|
469
|
+
| Coût par scan | Tokens (évolue avec la taille du diff) | **Zéro** (local, installé) |
|
|
470
|
+
| Voit toute la suite + toutes les configs CI | Seulement le diff de PR montré | **Tout, à chaque fois** |
|
|
471
|
+
| Déterministe (même entrée → même sortie) | ❌ (non déterministe) | **✅** |
|
|
472
|
+
| Détecte des motifs dormants depuis des mois | Seulement s'il est dans le contexte | **✅** (scanne tous les fichiers) |
|
|
473
|
+
| Se souvient des constats entre les runs | ❌ (aucune mémoire entre sessions) | **✅** (baseline + diff) |
|
|
474
|
+
| Tourne sans déclencheur humain | Nécessite une PR ou un prompt | **✅** (hook CI, 3 secondes) |
|
|
475
|
+
|
|
476
|
+
**Utilisez les deux.** L'IA attrape la nuance, l'intention et les
|
|
477
|
+
défauts de conception qu'aucune regex ne trouve. Mjölnir attrape les
|
|
478
|
+
motifs structurels que l'IA néglige parce qu'ils semblent
|
|
479
|
+
« intentionnels » — un `.only` commité, un code de sortie avalé, un
|
|
480
|
+
`continue-on-error` sur un job de test. Ce ne sont pas des bugs qui
|
|
481
|
+
demandent du raisonnement ; ce sont des faits qui demandent un scan.
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## 🤖 Intégration CI
|
|
486
|
+
|
|
487
|
+
Une commande génère un workflow de PR — consultatif par défaut, jamais
|
|
488
|
+
bloquant :
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
mjolnir ci install
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
Ou branchez-le nativement dans GitHub Code Scanning via SARIF :
|
|
495
|
+
|
|
496
|
+
```yaml
|
|
497
|
+
- run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
|
|
498
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
499
|
+
with:
|
|
500
|
+
sarif_file: mjolnir.sarif
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
Configuration éditeur et pipeline pour SARIF :
|
|
504
|
+
[docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
|
|
505
|
+
|
|
506
|
+
### Couverture du périmètre modifié
|
|
507
|
+
|
|
508
|
+
`--scope changed` attribue les constats aux lignes ajoutées dans votre
|
|
509
|
+
branche par rapport à la base de fusion avec `main`. Il couvre les
|
|
510
|
+
fichiers de tests (`*.spec.*`, `*.test.*`) plus les fichiers de
|
|
511
|
+
workflow GitHub et les configurations Playwright du diff. Quand la base
|
|
512
|
+
de fusion ne peut pas être résolue — clone superficiel, HEAD détaché,
|
|
513
|
+
cible non-git, branche par défaut différente — il dégrade honnêtement :
|
|
514
|
+
les constats retombent sur une attribution au fichier entier, et le
|
|
515
|
+
rapport le dit. Remplacez la ref de base avec `--base <ref>`.
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
## Configuration
|
|
520
|
+
|
|
521
|
+
Mjölnir est zéro-config. Un `mjolnir.config.json` optionnel (ou
|
|
522
|
+
`.mjolnir.json`) à la racine du dépôt ajuste sévérité, gating et
|
|
523
|
+
périmètre — il ne change jamais la sémantique de détection.
|
|
524
|
+
|
|
525
|
+
| Clé | Type | Effet |
|
|
526
|
+
| ------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
527
|
+
| `exclude` | `string[]` | Globs d'ignore supplémentaires (sous-ensemble gitignore), au-dessus des valeurs par défaut intégrées |
|
|
528
|
+
| `gate` | `"advisory" \| "error" \| "warning"` | Quelles sévérités sortent avec un code non nul (défaut `error` ; `advisory` ne bloque jamais) |
|
|
529
|
+
| `severityOverrides` | `{ "<RULE-ID>": severity }` | Re-classe les constats d'une règle pour votre dépôt |
|
|
530
|
+
| `ignore` | `IgnoreEntry[]` | Supprime des constats — **`reason` est obligatoire** ; les entrées expirent après 90 jours (une date `expires` explicite, ou la date de dernière modification du fichier de config pour les entrées sans) |
|
|
531
|
+
| `plugins` | `string[]` | Paquets de règles tiers (voir [Modèle de confiance](#modèle-de-confiance)) |
|
|
532
|
+
|
|
533
|
+
```json
|
|
534
|
+
{
|
|
535
|
+
"gate": "error",
|
|
536
|
+
"exclude": ["legacy/**"],
|
|
537
|
+
"severityOverrides": { "QA-PW-118": "warning" },
|
|
538
|
+
"ignore": [
|
|
539
|
+
{
|
|
540
|
+
"ruleId": "QA-TEST-004",
|
|
541
|
+
"files": ["e2e/legacy-login.spec.ts"],
|
|
542
|
+
"reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
|
|
543
|
+
"expires": "2026-12-31"
|
|
544
|
+
}
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
- **`.mjolnirignore`** — un fichier simple façon gitignore pour les
|
|
550
|
+
exclusions de chemins, même dialecte que `exclude`. Utilisez-le pour
|
|
551
|
+
le bruit propre à la machine ; utilisez `exclude` quand la liste
|
|
552
|
+
appartient au contrôle de version, à côté du reste de la config.
|
|
553
|
+
- **Surclassements CLI** — `--strict` (inclure les règles en
|
|
554
|
+
quarantaine), `--width <cols>` et `--ascii` / `--no-ascii` (rendu
|
|
555
|
+
terminal), `--tone blunt` (messages plus directs),
|
|
556
|
+
`--max-duration <sec>` (scan partiel borné).
|
|
557
|
+
- Suppression de règles et cycle de vie de dépréciation :
|
|
558
|
+
[docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md).
|
|
559
|
+
|
|
560
|
+
Les entrées `ignore` alimentent aussi la commande autonome
|
|
561
|
+
`mjolnir suppressions`, qui liste ce qui est actuellement supprimé et
|
|
562
|
+
quand chaque entrée expire.
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
## 📐 Codes de sortie & contrats
|
|
567
|
+
|
|
568
|
+
Figés — sûrs pour construire de la logique CI dessus :
|
|
569
|
+
|
|
570
|
+
| Code de sortie | Signification |
|
|
571
|
+
| -------------- | ------------------------------------------------------------------------------ |
|
|
572
|
+
| `0` | Propre — aucun constat au-dessus ou à l'égal du gate |
|
|
573
|
+
| `1` | Constats au-dessus ou à l'égal du gate |
|
|
574
|
+
| `2` | Scan partiel (budget de temps atteint, fichiers illisibles) — ne bloque jamais |
|
|
575
|
+
| `10` | Erreur d'usage (mauvais flag, cible manquante) |
|
|
576
|
+
| `20` | Erreur interne |
|
|
577
|
+
|
|
578
|
+
Le rapport JSON/SARIF est `schemaVersion: 1`. Les ID de règles
|
|
579
|
+
(`QA-<FAMILY>-NNN`) sont immuables une fois livrés et jamais
|
|
580
|
+
réutilisés.
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## Modèle de confiance
|
|
585
|
+
|
|
586
|
+
- **Local-first** — zéro appel réseau pendant le scan. Jamais. Zéro
|
|
587
|
+
télémétrie.
|
|
588
|
+
- **Pas de fausse preuve** — nous préférons dire « inconnu » que
|
|
589
|
+
« vérifié ». Un dépôt vide obtient `score: null`, jamais un faux 100.
|
|
590
|
+
- **Honnêteté partielle** — si l'analyse a été interrompue, la sortie le
|
|
591
|
+
dit. Jamais « complete » quand ça ne l'est pas.
|
|
592
|
+
- **Pare-feu FP** — la détection tourne sur une vue du code sans
|
|
593
|
+
commentaires ni chaînes (les règles TypeScript utilisent l'AST du
|
|
594
|
+
compilateur) : un motif dans un commentaire de prose ou une chaîne
|
|
595
|
+
d'exemple de doc est de la documentation, pas un constat.
|
|
596
|
+
- **Mesuré, pas affirmé** — seules les règles avec un taux de faux
|
|
597
|
+
positifs issu de vrai code OSS sortent dans les tiers vedettes (voir
|
|
598
|
+
[Quelle part est mesurée](#quelle-part-est-mesurée)) ; le pied de scan
|
|
599
|
+
et `mjolnir rules --unmeasured` vous disent lesquels.
|
|
600
|
+
- **Confiance plugins** — les plugins sont des paquets npm déclarés
|
|
601
|
+
sous `"plugins"`. Il n'y a **pas de sandbox** : le code d'un plugin
|
|
602
|
+
tourne avec tous les privilèges Node, le même modèle de confiance que
|
|
603
|
+
les plugins ESLint ou Vitest. Les préfixes d'ID de règles core sont
|
|
604
|
+
réservés et refusés aux plugins pour empêcher l'usurpation.
|
|
605
|
+
- **Règles externes locales au workspace** (par dossier, zéro réseau) —
|
|
606
|
+
un répertoire `mjolnir-rules/` à côté de la cible du scan charge des
|
|
607
|
+
règles personnalisées : des fichiers JSON déclarent des motifs regex
|
|
608
|
+
(aucun code exécuté), les modules `.mjs`/`.js` exportent `rules`
|
|
609
|
+
(confiance Node complète, comme les plugins). Les règles externes
|
|
610
|
+
portent les mêmes métadonnées de confiance que core ; elles ne
|
|
611
|
+
peuvent jamais sortir dans le tier core (core exige un taux de FP
|
|
612
|
+
mesuré depuis le sidecar corpus — un `tier: "core"` déclaré est
|
|
613
|
+
borné à `extended`), obéissent aux caps de tier et sont vérifiées
|
|
614
|
+
contre la dérive : `mjolnir rules --md --external` rend le catalogue
|
|
615
|
+
depuis les fichiers chargés (provenance `external`), et le générateur
|
|
616
|
+
de matrice accepte `--external <root>`.
|
|
617
|
+
|
|
618
|
+
---
|
|
619
|
+
|
|
620
|
+
## 🏗️ Architecture
|
|
621
|
+
|
|
622
|
+
<details>
|
|
623
|
+
<summary>Déplier l'arbre</summary>
|
|
624
|
+
|
|
625
|
+
```
|
|
626
|
+
mjolnir/
|
|
627
|
+
├── src/
|
|
628
|
+
│ ├── engine/ # LanguageAdapter interface + rule runner
|
|
629
|
+
│ ├── adapters/ # typescript · python · java · csharp · github-actions
|
|
630
|
+
│ ├── rules/ # rules across 8 families + the measured-FP table
|
|
631
|
+
│ ├── playwright/ # Selector Health Score engine
|
|
632
|
+
│ ├── discovery/ # workspace, frameworks, ignore resolution
|
|
633
|
+
│ ├── scope/ # git merge-base changed-scope engine
|
|
634
|
+
│ ├── scorer/ # transparent deduction table + prioritization
|
|
635
|
+
│ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
|
|
636
|
+
│ ├── forensics/ # run-data ingestion · flake verdicts · triage
|
|
637
|
+
│ ├── config/ # mjolnir.config.json + suppressions
|
|
638
|
+
│ ├── plugins/ # third-party rule loading (no sandbox)
|
|
639
|
+
│ └── commands/ # every subcommand
|
|
640
|
+
└── tests/
|
|
641
|
+
├── fixtures/ # must-fire / must-not-fire per rule
|
|
642
|
+
└── golden/ # frozen score regression locks
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
</details>
|
|
646
|
+
|
|
647
|
+
- **Les règles sont des fonctions pures** —
|
|
648
|
+
`(SourceFileContext) → Finding[]`, pas d'I/O, pas de globales. Ajouter
|
|
649
|
+
un écosystème = un adaptateur + ses règles.
|
|
650
|
+
- **TypeScript/Playwright utilise l'AST du compilateur** (ts-morph).
|
|
651
|
+
Python, Java et C# tournent sur une couche regex partagée avec
|
|
652
|
+
commentaires/chaînes masqués.
|
|
653
|
+
- Une couche AST tree-sitter WASM pour Java et C# existe et constitue
|
|
654
|
+
la prochaine étape de précision — elle n'est pas encore câblée dans
|
|
655
|
+
le pipeline de scan synchrone.
|
|
656
|
+
|
|
657
|
+
---
|
|
658
|
+
|
|
659
|
+
## 📚 Documentation
|
|
660
|
+
|
|
661
|
+
| Document | Contenu |
|
|
662
|
+
| ------------------------------------------------------ | -------------------------------------------------- |
|
|
663
|
+
| [docs/SCORING.md](docs/SCORING.md) | Normalisation du score + pondération par la preuve |
|
|
664
|
+
| [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Taux de faux positifs mesurés + méthode |
|
|
665
|
+
| [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | États des règles, suppression, dépréciation |
|
|
666
|
+
| [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | Sortie SARIF + configuration éditeur/CI |
|
|
667
|
+
| [docs/rules/](docs/rules/) | Catalogue généré par règle |
|
|
668
|
+
| [CONTRIBUTING.md](CONTRIBUTING.md) | Setup dev + workflow de contribution |
|
|
669
|
+
| [CHANGELOG.md](CHANGELOG.md) | Historique des versions |
|
|
670
|
+
| [SECURITY.md](SECURITY.md) | Signalement de vulnérabilités |
|
|
671
|
+
|
|
672
|
+
---
|
|
673
|
+
|
|
674
|
+
## 📈 Statut
|
|
675
|
+
|
|
676
|
+
**v0.5.x · bêta ouverte.** Le schéma JSON et les codes de sortie sont
|
|
677
|
+
des contrats figés. TypeScript et Python ont la couverture mesurée la
|
|
678
|
+
plus large ; Java et C# sont plus récents — lisez-les via la
|
|
679
|
+
[table des tiers](#tiers-de-règles-et-maturité-par-langage).
|
|
680
|
+
|
|
681
|
+
---
|
|
682
|
+
|
|
683
|
+
## 🤝 Contribuer
|
|
684
|
+
|
|
685
|
+
Les nouvelles règles sont la première contribution la plus simple —
|
|
686
|
+
une commande scaffolde la règle plus ses fixtures must-fire **et**
|
|
687
|
+
must-not-fire (la règle générée échoue volontairement à ses fixtures
|
|
688
|
+
tant que vous n'implémentez pas une vraie détection — un stub ne peut
|
|
689
|
+
pas sortir) :
|
|
690
|
+
|
|
691
|
+
```bash
|
|
692
|
+
mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
Le setup dev complet, les commandes de la barrière permanente et les
|
|
696
|
+
lois anti-dérive / pare-feu à fixtures sont dans
|
|
697
|
+
[CONTRIBUTING.md](CONTRIBUTING.md).
|
|
698
|
+
|
|
699
|
+
---
|
|
700
|
+
|
|
701
|
+
<div align="center">
|
|
702
|
+
|
|
703
|
+
**Ne livrez plus des tests auxquels vous ne pouvez pas faire
|
|
704
|
+
confiance.**
|
|
705
|
+
|
|
706
|
+
```bash
|
|
707
|
+
npx mjolnir-qa@latest
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
**Star ⭐ · Watch 👀 · Contribute 🤝**
|
|
711
|
+
|
|
712
|
+
Construit par [Sergey Bar](https://www.linkedin.com/in/sergeybar/)
|
|
713
|
+
|
|
714
|
+
</div>
|