laxy-verify 1.1.14 → 1.1.16
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/README.md +174 -138
- package/dist/cli.js +88 -45
- package/dist/e2e.d.ts +3 -1
- package/dist/e2e.js +39 -1
- package/dist/entitlement.d.ts +3 -0
- package/dist/entitlement.js +53 -0
- package/dist/init.js +30 -30
- package/dist/lighthouse.js +32 -32
- package/dist/multi-viewport.js +43 -43
- package/dist/report-markdown.d.ts +39 -0
- package/dist/report-markdown.js +394 -0
- package/dist/serve.js +21 -10
- package/dist/verification-core/report.js +34 -6
- package/dist/verification-core/types.d.ts +3 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,171 +1,207 @@
|
|
|
1
|
-
# laxy-verify
|
|
2
|
-
|
|
3
|
-
CLI verification for frontend apps.
|
|
4
|
-
|
|
5
|
-
`laxy-verify` runs production build checks, Lighthouse, tiered verify E2E, and plan-gated verification features for Free, Pro, and Pro+ accounts.
|
|
6
|
-
It is designed around three user questions:
|
|
7
|
-
|
|
8
|
-
- Free: "Is this likely to break right now?"
|
|
9
|
-
- Pro: "Is this strong enough to send to a client?"
|
|
10
|
-
- Pro+: "Can I call this release-ready with confidence?"
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
npx laxy-verify --init --run
|
|
1
|
+
# laxy-verify
|
|
2
|
+
|
|
3
|
+
CLI verification for frontend apps.
|
|
4
|
+
|
|
5
|
+
`laxy-verify` runs production build checks, Lighthouse, tiered verify E2E, and plan-gated verification features for Free, Pro, and Pro+ accounts.
|
|
6
|
+
It is designed around three user questions:
|
|
7
|
+
|
|
8
|
+
- Free: "Is this likely to break right now?"
|
|
9
|
+
- Pro: "Is this strong enough to send to a client?"
|
|
10
|
+
- Pro+: "Can I call this release-ready with confidence?"
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx laxy-verify --init --run
|
|
14
14
|
npx laxy-verify .
|
|
15
|
+
npx laxy-verify . --plan-override pro
|
|
15
16
|
npx laxy-verify login
|
|
16
17
|
npx laxy-verify whoami
|
|
17
18
|
npx laxy-verify --help
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Quick Start
|
|
21
|
-
|
|
22
|
-
### 1. Initialize
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
cd your-project
|
|
26
|
-
npx laxy-verify --init
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
This generates `.laxy.yml` and a GitHub Actions workflow.
|
|
30
|
-
|
|
31
|
-
### 2. Run locally
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npx laxy-verify .
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### 3. Add to CI
|
|
38
|
-
|
|
39
|
-
Commit the generated workflow. Each PR gets a verification run, grade output, and optional GitHub reporting.
|
|
40
|
-
|
|
41
|
-
## Verification Tiers
|
|
42
|
-
|
|
43
|
-
| Plan | Question it answers |
|
|
44
|
-
|------|---------------------|
|
|
45
|
-
| Free | Is this likely to break right now? |
|
|
46
|
-
| Pro | Is this strong enough to send to a client? |
|
|
47
|
-
| Pro+ | Can I call this release-ready with confidence? |
|
|
48
|
-
|
|
49
|
-
## Grades
|
|
50
|
-
|
|
51
|
-
| Grade | Meaning |
|
|
52
|
-
|-------|---------|
|
|
53
|
-
| Gold | Build passed + E2E passed + Lighthouse passed + Pro+ viewport evidence passed |
|
|
54
|
-
| Silver | Build passed + E2E passed |
|
|
55
|
-
| Bronze | Build passed |
|
|
56
|
-
| Unverified | Build failed |
|
|
57
|
-
|
|
58
|
-
## Paid Features
|
|
59
|
-
|
|
60
|
-
Log in with your Laxy account to unlock paid plan features.
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
npx laxy-verify login
|
|
64
|
-
npx laxy-verify whoami
|
|
65
|
-
npx laxy-verify logout
|
|
66
|
-
```
|
|
67
|
-
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### 1. Initialize
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd your-project
|
|
27
|
+
npx laxy-verify --init
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This generates `.laxy.yml` and a GitHub Actions workflow.
|
|
31
|
+
|
|
32
|
+
### 2. Run locally
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx laxy-verify .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 3. Add to CI
|
|
39
|
+
|
|
40
|
+
Commit the generated workflow. Each PR gets a verification run, grade output, and optional GitHub reporting.
|
|
41
|
+
|
|
42
|
+
## Verification Tiers
|
|
43
|
+
|
|
44
|
+
| Plan | Question it answers |
|
|
45
|
+
|------|---------------------|
|
|
46
|
+
| Free | Is this likely to break right now? |
|
|
47
|
+
| Pro | Is this strong enough to send to a client? |
|
|
48
|
+
| Pro+ | Can I call this release-ready with confidence? |
|
|
49
|
+
|
|
50
|
+
## Grades
|
|
51
|
+
|
|
52
|
+
| Grade | Meaning |
|
|
53
|
+
|-------|---------|
|
|
54
|
+
| Gold | Build passed + E2E passed + Lighthouse passed + Pro+ viewport evidence passed |
|
|
55
|
+
| Silver | Build passed + E2E passed |
|
|
56
|
+
| Bronze | Build passed |
|
|
57
|
+
| Unverified | Build failed |
|
|
58
|
+
|
|
59
|
+
## Paid Features
|
|
60
|
+
|
|
61
|
+
Log in with your Laxy account to unlock paid plan features.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx laxy-verify login
|
|
65
|
+
npx laxy-verify whoami
|
|
66
|
+
npx laxy-verify logout
|
|
67
|
+
```
|
|
68
|
+
|
|
68
69
|
| Feature | Free | Pro | Pro+ |
|
|
69
70
|
|---------|------|-----|------|
|
|
70
71
|
| Build verification | Yes | Yes | Yes |
|
|
71
72
|
| Lighthouse | 1 run | 3 runs | 3 runs |
|
|
72
73
|
| Verify E2E | Smoke | Deeper client-send checks | Deeper client-send checks |
|
|
73
74
|
| Detailed report view | No | Yes | Yes |
|
|
74
|
-
|
|
|
75
|
+
| `laxy-verify-report.md` export | No | Yes | Yes |
|
|
75
76
|
| Multi-viewport verification | No | No | Yes |
|
|
76
77
|
| Visual diff | No | No | Yes |
|
|
77
78
|
| Failure analysis signals | No | No | Yes |
|
|
78
|
-
|
|
79
|
-
Pro is for delivery verification.
|
|
80
|
-
Pro+ is for release-confidence verification with extra evidence before you say "ship it."
|
|
81
|
-
|
|
82
|
-
For CI, set `LAXY_TOKEN` instead of using interactive login.
|
|
83
|
-
|
|
84
|
-
```yaml
|
|
85
|
-
env:
|
|
86
|
-
LAXY_TOKEN: ${{ secrets.LAXY_TOKEN }}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Configuration
|
|
90
|
-
|
|
91
|
-
All fields are optional in `.laxy.yml`.
|
|
92
|
-
|
|
93
|
-
```yaml
|
|
94
|
-
framework: "auto"
|
|
95
|
-
build_command: ""
|
|
96
|
-
dev_command: ""
|
|
97
|
-
package_manager: "auto"
|
|
98
|
-
port: 3000
|
|
99
|
-
build_timeout: 300
|
|
100
|
-
dev_timeout: 60
|
|
101
|
-
lighthouse_runs: 1
|
|
102
|
-
|
|
103
|
-
thresholds:
|
|
104
|
-
performance: 70
|
|
105
|
-
accessibility: 85
|
|
106
|
-
seo: 80
|
|
107
|
-
best_practices: 80
|
|
108
|
-
|
|
109
|
-
fail_on: "bronze"
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## CLI Options
|
|
113
|
-
|
|
114
|
-
```text
|
|
115
|
-
npx laxy-verify [project-dir]
|
|
116
|
-
|
|
117
|
-
Options:
|
|
118
|
-
--format console|json
|
|
119
|
-
--ci
|
|
120
|
-
--config <path>
|
|
79
|
+
|
|
80
|
+
Pro is for delivery verification.
|
|
81
|
+
Pro+ is for release-confidence verification with extra evidence before you say "ship it."
|
|
82
|
+
|
|
83
|
+
For CI, set `LAXY_TOKEN` instead of using interactive login.
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
env:
|
|
87
|
+
LAXY_TOKEN: ${{ secrets.LAXY_TOKEN }}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Configuration
|
|
91
|
+
|
|
92
|
+
All fields are optional in `.laxy.yml`.
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
framework: "auto"
|
|
96
|
+
build_command: ""
|
|
97
|
+
dev_command: ""
|
|
98
|
+
package_manager: "auto"
|
|
99
|
+
port: 3000
|
|
100
|
+
build_timeout: 300
|
|
101
|
+
dev_timeout: 60
|
|
102
|
+
lighthouse_runs: 1
|
|
103
|
+
|
|
104
|
+
thresholds:
|
|
105
|
+
performance: 70
|
|
106
|
+
accessibility: 85
|
|
107
|
+
seo: 80
|
|
108
|
+
best_practices: 80
|
|
109
|
+
|
|
110
|
+
fail_on: "bronze"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## CLI Options
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
npx laxy-verify [project-dir]
|
|
117
|
+
|
|
118
|
+
Options:
|
|
119
|
+
--format console|json
|
|
120
|
+
--ci
|
|
121
|
+
--config <path>
|
|
121
122
|
--fail-on unverified|bronze|silver|gold
|
|
122
123
|
--skip-lighthouse
|
|
124
|
+
--plan-override free|pro|pro_plus
|
|
123
125
|
--badge
|
|
124
126
|
--init
|
|
125
127
|
--multi-viewport
|
|
126
128
|
--help
|
|
127
|
-
|
|
129
|
+
|
|
128
130
|
Subcommands:
|
|
129
131
|
login [email]
|
|
130
132
|
logout
|
|
131
133
|
whoami
|
|
132
134
|
```
|
|
133
135
|
|
|
134
|
-
|
|
136
|
+
`--plan-override` is for downgrade testing only.
|
|
137
|
+
Example: if your account is Pro+, you can run `--plan-override pro` or `--plan-override free` to verify the lower-tier behavior without changing your subscription.
|
|
138
|
+
It will reject upgrades above your real entitlement.
|
|
139
|
+
|
|
140
|
+
## Result Files
|
|
135
141
|
|
|
136
142
|
Each run writes `.laxy-result.json`.
|
|
137
143
|
|
|
144
|
+
Paid plans also write a readable markdown summary to `laxy-verify-report.md`.
|
|
145
|
+
|
|
146
|
+
- `Pro`: blocker-focused delivery report
|
|
147
|
+
- `Pro+`: release-readiness report with viewport and visual evidence
|
|
148
|
+
|
|
149
|
+
Exit behavior follows the verification verdict, not just the legacy grade.
|
|
150
|
+
|
|
151
|
+
- `build-failed` -> exit 1
|
|
152
|
+
- `hold` -> exit 1
|
|
153
|
+
- `Pro+ investigate` -> exit 1
|
|
154
|
+
- plain lower-tier pass states can still exit 0
|
|
155
|
+
|
|
138
156
|
```json
|
|
139
157
|
{
|
|
140
|
-
"grade": "Gold",
|
|
141
|
-
"timestamp": "2026-04-09T09:00:00Z",
|
|
142
|
-
"build": { "success": true, "durationMs": 12000, "errors": [] },
|
|
143
|
-
"e2e": { "passed": 5, "failed": 0, "total": 5, "results": [] },
|
|
144
|
-
"lighthouse": { "performance": 82, "accessibility": 94, "seo": 90, "bestPractices": 92, "runs": 3 },
|
|
145
|
-
"multiViewport": {
|
|
146
|
-
"allPassed": true,
|
|
147
|
-
"summary": "Desktop, tablet, and mobile checks passed."
|
|
148
|
-
},
|
|
149
|
-
"visualDiff": {
|
|
150
|
-
"verdict": "pass",
|
|
151
|
-
"differencePercentage": 0
|
|
152
|
-
},
|
|
153
|
-
"verification": {
|
|
154
|
-
"tier": "pro_plus",
|
|
155
|
-
"report": { "verdict": "release-ready" }
|
|
156
|
-
},
|
|
157
|
-
"exitCode": 0,
|
|
158
|
-
"_plan": "pro_plus"
|
|
158
|
+
"grade": "Gold",
|
|
159
|
+
"timestamp": "2026-04-09T09:00:00Z",
|
|
160
|
+
"build": { "success": true, "durationMs": 12000, "errors": [] },
|
|
161
|
+
"e2e": { "passed": 5, "failed": 0, "total": 5, "results": [] },
|
|
162
|
+
"lighthouse": { "performance": 82, "accessibility": 94, "seo": 90, "bestPractices": 92, "runs": 3 },
|
|
163
|
+
"multiViewport": {
|
|
164
|
+
"allPassed": true,
|
|
165
|
+
"summary": "Desktop, tablet, and mobile checks passed."
|
|
166
|
+
},
|
|
167
|
+
"visualDiff": {
|
|
168
|
+
"verdict": "pass",
|
|
169
|
+
"differencePercentage": 0
|
|
170
|
+
},
|
|
171
|
+
"verification": {
|
|
172
|
+
"tier": "pro_plus",
|
|
173
|
+
"report": { "verdict": "release-ready" }
|
|
174
|
+
},
|
|
175
|
+
"exitCode": 0,
|
|
176
|
+
"_plan": "pro_plus"
|
|
159
177
|
}
|
|
160
178
|
```
|
|
161
179
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
180
|
+
### `laxy-verify-report.md`
|
|
181
|
+
|
|
182
|
+
For Pro and Pro+ runs, the markdown report is designed to be easy to read and easy to paste into an AI coding tool.
|
|
183
|
+
|
|
184
|
+
It includes:
|
|
185
|
+
|
|
186
|
+
- the main decision in plain English
|
|
187
|
+
- what passed
|
|
188
|
+
- blockers and warnings
|
|
189
|
+
- exact verification evidence
|
|
190
|
+
- failed E2E scenarios
|
|
191
|
+
- a `Copy For AI` section you can paste directly into Codex, Cursor, Claude, or ChatGPT
|
|
192
|
+
|
|
193
|
+
## Regression Fixtures
|
|
194
|
+
|
|
195
|
+
The repo also includes dedicated regression fixtures under `.qa-regression-fixtures/`.
|
|
196
|
+
They intentionally break build, navigation, coverage, performance, viewport behavior, and visual stability so the verifier can be tested against known failure modes.
|
|
197
|
+
|
|
198
|
+
## Limitations
|
|
199
|
+
|
|
200
|
+
- Monorepos require targeting the app subdirectory explicitly.
|
|
201
|
+
- Dev-server-based Lighthouse can differ from production hosting.
|
|
202
|
+
- Pro+ visual diff and viewport checks increase runtime.
|
|
203
|
+
- Local verification is most stable on current LTS Node releases.
|
|
204
|
+
|
|
205
|
+
## License
|
|
206
|
+
|
|
207
|
+
MIT
|
package/dist/cli.js
CHANGED
|
@@ -53,9 +53,19 @@ const auth_js_1 = require("./auth.js");
|
|
|
53
53
|
const entitlement_js_1 = require("./entitlement.js");
|
|
54
54
|
const multi_viewport_js_1 = require("./multi-viewport.js");
|
|
55
55
|
const e2e_js_1 = require("./e2e.js");
|
|
56
|
+
const report_markdown_js_1 = require("./report-markdown.js");
|
|
56
57
|
const visual_diff_js_1 = require("./visual-diff.js");
|
|
57
58
|
const index_js_1 = require("./verification-core/index.js");
|
|
58
59
|
const package_json_1 = __importDefault(require("../package.json"));
|
|
60
|
+
function shouldFailVerificationResult(report, failOn) {
|
|
61
|
+
if (failOn === "unverified")
|
|
62
|
+
return false;
|
|
63
|
+
if (report.verdict === "build-failed" || report.verdict === "hold")
|
|
64
|
+
return true;
|
|
65
|
+
if (report.tier === "pro_plus" && report.verdict === "investigate")
|
|
66
|
+
return true;
|
|
67
|
+
return (0, grade_js_1.isWorseOrEqual)(report.grade, failOn);
|
|
68
|
+
}
|
|
59
69
|
function exitGracefully(code) {
|
|
60
70
|
if (process.platform === "win32") {
|
|
61
71
|
setTimeout(() => process.exit(code), 100);
|
|
@@ -116,6 +126,7 @@ function parseArgs() {
|
|
|
116
126
|
initRun: flags.init !== undefined && flags.run !== undefined,
|
|
117
127
|
multiViewport: flags["multi-viewport"] !== undefined,
|
|
118
128
|
failureAnalysis: flags["failure-analysis"] !== undefined,
|
|
129
|
+
planOverride: flags["plan-override"],
|
|
119
130
|
help: flags.help !== undefined || flags.h !== undefined,
|
|
120
131
|
};
|
|
121
132
|
}
|
|
@@ -209,6 +220,9 @@ function consoleOutput(result) {
|
|
|
209
220
|
console.log(` Status check: ${result.github.grade}`);
|
|
210
221
|
}
|
|
211
222
|
console.log(" Result: .laxy-result.json");
|
|
223
|
+
if (result.markdownReportPath) {
|
|
224
|
+
console.log(` Report: ${path.basename(result.markdownReportPath)}`);
|
|
225
|
+
}
|
|
212
226
|
console.log(` Exit code: ${result.exitCode}`);
|
|
213
227
|
if ((result.grade === "Silver" || result.grade === "Bronze") && (!result._plan || result._plan === "free")) {
|
|
214
228
|
console.log("\n Unlock deeper verification and Gold-grade confidence with Pro or Pro+:");
|
|
@@ -218,43 +232,44 @@ function consoleOutput(result) {
|
|
|
218
232
|
async function run() {
|
|
219
233
|
const args = parseArgs();
|
|
220
234
|
if (args.help) {
|
|
221
|
-
console.log(`
|
|
222
|
-
laxy-verify v${package_json_1.default.version}
|
|
223
|
-
Frontend quality gate: build + Lighthouse verification
|
|
224
|
-
|
|
225
|
-
Usage:
|
|
226
|
-
npx laxy-verify [project-dir] [options]
|
|
227
|
-
npx laxy-verify <subcommand>
|
|
228
|
-
|
|
229
|
-
Subcommands:
|
|
230
|
-
login [email] Log in to unlock Pro/Pro+ features
|
|
231
|
-
logout Remove saved credentials
|
|
232
|
-
whoami Show current login status
|
|
233
|
-
|
|
234
|
-
Options:
|
|
235
|
-
--init Generate .laxy.yml + GitHub workflow file
|
|
236
|
-
--init --run Generate config and immediately run verification
|
|
237
|
-
--format console | json (default: console)
|
|
238
|
-
--ci CI mode: -10 Performance threshold, runs=3
|
|
239
|
-
--config <path> Path to .laxy.yml
|
|
235
|
+
console.log(`
|
|
236
|
+
laxy-verify v${package_json_1.default.version}
|
|
237
|
+
Frontend quality gate: build + Lighthouse verification
|
|
238
|
+
|
|
239
|
+
Usage:
|
|
240
|
+
npx laxy-verify [project-dir] [options]
|
|
241
|
+
npx laxy-verify <subcommand>
|
|
242
|
+
|
|
243
|
+
Subcommands:
|
|
244
|
+
login [email] Log in to unlock Pro/Pro+ features
|
|
245
|
+
logout Remove saved credentials
|
|
246
|
+
whoami Show current login status
|
|
247
|
+
|
|
248
|
+
Options:
|
|
249
|
+
--init Generate .laxy.yml + GitHub workflow file
|
|
250
|
+
--init --run Generate config and immediately run verification
|
|
251
|
+
--format console | json (default: console)
|
|
252
|
+
--ci CI mode: -10 Performance threshold, runs=3
|
|
253
|
+
--config <path> Path to .laxy.yml
|
|
240
254
|
--fail-on unverified | bronze | silver | gold
|
|
241
255
|
--skip-lighthouse Skip Lighthouse but still run build and E2E
|
|
256
|
+
--plan-override free | pro | pro_plus (downgrade testing only)
|
|
242
257
|
--multi-viewport Pro+: Lighthouse on desktop/tablet/mobile
|
|
243
|
-
--badge Print shields.io badge markdown
|
|
244
|
-
--help Show this help
|
|
245
|
-
|
|
246
|
-
Exit codes:
|
|
247
|
-
0 Grade meets or exceeds fail_on threshold
|
|
248
|
-
1 Grade worse than fail_on, or build failed
|
|
249
|
-
2 Configuration error
|
|
250
|
-
|
|
251
|
-
Examples:
|
|
252
|
-
npx laxy-verify --init --run # Setup + first verification
|
|
253
|
-
npx laxy-verify . # Run in current directory
|
|
254
|
-
npx laxy-verify . --ci # CI mode
|
|
255
|
-
npx laxy-verify . --fail-on silver # Require Silver or better
|
|
256
|
-
|
|
257
|
-
Docs: https://github.com/psungmin24/laxy-verify
|
|
258
|
+
--badge Print shields.io badge markdown
|
|
259
|
+
--help Show this help
|
|
260
|
+
|
|
261
|
+
Exit codes:
|
|
262
|
+
0 Grade meets or exceeds fail_on threshold
|
|
263
|
+
1 Grade worse than fail_on, or build failed
|
|
264
|
+
2 Configuration error
|
|
265
|
+
|
|
266
|
+
Examples:
|
|
267
|
+
npx laxy-verify --init --run # Setup + first verification
|
|
268
|
+
npx laxy-verify . # Run in current directory
|
|
269
|
+
npx laxy-verify . --ci # CI mode
|
|
270
|
+
npx laxy-verify . --fail-on silver # Require Silver or better
|
|
271
|
+
|
|
272
|
+
Docs: https://github.com/psungmin24/laxy-verify
|
|
258
273
|
`);
|
|
259
274
|
exitGracefully(0);
|
|
260
275
|
return;
|
|
@@ -346,6 +361,7 @@ async function run() {
|
|
|
346
361
|
}
|
|
347
362
|
let scores;
|
|
348
363
|
let lighthouseResult = null;
|
|
364
|
+
let lighthouseErrorCount = 0;
|
|
349
365
|
const adjustedThresholds = {
|
|
350
366
|
performance: config.ciMode ? config.thresholds.performance - 10 : config.thresholds.performance,
|
|
351
367
|
accessibility: config.thresholds.accessibility,
|
|
@@ -369,12 +385,25 @@ async function run() {
|
|
|
369
385
|
failure_analysis: false,
|
|
370
386
|
fast_lane: false,
|
|
371
387
|
};
|
|
372
|
-
|
|
388
|
+
let effectiveFeatures = features;
|
|
389
|
+
if (args.planOverride) {
|
|
390
|
+
try {
|
|
391
|
+
effectiveFeatures = (0, entitlement_js_1.applyPlanOverride)(features, args.planOverride);
|
|
392
|
+
console.log(` Plan override: ${(0, entitlement_js_1.normalizePlan)(features.plan)} -> ${effectiveFeatures.plan} (testing lower-tier verification behavior)`);
|
|
393
|
+
}
|
|
394
|
+
catch (overrideErr) {
|
|
395
|
+
console.error(`Plan override error: ${overrideErr instanceof Error ? overrideErr.message : String(overrideErr)}`);
|
|
396
|
+
exitGracefully(2);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (effectiveFeatures.lighthouse_runs_3 && config.lighthouse_runs < 3) {
|
|
373
401
|
config = { ...config, lighthouse_runs: 3 };
|
|
374
402
|
}
|
|
375
403
|
let multiViewportScores = null;
|
|
376
404
|
let allViewportsOk = false;
|
|
377
405
|
let e2eResult;
|
|
406
|
+
let e2eCoverageGaps = [];
|
|
378
407
|
let visualDiffResult = null;
|
|
379
408
|
if (buildResult.success) {
|
|
380
409
|
let servePid;
|
|
@@ -382,10 +411,11 @@ async function run() {
|
|
|
382
411
|
const serve = await (0, serve_js_1.startDevServer)(devCmd, port, config.dev_timeout, args.projectDir);
|
|
383
412
|
servePid = serve.pid;
|
|
384
413
|
const verifyUrl = `http://127.0.0.1:${port}/`;
|
|
385
|
-
const verificationTier = (0, index_js_1.planToVerificationTier)(
|
|
414
|
+
const verificationTier = (0, index_js_1.planToVerificationTier)(effectiveFeatures.plan);
|
|
386
415
|
if (!args.skipLighthouse) {
|
|
387
416
|
try {
|
|
388
417
|
const lhResult = await (0, lighthouse_js_1.runLighthouse)(port, config.lighthouse_runs);
|
|
418
|
+
lighthouseErrorCount = lhResult.errors.length;
|
|
389
419
|
scores = lhResult.scores ?? undefined;
|
|
390
420
|
if (scores) {
|
|
391
421
|
lighthouseResult = {
|
|
@@ -401,10 +431,10 @@ async function run() {
|
|
|
401
431
|
console.error(`Lighthouse error: ${lhErr instanceof Error ? lhErr.message : String(lhErr)}`);
|
|
402
432
|
}
|
|
403
433
|
}
|
|
404
|
-
if (!args.skipLighthouse && args.multiViewport && !
|
|
434
|
+
if (!args.skipLighthouse && args.multiViewport && !effectiveFeatures.multi_viewport) {
|
|
405
435
|
console.log("\n Note: --multi-viewport requires Pro+. Run laxy-verify login with a paid account to unlock it.");
|
|
406
436
|
}
|
|
407
|
-
else if (!args.skipLighthouse &&
|
|
437
|
+
else if (!args.skipLighthouse && effectiveFeatures.multi_viewport) {
|
|
408
438
|
try {
|
|
409
439
|
multiViewportScores = await (0, multi_viewport_js_1.runMultiViewportLighthouse)(port);
|
|
410
440
|
(0, multi_viewport_js_1.printMultiViewportResults)(multiViewportScores, adjustedThresholds);
|
|
@@ -422,6 +452,10 @@ async function run() {
|
|
|
422
452
|
total: e2eRuns.results.length,
|
|
423
453
|
results: e2eRuns.results,
|
|
424
454
|
};
|
|
455
|
+
e2eCoverageGaps = e2eRuns.coverageGaps;
|
|
456
|
+
if (e2eRuns.coverageGaps.length > 0) {
|
|
457
|
+
console.error(`E2E coverage warning: ${e2eRuns.coverageGaps.join(" ")}`);
|
|
458
|
+
}
|
|
425
459
|
}
|
|
426
460
|
catch (e2eErr) {
|
|
427
461
|
console.error(`E2E error: ${e2eErr instanceof Error ? e2eErr.message : String(e2eErr)}`);
|
|
@@ -444,16 +478,18 @@ async function run() {
|
|
|
444
478
|
}
|
|
445
479
|
}
|
|
446
480
|
}
|
|
447
|
-
const verificationTier = (0, index_js_1.planToVerificationTier)(
|
|
481
|
+
const verificationTier = (0, index_js_1.planToVerificationTier)(effectiveFeatures.plan);
|
|
448
482
|
const viewportSummary = summarizeViewportIssues(multiViewportScores, adjustedThresholds);
|
|
449
483
|
const failureEvidence = [
|
|
450
484
|
...buildResult.errors.slice(0, 3).map((error) => `Build: ${error}`),
|
|
485
|
+
...(lighthouseErrorCount > 0 ? [`Lighthouse: ${lighthouseErrorCount} run error(s) were recorded during collection.`] : []),
|
|
451
486
|
...(e2eResult
|
|
452
487
|
? e2eResult.results
|
|
453
488
|
.filter((scenario) => !scenario.passed)
|
|
454
489
|
.slice(0, 2)
|
|
455
490
|
.map((scenario) => `E2E: ${scenario.name}${scenario.error ? ` - ${scenario.error}` : ""}`)
|
|
456
491
|
: []),
|
|
492
|
+
...e2eCoverageGaps.slice(0, 2).map((gap) => `E2E coverage: ${gap}`),
|
|
457
493
|
...(viewportSummary.count > 0 && viewportSummary.summary ? [`Viewport: ${viewportSummary.summary}`] : []),
|
|
458
494
|
...(visualDiffResult
|
|
459
495
|
? [
|
|
@@ -468,7 +504,9 @@ async function run() {
|
|
|
468
504
|
buildErrors: buildResult.errors,
|
|
469
505
|
e2ePassed: e2eResult?.passed,
|
|
470
506
|
e2eTotal: e2eResult?.total,
|
|
507
|
+
e2eCoverageGaps,
|
|
471
508
|
lighthouseSkipped: args.skipLighthouse,
|
|
509
|
+
lighthouseErrorCount,
|
|
472
510
|
viewportIssues: multiViewportScores ? viewportSummary.count : undefined,
|
|
473
511
|
multiViewportPassed: multiViewportScores ? allViewportsOk : undefined,
|
|
474
512
|
multiViewportSummary: multiViewportScores ? viewportSummary.summary : undefined,
|
|
@@ -483,11 +521,7 @@ async function run() {
|
|
|
483
521
|
});
|
|
484
522
|
const verificationView = (0, index_js_1.getTierVerificationView)(verificationReport);
|
|
485
523
|
const unifiedGrade = verificationReport.grade;
|
|
486
|
-
const exitCode = config.fail_on
|
|
487
|
-
? 0
|
|
488
|
-
: (0, grade_js_1.isWorseOrEqual)(unifiedGrade, config.fail_on)
|
|
489
|
-
? 1
|
|
490
|
-
: 0;
|
|
524
|
+
const exitCode = shouldFailVerificationResult(verificationReport, config.fail_on) ? 1 : 0;
|
|
491
525
|
const resultObj = {
|
|
492
526
|
grade: unifiedGrade.charAt(0).toUpperCase() + unifiedGrade.slice(1),
|
|
493
527
|
timestamp: new Date().toISOString(),
|
|
@@ -504,13 +538,22 @@ async function run() {
|
|
|
504
538
|
framework: detected.framework,
|
|
505
539
|
exitCode,
|
|
506
540
|
config_fail_on: config.fail_on,
|
|
507
|
-
_plan:
|
|
541
|
+
_plan: effectiveFeatures.plan,
|
|
508
542
|
verification: {
|
|
509
543
|
tier: verificationTier,
|
|
510
544
|
report: verificationReport,
|
|
511
545
|
view: verificationView,
|
|
512
546
|
},
|
|
513
547
|
};
|
|
548
|
+
const markdownReportPath = (0, report_markdown_js_1.getMarkdownReportPath)(args.projectDir);
|
|
549
|
+
if ((0, report_markdown_js_1.shouldWriteMarkdownReport)(resultObj)) {
|
|
550
|
+
const markdownReport = (0, report_markdown_js_1.buildMarkdownReport)(args.projectDir, resultObj);
|
|
551
|
+
fs.writeFileSync(markdownReportPath, markdownReport, "utf-8");
|
|
552
|
+
resultObj.markdownReportPath = markdownReportPath;
|
|
553
|
+
}
|
|
554
|
+
else if (fs.existsSync(markdownReportPath)) {
|
|
555
|
+
fs.rmSync(markdownReportPath, { force: true });
|
|
556
|
+
}
|
|
514
557
|
const inGitHubActions = !!process.env.GITHUB_ACTIONS;
|
|
515
558
|
if (inGitHubActions) {
|
|
516
559
|
try {
|
package/dist/e2e.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { VerificationTier } from "./verification-core/types.js";
|
|
2
2
|
export interface E2EStep {
|
|
3
|
-
type: "click" | "fill" | "check_visible" | "wait" | "scroll" | "clear_fill" | "check_validation";
|
|
3
|
+
type: "click" | "fill" | "check_visible" | "wait" | "scroll" | "clear_fill" | "check_validation" | "check_healthy_page";
|
|
4
4
|
selector?: string;
|
|
5
5
|
value?: string;
|
|
6
6
|
duration?: number;
|
|
@@ -26,11 +26,13 @@ interface DomSnapshot {
|
|
|
26
26
|
structures: string[];
|
|
27
27
|
}
|
|
28
28
|
export declare function isNavigableInternalHref(href: string): boolean;
|
|
29
|
+
export declare function getVerificationCoverageGaps(scenarios: E2EScenario[], tier: VerificationTier): string[];
|
|
29
30
|
export declare function buildVerifyScenarios(snapshot: DomSnapshot, tier: VerificationTier): E2EScenario[];
|
|
30
31
|
export declare function runVerifyE2E(url: string, tier: VerificationTier): Promise<{
|
|
31
32
|
scenarios: E2EScenario[];
|
|
32
33
|
results: E2EScenarioResult[];
|
|
33
34
|
passed: number;
|
|
34
35
|
failed: number;
|
|
36
|
+
coverageGaps: string[];
|
|
35
37
|
}>;
|
|
36
38
|
export {};
|