@sun-asterisk/sungen 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -51
- package/dist/cli/index.js +1 -1
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts +6 -1
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/scenario.hbs +0 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/drag-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/expand-action.hbs +11 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-editor-action.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/toggle-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-dialog-heading-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/is-hidden-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/label-value-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/list-item-count-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/loading-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/page-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/selected-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/sorted-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-filter.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-index.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-column-exists.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-dialog-heading-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +25 -0
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +41 -3
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.js +58 -6
- package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/form-patterns.js +3 -3
- package/dist/generators/test-generator/patterns/form-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.js +86 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/navigation-patterns.js +2 -2
- package/dist/generators/test-generator/patterns/navigation-patterns.js.map +1 -1
- package/dist/generators/test-generator/template-engine.d.ts +6 -0
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
- package/dist/generators/test-generator/template-engine.js +1 -0
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +15 -8
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +26 -197
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +4 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +49 -4
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +4 -3
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md +11 -46
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +11 -46
- package/dist/orchestrator/templates/ai-instructions/claude-config.md +9 -8
- package/dist/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +11 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +8 -4
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +206 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +19 -21
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +256 -0
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +14 -17
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +16 -47
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +16 -47
- package/dist/orchestrator/templates/ai-instructions/copilot-config.md +8 -7
- package/dist/orchestrator/templates/ai-instructions/{copilot-skill-error-mapping.md → github-skill-sungen-error-mapping.md} +14 -3
- package/{src/orchestrator/templates/ai-instructions/copilot-skill-gherkin-syntax.md → dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md} +11 -7
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +206 -0
- package/dist/orchestrator/templates/ai-instructions/{copilot-skill-selector-keys.md → github-skill-sungen-selector-keys.md} +22 -24
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +256 -0
- package/dist/orchestrator/templates/playwright.config.d.ts.map +1 -1
- package/dist/orchestrator/templates/playwright.config.js +3 -1
- package/dist/orchestrator/templates/playwright.config.js.map +1 -1
- package/dist/orchestrator/templates/playwright.config.ts +3 -1
- package/dist/orchestrator/templates/readme.md +78 -101
- package/package.json +1 -1
- package/src/cli/index.ts +1 -1
- package/src/generators/test-generator/adapters/adapter-interface.ts +7 -1
- package/src/generators/test-generator/adapters/playwright/templates/scenario.hbs +0 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/drag-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/expand-action.hbs +11 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-editor-action.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/toggle-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-dialog-heading-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/is-hidden-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/label-value-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/list-item-count-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/loading-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/page-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/selected-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/sorted-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-filter.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-index.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-column-exists.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-dialog-heading-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +25 -0
- package/src/generators/test-generator/code-generator.ts +50 -8
- package/src/generators/test-generator/patterns/assertion-patterns.ts +62 -6
- package/src/generators/test-generator/patterns/form-patterns.ts +3 -3
- package/src/generators/test-generator/patterns/interaction-patterns.ts +93 -1
- package/src/generators/test-generator/patterns/navigation-patterns.ts +2 -2
- package/src/generators/test-generator/template-engine.ts +4 -0
- package/src/generators/test-generator/utils/selector-resolver.ts +27 -204
- package/src/orchestrator/project-initializer.ts +60 -5
- package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +4 -3
- package/src/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md +11 -46
- package/src/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +11 -46
- package/src/orchestrator/templates/ai-instructions/claude-config.md +9 -8
- package/src/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +11 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +8 -4
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +206 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +19 -21
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +256 -0
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +14 -17
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +16 -47
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +16 -47
- package/src/orchestrator/templates/ai-instructions/copilot-config.md +8 -7
- package/src/orchestrator/templates/ai-instructions/{copilot-skill-error-mapping.md → github-skill-sungen-error-mapping.md} +14 -3
- package/{dist/orchestrator/templates/ai-instructions/copilot-skill-gherkin-syntax.md → src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md} +11 -7
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +206 -0
- package/src/orchestrator/templates/ai-instructions/{copilot-skill-selector-keys.md → github-skill-sungen-selector-keys.md} +22 -24
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +256 -0
- package/src/orchestrator/templates/playwright.config.ts +3 -1
- package/src/orchestrator/templates/readme.md +78 -101
|
@@ -17,11 +17,17 @@ sungen generate → compiles Gherkin + selectors + data → Playwright .spec.ts
|
|
|
17
17
|
│ ├── selectors/ # Element locator YAML mappings
|
|
18
18
|
│ └── test-data/ # Test data YAML values
|
|
19
19
|
├── specs/
|
|
20
|
-
│
|
|
21
|
-
|
|
20
|
+
│ └── generated/ # Auto-generated Playwright tests
|
|
21
|
+
├── .claude/
|
|
22
|
+
│ ├── commands/sungen/ # Claude Code slash commands
|
|
23
|
+
│ └── skills/ # Claude Code auto-loaded skills
|
|
22
24
|
├── .github/
|
|
23
|
-
│
|
|
24
|
-
└──
|
|
25
|
+
│ ├── copilot-instructions.md # AI rules for GitHub Copilot
|
|
26
|
+
│ └── prompts/ # Copilot slash commands
|
|
27
|
+
├── .vscode/
|
|
28
|
+
│ └── settings.json # Copilot auto-attach settings
|
|
29
|
+
├── CLAUDE.md # AI rules for Claude Code
|
|
30
|
+
└── playwright.config.ts # Playwright config (timeout: 10s, workers: 2)
|
|
25
31
|
```
|
|
26
32
|
|
|
27
33
|
## Workflow
|
|
@@ -32,45 +38,50 @@ Step 1 Step 2 Step 3
|
|
|
32
38
|
│ /add- │────────▶│ /make-tc │────────▶│ /make- │
|
|
33
39
|
│ screen │ │ │ │ test │
|
|
34
40
|
└──────────┘ └──────────┘ └──────────┘
|
|
35
|
-
Scaffold
|
|
36
|
-
directories
|
|
37
|
-
|
|
41
|
+
Scaffold Pick sections Generate
|
|
42
|
+
directories → design TCs selectors
|
|
43
|
+
→ generate → compile
|
|
44
|
+
.feature + → run tests
|
|
45
|
+
test-data → auto-fix
|
|
38
46
|
```
|
|
39
47
|
|
|
40
48
|
Use AI commands (Claude Code or GitHub Copilot) to drive the workflow:
|
|
41
49
|
|
|
42
50
|
### Step 1: Add a screen
|
|
43
51
|
|
|
44
|
-
| Claude Code | GitHub Copilot |
|
|
52
|
+
| Claude Code | GitHub Copilot (VS Code) |
|
|
45
53
|
|---|---|
|
|
46
|
-
| `/sungen:add-screen
|
|
54
|
+
| `/sungen:add-screen` | `/sungen-add-screen` |
|
|
47
55
|
|
|
48
56
|
Scaffolds `qa/screens/<name>/` with empty feature, selectors, and test-data files, then asks if you want to create test cases.
|
|
49
57
|
|
|
50
58
|
### Step 2: Create test cases
|
|
51
59
|
|
|
52
|
-
| Claude Code | GitHub Copilot |
|
|
60
|
+
| Claude Code | GitHub Copilot (VS Code) |
|
|
53
61
|
|---|---|
|
|
54
|
-
| `/sungen:make-tc login` |
|
|
62
|
+
| `/sungen:make-tc login` | `/sungen-make-tc login` |
|
|
55
63
|
|
|
56
|
-
AI acts as a **Senior QA Engineer**:
|
|
64
|
+
AI acts as a **Senior QA Engineer**:
|
|
65
|
+
1. Explores the live page via Playwright MCP (asks user to log in if auth needed)
|
|
66
|
+
2. Identifies screen sections → asks user which to focus on
|
|
67
|
+
3. Generates **20+ scenarios per viewpoint** (UI/UX, Validation, Logic, Security) for each section
|
|
68
|
+
4. Confirms test plan before generating `.feature` + `test-data.yaml`
|
|
57
69
|
|
|
58
70
|
### Step 3: Compile & run tests
|
|
59
71
|
|
|
60
|
-
| Claude Code | GitHub Copilot |
|
|
72
|
+
| Claude Code | GitHub Copilot (VS Code) |
|
|
61
73
|
|---|---|
|
|
62
|
-
| `/sungen:make-test login` |
|
|
74
|
+
| `/sungen:make-test login` | `/sungen-make-test login` |
|
|
63
75
|
|
|
64
|
-
AI acts as a **Senior Developer**:
|
|
76
|
+
AI acts as a **Senior Developer**:
|
|
77
|
+
1. Navigates to live page, takes snapshot, verifies DOM properties
|
|
78
|
+
2. Generates `selectors.yaml` from exact accessible names
|
|
79
|
+
3. Compiles Gherkin → Playwright `.spec.ts`
|
|
80
|
+
4. Runs tests, auto-fixes selectors on failure (up to 5 attempts)
|
|
65
81
|
|
|
66
|
-
### Auth setup
|
|
82
|
+
### Auth setup
|
|
67
83
|
|
|
68
|
-
If any page requires authentication,
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
sungen makeauth admin --url <baseURL>
|
|
72
|
-
# Opens browser → login manually → saves specs/.auth/admin.json
|
|
73
|
-
```
|
|
84
|
+
If any page requires authentication, the AI will ask you to **log in manually via the MCP browser** during Step 2 or Step 3. No separate auth command needed.
|
|
74
85
|
|
|
75
86
|
### Manual CLI commands
|
|
76
87
|
|
|
@@ -85,7 +96,24 @@ npx playwright test --ui # Interactive mode
|
|
|
85
96
|
|
|
86
97
|
---
|
|
87
98
|
|
|
88
|
-
##
|
|
99
|
+
## VS Code Copilot Setup
|
|
100
|
+
|
|
101
|
+
Add to `.vscode/settings.json` to auto-load Gherkin syntax when editing `.feature` files:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"github.copilot.chat.codeGeneration.instructions": [
|
|
106
|
+
{
|
|
107
|
+
"file": ".github/prompts/sungen-gherkin-syntax.prompt.md",
|
|
108
|
+
"applyTo": "**/*.feature"
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Gherkin Quick Reference
|
|
89
117
|
|
|
90
118
|
### Syntax
|
|
91
119
|
|
|
@@ -97,101 +125,50 @@ User <action> [<Target>] <type> with {{<Value>}}
|
|
|
97
125
|
- `{{Value}}` → test data reference → lookup in `test-data/*.yaml`
|
|
98
126
|
- `<type>` → element type: button, link, field, heading, text, etc.
|
|
99
127
|
|
|
100
|
-
###
|
|
101
|
-
|
|
102
|
-
#### Actions
|
|
103
|
-
|
|
104
|
-
| Pattern | Example |
|
|
105
|
-
|---|---|
|
|
106
|
-
| Simple click | `User click [Submit] button` |
|
|
107
|
-
| Click with data | `User click [Teammate] button with {{name}}` |
|
|
108
|
-
| Fill field | `User fill [Email] field with {{email}}` |
|
|
109
|
-
| Check/Uncheck | `User check [Remember me] checkbox` |
|
|
110
|
-
| Select dropdown | `User select [Country] dropdown with {{country}}` |
|
|
111
|
-
| Upload file | `User upload [Avatar] file with {{path}}` |
|
|
112
|
-
| Double click | `User double click [Cell] element` |
|
|
113
|
-
| Hover | `User hover [Info] icon` |
|
|
114
|
-
| Clear | `User clear [Search] field` |
|
|
115
|
-
|
|
116
|
-
#### Keyboard
|
|
117
|
-
|
|
118
|
-
| Pattern | Example |
|
|
119
|
-
|---|---|
|
|
120
|
-
| Global key | `User press Escape key` |
|
|
121
|
-
| Key on element | `User press Enter on [Search] field` |
|
|
122
|
-
|
|
123
|
-
#### Navigation
|
|
124
|
-
|
|
125
|
-
| Pattern | Example |
|
|
126
|
-
|---|---|
|
|
127
|
-
| Open page | `User is on [login] page` |
|
|
128
|
-
| See page (URL) | `User see [dashboard] page` |
|
|
129
|
-
|
|
130
|
-
#### Wait
|
|
131
|
-
|
|
132
|
-
| Pattern | Example |
|
|
133
|
-
|---|---|
|
|
134
|
-
| Wait timeout | `User wait for 3 seconds` |
|
|
135
|
-
| Wait element | `User wait for [Modal] dialog` |
|
|
136
|
-
| Wait hidden | `User wait for [Loading] spinner is hidden` |
|
|
137
|
-
| Wait with data | `User wait for [Dialog] dialog with {{title}}` |
|
|
138
|
-
|
|
139
|
-
#### Assertions
|
|
140
|
-
|
|
141
|
-
| Pattern | Example |
|
|
142
|
-
|---|---|
|
|
143
|
-
| See element | `User see [Welcome] heading` |
|
|
144
|
-
| See with value | `User see [Title] heading with {{title}}` |
|
|
145
|
-
| State check | `User see [Submit] button is disabled` |
|
|
146
|
-
| State + data | `User see [Panel] dialog with {{title}} is hidden` |
|
|
147
|
-
| Text contains | `User see [Message] text contains {{partial}}` |
|
|
148
|
-
|
|
149
|
-
States: `hidden`, `visible`, `disabled`, `enabled`, `checked`, `unchecked`, `focused`, `empty`
|
|
150
|
-
|
|
151
|
-
#### Table
|
|
128
|
+
### Key Patterns
|
|
152
129
|
|
|
153
130
|
| Pattern | Example |
|
|
154
131
|
|---|---|
|
|
155
|
-
|
|
|
156
|
-
|
|
|
157
|
-
|
|
|
158
|
-
|
|
|
159
|
-
|
|
|
160
|
-
|
|
|
161
|
-
|
|
|
162
|
-
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
| Pattern | Example |
|
|
167
|
-
|---|---|
|
|
168
|
-
| Scroll | `User scroll to [Footer] section` |
|
|
169
|
-
| Frame enter | `User switch to [Payment] frame` |
|
|
170
|
-
| Frame exit | `User switch to [main] frame` |
|
|
132
|
+
| Navigate | `User is on [login] page` |
|
|
133
|
+
| Click | `User click [Submit] button` |
|
|
134
|
+
| Fill | `User fill [Email] field with {{email}}` |
|
|
135
|
+
| Assert visible | `User see [Welcome] heading is visible` |
|
|
136
|
+
| Assert text | `User see [Title] heading with {{title}}` |
|
|
137
|
+
| Assert state | `User see [Submit] button is disabled` |
|
|
138
|
+
| Wait for | `User wait for [Modal] dialog is visible` |
|
|
139
|
+
| Table row | `User see [Users] table has row with {{name}}` |
|
|
140
|
+
| Table column | `User see [Users] table has [Email] column` |
|
|
141
|
+
|
|
142
|
+
States: `hidden` `visible` `disabled` `enabled` `checked` `unchecked` `focused` `empty` `loading` `selected`
|
|
171
143
|
|
|
172
144
|
### Tags
|
|
173
145
|
|
|
174
146
|
| Tag | Purpose |
|
|
175
147
|
|---|---|
|
|
176
148
|
| `@auth:role` | Use Playwright storage state for auth |
|
|
149
|
+
| `@no-auth` | Disable inherited auth for this scenario |
|
|
177
150
|
| `@steps:name` | Define reusable step group |
|
|
178
151
|
| `@extend:name` | Inherit steps from another scenario |
|
|
179
152
|
| `@manual` | Skip scenario in generation |
|
|
180
153
|
|
|
181
|
-
### Selector
|
|
154
|
+
### YAML Selector Keys
|
|
155
|
+
|
|
156
|
+
Keys are **lowercase with spaces**, Unicode preserved:
|
|
157
|
+
- `[Search Content]` → `search content:`
|
|
158
|
+
- `[Thời gian]` → `thời gian:`
|
|
159
|
+
- Same label, different types → `add campaign--button:` / `add campaign--text:`
|
|
160
|
+
|
|
161
|
+
### Selector Types
|
|
182
162
|
|
|
183
163
|
```yaml
|
|
184
|
-
|
|
185
|
-
type: '
|
|
186
|
-
value: '
|
|
187
|
-
name: '
|
|
188
|
-
nth: 0
|
|
189
|
-
exact: true # exact name match (avoid "Submit" matching "Submit Form")
|
|
190
|
-
scope: 'desktop navigation' # scope to parent landmark
|
|
191
|
-
match: 'exact' # exact text match for getByText
|
|
164
|
+
search:
|
|
165
|
+
type: 'placeholder' # testid, role, placeholder, label, text, locator, page
|
|
166
|
+
value: 'Search users' # placeholder text, role name, CSS selector, etc.
|
|
167
|
+
name: 'Search' # accessible name (for role type)
|
|
168
|
+
nth: 0 # element index (for multiple matches)
|
|
192
169
|
```
|
|
193
170
|
|
|
194
|
-
|
|
171
|
+
Priority: `data-testid` > `role+name` > `placeholder` > `label` > `text` > `CSS locator`
|
|
195
172
|
|
|
196
173
|
---
|
|
197
174
|
|
package/package.json
CHANGED
package/src/cli/index.ts
CHANGED
|
@@ -3,13 +3,19 @@
|
|
|
3
3
|
* Defines the contract for framework-specific test code generation
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
export interface AuthGroup {
|
|
7
|
+
authRole?: string;
|
|
8
|
+
scenarios: string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
export interface TestFileData {
|
|
7
12
|
imports: string;
|
|
8
13
|
featureName: string;
|
|
9
14
|
featureDescription?: string;
|
|
10
15
|
background?: string;
|
|
11
16
|
scenarios: string[];
|
|
12
|
-
|
|
17
|
+
authGroups?: AuthGroup[]; // Grouped by auth role for nested describes
|
|
18
|
+
singleAuthRole?: string; // Auth role when all scenarios share the same role
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
export interface ScenarioData {
|
package/src/generators/test-generator/adapters/playwright/templates/steps/actions/drag-action.hbs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await {{> locator}}.dragTo({{targetLocator}});
|
package/src/generators/test-generator/adapters/playwright/templates/steps/actions/expand-action.hbs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{{#if (eq direction 'expand')}}
|
|
2
|
+
if (await {{> locator}}.getAttribute('aria-expanded') !== 'true') {
|
|
3
|
+
await {{> locator}}.click();
|
|
4
|
+
}
|
|
5
|
+
await expect({{> locator}}).toHaveAttribute('aria-expanded', 'true');
|
|
6
|
+
{{else}}
|
|
7
|
+
if (await {{> locator}}.getAttribute('aria-expanded') !== 'false') {
|
|
8
|
+
await {{> locator}}.click();
|
|
9
|
+
}
|
|
10
|
+
await expect({{> locator}}).toHaveAttribute('aria-expanded', 'false');
|
|
11
|
+
{{/if}}
|
package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
await {{> locator}}.fill('{{fillValue}}');
|
|
1
|
+
await {{> locator}}.fill('{{escapeQuotes fillValue}}');
|
package/src/generators/test-generator/adapters/playwright/templates/steps/actions/toggle-action.hbs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await {{> locator}}.click();
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
await page.waitForLoadState('networkidle');
|
|
2
1
|
const {{columnIndexVar}} = (await page.getByRole('columnheader').allTextContents()).findIndex(h => h.includes('{{columnName}}'));
|
|
3
2
|
await expect(page.getByRole('row').nth({{rowNth}}).getByRole('cell').nth({{columnIndexVar}})).toHaveText('{{dataValue}}');
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
await page.waitForLoadState('networkidle');
|
|
2
1
|
{{#if name}}
|
|
3
2
|
await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{#if (gte nth 0)}}.nth({{nth}}){{/if}}).toBeDisabled();
|
|
4
3
|
{{else}}
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
await page.waitForLoadState('networkidle');
|
|
2
1
|
await expect(page.getByText('{{escapeQuotes selectorRef}}').filter({ hasText: {{#if (eq selectorValue "")}}/.*{{escapeRegex dataValue}}$/{{else}}'{{escapeQuotes dataValue}}'{{/if}} }){{#if (gte nth 0)}}.nth({{nth}}){{/if}}).toBeDisabled();
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
await page.waitForLoadState('networkidle');
|
|
2
1
|
{{#if name}}
|
|
3
2
|
await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{#if (gte nth 0)}}.nth({{nth}}){{/if}}).toBeHidden();
|
|
4
3
|
{{else}}
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
await page.waitForLoadState('networkidle');
|
|
2
1
|
await expect(page.getByText('{{escapeQuotes selectorRef}}').filter({ hasText: {{#if (eq selectorValue "")}}/.*{{escapeRegex dataValue}}$/{{else}}'{{escapeQuotes dataValue}}'{{/if}} }){{#if (gte nth 0)}}.nth({{nth}}){{/if}}).toBeHidden();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await expect({{> locator}}).toHaveAttribute('aria-busy', 'true');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await expect({{> locator}}).toHaveAttribute('aria-selected', 'true');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await expect({{> locator}}).toHaveAttribute('aria-sort', '{{sortDirection}}');
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
await page.waitForLoadState('networkidle');
|
|
2
1
|
{{#if name}}
|
|
3
2
|
{{#if dataValue}}
|
|
4
3
|
await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{#if (gte nth 0)}}.nth({{nth}}){{/if}}).toBeVisible();
|
package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
await page.goto('{{#if baseURL}}{{baseURL}}{{/if}}{{path}}', { waitUntil: '
|
|
1
|
+
await page.goto('{{#if baseURL}}{{baseURL}}{{/if}}{{path}}', { waitUntil: 'load' });
|
|
@@ -8,12 +8,37 @@
|
|
|
8
8
|
{{/if}}
|
|
9
9
|
|
|
10
10
|
test.describe('{{featureName}}', () => {
|
|
11
|
+
{{#if singleAuthRole}}
|
|
12
|
+
test.use({ storageState: 'specs/.auth/{{singleAuthRole}}.json' });
|
|
13
|
+
|
|
14
|
+
{{/if}}
|
|
11
15
|
{{#if background}}
|
|
12
16
|
{{background}}
|
|
13
17
|
|
|
14
18
|
{{/if}}
|
|
19
|
+
{{#if authGroups}}
|
|
20
|
+
{{#each authGroups}}
|
|
21
|
+
{{#if authRole}}
|
|
22
|
+
test.describe('{{authRole}}', () => {
|
|
23
|
+
test.use({ storageState: 'specs/.auth/{{authRole}}.json' });
|
|
24
|
+
|
|
25
|
+
{{#each scenarios}}
|
|
26
|
+
{{indent this 2}}
|
|
27
|
+
|
|
28
|
+
{{/each}}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
{{else}}
|
|
32
|
+
{{#each scenarios}}
|
|
33
|
+
{{this}}
|
|
34
|
+
|
|
35
|
+
{{/each}}
|
|
36
|
+
{{/if}}
|
|
37
|
+
{{/each}}
|
|
38
|
+
{{else}}
|
|
15
39
|
{{#each scenarios}}
|
|
16
40
|
{{this}}
|
|
17
41
|
|
|
18
42
|
{{/each}}
|
|
43
|
+
{{/if}}
|
|
19
44
|
});
|
|
@@ -229,7 +229,8 @@ export class CodeGenerator {
|
|
|
229
229
|
|
|
230
230
|
// Generate all scenarios with feature tags for inheritance
|
|
231
231
|
// Skip scenarios tagged with @manual
|
|
232
|
-
|
|
232
|
+
// Track auth role per scenario for grouping
|
|
233
|
+
const renderedScenarios: Array<{ code: string; authRole?: string }> = [];
|
|
233
234
|
for (const scenario of feature.scenarios) {
|
|
234
235
|
if (isManual(scenario.tags)) {
|
|
235
236
|
if (this.options.verbose) {
|
|
@@ -237,22 +238,63 @@ export class CodeGenerator {
|
|
|
237
238
|
}
|
|
238
239
|
continue;
|
|
239
240
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
)
|
|
241
|
+
|
|
242
|
+
// Resolve auth tags for @extend scenarios (same logic as generateScenario)
|
|
243
|
+
let authFeatureTags = feature.tags || [];
|
|
244
|
+
if (scenario.extendsName) {
|
|
245
|
+
const baseScenario = this.stepsRegistry.get(scenario.extendsName);
|
|
246
|
+
if (baseScenario) {
|
|
247
|
+
authFeatureTags = [...baseScenario.tags, ...authFeatureTags];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const authRole = getEffectiveAuthRole(scenario.tags, authFeatureTags);
|
|
251
|
+
|
|
252
|
+
const code = await this.generateScenario(
|
|
253
|
+
scenario,
|
|
254
|
+
!!feature.background,
|
|
255
|
+
feature.tags || []
|
|
246
256
|
);
|
|
257
|
+
renderedScenarios.push({ code, authRole });
|
|
247
258
|
}
|
|
248
259
|
|
|
260
|
+
// Group scenarios by auth role for nested test.describe blocks
|
|
261
|
+
// This ensures test.use({ storageState }) only applies to its group
|
|
262
|
+
const authGroupMap = new Map<string, string[]>();
|
|
263
|
+
const groupOrder: string[] = [];
|
|
264
|
+
for (const { code, authRole } of renderedScenarios) {
|
|
265
|
+
const key = authRole || '';
|
|
266
|
+
if (!authGroupMap.has(key)) {
|
|
267
|
+
authGroupMap.set(key, []);
|
|
268
|
+
groupOrder.push(key);
|
|
269
|
+
}
|
|
270
|
+
authGroupMap.get(key)!.push(code);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const authGroups = groupOrder.map(key => ({
|
|
274
|
+
authRole: key || undefined,
|
|
275
|
+
scenarios: authGroupMap.get(key)!,
|
|
276
|
+
}));
|
|
277
|
+
|
|
278
|
+
// Determine rendering strategy:
|
|
279
|
+
// - Single group: flat structure (test.use at describe level if auth)
|
|
280
|
+
// - Multiple groups: nested describes per auth role
|
|
281
|
+
const needsGrouping = authGroups.length > 1;
|
|
282
|
+
const scenarios = renderedScenarios.map(s => s.code);
|
|
283
|
+
|
|
284
|
+
// For single group, extract the auth role to put test.use at describe level
|
|
285
|
+
const singleAuthRole = !needsGrouping && authGroups.length === 1
|
|
286
|
+
? authGroups[0].authRole
|
|
287
|
+
: undefined;
|
|
288
|
+
|
|
249
289
|
// Use adapter to render the complete test file structure
|
|
250
290
|
return this.adapter.renderTestFile({
|
|
251
291
|
imports: '', // Not used in template as it's rendered separately
|
|
252
292
|
featureName: feature.name,
|
|
253
293
|
featureDescription: feature.description,
|
|
254
294
|
background,
|
|
255
|
-
scenarios,
|
|
295
|
+
scenarios: needsGrouping ? [] : scenarios,
|
|
296
|
+
authGroups: needsGrouping ? authGroups : undefined,
|
|
297
|
+
singleAuthRole,
|
|
256
298
|
});
|
|
257
299
|
}
|
|
258
300
|
|