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.it.md
ADDED
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/readme/logo.png" alt="Mjölnir — Verification Trust Engine" width="800" />
|
|
4
|
+
|
|
5
|
+
### I tuoi test ti mentono. Noi lo dimostriamo.
|
|
6
|
+
|
|
7
|
+
**Verification Trust Engine per la QA.** Mjölnir audita le suite di test
|
|
8
|
+
e le pipeline CI, riporta un punteggio di idoneità e mostra esattamente
|
|
9
|
+
dove la fiducia si rompe.
|
|
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](README.fr.md) | Italiano | [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
|
+
**I tuoi test sono degni di fiducia?**
|
|
25
|
+
|
|
26
|
+
[Guardalo in azione](#-guardalo-in-azione) ·
|
|
27
|
+
[Avvio rapido](#-avvio-rapido) ·
|
|
28
|
+
[Cosa controlla](#-cosa-controlla-mjölnir) ·
|
|
29
|
+
[Punteggio](#come-funziona-il-punteggio) ·
|
|
30
|
+
[CI](#-integrazione-ci) · [Configurazione](#configurazione) ·
|
|
31
|
+
[Documentazione](#-documentazione)
|
|
32
|
+
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 🎬 Guardalo in azione
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<img src="assets/readme/demo.svg" alt="Il report --verbose completo di Mjölnir su un repo demo: WORTHINESS 75/100 NEEDS WORK, un dettaglio delle diagnosti per categoria, una lista FIX THIS FIRST e ogni riscontro con ID di regola e numero di riga attraverso CI, Playwright, igiene dei test e regole Python" width="900" />
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
<sub>L'output completo di `npx mjolnir-qa ./examples/demo-repo --verbose`,
|
|
44
|
+
renderizzato dal reporter vero — nulla di tagliato. Rigenerato con
|
|
45
|
+
`npm run docs:demo`;
|
|
46
|
+
[`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
|
|
47
|
+
fa fallire la CI se deriva da ciò che lo strumento stampa.</sub>
|
|
48
|
+
|
|
49
|
+
**Cosa è appena successo:**
|
|
50
|
+
|
|
51
|
+
1. Mjölnir ha individuato le spec Playwright, la sua configurazione, il
|
|
52
|
+
workflow CI e un file di test Python — quattro linguaggi/formati, un
|
|
53
|
+
solo passaggio.
|
|
54
|
+
2. Ha trovato evidenze che indeboliscono la fiducia nella suite — un
|
|
55
|
+
`continue-on-error` che maschera un job, un `|| true` che ingoia un
|
|
56
|
+
exit code, sleep hardcoded, un selettore fragile, URL di staging
|
|
57
|
+
hardcoded, un'attesa `networkidle`.
|
|
58
|
+
3. Di ciascuna ha fatto un riscontro concreto con ID di regola,
|
|
59
|
+
posizione e fix — e un unico punteggio su cui fare gate di una PR.
|
|
60
|
+
|
|
61
|
+
### Un riscontro da vicino
|
|
62
|
+
|
|
63
|
+
Esegui `mjolnir explain QA-CI-001` sul primo riscontro qui sopra e
|
|
64
|
+
ottieni:
|
|
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
|
+
Questa è l'unità di valore: non una svista di stile, ma un punto in cui
|
|
86
|
+
la tua CI ti dice che qualcosa è passato quando non è passato.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## ⚡ Avvio rapido
|
|
91
|
+
|
|
92
|
+
Eseguilo su un repo per un report completo e un punteggio di idoneità:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx mjolnir-qa@latest
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**In CI il prodotto è un solo comando.** Scansiona solo ciò che il
|
|
99
|
+
branch ha toccato ed esce con un codice diverso da zero su problemi
|
|
100
|
+
nuovi:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npx mjolnir-qa@latest --scope changed
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Metti quello in un check di PR — `mjolnir ci install` scrive il
|
|
107
|
+
workflow — e hai finito. Tutto il resto è opzionale.
|
|
108
|
+
|
|
109
|
+
| Comando | Cosa fa |
|
|
110
|
+
| ----------------------------------- | ---------------------------------------------------------------- |
|
|
111
|
+
| `mjolnir` | Scansione completa del repo + punteggio di idoneità |
|
|
112
|
+
| `mjolnir --scope changed` | Solo ciò che il tuo branch ha introdotto — la forma CI |
|
|
113
|
+
| `mjolnir ci install` | Genera il workflow di PR consultivo |
|
|
114
|
+
| `mjolnir explain QA-CI-001` | Cosa / perché / fix + tasso di FP misurato di una regola |
|
|
115
|
+
| `mjolnir rules --unmeasured` | Le regole che girano per assunzione, non per misurazione |
|
|
116
|
+
| `mjolnir --json` / `--format sarif` | Leggibile da macchina / GitHub Code Scanning |
|
|
117
|
+
| `mjolnir --strict` | Esegue anche le regole del tier quarantena (rischio FP più alto) |
|
|
118
|
+
|
|
119
|
+
<details>
|
|
120
|
+
<summary><strong>Quando qualcosa è instabile</strong></summary>
|
|
121
|
+
|
|
122
|
+
| Comando | Cosa fa |
|
|
123
|
+
| ----------------------------------- | ---------------------------------------------------------- |
|
|
124
|
+
| `mjolnir forensics ./test-results/` | Dati di run reali → verdetti `TRUE-FLAKE`, `FLAKY.md` |
|
|
125
|
+
| `mjolnir triage ./test-results/` | Proposta di quarantena dalla cronologia di esecuzione |
|
|
126
|
+
| `mjolnir pw-report ./test-results/` | Riepilogo di run Playwright — retry / flake / più lenti |
|
|
127
|
+
| `mjolnir doctor:playwright` | Scansione profonda solo Playwright + Selector Health Score |
|
|
128
|
+
|
|
129
|
+
</details>
|
|
130
|
+
|
|
131
|
+
<details>
|
|
132
|
+
<summary><strong>Occasionale / report</strong></summary>
|
|
133
|
+
|
|
134
|
+
| Comando | Cosa fa |
|
|
135
|
+
| ------------------------------- | ---------------------------------------------------------- |
|
|
136
|
+
| `mjolnir fix --dry-run` / `fix` | Auto-fix sicuri con prova |
|
|
137
|
+
| `mjolnir baseline` / `diff` | Snapshot dei riscontri, poi riporta solo nuovi/peggiorati |
|
|
138
|
+
| `mjolnir impact --since <ref>` | Cosa è cambiato da un commit precedente |
|
|
139
|
+
| `mjolnir debt` | Registro del debito di test con un modello di costo |
|
|
140
|
+
| `mjolnir handover` | Mappa di onboarding della suite per un nuovo QA |
|
|
141
|
+
| `mjolnir stats` | Contatori locali storici dei fix visti |
|
|
142
|
+
| `mjolnir badge` | JSON endpoint di shields.io + snippet |
|
|
143
|
+
| `mjolnir rules --md` | Catalogo completo delle regole (JSON o Markdown) |
|
|
144
|
+
| `mjolnir doctor` | Auto-audit della stessa base di regole di Mjölnir |
|
|
145
|
+
| `mjolnir create-rule <ID>` | Imposta lo scheletro di una nuova regola + fixture |
|
|
146
|
+
| `mjolnir --format mermaid` | Diagramma dell'architettura dei test per un commento di PR |
|
|
147
|
+
|
|
148
|
+
</details>
|
|
149
|
+
|
|
150
|
+
Installalo globalmente invece che con `npx` se preferisci:
|
|
151
|
+
`npm i -g mjolnir-qa`. Richiede Node.js ≥ 22.18. Funziona su Windows,
|
|
152
|
+
macOS e Linux.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 👥 Per chi è?
|
|
157
|
+
|
|
158
|
+
- **QA / SDET** che possiedono una suite e2e o di integrazione e
|
|
159
|
+
hanno bisogno di evidenze che la suite meriti davvero la spunta
|
|
160
|
+
verde che produce.
|
|
161
|
+
- **Team Piattaforma / DevEx** responsabili dell'integrità CI e dei
|
|
162
|
+
release gate — le persone per cui un `continue-on-error` non deve mai
|
|
163
|
+
trasformare in silenzio una pipeline rossa in verde.
|
|
164
|
+
- **Maintainer OSS** che vogliono un gate di verifica economico,
|
|
165
|
+
sempre attivo, che gira in locale e in CI senza chiamate di rete.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 🔨 Cosa controlla Mjölnir
|
|
170
|
+
|
|
171
|
+
| | |
|
|
172
|
+
| --- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
173
|
+
| ⚖️ | **Punteggio di idoneità** — un numero, tabella di deduzioni trasparente, nessuna black box |
|
|
174
|
+
| 🎭 | **Selector Health Score** — valuta i tuoi locator Playwright, non solo il pass rate |
|
|
175
|
+
| 🔬 | **Forensica di runtime** — legge dati di run reali Playwright/JUnit per agganciare `TRUE-FLAKE`, non solo ipotesi statiche |
|
|
176
|
+
| 🚨 | **Regole di integrità CI** — becca `continue-on-error`, `\|\| true` e altri trucchi da falso verde |
|
|
177
|
+
| 🐍 | **Tutti e quattro i binding Playwright** — TypeScript, Python, Java, C#/.NET — più pytest, JUnit/TestNG e workflow CI |
|
|
178
|
+
| 🔒 | **Local-first** — zero chiamate di rete durante la scansione, zero telemetria, gira in secondi |
|
|
179
|
+
|
|
180
|
+
### Le regole
|
|
181
|
+
|
|
182
|
+
Ogni regola arriva con fixture must-fire **e** must-not-fire. Una
|
|
183
|
+
regola che scatta sulla propria fixture negativa non può essere
|
|
184
|
+
pubblicata — quello è il firewall dei falsi positivi.
|
|
185
|
+
|
|
186
|
+
<details>
|
|
187
|
+
<summary><strong>Igiene dei test</strong></summary>
|
|
188
|
+
|
|
189
|
+
| ID | Regola | Severity |
|
|
190
|
+
| ----------- | -------------------------------------------------------- | -------- |
|
|
191
|
+
| QA-TEST-001 | Test focalizzato committato (`.only`, `fit`) | error |
|
|
192
|
+
| QA-TEST-002 | Test saltato senza giustificazione | error |
|
|
193
|
+
| QA-TEST-002 | Test saltato con giustificazione tracciata | warning |
|
|
194
|
+
| QA-TEST-003 | Test senza asserzioni | error |
|
|
195
|
+
| QA-TEST-004 | Sleep hardcoded (`waitForTimeout`, `sleep()`, `delay()`) | warning |
|
|
196
|
+
| QA-TEST-006 | Abuso di retry che nasconde l'instabilità | warning |
|
|
197
|
+
| QA-TEST-010 | Corpo del test vuoto | error |
|
|
198
|
+
|
|
199
|
+
</details>
|
|
200
|
+
|
|
201
|
+
<details>
|
|
202
|
+
<summary><strong>Qualità dei test</strong></summary>
|
|
203
|
+
|
|
204
|
+
| ID | Regla | Severity |
|
|
205
|
+
| ------------ | --------------------------------- | -------- |
|
|
206
|
+
| QA-TQUAL-001 | Verifica solo con mock | info |
|
|
207
|
+
| QA-TQUAL-002 | Asserzione tautologica | error |
|
|
208
|
+
| QA-TQUAL-009 | Asserzione di promise senza await | error |
|
|
209
|
+
| QA-TQUAL-011 | Test commentati | warning |
|
|
210
|
+
|
|
211
|
+
</details>
|
|
212
|
+
|
|
213
|
+
<details>
|
|
214
|
+
<summary><strong>Playwright 🎭</strong></summary>
|
|
215
|
+
|
|
216
|
+
| ID | Regla | Severity |
|
|
217
|
+
| --------- | --------------------------------------------- | -------- |
|
|
218
|
+
| QA-PW-002 | Asserzione di locator senza await | error |
|
|
219
|
+
| QA-PW-003 | `page.pause()` / `test.only()` committati | error |
|
|
220
|
+
| QA-PW-004 | Selettori CSS/XPath fragili | warning |
|
|
221
|
+
| QA-PW-005 | Logica di business dentro `page.evaluate()` | info |
|
|
222
|
+
| QA-PW-114 | Element handle legacy (`page.$`) | info |
|
|
223
|
+
| QA-PW-118 | Attese `networkidle` (instabili per progetto) | info |
|
|
224
|
+
| QA-PW-123 | URL di ambiente hardcoded | warning |
|
|
225
|
+
|
|
226
|
+
</details>
|
|
227
|
+
|
|
228
|
+
<details>
|
|
229
|
+
<summary><strong>Integrità CI</strong></summary>
|
|
230
|
+
|
|
231
|
+
| ID | Regla | Severity |
|
|
232
|
+
| --------- | ------------------------------------------------------------------ | -------- |
|
|
233
|
+
| QA-CI-001 | `continue-on-error` maschera i fallimenti | error |
|
|
234
|
+
| QA-CI-002 | `\|\| true` ingoia gli exit code | error |
|
|
235
|
+
| QA-CI-005 | Report consumato ma mai generato | error |
|
|
236
|
+
| QA-CI-007 | Wrapper di retry attorno ai test | warning |
|
|
237
|
+
| QA-CI-008 | Step sempre riuscito maschera i fallimenti | error |
|
|
238
|
+
| QA-CI-009 | Exit code del test non propagato (`\|` senza pipefail, catene `;`) | error |
|
|
239
|
+
| QA-CI-010 | Test saltati dove devono bloccare (guardie skip-on-PR) | error |
|
|
240
|
+
|
|
241
|
+
</details>
|
|
242
|
+
|
|
243
|
+
<details>
|
|
244
|
+
<summary><strong>Python / pytest 🐍</strong></summary>
|
|
245
|
+
|
|
246
|
+
| ID | Regla | Severity |
|
|
247
|
+
| --------- | ------------------------------------------ | -------- |
|
|
248
|
+
| QA-PY-002 | Test saltato (`skip`, `xfail` non strict) | warning |
|
|
249
|
+
| QA-PY-003 | Funzione di test senza asserzioni | error |
|
|
250
|
+
| QA-PY-005 | `time.sleep()` nei test | warning |
|
|
251
|
+
| QA-PY-006 | Corpo del test vuoto (`pass`) | info |
|
|
252
|
+
| QA-PY-010 | Dipendenza da casualità/tempo senza freeze | info |
|
|
253
|
+
| QA-PY-012 | Asserzione tautologica | error |
|
|
254
|
+
|
|
255
|
+
20 regole Python in totale (QA-PY-001…012 igiene pytest + QA-PY-101…108 Playwright-Python).
|
|
256
|
+
|
|
257
|
+
</details>
|
|
258
|
+
|
|
259
|
+
<details>
|
|
260
|
+
<summary><strong>Java / JUnit · TestNG ☕</strong></summary>
|
|
261
|
+
|
|
262
|
+
| ID | Regla | Severity |
|
|
263
|
+
| --------- | --------------------------------------------- | -------- |
|
|
264
|
+
| QA-JV-101 | Test disabilitato (`@Disabled`) | warning |
|
|
265
|
+
| QA-JV-102 | Sleep hardcoded (`Thread.sleep()`) | warning |
|
|
266
|
+
| QA-JV-103 | Metodo di test senza asserzioni | error |
|
|
267
|
+
| QA-JV-105 | Sleep hardcoded Playwright `waitForTimeout()` | warning |
|
|
268
|
+
| QA-JV-106 | Selettore fragile invece di un role locator | warning |
|
|
269
|
+
| QA-JV-108 | URL di ambiente hardcoded nel test | info |
|
|
270
|
+
| QA-JV-111 | Mock blanket `page.route("**")` | info |
|
|
271
|
+
|
|
272
|
+
</details>
|
|
273
|
+
|
|
274
|
+
<details>
|
|
275
|
+
<summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
|
|
276
|
+
|
|
277
|
+
| ID | Regla | Severity |
|
|
278
|
+
| --------- | ----------------------------------------------- | -------- |
|
|
279
|
+
| QA-CS-101 | Test saltato (`[Ignore]`, `[Fact(Skip=)]`) | warning |
|
|
280
|
+
| QA-CS-102 | Sleep hardcoded (`Thread.Sleep` / `Task.Delay`) | warning |
|
|
281
|
+
| QA-CS-103 | Metodo di test senza asserzioni | error |
|
|
282
|
+
| QA-CS-105 | Sleep hardcoded `WaitForTimeoutAsync()` | warning |
|
|
283
|
+
| QA-CS-106 | Selettore fragile invece di un role locator | warning |
|
|
284
|
+
| QA-CS-108 | URL di ambiente hardcoded nel test | info |
|
|
285
|
+
| QA-CS-111 | Mock blanket `page.RouteAsync("**")` | info |
|
|
286
|
+
|
|
287
|
+
</details>
|
|
288
|
+
|
|
289
|
+
> Il catalogo live completo — ogni regola con tier, confidence, rischio
|
|
290
|
+
> di falso positivo e disponibilità di autofix — è generato dal
|
|
291
|
+
> registro:
|
|
292
|
+
>
|
|
293
|
+
> ```bash
|
|
294
|
+
> mjolnir rules --md
|
|
295
|
+
> ```
|
|
296
|
+
>
|
|
297
|
+
> Le pagine per regola vivono in [`docs/rules/`](docs/rules/).
|
|
298
|
+
|
|
299
|
+
### Quanto è misurato
|
|
300
|
+
|
|
301
|
+
**74 regole su 99 portano un tasso di falsi positivi misurato su vero
|
|
302
|
+
codice OSS** (≥ 10 riscontri classificati a mano ciascuna; vedi
|
|
303
|
+
[docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Le altre 19 escono sulla stima
|
|
304
|
+
dell'autore. Ogni footer di scansione dice quante delle regole
|
|
305
|
+
_scattate_ sono misurate; `mjolnir rules --unmeasured` elenca quelle
|
|
306
|
+
che non lo sono; la pagina `mjolnir explain` di ogni regola dichiara il
|
|
307
|
+
suo stato. Pubblichiamo il tasso anche quando è brutto — QA-CS-103 si
|
|
308
|
+
audita al 95 % ed è in quarantena per questo. Far crescere quel 78 è il
|
|
309
|
+
lavoro continuo del progetto.
|
|
310
|
+
|
|
311
|
+
### Tier delle regole e maturità per linguaggio
|
|
312
|
+
|
|
313
|
+
Ogni regola è `core`, `extended` o `quarantine`, assegnato in base al
|
|
314
|
+
suo tasso di falsi positivi **misurato**:
|
|
315
|
+
|
|
316
|
+
| Tier | Significato | Scansione predefinita | `--strict` |
|
|
317
|
+
| ------------ | --------------------------------------------- | :-------------------: | :--------: |
|
|
318
|
+
| `core` | ≤ 10 % di FP misurato | ✅ | ✅ |
|
|
319
|
+
| `extended` | ≤ 30 % di FP misurato | ✅ | ✅ |
|
|
320
|
+
| `quarantine` | sopra il 30 %, o non ancora misurato (n < 10) | ❌ | ✅ |
|
|
321
|
+
|
|
322
|
+
| Linguaggio | Adattatore | Copertura oggi |
|
|
323
|
+
| --------------- | ------------------- | --------------------------------------------------------- |
|
|
324
|
+
| TypeScript / JS | AST del compilatore | la più ampia e misurata — soprattutto `core`/`extended` |
|
|
325
|
+
| Python / pytest | Livello regex | ampia, auditata su corpus — soprattutto `core`/`extended` |
|
|
326
|
+
| Java | Livello regex | più recente — soprattutto `extended`/`quarantine` |
|
|
327
|
+
| C# / .NET | Livello regex | più recente — soprattutto `extended`/`quarantine` |
|
|
328
|
+
|
|
329
|
+
TypeScript e Python hanno la copertura misurata più ampia. Java e C#
|
|
330
|
+
sono pubblicati, documentati, e restano fuori dal numero di testa fino
|
|
331
|
+
a quando una vera suite consumatrice (non i test della stessa
|
|
332
|
+
libreria di binding) non sarà stata auditata.
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Come funziona il punteggio
|
|
337
|
+
|
|
338
|
+
<p align="center">
|
|
339
|
+
<img src="assets/readme/terminal-hero.svg" alt="Output terminale di Mjölnir — WORTHINESS 75/100 NEEDS WORK, un dettaglio delle diagnosti per categoria e una lista FIX THIS FIRST" width="820" />
|
|
340
|
+
</p>
|
|
341
|
+
|
|
342
|
+
<sub>Rigenerato con `npm run docs:hero`;
|
|
343
|
+
[`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
|
|
344
|
+
fa fallire la CI se deriva da ciò che il reporter stampa davvero.</sub>
|
|
345
|
+
|
|
346
|
+
Il punteggio è trasparente: **error −8, warning −3, info −1**, poi
|
|
347
|
+
normalizzato per l'esposizione della suite (deduzioni per dichiarazione
|
|
348
|
+
di test). Le deduzioni ponderate per evidenza significano che i segnali
|
|
349
|
+
deboli costano meno. Il terminale mostra gli stessi numeri scontati che
|
|
350
|
+
usa il punteggio — niente black box. Metodo completo:
|
|
351
|
+
[docs/SCORING.md](docs/SCORING.md).
|
|
352
|
+
|
|
353
|
+
**Verdetti**
|
|
354
|
+
|
|
355
|
+
| Score | Verdetto |
|
|
356
|
+
| ------- | ---------------- |
|
|
357
|
+
| ≥ 80 | ✓ **WORTHY** |
|
|
358
|
+
| 50 – 79 | ⚠ **NEEDS WORK** |
|
|
359
|
+
| < 50 | ✖ **UNWORTHY** |
|
|
360
|
+
|
|
361
|
+
**Livelli di evidenza** — ogni riscontro ne porta uno; fissano il peso
|
|
362
|
+
del riscontro nel punteggio:
|
|
363
|
+
|
|
364
|
+
| Livello | Significato | Impatto sul punteggio | Esempio |
|
|
365
|
+
| ------- | ---------------------- | --------------------- | ------------------------------------------------------ |
|
|
366
|
+
| E2 | Difetto deterministico | Deduzione piena | `.only` committato — dimostrabile strutturalmente |
|
|
367
|
+
| E1 | Pattern euristico | Mezza deduzione | `sleep()` trovato via regex — segnale forte, non prova |
|
|
368
|
+
| E0 | Osservazione | Zero (solo info) | Riportato ma non fa mai gate alla CI né deduce |
|
|
369
|
+
|
|
370
|
+
La maggior parte delle regole è **E1**. Lo slogan «we prove it» si
|
|
371
|
+
riferisce a questo sistema: i riscontri E2 sono prova strutturale; i
|
|
372
|
+
riscontri E1 sono avvertimenti correttamente posizionati, non prove
|
|
373
|
+
formali.
|
|
374
|
+
|
|
375
|
+
Un repo vuoto ottiene `null`, mai un falso 100 — vedi
|
|
376
|
+
[Modello di fiducia](#modello-di-fiducia).
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## 🎭 Selector Health Score
|
|
381
|
+
|
|
382
|
+
La metrica di testa per le suite Playwright — quanto sono resilienti
|
|
383
|
+
i tuoi locator:
|
|
384
|
+
|
|
385
|
+
```text
|
|
386
|
+
▚▞ SELECTOR HEALTH — e2e/checkout.spec.ts
|
|
387
|
+
|
|
388
|
+
[█████████████████░░░] 83 / 100
|
|
389
|
+
role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
I locator basati sui ruoli prendono il punteggio pieno. Le catene di
|
|
393
|
+
classi CSS e XPath affossano il punteggio — si rompono a ogni refactor
|
|
394
|
+
del DOM senza dirti quale comportamento è regredito.
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## 🔬 Evidenza di runtime
|
|
399
|
+
|
|
400
|
+
La rilevazione statica di instabilità è tirare a indovinare. Mjölnir
|
|
401
|
+
legge **veri dati di esecuzione** — report JSON Playwright e XML JUnit
|
|
402
|
+
da qualsiasi runner:
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
mjolnir forensics ./test-results/
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
```text
|
|
409
|
+
▚▞ FLAKINESS LEADERBOARD
|
|
410
|
+
|
|
411
|
+
3 tests · 1 failed · 1 flaky · 1 retried
|
|
412
|
+
|
|
413
|
+
TRUE-FLAKE completes checkout with saved card (e2e/checkout.spec.ts)
|
|
414
|
+
████████████████████ 6.0s · 2 attempts
|
|
415
|
+
FAILING declines an expired card (e2e/checkout.spec.ts)
|
|
416
|
+
████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Un test che passa solo dal tentativo ≥ 2 non è un test che passa — è un
|
|
420
|
+
test fortunato. Viene marcato `TRUE-FLAKE` a prescindere dalla spunta
|
|
421
|
+
verde finale.
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## ⚡ Mjölnir non è un linter in più
|
|
426
|
+
|
|
427
|
+
I linter ti dicono se il codice segue le regole. Mjölnir ti dice se la
|
|
428
|
+
tua verifica può essere ritenuta affidabile.
|
|
429
|
+
|
|
430
|
+
| | ESLint / SonarQube | Strumenti di coverage | Review manuale | **Mjölnir** |
|
|
431
|
+
| ------------------------------------------------------------- | :----------------: | :-------------------: | :------------: | :---------: |
|
|
432
|
+
| Integrità dei workflow CI (`continue-on-error`, `\|\| true`) | ❌ | ❌ | raramente | ✅ |
|
|
433
|
+
| Cross-linguaggio (TS, Python, Java, C#) da un solo strumento | ❌ | ❌ | ❌ | ✅ |
|
|
434
|
+
| Valuta la resilienza dei locator Playwright (Selector Health) | ❌ | ❌ | raramente | ✅ |
|
|
435
|
+
| Segnala test senza vere asserzioni | ✅ (plugin)\* | ❌ | a volte | ✅ |
|
|
436
|
+
| Becca gli sleep hardcoded (`waitForTimeout`, `time.sleep`) | ✅ (plugin)\* | ❌ | a volte | ✅ |
|
|
437
|
+
| Gira in secondi, zero chiamate di rete durante la scansione | ✅ | ✅ | — | ✅ |
|
|
438
|
+
|
|
439
|
+
\*`eslint-plugin-jest` (`expect-expect`) e `eslint-plugin-playwright`
|
|
440
|
+
(`expect-expect`, `no-wait-for-timeout`) coprono questo per i rispettivi
|
|
441
|
+
framework.
|
|
442
|
+
|
|
443
|
+
**L'analisi di runtime** è una categoria a sé rispetto al linting
|
|
444
|
+
statico:
|
|
445
|
+
|
|
446
|
+
| | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
|
|
447
|
+
| -------------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
|
|
448
|
+
| Legge dati di run reali per verdetti `TRUE-FLAKE` | parziale\* | parziale (tag) | ✅ |
|
|
449
|
+
| Report di triage dell'instabilità dalla cronologia | ❌ | ✅ | ✅ |
|
|
450
|
+
| Si integra con il punteggio di idoneità statico | ❌ | ❌ | ✅ |
|
|
451
|
+
|
|
452
|
+
\*Playwright traccia i retry internamente ma non produce un report di
|
|
453
|
+
instabilità autonomo con etichette di verdetto.
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## 🤖 Perché non usare semplicemente la code review con IA?
|
|
458
|
+
|
|
459
|
+
Problema diverso, livello diverso. Una review IA può beccare una
|
|
460
|
+
modifica sospetta a un test in un diff; non dimostra che il sistema di
|
|
461
|
+
verifica nel suo insieme sia affidabile — e vede solo il diff che gli
|
|
462
|
+
mostri.
|
|
463
|
+
|
|
464
|
+
| | Code review IA (Copilot, ecc.) | **Mjölnir** |
|
|
465
|
+
| --------------------------------------------- | :--------------------------------------: | :-----------------------------: |
|
|
466
|
+
| Costo per scansione | Token (scala con la dimensione del diff) | **Zero** (locale, installato) |
|
|
467
|
+
| Vede tutta la suite + tutte le config CI | Solo il diff di PR che mostri | **Tutto, ogni volta** |
|
|
468
|
+
| Deterministico (stesso input → stesso output) | ❌ (non deterministico) | **✅** |
|
|
469
|
+
| Becca pattern dormienti da mesi | Solo se è nel contesto | **✅** (scansiona tutti i file) |
|
|
470
|
+
| Ricorda i riscontri tra le esecuzioni | ❌ (nessuna memoria tra sessioni) | **✅** (baseline + diff) |
|
|
471
|
+
| Gira senza innesco umano | Serve una PR o un prompt | **✅** (hook CI, 3 secondi) |
|
|
472
|
+
|
|
473
|
+
**Usali entrambi.** L'IA becca la sfumatura, l'intento e i difetti di
|
|
474
|
+
design che nessuna regex trova. Mjölnir becca i pattern strutturali che
|
|
475
|
+
l'IA trascura perché sembrano "intenzionali" — un `.only` committato,
|
|
476
|
+
un exit code ingoiato, un `continue-on-error` su un job di test. Non
|
|
477
|
+
sono bug che richiedono ragionamento; sono fatti che richiedono
|
|
478
|
+
scansione.
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## 🤖 Integrazione CI
|
|
483
|
+
|
|
484
|
+
Un comando genera un workflow di PR — consultivo per impostazione
|
|
485
|
+
predefinita, mai bloccante:
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
mjolnir ci install
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Oppure collegalo nativamente a GitHub Code Scanning via SARIF:
|
|
492
|
+
|
|
493
|
+
```yaml
|
|
494
|
+
- run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
|
|
495
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
496
|
+
with:
|
|
497
|
+
sarif_file: mjolnir.sarif
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
Setup per editor e pipeline per SARIF:
|
|
501
|
+
[docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
|
|
502
|
+
|
|
503
|
+
### Copertura del perimetro modificato
|
|
504
|
+
|
|
505
|
+
`--scope changed` attribuisce i riscontri alle righe aggiunte nel tuo
|
|
506
|
+
branch rispetto al merge-base con `main`. Copre i file di test
|
|
507
|
+
(`*.spec.*`, `*.test.*`) più i file di workflow GitHub e le
|
|
508
|
+
configurazioni Playwright nel diff. Quando il merge-base non si può
|
|
509
|
+
risolvere — clone shallow, HEAD detached, target non git, branch
|
|
510
|
+
predefinito diverso — degrada onestamente: i riscontri tornano a
|
|
511
|
+
un'attribuzione per intero file e il report lo dice. Sovrascrivi la ref
|
|
512
|
+
di base con `--base <ref>`.
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## Configurazione
|
|
517
|
+
|
|
518
|
+
Mjölnir è zero-config. Un `mjolnir.config.json` opzionale (o
|
|
519
|
+
`.mjolnir.json`) alla radice del repo regola severità, gating e
|
|
520
|
+
perimetro — non cambia mai la semantica di rilevamento.
|
|
521
|
+
|
|
522
|
+
| Key | Tipo | Effetto |
|
|
523
|
+
| ------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
524
|
+
| `exclude` | `string[]` | Glob di ignore aggiuntivi (sottoinsieme gitignore), sopra i default integrati |
|
|
525
|
+
| `gate` | `"advisory" \| "error" \| "warning"` | Quali severità escono con codice diverso da zero (predefinito `error`; `advisory` non blocca mai) |
|
|
526
|
+
| `severityOverrides` | `{ "<RULE-ID>": severity }` | Riordina i riscontri di una regola per il tuo repo |
|
|
527
|
+
| `ignore` | `IgnoreEntry[]` | Sopprime riscontri — **`reason` è obbligatorio**; le voci scadono dopo 90 giorni (una data `expires` esplicita, o la data di modifica del file di config per le voci senza) |
|
|
528
|
+
| `plugins` | `string[]` | Pacchetti di regole di terze parti (vedi [Modello di fiducia](#modello-di-fiducia)) |
|
|
529
|
+
|
|
530
|
+
```json
|
|
531
|
+
{
|
|
532
|
+
"gate": "error",
|
|
533
|
+
"exclude": ["legacy/**"],
|
|
534
|
+
"severityOverrides": { "QA-PW-118": "warning" },
|
|
535
|
+
"ignore": [
|
|
536
|
+
{
|
|
537
|
+
"ruleId": "QA-TEST-004",
|
|
538
|
+
"files": ["e2e/legacy-login.spec.ts"],
|
|
539
|
+
"reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
|
|
540
|
+
"expires": "2026-12-31"
|
|
541
|
+
}
|
|
542
|
+
]
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
- **`.mjolnirignore`** — un file semplice in stile gitignore per le
|
|
547
|
+
esclusioni di percorsi, stesso dialetto di `exclude`. Usalo per il
|
|
548
|
+
rumore specifico della macchina; usa `exclude` quando la lista
|
|
549
|
+
appartiene al version control, accanto al resto della configurazione.
|
|
550
|
+
- **Override CLI** — `--strict` (includere le regole in quarantena),
|
|
551
|
+
`--width <cols>` e `--ascii` / `--no-ascii` (rendering terminale),
|
|
552
|
+
`--tone blunt` (messaggi più secchi), `--max-duration <sec>` (scansione
|
|
553
|
+
parziale limitata).
|
|
554
|
+
- Soppressione delle regole e ciclo di vita delle deprecazioni:
|
|
555
|
+
[docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md).
|
|
556
|
+
|
|
557
|
+
Le voci `ignore` alimentano anche il comando autonomo
|
|
558
|
+
`mjolnir suppressions`, che elenca ciò che è attualmente soppresso e
|
|
559
|
+
quando scade ogni voce.
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## 📐 Exit code & contratti
|
|
564
|
+
|
|
565
|
+
Congelati — sicuri su cui costruire logica CI:
|
|
566
|
+
|
|
567
|
+
| Exit code | Significato |
|
|
568
|
+
| --------- | -------------------------------------------------------------------------------- |
|
|
569
|
+
| `0` | Pulito — nessun riscontro a o sopra il gate |
|
|
570
|
+
| `1` | Riscontri a o sopra il gate |
|
|
571
|
+
| `2` | Scansione parziale (budget di tempo esaurito, file illeggibili) — non blocca mai |
|
|
572
|
+
| `10` | Errore d'uso (flag errato, target mancante) |
|
|
573
|
+
| `20` | Errore interno |
|
|
574
|
+
|
|
575
|
+
Il report JSON/SARIF è `schemaVersion: 1`. Gli ID di regola
|
|
576
|
+
(`QA-<FAMILY>-NNN`) sono immutabili una volta pubblicati e mai
|
|
577
|
+
riusati.
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## Modello di fiducia
|
|
582
|
+
|
|
583
|
+
- **Local-first** — zero chiamate di rete durante la scansione. Mai.
|
|
584
|
+
Zero telemetria.
|
|
585
|
+
- **Nessuna prova falsa** — preferiamo dire "sconosciuto" che
|
|
586
|
+
"verificato". Un repo vuoto riceve `score: null`, mai un falso 100.
|
|
587
|
+
- **Onestà parziale** — se l'analisi è stata troncata, l'output lo dice.
|
|
588
|
+
Mai "complete" quando non lo è.
|
|
589
|
+
- **Firewall FP** — il rilevamento gira su una vista del codice senza
|
|
590
|
+
commenti/stringhe (le regole TypeScript usano l'AST del compilatore):
|
|
591
|
+
un pattern dentro un commento di prosa o una stringa di esempio di
|
|
592
|
+
documentazione è documentazione, non un riscontro.
|
|
593
|
+
- **Misurato, non affermato** — solo le regole con un tasso di falsi
|
|
594
|
+
positivi da vero codice OSS escono nei tier di testa (vedi
|
|
595
|
+
[Quanto è misurato](#quanto-è-misurato)); il footer della scansione e
|
|
596
|
+
`mjolnir rules --unmeasured` ti dicono quale è quale.
|
|
597
|
+
- **Fiducia nei plugin** — i plugin sono pacchetti npm dichiarati sotto
|
|
598
|
+
`"plugins"`. **Non c'è sandbox**: il codice del plugin gira con tutti
|
|
599
|
+
i privilegi Node, lo stesso modello di fiducia dei plugin ESLint o
|
|
600
|
+
Vitest. I prefissi di ID delle regole core sono riservati e rifiutati
|
|
601
|
+
dai plugin per evitare spoofing.
|
|
602
|
+
- **Regole esterne locali al workspace** (basate su cartella, zero
|
|
603
|
+
rete) — una directory `mjolnir-rules/` accanto al target della
|
|
604
|
+
scansione carica regole personalizzate: i file JSON dichiarano
|
|
605
|
+
pattern regex (nessun codice eseguito), i moduli `.mjs`/`.js`
|
|
606
|
+
esportano `rules` (fiducia Node piena, come i plugin). Le regole
|
|
607
|
+
esterne portano gli stessi metadati di fiducia del core; non possono
|
|
608
|
+
mai uscire nel tier core (core richiede un tasso di FP misurato dal
|
|
609
|
+
sidecar corpus — un `tier: "core"` dichiarato viene limitato a
|
|
610
|
+
`extended`), obbediscono ai tetti di tier e sono controllate contro
|
|
611
|
+
la deriva: `mjolnir rules --md --external` renderizza il catalogo dai
|
|
612
|
+
file caricati (provenienza `external`), e il generatore di matrice
|
|
613
|
+
accetta `--external <root>`.
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
## 🏗️ Architettura
|
|
618
|
+
|
|
619
|
+
<details>
|
|
620
|
+
<summary>Espandi l'albero</summary>
|
|
621
|
+
|
|
622
|
+
```
|
|
623
|
+
mjolnir/
|
|
624
|
+
├── src/
|
|
625
|
+
│ ├── engine/ # LanguageAdapter interface + rule runner
|
|
626
|
+
│ ├── adapters/ # typescript · python · java · csharp · github-actions
|
|
627
|
+
│ ├── rules/ # rules across 8 families + the measured-FP table
|
|
628
|
+
│ ├── playwright/ # Selector Health Score engine
|
|
629
|
+
│ ├── discovery/ # workspace, frameworks, ignore resolution
|
|
630
|
+
│ ├── scope/ # git merge-base changed-scope engine
|
|
631
|
+
│ ├── scorer/ # transparent deduction table + prioritization
|
|
632
|
+
│ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
|
|
633
|
+
│ ├── forensics/ # run-data ingestion · flake verdicts · triage
|
|
634
|
+
│ ├── config/ # mjolnir.config.json + suppressions
|
|
635
|
+
│ ├── plugins/ # third-party rule loading (no sandbox)
|
|
636
|
+
│ └── commands/ # every subcommand
|
|
637
|
+
└── tests/
|
|
638
|
+
├── fixtures/ # must-fire / must-not-fire per rule
|
|
639
|
+
└── golden/ # frozen score regression locks
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
</details>
|
|
643
|
+
|
|
644
|
+
- **Le regole sono funzioni pure** —
|
|
645
|
+
`(SourceFileContext) → Finding[]`, niente I/O, niente globali.
|
|
646
|
+
Aggiungere un ecosistema = un adattatore + le sue regole.
|
|
647
|
+
- **TypeScript/Playwright usa l'AST del compilatore** (ts-morph).
|
|
648
|
+
Python, Java e C# girano su un livello regex condiviso con
|
|
649
|
+
commenti/stringhe mascherati.
|
|
650
|
+
- Un livello AST tree-sitter WASM per Java e C# esiste ed è il
|
|
651
|
+
prossimo passo di precisione — non è ancora cablato nella pipeline
|
|
652
|
+
di scansione sincrona.
|
|
653
|
+
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
## 📚 Documentazione
|
|
657
|
+
|
|
658
|
+
| Documento | Cosa contiene |
|
|
659
|
+
| ------------------------------------------------------ | ---------------------------------------------------------- |
|
|
660
|
+
| [docs/SCORING.md](docs/SCORING.md) | Normalizzazione del punteggio + ponderazione dell'evidenza |
|
|
661
|
+
| [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Tassi di falsi positivi misurati + metodo |
|
|
662
|
+
| [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Stati delle regole, soppressione, deprecazione |
|
|
663
|
+
| [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | Output SARIF + setup editor/CI |
|
|
664
|
+
| [docs/rules/](docs/rules/) | Catalogo generato per regola |
|
|
665
|
+
| [CONTRIBUTING.md](CONTRIBUTING.md) | Setup dev + workflow di contribuzione |
|
|
666
|
+
| [CHANGELOG.md](CHANGELOG.md) | Cronologia delle release |
|
|
667
|
+
| [SECURITY.md](SECURITY.md) | Segnalazione vulnerabilità |
|
|
668
|
+
|
|
669
|
+
---
|
|
670
|
+
|
|
671
|
+
## 📈 Stato
|
|
672
|
+
|
|
673
|
+
**v0.5.x · beta aperta.** Lo schema JSON e gli exit code sono contratti
|
|
674
|
+
congelati. TypeScript e Python hanno la copertura misurata più ampia;
|
|
675
|
+
Java e C# sono più recenti — leggili attraverso la
|
|
676
|
+
[tabella dei tier](#tier-delle-regole-e-maturità-per-linguaggio).
|
|
677
|
+
|
|
678
|
+
---
|
|
679
|
+
|
|
680
|
+
## 🤝 Contribuire
|
|
681
|
+
|
|
682
|
+
Le nuove regole sono il primo contributo più semplice — un comando
|
|
683
|
+
imposta lo scheletro della regola più le sue fixture must-fire **e**
|
|
684
|
+
must-not-fire (la regola generata fallisce apposta le fixture finché
|
|
685
|
+
non implementi il rilevamento vero — uno stub non può uscire):
|
|
686
|
+
|
|
687
|
+
```bash
|
|
688
|
+
mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
Setup dev completo, i comandi della barriera permanente e le leggi
|
|
692
|
+
anti-creep / firewall delle fixture sono in
|
|
693
|
+
[CONTRIBUTING.md](CONTRIBUTING.md).
|
|
694
|
+
|
|
695
|
+
---
|
|
696
|
+
|
|
697
|
+
<div align="center">
|
|
698
|
+
|
|
699
|
+
**Smetti di pubblicare test di cui non ti puoi fidare.**
|
|
700
|
+
|
|
701
|
+
```bash
|
|
702
|
+
npx mjolnir-qa@latest
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
**Star ⭐ · Watch 👀 · Contribute 🤝**
|
|
706
|
+
|
|
707
|
+
Costruito da [Sergey Bar](https://www.linkedin.com/in/sergeybar/)
|
|
708
|
+
|
|
709
|
+
</div>
|