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 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.session.json`**. Treat it like a password: Git ignores it, never share it.
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** | A pixel comparison of every page with the saved baseline, with the changed areas in red |
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 API file in `api/` |
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 filter);
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.config.json`, and your editor autocompletes it through `$schema`.
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": "./node_modules/frontend-go-live-check/schema.json",
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.env` (Git ignores it) or in your pipeline's secrets. They are replaced with `‹secret›` in every report.
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 an **`api/`** folder at the root of your project, with **exactly these names**:
428
+ Put your API description in **`go-live-check/api/`**, with **exactly these names**:
380
429
 
381
430
  ```text
382
- my-project/
383
- ├── api/
384
- │ ├── postman_collection.json ← the Postman collection (v2.1)
385
- │ ├── postman_environment.json ← optional: its environment ({{baseUrl}} and other variables)
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.env` or pipeline secrets, and never appear in a report or log.
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.env.example` to `go-live-check.env` and fill it in |
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.4.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.env."
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.session.json (never commit it)."
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.config.json, or --api <file>.');
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
  }
@@ -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.config.json.` });
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();
@@ -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
  }
@@ -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);
@@ -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);
@@ -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.config.json)
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
- Settings: go-live-check.config.json (all optional). Test passwords: go-live-check.env (never committed).
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.config.json).\n Give a project folder, or --url <address> to check a site that is already running.`);
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');
@@ -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 go-live-check.config.json ("$NAME").
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 { CONFIG_FILE } from '../core/config.mjs';
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, CONFIG_FILE);
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: go-live-report
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('go-live-report/summary.md') ? fs.readFileSync('go-live-report/summary.md', 'utf8') : 'The go-live check did not finish: open the job log.';
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: [go-live-report/]
120
+ paths: [${report}/]
121
121
  reports:
122
- junit: go-live-report/junit.xml
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: go-live-report/junit.xml
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: go-live-report
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
- - go-live-report/**
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: 'go-live-report/junit.xml'
210
- archiveArtifacts artifacts: 'go-live-report/**', allowEmptyArchive: true
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;
@@ -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 { CONFIG_FILE, ENV_FILE, loadConfig } from '../core/config.mjs';
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: ${CONFIG_FILE}`);
46
- else warn(`No ${CONFIG_FILE}: the defaults are used (run frontend-go-live-check init to create one)`);
47
- if (envFile) ok(`Test passwords file: ${ENV_FILE}`);
48
- if (missingSecrets.length) bad(`Not set: ${missingSecrets.join(', ')} (put them in ${ENV_FILE}, or the environment)`);
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('No API file: put your Postman collection at api/postman_collection.json (or OpenAPI at api/openapi.json) to add the API tests');
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('No .gitignore: make sure go-live-check.env is never committed');
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
  }