@sun-asterisk/sungen 2.6.11 → 2.6.14
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/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +215 -65
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/dashboard/snapshot-builder.d.ts.map +1 -1
- package/dist/dashboard/snapshot-builder.js +173 -32
- package/dist/dashboard/snapshot-builder.js.map +1 -1
- package/dist/dashboard/templates/index.html +84 -84
- package/dist/dashboard/types.d.ts +35 -0
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.d.ts +24 -3
- package/dist/exporters/csv-exporter.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.js +28 -7
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/json-exporter.d.ts +15 -0
- package/dist/exporters/json-exporter.d.ts.map +1 -1
- package/dist/exporters/json-exporter.js +7 -2
- package/dist/exporters/json-exporter.js.map +1 -1
- package/dist/exporters/playwright-report-parser.d.ts +7 -0
- package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
- package/dist/exporters/playwright-report-parser.js +20 -0
- package/dist/exporters/playwright-report-parser.js.map +1 -1
- package/dist/exporters/selector-key-resolver.d.ts +55 -0
- package/dist/exporters/selector-key-resolver.d.ts.map +1 -0
- package/dist/exporters/selector-key-resolver.js +208 -0
- package/dist/exporters/selector-key-resolver.js.map +1 -0
- package/dist/exporters/test-data-resolver.d.ts +15 -2
- package/dist/exporters/test-data-resolver.d.ts.map +1 -1
- package/dist/exporters/test-data-resolver.js +61 -8
- package/dist/exporters/test-data-resolver.js.map +1 -1
- package/dist/exporters/types.d.ts +1 -0
- package/dist/exporters/types.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.d.ts +28 -3
- package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.js +34 -6
- package/dist/exporters/xlsx-exporter.js.map +1 -1
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +13 -0
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +9 -0
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +18 -2
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
- package/dist/orchestrator/ai-rules-updater.js +4 -0
- package/dist/orchestrator/ai-rules-updater.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +1 -2
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +48 -14
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-dashboard.md +4 -1
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-delivery.md +22 -11
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-locale.md +71 -0
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-review.md +23 -8
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +45 -6
- package/dist/orchestrator/templates/ai-instructions/claude-config.md +6 -1
- package/dist/orchestrator/templates/ai-instructions/claude-skill-locale.md +316 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +1 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +38 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +2 -0
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +50 -13
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-dashboard.md +4 -1
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-delivery.md +20 -9
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-locale.md +70 -0
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-review.md +23 -8
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +44 -6
- package/dist/orchestrator/templates/ai-instructions/copilot-config.md +6 -1
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-locale.md +291 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +1 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +38 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +2 -0
- package/dist/orchestrator/templates/playwright.config.ts +25 -8
- package/dist/orchestrator/templates/specs-base.ts +9 -0
- package/dist/orchestrator/templates/specs-locale-fixture.ts +105 -0
- package/package.json +1 -1
- package/src/cli/commands/delivery.ts +256 -65
- package/src/cli/index.ts +1 -1
- package/src/dashboard/snapshot-builder.ts +207 -32
- package/src/dashboard/templates/index.html +84 -84
- package/src/dashboard/types.ts +40 -3
- package/src/exporters/csv-exporter.ts +36 -7
- package/src/exporters/json-exporter.ts +22 -2
- package/src/exporters/playwright-report-parser.ts +20 -0
- package/src/exporters/selector-key-resolver.ts +190 -0
- package/src/exporters/test-data-resolver.ts +65 -7
- package/src/exporters/types.ts +1 -0
- package/src/exporters/xlsx-exporter.ts +61 -7
- package/src/generators/test-generator/code-generator.ts +14 -0
- package/src/generators/test-generator/utils/selector-resolver.ts +19 -2
- package/src/orchestrator/ai-rules-updater.ts +4 -0
- package/src/orchestrator/project-initializer.ts +1 -2
- package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +48 -14
- package/src/orchestrator/templates/ai-instructions/claude-cmd-dashboard.md +4 -1
- package/src/orchestrator/templates/ai-instructions/claude-cmd-delivery.md +22 -11
- package/src/orchestrator/templates/ai-instructions/claude-cmd-locale.md +71 -0
- package/src/orchestrator/templates/ai-instructions/claude-cmd-review.md +23 -8
- package/src/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +45 -6
- package/src/orchestrator/templates/ai-instructions/claude-config.md +6 -1
- package/src/orchestrator/templates/ai-instructions/claude-skill-locale.md +316 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +1 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +38 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +2 -0
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +50 -13
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-dashboard.md +4 -1
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-delivery.md +20 -9
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-locale.md +70 -0
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-review.md +23 -8
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +44 -6
- package/src/orchestrator/templates/ai-instructions/copilot-config.md +6 -1
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-locale.md +291 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +1 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +38 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +2 -0
- package/src/orchestrator/templates/playwright.config.ts +25 -8
- package/src/orchestrator/templates/specs-base.ts +9 -0
- package/src/orchestrator/templates/specs-locale-fixture.ts +105 -0
- package/dist/orchestrator/templates/playwright.config.d.ts +0 -10
- package/dist/orchestrator/templates/playwright.config.d.ts.map +0 -1
- package/dist/orchestrator/templates/playwright.config.js +0 -104
- package/dist/orchestrator/templates/playwright.config.js.map +0 -1
- package/dist/orchestrator/templates/specs-base.d.ts +0 -14
- package/dist/orchestrator/templates/specs-base.d.ts.map +0 -1
- package/dist/orchestrator/templates/specs-base.js +0 -77
- package/dist/orchestrator/templates/specs-base.js.map +0 -1
- package/dist/orchestrator/templates/specs-test-data.d.ts +0 -16
- package/dist/orchestrator/templates/specs-test-data.d.ts.map +0 -1
- package/dist/orchestrator/templates/specs-test-data.js +0 -151
- package/dist/orchestrator/templates/specs-test-data.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: delivery
|
|
3
3
|
description: 'Export Gherkin scenarios + Playwright results to CSV test case file for QA delivery.'
|
|
4
|
-
argument-hint: "[screen-name...] (omit for all
|
|
4
|
+
argument-hint: "[screen-name...] [--env <locale>] (omit screens for all; --env for locale-specific export)"
|
|
5
5
|
allowed-tools: Bash, Read, AskUserQuestion
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -11,28 +11,34 @@ You are a **QA Test Delivery Engineer**. Your job is to invoke the deterministic
|
|
|
11
11
|
|
|
12
12
|
## Parameters
|
|
13
13
|
|
|
14
|
-
Parse
|
|
15
|
-
-
|
|
16
|
-
-
|
|
14
|
+
Parse from `$ARGUMENTS`:
|
|
15
|
+
- **screens** — zero or more screen/flow names. Empty → CLI processes all targets in `qa/screens/` + `qa/flows/`.
|
|
16
|
+
- **`--env <locale>`** — optional. Sets `SUNGEN_ENV=<locale>` for the run so the CLI merges `<name>.<locale>.yaml` over the base test-data and writes `<name>-testcases.<locale>.csv` / `.xlsx`. Accept `--locale <locale>` as an alias.
|
|
17
|
+
|
|
18
|
+
If `--env` is passed but no value follows, ask the user which locale to use.
|
|
17
19
|
|
|
18
20
|
## Steps
|
|
19
21
|
|
|
20
22
|
### 1. Invoke the CLI
|
|
21
23
|
|
|
22
|
-
Run via Bash (single command, no extra parsing)
|
|
24
|
+
Run via Bash (single command, no extra parsing). Prefer the local `./bin/sungen.js` when it exists — the sungen monorepo ships local-only features the global npm package doesn't have yet (multi-sheet locale aggregation, `.<env>` filename suffix, locale-aware step rendering). Fall back to `npx sungen` in downstream projects.
|
|
23
25
|
|
|
24
26
|
```bash
|
|
25
|
-
|
|
27
|
+
# No env — local-first dispatcher:
|
|
28
|
+
[ -x ./bin/sungen.js ] && ./bin/sungen.js delivery <screens> || npx sungen delivery <screens>
|
|
29
|
+
|
|
30
|
+
# Locale-specific:
|
|
31
|
+
[ -x ./bin/sungen.js ] && SUNGEN_ENV=<locale> ./bin/sungen.js delivery <screens> || SUNGEN_ENV=<locale> npx sungen delivery <screens>
|
|
26
32
|
```
|
|
27
33
|
|
|
28
|
-
- If no screen args →
|
|
29
|
-
- If
|
|
34
|
+
- If no screen args → omit `<screens>` (CLI processes all targets).
|
|
35
|
+
- If `--env <locale>` was provided → prepend `SUNGEN_ENV=<locale>` to the command. Do NOT pass `--env` to the CLI itself — it's not a CLI flag, only a slash-command convenience.
|
|
30
36
|
|
|
31
37
|
The CLI handles:
|
|
32
|
-
- Scope detection (all screens vs specific)
|
|
38
|
+
- Scope detection (all screens + flows vs specific)
|
|
33
39
|
- Pre-flight source checks with colorful output
|
|
34
|
-
- Parsing `.feature`, `.spec.ts`, `test-data.yaml
|
|
35
|
-
- Generating CSV at `qa/deliverables/<
|
|
40
|
+
- Parsing `.feature`, `.spec.ts`, `test-data.yaml` (+ `<name>.<env>.yaml` overlay when `SUNGEN_ENV` is set), and per-target `<name>-test-result[.<env>].json`
|
|
41
|
+
- Generating CSV/XLSX at `qa/deliverables/<name>-testcases[.<env>].csv` / `.xlsx`
|
|
36
42
|
- Printing summary table
|
|
37
43
|
|
|
38
44
|
### 2. Handle pre-flight failures (if CLI exits non-zero)
|
|
@@ -68,4 +74,9 @@ Forward the CLI's summary table to the user verbatim. Then use `AskUserQuestion`
|
|
|
68
74
|
sungen delivery [screens...]
|
|
69
75
|
[--skip-preflight] Skip pre-flight checks (not recommended)
|
|
70
76
|
[--continue-on-missing] Skip screens with blocking misses
|
|
77
|
+
|
|
78
|
+
# Locale-aware export (env var, not a CLI flag):
|
|
79
|
+
SUNGEN_ENV=<locale> sungen delivery [screens...]
|
|
80
|
+
→ reads <name>.<locale>.yaml overlay, picks <name>-test-result.<locale>.json,
|
|
81
|
+
writes <name>-testcases.<locale>.csv / .xlsx
|
|
71
82
|
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 'Bootstrap i18n for a screen/flow — audit selectors, detect locale switch mechanism via Playwright, generate test-data overlay so `sungen:run-test --env <locale>` works.'
|
|
3
|
+
argument-hint: "<name> <locale> [--base-locale <code>] [--offline]"
|
|
4
|
+
allowed-tools: Read, Grep, Bash, Glob, Edit, Write, AskUserQuestion, mcp__playwright__browser_navigate, mcp__playwright__browser_snapshot, mcp__playwright__browser_take_screenshot, mcp__playwright__browser_wait_for, mcp__playwright__browser_evaluate, mcp__playwright__browser_click, mcp__playwright__browser_storage_state, mcp__playwright__browser_set_storage_state
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Role
|
|
8
|
+
|
|
9
|
+
You are a **Senior QA Localization Engineer**. Use the `sungen-locale` skill — it contains the full phased strategy. Your job in this command is parameter parsing, context detection, and final hand-off.
|
|
10
|
+
|
|
11
|
+
## Parameters
|
|
12
|
+
|
|
13
|
+
Parse from `$ARGUMENTS`:
|
|
14
|
+
- **name** — screen or flow name (e.g. `home`, `awards`, `kudo-to-display-on-kudos`). If missing → AskUserQuestion.
|
|
15
|
+
- **locale** — target locale code (e.g. `en`, `ja`, `en-US`, `staging-ja`). If missing → AskUserQuestion. This becomes the suffix in `test-data/<feature>.<locale>.yaml` and `SUNGEN_ENV=<locale>` at run time.
|
|
16
|
+
- **`--base-locale <code>`** — optional. The locale of existing `test-data/<feature>.yaml`. Default `vi`. Used only for reporting/UX — files never get renamed.
|
|
17
|
+
- **`--offline`** — force OFFLINE mode (skip Playwright capture). Useful when you know the live page can't be reached or when prepping a test-only template.
|
|
18
|
+
|
|
19
|
+
Reject if name == locale (common typo).
|
|
20
|
+
|
|
21
|
+
## Auto-detect context
|
|
22
|
+
|
|
23
|
+
Same as `/sungen:run-test`:
|
|
24
|
+
- `qa/flows/<name>/` exists → flow mode (base path: `qa/flows/<name>/`)
|
|
25
|
+
- Else `qa/screens/<name>/` exists → screen mode (base path: `qa/screens/<name>/`)
|
|
26
|
+
- Neither exists → tell user and stop
|
|
27
|
+
|
|
28
|
+
## Steps
|
|
29
|
+
|
|
30
|
+
The `sungen-locale` skill defines 7 phases. Execute them in order:
|
|
31
|
+
|
|
32
|
+
1. **Phase 1 — Selector audit** (always, no MCP)
|
|
33
|
+
2. **Phase 2 — Capture base locale** (Live mode only)
|
|
34
|
+
3. **Phase 3 — Switch locale + detect mechanism** (Live mode only)
|
|
35
|
+
4. **Phase 4 — Diff base ↔ target** (Live mode only)
|
|
36
|
+
5. **Phase 5 — Confirm proposal** (always)
|
|
37
|
+
6. **Phase 6 — Apply files** (always, after confirmation)
|
|
38
|
+
7. **Phase 7 — Hand off** (always)
|
|
39
|
+
|
|
40
|
+
**Mode selection** happens at the start of Phase 2:
|
|
41
|
+
|
|
42
|
+
- If `--offline` flag → OFFLINE mode (skip Phases 2-4, Phase 5 shows empty `target text` column for user to fill manually).
|
|
43
|
+
- Else try `browser_navigate(baseURL)` — if it succeeds and page renders content → LIVE mode.
|
|
44
|
+
- If navigate fails / page redirects to login / shows blocker → fall back to OFFLINE mode automatically. Announce the fallback clearly so the user knows what they got.
|
|
45
|
+
|
|
46
|
+
## Enumerate features
|
|
47
|
+
|
|
48
|
+
For multi-feature screens (e.g. `home` has `home.feature` + `home-modal.feature`), run Phases 1, 4, 6 once **per feature file**; run Phases 2 & 3 (live capture + mechanism detection) **once per screen** — the mechanism doesn't change between features in the same screen.
|
|
49
|
+
|
|
50
|
+
Discover features the same way delivery does:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
ls qa/screens/<name>/features/*.feature
|
|
54
|
+
# or
|
|
55
|
+
ls qa/flows/<name>/features/*.feature
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## After Phase 7
|
|
59
|
+
|
|
60
|
+
Use `AskUserQuestion` to offer next steps:
|
|
61
|
+
|
|
62
|
+
- **`/sungen:run-test <name> --env <locale>`** — Run the tests against the new locale (Recommended)
|
|
63
|
+
- **`/sungen:locale <name> <other-locale>`** — Bootstrap another locale (e.g. add `ja` after `en`)
|
|
64
|
+
- **Open the overlay file** — Show `test-data/<feature>.<locale>.yaml` so user can review / edit before running
|
|
65
|
+
- **Done** — Stop
|
|
66
|
+
|
|
67
|
+
## Notes
|
|
68
|
+
|
|
69
|
+
- Do NOT run tests yourself. `/sungen:run-test` is the executor. This command only PREPS files.
|
|
70
|
+
- Do NOT modify `.feature` files. Localization happens entirely through `selectors/*.yaml` + `test-data/*.yaml`.
|
|
71
|
+
- Auth blocker on SAA staging? Use `--offline`. See [[saa-auth-blocker]] memory.
|
|
@@ -17,13 +17,28 @@ Parse **name** from `$ARGUMENTS`. If missing, ask the user.
|
|
|
17
17
|
|
|
18
18
|
## Steps
|
|
19
19
|
|
|
20
|
-
1.
|
|
21
|
-
2.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
1. **Enumerate feature files** — glob `<base>/<name>/features/*.feature`. A screen may have one main file (`<name>.feature`) plus sub-features (`<name>-<sub>.feature` like `awards-modal.feature`); a flow has a single `<name>.feature`. If zero `.feature` files found → `/sungen:create-test` first.
|
|
21
|
+
2. **Review every feature file** — for each `<basename>.feature` discovered in step 1:
|
|
22
|
+
- Read `<basename>.feature` and the matching `test-data/<basename>.yaml`.
|
|
23
|
+
- Apply the `sungen-tc-review` skill — score 3 dimensions: Syntax (30pts), Coverage (40pts), Viewpoint (30pts). **For flows**, also apply the "Flow Review Additions" section. Use `sungen-viewpoint` for pattern checklists.
|
|
24
|
+
- Apply the **Unverified Selectors check** — if `<base>/<name>/selectors/<basename>.yaml` exists, count lines matching `@needs-live-verify`. Include in the per-file report as a non-scoring metric. Does NOT affect the 60% threshold.
|
|
25
|
+
3. **Aggregated output** — present scores in a per-feature table, then a screen-level rollup:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
Feature Syntax Coverage Viewpoint Total Verdict
|
|
29
|
+
──────────────────────────────────────────────────────────────────
|
|
30
|
+
home.feature 28/30 36/40 27/30 91% PASS
|
|
31
|
+
home-modal.feature 26/30 24/40 22/30 72% PASS
|
|
32
|
+
──────────────────────────────────────────────────────────────────
|
|
33
|
+
Screen rollup (mean) 27/30 30/40 24.5/30 81.5% PASS
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- **>= 60% per file**: PASS that file.
|
|
37
|
+
- **< 60% per file**: FAIL that file with recommendations.
|
|
38
|
+
- Show the full per-file report (recommendations, top issues) **only for files that fail**, or when the user asks for the deep report.
|
|
39
|
+
4. If any file is FAIL and user confirms → update that file's test cases following `sungen-gherkin-syntax` and `sungen-tc-generation` skills, then re-review **only the failing files** (skip already-passing ones to save time).
|
|
40
|
+
5. After all files PASS (or user decides to proceed), use `AskUserQuestion` to offer next steps:
|
|
41
|
+
|
|
42
|
+
- **`/sungen:run-test <name>`** — Generate selectors, compile, and run tests for **every feature** in this screen (Recommended)
|
|
28
43
|
- **`/sungen:create-test <name>`** — Add more test cases before running
|
|
29
44
|
- **Done for now** — I'll come back later
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: run-test
|
|
3
3
|
description: 'Generate selectors + auth state via Playwright MCP, compile, and run Playwright tests — auto-fixes selectors on failure'
|
|
4
|
-
argument-hint: [screen-name]
|
|
4
|
+
argument-hint: "[screen-name] [--env <locale>]"
|
|
5
5
|
allowed-tools: Read, Grep, Bash, Glob, Edit, Write, AskUserQuestion, mcp__playwright__browser_navigate, mcp__playwright__browser_snapshot, mcp__playwright__browser_take_screenshot, mcp__playwright__browser_wait_for, mcp__playwright__browser_evaluate, mcp__playwright__browser_run_code, mcp__playwright__browser_storage_state, mcp__playwright__browser_set_storage_state
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -11,7 +11,24 @@ You are a **Senior Developer**. Use `sungen-selector-fix`, `sungen-selector-keys
|
|
|
11
11
|
|
|
12
12
|
## Parameters
|
|
13
13
|
|
|
14
|
-
Parse
|
|
14
|
+
Parse from `$ARGUMENTS`:
|
|
15
|
+
- **name** — screen or flow name. If missing, ask the user.
|
|
16
|
+
- **`--env <locale>`** — optional. Sets `SUNGEN_ENV=<locale>` for the test run so the runtime test-data resolver merges `<name>.<locale>.yaml` over the base, and `playwright.config.ts` writes results to `<name>-test-result.<locale>.json`. Accept `--locale <locale>` as an alias.
|
|
17
|
+
|
|
18
|
+
If `--env` is passed but no value follows, ask the user which locale to use.
|
|
19
|
+
|
|
20
|
+
**`--env <locale>` pre-flight**: when `--env` is passed AND the matching overlay file doesn't exist yet (`test-data/<feature>.<locale>.yaml` missing), tests will silently fall back to base locale values — the run will execute but won't actually exercise the locale. Before kicking off Phase 0.5, check:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
ls qa/<screens|flows>/<name>/test-data/*.<locale>.yaml 2>/dev/null | wc -l
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
If the count is 0 → use `AskUserQuestion` to offer:
|
|
27
|
+
- **Run `/sungen:locale <name> <locale>` first** (Recommended) — bootstrap the overlay before running tests
|
|
28
|
+
- **Continue anyway** — run with empty overlay (tests will assert base-locale text, will likely fail on a real locale page)
|
|
29
|
+
- **Cancel**
|
|
30
|
+
|
|
31
|
+
Skip this pre-flight when `--env` matches the base locale (no overlay needed in that case).
|
|
15
32
|
|
|
16
33
|
**Auto-detect context**: check if `qa/flows/<name>/` exists → flow mode (base path: `qa/flows/<name>/`). Else check `qa/screens/<name>/` → screen mode (base path: `qa/screens/<name>/`).
|
|
17
34
|
|
|
@@ -52,7 +69,11 @@ Parse **name** from `$ARGUMENTS`. If missing, ask the user.
|
|
|
52
69
|
name: "Submit"
|
|
53
70
|
```
|
|
54
71
|
3. **Phase 0.5 — Auth Persistence**: if the feature has `@auth:<role>` tags and `specs/.auth/<role>.json` is missing/expired, run Phase 0.5 from `sungen-selector-fix` — user logs in manually in MCP browser → `browser_storage_state` → `specs/.auth/<role>.json`. Offer `sungen makeauth <role>` as CLI fallback only if `browser_storage_state` isn't available in this MCP version.
|
|
55
|
-
4. Compile
|
|
72
|
+
4. Compile via local-first dispatcher so the sungen monorepo's unpublished selector-resolver features (i18n `{{var}}` interpolation, namespaced selector lookup) are picked up:
|
|
73
|
+
- **Screen**: `[ -x ./bin/sungen.js ] && ./bin/sungen.js generate --screen <name> || npx sungen generate --screen <name>`
|
|
74
|
+
- **Flow**: `[ -x ./bin/sungen.js ] && ./bin/sungen.js generate --flow <name> || npx sungen generate --flow <name>`
|
|
75
|
+
|
|
76
|
+
Default: runtime data loading from YAML. Use `--inline-data` only if user requests compile-time hardcoded values.
|
|
56
77
|
|
|
57
78
|
## Run & Fix (phased — per `sungen-selector-fix` skill)
|
|
58
79
|
|
|
@@ -63,13 +84,31 @@ Parse **name** from `$ARGUMENTS`. If missing, ask the user.
|
|
|
63
84
|
|
|
64
85
|
## Playwright command guidelines
|
|
65
86
|
|
|
66
|
-
**
|
|
87
|
+
**Multi-feature screens** — `sungen generate --screen <name>` produces one `<basename>.spec.ts` per `.feature` file (e.g. `home.spec.ts` + `home-modal.spec.ts`). You must **invoke playwright once per spec file** so each gets its own JSON result that `sungen delivery` can pick up. Do NOT run a single command with the directory as the test argument — that bundles everything into one results file that delivery can't disambiguate.
|
|
88
|
+
|
|
89
|
+
**Per-spec JSON results** — each invocation writes its JSON report to a path matching the spec basename. When `--env <locale>` was parsed from `$ARGUMENTS`, prepend `SUNGEN_ENV=<locale>` — `playwright.config.ts` auto-inserts `.<locale>` before `.json` in the output path:
|
|
67
90
|
|
|
68
91
|
```bash
|
|
69
|
-
# ✅ Screen
|
|
92
|
+
# ✅ Screen with 1 feature
|
|
70
93
|
PLAYWRIGHT_JSON_OUTPUT_NAME=specs/generated/<name>/<name>-test-result.json \
|
|
71
94
|
npx playwright test specs/generated/<name>/<name>.spec.ts
|
|
72
95
|
|
|
96
|
+
# ✅ Screen with multiple features — loop in shell:
|
|
97
|
+
for spec in specs/generated/<name>/*.spec.ts; do
|
|
98
|
+
base=$(basename "$spec" .spec.ts)
|
|
99
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME="specs/generated/<name>/${base}-test-result.json" \
|
|
100
|
+
npx playwright test "$spec"
|
|
101
|
+
done
|
|
102
|
+
|
|
103
|
+
# ✅ Locale 'vi' — same loop, just prepend SUNGEN_ENV=vi
|
|
104
|
+
for spec in specs/generated/<name>/*.spec.ts; do
|
|
105
|
+
base=$(basename "$spec" .spec.ts)
|
|
106
|
+
SUNGEN_ENV=vi \
|
|
107
|
+
PLAYWRIGHT_JSON_OUTPUT_NAME="specs/generated/<name>/${base}-test-result.json" \
|
|
108
|
+
npx playwright test "$spec"
|
|
109
|
+
done
|
|
110
|
+
# → writes <basename>-test-result.vi.json for each feature
|
|
111
|
+
|
|
73
112
|
# ✅ Flow
|
|
74
113
|
PLAYWRIGHT_JSON_OUTPUT_NAME=specs/generated/flows/<name>/<name>-test-result.json \
|
|
75
114
|
npx playwright test specs/generated/flows/<name>/<name>.spec.ts
|
|
@@ -88,7 +127,7 @@ npx playwright test specs/generated/<screen>/<screen>.spec.ts
|
|
|
88
127
|
|
|
89
128
|
If you want to filter scenarios, use `-g "<pattern>"` instead of a reporter override.
|
|
90
129
|
|
|
91
|
-
`sungen delivery` reads the per-
|
|
130
|
+
`sungen delivery` reads per-feature `<basename>-test-result[.env].json` files (one per feature in the screen) and writes one CSV/XLSX per feature (e.g. `home-testcases.csv` + `home-modal-testcases.csv`). When `--env <locale>` was used here, run delivery with the same locale (`/sungen:delivery <name> --env <locale>`) so it picks the matching `*-test-result.<locale>.json` files and produces `*-testcases.<locale>.csv` / `.xlsx`.
|
|
92
131
|
|
|
93
132
|
## Next steps
|
|
94
133
|
|
|
@@ -20,8 +20,9 @@ You generate 3 files for sungen — a Gherkin compiler that produces Playwright
|
|
|
20
20
|
| `sungen-capture-local` | Load existing UI assets (screenshots, mockups, Figma exports) from `requirements/ui/` |
|
|
21
21
|
| `sungen-capture-live` | Capture a live running page via Playwright MCP (snapshot + screenshot) |
|
|
22
22
|
| `sungen-figma-source` | Figma URL → spec_figma.md + ui/*.png + provisional selectors |
|
|
23
|
+
| `sungen-locale` | Bootstrap i18n — audit selectors, detect locale switch mechanism, generate test-data overlay |
|
|
23
24
|
|
|
24
|
-
## Workflow (
|
|
25
|
+
## Workflow (7 AI commands)
|
|
25
26
|
|
|
26
27
|
| Command | What it does |
|
|
27
28
|
|---|---|
|
|
@@ -31,9 +32,11 @@ You generate 3 files for sungen — a Gherkin compiler that produces Playwright
|
|
|
31
32
|
| `/sungen:review <name>` | Score syntax, coverage, viewpoint quality (auto-detects screen or flow) |
|
|
32
33
|
| `/sungen:run-test <name>` | Generate `selectors.yaml`, compile, run, auto-fix (auto-detects screen or flow) |
|
|
33
34
|
| `/sungen:delivery [name...]` | Export test cases → CSV for QA delivery (all screens if no arg) |
|
|
35
|
+
| `/sungen:locale <name> <locale>` | Bootstrap i18n for a screen — audit selectors, detect locale switch, generate overlay (run before `/sungen:run-test --env <locale>`) |
|
|
34
36
|
|
|
35
37
|
**Screen path:** add-screen → create-test → review → run-test → delivery.
|
|
36
38
|
**Flow path:** add-flow → create-test → review → run-test → delivery.
|
|
39
|
+
**i18n path:** (after run-test passes for base locale) → locale → run-test --env <locale> → delivery --env <locale>.
|
|
37
40
|
|
|
38
41
|
`create-test`, `review`, and `run-test` auto-detect context: if `qa/flows/<name>/` exists → flow mode, else `qa/screens/<name>/` → screen mode.
|
|
39
42
|
|
|
@@ -82,6 +85,8 @@ qa/deliverables/<name>-testcases.xlsx # Styled workbook for client hand-off
|
|
|
82
85
|
|
|
83
86
|
**Environment overrides**: `SUNGEN_ENV=staging npx playwright test` merges `<screen>.staging.yaml` on top of `<screen>.yaml`. Create `<screen>.<env>.yaml` for environment-specific values (different credentials, URLs, test users).
|
|
84
87
|
|
|
88
|
+
**i18n support**: for multilingual sites, use `{{variable}}` in selector `name`/`value` fields to reference locale-dependent text from test-data. Create locale overlay files (e.g., `<screen>.vi.yaml`, `<screen>.staging-ja.yaml`) and run with `SUNGEN_ENV=vi`. One feature file + one selector file works across all locales. See `sungen-selector-keys` skill for details.
|
|
89
|
+
|
|
85
90
|
## CLI Commands
|
|
86
91
|
|
|
87
92
|
```bash
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sungen-locale
|
|
3
|
+
description: 'Bootstrap i18n for a screen/flow — audit hardcoded selector text, detect locale-switch mechanism via live Playwright, generate test-data overlay file. Auto-loaded by /sungen:locale command.'
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Take a screen/flow whose `selectors/*.yaml` and `.feature` files were authored against the default locale (usually Vietnamese) and prepare it to run against a second locale. Output:
|
|
10
|
+
|
|
11
|
+
1. `selectors/<feature>.yaml` — hardcoded `name`/`value` replaced with `{{var}}`
|
|
12
|
+
2. `test-data/<feature>.yaml` — base locale, complete with all new keys
|
|
13
|
+
3. `test-data/<feature>.<locale>.yaml` — overlay with only the keys that change
|
|
14
|
+
4. (Optional) `selectors/<feature>.yaml` Pages block updated when locale uses URL prefix or query param
|
|
15
|
+
|
|
16
|
+
After this skill finishes, `sungen run-test <name> --env <locale>` Just Works.
|
|
17
|
+
|
|
18
|
+
## Run mode — Live (preferred) vs Offline (fallback)
|
|
19
|
+
|
|
20
|
+
Pick mode **once at start**, based on whether MCP Playwright can reach the page.
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
Try: browser_navigate(baseURL)
|
|
24
|
+
→ succeeds + page renders content → LIVE MODE (all 6 phases)
|
|
25
|
+
→ fails (auth blocked, network down, app broken) → OFFLINE MODE
|
|
26
|
+
(audit + scaffold template + ask user to fill, skip detection phases)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Live mode is the value-add** — it auto-detects locale switch mechanism and auto-fills translations. Offline mode just makes the file structure right; user fills in text manually.
|
|
30
|
+
|
|
31
|
+
Announce which mode is being used before Phase 1.
|
|
32
|
+
|
|
33
|
+
## Phase 1 — Audit selectors (always, no MCP)
|
|
34
|
+
|
|
35
|
+
For each `.feature` file under the screen, read the matching `selectors/<feature>.yaml`. List every entry whose `name` or `value` field contains literal text WITHOUT `{{…}}` AND is not a CSS/href selector.
|
|
36
|
+
|
|
37
|
+
Classify each candidate:
|
|
38
|
+
- **`name` field of `role`-type selector** → very likely locale-dependent
|
|
39
|
+
- **`value` field of `text`-type selector** → very likely locale-dependent
|
|
40
|
+
- **`value` of `locator`-type selector** that contains `:has-text("…")` → check if the text is in target language
|
|
41
|
+
- **`value` of `page`-type selector** → URL — handled separately in Phase 3
|
|
42
|
+
- **CSS / href / attribute selectors** (e.g. `a[href="/awards"]`) → skip, locale-invariant
|
|
43
|
+
|
|
44
|
+
Print the candidate list as a table:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
selector key | field | hardcoded value | classification
|
|
48
|
+
-------------------+-------+---------------------------+------------------
|
|
49
|
+
nav about | name | Giới thiệu SAA 2025 | locale-dependent
|
|
50
|
+
nav awards | name | Thông tin giải thưởng | locale-dependent
|
|
51
|
+
event date | name | 26/12/2025 | maybe (date format)
|
|
52
|
+
nav kudos | name | Sun* Kudos | brand — skip
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
If there are zero candidates and no `{{var}}` either, the screen has no localizable text → tell user and stop.
|
|
56
|
+
|
|
57
|
+
## Phase 2 — Capture base locale (LIVE only)
|
|
58
|
+
|
|
59
|
+
1. Read `playwright.config.ts` for `baseURL`. Read `Path:` from `.feature` for the entry-point path.
|
|
60
|
+
2. If the screen has `@auth:<role>` tags, load `specs/.auth/<role>.json` via `browser_set_storage_state` first.
|
|
61
|
+
3. `browser_navigate(baseURL + entryPath)` then `browser_wait_for` something stable.
|
|
62
|
+
4. Capture:
|
|
63
|
+
- `browser_snapshot()` — DOM accessibility tree
|
|
64
|
+
- `browser_evaluate(() => location.href)` — full URL
|
|
65
|
+
- `browser_evaluate(() => ({ localStorage: {...localStorage}, cookies: document.cookie }))` — storage state hash
|
|
66
|
+
5. For each candidate from Phase 1: verify the hardcoded text actually appears on the page (string contains check against snapshot text). Drop candidates that don't appear (probably stale selectors or false positives).
|
|
67
|
+
|
|
68
|
+
Save state to memory as `baseLocale = { url, snapshot, storage }`.
|
|
69
|
+
|
|
70
|
+
If page redirects to `/login` (auth blocker) → stop. Print: *"Auth blocked — cannot capture live page. Fall back to OFFLINE mode by re-running once auth works, or pick `Offline-only` next time."*
|
|
71
|
+
|
|
72
|
+
## Phase 3 — Switch locale (LIVE only) — detect mechanism + storage delta
|
|
73
|
+
|
|
74
|
+
Goal: identify (a) HOW to switch the app to the target locale, and (b) WHAT app-side storage state ends up holding the locale preference so a fresh BrowserContext can be primed identically without driving the UI.
|
|
75
|
+
|
|
76
|
+
Before triggering the switch, capture full storage baseline:
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
const before = await browser_evaluate(() => ({
|
|
80
|
+
sessionStorage: { ...sessionStorage },
|
|
81
|
+
localStorage: { ...localStorage },
|
|
82
|
+
cookies: document.cookie,
|
|
83
|
+
url: location.href,
|
|
84
|
+
}));
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Then try mechanisms in order. First one that produces visibly different text wins. For EACH attempt, capture `after` state and diff against `before`.
|
|
88
|
+
|
|
89
|
+
**3a. URL prefix**
|
|
90
|
+
- `browser_navigate(baseURL + '/' + locale + entryPath)` (e.g. `https://saa-2025.../en/`)
|
|
91
|
+
- Wait, snapshot
|
|
92
|
+
- Compare text content with baseLocale snapshot for a known-changed element
|
|
93
|
+
- If text differs and page didn't 404 → **URL prefix mechanism**. Save `localePrefix = '/' + locale`.
|
|
94
|
+
|
|
95
|
+
**3b. Query param** (only if 3a failed)
|
|
96
|
+
- `browser_navigate(baseURL + entryPath + '?lang=' + locale)` (also try `?locale=`, `?lng=`, `?l=`, `?language=`)
|
|
97
|
+
- Same diff check
|
|
98
|
+
- If text differs → **Query param mechanism**. Save `localeQuery = '?lang=' + locale` (or whichever variant worked).
|
|
99
|
+
|
|
100
|
+
**3c. Language switcher UI** (only if 3a + 3b failed)
|
|
101
|
+
- Look in base-locale snapshot for buttons/links matching: `'Select language'`, `'Language'`, `'言語'`, `'Ngôn ngữ'`, role=combobox
|
|
102
|
+
- If found, ask user: *"Detected possible language switcher: [X]. Click it and proceed?"*
|
|
103
|
+
- On confirm: `browser_click` the switcher, then `browser_click` the locale option
|
|
104
|
+
- Verify text changed
|
|
105
|
+
- If yes → **UI switcher mechanism**. Save selector + option details.
|
|
106
|
+
|
|
107
|
+
**3d. None detected**
|
|
108
|
+
- AskUserQuestion: *"Couldn't auto-detect locale switching. Either (a) demo it for me in browser then I'll snapshot, (b) provide locale URL manually, (c) skip — assume URL prefix `/<locale>`."*
|
|
109
|
+
|
|
110
|
+
### Phase 3.5 — Storage diff (always run after Phase 3 succeeds)
|
|
111
|
+
|
|
112
|
+
After locale text confirmed switched, capture `after` state and diff:
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
const after = await browser_evaluate(() => ({
|
|
116
|
+
sessionStorage: { ...sessionStorage },
|
|
117
|
+
localStorage: { ...localStorage },
|
|
118
|
+
cookies: document.cookie,
|
|
119
|
+
url: location.href,
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
// Diff each storage area:
|
|
123
|
+
const sessionDiff = diffEntries(before.sessionStorage, after.sessionStorage);
|
|
124
|
+
const localDiff = diffEntries(before.localStorage, after.localStorage);
|
|
125
|
+
const cookieDiff = diffCookies(before.cookies, after.cookies);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Filter noise** — only keep entries where:
|
|
129
|
+
- Key name contains `lang|locale|language|i18n|intl` (case-insensitive), OR
|
|
130
|
+
- Value matches a locale code pattern: `^[a-z]{2}(-[A-Z]{2})?$` or matches the target locale code exactly
|
|
131
|
+
|
|
132
|
+
Drop known noise:
|
|
133
|
+
- Auth tokens: `sb-*`, `*-token`, `*-jwt`, `*-session-id`
|
|
134
|
+
- Analytics: `_ga*`, `_gid`, `_fbp`
|
|
135
|
+
- App state: `csrf*`, `last-*`, `visit-*`
|
|
136
|
+
|
|
137
|
+
**Auto-confidence scoring** per remaining entry:
|
|
138
|
+
- High confidence: key name contains locale-related word AND value is a locale code matching target → KEEP, no user prompt
|
|
139
|
+
- Medium: only one signal matches → present to user for confirmation
|
|
140
|
+
- Low: neither matches but key changed → present to user, default to skip
|
|
141
|
+
|
|
142
|
+
### Phase 3.6 — Verification (always run after Phase 3.5)
|
|
143
|
+
|
|
144
|
+
For each high/medium-confidence storage entry, verify by replaying in a FRESH context:
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
// 1. Capture current page state (have JWT in memory) — note: can't replay JWT separately
|
|
148
|
+
// 2. Open new browser_navigate to baseURL but pre-set the detected key via addInitScript
|
|
149
|
+
// 3. Snapshot new page and confirm a known target-locale string appears
|
|
150
|
+
|
|
151
|
+
// Pseudo-code — real impl uses Playwright MCP `browser_evaluate` with init pattern
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Actually since MCP browser sessions don't expose `addInitScript` directly, use a softer verification:
|
|
155
|
+
- Manually set the candidate key via `browser_evaluate(() => sessionStorage.setItem('key', 'value'))`
|
|
156
|
+
- Hard reload the page: `browser_navigate(baseURL)` to force re-init
|
|
157
|
+
- Snapshot, look for known target-locale text
|
|
158
|
+
- Caveat: hard reload kills any in-memory JWT — auth blocker amplifies failure surface. Skip verification if the screen has `@auth:*` tags and the staging app has known JWT-persistence issues.
|
|
159
|
+
|
|
160
|
+
If verification fails / can't run → drop confidence by one tier, ask user.
|
|
161
|
+
|
|
162
|
+
Save mechanism + verified storage delta to memory for use in Phase 6.
|
|
163
|
+
|
|
164
|
+
## Phase 4 — Diff base ↔ target (LIVE only)
|
|
165
|
+
|
|
166
|
+
For each candidate from Phase 1 that survived Phase 2:
|
|
167
|
+
- Find the SAME element in the target-locale snapshot (match by `role`+position, by `aria-label` if available, by neighbor structure)
|
|
168
|
+
- Extract its text → `targetText`
|
|
169
|
+
- Pair: `(selectorKey, hardcoded baseText, observed targetText)`
|
|
170
|
+
|
|
171
|
+
If matching fails for a candidate → mark as "needs manual" — flag for user input rather than skip.
|
|
172
|
+
|
|
173
|
+
Result: list of triples `{ selectorKey, baseText, targetText, confidence }`.
|
|
174
|
+
|
|
175
|
+
## Phase 5 — Confirm + edit (always)
|
|
176
|
+
|
|
177
|
+
Present the proposal table:
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
selector key | base text | target text | proposed var | apply?
|
|
181
|
+
-------------------+--------------------------+----------------------+------------------+-------
|
|
182
|
+
nav about | Giới thiệu SAA 2025 | About SAA 2025 | nav_about | [✓]
|
|
183
|
+
nav awards | Thông tin giải thưởng | Awards Info | nav_awards | [✓]
|
|
184
|
+
event location | Au Co Arts Centre, Hanoi | Au Co Arts Centre… | event_location | (already has {{}})
|
|
185
|
+
nav kudos | Sun* Kudos | Sun* Kudos | — | [skip — same]
|
|
186
|
+
event date | 26/12/2025 | 26/12/2025 | — | [skip — same]
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Propose **var names** by snake_casing the selector key. Avoid collisions with existing test-data keys.
|
|
190
|
+
|
|
191
|
+
Auto-skip rows where `baseText === targetText` (brand names, dates that don't change between locales).
|
|
192
|
+
|
|
193
|
+
Ask user via AskUserQuestion: *"Review the table above. Confirm to apply / edit individual rows / re-run capture / cancel."*
|
|
194
|
+
|
|
195
|
+
If user wants to edit a row → fall through to a per-row prompt asking for `var name` or `targetText` correction.
|
|
196
|
+
|
|
197
|
+
In OFFLINE mode this phase is the same table but `target text` column is blank — user fills via subsequent prompts or by editing the overlay file after the skill finishes.
|
|
198
|
+
|
|
199
|
+
## Phase 6 — Apply changes (always, after confirmation)
|
|
200
|
+
|
|
201
|
+
For each confirmed row:
|
|
202
|
+
|
|
203
|
+
**6a. Update `selectors/<feature>.yaml`**
|
|
204
|
+
|
|
205
|
+
Replace `name: '<baseText>'` with `name: '{{<varName>}}'`. Preserve quoting style.
|
|
206
|
+
|
|
207
|
+
**6b. Update `test-data/<feature>.yaml`** (base locale, complete dictionary)
|
|
208
|
+
|
|
209
|
+
Append new keys at the end, grouped under a `# === i18n: <screen> ===` comment. Example:
|
|
210
|
+
|
|
211
|
+
```yaml
|
|
212
|
+
# === i18n: home ===
|
|
213
|
+
nav_about: 'Giới thiệu SAA 2025'
|
|
214
|
+
nav_awards: 'Thông tin giải thưởng'
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**6c. Create `test-data/<feature>.<locale>.yaml`** (overlay, only diffs)
|
|
218
|
+
|
|
219
|
+
```yaml
|
|
220
|
+
# home — <locale> overlay. Only keys that change vs base.
|
|
221
|
+
# Run with: SUNGEN_ENV=<locale> npx playwright test ...
|
|
222
|
+
|
|
223
|
+
nav_about: 'About SAA 2025'
|
|
224
|
+
nav_awards: 'Awards Info'
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**6d. (URL/query mechanism only) Update Pages selectors**
|
|
228
|
+
|
|
229
|
+
If Phase 3 detected URL prefix:
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
# selectors/<feature>.yaml — Pages block becomes locale-aware
|
|
233
|
+
home:
|
|
234
|
+
type: 'page'
|
|
235
|
+
value: '{{base_path}}/'
|
|
236
|
+
awards:
|
|
237
|
+
type: 'page'
|
|
238
|
+
value: '{{base_path}}/awards'
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
And add to test-data:
|
|
242
|
+
```yaml
|
|
243
|
+
# test-data/<feature>.yaml (base)
|
|
244
|
+
base_path: ''
|
|
245
|
+
|
|
246
|
+
# test-data/<feature>.<locale>.yaml (overlay)
|
|
247
|
+
base_path: '/en'
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
If Phase 3 detected query param: similar but with `query_suffix: ''` / `query_suffix: '?lang=en'` appended to each page value.
|
|
251
|
+
|
|
252
|
+
If Phase 3 detected UI switcher: do NOT modify Pages. Instead, write the storage delta from Phase 3.5 into `specs/generated/locale-config.json` (sibling of `specs/generated/base.ts` + `specs/generated/locale-fixture.ts`) (see 6f below).
|
|
253
|
+
|
|
254
|
+
**6f. Storage injection config — `specs/generated/locale-config.json` (sibling of `specs/generated/base.ts` + `specs/generated/locale-fixture.ts`)**
|
|
255
|
+
|
|
256
|
+
Always write `specs/generated/locale-config.json` (sibling of `specs/generated/base.ts` + `specs/generated/locale-fixture.ts`) with the verified storage delta from Phase 3.5/3.6. This file is consumed by `specs/locale-fixture.ts` (auto-generated alongside `specs/base.ts`) — the fixture wraps Playwright's context, calls `addInitScript` to seed sessionStorage / localStorage, and `addCookies` for any cookies, BEFORE the first page navigation. Without this file, `SUNGEN_ENV=<locale>` only swaps test-data overlay — the browser would still boot in base locale.
|
|
257
|
+
|
|
258
|
+
Schema (only include areas that actually changed):
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"$schema": "sungen-locale-config-v1",
|
|
263
|
+
"sessionStorage": {
|
|
264
|
+
"saa-language-preference": "${SUNGEN_ENV}"
|
|
265
|
+
},
|
|
266
|
+
"localStorage": {},
|
|
267
|
+
"cookies": [],
|
|
268
|
+
"notes": "Detected by /sungen:locale on YYYY-MM-DD. App persists locale in sessionStorage; initial trigger uses ?language=<code> query param."
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Notes on the schema:
|
|
273
|
+
- Use the literal placeholder `"${SUNGEN_ENV}"` (or `"{{SUNGEN_ENV}}"`) when the stored value equals the locale code itself. The fixture substitutes `process.env.SUNGEN_ENV` at runtime, so the same file works for `en`, `ja`, etc.
|
|
274
|
+
- Use a hardcoded literal (e.g. `"en_US"`) when the stored value is fixed regardless of which locale runs.
|
|
275
|
+
- `cookies[]` entries need either `domain` or `url`. Other fields (`path`, `httpOnly`, `secure`, `sameSite`) are optional and pass through to `context.addCookies()`.
|
|
276
|
+
- Do not commit auth tokens / session IDs into this file even if they showed up in the Phase 3.5 diff — they are noise. The skill's Phase 3.5 filter must drop them; if one slipped through, edit it out before applying.
|
|
277
|
+
- `notes` is free-form, informational; the fixture ignores it.
|
|
278
|
+
|
|
279
|
+
Multi-locale projects: the same `locale-config.json` works for every locale because the value templating uses `${SUNGEN_ENV}`. No per-locale file needed.
|
|
280
|
+
|
|
281
|
+
The file lives at `specs/generated/locale-config.json` (sibling of `specs/generated/base.ts` + `specs/generated/locale-fixture.ts`) (sibling of `specs/base.ts`). If a different output directory is used, mirror the existing `specs/base.ts` location.
|
|
282
|
+
|
|
283
|
+
**6e. Compile**
|
|
284
|
+
|
|
285
|
+
Run `sungen generate --screen <name>` (or `--flow`) and report any compile errors back to the user. The selectors changed so compile must succeed before run-test.
|
|
286
|
+
|
|
287
|
+
## Phase 7 — Hand off
|
|
288
|
+
|
|
289
|
+
Print summary:
|
|
290
|
+
- N selectors converted to `{{var}}`
|
|
291
|
+
- M base keys added to `test-data/<feature>.yaml`
|
|
292
|
+
- K overlay keys written to `test-data/<feature>.<locale>.yaml`
|
|
293
|
+
- Pages selectors updated: yes/no
|
|
294
|
+
- Locale-switching mechanism: URL prefix `/en` / query `?lang=en` / UI switcher / manual
|
|
295
|
+
|
|
296
|
+
Suggest:
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
/sungen:run-test <name> --env <locale>
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Multi-feature screens
|
|
303
|
+
|
|
304
|
+
If the screen has multiple `.feature` files (e.g. `home.feature` + `home-modal.feature`), repeat Phase 1 → Phase 6 for each feature file with its own selectors + test-data pair. Phase 2 + 3 (live capture + locale switch detection) run **once per screen** — the mechanism is the same. Phase 4 (text diff) runs per feature because each has its own scope of UI.
|
|
305
|
+
|
|
306
|
+
## What NOT to do
|
|
307
|
+
|
|
308
|
+
- Do not edit `.feature` files. The i18n shape is already correct there (`{{var}}` was the convention from the start). Only selectors + test-data need surgery.
|
|
309
|
+
- Do not write a separate selectors file per locale (`home.en.yaml`). One selectors file with `{{var}}` works across all locales — that's the whole point.
|
|
310
|
+
- Do not delete keys from the base `test-data/<feature>.yaml`. Always append. Existing tests rely on base values being complete.
|
|
311
|
+
- Do not run tests in this skill. Hand off to `/sungen:run-test` for execution.
|
|
312
|
+
- Do not commit. Hand off to user.
|
|
313
|
+
|
|
314
|
+
## Memory link
|
|
315
|
+
|
|
316
|
+
Related: [[saa-auth-blocker]] — Live mode will not work on SAA staging while the upstream Supabase `persistSession: false` issue is open. Default to Offline mode on SAA until that ships.
|
|
@@ -61,6 +61,7 @@ When running Phase 0 for a **flow** (`qa/flows/<name>/`), check existing screen
|
|
|
61
61
|
- Selector priority: follow the table in **Diagnosis & Fix § Step 3** (`testid` > `role`+name > `placeholder` > `label` > `locator` > `text`).
|
|
62
62
|
- Copy names **character-for-character** from the snapshot. Never infer from the Gherkin label.
|
|
63
63
|
- If an element is auto-inferable per `sungen-selector-keys` § Auto-Infer, **omit it** from YAML — keep the file minimal.
|
|
64
|
+
- **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).
|
|
64
65
|
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.
|
|
65
66
|
8. **Merge, don't overwrite**: preserve the page selector and any user-authored entries in `selectors.yaml`. Only add missing keys.
|
|
66
67
|
9. **Show summary + confirm**: list the keys that will be added, ask the user to approve, then write the file.
|