@trylayout/qa 0.1.3 → 0.1.5
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 +22 -10
- package/build/cli/layoutQa.js +1 -1
- package/build/report.js +119 -51
- package/build/runner.js +19 -19
- package/docs/assets/layout-qa-report.png +0 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Layout QA
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@trylayout/qa)
|
|
4
|
+
[](https://github.com/Layout-App/layout-qa/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
3
7
|
Layout QA is a local browser QA protocol and runner for AI-built frontends. It runs deterministic flows against a local or preview URL, captures screenshots at meaningful checkpoints, checks browser health, and writes a static HTML report.
|
|
4
8
|
|
|
5
9
|
The core loop is intentionally local:
|
|
@@ -12,6 +16,10 @@ npx @trylayout/qa run --target-url http://localhost:5173 --scenario happy_path -
|
|
|
12
16
|
|
|
13
17
|
No account, upload, hosted service, or external docs are required.
|
|
14
18
|
|
|
19
|
+
## Example Report
|
|
20
|
+
|
|
21
|
+

|
|
22
|
+
|
|
15
23
|
Package names:
|
|
16
24
|
|
|
17
25
|
- Canonical npm package: `@trylayout/qa`
|
|
@@ -29,8 +37,8 @@ npx layout-qa run --target-url http://localhost:5173 --scenario happy_path --ope
|
|
|
29
37
|
|
|
30
38
|
Frontend agents can move faster when they have a visual feedback loop they can run themselves. Layout gives the agent a small protocol:
|
|
31
39
|
|
|
32
|
-
- Wire deterministic API/auth
|
|
33
|
-
- Switch
|
|
40
|
+
- Wire deterministic API/auth responses behind a local env flag such as `VITE_LAYOUT_QA_MOCKS=1`.
|
|
41
|
+
- Switch response states with `localStorage["layout.qa.scenario"]`.
|
|
34
42
|
- Declare high-value browser flows in `.layout/qa-flows.json`.
|
|
35
43
|
- Run the CLI locally and inspect the generated screenshots/report.
|
|
36
44
|
|
|
@@ -71,7 +79,7 @@ Create a starter flow manifest:
|
|
|
71
79
|
npx @trylayout/qa init
|
|
72
80
|
```
|
|
73
81
|
|
|
74
|
-
Start your app with whatever
|
|
82
|
+
Start your app with whatever local QA flag your project uses:
|
|
75
83
|
|
|
76
84
|
```bash
|
|
77
85
|
VITE_LAYOUT_QA_MOCKS=1 npm run dev
|
|
@@ -111,7 +119,7 @@ Options:
|
|
|
111
119
|
|
|
112
120
|
```text
|
|
113
121
|
--target-url <url> URL of the running frontend to test.
|
|
114
|
-
--scenario <name>
|
|
122
|
+
--scenario <name> Scenario to activate. Defaults to happy_path.
|
|
115
123
|
--flows <path> Flow manifest path. Defaults to .layout/qa-flows.json.
|
|
116
124
|
--out <path> Artifact directory. Defaults to .layout/runs.
|
|
117
125
|
--viewport <value> Viewport preset or size. Use desktop, tablet, mobile, or WIDTHxHEIGHT. Defaults to desktop.
|
|
@@ -241,7 +249,7 @@ Presets:
|
|
|
241
249
|
|
|
242
250
|
The selected viewport is written to `result.json`, shown in the HTML report, and included in the run directory name.
|
|
243
251
|
|
|
244
|
-
##
|
|
252
|
+
## Scenarios
|
|
245
253
|
|
|
246
254
|
Before the app loads, the runner sets:
|
|
247
255
|
|
|
@@ -250,7 +258,7 @@ localStorage.setItem("layout.qa.scenario", "<scenario>");
|
|
|
250
258
|
sessionStorage.setItem("layout.qa.runner", "1");
|
|
251
259
|
```
|
|
252
260
|
|
|
253
|
-
Your app can use `layout.qa.scenario` to switch deterministic
|
|
261
|
+
Your app can use `layout.qa.scenario` to switch deterministic API/auth response states:
|
|
254
262
|
|
|
255
263
|
- `happy_path`: normal populated data.
|
|
256
264
|
- `empty`: successful responses with empty states.
|
|
@@ -271,14 +279,14 @@ Create a local-only browser QA loop that an agent can run while changing fronten
|
|
|
271
279
|
Rules:
|
|
272
280
|
- Do not add a standalone mock server.
|
|
273
281
|
- Do not require a hosted Layout service.
|
|
274
|
-
- Keep all
|
|
275
|
-
- Gate
|
|
276
|
-
- Use localStorage["layout.qa.scenario"] to select at least happy_path, empty, and error
|
|
282
|
+
- Keep all deterministic response fixtures local to this app.
|
|
283
|
+
- Gate deterministic API/auth responses behind a local-only env flag such as VITE_LAYOUT_QA_MOCKS=1, NEXT_PUBLIC_LAYOUT_QA_MOCKS=1, or the framework-appropriate equivalent.
|
|
284
|
+
- Use localStorage["layout.qa.scenario"] to select at least happy_path, empty, and error response states.
|
|
277
285
|
- Hide any local QA switcher or debug controls when sessionStorage["layout.qa.runner"] === "1".
|
|
278
286
|
|
|
279
287
|
Implementation:
|
|
280
288
|
- Add deterministic API fixtures for the highest-value frontend route.
|
|
281
|
-
- If the app has a central auth/session abstraction, add a deterministic
|
|
289
|
+
- If the app has a central auth/session abstraction, add a deterministic QA user only when the Layout QA env flag is enabled.
|
|
282
290
|
- If auth is scattered or provider-SDK-only, leave a clear note in the PR/code comments and start with public or logged-out flows.
|
|
283
291
|
- Add .layout/qa-flows.json with one smoke flow for the most important page.
|
|
284
292
|
- Prefer visible text and stable selectors.
|
|
@@ -332,3 +340,7 @@ This package is intentionally small:
|
|
|
332
340
|
- It does not perform AI review by itself.
|
|
333
341
|
|
|
334
342
|
Those hosted/reporting layers can be added later without changing the local protocol.
|
|
343
|
+
|
|
344
|
+
## Feedback
|
|
345
|
+
|
|
346
|
+
Issues and examples are welcome in [GitHub Issues](https://github.com/Layout-App/layout-qa/issues). You can also reach me on X at [@tscepo](https://x.com/tscepo).
|
package/build/cli/layoutQa.js
CHANGED
|
@@ -26,7 +26,7 @@ Commands:
|
|
|
26
26
|
|
|
27
27
|
Options:
|
|
28
28
|
--target-url <url> URL of the running frontend to test.
|
|
29
|
-
--scenario <name>
|
|
29
|
+
--scenario <name> Scenario to activate. Defaults to happy_path.
|
|
30
30
|
--flows <path> Flow manifest path. Defaults to .layout/qa-flows.json.
|
|
31
31
|
--out <path> Artifact directory. Defaults to .layout/runs.
|
|
32
32
|
--viewport <value> Viewport preset or size. Use desktop, tablet, mobile, or WIDTHxHEIGHT. Defaults to desktop.
|
package/build/report.js
CHANGED
|
@@ -57,7 +57,7 @@ function renderStatusBadge(status) {
|
|
|
57
57
|
function renderCheckList(result) {
|
|
58
58
|
return result.checks
|
|
59
59
|
.map(check => `<li class="row">
|
|
60
|
-
<span class="status ${check.passed ? 'passed' : 'failed'}">${check.passed ? '
|
|
60
|
+
<span class="status ${check.passed ? 'passed' : 'failed'}">${check.passed ? 'Pass' : 'Fail'}</span>
|
|
61
61
|
<div>
|
|
62
62
|
<p class="row-title">${escapeHtml(check.label)}</p>
|
|
63
63
|
${check.detail
|
|
@@ -123,81 +123,149 @@ function renderReport(input) {
|
|
|
123
123
|
<style>
|
|
124
124
|
:root {
|
|
125
125
|
color-scheme: light;
|
|
126
|
-
--bg: #
|
|
127
|
-
--
|
|
128
|
-
--
|
|
129
|
-
--
|
|
130
|
-
--
|
|
131
|
-
--
|
|
132
|
-
--
|
|
133
|
-
--
|
|
134
|
-
--
|
|
135
|
-
--
|
|
136
|
-
--amber
|
|
126
|
+
--bg: #ffffff;
|
|
127
|
+
--surface: #f5f5f7;
|
|
128
|
+
--surface-strong: #fbfbfd;
|
|
129
|
+
--line: #d2d2d7;
|
|
130
|
+
--line-soft: #e8e8ed;
|
|
131
|
+
--text: #1d1d1f;
|
|
132
|
+
--muted: #6e6e73;
|
|
133
|
+
--blue: #06c;
|
|
134
|
+
--green: #248a3d;
|
|
135
|
+
--red: #d70015;
|
|
136
|
+
--amber: #b25000;
|
|
137
137
|
}
|
|
138
138
|
* { box-sizing: border-box; }
|
|
139
139
|
body {
|
|
140
140
|
margin: 0;
|
|
141
141
|
background: var(--bg);
|
|
142
142
|
color: var(--text);
|
|
143
|
-
font-family:
|
|
144
|
-
line-height: 1.
|
|
143
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
|
|
144
|
+
line-height: 1.47059;
|
|
145
|
+
-webkit-font-smoothing: antialiased;
|
|
146
|
+
text-rendering: optimizeLegibility;
|
|
147
|
+
}
|
|
148
|
+
main { max-width: 1180px; margin: 0 auto; padding: 56px 28px 72px; }
|
|
149
|
+
header.page {
|
|
150
|
+
display: flex;
|
|
151
|
+
justify-content: space-between;
|
|
152
|
+
gap: 32px;
|
|
153
|
+
align-items: flex-start;
|
|
154
|
+
border-bottom: 1px solid var(--line-soft);
|
|
155
|
+
padding-bottom: 36px;
|
|
145
156
|
}
|
|
146
|
-
main { max-width: 1120px; margin: 0 auto; padding: 32px 24px 56px; }
|
|
147
|
-
header.page { display: flex; justify-content: space-between; gap: 24px; align-items: flex-start; border-bottom: 1px solid var(--line); padding-bottom: 24px; }
|
|
148
157
|
h1, h2, h3, p { margin: 0; }
|
|
149
|
-
h1 {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
158
|
+
h1 {
|
|
159
|
+
font-size: clamp(2.75rem, 7vw, 5.5rem);
|
|
160
|
+
line-height: .95;
|
|
161
|
+
font-weight: 500;
|
|
162
|
+
letter-spacing: 0;
|
|
163
|
+
}
|
|
164
|
+
h2 { font-size: 1.375rem; line-height: 1.2; font-weight: 500; margin-bottom: 18px; }
|
|
165
|
+
h3 { font-size: 1.0625rem; line-height: 1.25; font-weight: 500; }
|
|
166
|
+
section { margin-top: 42px; }
|
|
167
|
+
a { color: var(--blue); text-decoration: none; }
|
|
168
|
+
a:hover { text-decoration: underline; }
|
|
169
|
+
.eyebrow {
|
|
170
|
+
color: var(--muted);
|
|
171
|
+
font-size: .8125rem;
|
|
172
|
+
font-weight: 500;
|
|
173
|
+
text-transform: uppercase;
|
|
174
|
+
letter-spacing: .04em;
|
|
175
|
+
margin-bottom: 10px;
|
|
176
|
+
}
|
|
177
|
+
.summary {
|
|
178
|
+
display: grid;
|
|
179
|
+
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
180
|
+
gap: 1px;
|
|
181
|
+
margin-top: 32px;
|
|
182
|
+
border: 1px solid var(--line-soft);
|
|
183
|
+
border-radius: 14px;
|
|
184
|
+
overflow: hidden;
|
|
185
|
+
background: var(--line-soft);
|
|
186
|
+
}
|
|
156
187
|
.metric, .panel, .step, .issue {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
188
|
+
background: var(--surface-strong);
|
|
189
|
+
}
|
|
190
|
+
.metric { padding: 18px 20px; min-width: 0; }
|
|
191
|
+
.metric dt { color: var(--muted); font-size: .8125rem; }
|
|
192
|
+
.metric dd { margin: 5px 0 0; font-weight: 500; overflow-wrap: anywhere; }
|
|
193
|
+
.panel {
|
|
194
|
+
padding: 22px;
|
|
195
|
+
border: 1px solid var(--line-soft);
|
|
196
|
+
border-radius: 14px;
|
|
197
|
+
}
|
|
198
|
+
.stack { display: grid; gap: 0; list-style: none; margin: 0; padding: 0; }
|
|
199
|
+
.row {
|
|
200
|
+
display: grid;
|
|
201
|
+
grid-template-columns: 64px minmax(0, 1fr);
|
|
202
|
+
gap: 16px;
|
|
203
|
+
padding: 14px 0;
|
|
204
|
+
border-top: 1px solid var(--line-soft);
|
|
160
205
|
}
|
|
161
|
-
.metric { padding: 14px; min-width: 0; }
|
|
162
|
-
.metric dt { color: var(--muted); font-size: .82rem; }
|
|
163
|
-
.metric dd { margin: 4px 0 0; font-weight: 650; overflow-wrap: anywhere; }
|
|
164
|
-
.panel { padding: 16px; }
|
|
165
|
-
.stack { display: grid; gap: 10px; list-style: none; margin: 0; padding: 0; }
|
|
166
|
-
.row { display: grid; grid-template-columns: 56px minmax(0, 1fr); gap: 12px; padding: 10px 0; border-top: 1px solid var(--line); }
|
|
167
206
|
.row:first-child { border-top: 0; padding-top: 0; }
|
|
168
207
|
.row:last-child { padding-bottom: 0; }
|
|
169
|
-
.row-title { font-weight:
|
|
170
|
-
.muted { color: var(--muted); font-size: .
|
|
171
|
-
.detail { margin-top:
|
|
208
|
+
.row-title { font-weight: 500; overflow-wrap: anywhere; }
|
|
209
|
+
.muted { color: var(--muted); font-size: .9375rem; overflow-wrap: anywhere; }
|
|
210
|
+
.detail { margin-top: 10px; color: #424245; overflow-wrap: anywhere; }
|
|
172
211
|
.break { word-break: break-all; }
|
|
173
|
-
.status { font-size: .
|
|
212
|
+
.status { font-size: .8125rem; font-weight: 500; padding-top: 2px; }
|
|
174
213
|
.status.passed { color: var(--green); }
|
|
175
214
|
.status.failed { color: var(--red); }
|
|
176
215
|
.badge {
|
|
177
216
|
display: inline-flex;
|
|
178
217
|
align-items: center;
|
|
179
|
-
border-radius:
|
|
180
|
-
border: 1px solid var(--line);
|
|
181
|
-
padding:
|
|
182
|
-
font-size: .
|
|
183
|
-
font-weight:
|
|
218
|
+
border-radius: 999px;
|
|
219
|
+
border: 1px solid var(--line-soft);
|
|
220
|
+
padding: 5px 10px;
|
|
221
|
+
font-size: .875rem;
|
|
222
|
+
font-weight: 500;
|
|
184
223
|
text-transform: capitalize;
|
|
185
224
|
white-space: nowrap;
|
|
225
|
+
background: var(--surface);
|
|
226
|
+
}
|
|
227
|
+
.badge.passed { color: var(--green); }
|
|
228
|
+
.badge.failed { color: var(--red); }
|
|
229
|
+
.badge.skipped { color: var(--amber); }
|
|
230
|
+
.badge.hero { font-size: 1rem; padding: 8px 14px; }
|
|
231
|
+
.step {
|
|
232
|
+
padding: 22px;
|
|
233
|
+
margin-top: 16px;
|
|
234
|
+
border: 1px solid var(--line-soft);
|
|
235
|
+
border-radius: 14px;
|
|
186
236
|
}
|
|
187
|
-
.badge.passed { color: var(--green); background: var(--green-bg); border-color: #c8e6d0; }
|
|
188
|
-
.badge.failed { color: var(--red); background: var(--red-bg); border-color: #f0c9c9; }
|
|
189
|
-
.badge.skipped { color: var(--amber); background: var(--amber-bg); border-color: #f0dc9f; }
|
|
190
|
-
.badge.hero { font-size: .95rem; padding: 8px 12px; }
|
|
191
|
-
.step { padding: 16px; margin-top: 12px; }
|
|
192
237
|
.step-header { display: flex; justify-content: space-between; gap: 16px; align-items: flex-start; }
|
|
193
|
-
.screenshot-link {
|
|
238
|
+
.screenshot-link {
|
|
239
|
+
display: block;
|
|
240
|
+
margin-top: 18px;
|
|
241
|
+
border: 1px solid var(--line);
|
|
242
|
+
border-radius: 12px;
|
|
243
|
+
overflow: hidden;
|
|
244
|
+
background: white;
|
|
245
|
+
box-shadow: 0 12px 36px rgba(0, 0, 0, .06);
|
|
246
|
+
}
|
|
247
|
+
.screenshot-link:hover { text-decoration: none; }
|
|
194
248
|
img { display: block; width: 100%; height: auto; }
|
|
195
|
-
.issue {
|
|
196
|
-
|
|
249
|
+
.issue {
|
|
250
|
+
padding: 16px;
|
|
251
|
+
border: 1px solid #ffd7d9;
|
|
252
|
+
border-radius: 12px;
|
|
253
|
+
background: #fff7f7;
|
|
254
|
+
}
|
|
255
|
+
.next { background: var(--surface); }
|
|
197
256
|
.empty { color: var(--muted); }
|
|
198
|
-
.footer {
|
|
257
|
+
.footer {
|
|
258
|
+
margin-top: 42px;
|
|
259
|
+
padding-top: 20px;
|
|
260
|
+
border-top: 1px solid var(--line-soft);
|
|
261
|
+
color: var(--muted);
|
|
262
|
+
font-size: .9375rem;
|
|
263
|
+
overflow-wrap: anywhere;
|
|
264
|
+
}
|
|
265
|
+
ul:not(.stack) { margin: 14px 0 0; padding-left: 1.2rem; color: #424245; }
|
|
266
|
+
li + li { margin-top: 6px; }
|
|
199
267
|
@media (max-width: 760px) {
|
|
200
|
-
main { padding:
|
|
268
|
+
main { padding: 34px 18px 48px; }
|
|
201
269
|
header.page { display: grid; }
|
|
202
270
|
.summary { grid-template-columns: 1fr; }
|
|
203
271
|
}
|
package/build/runner.js
CHANGED
|
@@ -81,7 +81,7 @@ function buildChecks(input) {
|
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
id: 'scenario_ready',
|
|
84
|
-
label: '
|
|
84
|
+
label: 'Scenario flag is available',
|
|
85
85
|
passed: scenarioReady,
|
|
86
86
|
detail: input.controlsPresent
|
|
87
87
|
? `Layout QA controls detected; requested ${input.scenario}.`
|
|
@@ -126,11 +126,11 @@ function buildNextAction(input) {
|
|
|
126
126
|
return {
|
|
127
127
|
category: 'target_unreachable',
|
|
128
128
|
title: 'Target URL did not load cleanly',
|
|
129
|
-
detail: 'Layout could not reach the target page well enough to evaluate
|
|
129
|
+
detail: 'Layout could not reach the target page well enough to evaluate deterministic response states.',
|
|
130
130
|
docsPath: flows_1.FLOW_MANIFEST_PATH,
|
|
131
131
|
nextSteps: [
|
|
132
132
|
'Start the app or deploy preview URL before running Layout QA.',
|
|
133
|
-
'Use the URL where the frontend is served with the Layout
|
|
133
|
+
'Use the URL where the frontend is served with the Layout QA env flag enabled.',
|
|
134
134
|
'Retry the same scenario after the target URL is reachable from the runner.',
|
|
135
135
|
],
|
|
136
136
|
};
|
|
@@ -138,13 +138,13 @@ function buildNextAction(input) {
|
|
|
138
138
|
if (failedCheckIds.has('scenario_ready')) {
|
|
139
139
|
return {
|
|
140
140
|
category: 'fixtures',
|
|
141
|
-
title: '
|
|
142
|
-
detail: 'The page loaded, but Layout could not confirm that the requested
|
|
141
|
+
title: 'Scenario flag was not active',
|
|
142
|
+
detail: 'The page loaded, but Layout could not confirm that the requested scenario was available.',
|
|
143
143
|
docsPath: flows_1.FLOW_MANIFEST_PATH,
|
|
144
144
|
nextSteps: [
|
|
145
|
-
'Confirm the target is running with the Layout
|
|
145
|
+
'Confirm the target is running with the Layout QA env flag set to 1.',
|
|
146
146
|
'Check that the app reads localStorage["layout.qa.scenario"] before API calls run.',
|
|
147
|
-
`Review ${flows_1.FLOW_MANIFEST_PATH}, the Layout QA docs, and the
|
|
147
|
+
`Review ${flows_1.FLOW_MANIFEST_PATH}, the Layout QA docs, and the API/auth response fixtures for missing handlers.`,
|
|
148
148
|
],
|
|
149
149
|
};
|
|
150
150
|
}
|
|
@@ -154,11 +154,11 @@ function buildNextAction(input) {
|
|
|
154
154
|
category: 'flow',
|
|
155
155
|
title: 'Flow step needs review',
|
|
156
156
|
detail: failedStep?.detail ||
|
|
157
|
-
'The target loaded with
|
|
157
|
+
'The target loaded with deterministic responses, but a declared QA flow step failed.',
|
|
158
158
|
docsPath: flows_1.FLOW_MANIFEST_PATH,
|
|
159
159
|
nextSteps: [
|
|
160
160
|
`Inspect ${flows_1.FLOW_MANIFEST_PATH} and confirm the failing step still matches the app UI.`,
|
|
161
|
-
'Update selectors, visible text assertions, or scenario
|
|
161
|
+
'Update selectors, visible text assertions, or scenario responses so the flow follows real user behavior.',
|
|
162
162
|
`Use ${flows_1.QA_DOCS_URL} for the supported flow step schema.`,
|
|
163
163
|
],
|
|
164
164
|
};
|
|
@@ -169,24 +169,24 @@ function buildNextAction(input) {
|
|
|
169
169
|
return {
|
|
170
170
|
category: 'browser_errors',
|
|
171
171
|
title: 'Browser errors need review',
|
|
172
|
-
detail: 'The target loaded with
|
|
172
|
+
detail: 'The target loaded with deterministic responses, but browser errors or failed requests were observed.',
|
|
173
173
|
docsPath: flows_1.FLOW_MANIFEST_PATH,
|
|
174
174
|
nextSteps: [
|
|
175
175
|
'Inspect the issues captured on this QA run.',
|
|
176
|
-
'Add or correct fixtures for unhandled frontend API requests.',
|
|
177
|
-
'Fix app code that throws under the selected
|
|
176
|
+
'Add or correct fixtures for unhandled frontend API/auth requests.',
|
|
177
|
+
'Fix app code that throws under the selected scenario, then rerun.',
|
|
178
178
|
],
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
181
|
if (appearsToBePublicOrAuthSurface(input.bodyTextSample)) {
|
|
182
182
|
return {
|
|
183
183
|
category: 'auth_boundary',
|
|
184
|
-
title: 'Public surface reached; wire
|
|
185
|
-
detail: 'The run passed the basic
|
|
184
|
+
title: 'Public surface reached; wire deterministic auth next',
|
|
185
|
+
detail: 'The run passed the basic browser checks, but the page appears to be a logged-out or public surface. Authenticated flows need a deterministic auth boundary before Layout can test them end to end.',
|
|
186
186
|
docsPath: flows_1.FLOW_MANIFEST_PATH,
|
|
187
187
|
nextSteps: [
|
|
188
|
-
`Use ${flows_1.FLOW_MANIFEST_PATH} and the Layout QA docs to add or confirm a central
|
|
189
|
-
'Expose a deterministic
|
|
188
|
+
`Use ${flows_1.FLOW_MANIFEST_PATH} and the Layout QA docs to add or confirm a central auth boundary for QA runs.`,
|
|
189
|
+
'Expose a deterministic user/session only when the Layout QA env flag is enabled.',
|
|
190
190
|
'Point the next QA run at an authenticated route and rerun happy_path, empty, and error scenarios.',
|
|
191
191
|
],
|
|
192
192
|
};
|
|
@@ -194,11 +194,11 @@ function buildNextAction(input) {
|
|
|
194
194
|
return {
|
|
195
195
|
category: 'ready',
|
|
196
196
|
title: 'Ready for deeper flow coverage',
|
|
197
|
-
detail: 'The target loaded with the requested
|
|
197
|
+
detail: 'The target loaded with the requested scenario and no basic browser issues were detected.',
|
|
198
198
|
docsPath: flows_1.FLOW_MANIFEST_PATH,
|
|
199
199
|
nextSteps: [
|
|
200
200
|
`Add route-specific Playwright-style flow steps to ${flows_1.FLOW_MANIFEST_PATH} for the highest-value user path.`,
|
|
201
|
-
'Expand
|
|
201
|
+
'Expand deterministic API/auth responses for any requests encountered by that flow.',
|
|
202
202
|
'Run the same flow across happy_path, empty, and error scenarios.',
|
|
203
203
|
],
|
|
204
204
|
};
|
|
@@ -635,7 +635,7 @@ function buildRunnerErrorResult(message, viewport = viewports_1.DEFAULT_VIEWPORT
|
|
|
635
635
|
docsPath: flows_1.FLOW_MANIFEST_PATH,
|
|
636
636
|
nextSteps: [
|
|
637
637
|
'Confirm the target URL is reachable by the runner.',
|
|
638
|
-
'Confirm the app is served with the Layout
|
|
638
|
+
'Confirm the app is served with the Layout QA env flag enabled.',
|
|
639
639
|
'Retry after the target loads consistently in a browser.',
|
|
640
640
|
`Viewport used for this run: ${(0, viewports_1.formatViewport)(viewport)}.`,
|
|
641
641
|
],
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trylayout/qa",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Local browser QA runner and HTML reports for AI-built frontends.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Layout",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"build",
|
|
29
|
+
"docs/assets/layout-qa-report.png",
|
|
29
30
|
"README.md",
|
|
30
31
|
"LICENSE"
|
|
31
32
|
],
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
"scripts": {
|
|
39
40
|
"build": "tsc",
|
|
40
41
|
"check": "tsc --noEmit",
|
|
42
|
+
"publish:alias": "cd packages/layout-qa && npm publish --access public",
|
|
41
43
|
"prepack": "npm run build"
|
|
42
44
|
},
|
|
43
45
|
"dependencies": {
|