@sun-asterisk/sungen 3.1.1 → 3.1.2-beta.100
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 +4 -428
- package/dist/capabilities/builtins.d.ts +31 -0
- package/dist/capabilities/builtins.d.ts.map +1 -0
- package/dist/capabilities/builtins.js +84 -0
- package/dist/capabilities/builtins.js.map +1 -0
- package/dist/capabilities/context-router.d.ts +34 -0
- package/dist/capabilities/context-router.d.ts.map +1 -0
- package/dist/capabilities/context-router.js +49 -0
- package/dist/capabilities/context-router.js.map +1 -0
- package/dist/capabilities/context.d.ts +51 -0
- package/dist/capabilities/context.d.ts.map +1 -0
- package/dist/capabilities/context.js +17 -0
- package/dist/capabilities/context.js.map +1 -0
- package/dist/capabilities/discover.d.ts +2 -0
- package/dist/capabilities/discover.d.ts.map +1 -0
- package/dist/capabilities/discover.js +48 -0
- package/dist/capabilities/discover.js.map +1 -0
- package/dist/capabilities/registry.d.ts +90 -0
- package/dist/capabilities/registry.d.ts.map +1 -0
- package/dist/capabilities/registry.js +43 -0
- package/dist/capabilities/registry.js.map +1 -0
- package/dist/capabilities/sensor.d.ts +49 -0
- package/dist/capabilities/sensor.d.ts.map +1 -0
- package/dist/capabilities/sensor.js +3 -0
- package/dist/capabilities/sensor.js.map +1 -0
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +7 -3
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/index.js +10 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/exporters/spec-parser.d.ts.map +1 -1
- package/dist/exporters/spec-parser.js +4 -1
- package/dist/exporters/spec-parser.js.map +1 -1
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts +1 -0
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +1 -0
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
- package/dist/generators/test-generator/code-generator.d.ts +18 -9
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +76 -119
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +0 -10
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +10 -47
- package/dist/generators/test-generator/patterns/index.js.map +1 -1
- package/dist/generators/test-generator/template-engine.d.ts +1 -0
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
- package/dist/generators/test-generator/template-engine.js +1 -1
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/harness/annotation-overrides.d.ts +9 -0
- package/dist/harness/annotation-overrides.d.ts.map +1 -0
- package/dist/harness/annotation-overrides.js +36 -0
- package/dist/harness/annotation-overrides.js.map +1 -0
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +35 -7
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/catalog/drivers.yaml +35 -12
- package/dist/harness/parse.d.ts +1 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +13 -4
- package/dist/harness/parse.js.map +1 -1
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +4 -3
- package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +1 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +3 -0
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +4 -3
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +1 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +3 -0
- package/dist/orchestrator/templates/specs-api.d.ts +19 -0
- package/dist/orchestrator/templates/specs-api.d.ts.map +1 -0
- package/dist/orchestrator/templates/specs-api.js +128 -0
- package/dist/orchestrator/templates/specs-api.js.map +1 -0
- package/dist/orchestrator/templates/specs-api.ts +101 -0
- package/package.json +7 -30
- package/src/capabilities/builtins.ts +85 -0
- package/src/capabilities/context-router.ts +66 -0
- package/src/capabilities/context.ts +46 -0
- package/src/capabilities/discover.ts +42 -0
- package/src/capabilities/registry.ts +111 -0
- package/src/capabilities/sensor.ts +47 -0
- package/src/cli/commands/generate.ts +7 -3
- package/src/cli/index.ts +10 -1
- package/src/exporters/spec-parser.ts +4 -1
- package/src/generators/test-generator/adapters/adapter-interface.ts +1 -1
- package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
- package/src/generators/test-generator/code-generator.ts +71 -118
- package/src/generators/test-generator/patterns/index.ts +9 -35
- package/src/generators/test-generator/template-engine.ts +2 -2
- package/src/harness/annotation-overrides.ts +25 -0
- package/src/harness/audit.ts +37 -8
- package/src/harness/catalog/drivers.yaml +35 -12
- package/src/harness/parse.ts +7 -2
- package/src/index.ts +30 -0
- package/src/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +4 -3
- package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +1 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +3 -0
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +4 -3
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +1 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +3 -0
- package/src/orchestrator/templates/specs-api.ts +101 -0
- package/dist/generators/test-generator/patterns/assertion-patterns.d.ts +0 -7
- package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +0 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.js +0 -626
- package/dist/generators/test-generator/patterns/assertion-patterns.js.map +0 -1
- package/dist/generators/test-generator/patterns/capture-patterns.d.ts +0 -21
- package/dist/generators/test-generator/patterns/capture-patterns.d.ts.map +0 -1
- package/dist/generators/test-generator/patterns/capture-patterns.js +0 -87
- package/dist/generators/test-generator/patterns/capture-patterns.js.map +0 -1
- package/dist/generators/test-generator/patterns/database-patterns.d.ts +0 -6
- package/dist/generators/test-generator/patterns/database-patterns.d.ts.map +0 -1
- package/dist/generators/test-generator/patterns/database-patterns.js +0 -95
- package/dist/generators/test-generator/patterns/database-patterns.js.map +0 -1
- package/dist/generators/test-generator/patterns/form-patterns.d.ts +0 -6
- package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +0 -1
- package/dist/generators/test-generator/patterns/form-patterns.js +0 -160
- package/dist/generators/test-generator/patterns/form-patterns.js.map +0 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.d.ts +0 -6
- package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +0 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.js +0 -433
- package/dist/generators/test-generator/patterns/interaction-patterns.js.map +0 -1
- package/dist/generators/test-generator/patterns/keyboard-patterns.d.ts +0 -7
- package/dist/generators/test-generator/patterns/keyboard-patterns.d.ts.map +0 -1
- package/dist/generators/test-generator/patterns/keyboard-patterns.js +0 -47
- package/dist/generators/test-generator/patterns/keyboard-patterns.js.map +0 -1
- package/dist/generators/test-generator/patterns/navigation-patterns.d.ts +0 -6
- package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +0 -1
- package/dist/generators/test-generator/patterns/navigation-patterns.js +0 -125
- package/dist/generators/test-generator/patterns/navigation-patterns.js.map +0 -1
- package/dist/generators/test-generator/patterns/scope-patterns.d.ts +0 -7
- package/dist/generators/test-generator/patterns/scope-patterns.d.ts.map +0 -1
- package/dist/generators/test-generator/patterns/scope-patterns.js +0 -36
- package/dist/generators/test-generator/patterns/scope-patterns.js.map +0 -1
- package/dist/generators/test-generator/patterns/scroll-patterns.d.ts +0 -7
- package/dist/generators/test-generator/patterns/scroll-patterns.d.ts.map +0 -1
- package/dist/generators/test-generator/patterns/scroll-patterns.js +0 -25
- package/dist/generators/test-generator/patterns/scroll-patterns.js.map +0 -1
- package/dist/generators/test-generator/patterns/setup-patterns.d.ts +0 -6
- package/dist/generators/test-generator/patterns/setup-patterns.d.ts.map +0 -1
- package/dist/generators/test-generator/patterns/setup-patterns.js +0 -72
- package/dist/generators/test-generator/patterns/setup-patterns.js.map +0 -1
- package/dist/generators/test-generator/patterns/table-patterns.d.ts +0 -19
- package/dist/generators/test-generator/patterns/table-patterns.d.ts.map +0 -1
- package/dist/generators/test-generator/patterns/table-patterns.js +0 -239
- package/dist/generators/test-generator/patterns/table-patterns.js.map +0 -1
- package/docs/orchestration-spec.md +0 -267
- package/src/generators/test-generator/patterns/assertion-patterns.ts +0 -691
- package/src/generators/test-generator/patterns/capture-patterns.ts +0 -97
- package/src/generators/test-generator/patterns/database-patterns.ts +0 -96
- package/src/generators/test-generator/patterns/form-patterns.ts +0 -167
- package/src/generators/test-generator/patterns/interaction-patterns.ts +0 -465
- package/src/generators/test-generator/patterns/keyboard-patterns.ts +0 -51
- package/src/generators/test-generator/patterns/navigation-patterns.ts +0 -140
- package/src/generators/test-generator/patterns/scope-patterns.ts +0 -40
- package/src/generators/test-generator/patterns/scroll-patterns.ts +0 -27
- package/src/generators/test-generator/patterns/setup-patterns.ts +0 -76
- package/src/generators/test-generator/patterns/table-patterns.ts +0 -279
package/README.md
CHANGED
|
@@ -1,431 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @sun-asterisk/sungen
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
**Gherkin + Selector YAML + Test Data → Playwright .spec.ts**
|
|
3
|
+
Deterministic E2E test compiler — Gherkin + selector YAML + test-data → Playwright tests, with a Capability Planner and a Harness/audit layer.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
This is the **core kernel**: Gherkin→intent IR, the selector contract, the harness/audit + planner, the CLI, and the **capability SPI** that drivers plug into. It embeds no test runtime — UI, DB, and API capabilities ship as separate `@sungen/driver-*` packages discovered at runtime.
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Sungen is a deterministic compiler that converts Gherkin features into Playwright tests.
|
|
11
|
-
|
|
12
|
-
**v2 architecture**: AI generates the input files (Gherkin + selectors), sungen compiles to tests. No AI inside sungen itself.
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
AI (Copilot/Claude + MCP Playwright) → visits URL → generates files
|
|
16
|
-
sungen generate → compiles .feature + selectors.yaml + data.yaml → .spec.ts
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Quick Start
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
# Install
|
|
25
|
-
npm install -g @sun-asterisk/sungen@latest
|
|
26
|
-
|
|
27
|
-
# Initialize project (creates AI rules + directory structure)
|
|
28
|
-
sungen init --base-url https://your-app.com
|
|
29
|
-
|
|
30
|
-
# Use AI slash commands to generate tests
|
|
31
|
-
# In GitHub Copilot Chat:
|
|
32
|
-
/sungen-add-screen login /auth/login
|
|
33
|
-
/sungen-create-test login
|
|
34
|
-
/sungen-run-test login
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
## Generate from Figma (web-only, no desktop)
|
|
40
|
-
|
|
41
|
-
Use a Figma design URL as the source for screen specs and test scaffolding — no live app needed.
|
|
42
|
-
|
|
43
|
-
### Quick start (5 steps)
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
# 1. Authenticate once — stores PAT in .env (gitignored)
|
|
47
|
-
sungen figma auth set
|
|
48
|
-
|
|
49
|
-
# 2. Scaffold screen from a Figma frame URL
|
|
50
|
-
sungen add --screen my-screen --figma "https://www.figma.com/design/<key>/<name>?node-id=<id>"
|
|
51
|
-
|
|
52
|
-
# 3. Generate test cases (AI reads spec_figma.md + ui/*.png)
|
|
53
|
-
# /sungen:create-test my-screen
|
|
54
|
-
|
|
55
|
-
# 4. Review syntax and coverage
|
|
56
|
-
# /sungen:review my-screen
|
|
57
|
-
|
|
58
|
-
# 5. Generate selectors + compile + run (no live page needed for compilation)
|
|
59
|
-
# /sungen:run-test my-screen
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### PAT setup
|
|
63
|
-
|
|
64
|
-
1. Go to **figma.com → Settings → Security → Personal access tokens**
|
|
65
|
-
2. Click **Generate new token**
|
|
66
|
-
3. Set scopes: `file_content:read`, `file_metadata:read`
|
|
67
|
-
4. Copy the token — it is shown once
|
|
68
|
-
5. Run `sungen figma auth set` and paste when prompted
|
|
69
|
-
|
|
70
|
-
### Figma URL format
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
https://www.figma.com/design/<fileKey>/<fileName>?node-id=<nodeId>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Example:
|
|
77
|
-
```
|
|
78
|
-
https://www.figma.com/design/AbCdEfGhIjKlMnOpQrSt/My-App?node-id=1-23
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
The `node-id` selects a specific frame or component. Right-click any frame in Figma → "Copy link to selection" to get the correct URL.
|
|
82
|
-
|
|
83
|
-
### Flags
|
|
84
|
-
|
|
85
|
-
| Flag | Default | Description |
|
|
86
|
-
|------|---------|-------------|
|
|
87
|
-
| `--refresh` | false | Re-fetch image even if cached |
|
|
88
|
-
| `--scale <n>` | 2 | Export resolution multiplier (1–4) |
|
|
89
|
-
| `--hi-res` | false | Alias for `--scale 4` |
|
|
90
|
-
|
|
91
|
-
### Cache
|
|
92
|
-
|
|
93
|
-
Rendered PNGs are cached in `.sungen/figma-cache/` (gitignored). On re-run, cached images are used unless `--refresh` is passed. Cache entries are keyed by file version — a new Figma publish busts the cache automatically.
|
|
94
|
-
|
|
95
|
-
### Migration notes
|
|
96
|
-
|
|
97
|
-
Existing screens are unaffected. To add Figma specs to an existing screen:
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
sungen add --screen <existing-screen> --figma <figma-url>
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
This adds `spec_figma.md` + `requirements/ui/*.png` without touching `spec.md` or `.feature` files (idempotent).
|
|
104
|
-
|
|
105
|
-
### Troubleshooting
|
|
106
|
-
|
|
107
|
-
| Error | Cause | Fix |
|
|
108
|
-
|-------|-------|-----|
|
|
109
|
-
| `401 Unauthorized` | PAT invalid or expired | Re-run `sungen figma auth set` |
|
|
110
|
-
| `403 Forbidden` | No access to file | Request access from file owner in Figma |
|
|
111
|
-
| `429 Too Many Requests` | Rate limited | Automatic exponential backoff; wait and retry |
|
|
112
|
-
| Expired S3 URL | Image URL expired (~30 days) | Re-run `sungen add --figma` — PNGs are downloaded locally so this is rare |
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## CLI Commands
|
|
117
|
-
|
|
118
|
-
| Command | Description |
|
|
119
|
-
|---------|-------------|
|
|
120
|
-
| `sungen init` | Scaffold project + generate AI rules |
|
|
121
|
-
| `sungen add --screen <name> --path <path>` | Create screen with empty templates |
|
|
122
|
-
| `sungen add --screen <name> --figma <url>` | Scaffold from Figma frame (alternative to `--path`) |
|
|
123
|
-
| `sungen add --screen <name> --feature <name>` | Add feature file to existing screen |
|
|
124
|
-
| `sungen generate --screen <name>` | Compile Gherkin → Playwright .spec.ts |
|
|
125
|
-
| `sungen generate --all` | Compile all screens |
|
|
126
|
-
| `sungen makeauth <role>` | Capture browser auth state (SSO support) |
|
|
127
|
-
| `sungen figma auth set` | Store Figma PAT (one-time setup) |
|
|
128
|
-
| `sungen figma auth check` | Verify PAT is stored and valid |
|
|
129
|
-
| `sungen figma auth clear` | Remove stored PAT |
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
## AI Slash Commands
|
|
134
|
-
|
|
135
|
-
| Action | GitHub Copilot (VS Code) | Claude Code |
|
|
136
|
-
|--------|--------------------------|-------------|
|
|
137
|
-
| Add screen | `/sungen-add-screen login /auth/login` | `/sungen:add-screen login /auth/login` |
|
|
138
|
-
| Create test cases | `/sungen-create-test login` | `/sungen:create-test login` |
|
|
139
|
-
| Compile & run tests | `/sungen-run-test login` | `/sungen:run-test login` |
|
|
140
|
-
|
|
141
|
-
### create-test — Create test cases
|
|
142
|
-
|
|
143
|
-
AI acts as a **Senior QA Engineer**. Supports 3 input modes:
|
|
144
|
-
- **Live page** — AI visits the page via MCP Playwright, analyzes UI elements
|
|
145
|
-
- **Figma / screenshots** — AI generates tests from static designs
|
|
146
|
-
- **Update mode** — AI detects existing tests and asks: add new, expand, or replace
|
|
147
|
-
|
|
148
|
-
Output: `.feature` + `test-data.yaml` (no selectors — those are generated during run-test)
|
|
149
|
-
|
|
150
|
-
### run-test — Compile & run
|
|
151
|
-
|
|
152
|
-
AI acts as a **Senior Developer**:
|
|
153
|
-
1. Explores live page → generates `selectors.yaml`
|
|
154
|
-
2. Compiles: `sungen generate --screen <name>`
|
|
155
|
-
3. Runs: `npx playwright test`
|
|
156
|
-
4. If fail → auto-fixes selectors/test-data → retry (up to 5 attempts)
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## Workflow
|
|
161
|
-
|
|
162
|
-
### 1. Add a screen
|
|
163
|
-
|
|
164
|
-
```bash
|
|
165
|
-
sungen add --screen awards --path /awards
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
Creates:
|
|
169
|
-
```
|
|
170
|
-
qa/screens/awards/
|
|
171
|
-
├── features/awards.feature # Gherkin template
|
|
172
|
-
├── selectors/awards.yaml # Selector YAML (page entry pre-filled)
|
|
173
|
-
└── test-data/awards.yaml # Test data (empty)
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### 2. AI generates Gherkin + selectors
|
|
177
|
-
|
|
178
|
-
Use your AI assistant (GitHub Copilot or Claude Code) with MCP Playwright to:
|
|
179
|
-
1. Navigate to the page URL
|
|
180
|
-
2. Take an accessibility snapshot
|
|
181
|
-
3. Generate `.feature` + `selectors.yaml` + `test-data.yaml`
|
|
182
|
-
|
|
183
|
-
The AI rules in `.github/prompts/` and `.claude/` (created by `sungen init`) teach the AI the correct syntax.
|
|
184
|
-
|
|
185
|
-
### 3. Compile + run
|
|
186
|
-
|
|
187
|
-
```bash
|
|
188
|
-
sungen generate --screen awards
|
|
189
|
-
npx playwright test
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### 4. Fix failures (AI verify loop)
|
|
193
|
-
|
|
194
|
-
When tests fail → AI reads error → fixes `selectors.yaml` → recompile → re-run.
|
|
195
|
-
|
|
196
|
-
| Error | Fix in selectors.yaml |
|
|
197
|
-
|---|---|
|
|
198
|
-
| strict mode: resolved to N elements | Add `exact: true` or `scope` or `nth` |
|
|
199
|
-
| element(s) not found | Fix `name`, `type`, or `value` |
|
|
200
|
-
| getByText matched too many | Add `match: 'exact'` or `nth` |
|
|
201
|
-
|
|
202
|
-
---
|
|
203
|
-
|
|
204
|
-
## Gherkin Syntax
|
|
205
|
-
|
|
206
|
-
**Pattern**: `[Keyword] User <Action> [Target Name] <Target Type> <with {{Value}}> <is State>`
|
|
207
|
-
|
|
208
|
-
- `[Target]` → selector reference → lookup in `selectors/*.yaml`
|
|
209
|
-
- `{{Value}}` → test data reference → lookup in `test-data/*.yaml`
|
|
210
|
-
|
|
211
|
-
### Example
|
|
212
|
-
|
|
213
|
-
```gherkin
|
|
214
|
-
@feature_auth
|
|
215
|
-
Feature: Login Screen
|
|
216
|
-
Path: /auth/login
|
|
217
|
-
|
|
218
|
-
@auto @smoke
|
|
219
|
-
Scenario: User logs in successfully
|
|
220
|
-
Given User is on [Login] page
|
|
221
|
-
When User fill [Email] field with {{valid_email}}
|
|
222
|
-
And User fill [Password] field with {{valid_password}}
|
|
223
|
-
And User click [Login] button
|
|
224
|
-
Then User see [Dashboard] page
|
|
225
|
-
And User see [Welcome] heading with {{success_message}}
|
|
226
|
-
And User see [Logout] button is enabled
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
---
|
|
230
|
-
|
|
231
|
-
## Pattern Shapes (18 categories)
|
|
232
|
-
|
|
233
|
-
### Actions
|
|
234
|
-
|
|
235
|
-
| Pattern | Example |
|
|
236
|
-
|---|---|
|
|
237
|
-
| Simple click | `User click [Submit] button` |
|
|
238
|
-
| Click dynamic list | `User click [Order] row with {{order_name}}` |
|
|
239
|
-
| Fill field | `User fill [Email] field with {{email}}` |
|
|
240
|
-
| Fill search | `User fill [Global] search with {{keyword}}` |
|
|
241
|
-
| Check/Uncheck | `User check [Remember me] checkbox` / `User check [Notification] toggle` |
|
|
242
|
-
| Select dropdown | `User select [Country] dropdown with {{country}}` |
|
|
243
|
-
| Upload file | `User upload [Avatar] uploader with {{path}}` |
|
|
244
|
-
| Double click | `User double click [Cell] element` |
|
|
245
|
-
| Hover | `User hover [Info] icon` / `User hover [Order] row` |
|
|
246
|
-
| Clear | `User clear [Search] field` |
|
|
247
|
-
|
|
248
|
-
> **click + with rule**: `with {{Value}}` only for dynamic lists (`row`, `item`, `card`, `option`). Never for static elements (`button`, `link`, `icon`, `tab`).
|
|
249
|
-
|
|
250
|
-
### Browser Alert
|
|
251
|
-
|
|
252
|
-
| Pattern | Example |
|
|
253
|
-
|---|---|
|
|
254
|
-
| Accept | `User click [OK] alert` (also: Accept, Yes, Confirm) |
|
|
255
|
-
| Dismiss | `User click [Cancel] alert` (also: Dismiss, No) |
|
|
256
|
-
| Fill prompt | `User fill [Name] alert with {{value}}` |
|
|
257
|
-
|
|
258
|
-
> Alert steps must appear **before** the triggering action. Generates `page.once('dialog', ...)`.
|
|
259
|
-
|
|
260
|
-
### Keyboard
|
|
261
|
-
|
|
262
|
-
| Pattern | Example |
|
|
263
|
-
|---|---|
|
|
264
|
-
| Global key | `User press Escape key` |
|
|
265
|
-
| Key on element | `User press Enter on [Search] field` |
|
|
266
|
-
|
|
267
|
-
### Navigation
|
|
268
|
-
|
|
269
|
-
| Pattern | Example |
|
|
270
|
-
|---|---|
|
|
271
|
-
| Open page | `User is on [Login] page` |
|
|
272
|
-
| See page (URL) | `User see [Dashboard] page` |
|
|
273
|
-
|
|
274
|
-
### Wait
|
|
275
|
-
|
|
276
|
-
| Pattern | Example |
|
|
277
|
-
|---|---|
|
|
278
|
-
| Wait timeout | `User wait for 3 seconds` |
|
|
279
|
-
| Wait element | `User wait for [Modal] dialog` |
|
|
280
|
-
| Wait hidden | `User wait for [Loading] spinner is hidden` |
|
|
281
|
-
| Wait with data | `User wait for [Dialog] dialog with {{title}}` |
|
|
282
|
-
|
|
283
|
-
### Assertions (7 verify patterns)
|
|
284
|
-
|
|
285
|
-
| Pattern | Assertion | Example |
|
|
286
|
-
|---|---|---|
|
|
287
|
-
| Visibility | `toBeVisible()` | `User see [Success] message` |
|
|
288
|
-
| Hidden | `toBeHidden()` | `User see [Ads] modal is hidden` |
|
|
289
|
-
| Text content | `toHaveText()` | `User see [Error] message with {{err_msg}}` |
|
|
290
|
-
| Input value | `toHaveValue()` | `User see [Email] field with {{user_email}}` |
|
|
291
|
-
| Partial text | `toContainText()` | `User see [Title] text contains {{partial}}` |
|
|
292
|
-
| Component state | `toBeDisabled()` etc. | `User see [Submit] button is disabled` |
|
|
293
|
-
| Page context | `toHaveURL()` | `User see [Dashboard] page` |
|
|
294
|
-
|
|
295
|
-
**States**: `hidden`, `visible`, `disabled`, `enabled`, `checked`, `unchecked`, `focused`, `empty`, `loading`, `selected`, `sorted ascending`, `sorted descending`
|
|
296
|
-
|
|
297
|
-
### Table
|
|
298
|
-
|
|
299
|
-
| Pattern | Example |
|
|
300
|
-
|---|---|
|
|
301
|
-
| Column exists | `User see [Email] column in [Users] table` |
|
|
302
|
-
| Row exists | `User see [Username] row in [Users] table with {{name}}` |
|
|
303
|
-
| Row hidden | `User see [Username] row in [Users] table with {{name}} is hidden` |
|
|
304
|
-
| Row count | `User see [Users] table with {{count}}` |
|
|
305
|
-
| Empty table | `User see [Users] table is empty` |
|
|
306
|
-
| Cell value | `User see [Status] column with {{status}}` (row scoped) |
|
|
307
|
-
| Action in row | `User click [Edit] button in [Users] table with {{name}}` |
|
|
308
|
-
|
|
309
|
-
### Scope
|
|
310
|
-
|
|
311
|
-
| Pattern | Example |
|
|
312
|
-
|---|---|
|
|
313
|
-
| Scroll | `User scroll to [Footer] section` |
|
|
314
|
-
| Frame enter | `User switch to [Payment] frame` |
|
|
315
|
-
| Frame exit | `User switch to [main] frame` |
|
|
316
|
-
|
|
317
|
-
### Element Types
|
|
318
|
-
|
|
319
|
-
| Group | Types |
|
|
320
|
-
|---|---|
|
|
321
|
-
| **Context** | `page` `dialog` `modal` `drawer` `tab` `alert` `overlay` `step` |
|
|
322
|
-
| **Input** | `field` `textarea` `search` `dropdown` `option` `checkbox` `radio` `toggle` `uploader` `slider` `date-picker` |
|
|
323
|
-
| **Trigger** | `button` `link` `icon` `menuitem` `tag` |
|
|
324
|
-
| **Data** | `table` `row` `column` `cell` `list` `item` `card` `section` |
|
|
325
|
-
| **Feedback** | `message` `header` `label` `text` `tooltip` `badge` `breadcrumb` `image` |
|
|
326
|
-
| **System** | `key` `frame` `spinner` `progressbar` |
|
|
327
|
-
|
|
328
|
-
---
|
|
329
|
-
|
|
330
|
-
## Selector YAML v2 — NFC Key Format
|
|
331
|
-
|
|
332
|
-
Sungen v2 uses **Unicode NFC normalization** for selector keys — not dot notation. Keys use spaces, support Vietnamese, Japanese, and all Unicode characters.
|
|
333
|
-
|
|
334
|
-
```yaml
|
|
335
|
-
# NFC keys (spaces, not dots)
|
|
336
|
-
email address:
|
|
337
|
-
type: 'role'
|
|
338
|
-
value: 'textbox'
|
|
339
|
-
name: 'Email Address'
|
|
340
|
-
exact: true
|
|
341
|
-
|
|
342
|
-
# Japanese
|
|
343
|
-
ログイン:
|
|
344
|
-
type: 'role'
|
|
345
|
-
value: 'button'
|
|
346
|
-
name: 'ログイン'
|
|
347
|
-
|
|
348
|
-
# Page navigation
|
|
349
|
-
login:
|
|
350
|
-
type: 'page'
|
|
351
|
-
value: '/auth/login'
|
|
352
|
-
|
|
353
|
-
# Scoped element
|
|
354
|
-
header login:
|
|
355
|
-
type: 'role'
|
|
356
|
-
value: 'button'
|
|
357
|
-
name: 'Login'
|
|
358
|
-
scope: 'main navigation'
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
**Locator priority**: `data-testid` > `role+name` > `label` > `text` > `CSS`
|
|
362
|
-
|
|
363
|
-
**Types**: `testid`, `role`, `text`, `label`, `placeholder`, `locator`, `page`, `upload`, `frame`
|
|
364
|
-
|
|
365
|
-
---
|
|
366
|
-
|
|
367
|
-
## Tags
|
|
368
|
-
|
|
369
|
-
| Tag | Level | Description |
|
|
370
|
-
|-----|-------|-------------|
|
|
371
|
-
| `@auto` | Scenario | Standard scenario, ready for automation |
|
|
372
|
-
| `@manual` | Scenario | Skip in generation |
|
|
373
|
-
| `@smoke` / `@regression` | Scenario | Test suite grouping |
|
|
374
|
-
| `@auth:<role>` | Feature/Scenario | Use Playwright auth storage state |
|
|
375
|
-
| `@no-auth` | Scenario | Disable inherited auth |
|
|
376
|
-
| `@steps:<name>` | Scenario | Mark as reusable step block (base scenario) |
|
|
377
|
-
| `@extend:<name>` | Scenario | Prepend Given→When from `@steps` block (skip Then) |
|
|
378
|
-
|
|
379
|
-
**Auth precedence**: Scenario > Feature > None
|
|
380
|
-
|
|
381
|
-
### Reusable Steps
|
|
382
|
-
|
|
383
|
-
```gherkin
|
|
384
|
-
@auth:user @steps:open-dialog
|
|
385
|
-
Scenario: Open kudos dialog
|
|
386
|
-
Given User is on [kudo] page
|
|
387
|
-
When User click [Notifications] button
|
|
388
|
-
And User click [Write Kudos] menuitem
|
|
389
|
-
Then User see [panel] dialog with {{kudo_title}}
|
|
390
|
-
|
|
391
|
-
@auth:user @extend:open-dialog
|
|
392
|
-
Scenario: User sends a thank you message
|
|
393
|
-
Given User is on [panel] dialog with {{kudo_title}}
|
|
394
|
-
When User fill [Search] field with {{teammate_name}}
|
|
395
|
-
And User click [Send] button
|
|
396
|
-
Then User see [panel] dialog with {{kudo_title}} is hidden
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
---
|
|
400
|
-
|
|
401
|
-
## Project Structure
|
|
402
|
-
|
|
403
|
-
```
|
|
404
|
-
qa/screens/<name>/
|
|
405
|
-
├── features/ # Gherkin .feature files
|
|
406
|
-
├── selectors/ # Element selector YAML mappings
|
|
407
|
-
└── test-data/ # Test data YAML values
|
|
408
|
-
|
|
409
|
-
specs/generated/<name>/
|
|
410
|
-
└── <feature>.spec.ts # Generated Playwright tests
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
---
|
|
414
|
-
|
|
415
|
-
## Documentation
|
|
416
|
-
|
|
417
|
-
Full documentation at **[sungen.sun-asterisk.vn](https://sungen.sun-asterisk.vn)**
|
|
418
|
-
|
|
419
|
-
- **[Installation Guide](https://sun-asterisk.github.io/sungen/docs/guides/installation)** — Windows (Git Bash + VS Code + Copilot), macOS, Linux
|
|
420
|
-
- **[Quick Start](https://sun-asterisk.github.io/sungen/docs/guides/quick-start)** — From zero to running tests
|
|
421
|
-
- **[Selector YAML Guide](https://sun-asterisk.github.io/sungen/docs/guides/selector-override)** — NFC keys, auto-infer, Japanese/Vietnamese support
|
|
422
|
-
- **[Tips & Troubleshooting](https://sun-asterisk.github.io/sungen/docs/guides/tips)** — When AI gets stuck fixing tests
|
|
423
|
-
- **[Gherkin Patterns](https://sun-asterisk.github.io/sungen/docs/gherkin/patterns)** — Full grammar, all patterns, compiler rules
|
|
424
|
-
|
|
425
|
-
---
|
|
426
|
-
|
|
427
|
-
## License
|
|
428
|
-
|
|
429
|
-
MIT License - see LICENSE file for details.
|
|
430
|
-
|
|
431
|
-
**Made with ❤️ by eqe team (engineer & quality) — Sun Asterisk**
|
|
7
|
+
See the project README and docs site for usage.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-core capability registrations (Capability SPI).
|
|
3
|
+
*
|
|
4
|
+
* `ui` has moved to `@sungen/driver-ui` (R5.4); `db` and `api` still register from here via
|
|
5
|
+
* `LOCAL_DRIVERS` (they relocate in R5.5/R5.6), and `core` (the generic expect/lint/verification
|
|
6
|
+
* layer) always stays in core. Discovery (`discover.ts`) loads the external driver(s) first, then
|
|
7
|
+
* these, then `core` — preserving the historical pattern composition order, so compiled output is
|
|
8
|
+
* byte-identical (golden + audit snapshots are the proof).
|
|
9
|
+
*/
|
|
10
|
+
import type { CapabilityRegistry } from './registry';
|
|
11
|
+
/**
|
|
12
|
+
* core — generic, capability-agnostic steps (data-vs-data `expect`) + the advisory @cases/@query
|
|
13
|
+
* lint and the gate-level `verification` sensor. Stays in core (not a driver) — the always-present
|
|
14
|
+
* generic layer. All three real capabilities (ui/db/api) now ship as `@sungen/driver-*` packages.
|
|
15
|
+
*/
|
|
16
|
+
export declare function registerCoreCapability(registry: CapabilityRegistry): void;
|
|
17
|
+
/**
|
|
18
|
+
* In-core driver manifest — now empty: `ui`, `db`, and `api` all ship as `@sungen/driver-*` packages
|
|
19
|
+
* loaded by discovery (`discover.ts`). Kept (empty) as the seam for any future in-core capability;
|
|
20
|
+
* `core` itself registers separately via `registerCoreCapability`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const LOCAL_DRIVERS: ReadonlyArray<{
|
|
23
|
+
capability: string;
|
|
24
|
+
register: (r: CapabilityRegistry) => void;
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated Use `discoverAndRegisterCapabilities()` from `./discover`. Kept as a thin alias so
|
|
28
|
+
* existing call-sites/tests don't break during R5; removed once everything routes through discovery.
|
|
29
|
+
*/
|
|
30
|
+
export declare function registerBuiltinCapabilities(): void;
|
|
31
|
+
//# sourceMappingURL=builtins.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../../src/capabilities/builtins.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAgDrD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAMzE;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAA;CAAE,CAAM,CAAC;AAElH;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAGlD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LOCAL_DRIVERS = void 0;
|
|
4
|
+
exports.registerCoreCapability = registerCoreCapability;
|
|
5
|
+
exports.registerBuiltinCapabilities = registerBuiltinCapabilities;
|
|
6
|
+
const data_driven_lint_1 = require("../harness/data-driven-lint");
|
|
7
|
+
const query_catalog_1 = require("../harness/query-catalog");
|
|
8
|
+
const expect_patterns_1 = require("../generators/test-generator/patterns/expect-patterns");
|
|
9
|
+
/** Advisory @cases/@query lint, exposed through the Sensor SPI (generate-time). */
|
|
10
|
+
const dataDrivenLintSensor = {
|
|
11
|
+
id: 'data-driven-lint',
|
|
12
|
+
capability: 'core',
|
|
13
|
+
kind: 'advisory',
|
|
14
|
+
run: ({ dir, cwd }) => (0, data_driven_lint_1.lintDataDriven)(dir, cwd).map((w) => ({
|
|
15
|
+
sensorId: 'data-driven-lint',
|
|
16
|
+
capability: 'core',
|
|
17
|
+
scenario: w.scenario,
|
|
18
|
+
message: w.message,
|
|
19
|
+
severity: 'warn',
|
|
20
|
+
})),
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Generic `verification` gate sensor: every referenced named query (`@query`) must resolve in its
|
|
24
|
+
* catalog, and the catalog must lint clean. An unresolved/invalid reference is a gate-level error.
|
|
25
|
+
* (On a project with no `@query` refs it yields nothing.) `query-catalog` lives in core's harness —
|
|
26
|
+
* shared with the data-driven advisory lint. The `@api` counterpart now lives in
|
|
27
|
+
* `@sungen/driver-api`'s `api-verification` gate sensor. Message prefix unchanged → audit byte-identical.
|
|
28
|
+
*/
|
|
29
|
+
const verificationGateSensor = {
|
|
30
|
+
id: 'verification',
|
|
31
|
+
capability: 'core',
|
|
32
|
+
kind: 'gate',
|
|
33
|
+
run: ({ screenName, scenarios, cwd }) => {
|
|
34
|
+
const findings = [];
|
|
35
|
+
const fail = (msg) => findings.push({ sensorId: 'verification', capability: 'core', message: `VERIFICATION-FAIL: ${msg}`, severity: 'error' });
|
|
36
|
+
const queryRefs = new Set();
|
|
37
|
+
for (const s of scenarios)
|
|
38
|
+
for (const r of s.queryRefs ?? [])
|
|
39
|
+
queryRefs.add(r);
|
|
40
|
+
for (const name of queryRefs) {
|
|
41
|
+
try {
|
|
42
|
+
(0, query_catalog_1.resolveQuery)(name, screenName, cwd);
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
fail(e?.message || `query "${name}" does not resolve`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (queryRefs.size) {
|
|
49
|
+
try {
|
|
50
|
+
for (const err of (0, query_catalog_1.lintCatalog)(screenName, null, cwd).errors)
|
|
51
|
+
fail(err);
|
|
52
|
+
}
|
|
53
|
+
catch { /* no catalog */ }
|
|
54
|
+
}
|
|
55
|
+
return findings;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* core — generic, capability-agnostic steps (data-vs-data `expect`) + the advisory @cases/@query
|
|
60
|
+
* lint and the gate-level `verification` sensor. Stays in core (not a driver) — the always-present
|
|
61
|
+
* generic layer. All three real capabilities (ui/db/api) now ship as `@sungen/driver-*` packages.
|
|
62
|
+
*/
|
|
63
|
+
function registerCoreCapability(registry) {
|
|
64
|
+
registry.register({
|
|
65
|
+
id: 'core',
|
|
66
|
+
patterns: [...expect_patterns_1.expectPatterns],
|
|
67
|
+
sensors: [dataDrivenLintSensor, verificationGateSensor],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* In-core driver manifest — now empty: `ui`, `db`, and `api` all ship as `@sungen/driver-*` packages
|
|
72
|
+
* loaded by discovery (`discover.ts`). Kept (empty) as the seam for any future in-core capability;
|
|
73
|
+
* `core` itself registers separately via `registerCoreCapability`.
|
|
74
|
+
*/
|
|
75
|
+
exports.LOCAL_DRIVERS = [];
|
|
76
|
+
/**
|
|
77
|
+
* @deprecated Use `discoverAndRegisterCapabilities()` from `./discover`. Kept as a thin alias so
|
|
78
|
+
* existing call-sites/tests don't break during R5; removed once everything routes through discovery.
|
|
79
|
+
*/
|
|
80
|
+
function registerBuiltinCapabilities() {
|
|
81
|
+
// Lazy import avoids a cycle (discover imports builtins).
|
|
82
|
+
require('./discover').discoverAndRegisterCapabilities();
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=builtins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtins.js","sourceRoot":"","sources":["../../src/capabilities/builtins.ts"],"names":[],"mappings":";;;AA8DA,wDAMC;AAaD,kEAGC;AAzED,kEAA6D;AAC7D,4DAAqE;AACrE,2FAAuF;AAEvF,mFAAmF;AACnF,MAAM,oBAAoB,GAA8B;IACtD,EAAE,EAAE,kBAAkB;IACtB,UAAU,EAAE,MAAM;IAClB,IAAI,EAAE,UAAU;IAChB,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CACpB,IAAA,iCAAc,EAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnC,QAAQ,EAAE,kBAAkB;QAC5B,UAAU,EAAE,MAAM;QAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,QAAQ,EAAE,MAAe;KAC1B,CAAC,CAAC;CACN,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,sBAAsB,GAAsB;IAChD,EAAE,EAAE,cAAc;IAClB,UAAU,EAAE,MAAM;IAClB,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE;QACtC,MAAM,QAAQ,GAAG,EAA+B,CAAC;QACjD,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAEvJ,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;QACpC,KAAK,MAAM,CAAC,IAAI,SAAS;YAAE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE;gBAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC/E,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,IAAI,CAAC;gBAAC,IAAA,4BAAY,EAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;YAAC,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBAAC,IAAI,CAAC,CAAC,EAAE,OAAO,IAAI,UAAU,IAAI,oBAAoB,CAAC,CAAC;YAAC,CAAC;QACzH,CAAC;QACD,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;YACnB,IAAI,CAAC;gBAAC,KAAK,MAAM,GAAG,IAAI,IAAA,2BAAW,EAAC,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,MAAM;oBAAE,IAAI,CAAC,GAAG,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,gBAAgB,CAAC,CAAC;QAC5G,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF;;;;GAIG;AACH,SAAgB,sBAAsB,CAAC,QAA4B;IACjE,QAAQ,CAAC,QAAQ,CAAC;QAChB,EAAE,EAAE,MAAM;QACV,QAAQ,EAAE,CAAC,GAAG,gCAAc,CAAC;QAC7B,OAAO,EAAE,CAAC,oBAAoB,EAAE,sBAAsB,CAAC;KACxD,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACU,QAAA,aAAa,GAAqF,EAAE,CAAC;AAElH;;;GAGG;AACH,SAAgB,2BAA2B;IACzC,0DAA0D;IAC1D,OAAO,CAAC,YAAY,CAAC,CAAC,+BAA+B,EAAE,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface RouteTask {
|
|
2
|
+
/** What is being worked on now. */
|
|
3
|
+
target: {
|
|
4
|
+
kind: string;
|
|
5
|
+
id: string;
|
|
6
|
+
};
|
|
7
|
+
/** What artifact is being produced/checked: 'feature' | 'scenario' | 'selectors' | 'apis.yaml' | … */
|
|
8
|
+
artifact: string;
|
|
9
|
+
/** Capability tags present on the target (e.g. ['@query'], ['@api'], ['@cases']). */
|
|
10
|
+
tags?: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface RoutePlan {
|
|
13
|
+
/** Applicable capability ids: the default/implicit one + any whose annotations match the tags. */
|
|
14
|
+
capabilities: string[];
|
|
15
|
+
/** Gate sensors to run for this task (ids). */
|
|
16
|
+
gateSensorIds: string[];
|
|
17
|
+
/** Advisory sensors to run for this task (ids). */
|
|
18
|
+
advisorySensorIds: string[];
|
|
19
|
+
/** Scope: the target slice + a token/context budget (null = unbounded for now). */
|
|
20
|
+
scope: {
|
|
21
|
+
target: RouteTask['target'];
|
|
22
|
+
budget: number | null;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
declare class ContextRouter {
|
|
26
|
+
/** Which capabilities a set of tags activates: the default capability + any owning a present tag. */
|
|
27
|
+
capabilitiesFor(tags?: string[]): string[];
|
|
28
|
+
/** Compute the routing plan for a task (deterministic). */
|
|
29
|
+
route(task: RouteTask): RoutePlan;
|
|
30
|
+
}
|
|
31
|
+
/** Process-wide singleton. */
|
|
32
|
+
export declare const contextRouter: ContextRouter;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=context-router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-router.d.ts","sourceRoot":"","sources":["../../src/capabilities/context-router.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,SAAS;IACxB,mCAAmC;IACnC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,sGAAsG;IACtG,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,kGAAkG;IAClG,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,+CAA+C;IAC/C,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,mDAAmD;IACnD,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,mFAAmF;IACnF,KAAK,EAAE;QAAE,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAC/D;AAED,cAAM,aAAa;IACjB,qGAAqG;IACrG,eAAe,CAAC,IAAI,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE;IAU9C,2DAA2D;IAC3D,KAAK,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS;CAclC;AAED,8BAA8B;AAC9B,eAAO,MAAM,aAAa,eAAsB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.contextRouter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* ContextRouter (Capability SPI, R1 — skeleton).
|
|
6
|
+
*
|
|
7
|
+
* The overload guard: for each unit of work it decides the minimal slice of Context + the relevant
|
|
8
|
+
* capabilities + the gate to load, so a project with many drivers doesn't gather everything for
|
|
9
|
+
* every artifact (cost scales by *target*, not *repo × drivers*). See
|
|
10
|
+
* `docs/spec/sungen_capability_spi_spec.md` §5.
|
|
11
|
+
*
|
|
12
|
+
* R1 scope (skeleton, deterministic, not yet enforced in the pipeline): it computes the routing
|
|
13
|
+
* plan — which capabilities apply (the default/implicit one + any whose annotation tags appear),
|
|
14
|
+
* and which sensors gate the task. Scope-trimming/token-budget enforcement is wired in a later step;
|
|
15
|
+
* for now `scope.budget` is a placeholder (null = unbounded) and the plan is advisory.
|
|
16
|
+
*/
|
|
17
|
+
const registry_1 = require("./registry");
|
|
18
|
+
class ContextRouter {
|
|
19
|
+
/** Which capabilities a set of tags activates: the default capability + any owning a present tag. */
|
|
20
|
+
capabilitiesFor(tags = []) {
|
|
21
|
+
const ids = new Set();
|
|
22
|
+
const def = registry_1.capabilityRegistry.defaultCapabilityId();
|
|
23
|
+
if (def)
|
|
24
|
+
ids.add(def);
|
|
25
|
+
for (const cap of registry_1.capabilityRegistry.all()) {
|
|
26
|
+
if ((cap.annotations ?? []).some((a) => tags.includes(a)))
|
|
27
|
+
ids.add(cap.id);
|
|
28
|
+
}
|
|
29
|
+
return [...ids];
|
|
30
|
+
}
|
|
31
|
+
/** Compute the routing plan for a task (deterministic). */
|
|
32
|
+
route(task) {
|
|
33
|
+
const capabilities = this.capabilitiesFor(task.tags ?? []);
|
|
34
|
+
// Generic sensors (no capability, or the always-on `core`) run regardless of the task's tags;
|
|
35
|
+
// capability-specific sensors run only when their capability is in scope.
|
|
36
|
+
const inScope = (capId) => capId == null || capId === 'core' || capabilities.includes(capId);
|
|
37
|
+
const gateSensorIds = registry_1.capabilityRegistry.sensors('gate').filter((s) => inScope(s.capability)).map((s) => s.id);
|
|
38
|
+
const advisorySensorIds = registry_1.capabilityRegistry.sensors('advisory').filter((s) => inScope(s.capability)).map((s) => s.id);
|
|
39
|
+
return {
|
|
40
|
+
capabilities,
|
|
41
|
+
gateSensorIds,
|
|
42
|
+
advisorySensorIds,
|
|
43
|
+
scope: { target: task.target, budget: null },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Process-wide singleton. */
|
|
48
|
+
exports.contextRouter = new ContextRouter();
|
|
49
|
+
//# sourceMappingURL=context-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-router.js","sourceRoot":"","sources":["../../src/capabilities/context-router.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;GAYG;AACH,yCAAgD;AAsBhD,MAAM,aAAa;IACjB,qGAAqG;IACrG,eAAe,CAAC,OAAiB,EAAE;QACjC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;QAC9B,MAAM,GAAG,GAAG,6BAAkB,CAAC,mBAAmB,EAAE,CAAC;QACrD,IAAI,GAAG;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtB,KAAK,MAAM,GAAG,IAAI,6BAAkB,CAAC,GAAG,EAAE,EAAE,CAAC;YAC3C,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;IAClB,CAAC;IAED,2DAA2D;IAC3D,KAAK,CAAC,IAAe;QACnB,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC3D,8FAA8F;QAC9F,0EAA0E;QAC1E,MAAM,OAAO,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,MAAM,IAAI,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACtG,MAAM,aAAa,GAAG,6BAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/G,MAAM,iBAAiB,GAAG,6BAAkB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACvH,OAAO;YACL,YAAY;YACZ,aAAa;YACb,iBAAiB;YACjB,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE;SAC7C,CAAC;IACJ,CAAC;CACF;AAED,8BAA8B;AACjB,QAAA,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC"}
|