@eidra-umain/greenlight 0.1.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/README.md +391 -0
- package/dist/browser/browser.d.ts +24 -0
- package/dist/browser/browser.d.ts.map +1 -0
- package/dist/browser/browser.js +44 -0
- package/dist/browser/browser.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +140 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/run.d.ts +9 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +277 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +107 -0
- package/dist/config.js.map +1 -0
- package/dist/globals.d.ts +21 -0
- package/dist/globals.d.ts.map +1 -0
- package/dist/globals.js +24 -0
- package/dist/globals.js.map +1 -0
- package/dist/parser/loader.d.ts +7 -0
- package/dist/parser/loader.d.ts.map +1 -0
- package/dist/parser/loader.js +43 -0
- package/dist/parser/loader.js.map +1 -0
- package/dist/parser/schema.d.ts +42 -0
- package/dist/parser/schema.d.ts.map +1 -0
- package/dist/parser/schema.js +33 -0
- package/dist/parser/schema.js.map +1 -0
- package/dist/parser/steps.d.ts +13 -0
- package/dist/parser/steps.d.ts.map +1 -0
- package/dist/parser/steps.js +44 -0
- package/dist/parser/steps.js.map +1 -0
- package/dist/parser/variables.d.ts +18 -0
- package/dist/parser/variables.d.ts.map +1 -0
- package/dist/parser/variables.js +44 -0
- package/dist/parser/variables.js.map +1 -0
- package/dist/pilot/a11y-parser.d.ts +26 -0
- package/dist/pilot/a11y-parser.d.ts.map +1 -0
- package/dist/pilot/a11y-parser.js +195 -0
- package/dist/pilot/a11y-parser.js.map +1 -0
- package/dist/pilot/assertions.d.ts +30 -0
- package/dist/pilot/assertions.d.ts.map +1 -0
- package/dist/pilot/assertions.js +219 -0
- package/dist/pilot/assertions.js.map +1 -0
- package/dist/pilot/checkbox.d.ts +12 -0
- package/dist/pilot/checkbox.d.ts.map +1 -0
- package/dist/pilot/checkbox.js +104 -0
- package/dist/pilot/checkbox.js.map +1 -0
- package/dist/pilot/executor.d.ts +17 -0
- package/dist/pilot/executor.d.ts.map +1 -0
- package/dist/pilot/executor.js +462 -0
- package/dist/pilot/executor.js.map +1 -0
- package/dist/pilot/form-fields.d.ts +34 -0
- package/dist/pilot/form-fields.d.ts.map +1 -0
- package/dist/pilot/form-fields.js +139 -0
- package/dist/pilot/form-fields.js.map +1 -0
- package/dist/pilot/llm.d.ts +49 -0
- package/dist/pilot/llm.d.ts.map +1 -0
- package/dist/pilot/llm.js +188 -0
- package/dist/pilot/llm.js.map +1 -0
- package/dist/pilot/locator.d.ts +58 -0
- package/dist/pilot/locator.d.ts.map +1 -0
- package/dist/pilot/locator.js +248 -0
- package/dist/pilot/locator.js.map +1 -0
- package/dist/pilot/message-builder.d.ts +31 -0
- package/dist/pilot/message-builder.d.ts.map +1 -0
- package/dist/pilot/message-builder.js +112 -0
- package/dist/pilot/message-builder.js.map +1 -0
- package/dist/pilot/network.d.ts +23 -0
- package/dist/pilot/network.d.ts.map +1 -0
- package/dist/pilot/network.js +92 -0
- package/dist/pilot/network.js.map +1 -0
- package/dist/pilot/pilot.d.ts +27 -0
- package/dist/pilot/pilot.d.ts.map +1 -0
- package/dist/pilot/pilot.js +249 -0
- package/dist/pilot/pilot.js.map +1 -0
- package/dist/pilot/prompts.d.ts +8 -0
- package/dist/pilot/prompts.d.ts.map +1 -0
- package/dist/pilot/prompts.js +163 -0
- package/dist/pilot/prompts.js.map +1 -0
- package/dist/pilot/providers/anthropic.d.ts +6 -0
- package/dist/pilot/providers/anthropic.d.ts.map +1 -0
- package/dist/pilot/providers/anthropic.js +45 -0
- package/dist/pilot/providers/anthropic.js.map +1 -0
- package/dist/pilot/providers/gemini.d.ts +6 -0
- package/dist/pilot/providers/gemini.d.ts.map +1 -0
- package/dist/pilot/providers/gemini.js +55 -0
- package/dist/pilot/providers/gemini.js.map +1 -0
- package/dist/pilot/providers/index.d.ts +10 -0
- package/dist/pilot/providers/index.d.ts.map +1 -0
- package/dist/pilot/providers/index.js +25 -0
- package/dist/pilot/providers/index.js.map +1 -0
- package/dist/pilot/providers/openai-compatible.d.ts +7 -0
- package/dist/pilot/providers/openai-compatible.d.ts.map +1 -0
- package/dist/pilot/providers/openai-compatible.js +34 -0
- package/dist/pilot/providers/openai-compatible.js.map +1 -0
- package/dist/pilot/providers/types.d.ts +12 -0
- package/dist/pilot/providers/types.d.ts.map +1 -0
- package/dist/pilot/providers/types.js +2 -0
- package/dist/pilot/providers/types.js.map +1 -0
- package/dist/pilot/response-parser.d.ts +31 -0
- package/dist/pilot/response-parser.d.ts.map +1 -0
- package/dist/pilot/response-parser.js +188 -0
- package/dist/pilot/response-parser.js.map +1 -0
- package/dist/pilot/state.d.ts +19 -0
- package/dist/pilot/state.d.ts.map +1 -0
- package/dist/pilot/state.js +67 -0
- package/dist/pilot/state.js.map +1 -0
- package/dist/pilot/trace.d.ts +16 -0
- package/dist/pilot/trace.d.ts.map +1 -0
- package/dist/pilot/trace.js +117 -0
- package/dist/pilot/trace.js.map +1 -0
- package/dist/planner/hasher.d.ts +14 -0
- package/dist/planner/hasher.d.ts.map +1 -0
- package/dist/planner/hasher.js +21 -0
- package/dist/planner/hasher.js.map +1 -0
- package/dist/planner/plan-generator.d.ts +23 -0
- package/dist/planner/plan-generator.d.ts.map +1 -0
- package/dist/planner/plan-generator.js +56 -0
- package/dist/planner/plan-generator.js.map +1 -0
- package/dist/planner/plan-runner.d.ts +16 -0
- package/dist/planner/plan-runner.d.ts.map +1 -0
- package/dist/planner/plan-runner.js +375 -0
- package/dist/planner/plan-runner.js.map +1 -0
- package/dist/planner/plan-store.d.ts +22 -0
- package/dist/planner/plan-store.d.ts.map +1 -0
- package/dist/planner/plan-store.js +71 -0
- package/dist/planner/plan-store.js.map +1 -0
- package/dist/planner/plan-types.d.ts +64 -0
- package/dist/planner/plan-types.d.ts.map +1 -0
- package/dist/planner/plan-types.js +7 -0
- package/dist/planner/plan-types.js.map +1 -0
- package/dist/reporter/types.d.ts +130 -0
- package/dist/reporter/types.d.ts.map +1 -0
- package/dist/reporter/types.js +5 -0
- package/dist/reporter/types.js.map +1 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/greenlight_banner.png" alt="GreenLight — AI-driven E2E Testing" width="500">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# GreenLight
|
|
6
|
+
|
|
7
|
+
Natural language driven end-to-end testing for web applications. Write tests as plain-English user stories, and an AI agent (the Pilot) executes them against your staging environment using a real browser.
|
|
8
|
+
|
|
9
|
+
No selectors. No XPaths. No test IDs, drivers or glue code. Just describe what a user would do.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
**[How it works](#how-it-works)** | **[Quick start](#quick-start)** | **[Project configuration](#project-configuration)** | **[CLI](#cli)** | **[Test syntax](#test-syntax)** | **[Cached plans](#cached-plans)** | **[LLM setup](#llm-setup)** | **[Architecture](#architecture)** | **[CI/CD](#cicd)**
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## How it works
|
|
18
|
+
|
|
19
|
+
```yaml
|
|
20
|
+
suite: "Product Search"
|
|
21
|
+
base_url: "https://staging.example.com"
|
|
22
|
+
|
|
23
|
+
tests:
|
|
24
|
+
- name: "Filtering reduces results"
|
|
25
|
+
steps:
|
|
26
|
+
- navigate to Products from the main menu
|
|
27
|
+
- remember the number of results shown
|
|
28
|
+
- select "Electronics" in the category filter
|
|
29
|
+
- check that the number of results has decreased
|
|
30
|
+
- search for "wireless headphones"
|
|
31
|
+
- check that the page contains "wireless"
|
|
32
|
+
- fill in the inquiry form with email "test@example.com" and some test data
|
|
33
|
+
- submit the form
|
|
34
|
+
- check that you see "Thanks for your inquiry"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
GreenLight understands form wizards, custom dropdowns, autocomplete fields, and checkbox consent flows. It fills in forms with realistic test data, handles before/after value comparisons, and works with any UI framework.
|
|
38
|
+
|
|
39
|
+
The first run uses an LLM to discover the right actions (the **discovery run**). After that, GreenLight caches a concrete action plan and replays it without LLM calls — making subsequent runs fast and deterministic.
|
|
40
|
+
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
1. Add GreenLight to your project:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install @eidra-umain/greenlight
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
2. Create a `greenlight.yaml` in your project root:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
suites:
|
|
53
|
+
- tests/e2e/login.yaml
|
|
54
|
+
- tests/e2e/checkout.yaml
|
|
55
|
+
|
|
56
|
+
deployments:
|
|
57
|
+
staging:
|
|
58
|
+
base_url: https://staging.myapp.com
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
3. Run:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
greenlight run
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Project configuration
|
|
68
|
+
|
|
69
|
+
GreenLight looks for a `greenlight.yaml` in the working directory. This file defines which suites to run and supports multiple deployment targets.
|
|
70
|
+
|
|
71
|
+
### Single deployment
|
|
72
|
+
|
|
73
|
+
When there is only one deployment, it is used automatically:
|
|
74
|
+
|
|
75
|
+
```yaml
|
|
76
|
+
suites:
|
|
77
|
+
- tests/e2e/*.yaml
|
|
78
|
+
|
|
79
|
+
deployments:
|
|
80
|
+
staging:
|
|
81
|
+
base_url: https://staging.myapp.com
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Multiple deployments
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
suites:
|
|
88
|
+
- tests/e2e/*.yaml
|
|
89
|
+
|
|
90
|
+
model: anthropic/claude-sonnet-4
|
|
91
|
+
timeout: 15000
|
|
92
|
+
|
|
93
|
+
deployments:
|
|
94
|
+
dev:
|
|
95
|
+
base_url: https://dev.myapp.com
|
|
96
|
+
staging:
|
|
97
|
+
base_url: https://staging.myapp.com
|
|
98
|
+
prod:
|
|
99
|
+
base_url: https://myapp.com
|
|
100
|
+
timeout: 30000
|
|
101
|
+
|
|
102
|
+
default_deployment: staging
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Shared settings go at the top level. Deployment-specific settings override them.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
greenlight run # uses default_deployment (staging)
|
|
109
|
+
greenlight run -d prod # selects the prod deployment
|
|
110
|
+
greenlight run -d dev # selects the dev deployment
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
If there are multiple deployments and no `default_deployment` is set, the `--deployment` flag is required.
|
|
114
|
+
|
|
115
|
+
### All config fields
|
|
116
|
+
|
|
117
|
+
| Field | Type | Description |
|
|
118
|
+
|-------|------|-------------|
|
|
119
|
+
| `suites` | string[] | Paths or globs to suite YAML files (required) |
|
|
120
|
+
| `deployments` | map | Named deployment targets |
|
|
121
|
+
| `default_deployment` | string | Which deployment to use by default |
|
|
122
|
+
| `base_url` | string | Base URL for the site under test |
|
|
123
|
+
| `model` | string | LLM model identifier |
|
|
124
|
+
| `llm_base_url` | string | Base URL for the OpenAI-compatible API |
|
|
125
|
+
| `timeout` | number | Per-step timeout in milliseconds |
|
|
126
|
+
| `headed` | boolean | Run browser in visible mode |
|
|
127
|
+
| `parallel` | number | Number of concurrent test cases |
|
|
128
|
+
| `reporter` | string | Output format: `cli`, `json`, or `html` |
|
|
129
|
+
| `viewport` | object | `{ width, height }` for the browser viewport |
|
|
130
|
+
|
|
131
|
+
All fields except `suites` can appear at the top level or inside a deployment. Priority: **CLI flags > deployment > top-level config > built-in defaults**.
|
|
132
|
+
|
|
133
|
+
## CLI
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
greenlight run [suites...] # run suite YAML files (overrides greenlight.yaml)
|
|
137
|
+
greenlight run # run suites from greenlight.yaml
|
|
138
|
+
greenlight run -d, --deployment <name> # select a named deployment
|
|
139
|
+
greenlight run -t, --test <name> # filter by test name
|
|
140
|
+
greenlight run --base-url <url> # override base URL
|
|
141
|
+
greenlight run --headed # visible browser
|
|
142
|
+
greenlight run -p, --parallel 4 # concurrent test cases
|
|
143
|
+
greenlight run -r, --reporter json # json output (also: cli, html)
|
|
144
|
+
greenlight run -o, --output results.json # write to file
|
|
145
|
+
greenlight run --timeout 15000 # per-step timeout (ms)
|
|
146
|
+
greenlight run --model openai/gpt-4o # override LLM model
|
|
147
|
+
greenlight run --llm-base-url <url> # use a different OpenAI-compatible API
|
|
148
|
+
greenlight run --debug # verbose output (actions, LLM modes, timings)
|
|
149
|
+
greenlight run --trace # timestamped browser events for perf analysis
|
|
150
|
+
greenlight run --discover # force discovery run, ignore cached plans
|
|
151
|
+
greenlight run --plan-status # show cache status for all tests
|
|
152
|
+
greenlight run --on-drift rerun # re-discover on cached plan drift (default: fail)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## GreenLight philosophy compared to Gherkin/Cucumber
|
|
156
|
+
|
|
157
|
+
Traditional BDD tools like Cucumber use **Gherkin** — a structured `Given/When/Then` syntax where every step requires a developer-written **step definition** (glue code) that maps the English phrase to actual browser automation with CSS selectors or XPaths.
|
|
158
|
+
|
|
159
|
+
GreenLight takes a different approach:
|
|
160
|
+
|
|
161
|
+
| | GreenLight | Gherkin (Cucumber) |
|
|
162
|
+
|---|---|---|
|
|
163
|
+
| **Test language** | Freeform plain English | Structured `Given/When/Then` keywords |
|
|
164
|
+
| **Element targeting** | AI resolves via accessibility tree — no selectors | Developers write glue code with selectors/XPaths |
|
|
165
|
+
| **Maintenance** | Tests survive UI refactors that don't change behavior | Selector changes break tests, requiring glue code updates |
|
|
166
|
+
| **Authoring** | Non-technical testers, no code required | Readable specs, but developers must write step definitions |
|
|
167
|
+
| **Determinism** | Cached plans are deterministic; discovery runs have LLM variability | Fully deterministic — same input, same execution path |
|
|
168
|
+
| **Maturity** | New, LLM-dependent | Battle-tested (15+ years), broad ecosystem |
|
|
169
|
+
|
|
170
|
+
**In short:** Gherkin requires developers to bridge English and browser automation via step definitions. GreenLight uses AI as that bridge — eliminating the glue code layer at the cost of introducing LLM-dependent variability.
|
|
171
|
+
|
|
172
|
+
## Test syntax
|
|
173
|
+
|
|
174
|
+
Tests are plain English. The Pilot interprets intent, so phrasing is flexible. Common patterns:
|
|
175
|
+
|
|
176
|
+
| Action | Example |
|
|
177
|
+
|--------|---------|
|
|
178
|
+
| Navigate | `go to "/products"` or `navigate to About from the menu` |
|
|
179
|
+
| Click | `click "Add to Cart"` or `click the Submit button` |
|
|
180
|
+
| Type | `enter "jane@example.com" into "Email"` |
|
|
181
|
+
| Select | `select "Canada" from "Country"` (works with native and custom dropdowns) |
|
|
182
|
+
| Form fill | `fill in the contact form with email "a@b.com" and some test data` |
|
|
183
|
+
| Autocomplete | `type "Stock" into the city field and select the first suggestion` |
|
|
184
|
+
| Check | `check the "I agree to terms" checkbox` |
|
|
185
|
+
| Remember | `remember the number of search results` |
|
|
186
|
+
| Compare | `check that the number of results is less than before` |
|
|
187
|
+
| Assert | `check that page contains "Order Confirmed"` |
|
|
188
|
+
| Multi-step | `Select Red - Green - Blue in the color picker` (auto-split into 3 clicks) |
|
|
189
|
+
|
|
190
|
+
### Form filling
|
|
191
|
+
|
|
192
|
+
Steps like "fill in the form with some test data and submit it" are automatically expanded at runtime. GreenLight inspects the actual form fields (labels, placeholders, input types) and generates appropriate test data. Autocomplete fields are detected and handled with type-wait-select flows. Consent checkboxes are automatically checked.
|
|
193
|
+
|
|
194
|
+
### Value comparisons
|
|
195
|
+
|
|
196
|
+
Remember a value before an action, then compare after:
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
steps:
|
|
200
|
+
- remember the total shown in the results badge
|
|
201
|
+
- apply the "In Stock" filter
|
|
202
|
+
- check that the total has decreased
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Reusable steps
|
|
206
|
+
|
|
207
|
+
Define common sequences at the suite level and invoke by name:
|
|
208
|
+
|
|
209
|
+
```yaml
|
|
210
|
+
reusable_steps:
|
|
211
|
+
log in as admin:
|
|
212
|
+
- enter "{{admin_email}}" into "Email"
|
|
213
|
+
- enter "{{admin_password}}" into "Password"
|
|
214
|
+
- click "Sign In"
|
|
215
|
+
|
|
216
|
+
tests:
|
|
217
|
+
- name: "Admin can access settings"
|
|
218
|
+
steps:
|
|
219
|
+
- log in as admin
|
|
220
|
+
- click "Settings"
|
|
221
|
+
- check that page contains "Account Settings"
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Cached plans
|
|
225
|
+
|
|
226
|
+
The first run of a test uses LLM calls to discover the right browser actions (**discovery run**). After a successful run, GreenLight caches the concrete action sequence as a **heuristic plan** in `.greenlight/plans/`.
|
|
227
|
+
|
|
228
|
+
Subsequent runs replay the cached plan directly via Playwright — no LLM calls, no API costs, significantly faster. If you change the test steps in YAML, the hash changes and GreenLight automatically re-discovers.
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
greenlight run # uses cached plans where available
|
|
232
|
+
greenlight run --discover # force fresh discovery, ignore cache
|
|
233
|
+
greenlight run --plan-status # show which tests have cached plans
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## LLM setup
|
|
237
|
+
|
|
238
|
+
### API key
|
|
239
|
+
|
|
240
|
+
Set your API key via the `LLM_API_KEY` environment variable or a `.env` file in the project root:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
LLM_API_KEY=sk-...
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The same key is used regardless of provider. `OPENROUTER_API_KEY` is also accepted for backward compatibility.
|
|
247
|
+
|
|
248
|
+
### Providers
|
|
249
|
+
|
|
250
|
+
GreenLight supports four LLM providers with native API integrations:
|
|
251
|
+
|
|
252
|
+
| Provider | Value | API |
|
|
253
|
+
|----------|-------|-----|
|
|
254
|
+
| [OpenRouter](https://openrouter.ai) | `openrouter` (default) | Access all models through a single API |
|
|
255
|
+
| [OpenAI](https://platform.openai.com) | `openai` | GPT-4o, GPT-4o-mini, etc. |
|
|
256
|
+
| [Google Gemini](https://ai.google.dev) | `gemini` | Gemini 2.5 Flash, Pro, etc. |
|
|
257
|
+
| [Anthropic Claude](https://console.anthropic.com) | `claude` | Claude Sonnet, Haiku, etc. |
|
|
258
|
+
|
|
259
|
+
Set the provider in `greenlight.yaml` or via CLI:
|
|
260
|
+
|
|
261
|
+
```yaml
|
|
262
|
+
provider: openai
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
greenlight run --provider gemini
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Only one provider is active at a time. OpenRouter is the default — it lets you access models from all vendors through a single API key, which is the easiest way to get started.
|
|
270
|
+
|
|
271
|
+
### Model selection
|
|
272
|
+
|
|
273
|
+
GreenLight uses the LLM in two distinct roles with different requirements:
|
|
274
|
+
|
|
275
|
+
- **Planner** — interprets the test steps, splits compound actions, and expands form-filling steps. This runs once per test case and benefits from a more capable model for consistent, correct results.
|
|
276
|
+
- **Pilot** — resolves individual steps against the live page (picking which element to click/type). This runs many times per test and should use a fast, inexpensive model to keep costs and execution time low.
|
|
277
|
+
|
|
278
|
+
Configure both in `greenlight.yaml`:
|
|
279
|
+
|
|
280
|
+
```yaml
|
|
281
|
+
provider: openrouter
|
|
282
|
+
model:
|
|
283
|
+
planner: anthropic/claude-sonnet-4 # smarter model, runs once per test
|
|
284
|
+
pilot: openai/gpt-4o-mini # fast model, runs per step
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Or use a single model for both roles:
|
|
288
|
+
|
|
289
|
+
```yaml
|
|
290
|
+
model: anthropic/claude-sonnet-4
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
The `--model` CLI flag sets both roles to the same model (useful for quick overrides):
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
greenlight run --model openai/gpt-4o
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Model names must match the provider's naming convention:
|
|
300
|
+
|
|
301
|
+
| Provider | Example model names |
|
|
302
|
+
|----------|-------------------|
|
|
303
|
+
| OpenRouter | `anthropic/claude-sonnet-4`, `openai/gpt-4o-mini`, `google/gemini-2.5-flash` |
|
|
304
|
+
| OpenAI | `gpt-4o`, `gpt-4o-mini` |
|
|
305
|
+
| Gemini | `gemini-2.5-flash`, `gemini-2.5-pro` |
|
|
306
|
+
| Claude | `claude-sonnet-4-20250514`, `claude-haiku-4-20250514` |
|
|
307
|
+
|
|
308
|
+
### Custom LLM endpoint
|
|
309
|
+
|
|
310
|
+
Each provider has a default API endpoint. You can override it with `llm_base_url` for proxies, self-hosted models, or compatible APIs:
|
|
311
|
+
|
|
312
|
+
```yaml
|
|
313
|
+
# Local Ollama (OpenAI-compatible)
|
|
314
|
+
provider: openai
|
|
315
|
+
llm_base_url: http://localhost:11434/v1
|
|
316
|
+
model: llama3
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
greenlight run --provider openai --llm-base-url http://localhost:11434/v1 --model llama3
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Tech stack
|
|
324
|
+
|
|
325
|
+
| Layer | Technology |
|
|
326
|
+
|-------|-----------|
|
|
327
|
+
| Browser automation | Playwright (Chromium) |
|
|
328
|
+
| Page representation | Accessibility tree with stable element refs |
|
|
329
|
+
| AI | OpenRouter (any OpenAI-compatible provider) |
|
|
330
|
+
| Plan caching | SHA-256 hash-based invalidation, `.greenlight/plans/` |
|
|
331
|
+
| Test definitions | YAML |
|
|
332
|
+
| Language | TypeScript (Node.js, ESM) |
|
|
333
|
+
|
|
334
|
+
## Architecture
|
|
335
|
+
|
|
336
|
+
```mermaid
|
|
337
|
+
flowchart TD
|
|
338
|
+
subgraph cli[greenlight CLI]
|
|
339
|
+
yaml[YAML Parser]
|
|
340
|
+
output[CLI Output<br/>step results, pass/fail]
|
|
341
|
+
|
|
342
|
+
subgraph runner[Test Orchestrator]
|
|
343
|
+
orchestrator[Run Loop<br/>suite loading, browser lifecycle,<br/>plan cache decisions]
|
|
344
|
+
planner[Plan Cache<br/>.greenlight/plans/]
|
|
345
|
+
|
|
346
|
+
subgraph pilot[The Pilot — discovery run]
|
|
347
|
+
state[Page State Capture<br/>a11y snapshot + stable refs]
|
|
348
|
+
llm[LLM Client<br/>plan, resolve, expand]
|
|
349
|
+
executor[Action Executor<br/>click, type, select, autocomplete,<br/>check, remember, compare, ...]
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
replay[Plan Runner<br/>cached replay, no LLM]
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
subgraph browser[Browser Layer]
|
|
357
|
+
chromium[(Chromium<br/>Browser Context<br/>staging site)]
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
yaml --> orchestrator
|
|
361
|
+
orchestrator -->|no cache| pilot
|
|
362
|
+
orchestrator -->|cached| replay
|
|
363
|
+
orchestrator -->|save plan| planner
|
|
364
|
+
planner -->|load plan| orchestrator
|
|
365
|
+
state --> llm
|
|
366
|
+
llm --> executor
|
|
367
|
+
state <--> chromium
|
|
368
|
+
executor --> chromium
|
|
369
|
+
replay <--> chromium
|
|
370
|
+
orchestrator --> output
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**Discovery run:** capture page state (a11y tree with stable refs) → LLM determines action → execute via Playwright → record for cache.
|
|
374
|
+
|
|
375
|
+
**Cached run:** replay stored actions directly via Playwright — no LLM calls, no API costs.
|
|
376
|
+
|
|
377
|
+
## Documentation
|
|
378
|
+
|
|
379
|
+
- [Specifications](docs/specifications.md) — full feature spec, technology decisions, MCP strategy
|
|
380
|
+
- [Implementation Plan](docs/implementation.md) — step-by-step build plan
|
|
381
|
+
|
|
382
|
+
## CI/CD
|
|
383
|
+
|
|
384
|
+
```yaml
|
|
385
|
+
- name: Run E2E tests
|
|
386
|
+
run: greenlight run -d staging --reporter json --output results.json
|
|
387
|
+
env:
|
|
388
|
+
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Exit code 0 on all-pass, non-zero on any failure.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright browser lifecycle management.
|
|
3
|
+
* Wraps launch, context creation, page creation, and cleanup.
|
|
4
|
+
*/
|
|
5
|
+
import { type Browser, type BrowserContext, type Page } from "playwright";
|
|
6
|
+
import type { RunConfig } from "../types.js";
|
|
7
|
+
export interface BrowserOptions {
|
|
8
|
+
headed: boolean;
|
|
9
|
+
viewport: {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/** Launch a Chromium browser instance. */
|
|
15
|
+
export declare function launchBrowser(config: BrowserOptions): Promise<Browser>;
|
|
16
|
+
/** Create an isolated browser context with configured viewport and test mode headers. */
|
|
17
|
+
export declare function createContext(browser: Browser, config: BrowserOptions): Promise<BrowserContext>;
|
|
18
|
+
/** Create a new page within a browser context and inject test mode global. */
|
|
19
|
+
export declare function createPage(context: BrowserContext): Promise<Page>;
|
|
20
|
+
/** Close the browser and all its contexts. */
|
|
21
|
+
export declare function closeBrowser(browser: Browser): Promise<void>;
|
|
22
|
+
/** Extract browser options from RunConfig. */
|
|
23
|
+
export declare function toBrowserOptions(config: RunConfig): BrowserOptions;
|
|
24
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/browser/browser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAY,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,IAAI,EAAE,MAAM,YAAY,CAAA;AACnF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,MAAM,WAAW,cAAc;IAC9B,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3C;AAED,0CAA0C;AAC1C,wBAAsB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAI5E;AAED,yFAAyF;AACzF,wBAAsB,aAAa,CAClC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,cAAc,GACpB,OAAO,CAAC,cAAc,CAAC,CAOzB;AAED,8EAA8E;AAC9E,wBAAsB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAUvE;AAED,8CAA8C;AAC9C,wBAAsB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAElE;AAED,8CAA8C;AAC9C,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,cAAc,CAKlE"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright browser lifecycle management.
|
|
3
|
+
* Wraps launch, context creation, page creation, and cleanup.
|
|
4
|
+
*/
|
|
5
|
+
import { chromium } from "playwright";
|
|
6
|
+
/** Launch a Chromium browser instance. */
|
|
7
|
+
export async function launchBrowser(config) {
|
|
8
|
+
return chromium.launch({
|
|
9
|
+
headless: !config.headed,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
/** Create an isolated browser context with configured viewport and test mode headers. */
|
|
13
|
+
export async function createContext(browser, config) {
|
|
14
|
+
return browser.newContext({
|
|
15
|
+
viewport: config.viewport,
|
|
16
|
+
extraHTTPHeaders: {
|
|
17
|
+
"X-E2E-Test": "true",
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/** Create a new page within a browser context and inject test mode global. */
|
|
22
|
+
export async function createPage(context) {
|
|
23
|
+
const page = await context.newPage();
|
|
24
|
+
await page.addInitScript(() => {
|
|
25
|
+
Object.defineProperty(window, "__E2E_TEST__", {
|
|
26
|
+
value: true,
|
|
27
|
+
writable: false,
|
|
28
|
+
configurable: false,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
return page;
|
|
32
|
+
}
|
|
33
|
+
/** Close the browser and all its contexts. */
|
|
34
|
+
export async function closeBrowser(browser) {
|
|
35
|
+
await browser.close();
|
|
36
|
+
}
|
|
37
|
+
/** Extract browser options from RunConfig. */
|
|
38
|
+
export function toBrowserOptions(config) {
|
|
39
|
+
return {
|
|
40
|
+
headed: config.headed,
|
|
41
|
+
viewport: config.viewport,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/browser/browser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAgD,MAAM,YAAY,CAAA;AAQnF,0CAA0C;AAC1C,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAsB;IACzD,OAAO,QAAQ,CAAC,MAAM,CAAC;QACtB,QAAQ,EAAE,CAAC,MAAM,CAAC,MAAM;KACxB,CAAC,CAAA;AACH,CAAC;AAED,yFAAyF;AACzF,MAAM,CAAC,KAAK,UAAU,aAAa,CAClC,OAAgB,EAChB,MAAsB;IAEtB,OAAO,OAAO,CAAC,UAAU,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,gBAAgB,EAAE;YACjB,YAAY,EAAE,MAAM;SACpB;KACD,CAAC,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAuB;IACvD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;IACpC,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE;QAC7B,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE;YAC7C,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,KAAK;SACnB,CAAC,CAAA;IACH,CAAC,CAAC,CAAA;IACF,OAAO,IAAI,CAAA;AACZ,CAAC;AAED,8CAA8C;AAC9C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAgB;IAClD,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;AACtB,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,gBAAgB,CAAC,MAAiB;IACjD,OAAO;QACN,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;KACzB,CAAA;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAA"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { DEFAULTS } from "../types.js";
|
|
5
|
+
import { loadProjectConfig } from "../config.js";
|
|
6
|
+
import { createTraceLogger } from "../pilot/trace.js";
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
import { glob } from "node:fs";
|
|
9
|
+
import { initGlobals } from "../globals.js";
|
|
10
|
+
import { runCommand, showPlanStatus } from "./run.js";
|
|
11
|
+
const program = new Command();
|
|
12
|
+
program
|
|
13
|
+
.name("greenlight")
|
|
14
|
+
.description("AI-driven E2E testing tool")
|
|
15
|
+
.version("0.1.0");
|
|
16
|
+
program
|
|
17
|
+
.command("run")
|
|
18
|
+
.description("Run test suites against a staging environment")
|
|
19
|
+
.argument("[suites...]", "paths to suite YAML files (overrides greenlight.yaml)")
|
|
20
|
+
.option("-t, --test <name>", "run only the test case matching this name")
|
|
21
|
+
.option("--base-url <url>", "override the suite base URL")
|
|
22
|
+
.option("-r, --reporter <format>", "output format: cli, json, or html")
|
|
23
|
+
.option("-o, --output <path>", "write report to file instead of stdout")
|
|
24
|
+
.option("--headed", "run browser in visible (headed) mode")
|
|
25
|
+
.option("-p, --parallel <n>", "number of test cases to run concurrently")
|
|
26
|
+
.option("--timeout <ms>", "per-step timeout in milliseconds")
|
|
27
|
+
.option("--model <model>", "LLM model identifier (e.g. anthropic/claude-sonnet-4)")
|
|
28
|
+
.option("--llm-base-url <url>", "base URL for the LLM API (override)")
|
|
29
|
+
.option("--provider <name>", "LLM provider: openrouter, openai, gemini, or claude")
|
|
30
|
+
.option("-d, --deployment <name>", "select a named deployment from greenlight.yaml")
|
|
31
|
+
.option("--debug", "enable verbose debug output", false)
|
|
32
|
+
.option("--trace", "log timestamped browser events for performance analysis", false)
|
|
33
|
+
.option("--discover", "force full discovery run, ignore cached plans", false)
|
|
34
|
+
.option("--on-drift <mode>", 'behavior on plan drift: "fail" or "rerun" (default: fail)')
|
|
35
|
+
.option("--plan-status", "show cached plan status for all test cases and exit", false)
|
|
36
|
+
.action(async (suitesArg, opts) => {
|
|
37
|
+
// Load project config (greenlight.yaml)
|
|
38
|
+
const projectConfig = await loadProjectConfig(opts.deployment);
|
|
39
|
+
// Resolve suite files: CLI args > greenlight.yaml > error
|
|
40
|
+
let suiteFiles;
|
|
41
|
+
if (suitesArg.length > 0) {
|
|
42
|
+
suiteFiles = suitesArg.map((s) => resolve(s));
|
|
43
|
+
}
|
|
44
|
+
else if (projectConfig?.suites) {
|
|
45
|
+
suiteFiles = projectConfig.suites.map((s) => resolve(s));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.error("No suite files specified. Provide them as arguments or in greenlight.yaml.");
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
// Build config: CLI flags > greenlight.yaml > built-in defaults
|
|
52
|
+
const config = {
|
|
53
|
+
suiteFiles,
|
|
54
|
+
testFilter: opts.test,
|
|
55
|
+
baseUrl: opts.baseUrl ?? projectConfig?.base_url,
|
|
56
|
+
reporter: parseReporter(opts.reporter ?? projectConfig?.reporter ?? DEFAULTS.reporter),
|
|
57
|
+
outputPath: opts.output ? resolve(opts.output) : undefined,
|
|
58
|
+
headed: opts.headed ?? projectConfig?.headed ?? DEFAULTS.headed,
|
|
59
|
+
parallel: opts.parallel
|
|
60
|
+
? parseInt(opts.parallel, 10)
|
|
61
|
+
: (projectConfig?.parallel ?? DEFAULTS.parallel),
|
|
62
|
+
timeout: opts.timeout
|
|
63
|
+
? parseInt(opts.timeout, 10)
|
|
64
|
+
: (projectConfig?.timeout ?? DEFAULTS.timeout),
|
|
65
|
+
viewport: projectConfig?.viewport
|
|
66
|
+
? { ...projectConfig.viewport }
|
|
67
|
+
: { ...DEFAULTS.viewport },
|
|
68
|
+
model: opts.model ?? projectConfig?.model ?? DEFAULTS.model,
|
|
69
|
+
provider: parseProvider(opts.provider ??
|
|
70
|
+
projectConfig?.provider ??
|
|
71
|
+
DEFAULTS.provider),
|
|
72
|
+
llmBaseUrl: opts.llmBaseUrl ?? projectConfig?.llm_base_url,
|
|
73
|
+
discover: opts.discover,
|
|
74
|
+
onDrift: parseOnDrift(opts.onDrift ?? DEFAULTS.onDrift),
|
|
75
|
+
};
|
|
76
|
+
// Initialize global runtime state (debug, trace)
|
|
77
|
+
initGlobals({
|
|
78
|
+
debug: opts.debug,
|
|
79
|
+
trace: createTraceLogger(opts.trace),
|
|
80
|
+
});
|
|
81
|
+
// Resolve glob patterns in suite file paths
|
|
82
|
+
const resolvedFiles = await resolveGlobs(config.suiteFiles);
|
|
83
|
+
if (resolvedFiles.length === 0) {
|
|
84
|
+
console.error("No suite files found matching:", config.suiteFiles);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
// --plan-status: show cache status and exit
|
|
88
|
+
if (opts.planStatus) {
|
|
89
|
+
await showPlanStatus(resolvedFiles, process.cwd(), config);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
await runCommand(config, resolvedFiles);
|
|
93
|
+
});
|
|
94
|
+
function parseProvider(value) {
|
|
95
|
+
if (value === "openrouter" ||
|
|
96
|
+
value === "openai" ||
|
|
97
|
+
value === "gemini" ||
|
|
98
|
+
value === "claude") {
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
console.error(`Invalid provider "${value}". Must be openrouter, openai, gemini, or claude.`);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
function parseReporter(value) {
|
|
105
|
+
if (value === "cli" || value === "json" || value === "html") {
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
console.error(`Invalid reporter "${value}". Must be cli, json, or html.`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
function parseOnDrift(value) {
|
|
112
|
+
if (value === "fail" || value === "rerun") {
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
console.error(`Invalid --on-drift value "${value}". Must be "fail" or "rerun".`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
/** Expand glob patterns into concrete file paths. */
|
|
119
|
+
async function resolveGlobs(patterns) {
|
|
120
|
+
const files = [];
|
|
121
|
+
for (const pattern of patterns) {
|
|
122
|
+
// If pattern has no glob chars, treat as literal path
|
|
123
|
+
if (!pattern.includes("*")) {
|
|
124
|
+
files.push(pattern);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const matches = await new Promise((res, rej) => {
|
|
128
|
+
glob(pattern, (err, result) => {
|
|
129
|
+
if (err)
|
|
130
|
+
rej(err);
|
|
131
|
+
else
|
|
132
|
+
res(result);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
files.push(...matches);
|
|
136
|
+
}
|
|
137
|
+
return files;
|
|
138
|
+
}
|
|
139
|
+
program.parse();
|
|
140
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RunConfig } from "../types.js";
|
|
2
|
+
/** Show plan status for all test cases across all suites. */
|
|
3
|
+
export declare function showPlanStatus(suiteFiles: string[], cwd: string, config: RunConfig): Promise<void>;
|
|
4
|
+
/**
|
|
5
|
+
* Run all test suites according to the resolved configuration.
|
|
6
|
+
* This is the main test execution entry point called by the CLI.
|
|
7
|
+
*/
|
|
8
|
+
export declare function runCommand(config: RunConfig, resolvedFiles: string[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAuC5C,6DAA6D;AAC7D,wBAAsB,cAAc,CACnC,UAAU,EAAE,MAAM,EAAE,EACpB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,SAAS,GACf,OAAO,CAAC,IAAI,CAAC,CAgDf;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC/B,MAAM,EAAE,SAAS,EACjB,aAAa,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,IAAI,CAAC,CAuQf"}
|