@trylayout/qa 0.1.0 → 0.1.1
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 +148 -41
- package/build/flows.d.ts +1 -3
- package/build/flows.js +1 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Layout QA
|
|
2
2
|
|
|
3
|
-
Layout QA is a local browser QA protocol and runner for AI-built frontends. It runs deterministic
|
|
3
|
+
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
4
|
|
|
5
5
|
The core loop is intentionally local:
|
|
6
6
|
|
|
@@ -9,16 +9,18 @@ npx @trylayout/qa init
|
|
|
9
9
|
npx @trylayout/qa run --target-url http://localhost:5173 --scenario happy_path --open
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
No account, upload, hosted service, or external docs are required.
|
|
13
|
+
|
|
14
|
+
## Why This Exists
|
|
13
15
|
|
|
14
16
|
Frontend agents can move faster when they have a visual feedback loop they can run themselves. Layout gives the agent a small protocol:
|
|
15
17
|
|
|
16
|
-
- Wire
|
|
17
|
-
-
|
|
18
|
+
- Wire deterministic API/auth mocks behind a local env flag such as `VITE_LAYOUT_QA_MOCKS=1`.
|
|
19
|
+
- Switch mock states with `localStorage["layout.qa.scenario"]`.
|
|
18
20
|
- Declare high-value browser flows in `.layout/qa-flows.json`.
|
|
19
21
|
- Run the CLI locally and inspect the generated screenshots/report.
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
The goal is not to replace Playwright. The goal is to make the browser QA loop simple enough for a coding agent to set up, run, and iterate on while building a frontend branch.
|
|
22
24
|
|
|
23
25
|
## Install
|
|
24
26
|
|
|
@@ -35,21 +37,27 @@ npm install --save-dev @trylayout/qa
|
|
|
35
37
|
npx trylayout run --target-url http://localhost:5173
|
|
36
38
|
```
|
|
37
39
|
|
|
38
|
-
The package uses Playwright. If your environment does not already have
|
|
40
|
+
The package uses Playwright. If your environment does not already have Chromium installed for Playwright, run:
|
|
39
41
|
|
|
40
42
|
```bash
|
|
41
43
|
npx playwright install chromium
|
|
42
44
|
```
|
|
43
45
|
|
|
44
|
-
##
|
|
46
|
+
## Quick Start
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
Create a starter flow manifest:
|
|
47
49
|
|
|
48
50
|
```bash
|
|
49
51
|
npx @trylayout/qa init
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
Start your app with whatever mock flag your project uses:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
VITE_LAYOUT_QA_MOCKS=1 npm run dev
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Run a scenario:
|
|
53
61
|
|
|
54
62
|
```bash
|
|
55
63
|
npx @trylayout/qa run \
|
|
@@ -58,19 +66,6 @@ npx @trylayout/qa run \
|
|
|
58
66
|
--open
|
|
59
67
|
```
|
|
60
68
|
|
|
61
|
-
Useful options:
|
|
62
|
-
|
|
63
|
-
```text
|
|
64
|
-
--target-url <url> URL of the running frontend to test.
|
|
65
|
-
--scenario <name> Mock scenario to activate. Defaults to happy_path.
|
|
66
|
-
--flows <path> Flow manifest path. Defaults to .layout/qa-flows.json.
|
|
67
|
-
--out <path> Artifact directory. Defaults to .layout/runs.
|
|
68
|
-
--timeout <ms> Browser run timeout. Defaults to 60000.
|
|
69
|
-
--headed Show the browser instead of running headless.
|
|
70
|
-
--open Open the generated local HTML report after the run.
|
|
71
|
-
--json Print machine-readable JSON.
|
|
72
|
-
```
|
|
73
|
-
|
|
74
69
|
Each run writes:
|
|
75
70
|
|
|
76
71
|
```text
|
|
@@ -84,14 +79,33 @@ Each run writes:
|
|
|
84
79
|
|
|
85
80
|
The process exits `0` on pass and `1` on failure, so the same command can run in CI.
|
|
86
81
|
|
|
82
|
+
## Commands
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
trylayout init [options]
|
|
86
|
+
trylayout run --target-url <url> [options]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Options:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
--target-url <url> URL of the running frontend to test.
|
|
93
|
+
--scenario <name> Mock scenario to activate. Defaults to happy_path.
|
|
94
|
+
--flows <path> Flow manifest path. Defaults to .layout/qa-flows.json.
|
|
95
|
+
--out <path> Artifact directory. Defaults to .layout/runs.
|
|
96
|
+
--timeout <ms> Browser run timeout. Defaults to 60000.
|
|
97
|
+
--headed Show the browser instead of running headless.
|
|
98
|
+
--open Open the generated local HTML report after the run.
|
|
99
|
+
--json Print machine-readable JSON.
|
|
100
|
+
--force Overwrite an existing flow file during init.
|
|
101
|
+
```
|
|
102
|
+
|
|
87
103
|
## Flow Manifest
|
|
88
104
|
|
|
89
105
|
Default path: `.layout/qa-flows.json`.
|
|
90
106
|
|
|
91
107
|
```json
|
|
92
108
|
{
|
|
93
|
-
"$schema": "https://trylayout.com/schemas/qa-flows.v1.json",
|
|
94
|
-
"docsUrl": "https://trylayout.com/docs/qa",
|
|
95
109
|
"schemaVersion": 1,
|
|
96
110
|
"flows": [
|
|
97
111
|
{
|
|
@@ -105,6 +119,12 @@ Default path: `.layout/qa-flows.json`.
|
|
|
105
119
|
"type": "assert_visible_text",
|
|
106
120
|
"text": "Dashboard",
|
|
107
121
|
"screenshot": true
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"id": "open_settings",
|
|
125
|
+
"type": "click",
|
|
126
|
+
"text": "Settings",
|
|
127
|
+
"screenshot": true
|
|
108
128
|
}
|
|
109
129
|
]
|
|
110
130
|
}
|
|
@@ -112,6 +132,27 @@ Default path: `.layout/qa-flows.json`.
|
|
|
112
132
|
}
|
|
113
133
|
```
|
|
114
134
|
|
|
135
|
+
Top-level fields:
|
|
136
|
+
|
|
137
|
+
- `schemaVersion`: currently `1`.
|
|
138
|
+
- `flows`: array of flow definitions.
|
|
139
|
+
|
|
140
|
+
Flow fields:
|
|
141
|
+
|
|
142
|
+
- `id`: stable machine-readable flow id.
|
|
143
|
+
- `name`: human-readable report title.
|
|
144
|
+
- `startUrl`: path or absolute URL where the flow starts.
|
|
145
|
+
- `scenarios`: scenario names this flow can run against. Use an empty array to allow all scenarios.
|
|
146
|
+
- `steps`: ordered browser steps.
|
|
147
|
+
|
|
148
|
+
Step fields:
|
|
149
|
+
|
|
150
|
+
- `id`: stable machine-readable step id.
|
|
151
|
+
- `type`: step type.
|
|
152
|
+
- `label`: optional human-readable report label.
|
|
153
|
+
- `screenshot`: set `true` to capture a screenshot after the step.
|
|
154
|
+
- `timeoutMs`: optional per-step timeout.
|
|
155
|
+
|
|
115
156
|
Supported step types:
|
|
116
157
|
|
|
117
158
|
- `goto`: navigate to `url`.
|
|
@@ -122,14 +163,36 @@ Supported step types:
|
|
|
122
163
|
- `assert_url`: require current URL to equal `url` or contain `contains`.
|
|
123
164
|
- `screenshot`: capture a screenshot checkpoint.
|
|
124
165
|
|
|
125
|
-
|
|
166
|
+
Examples:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{ "id": "open_settings", "type": "click", "text": "Settings" }
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{ "id": "email", "type": "fill", "selector": "input[name='email']", "value": "layout@example.com" }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{ "id": "settings_url", "type": "assert_url", "contains": "/settings" }
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Mock Scenarios
|
|
181
|
+
|
|
182
|
+
Before the app loads, the runner sets:
|
|
126
183
|
|
|
127
184
|
```js
|
|
128
185
|
localStorage.setItem("layout.qa.scenario", "<scenario>");
|
|
129
186
|
sessionStorage.setItem("layout.qa.runner", "1");
|
|
130
187
|
```
|
|
131
188
|
|
|
132
|
-
Your app can use `layout.qa.scenario` to switch deterministic mock states
|
|
189
|
+
Your app can use `layout.qa.scenario` to switch deterministic mock states:
|
|
190
|
+
|
|
191
|
+
- `happy_path`: normal populated data.
|
|
192
|
+
- `empty`: successful responses with empty states.
|
|
193
|
+
- `error`: failed or error responses that should render recovery UI.
|
|
194
|
+
|
|
195
|
+
The `layout.qa.runner` flag is useful for hiding local-only QA switchers from screenshots.
|
|
133
196
|
|
|
134
197
|
## Agent Setup Prompt
|
|
135
198
|
|
|
@@ -138,24 +201,68 @@ Paste this into your coding agent inside the frontend repo:
|
|
|
138
201
|
```text
|
|
139
202
|
Set up Layout QA for this web app.
|
|
140
203
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
204
|
+
Goal:
|
|
205
|
+
Create a local-only browser QA loop that an agent can run while changing frontend code.
|
|
206
|
+
|
|
207
|
+
Rules:
|
|
208
|
+
- Do not add a standalone mock server.
|
|
209
|
+
- Do not require a hosted Layout service.
|
|
210
|
+
- Keep all mock data local to this app.
|
|
211
|
+
- Gate mocks behind a local-only env flag such as VITE_LAYOUT_QA_MOCKS=1, NEXT_PUBLIC_LAYOUT_QA_MOCKS=1, or the framework-appropriate equivalent.
|
|
212
|
+
- Use localStorage["layout.qa.scenario"] to select at least happy_path, empty, and error mock states.
|
|
213
|
+
- Hide any local QA switcher or debug controls when sessionStorage["layout.qa.runner"] === "1".
|
|
214
|
+
|
|
215
|
+
Implementation:
|
|
216
|
+
- Add deterministic API fixtures for the highest-value frontend route.
|
|
217
|
+
- If the app has a central auth/session abstraction, add a deterministic mock user only when the Layout QA env flag is enabled.
|
|
218
|
+
- 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.
|
|
219
|
+
- Add .layout/qa-flows.json with one smoke flow for the most important page.
|
|
220
|
+
- Prefer visible text and stable selectors.
|
|
221
|
+
- Add screenshot checkpoints after meaningful user-visible states.
|
|
222
|
+
|
|
223
|
+
Run:
|
|
149
224
|
npx @trylayout/qa run --target-url <local app url> --scenario happy_path --open
|
|
225
|
+
npx @trylayout/qa run --target-url <local app url> --scenario empty --open
|
|
226
|
+
npx @trylayout/qa run --target-url <local app url> --scenario error --open
|
|
150
227
|
```
|
|
151
228
|
|
|
152
|
-
##
|
|
229
|
+
## CI Example
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
name: Layout QA
|
|
233
|
+
|
|
234
|
+
on:
|
|
235
|
+
pull_request:
|
|
236
|
+
|
|
237
|
+
jobs:
|
|
238
|
+
qa:
|
|
239
|
+
runs-on: ubuntu-latest
|
|
240
|
+
steps:
|
|
241
|
+
- uses: actions/checkout@v4
|
|
242
|
+
- uses: actions/setup-node@v4
|
|
243
|
+
with:
|
|
244
|
+
node-version: 20
|
|
245
|
+
cache: npm
|
|
246
|
+
- run: npm ci
|
|
247
|
+
- run: npx playwright install chromium
|
|
248
|
+
- run: VITE_LAYOUT_QA_MOCKS=1 npm run dev -- --host 127.0.0.1 --port 5173 &
|
|
249
|
+
- run: npx @trylayout/qa run --target-url http://127.0.0.1:5173 --scenario happy_path
|
|
250
|
+
- uses: actions/upload-artifact@v4
|
|
251
|
+
if: always()
|
|
252
|
+
with:
|
|
253
|
+
name: layout-qa-report
|
|
254
|
+
path: .layout/runs
|
|
255
|
+
```
|
|
153
256
|
|
|
154
|
-
|
|
257
|
+
## Current Scope
|
|
155
258
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
259
|
+
This package is intentionally small:
|
|
260
|
+
|
|
261
|
+
- It does run Playwright against an already-running frontend.
|
|
262
|
+
- It does write local screenshots and an HTML report.
|
|
263
|
+
- It does support deterministic scenario switching.
|
|
264
|
+
- It does not build or host your app.
|
|
265
|
+
- It does not upload results.
|
|
266
|
+
- It does not perform AI review by itself.
|
|
160
267
|
|
|
161
|
-
|
|
268
|
+
Those hosted/reporting layers can be added later without changing the local protocol.
|
package/build/flows.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { LoadedQaFlow, QaFlowDefinition } from './types';
|
|
|
2
2
|
export declare const DEFAULT_TEST_TIMEOUT_MS: number;
|
|
3
3
|
export declare const SCREENSHOT_LIMIT_BYTES: number;
|
|
4
4
|
export declare const FLOW_MANIFEST_PATH = ".layout/qa-flows.json";
|
|
5
|
-
export declare const QA_DOCS_URL = "https://
|
|
5
|
+
export declare const QA_DOCS_URL = "https://github.com/Layout-App/layout-qa#readme";
|
|
6
6
|
export declare function getTestTimeoutMs(): number;
|
|
7
7
|
export declare function selectFlowFromManifest(raw: unknown, scenario: string): QaFlowDefinition | null;
|
|
8
8
|
export declare function parseFlowManifestContent(content: string, scenario: string): LoadedQaFlow | null;
|
|
@@ -17,8 +17,6 @@ export declare function loadFlow(input: {
|
|
|
17
17
|
manifestFound: boolean;
|
|
18
18
|
}>;
|
|
19
19
|
export declare function starterFlowManifest(): {
|
|
20
|
-
$schema: string;
|
|
21
|
-
docsUrl: string;
|
|
22
20
|
schemaVersion: number;
|
|
23
21
|
flows: {
|
|
24
22
|
id: string;
|
package/build/flows.js
CHANGED
|
@@ -16,7 +16,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
16
16
|
exports.DEFAULT_TEST_TIMEOUT_MS = 60 * 1000;
|
|
17
17
|
exports.SCREENSHOT_LIMIT_BYTES = 300 * 1024;
|
|
18
18
|
exports.FLOW_MANIFEST_PATH = '.layout/qa-flows.json';
|
|
19
|
-
exports.QA_DOCS_URL = 'https://
|
|
19
|
+
exports.QA_DOCS_URL = 'https://github.com/Layout-App/layout-qa#readme';
|
|
20
20
|
function getTestTimeoutMs() {
|
|
21
21
|
const value = Number(process.env.LAYOUT_QA_TEST_TIMEOUT_MS);
|
|
22
22
|
return Number.isFinite(value) && value > 0 ? value : exports.DEFAULT_TEST_TIMEOUT_MS;
|
|
@@ -159,8 +159,6 @@ async function loadFlow(input) {
|
|
|
159
159
|
}
|
|
160
160
|
function starterFlowManifest() {
|
|
161
161
|
return {
|
|
162
|
-
$schema: 'https://trylayout.com/schemas/qa-flows.v1.json',
|
|
163
|
-
docsUrl: exports.QA_DOCS_URL,
|
|
164
162
|
schemaVersion: 1,
|
|
165
163
|
flows: [
|
|
166
164
|
{
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trylayout/qa",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Local browser QA runner and HTML reports for AI-built frontends.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Layout",
|
|
7
|
-
"homepage": "https://
|
|
7
|
+
"homepage": "https://github.com/Layout-App/layout-qa#readme",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/Layout-App/layout-qa.git"
|