@sun-asterisk/sungen 2.7.0-beta.0 → 3.0.0-beta.71
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 +1 -1
- package/dist/cli/commands/add.js +3 -3
- package/dist/cli/commands/add.js.map +1 -1
- package/dist/cli/commands/audit.d.ts +3 -0
- package/dist/cli/commands/audit.d.ts.map +1 -0
- package/dist/cli/commands/audit.js +134 -0
- package/dist/cli/commands/audit.js.map +1 -0
- package/dist/cli/commands/blindspot.d.ts +3 -0
- package/dist/cli/commands/blindspot.d.ts.map +1 -0
- package/dist/cli/commands/blindspot.js +58 -0
- package/dist/cli/commands/blindspot.js.map +1 -0
- package/dist/cli/commands/challenge.d.ts +3 -0
- package/dist/cli/commands/challenge.d.ts.map +1 -0
- package/dist/cli/commands/challenge.js +102 -0
- package/dist/cli/commands/challenge.js.map +1 -0
- package/dist/cli/commands/feedback.d.ts +3 -0
- package/dist/cli/commands/feedback.d.ts.map +1 -0
- package/dist/cli/commands/feedback.js +72 -0
- package/dist/cli/commands/feedback.js.map +1 -0
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +22 -0
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/ledger.d.ts +3 -0
- package/dist/cli/commands/ledger.d.ts.map +1 -0
- package/dist/cli/commands/ledger.js +71 -0
- package/dist/cli/commands/ledger.js.map +1 -0
- package/dist/cli/commands/manifest.d.ts +3 -0
- package/dist/cli/commands/manifest.d.ts.map +1 -0
- package/dist/cli/commands/manifest.js +101 -0
- package/dist/cli/commands/manifest.js.map +1 -0
- package/dist/cli/commands/script-check.d.ts +3 -0
- package/dist/cli/commands/script-check.d.ts.map +1 -0
- package/dist/cli/commands/script-check.js +97 -0
- package/dist/cli/commands/script-check.js.map +1 -0
- package/dist/cli/commands/trace.d.ts +3 -0
- package/dist/cli/commands/trace.d.ts.map +1 -0
- package/dist/cli/commands/trace.js +110 -0
- package/dist/cli/commands/trace.js.map +1 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +22 -9
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/index.js +16 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-assertion.hbs +7 -0
- package/dist/generators/test-generator/patterns/capture-patterns.d.ts +16 -0
- package/dist/generators/test-generator/patterns/capture-patterns.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/capture-patterns.js +54 -0
- package/dist/generators/test-generator/patterns/capture-patterns.js.map +1 -0
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +2 -0
- package/dist/generators/test-generator/patterns/index.js.map +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +1 -0
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.d.ts +5 -0
- package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.js +17 -0
- package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
- package/dist/harness/audit.d.ts +24 -0
- package/dist/harness/audit.d.ts.map +1 -0
- package/dist/harness/audit.js +115 -0
- package/dist/harness/audit.js.map +1 -0
- package/dist/harness/blindspot.d.ts +15 -0
- package/dist/harness/blindspot.d.ts.map +1 -0
- package/dist/harness/blindspot.js +85 -0
- package/dist/harness/blindspot.js.map +1 -0
- package/dist/harness/catalog/universal-viewpoints.yaml +114 -0
- package/dist/harness/challenge.d.ts +21 -0
- package/dist/harness/challenge.d.ts.map +1 -0
- package/dist/harness/challenge.js +151 -0
- package/dist/harness/challenge.js.map +1 -0
- package/dist/harness/feedback.d.ts +29 -0
- package/dist/harness/feedback.d.ts.map +1 -0
- package/dist/harness/feedback.js +106 -0
- package/dist/harness/feedback.js.map +1 -0
- package/dist/harness/intent.d.ts +11 -0
- package/dist/harness/intent.d.ts.map +1 -0
- package/dist/harness/intent.js +86 -0
- package/dist/harness/intent.js.map +1 -0
- package/dist/harness/ledger.d.ts +42 -0
- package/dist/harness/ledger.d.ts.map +1 -0
- package/dist/harness/ledger.js +171 -0
- package/dist/harness/ledger.js.map +1 -0
- package/dist/harness/manifest.d.ts +42 -0
- package/dist/harness/manifest.d.ts.map +1 -0
- package/dist/harness/manifest.js +209 -0
- package/dist/harness/manifest.js.map +1 -0
- package/dist/harness/parse.d.ts +22 -0
- package/dist/harness/parse.d.ts.map +1 -0
- package/dist/harness/parse.js +163 -0
- package/dist/harness/parse.js.map +1 -0
- package/dist/harness/script-check.d.ts +16 -0
- package/dist/harness/script-check.d.ts.map +1 -0
- package/dist/harness/script-check.js +169 -0
- package/dist/harness/script-check.js.map +1 -0
- package/dist/harness/secret-scan.d.ts +8 -0
- package/dist/harness/secret-scan.d.ts.map +1 -0
- package/dist/harness/secret-scan.js +88 -0
- package/dist/harness/secret-scan.js.map +1 -0
- package/dist/harness/sensors.d.ts +88 -0
- package/dist/harness/sensors.d.ts.map +1 -0
- package/dist/harness/sensors.js +232 -0
- package/dist/harness/sensors.js.map +1 -0
- package/dist/harness/trace.d.ts +31 -0
- package/dist/harness/trace.d.ts.map +1 -0
- package/dist/harness/trace.js +173 -0
- package/dist/harness/trace.js.map +1 -0
- package/dist/orchestrator/ai-rules-updater.d.ts +1 -0
- package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
- package/dist/orchestrator/ai-rules-updater.js +55 -11
- package/dist/orchestrator/ai-rules-updater.js.map +1 -1
- package/dist/orchestrator/figma/spec-figma-renderer.d.ts +2 -2
- package/dist/orchestrator/figma/spec-figma-renderer.js +2 -2
- package/dist/orchestrator/figma/spec-figma-section-renderers.d.ts +1 -1
- package/dist/orchestrator/figma/spec-figma-section-renderers.js +1 -1
- package/dist/orchestrator/project-initializer.d.ts +5 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +26 -6
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/templates/ai-instructions/claude-agent-challenge.md +46 -0
- package/dist/orchestrator/templates/ai-instructions/claude-agent-discovery.md +32 -0
- package/dist/orchestrator/templates/ai-instructions/claude-agent-reviewer.md +37 -0
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-flow.md +3 -3
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +5 -5
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +45 -13
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-design.md +12 -0
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-feedback.md +36 -0
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-review.md +27 -30
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +8 -3
- package/dist/orchestrator/templates/ai-instructions/claude-config.md +1 -4
- package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-mcp.md +82 -0
- package/dist/orchestrator/templates/ai-instructions/{github-skill-sungen-figma-source.md → claude-skill-capture-mode-figma-pat.md} +14 -48
- package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-mode-live.md +60 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-mode-local.md +38 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-capture.md +35 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-harness-audit.md +84 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +57 -11
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +41 -31
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +53 -1
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-flow.md +3 -3
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +4 -4
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +27 -11
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-design.md +13 -0
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-feedback.md +24 -0
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-review.md +20 -30
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +6 -3
- package/dist/orchestrator/templates/ai-instructions/copilot-config.md +1 -4
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-mcp.md +82 -0
- package/{src/orchestrator/templates/ai-instructions/claude-skill-figma-source.md → dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-pat.md} +14 -48
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-live.md +60 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-local.md +38 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture.md +35 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-harness-audit.md +84 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +62 -16
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +41 -31
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +40 -1
- package/dist/orchestrator/templates/qa-context.md +90 -0
- package/dist/orchestrator/templates/readme.md +16 -13
- package/dist/orchestrator/templates/specs-test-data.ts +9 -0
- package/dist/tools/figma/figma-auth.d.ts +5 -2
- package/dist/tools/figma/figma-auth.d.ts.map +1 -1
- package/dist/tools/figma/figma-auth.js +19 -9
- package/dist/tools/figma/figma-auth.js.map +1 -1
- package/docs/orchestration-spec.md +267 -0
- package/package.json +10 -6
- package/src/cli/commands/add.ts +3 -3
- package/src/cli/commands/audit.ts +92 -0
- package/src/cli/commands/blindspot.ts +48 -0
- package/src/cli/commands/challenge.ts +55 -0
- package/src/cli/commands/feedback.ts +65 -0
- package/src/cli/commands/generate.ts +19 -0
- package/src/cli/commands/ledger.ts +61 -0
- package/src/cli/commands/manifest.ts +55 -0
- package/src/cli/commands/script-check.ts +50 -0
- package/src/cli/commands/trace.ts +60 -0
- package/src/cli/commands/update.ts +30 -10
- package/src/cli/index.ts +16 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-assertion.hbs +7 -0
- package/src/generators/test-generator/patterns/capture-patterns.ts +59 -0
- package/src/generators/test-generator/patterns/index.ts +2 -0
- package/src/generators/test-generator/step-mapper.ts +1 -0
- package/src/generators/test-generator/utils/data-resolver.ts +20 -0
- package/src/harness/audit.ts +112 -0
- package/src/harness/blindspot.ts +51 -0
- package/src/harness/catalog/universal-viewpoints.yaml +114 -0
- package/src/harness/challenge.ts +131 -0
- package/src/harness/feedback.ts +84 -0
- package/src/harness/intent.ts +58 -0
- package/src/harness/ledger.ts +155 -0
- package/src/harness/manifest.ts +173 -0
- package/src/harness/parse.ts +145 -0
- package/src/harness/script-check.ts +149 -0
- package/src/harness/secret-scan.ts +51 -0
- package/src/harness/sensors.ts +279 -0
- package/src/harness/trace.ts +138 -0
- package/src/orchestrator/ai-rules-updater.ts +57 -10
- package/src/orchestrator/figma/spec-figma-renderer.ts +2 -2
- package/src/orchestrator/figma/spec-figma-section-renderers.ts +1 -1
- package/src/orchestrator/project-initializer.ts +30 -7
- package/src/orchestrator/templates/ai-instructions/claude-agent-challenge.md +46 -0
- package/src/orchestrator/templates/ai-instructions/claude-agent-discovery.md +32 -0
- package/src/orchestrator/templates/ai-instructions/claude-agent-reviewer.md +37 -0
- package/src/orchestrator/templates/ai-instructions/claude-cmd-add-flow.md +3 -3
- package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +5 -5
- package/src/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +45 -13
- package/src/orchestrator/templates/ai-instructions/claude-cmd-design.md +12 -0
- package/src/orchestrator/templates/ai-instructions/claude-cmd-feedback.md +36 -0
- package/src/orchestrator/templates/ai-instructions/claude-cmd-review.md +27 -30
- package/src/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +8 -3
- package/src/orchestrator/templates/ai-instructions/claude-config.md +1 -4
- package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-mcp.md +82 -0
- package/{dist/orchestrator/templates/ai-instructions/copilot-skill-figma-source.md → src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-pat.md} +14 -48
- package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-live.md +60 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-local.md +38 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-capture.md +35 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-harness-audit.md +84 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +57 -11
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +41 -31
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +53 -1
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-flow.md +3 -3
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +4 -4
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +27 -11
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-design.md +13 -0
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-feedback.md +24 -0
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-review.md +20 -30
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +6 -3
- package/src/orchestrator/templates/ai-instructions/copilot-config.md +1 -4
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-mcp.md +82 -0
- package/{dist/orchestrator/templates/ai-instructions/claude-skill-figma-source.md → src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-pat.md} +14 -48
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-live.md +60 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-local.md +38 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture.md +35 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-harness-audit.md +84 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +62 -16
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +41 -31
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +40 -1
- package/src/orchestrator/templates/qa-context.md +90 -0
- package/src/orchestrator/templates/readme.md +16 -13
- package/src/orchestrator/templates/specs-test-data.ts +9 -0
- package/src/tools/figma/figma-auth.ts +20 -9
- package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-figma.md +0 -142
- package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-live.md +0 -112
- package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-local.md +0 -73
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-figma.md +0 -142
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-live.md +0 -112
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-local.md +0 -73
- package/src/orchestrator/templates/ai-instructions/claude-skill-capture-figma.md +0 -142
- package/src/orchestrator/templates/ai-instructions/claude-skill-capture-live.md +0 -112
- package/src/orchestrator/templates/ai-instructions/claude-skill-capture-local.md +0 -73
- package/src/orchestrator/templates/ai-instructions/copilot-skill-figma-source.md +0 -151
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-figma.md +0 -142
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-live.md +0 -112
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-local.md +0 -73
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-figma-source.md +0 -151
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
name: sungen-figma-source
|
|
3
|
-
description: 'Figma URL → spec_figma.md envelope + LLM-synthesized narrative from cached raw node JSON. Auto-loaded when --figma flag present or spec_figma.md exists.'
|
|
4
|
-
user-invocable: false
|
|
5
|
-
---
|
|
1
|
+
# Capture mode: figma-pat
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Auto-load triggers (any one is sufficient):
|
|
10
|
-
|
|
11
|
-
- Any sungen AI command invoked with `--figma` flag
|
|
12
|
-
- `requirements/spec_figma.md` exists in the screen directory
|
|
13
|
-
- User mentions a Figma URL or says "generate from Figma"
|
|
14
|
-
|
|
15
|
-
---
|
|
3
|
+
Figma URL → `spec_figma.md` envelope + LLM-synthesized narrative from cached raw node JSON. This mode is the active one when any of these is true: a sungen command was invoked with `--figma`, `requirements/spec_figma.md` exists, or the user says "generate from Figma".
|
|
16
4
|
|
|
17
5
|
## Prerequisites
|
|
18
6
|
|
|
@@ -22,8 +10,6 @@ Auto-load triggers (any one is sufficient):
|
|
|
22
10
|
|
|
23
11
|
**Never paste the PAT into any transcript, spec file, or commit.**
|
|
24
12
|
|
|
25
|
-
---
|
|
26
|
-
|
|
27
13
|
## Two-Layer Architecture
|
|
28
14
|
|
|
29
15
|
`spec_figma.md` has two layers separated by the `<!-- SYNTHESIS-BELOW -->` marker:
|
|
@@ -31,34 +17,27 @@ Auto-load triggers (any one is sufficient):
|
|
|
31
17
|
| Layer | Producer | Overwrite Rule |
|
|
32
18
|
|---|---|---|
|
|
33
19
|
| **Envelope** (above marker) | sungen CLI | Regenerated each `sungen figma` run — deterministic |
|
|
34
|
-
| **Narrative** (below marker) | This
|
|
20
|
+
| **Narrative** (below marker) | This mode (LLM) | Replaced on re-synthesis — everything from marker to EOF |
|
|
35
21
|
|
|
36
22
|
The envelope contains: YAML frontmatter, Frame metadata, Screenshots. The narrative is synthesized by YOU from the cached raw Figma node JSON.
|
|
37
23
|
|
|
38
|
-
---
|
|
39
|
-
|
|
40
24
|
## Inputs You Read
|
|
41
25
|
|
|
42
26
|
The scaffolder persists a raw (unfiltered) Figma node tree to:
|
|
43
|
-
|
|
44
27
|
```
|
|
45
28
|
.sungen/figma-cache/<fileKey>/<versionId>/<nodeId>-raw.json
|
|
46
29
|
```
|
|
47
|
-
|
|
48
30
|
Read this file + the envelope frontmatter of `requirements/spec_figma.md` + any PNGs under `requirements/ui/`. You MUST NOT call the Figma REST API directly — the PAT is not available to you.
|
|
49
31
|
|
|
50
|
-
---
|
|
51
|
-
|
|
52
32
|
## Synthesis Task
|
|
53
33
|
|
|
54
|
-
Append 7 narrative sections below `<!-- SYNTHESIS-BELOW -->`. Each
|
|
34
|
+
Append 7 narrative sections below `<!-- SYNTHESIS-BELOW -->`. Each is inferred from the raw node tree (names, types, `characters`, layout bounds, auto-layout direction, componentProperties):
|
|
55
35
|
|
|
56
36
|
### 1. Purpose
|
|
57
37
|
One paragraph. What screen is this? Primary user goal? Infer from frame name + top-level text + dominant CTA.
|
|
58
38
|
|
|
59
39
|
### 2. ASCII Layout
|
|
60
|
-
Rough spatial sketch using box characters. Reflect top-bottom / left-right ordering from absoluteBoundingBox. Keep under ~20 lines
|
|
61
|
-
|
|
40
|
+
Rough spatial sketch using box characters. Reflect top-bottom / left-right ordering from absoluteBoundingBox. Keep under ~20 lines:
|
|
62
41
|
```
|
|
63
42
|
┌──────────────────────────────────────┐
|
|
64
43
|
│ [Logo] [Sign In] │
|
|
@@ -72,55 +51,46 @@ Rough spatial sketch using box characters. Reflect top-bottom / left-right order
|
|
|
72
51
|
```
|
|
73
52
|
|
|
74
53
|
### 3. Regions
|
|
75
|
-
Bulleted list of
|
|
54
|
+
Bulleted list of major layout regions (header, sidebar, main, footer, modal…) with a one-line purpose each. Use auto-layout frames as region hints.
|
|
76
55
|
|
|
77
56
|
### 4. Actions
|
|
78
|
-
Every interactive element the user can trigger
|
|
79
|
-
|
|
57
|
+
Every interactive element the user can trigger (button, link, icon-button, menu-item, toggle…):
|
|
80
58
|
```
|
|
81
59
|
- **<Action name>** — <what it does> (source: <node name>)
|
|
82
60
|
```
|
|
83
61
|
|
|
84
62
|
### 5. Form Fields
|
|
85
|
-
Every input
|
|
86
|
-
|
|
63
|
+
Every input. Include label, type (text/email/password/select/checkbox/radio/textarea/date), required hint if inferable, placeholder:
|
|
87
64
|
```
|
|
88
65
|
| Label | Type | Required | Placeholder |
|
|
89
66
|
|---|---|---|---|
|
|
90
67
|
```
|
|
91
|
-
|
|
92
|
-
Omit entirely (write `_none_`) if no inputs exist.
|
|
68
|
+
Omit (write `_none_`) if no inputs exist.
|
|
93
69
|
|
|
94
70
|
### 6. Data Columns
|
|
95
|
-
If the screen shows a table
|
|
71
|
+
If the screen shows a table/list/card grid — enumerate the columns/fields per row. Otherwise `_none_`.
|
|
96
72
|
|
|
97
73
|
### 7. Navigation
|
|
98
|
-
Outgoing links, tab bars, breadcrumbs, back buttons — anything that moves
|
|
99
|
-
|
|
100
|
-
---
|
|
74
|
+
Outgoing links, tab bars, breadcrumbs, back buttons — anything that moves to another screen. Include explicit nav components and implicit CTAs that navigate.
|
|
101
75
|
|
|
102
76
|
## Synthesis Workflow
|
|
103
77
|
|
|
104
78
|
1. Read `requirements/spec_figma.md` — note `file_key`, `node_id`, `figma_version_id` from frontmatter
|
|
105
79
|
2. Read `.sungen/figma-cache/<file_key>/<figma_version_id>/<safe_node_id>-raw.json` (colons in node_id become underscores)
|
|
106
|
-
3. Traverse the tree. Collect
|
|
80
|
+
3. Traverse the tree. Collect names, types, `characters`, `componentProperties`, `absoluteBoundingBox`
|
|
107
81
|
4. Produce the 7 sections above
|
|
108
82
|
5. **Locate the insertion point** in `spec_figma.md`:
|
|
109
83
|
- **If `<!-- SYNTHESIS-BELOW -->` is present** → replace everything from the marker (inclusive) to EOF with: the marker line, a blank line, then the 7 sections.
|
|
110
|
-
- **If the marker is NOT present** (older
|
|
84
|
+
- **If the marker is NOT present** (older file or hand-edited) → locate the last non-empty line of the envelope (usually the end of `## Screenshots`), append a blank line, then the marker, another blank line, then the 7 sections. Do NOT delete any envelope content.
|
|
111
85
|
- **If the file is missing entirely** → advise the user to re-run `sungen add --screen <screen> --figma <url> --refresh` to regenerate the envelope first. Do not fabricate one.
|
|
112
86
|
6. Preserve the envelope (frontmatter + Frame + Screenshots) byte-for-byte. Never touch content above the marker.
|
|
113
87
|
|
|
114
|
-
---
|
|
115
|
-
|
|
116
88
|
## Re-synthesis
|
|
117
89
|
|
|
118
90
|
- If the envelope's `figma_version_id` changed → envelope is fresh; re-run synthesis
|
|
119
|
-
- If only the narrative is stale
|
|
91
|
+
- If only the narrative is stale → truncate from marker to EOF and regenerate
|
|
120
92
|
- Never edit content ABOVE the marker — that is the scaffolder's territory
|
|
121
93
|
|
|
122
|
-
---
|
|
123
|
-
|
|
124
94
|
## Selector Heuristics (for downstream `run-test`)
|
|
125
95
|
|
|
126
96
|
During `run-test` Phase 0, provisional selectors can be seeded from the raw JSON:
|
|
@@ -135,15 +105,11 @@ During `run-test` Phase 0, provisional selectors can be seeded from the raw JSON
|
|
|
135
105
|
| Node name ends `Icon` | `role: img` + `name: "<accessible name>"` |
|
|
136
106
|
|
|
137
107
|
Every provisional entry MUST carry:
|
|
138
|
-
|
|
139
108
|
```
|
|
140
109
|
# @needs-live-verify source=figma node_id=<id>
|
|
141
110
|
```
|
|
142
|
-
|
|
143
111
|
Provisional selectors feed `selectors.yaml` as candidates. `run-test` Phase 0 verifies them against the live page and overwrites incorrect entries.
|
|
144
112
|
|
|
145
|
-
---
|
|
146
|
-
|
|
147
113
|
## Security
|
|
148
114
|
|
|
149
115
|
- Never include the PAT in `spec_figma.md`, selectors, test data, or any committed file
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Capture mode: live
|
|
2
|
+
|
|
3
|
+
Navigate a running application, take **one accessibility snapshot** and **one screenshot**, and save them as visual context for test generation. Use when the app is live (dev, staging, or production with read-only access) and you want tests grounded in the actual rendered UI. Handles auth gracefully: if the page redirects to login, ask the user to sign in manually rather than injecting cookies.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Playwright MCP connected.
|
|
8
|
+
- Dev/staging server reachable (or a public URL).
|
|
9
|
+
- `playwright.config.ts` exists at the project root (for `baseURL` fallback).
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
### 1. Resolve target URL
|
|
14
|
+
|
|
15
|
+
1. `Live URL` field in `requirements/spec.md` (Overview section)
|
|
16
|
+
2. `baseURL` from `playwright.config.ts` + `URL Path` from `spec.md`
|
|
17
|
+
3. Neither → `AskUserQuestion`: *"Paste the full URL for the page to scan"*
|
|
18
|
+
|
|
19
|
+
### 2. Navigate
|
|
20
|
+
|
|
21
|
+
`browser_navigate` to the resolved URL.
|
|
22
|
+
|
|
23
|
+
### 3. Handle auth redirect
|
|
24
|
+
|
|
25
|
+
If the page redirects to a login route (URL contains `/login`, `/signin`, `/auth`, or content indicates a login screen):
|
|
26
|
+
1. Tell the user which login URL they landed on.
|
|
27
|
+
2. `AskUserQuestion`:
|
|
28
|
+
- **I'll log in manually** — wait for confirmation, then re-navigate to the target URL
|
|
29
|
+
- **Skip live scan** — switch to mode `local`
|
|
30
|
+
- **Cancel**
|
|
31
|
+
3. **Never** inject cookies or localStorage via `browser_evaluate` / `browser_run_code`. Auth belongs to the user.
|
|
32
|
+
|
|
33
|
+
### 4. Snapshot
|
|
34
|
+
|
|
35
|
+
Take **ONE** `browser_snapshot`. This accessibility tree is the primary AI context — roles, names, text, structure that tc-generation uses to identify sections and fields.
|
|
36
|
+
|
|
37
|
+
### 5. Screenshot (recommended)
|
|
38
|
+
|
|
39
|
+
Take **ONE** `browser_take_screenshot` with `fullPage: true`. Save to `requirements/ui/live-<timestamp>.png`, where `<timestamp>` is `YYYYMMDD-HHMM` local time (e.g. `live-20260421-1430.png`).
|
|
40
|
+
|
|
41
|
+
### 6a. Verify unauthenticated redirect target (flow capture only)
|
|
42
|
+
|
|
43
|
+
When capturing for a **flow** with security scenarios (e.g. "unauthenticated user cannot access X"):
|
|
44
|
+
1. Open a **fresh incognito/unauthenticated** context (no storage state).
|
|
45
|
+
2. `browser_navigate` to the protected route.
|
|
46
|
+
3. Record the **actual redirect URL** — do NOT assume `/login`; it may be `/register`, `/`, etc.
|
|
47
|
+
4. Report the redirect target: *"Unauthenticated access to `/dashboard` redirects to `/register`"*.
|
|
48
|
+
5. The caller must use the **actual redirect URL** in Gherkin assertions, never an assumed one.
|
|
49
|
+
|
|
50
|
+
Skip if the flow has no security scenarios or the user says to skip.
|
|
51
|
+
|
|
52
|
+
### 6. Detect discrepancies vs spec
|
|
53
|
+
|
|
54
|
+
If `spec.md` exists, cross-check the snapshot against spec sections: fields in spec but not in snapshot → *missing in UI*; elements in snapshot but not in spec → *missing in spec*. Report findings but **do not** auto-edit `spec.md`.
|
|
55
|
+
|
|
56
|
+
### 7. Report back
|
|
57
|
+
|
|
58
|
+
> Captured live page `<URL>`: Snapshot N interactive elements · Screenshot `requirements/ui/live-<timestamp>.png` · Discrepancies vs spec: <count or "none">
|
|
59
|
+
|
|
60
|
+
Hand back to the calling command. Scans **exactly one** page per invocation.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Capture mode: local
|
|
2
|
+
|
|
3
|
+
Use **pre-existing images** in `requirements/ui/` as visual context. No network, no MCP, no live site — works for any design tool (Figma export, Sketch, Penpot, Zeplin, hand-drawn, staging screenshots). This is the **baseline fallback**: if the live domain is down and Figma MCP isn't configured, this always works as long as the user drops images in the folder.
|
|
4
|
+
|
|
5
|
+
## Steps
|
|
6
|
+
|
|
7
|
+
### 1. List available images
|
|
8
|
+
|
|
9
|
+
Glob `requirements/ui/*.{png,jpg,jpeg,webp,gif}` and report count + filenames. Filter out metadata files (e.g. `figma-meta.md` written by mode figma-mcp) — those are read by `tc-generation` separately, not treated as images here.
|
|
10
|
+
|
|
11
|
+
### 2. Handle empty folder
|
|
12
|
+
|
|
13
|
+
If no images found:
|
|
14
|
+
1. Tell the user the folder is empty, with the full path so they can open it in Finder.
|
|
15
|
+
2. `AskUserQuestion`:
|
|
16
|
+
- **I'll drop images now** — wait for confirmation, then re-glob
|
|
17
|
+
- **Switch to Figma** — switch to mode `figma-mcp`
|
|
18
|
+
- **Switch to live page scan** — switch to mode `live`
|
|
19
|
+
- **Cancel** — abort create-test
|
|
20
|
+
3. If "drop images now", wait for confirmation (e.g. "done") then re-run step 1.
|
|
21
|
+
|
|
22
|
+
### 3. Read images for context
|
|
23
|
+
|
|
24
|
+
Use the `Read` tool on each image — Claude Code reads PNG/JPG/WebP directly as visual context. For large sets (>10 images), ask which are primary and which are states/variants to avoid loading too much at once.
|
|
25
|
+
|
|
26
|
+
### 4. Summarize
|
|
27
|
+
|
|
28
|
+
> Loaded N image(s) from `requirements/ui/`:
|
|
29
|
+
> - `<filename-1>` — <one-line description of what's visible>
|
|
30
|
+
> - `<filename-2>` — <one-line description>
|
|
31
|
+
|
|
32
|
+
Hand back to the calling command.
|
|
33
|
+
|
|
34
|
+
## File naming hints for users
|
|
35
|
+
|
|
36
|
+
Nudge users toward consistent filenames (don't enforce):
|
|
37
|
+
- `<section>-default.png` / `-error.png` / `-loading.png` / `-empty.png` — section states
|
|
38
|
+
- `full-page.png` / `viewport.png` — whole screen (auto-generated by `sungen add --capture`)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sungen-capture
|
|
3
|
+
description: 'Acquire visual/design context for test generation from one of four sources (modes): figma-mcp, figma-pat, live, local. Auto-loaded by create-test/add-screen when a visual source is needed, or when --figma flag / spec_figma.md is present. Router skill — read only the mode file you need.'
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Bring **visual + design context** into test generation so `sungen-tc-generation` can author Gherkin + test-data grounded in the real UI. This is a **router**: pick exactly **one mode** for the run, then read only that mode's file. Do **not** read all four.
|
|
10
|
+
|
|
11
|
+
This skill never generates Gherkin or `selectors.yaml` — it only acquires context and reports back to the calling command.
|
|
12
|
+
|
|
13
|
+
## Pick the mode
|
|
14
|
+
|
|
15
|
+
| Mode | Read | Use when | Needs |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| **figma-mcp** | `mode-figma-mcp.md` | Pre-launch / Figma is source of truth, **Figma Dev Mode MCP** connected | Figma MCP + frame URL |
|
|
18
|
+
| **figma-pat** | `mode-figma-pat.md` | `--figma` flag was used, or `requirements/spec_figma.md` exists (synthesize narrative from cached raw node JSON) | `sungen figma auth` PAT |
|
|
19
|
+
| **live** | `mode-live.md` | App is running (dev/staging/prod read-only) and you want the actual rendered UI | Playwright MCP + reachable URL |
|
|
20
|
+
| **local** | `mode-local.md` | Images already dropped in `requirements/ui/` (any design tool, screenshots, mockups) — baseline fallback, no network | nothing |
|
|
21
|
+
|
|
22
|
+
### How the mode is chosen (when the caller didn't specify)
|
|
23
|
+
|
|
24
|
+
1. `requirements/spec_figma.md` exists → **figma-pat** (PAT flow already ran during `add-screen`).
|
|
25
|
+
2. `requirements/ui/` has images → **local**.
|
|
26
|
+
3. Neither → ask the user which source (figma-mcp / live / local), then load that one mode file.
|
|
27
|
+
|
|
28
|
+
Modes are **mutually exclusive per run**, but the user can run `create-test` again with a different mode to layer context. All modes write to `requirements/ui/` and report back.
|
|
29
|
+
|
|
30
|
+
## What this skill (any mode) does NOT do
|
|
31
|
+
|
|
32
|
+
- Does not generate Gherkin — that's `sungen-tc-generation`.
|
|
33
|
+
- Does not write `selectors.yaml` — that's `/sungen:run-test`.
|
|
34
|
+
- Does not inject auth/cookies — the user logs in manually (see `live`).
|
|
35
|
+
- Does not crawl or generate images.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sungen-harness-audit
|
|
3
|
+
description: 'How to read `sungen audit` output and repair test-design findings. Auto-loaded by the design orchestrator.'
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What `sungen audit` measures
|
|
8
|
+
|
|
9
|
+
`sungen audit --screen <name>` runs deterministic sensors over `features/<name>.feature` + `requirements/test-viewpoint.md` and writes `.sungen/reports/<name>-audit.json`. It is the **gate** the orchestrator repairs against. Run with `--json` to parse it.
|
|
10
|
+
|
|
11
|
+
### Report shape (key fields)
|
|
12
|
+
```jsonc
|
|
13
|
+
{
|
|
14
|
+
"score": { "overall": 3.9, "coverage": 0.4, "businessDepth": 0.18, "balance": 0.5, "traceability": 0.7 },
|
|
15
|
+
"gateStatus": "PASS" | "FAIL",
|
|
16
|
+
"gate": { "pageType": "ecommerce-list", "themesCovered": 2, "themesTotal": 5,
|
|
17
|
+
"gaps": [ { "theme": "cart-correctness", "keywords": [...] } ] },
|
|
18
|
+
"depth": { "businessCriticalShallow": 9, "businessCriticalTotal": 11,
|
|
19
|
+
"shallowBusinessCritical": [ { "name": "...", "category": "PRODUCT" } ] },
|
|
20
|
+
"balance": { "imbalanced": true, "coreCount": 11, "secondaryCount": 22, "byBucket": {...} },
|
|
21
|
+
"duplicates": { "clusters": [ { "sameDataLikely": false, "scenarios": [...] } ] },
|
|
22
|
+
"trace": { "mappedRatio": 0.4, "note": "..." },
|
|
23
|
+
"findings": [ "GATE: ...", "DEPTH: ...", "BALANCE: ...", "TRACE: ..." ]
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
- **`overall` score is business-weighted** (coverage 0.4 + businessDepth 0.3 + balance 0.15 + traceability 0.15). It is intentionally strict on business value — a high count with shallow business coverage scores low. Don't optimize the count; optimize coverage + depth.
|
|
27
|
+
- Exit code **2** when `gateStatus == FAIL` (usable in CI / loop).
|
|
28
|
+
|
|
29
|
+
## Finding → repair mapping
|
|
30
|
+
|
|
31
|
+
| Finding prefix | Meaning | Repair action |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| **GATE** | a critical theme for the page-type has no covering scenario | Generate scenarios for that theme. **If cross-screen** (cart-correctness, product-detail-consistency, filter-result-correctness, multi-item cart) → do NOT fake it on a single screen; plan a **flow** (`/sungen:add-flow`) and record the deferral. |
|
|
34
|
+
| **DEPTH** | business-critical scenarios assert only visibility/navigation | Replace `Then User see [X] page/section` with **observable data assertions**: `Then User see [X] with {{value}}`, `Then User see [T] table match data:`. Capture real expected values into `test-data.yaml`. |
|
|
35
|
+
| **BALANCE** | secondary viewpoints (UI/validation/security) outweigh business-core | **Stop expanding** secondary viewpoints; generate the missing business-core scenarios first. Do not add more subscription/UI variants while core is thin. |
|
|
36
|
+
| **TRACE** | scenarios use ad-hoc `VP-<CAT>-NNN` codes not linked to the viewpoint-overview | Make each scenario map to a viewpoint-overview id (align category codes, or add a mapping comment). |
|
|
37
|
+
| **UNIVERSAL** | a universal theme (error/empty-state, accessibility) is absent | Low priority — add if in scope; otherwise note as out-of-scope with reason. |
|
|
38
|
+
|
|
39
|
+
## P5 steps for deep cross-screen / list coverage
|
|
40
|
+
|
|
41
|
+
Use these when repairing GATE/DEPTH findings for the hard viewpoints (cart/detail/filter correctness). They need **runtime data mode** (the default).
|
|
42
|
+
|
|
43
|
+
- **Capture a value to compare across screens** (product-detail-consistency, cart-correctness):
|
|
44
|
+
```gherkin
|
|
45
|
+
When User remember [Product Name] text as {{selected_product_name}}
|
|
46
|
+
And User remember [Product Price] text as {{selected_product_price}}
|
|
47
|
+
And User click [View Product] link
|
|
48
|
+
Then User see [Detail Product Name] header with {{selected_product_name}}
|
|
49
|
+
And User see [Detail Product Price] text with {{selected_product_price}}
|
|
50
|
+
```
|
|
51
|
+
`remember` stores the element's text/value at runtime; later `{{var}}` resolves to it. This proves the detail/cart shows the SAME product, not a random one.
|
|
52
|
+
|
|
53
|
+
- **Assert every item in a result matches** (category/brand-filter-correctness):
|
|
54
|
+
```gherkin
|
|
55
|
+
When User click [Women] link
|
|
56
|
+
And User click [Dress] link
|
|
57
|
+
Then User see all [Result Product Name] contain {{selected_category}}
|
|
58
|
+
```
|
|
59
|
+
`see all [X] contain {{v}}` asserts EVERY matching element contains the value → "all displayed products belong to the selected category/brand", not just one.
|
|
60
|
+
|
|
61
|
+
> Cross-screen flows (home → detail/cart): if the target screen is a separate screen, prefer a **flow** (`/sungen:add-flow`) so the journey is one test. On a single screen, keep the cross-screen assertion but tag `@manual` with a `# Deferred to a flow` comment.
|
|
62
|
+
|
|
63
|
+
## Repair loop rules
|
|
64
|
+
|
|
65
|
+
1. **Budget = 3 rounds.** Re-run `sungen audit` after each repair; track score delta.
|
|
66
|
+
2. **Stop when** `gateStatus == PASS` AND `findings` empty — or budget exhausted.
|
|
67
|
+
3. **Never fake a pass.** A shallow `see [Cart] page` does not satisfy `cart-correctness`. If a gap is genuinely cross-screen or needs capabilities the DSL lacks (e.g. capture an element value to compare elsewhere), **report it as a residual gap / flow item** instead of forcing a green gate.
|
|
68
|
+
4. **EP/data families are OK.** A `duplicates` cluster with `sameDataLikely=false` is an intentional equivalence-partition family (e.g. many invalid-email cases) — keep it; only collapse `sameDataLikely=true` exact duplicates.
|
|
69
|
+
|
|
70
|
+
## Discovery / fallback tree (when input is limited)
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
spec.md đủ tốt? → YES: Spec-first
|
|
74
|
+
│ NO
|
|
75
|
+
source code có? → YES: Source-first (mine behavior từ code)
|
|
76
|
+
│ NO
|
|
77
|
+
testcase cũ tương tự?→ YES: History-first
|
|
78
|
+
│ NO
|
|
79
|
+
domain rủi ro+defect?→ YES: Defect-first
|
|
80
|
+
│ NO
|
|
81
|
+
→ hỏi QA; QA chưa phản hồi → OUTPUT kèm ASSUMPTION LIST rõ ràng (không stall)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
See `docs/orchestration-spec.md` for the full flow and `reports/sungen_refactor_spec.md` for the design rationale.
|
|
@@ -55,13 +55,23 @@ When running Phase 0 for a **flow** (`qa/flows/<name>/`), check existing screen
|
|
|
55
55
|
- Read `baseURL` from `playwright.config.ts`.
|
|
56
56
|
- `browser_navigate` to the page URL.
|
|
57
57
|
- If redirected to login → run **Phase 0.5: Auth Persistence** first (see below), then re-navigate to the target page.
|
|
58
|
-
5. **Snapshot**:
|
|
58
|
+
5. **Snapshot**: Wait for the page to fully load before snapshotting.
|
|
59
|
+
- Check if the page is still loading (spinner visible, skeleton placeholders, empty table with 0 rows). If so, use `browser_wait_for` to wait until content is rendered.
|
|
60
|
+
- Then take **ONE** `browser_snapshot`. All Phase 0 selectors come from this single snapshot.
|
|
59
61
|
6. **Generate YAML entries**:
|
|
60
62
|
- Keys: follow `sungen-selector-keys` (lowercase, Unicode preserved, `--type` / `--N` suffixes).
|
|
61
|
-
- Selector priority: follow the table in **Diagnosis & Fix § Step 3** (`testid` > `role`+name > `
|
|
63
|
+
- Selector priority: follow the table in **Diagnosis & Fix § Step 3** (`testid` > `role`+name > `label` > `placeholder` > `text` > `locator` CSS last resort).
|
|
62
64
|
- Copy names **character-for-character** from the snapshot. Never infer from the Gherkin label.
|
|
63
65
|
- If an element is auto-inferable per `sungen-selector-keys` § Auto-Infer, **omit it** from YAML — keep the file minimal.
|
|
64
66
|
- **i18n sites**: if the site supports multiple languages, use `{{variable}}` in `name`/`value` fields instead of hardcoded text. Add corresponding `lbl_*` keys to `test-data.yaml` + locale overlay files (see `sungen-selector-keys` § i18n).
|
|
67
|
+
- **Selector quality rule**: the Playwright MCP accessibility tree snapshot gives you roles and accessible names directly — use them. Do NOT write XPath or class-based CSS selectors. Only write `type: locator` when no role/text/label/placeholder/testid is available, and restrict the CSS to `#id` or `[data-*]` / `[aria-*]` attribute selectors.
|
|
68
|
+
6b. **Cross-verify Gherkin labels vs snapshot** (prevents the #1 production failure):
|
|
69
|
+
- For **every** `[Reference]` in the `.feature` that will rely on auto-infer (not written to YAML), check the snapshot:
|
|
70
|
+
- `[X] button` — is there a button with accessible name **exactly** `X`?
|
|
71
|
+
- `[X] field` — does an input have placeholder **exactly** `X`? Does it even have a placeholder?
|
|
72
|
+
- `[X] heading` / `text` / `message` — is that text literally visible in the snapshot?
|
|
73
|
+
- If any mismatch → write an explicit YAML entry using the real DOM name. Do not leave a mismatch to be caught at runtime.
|
|
74
|
+
- **Typical mismatch cases**: Gherkin uses English label (`[Submit]`) but app displays Vietnamese (`"Gửi"`); placeholder is descriptive (`"Nhập email của bạn"`) not a bare field name (`"Email"`); button text includes an icon glyph before/after the word.
|
|
65
75
|
7. **Substring ambiguity check**: for each `role` + `name` selector, check if any other element in the snapshot has a name that **contains** this name as a substring (e.g., `"Đăng ký"` vs `"Đăng ký bằng Google"`). If yes → add `exact: true` to prevent strict mode violation at runtime.
|
|
66
76
|
8. **Merge, don't overwrite**: preserve the page selector and any user-authored entries in `selectors.yaml`. Only add missing keys.
|
|
67
77
|
9. **Show summary + confirm**: list the keys that will be added, ask the user to approve, then write the file.
|
|
@@ -69,9 +79,13 @@ When running Phase 0 for a **flow** (`qa/flows/<name>/`), check existing screen
|
|
|
69
79
|
|
|
70
80
|
### Common Phase 0 pitfalls
|
|
71
81
|
|
|
72
|
-
- Writing keys inferred from the Gherkin label instead of the snapshot name → Phase 1 will fail with
|
|
82
|
+
- Writing keys inferred from the Gherkin label instead of the snapshot name → Phase 1 will fail with `No element found`.
|
|
73
83
|
- Skipping Phase 0.5 when an auth redirect happened → snapshot captures the login page, all selectors wrong.
|
|
84
|
+
- Taking snapshot while page is still loading (spinner visible, table empty) → selectors for dynamic content will be missing or wrong.
|
|
85
|
+
- Skipping step 6b for "simple" elements like buttons → silent mismatch between Gherkin label and DOM name fails at runtime.
|
|
74
86
|
- Using `browser_evaluate` alone to scrape cookies → misses httpOnly session cookies. Always use `browser_storage_state` (or the `browser_run_code` fallback).
|
|
87
|
+
- Writing XPath or class-based CSS selectors → breaks on DOM/style refactoring. Use role/testid/text/label/placeholder from the accessibility tree.
|
|
88
|
+
- Falling back to `locator: 'div.some-class > span'` when the element IS visible in the accessibility snapshot with a role + name → the snapshot gives you `getByRole` for free; use it.
|
|
75
89
|
- Overwriting user-authored selectors → always merge.
|
|
76
90
|
|
|
77
91
|
---
|
|
@@ -210,12 +224,24 @@ Selector priority (use first applicable):
|
|
|
210
224
|
|
|
211
225
|
| Priority | type | When |
|
|
212
226
|
|---|---|---|
|
|
213
|
-
| 1 | `testid` | `data-testid` exists |
|
|
214
|
-
| 2 | `role` + exact name | Interactive elements |
|
|
215
|
-
| 3 | `
|
|
216
|
-
| 4 | `
|
|
217
|
-
| 5 | `
|
|
218
|
-
| 6 | `
|
|
227
|
+
| 1 | `testid` | `data-testid` or any stable test attribute exists |
|
|
228
|
+
| 2 | `role` + exact name | Interactive elements with an accessible name |
|
|
229
|
+
| 3 | `label` | Form field with a visible `<label>` |
|
|
230
|
+
| 4 | `placeholder` | Input/textarea with a placeholder attribute |
|
|
231
|
+
| 5 | `text` | Static visible text content |
|
|
232
|
+
| 6 | `locator` (CSS) | Last resort — `#id` or `[attr=value]` **only** (see restrictions below) |
|
|
233
|
+
|
|
234
|
+
> ⚠️ **Playwright best practice** ([source](https://playwright.dev/docs/best-practices#use-locators)): user-facing locators (`role`, `label`, `text`, `placeholder`, `testid`) are resilient to refactoring and far less likely to break. CSS class selectors and XPath break whenever a developer renames a class or restructures the DOM — even without changing the UI.
|
|
235
|
+
>
|
|
236
|
+
> **Never write these in `selectors.yaml`**:
|
|
237
|
+
> - XPath: `xpath=//div[@class='...']` or `//button[contains(@class,'btn')]`
|
|
238
|
+
> - Class-based CSS: `div.btn-primary`, `.modal-footer > .submit-btn`
|
|
239
|
+
> - Deep structural CSS: `div:nth-child(3) > ul > li > button`
|
|
240
|
+
>
|
|
241
|
+
> **Acceptable CSS (last resort only)**:
|
|
242
|
+
> - Stable `id`: `#submit-button` (only if the id is truly stable and not dynamic)
|
|
243
|
+
> - Data attributes: `[data-id="123"]`, `[aria-controls="menu"]`
|
|
244
|
+
> - Input type: `input[type="file"]` (when no testid/label exists)
|
|
219
245
|
|
|
220
246
|
**Exact name rule**: copy name character-for-character from snapshot. Never infer from Gherkin label.
|
|
221
247
|
|
|
@@ -229,9 +255,9 @@ Common fixes:
|
|
|
229
255
|
- Name mismatch → copy exact name from snapshot
|
|
230
256
|
- Multiple matches → add `nth` or `exact: true`
|
|
231
257
|
- Substring ambiguity (e.g., `"Submit"` matches `"Submit"` and `"Submit & Continue"`) → add `exact: true`
|
|
232
|
-
- No accessible name → use `testid
|
|
258
|
+
- No accessible name → use `testid`; only fall back to `locator` CSS as last resort
|
|
233
259
|
- Element in iframe → add `frame` field
|
|
234
|
-
- Dynamic content → use `testid` or
|
|
260
|
+
- Dynamic content → use `testid` or `role` + `nth`
|
|
235
261
|
|
|
236
262
|
### Step 4: Recompile After Fix
|
|
237
263
|
|
|
@@ -248,6 +274,26 @@ Then re-run only the current phase's failing tests, not all tests.
|
|
|
248
274
|
|
|
249
275
|
---
|
|
250
276
|
|
|
277
|
+
## Common Failure Patterns
|
|
278
|
+
|
|
279
|
+
Quick reference for the most frequent production failures:
|
|
280
|
+
|
|
281
|
+
| Symptom | Root cause | Fix |
|
|
282
|
+
|---------|-----------|-----|
|
|
283
|
+
| `No element found` on button/link/heading | Gherkin `[Reference]` label ≠ DOM accessible name (different language or text) | Write explicit YAML: `type: role, value: button, name: "<exact DOM name>"` |
|
|
284
|
+
| `No element found` on `[X] field` | Field has no placeholder, or placeholder ≠ X | Write explicit YAML: `type: label, value: "Actual label"` or `type: placeholder, value: "Actual placeholder"` |
|
|
285
|
+
| `No element found` on `[X] text` / `message` | Visible text differs from Gherkin label, or text is dynamic | Write explicit YAML or use `{{variable}}` for dynamic content |
|
|
286
|
+
| `strict mode violation` | Multiple elements match the same name/text | Add `exact: true` to YAML entry, or add `nth` |
|
|
287
|
+
| `toBeVisible` timeout on dynamic content | Snapshot was taken while page was still loading | Wait for spinner/skeleton to clear before snapshotting; add `browser_wait_for` |
|
|
288
|
+
| All tests fail with page navigate error | Page selector URL wrong or baseURL mismatch | Re-check `playwright.config.ts` `baseURL` and page selector `value` path |
|
|
289
|
+
| Auth redirect on every test | `specs/.auth/<role>.json` missing or expired | Run Phase 0.5 to capture fresh session |
|
|
290
|
+
| Table row assertions fail | `columns` config has wrong indices | Count column headers left-to-right (0-indexed) from snapshot |
|
|
291
|
+
| Wrong text assertions on locale page | Hardcoded Vietnamese/English text in YAML `name`/`value` | Use `{{lbl_*}}` variables with locale overlay files |
|
|
292
|
+
| Element inside iframe not found | `frame` field missing in YAML entry | Add `frame: "iframe[src*='...']"` to the selector entry |
|
|
293
|
+
| Selector breaks after UI redesign with no functional change | CSS class or XPath used — brittle to style refactoring | Rewrite with `role`/`testid`/`label`/`text` from accessibility snapshot |
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
251
297
|
## Table Selectors
|
|
252
298
|
|
|
253
299
|
For table patterns, add table selectors with `columns` config:
|
|
@@ -166,37 +166,47 @@ Resolver searches in this order:
|
|
|
166
166
|
|
|
167
167
|
If no YAML key exists, the resolver infers from the Gherkin element type:
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
|
178
|
-
|
|
179
|
-
| `[X]
|
|
180
|
-
| `[X]
|
|
181
|
-
| `[X]
|
|
182
|
-
| `[X]
|
|
183
|
-
| `[X]
|
|
184
|
-
| `[X]
|
|
185
|
-
| `[X]
|
|
186
|
-
| `[X]
|
|
187
|
-
| `[X]
|
|
188
|
-
| `[X]
|
|
189
|
-
| `[X]
|
|
190
|
-
| `[X]
|
|
191
|
-
| `[X]
|
|
192
|
-
| `[X]
|
|
193
|
-
| `[X]
|
|
194
|
-
| `[X]
|
|
195
|
-
| `[X]
|
|
196
|
-
| `[X]
|
|
197
|
-
| `[X]
|
|
198
|
-
|
|
199
|
-
|
|
169
|
+
> ⚠️ **Auto-infer pitfall — the #1 cause of selector failures in production.**
|
|
170
|
+
>
|
|
171
|
+
> `[X] button` auto-infers as `getByRole('button', { name: 'X' })`. This **only works** when the button's accessible name in the DOM is **exactly `X`** — same language, same text, same casing.
|
|
172
|
+
>
|
|
173
|
+
> The Gherkin `[Reference]` is your human label for the element, **not** the DOM name. If the app is in Vietnamese (or any language where the Gherkin label differs from DOM text), auto-infer will produce `No element found` at runtime. **Write an explicit YAML entry** with the real DOM name instead.
|
|
174
|
+
>
|
|
175
|
+
> **Decision rule**: auto-infer is safe ONLY when you have confirmed in the snapshot that the DOM element's accessible name / placeholder text is literally `X`. When in doubt → write YAML.
|
|
176
|
+
|
|
177
|
+
| Gherkin | Inferred locator | Safe when… |
|
|
178
|
+
|---|---|---|
|
|
179
|
+
| `[X] button` | `getByRole('button', { name: 'X' })` | Button's accessible name = X |
|
|
180
|
+
| `[X] link` | `getByRole('link', { name: 'X' })` | Link text = X |
|
|
181
|
+
| `[X] heading` / `header` | `getByRole('heading', { name: 'X' })` | Heading text = X |
|
|
182
|
+
| `[X] checkbox` | `getByRole('checkbox', { name: 'X' })` | Checkbox label = X |
|
|
183
|
+
| `[X] radio` | `getByRole('radio', { name: 'X' })` | Radio label = X |
|
|
184
|
+
| `[X] field` | `getByPlaceholder('X')` | Placeholder text = X AND field has a placeholder |
|
|
185
|
+
| `[X] text` / `message` / `label` | `getByText('X')` | Visible text = X (partial match) |
|
|
186
|
+
| `[X] logo/image/icon` | `getByRole('img', { name: 'X' })` | Image alt = X |
|
|
187
|
+
| `[X] search` | `getByRole('searchbox', { name: 'X' })` | Searchbox label = X |
|
|
188
|
+
| `[X] option` | `getByRole('option', { name: 'X' })` | Option text = X |
|
|
189
|
+
| `[X] slider` | `getByRole('slider', { name: 'X' })` | Slider label = X |
|
|
190
|
+
| `[X] toggle` | `getByRole('switch', { name: 'X' })` | Toggle label = X |
|
|
191
|
+
| `[X] tab` | `getByRole('tab', { name: 'X' })` | Tab text = X |
|
|
192
|
+
| `[X] table` | `getByRole('table', { name: 'X' })` | Table aria-label = X |
|
|
193
|
+
| `[X] list` | `getByRole('list', { name: 'X' })` | List aria-label = X |
|
|
194
|
+
| `[X] column` | `getByRole('columnheader', { name: 'X' })` | Column header text = X |
|
|
195
|
+
| `[X] dialog` / `modal` / `drawer` | `getByRole('dialog', { name: 'X' })` | Dialog aria-label/heading = X |
|
|
196
|
+
| `[X] dropdown` / `select` | `getByRole('combobox', { name: 'X' })` | Combobox label = X |
|
|
197
|
+
| `[X] menuitem` | `getByRole('menuitem', { name: 'X' })` | Menu item text = X |
|
|
198
|
+
| `[X] progressbar` | `getByRole('progressbar', { name: 'X' })` | Progressbar label = X |
|
|
199
|
+
| `[X] section` | `getByRole('region', { name: 'X' })` | Section aria-label = X |
|
|
200
|
+
| `[X] card` | `getByRole('article', { name: 'X' })` | Card aria-label = X |
|
|
201
|
+
| `[X] item` | `getByRole('listitem', { name: 'X' })` | List item text = X |
|
|
202
|
+
| `[X] cell` | `getByRole('cell', { name: 'X' })` | Cell text = X |
|
|
203
|
+
| `[X] spinner` | `getByRole('status', { name: 'X' })` | Spinner aria-label = X |
|
|
204
|
+
| `[X] breadcrumb` | `getByRole('navigation', { name: 'X' })` | Navigation aria-label = X |
|
|
205
|
+
| `[X] badge` / `tooltip` / `tag` | `getByText('X')` | Visible text = X |
|
|
206
|
+
|
|
207
|
+
**Special note on `[X] field`**: `getByPlaceholder('X')` only works when (1) the field has a placeholder attribute AND (2) the placeholder text equals X. For fields without placeholders (floating labels, aria-label), write explicit YAML: `type: label, value: "Actual label text"`.
|
|
208
|
+
|
|
209
|
+
**Only add a YAML entry when** auto-infer cannot work: DOM name differs from Gherkin label, need `testid`, need `nth`, need `exact: true`, or the field type requires explicit config.
|
|
200
210
|
|
|
201
211
|
### Types requiring YAML entry (no auto-infer)
|
|
202
212
|
|