frontend-go-live-check 1.4.1 → 1.5.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.
- package/CHANGELOG.md +7 -0
- package/README.md +67 -21
- package/package.json +1 -1
- package/schema.json +2 -2
- package/src/api/model.mjs +1 -1
- package/src/checks/auth.mjs +1 -1
- package/src/checks/browser.mjs +5 -1
- package/src/checks/code.mjs +1 -1
- package/src/checks/sso.mjs +1 -1
- package/src/checks/visual.mjs +7 -2
- package/src/cli/main.mjs +5 -5
- package/src/commands/ci.mjs +18 -18
- package/src/commands/doctor.mjs +9 -8
- package/src/commands/init.mjs +34 -27
- package/src/commands/login.mjs +14 -11
- package/src/commands/run.mjs +9 -9
- package/src/core/config.mjs +48 -22
- package/src/core/explain.mjs +2 -2
- package/src/core/login.mjs +1 -1
- package/src/core/project.mjs +1 -1
- package/src/core/routes.mjs +1 -1
- package/src/core/rules.mjs +1 -1
- package/src/core/session.mjs +1 -1
- package/src/export/docx.mjs +4 -3
- package/src/export/model.mjs +16 -6
- package/src/export/pdf.mjs +4 -3
- package/src/report/report.mjs +50 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.0
|
|
4
|
+
|
|
5
|
+
- **One folder for everything**: `go-live-check/` holds the settings (`config.json`), the test passwords (`.env`), the API files (`api/`), the design baseline (`baseline/`), the report (`report/`) and a saved sign-in (`session.json`), with its own `.gitignore`. `init` creates it. Projects with the older root files keep working; the README shows how to move.
|
|
6
|
+
- Every section of the HTML report opens and closes (accordion), with **Expand all** and **Collapse all**.
|
|
7
|
+
- The date and time the report was created are at the top of the HTML report, on the PDF and Word cover, and in the page header.
|
|
8
|
+
- Visual changes show the real pages: **Before** (the approved look), **Now** and **Changes** side by side; in each finding, Actual is the page now and Expected is the approved look.
|
|
9
|
+
|
|
3
10
|
## 1.4.1
|
|
4
11
|
|
|
5
12
|
- Problems are grouped **by type** (Functionality, Design, Accessibility, Performance, Sign-in, APIs, Security, SEO, Code) in the HTML report, the PDF and the Word file, most serious first in each group.
|
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
20
|
<a href="#quick-start">Quick start</a> ·
|
|
21
|
+
<a href="#files-everything-in-one-folder">Files</a> ·
|
|
21
22
|
<a href="#sign-in-email-google-microsoft-uae-pass">Sign-in</a> ·
|
|
22
23
|
<a href="#what-it-checks">What it checks</a> ·
|
|
23
24
|
<a href="#how-it-works">How it works</a> ·
|
|
@@ -38,11 +39,11 @@ npx frontend-go-live-check init
|
|
|
38
39
|
npm run go-live
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
That's it. The result is `go-live-report/index.html`, plus a PDF and a Word file to share with your team.
|
|
42
|
+
That's it. The result is `go-live-check/report/index.html`, plus a PDF and a Word file to share with your team.
|
|
42
43
|
|
|
43
44
|
`init` adds the `go-live` scripts and a settings file, and downloads the test browser. It never overwrites anything.
|
|
44
45
|
|
|
45
|
-
Have an API? Save your Postman collection as **`api/postman_collection.json`** (or your OpenAPI file as `api/openapi.json`) and the API tests run too.
|
|
46
|
+
Have an API? Save your Postman collection as **`go-live-check/api/postman_collection.json`** (or your OpenAPI file as `go-live-check/api/openapi.json`) and the API tests run too.
|
|
46
47
|
|
|
47
48
|
<p align="center">
|
|
48
49
|
<img src="https://unpkg.com/frontend-go-live-check@1/docs/terminal.png" alt="Terminal output: NOT READY, 2 blocker, 7 major, 5 minor, with a score per area" width="760">
|
|
@@ -50,6 +51,53 @@ Have an API? Save your Postman collection as **`api/postman_collection.json`** (
|
|
|
50
51
|
|
|
51
52
|
---
|
|
52
53
|
|
|
54
|
+
## Files: everything in one folder
|
|
55
|
+
|
|
56
|
+
Everything the tool uses or makes is in **one `go-live-check/` folder** at the root of your project. `init` creates it.
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
my-project/
|
|
60
|
+
├── go-live-check/
|
|
61
|
+
│ ├── config.json ← the settings (all optional)
|
|
62
|
+
│ ├── .env ← test passwords (Git ignores it)
|
|
63
|
+
│ ├── .env.example ← the names to fill in (committed)
|
|
64
|
+
│ ├── api/
|
|
65
|
+
│ │ ├── postman_collection.json ← your Postman collection
|
|
66
|
+
│ │ ├── postman_environment.json ← optional: its environment
|
|
67
|
+
│ │ └── openapi.json ← or/and your OpenAPI file
|
|
68
|
+
│ ├── baseline/ ← the approved look of every page (commit it)
|
|
69
|
+
│ ├── report/ ← the results: HTML, PDF, Word, zip (Git ignores it)
|
|
70
|
+
│ ├── session.json ← a saved sign-in, after `login` (Git ignores it)
|
|
71
|
+
│ └── .gitignore ← keeps .env, report/ and session.json out of Git
|
|
72
|
+
├── src/
|
|
73
|
+
└── package.json
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
| Commit | Never commit (already ignored) |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `config.json`, `.env.example`, `api/`, `baseline/` | `.env`, `report/`, `session.json` |
|
|
79
|
+
|
|
80
|
+
<details>
|
|
81
|
+
<summary><b>Set up with an older version?</b></summary>
|
|
82
|
+
|
|
83
|
+
Projects with `go-live-check.config.json`, `go-live-check.env`, `api/`, `go-live-baseline/` and `go-live-report/` at the root keep working. To move to the folder:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
mkdir -p go-live-check
|
|
87
|
+
git mv go-live-check.config.json go-live-check/config.json
|
|
88
|
+
git mv go-live-check.env.example go-live-check/.env.example
|
|
89
|
+
git mv api go-live-check/api
|
|
90
|
+
git mv go-live-baseline go-live-check/baseline
|
|
91
|
+
mv go-live-check.env go-live-check/.env
|
|
92
|
+
printf '.env\nreport/\nsession.json\n' > go-live-check/.gitignore
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
In `go-live-check/config.json`, change `$schema` to `../node_modules/frontend-go-live-check/schema.json`. Remove the old `go-live-report/` folder and its `.gitignore` lines.
|
|
96
|
+
|
|
97
|
+
</details>
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
53
101
|
## Why
|
|
54
102
|
|
|
55
103
|
| Without it | With it |
|
|
@@ -99,7 +147,7 @@ npx frontend-go-live-check login
|
|
|
99
147
|
What happens:
|
|
100
148
|
|
|
101
149
|
- A normal browser window opens on your app. Sign in the way your users do (approve UAE PASS on your phone), wait for a signed-in page, then press Enter in the terminal.
|
|
102
|
-
- The session (cookies and browser storage) is saved in **`go-live-check
|
|
150
|
+
- The session (cookies and browser storage) is saved in **`go-live-check/session.json`**. Treat it like a password: Git ignores it, never share it.
|
|
103
151
|
- When it expires, the report says ***"The saved sign-in session has expired"*** (also when the app's API starts answering 401), and `doctor` tells you before you run. Run `login` again.
|
|
104
152
|
|
|
105
153
|
**Pipelines (`ciLogin`)**: a pipeline has no one to approve a sign-in, so it uses `ciLogin` instead of `login`. Pick one of these:
|
|
@@ -147,7 +195,7 @@ The report lists every sign-in method found (*email and password · Google · UA
|
|
|
147
195
|
| 🧭 | **Functionality** | JavaScript errors, console errors, failed requests, broken links, images and anchors, blank pages, buttons covered by something else, unfinished text (`undefined`, `NaN`, `[object Object]`, translation keys, lorem ipsum, TODO), loading spinners that never stop, the production build, the "page not found" page |
|
|
148
196
|
| 🛟 | **Resilience** | Each page opened again with its API calls failing: does it crash, go blank, or tell the user? |
|
|
149
197
|
| 📱 | **Design and responsive layout** | Every page at every screen size: sideways scrolling, text that spills out of its box (a button too small for its label), text on top of other text, content cut off at the screen edge, text cut off inside its box, overlapping buttons, stretched or squashed images, tiny text on phones, right-to-left layout (Arabic, Hebrew…), fonts that fail to load, too many fonts and sizes |
|
|
150
|
-
| 🖼️ | **Visual changes** |
|
|
198
|
+
| 🖼️ | **Visual changes** | Every page compared with its approved look (the baseline): **Before**, **Now** and the changed areas side by side |
|
|
151
199
|
| ♿ | **Accessibility** | axe-core against WCAG 2.0 / 2.1 / 2.2 A and AA; keyboard-only use: visible focus, no traps, nothing that only a mouse can click; form field types |
|
|
152
200
|
| ⚡ | **Performance** | Size, JavaScript, requests, load time, LCP, layout shift and frozen-page time for every page, against budgets; Lighthouse as a phone and as a desktop |
|
|
153
201
|
| 🔌 | **API calls made by the pages** | Slow, very large, repeated, too many, uncompressed or uncached calls |
|
|
@@ -170,7 +218,7 @@ The report's **What was checked** table says exactly what ran on your project, a
|
|
|
170
218
|
|
|
171
219
|
| Step | What happens |
|
|
172
220
|
| :-: | --- |
|
|
173
|
-
| **1. Detect** | Reads `package.json`: the framework, the package manager, the sign-in libraries, and your
|
|
221
|
+
| **1. Detect** | Reads `package.json`: the framework, the package manager, the sign-in libraries, and your files in `go-live-check/` |
|
|
174
222
|
| **2. Build & start** | Builds the app and starts it **the way it runs in production**, plus any services you listed (a backend or a mock API) |
|
|
175
223
|
| **3. Sign in** | With the test account, the saved session (`login`) or a test token, so the signed-in pages are tested too |
|
|
176
224
|
| **4. Find pages** | Follows every link, reads your routes in the source code and your `sitemap.xml` |
|
|
@@ -214,7 +262,8 @@ The framework and the package manager (npm, pnpm, yarn, bun) are detected. The a
|
|
|
214
262
|
|
|
215
263
|
The report has:
|
|
216
264
|
|
|
217
|
-
- the **verdict** and a **score per area** (click an area to
|
|
265
|
+
- when it was created, the **verdict** and a **score per area** (click an area to jump to it);
|
|
266
|
+
- every section opens and closes, with **Expand all** and **Collapse all**;
|
|
218
267
|
- **problems by type**: Functionality, Design, Accessibility, Performance, Sign-in, APIs, Security, SEO, Code, each with its score, worst problems first;
|
|
219
268
|
- the API results, the speed of every page, and the load test;
|
|
220
269
|
- screenshots of every page at every size, for the design review.
|
|
@@ -336,18 +385,18 @@ The short name `go-live-check` works too. After `init`, use `npm run go-live`, `
|
|
|
336
385
|
|
|
337
386
|
## Settings
|
|
338
387
|
|
|
339
|
-
Everything is optional. `init` writes `go-live-check
|
|
388
|
+
Everything is optional. `init` writes `go-live-check/config.json`, and your editor autocompletes it through `$schema`.
|
|
340
389
|
|
|
341
390
|
```json
|
|
342
391
|
{
|
|
343
|
-
"$schema": "
|
|
392
|
+
"$schema": "../node_modules/frontend-go-live-check/schema.json",
|
|
344
393
|
"login": { "page": "/login", "username": "test.user", "password": "$GO_LIVE_PASSWORD" },
|
|
345
394
|
"services": [{ "command": "npm run api", "url": "http://localhost:4000/health" }],
|
|
346
395
|
"failOn": "blocker"
|
|
347
396
|
}
|
|
348
397
|
```
|
|
349
398
|
|
|
350
|
-
API tests need no settings when your files are in `api/` (see *API tests from Postman or OpenAPI* below). Passwords are written as `"$NAME"`. Put their values in `go-live-check
|
|
399
|
+
API tests need no settings when your files are in `go-live-check/api/` (see *API tests from Postman or OpenAPI* below). Passwords are written as `"$NAME"`. Put their values in `go-live-check/.env` (Git ignores it) or in your pipeline's secrets. They are replaced with `‹secret›` in every report.
|
|
351
400
|
|
|
352
401
|
<details>
|
|
353
402
|
<summary><b>🔐 Sign-in forms that need help</b></summary>
|
|
@@ -376,19 +425,16 @@ An external single sign-on page usually can't be automated. Point `login` at a t
|
|
|
376
425
|
<details>
|
|
377
426
|
<summary><b>🧪 API tests from Postman or OpenAPI</b></summary>
|
|
378
427
|
|
|
379
|
-
Put your API description in
|
|
428
|
+
Put your API description in **`go-live-check/api/`**, with **exactly these names**:
|
|
380
429
|
|
|
381
430
|
```text
|
|
382
|
-
|
|
383
|
-
├──
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
│ └── openapi.json ← or/and the OpenAPI / Swagger file (openapi.yaml works too)
|
|
387
|
-
├── go-live-check.config.json
|
|
388
|
-
└── package.json
|
|
431
|
+
go-live-check/api/
|
|
432
|
+
├── postman_collection.json ← the Postman collection (v2.1)
|
|
433
|
+
├── postman_environment.json ← optional: its environment ({{baseUrl}} and other variables)
|
|
434
|
+
└── openapi.json ← or/and the OpenAPI / Swagger file (openapi.yaml works too)
|
|
389
435
|
```
|
|
390
436
|
|
|
391
|
-
That's all: no settings are needed. Postman export names (`My API.postman_collection.json`) need renaming to `postman_collection.json`. If both a Postman collection and an OpenAPI file are there, they are combined: OpenAPI gives the rules, Postman the examples. `init` creates `api/README.md` with these instructions. `frontend-go-live-check doctor` shows what it found.
|
|
437
|
+
That's all: no settings are needed. Postman export names (`My API.postman_collection.json`) need renaming to `postman_collection.json`. If both a Postman collection and an OpenAPI file are there, they are combined: OpenAPI gives the rules, Postman the examples. `init` creates `go-live-check/api/README.md` with these instructions. `frontend-go-live-check doctor` shows what it found.
|
|
392
438
|
|
|
393
439
|
Never put real passwords or tokens in these files: use `$NAMES` (see below).
|
|
394
440
|
|
|
@@ -416,7 +462,7 @@ To test roles and ownership, add test users. Include a **second user of the same
|
|
|
416
462
|
<details>
|
|
417
463
|
<summary><b>🖼️ Visual changes</b></summary>
|
|
418
464
|
|
|
419
|
-
The first run saves a picture of every page at every size in `go-live-baseline/`. Commit that folder. Later runs report pages that look different, with the changed areas in red.
|
|
465
|
+
The first run saves a picture of every page at every size in `go-live-check/baseline/`. Commit that folder. Later runs report pages that look different, with the changed areas in red.
|
|
420
466
|
|
|
421
467
|
After an intended design change, save the new look:
|
|
422
468
|
|
|
@@ -455,7 +501,7 @@ Hide parts that change on their own (clocks, live data) with `"visualMask": [".c
|
|
|
455
501
|
## Safety and privacy
|
|
456
502
|
|
|
457
503
|
- **Everything runs on your machine** or your own pipeline. Nothing is sent anywhere except to the app being tested.
|
|
458
|
-
- **Test passwords** come from `go-live-check
|
|
504
|
+
- **Test passwords** come from `go-live-check/.env` or pipeline secrets, and never appear in a report or log.
|
|
459
505
|
- **Use test accounts on a test environment.** Browser checks only read pages and submit forms empty.
|
|
460
506
|
- **API writes and the load test** run only against this computer or the hosts you list. Every record the tests create is deleted afterwards.
|
|
461
507
|
- **Closed networks.** Point `PLAYWRIGHT_DOWNLOAD_HOST` at your mirror, or `GO_LIVE_CHROMIUM` at an existing Chromium.
|
|
@@ -534,7 +580,7 @@ Security headers, compression and caching usually come from your web server or C
|
|
|
534
580
|
| You see | Fix |
|
|
535
581
|
| --- | --- |
|
|
536
582
|
| `Executable doesn't exist` | `npx playwright install chromium` |
|
|
537
|
-
| `Not set: GO_LIVE_PASSWORD` | Copy `go-live-check
|
|
583
|
+
| `Not set: GO_LIVE_PASSWORD` | Copy `go-live-check/.env.example` to `go-live-check/.env` and fill it in |
|
|
538
584
|
| `login-failed` | Check `login`; name the fields (`usernameField`, `passwordField`, `submit`) if the form is unusual |
|
|
539
585
|
| *Signed-in pages were not tested* | Set `login`, or run `npx frontend-go-live-check login` (Google, UAE PASS…) |
|
|
540
586
|
| *The saved sign-in session has expired* | Run `npx frontend-go-live-check login` again |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frontend-go-live-check",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Is your frontend ready to go live? One command tests any web frontend (React, Next.js, Vue, Angular, Svelte, plain HTML…): pages, responsive design, accessibility, keyboard, performance and load, API calls, security, SEO, sign-in and APIs from Postman/OpenAPI. Report with evidence, PDF and Word, and a pipeline gate that approves or rejects the merge.",
|
|
5
5
|
"homepage": "https://www.npmjs.com/package/frontend-go-live-check",
|
|
6
6
|
"repository": {
|
package/schema.json
CHANGED
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
},
|
|
155
155
|
"password": {
|
|
156
156
|
"type": "string",
|
|
157
|
-
"description": "Use \"$NAME\" and put NAME in go-live-check
|
|
157
|
+
"description": "Use \"$NAME\" and put NAME in go-live-check/.env."
|
|
158
158
|
},
|
|
159
159
|
"usernameField": {
|
|
160
160
|
"type": "string",
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
},
|
|
208
208
|
"sessionFile": {
|
|
209
209
|
"type": "string",
|
|
210
|
-
"description": "Where the saved session is kept. Default go-live-check
|
|
210
|
+
"description": "Where the saved session is kept. Default go-live-check/session.json (never commit it)."
|
|
211
211
|
}
|
|
212
212
|
},
|
|
213
213
|
"description": "How the tool signs in. Email/password: page + username + password. Google, Microsoft, Apple, UAE PASS\u2026: \"method\": \"session\" and run frontend-go-live-check login once. Pipelines: see ciLogin."
|
package/src/api/model.mjs
CHANGED
|
@@ -242,6 +242,6 @@ export async function loadApiMap(api, dir) {
|
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
const baseUrl = sources.find((s) => s.baseUrl)?.baseUrl;
|
|
245
|
-
if (!sources.length) notes.push('No API file given: set "api": { "file": "postman_collection.json" } (or "openapi") in go-live-check
|
|
245
|
+
if (!sources.length) notes.push('No API file given: set "api": { "file": "postman_collection.json" } (or "openapi") in go-live-check/config.json, or put the file in go-live-check/api/, or --api <file>.');
|
|
246
246
|
return { endpoints: [...map.values()], baseUrl, basePath: sources.find((s) => s.basePath)?.basePath, title: sources.find((s) => s.title)?.title, sources: sources.map((s) => s.kind), notes };
|
|
247
247
|
}
|
package/src/checks/auth.mjs
CHANGED
|
@@ -95,7 +95,7 @@ export async function runAuthChecks({ base, config, pages, network, log }) {
|
|
|
95
95
|
const landed = new URL(page.url()).pathname;
|
|
96
96
|
const asksToSignIn = (await page.locator('input[type=password]').count()) > 0;
|
|
97
97
|
if (landed === new URL(target.url).pathname && !asksToSignIn) {
|
|
98
|
-
add({ ruleId: 'page-without-sign-in', page: target.key, detail: `${target.key} opened without signing in. If it is public on purpose, add it to "publicPages" in go-live-check
|
|
98
|
+
add({ ruleId: 'page-without-sign-in', page: target.key, detail: `${target.key} opened without signing in. If it is public on purpose, add it to "publicPages" in the settings (go-live-check/config.json).` });
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
await context.close();
|
package/src/checks/browser.mjs
CHANGED
|
@@ -414,7 +414,7 @@ export async function runBrowserChecks({ base, config, reportDir, log, starts =
|
|
|
414
414
|
mkdirSync(join(reportDir, 'visual', 'now'), { recursive: true });
|
|
415
415
|
const taken = await page.screenshot({ path: shotPath, fullPage: true, type: 'png', animations: 'disabled', caret: 'hide', mask }).then(() => true).catch(() => false);
|
|
416
416
|
if (taken) {
|
|
417
|
-
const result = compareWithBaseline({ shotPath, baselineDir, diffDir: join(reportDir, 'visual', 'diff'), name, update: updateBaseline, threshold: config.visualThreshold ?? 0.001 });
|
|
417
|
+
const result = compareWithBaseline({ shotPath, baselineDir, diffDir: join(reportDir, 'visual', 'diff'), beforeDir: join(reportDir, 'visual', 'before'), name, update: updateBaseline, threshold: config.visualThreshold ?? 0.001 });
|
|
418
418
|
visualResults.push({ page: landedKey, view: tag, name, ...result });
|
|
419
419
|
if (result.status === 'changed') {
|
|
420
420
|
add({
|
|
@@ -423,6 +423,10 @@ export async function runBrowserChecks({ base, config, reportDir, log, starts =
|
|
|
423
423
|
view: tag,
|
|
424
424
|
detail: `${(result.ratio * 100).toFixed(1)}% of the page looks different from the saved baseline${result.sizeChanged ? ` (size ${result.before} → ${result.after})` : ''}.`,
|
|
425
425
|
link: `visual/diff/${name}`,
|
|
426
|
+
// Actual = the page now; Expected = the approved look (the baseline).
|
|
427
|
+
evidence: `visual/now/${name}`,
|
|
428
|
+
expected: `visual/before/${name}`,
|
|
429
|
+
evidenceView: tag,
|
|
426
430
|
});
|
|
427
431
|
}
|
|
428
432
|
}
|
package/src/checks/code.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { SECRET_PATTERNS } from './security.mjs';
|
|
|
10
10
|
function sourceFiles(dir, out = [], depth = 0) {
|
|
11
11
|
if (depth > 8 || out.length > 3000) return out;
|
|
12
12
|
for (const name of readdirSync(dir)) {
|
|
13
|
-
if (['node_modules', 'dist', 'build', '.next', 'out', 'coverage', 'reports', 'go-live-report', '.git', 'test-results'].includes(name)) continue;
|
|
13
|
+
if (['node_modules', 'dist', 'build', '.next', 'out', 'coverage', 'reports', 'go-live-report', 'go-live-check', '.git', 'test-results'].includes(name)) continue;
|
|
14
14
|
const path = join(dir, name);
|
|
15
15
|
const stat = statSync(path);
|
|
16
16
|
if (stat.isDirectory()) sourceFiles(path, out, depth + 1);
|
package/src/checks/sso.mjs
CHANGED
|
@@ -124,7 +124,7 @@ export function scanProject(dir) {
|
|
|
124
124
|
continue;
|
|
125
125
|
}
|
|
126
126
|
for (const name of names) {
|
|
127
|
-
if (['node_modules', 'dist', 'build', '.next', '.git', 'go-live-report', 'coverage'].includes(name)) continue;
|
|
127
|
+
if (['node_modules', 'dist', 'build', '.next', '.git', 'go-live-report', 'go-live-check', 'coverage'].includes(name)) continue;
|
|
128
128
|
const path = join(current, name);
|
|
129
129
|
const stat = statSync(path);
|
|
130
130
|
if (stat.isDirectory()) stack.push(path);
|
package/src/checks/visual.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Design changes between runs. The first run saves a picture of every page at every screen size
|
|
3
|
-
* in go-live-baseline/ (commit it). Later runs compare with it and report pages that look
|
|
3
|
+
* in go-live-check/baseline/ (commit it). Later runs compare with it and report pages that look
|
|
4
4
|
* different, with a picture that shows the changed area in red.
|
|
5
5
|
* After an intended design change: frontend-go-live-check --update-baseline.
|
|
6
6
|
*/
|
|
@@ -9,7 +9,7 @@ import { join } from 'node:path';
|
|
|
9
9
|
import pixelmatch from 'pixelmatch';
|
|
10
10
|
import { PNG } from 'pngjs';
|
|
11
11
|
|
|
12
|
-
export function compareWithBaseline({ shotPath, baselineDir, diffDir, name, update, threshold = 0.001 }) {
|
|
12
|
+
export function compareWithBaseline({ shotPath, baselineDir, diffDir, beforeDir, name, update, threshold = 0.001 }) {
|
|
13
13
|
mkdirSync(baselineDir, { recursive: true });
|
|
14
14
|
const baselinePath = join(baselineDir, name);
|
|
15
15
|
if (update || !existsSync(baselinePath)) {
|
|
@@ -33,5 +33,10 @@ export function compareWithBaseline({ shotPath, baselineDir, diffDir, name, upda
|
|
|
33
33
|
if (ratio <= threshold && !sizeChanged) return { status: 'same', ratio };
|
|
34
34
|
mkdirSync(diffDir, { recursive: true });
|
|
35
35
|
writeFileSync(join(diffDir, name), PNG.sync.write(diff));
|
|
36
|
+
// The approved look, next to the report, so Before and Now can be shown side by side.
|
|
37
|
+
if (beforeDir) {
|
|
38
|
+
mkdirSync(beforeDir, { recursive: true });
|
|
39
|
+
copyFileSync(baselinePath, join(beforeDir, name));
|
|
40
|
+
}
|
|
36
41
|
return { status: 'changed', ratio, sizeChanged, before: `${a.width}×${a.height}`, after: `${b.width}×${b.height}` };
|
|
37
42
|
}
|
package/src/cli/main.mjs
CHANGED
|
@@ -29,16 +29,16 @@ What to check
|
|
|
29
29
|
--update-baseline save the current look of every page as the new design baseline
|
|
30
30
|
|
|
31
31
|
Output
|
|
32
|
-
--out <folder> where the report goes (default go-live-report)
|
|
32
|
+
--out <folder> where the report goes (default go-live-check/report)
|
|
33
33
|
--open open the report when done
|
|
34
34
|
--fail-on <level> blocker (default) | major | minor | never: what makes the command fail
|
|
35
|
-
--config <file> the settings file (default go-live-check
|
|
35
|
+
--config <file> the settings file (default go-live-check/config.json)
|
|
36
36
|
--quiet print only the result
|
|
37
37
|
--ci pipeline mode: build summary, no prompts (the pipeline files use it)
|
|
38
38
|
--format pdf,docx which documents to write (default both)
|
|
39
39
|
--no-export no Word or PDF document
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Files: go-live-check/ (config.json settings, .env test passwords never committed, api/ Postman or OpenAPI, report/).
|
|
42
42
|
Docs: https://www.npmjs.com/package/go-live-check
|
|
43
43
|
`;
|
|
44
44
|
|
|
@@ -64,7 +64,7 @@ export function parseArgs(argv) {
|
|
|
64
64
|
return { options, positional };
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const isProject = (folder) => ['package.json', 'go-live-check.config.json', 'index.html'].some((f) => existsSync(join(folder, f)));
|
|
67
|
+
const isProject = (folder) => ['package.json', 'go-live-check/config.json', 'go-live-check.config.json', 'index.html'].some((f) => existsSync(join(folder, f)));
|
|
68
68
|
|
|
69
69
|
export async function main(argv) {
|
|
70
70
|
let parsed;
|
|
@@ -148,7 +148,7 @@ export async function main(argv) {
|
|
|
148
148
|
|
|
149
149
|
const projectFolder = isProject(folder) ? folder : undefined;
|
|
150
150
|
if (!projectFolder && !options.url) {
|
|
151
|
-
console.error(`✖ ${folder} is not a web project (no package.json, index.html or go-live-check
|
|
151
|
+
console.error(`✖ ${folder} is not a web project (no package.json, index.html or go-live-check/config.json).\n Give a project folder, or --url <address> to check a site that is already running.`);
|
|
152
152
|
return 2;
|
|
153
153
|
}
|
|
154
154
|
const { runCheck } = await import('../commands/run.mjs');
|
package/src/commands/ci.mjs
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
* Writes a pipeline file that runs the full check on every pull / merge request. The run fails
|
|
4
4
|
* (and the merge is blocked once the check is required) when the verdict reaches "failOn".
|
|
5
5
|
* The test passwords come from the pipeline's secret variables: the file lists the names found
|
|
6
|
-
* in
|
|
6
|
+
* in the settings ("$NAME").
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
9
9
|
import { dirname, join } from 'node:path';
|
|
10
|
-
import {
|
|
10
|
+
import { layout } from '../core/config.mjs';
|
|
11
11
|
|
|
12
12
|
const PLAYWRIGHT = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).dependencies.playwright;
|
|
13
13
|
const IMAGE = `mcr.microsoft.com/playwright:v${PLAYWRIGHT}-noble`;
|
|
14
14
|
|
|
15
15
|
function secretNames(dir) {
|
|
16
|
-
const path = join(dir,
|
|
16
|
+
const path = join(dir, layout(dir).config);
|
|
17
17
|
if (!existsSync(path)) return ['GO_LIVE_PASSWORD'];
|
|
18
18
|
const names = new Set();
|
|
19
19
|
const walk = (v) => {
|
|
@@ -31,7 +31,7 @@ function secretNames(dir) {
|
|
|
31
31
|
const TEMPLATES = {
|
|
32
32
|
github: {
|
|
33
33
|
file: '.github/workflows/go-live-check.yml',
|
|
34
|
-
text: (secrets) => `# Go-live check: runs on every pull request and blocks the merge when it fails.
|
|
34
|
+
text: (secrets, report) => `# Go-live check: runs on every pull request and blocks the merge when it fails.
|
|
35
35
|
# 1. Settings > Secrets and variables > Actions: add ${secrets.join(', ') || 'your test passwords'}.
|
|
36
36
|
# 2. Settings > Branches (or Rules) > protect your main branch > Require status checks: "frontend-go-live-check".
|
|
37
37
|
name: Go-live check
|
|
@@ -74,7 +74,7 @@ ${secrets.length ? ` env:\n${secrets.map((s) => ` ${s}: \${{ sec
|
|
|
74
74
|
uses: actions/upload-artifact@v4
|
|
75
75
|
with:
|
|
76
76
|
name: go-live-report
|
|
77
|
-
path:
|
|
77
|
+
path: ${report}
|
|
78
78
|
retention-days: 30
|
|
79
79
|
|
|
80
80
|
- name: Comment the result on the pull request
|
|
@@ -84,7 +84,7 @@ ${secrets.length ? ` env:\n${secrets.map((s) => ` ${s}: \${{ sec
|
|
|
84
84
|
script: |
|
|
85
85
|
const fs = require('fs');
|
|
86
86
|
const marker = '<!-- frontend-go-live-check -->';
|
|
87
|
-
const summary = fs.existsSync('
|
|
87
|
+
const summary = fs.existsSync('${report}/summary.md') ? fs.readFileSync('${report}/summary.md', 'utf8') : 'The go-live check did not finish: open the job log.';
|
|
88
88
|
const run = \`\${context.serverUrl}/\${context.repo.owner}/\${context.repo.repo}/actions/runs/\${context.runId}\`;
|
|
89
89
|
const body = \`\${marker}\\n\${summary}\\n\\nFull report, PDF and Word: the **go-live-report** artifact of [this run](\${run}).\`;
|
|
90
90
|
const { data: comments } = await github.rest.issues.listComments({ ...context.repo, issue_number: context.issue.number });
|
|
@@ -95,7 +95,7 @@ ${secrets.length ? ` env:\n${secrets.map((s) => ` ${s}: \${{ sec
|
|
|
95
95
|
},
|
|
96
96
|
gitlab: {
|
|
97
97
|
file: '.gitlab/go-live-check.gitlab-ci.yml',
|
|
98
|
-
text: (secrets) => `# Go-live check: runs on every merge request and blocks the merge when it fails.
|
|
98
|
+
text: (secrets, report) => `# Go-live check: runs on every merge request and blocks the merge when it fails.
|
|
99
99
|
# 1. Add to .gitlab-ci.yml: include: '.gitlab/go-live-check.gitlab-ci.yml'
|
|
100
100
|
# 2. Settings > CI/CD > Variables: add ${secrets.join(', ') || 'your test passwords'} (Masked, Protected as needed).
|
|
101
101
|
# 3. Settings > Merge requests > "Pipelines must succeed".
|
|
@@ -117,14 +117,14 @@ frontend-go-live-check:
|
|
|
117
117
|
artifacts:
|
|
118
118
|
when: always
|
|
119
119
|
expire_in: 30 days
|
|
120
|
-
paths: [
|
|
120
|
+
paths: [${report}/]
|
|
121
121
|
reports:
|
|
122
|
-
junit:
|
|
122
|
+
junit: ${report}/junit.xml
|
|
123
123
|
`,
|
|
124
124
|
},
|
|
125
125
|
azure: {
|
|
126
126
|
file: 'azure-pipelines.go-live-check.yml',
|
|
127
|
-
text: (secrets) => `# Go-live check: runs on every pull request and blocks the merge when it fails.
|
|
127
|
+
text: (secrets, report) => `# Go-live check: runs on every pull request and blocks the merge when it fails.
|
|
128
128
|
# 1. Pipelines > New pipeline > your repo > Existing YAML file > azure-pipelines.go-live-check.yml
|
|
129
129
|
# 2. Edit > Variables: add ${secrets.join(', ') || 'your test passwords'}, tick "Keep this value secret".
|
|
130
130
|
# 3. Project settings > Repositories > the repo > Policies > main > Build validation: add this pipeline, Required.
|
|
@@ -156,21 +156,21 @@ ${secrets.length ? ` env:\n${secrets.map((s) => ` ${s}: $(${s})`).join('
|
|
|
156
156
|
condition: always()
|
|
157
157
|
inputs:
|
|
158
158
|
testResultsFormat: JUnit
|
|
159
|
-
testResultsFiles:
|
|
159
|
+
testResultsFiles: ${report}/junit.xml
|
|
160
160
|
testRunTitle: Go-live check
|
|
161
161
|
displayName: Problems in the Tests tab
|
|
162
162
|
|
|
163
163
|
- task: PublishPipelineArtifact@1
|
|
164
164
|
condition: always()
|
|
165
165
|
inputs:
|
|
166
|
-
targetPath:
|
|
166
|
+
targetPath: ${report}
|
|
167
167
|
artifact: go-live-report
|
|
168
168
|
displayName: Keep the report (HTML, PDF, Word)
|
|
169
169
|
`,
|
|
170
170
|
},
|
|
171
171
|
bitbucket: {
|
|
172
172
|
file: 'bitbucket-pipelines.go-live-check.yml',
|
|
173
|
-
text: (secrets) => `# Go-live check for Bitbucket. Copy the "frontend-go-live-check" step into bitbucket-pipelines.yml.
|
|
173
|
+
text: (secrets, report) => `# Go-live check for Bitbucket. Copy the "frontend-go-live-check" step into bitbucket-pipelines.yml.
|
|
174
174
|
# 1. Repository settings > Repository variables: add ${secrets.join(', ') || 'your test passwords'} (Secured).
|
|
175
175
|
# 2. Repository settings > Branch restrictions > main > "Minimum successful builds": 1.
|
|
176
176
|
image: ${IMAGE}
|
|
@@ -186,12 +186,12 @@ pipelines:
|
|
|
186
186
|
- npm ci
|
|
187
187
|
- npx frontend-go-live-check --ci
|
|
188
188
|
artifacts:
|
|
189
|
-
-
|
|
189
|
+
- ${report}/**
|
|
190
190
|
`,
|
|
191
191
|
},
|
|
192
192
|
jenkins: {
|
|
193
193
|
file: 'Jenkinsfile.go-live-check',
|
|
194
|
-
text: (secrets) => `// Go-live check for Jenkins (multibranch pipeline: runs on every pull request).
|
|
194
|
+
text: (secrets, report) => `// Go-live check for Jenkins (multibranch pipeline: runs on every pull request).
|
|
195
195
|
// 1. Manage Jenkins > Credentials: add ${secrets.map((s) => `"${s.toLowerCase().replace(/_/g, '-')}"`).join(', ') || 'your test passwords'} as "Secret text".
|
|
196
196
|
// 2. Point the pipeline's Script Path to Jenkinsfile.go-live-check, and require it in your Git server's merge rules.
|
|
197
197
|
pipeline {
|
|
@@ -206,8 +206,8 @@ ${secrets.map((s) => ` ${s} = credentials('${s.toLowerCase().replace(/_/g, '-
|
|
|
206
206
|
}
|
|
207
207
|
post {
|
|
208
208
|
always {
|
|
209
|
-
junit allowEmptyResults: true, testResults: '
|
|
210
|
-
archiveArtifacts artifacts: '
|
|
209
|
+
junit allowEmptyResults: true, testResults: '${report}/junit.xml'
|
|
210
|
+
archiveArtifacts artifacts: '${report}/**', allowEmptyArchive: true
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
213
|
}
|
|
@@ -230,7 +230,7 @@ export function addPipeline(dir, provider, log = console.log) {
|
|
|
230
230
|
}
|
|
231
231
|
const secrets = secretNames(dir);
|
|
232
232
|
mkdirSync(dirname(target), { recursive: true });
|
|
233
|
-
writeFileSync(target, template.text(secrets));
|
|
233
|
+
writeFileSync(target, template.text(secrets, layout(dir).report));
|
|
234
234
|
log(`✔ added ${template.file}`);
|
|
235
235
|
log(` Read the lines at the top of the file: the secret${secrets.length === 1 ? '' : 's'} to add (${secrets.join(', ') || 'none'}) and how to make the check required for merging.`);
|
|
236
236
|
return true;
|
package/src/commands/doctor.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { existsSync } from 'node:fs';
|
|
|
6
6
|
import { join } from 'node:path';
|
|
7
7
|
import { chromium } from 'playwright';
|
|
8
8
|
import { loadApiMap } from '../api/model.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import { loadConfig } from '../core/config.mjs';
|
|
10
10
|
import { detectProject } from '../core/detect.mjs';
|
|
11
11
|
import { normaliseLogin } from '../core/login.mjs';
|
|
12
12
|
import { readSession, sessionExpiry } from '../core/session.mjs';
|
|
@@ -41,11 +41,12 @@ export async function doctor(dir, options = {}, log = console.log) {
|
|
|
41
41
|
bad(error.message);
|
|
42
42
|
return problems;
|
|
43
43
|
}
|
|
44
|
-
const { config, configFile, envFile, missingSecrets, hasApi } = loaded;
|
|
45
|
-
if (configFile) ok(`Settings: ${
|
|
46
|
-
else warn(`No ${
|
|
47
|
-
if (envFile) ok(`Test passwords file: ${
|
|
48
|
-
if (missingSecrets.length) bad(`Not set: ${missingSecrets.join(', ')} (put them in ${
|
|
44
|
+
const { config, configFile, envFile, missingSecrets, hasApi, paths } = loaded;
|
|
45
|
+
if (configFile) ok(`Settings: ${paths.config}`);
|
|
46
|
+
else warn(`No ${paths.config}: the defaults are used (run frontend-go-live-check init to create one)`);
|
|
47
|
+
if (envFile) ok(`Test passwords file: ${paths.env}`);
|
|
48
|
+
if (missingSecrets.length) bad(`Not set: ${missingSecrets.join(', ')} (put them in ${paths.env}, or the environment)`);
|
|
49
|
+
if (paths.legacy) warn('Files are at the project root (older layout). It still works; to move them into one go-live-check/ folder, see the README (Files).');
|
|
49
50
|
else if (configFile) ok('Every $NAME in the settings has a value');
|
|
50
51
|
|
|
51
52
|
if (config.url) ok(`Checks the running site ${config.url}`);
|
|
@@ -81,9 +82,9 @@ export async function doctor(dir, options = {}, log = console.log) {
|
|
|
81
82
|
} catch (error) {
|
|
82
83
|
bad(`API file: ${error.message}`);
|
|
83
84
|
}
|
|
84
|
-
} else warn(
|
|
85
|
+
} else warn(`No API file: put your Postman collection at ${paths.api}/postman_collection.json (or OpenAPI at ${paths.api}/openapi.json) to add the API tests`);
|
|
85
86
|
|
|
86
|
-
if (!existsSync(join(dir, '.gitignore'))) warn(
|
|
87
|
+
if (!existsSync(join(dir, paths.legacy ? '.gitignore' : 'go-live-check/.gitignore'))) warn(`No .gitignore: make sure ${paths.env} is never committed (run frontend-go-live-check init)`);
|
|
87
88
|
log(problems ? `\n✖ ${problems} problem(s) to fix first.\n` : '\n✔ Ready: run npm run go-live\n');
|
|
88
89
|
return problems;
|
|
89
90
|
}
|