@sun-asterisk/sungen 2.4.1 → 2.4.3
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/add.js +2 -2
- package/dist/cli/commands/add.js.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +27 -4
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
- package/dist/orchestrator/ai-rules-updater.js +2 -0
- package/dist/orchestrator/ai-rules-updater.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 +20 -3
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/screen-manager.d.ts +8 -0
- package/dist/orchestrator/screen-manager.d.ts.map +1 -1
- package/dist/orchestrator/screen-manager.js +120 -0
- package/dist/orchestrator/screen-manager.js.map +1 -1
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +6 -14
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +10 -2
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-review.md +5 -0
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +21 -13
- package/dist/orchestrator/templates/ai-instructions/claude-config.md +4 -97
- package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +84 -122
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +87 -23
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +71 -30
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-review.md +19 -14
- package/dist/orchestrator/templates/ai-instructions/claude-skill-test-design-techniques.md +99 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-viewpoint.md +151 -64
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +5 -10
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +10 -3
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-review.md +5 -0
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +21 -13
- package/dist/orchestrator/templates/ai-instructions/copilot-config.md +4 -97
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +85 -123
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +87 -23
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +72 -25
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-review.md +19 -14
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-test-design-techniques.md +99 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint.md +151 -64
- package/dist/orchestrator/templates/readme.md +1 -1
- package/dist/orchestrator/templates/tsconfig.json +21 -0
- package/package.json +1 -1
- package/src/cli/commands/add.ts +2 -2
- package/src/cli/index.ts +1 -1
- package/src/generators/test-generator/code-generator.ts +29 -4
- package/src/orchestrator/ai-rules-updater.ts +2 -0
- package/src/orchestrator/project-initializer.ts +24 -3
- package/src/orchestrator/screen-manager.ts +124 -0
- package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +6 -14
- package/src/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +10 -2
- package/src/orchestrator/templates/ai-instructions/claude-cmd-review.md +5 -0
- package/src/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +21 -13
- package/src/orchestrator/templates/ai-instructions/claude-config.md +4 -97
- package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +84 -122
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +87 -23
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +71 -30
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-review.md +19 -14
- package/src/orchestrator/templates/ai-instructions/claude-skill-test-design-techniques.md +99 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint.md +151 -64
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +5 -10
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +10 -3
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-review.md +5 -0
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +21 -13
- package/src/orchestrator/templates/ai-instructions/copilot-config.md +4 -97
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +85 -123
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +87 -23
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +72 -25
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-review.md +19 -14
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-test-design-techniques.md +99 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint.md +151 -64
- package/src/orchestrator/templates/readme.md +1 -1
- package/src/orchestrator/templates/tsconfig.json +21 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"lib": ["ES2020"],
|
|
5
|
+
"module": "commonjs",
|
|
6
|
+
"moduleResolution": "node",
|
|
7
|
+
"types": ["node", "@playwright/test"],
|
|
8
|
+
"strict": false,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"outDir": "./dist",
|
|
14
|
+
"baseUrl": ".",
|
|
15
|
+
"paths": {
|
|
16
|
+
"@/*": ["./*"]
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"include": ["specs/**/*.ts", "playwright.config.ts"],
|
|
20
|
+
"exclude": ["node_modules"]
|
|
21
|
+
}
|
package/package.json
CHANGED
package/src/cli/commands/add.ts
CHANGED
|
@@ -3,9 +3,9 @@ import { Command } from 'commander';
|
|
|
3
3
|
export function registerAddCommand(program: Command): void {
|
|
4
4
|
program
|
|
5
5
|
.command('add')
|
|
6
|
-
.description('Add screen or feature definition with scaffolded files')
|
|
6
|
+
.description('Add screen or feature definition with scaffolded files. Auto-captures screenshots when --path is provided.')
|
|
7
7
|
.requiredOption('--screen <name>', 'Screen name')
|
|
8
|
-
.option('-p, --path <path>', 'Screen route path (e.g. /awards)')
|
|
8
|
+
.option('-p, --path <path>', 'Screen route path (e.g. /awards). Also triggers auto-screenshot capture.')
|
|
9
9
|
.option('-f, --feature <name>', 'Add additional feature file to existing screen')
|
|
10
10
|
.option('-d, --description <text>', 'Screen description')
|
|
11
11
|
.action(async (options) => {
|
package/src/cli/index.ts
CHANGED
|
@@ -4,6 +4,29 @@ import { ParsedFeature, ParsedScenario, ParsedStep } from '../gherkin-parser';
|
|
|
4
4
|
import { StepMapper } from './step-mapper';
|
|
5
5
|
import { TestGeneratorAdapter, adapterRegistry } from './adapters';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Filter base scenario steps for @extend: only keep Given→When steps.
|
|
9
|
+
* Skips Then and any And/But that follow Then (since And/But inherit
|
|
10
|
+
* from the preceding primary keyword).
|
|
11
|
+
*/
|
|
12
|
+
function filterBaseStepsForExtend(steps: ParsedStep[]): ParsedStep[] {
|
|
13
|
+
const result: ParsedStep[] = [];
|
|
14
|
+
let lastPrimaryKeyword = 'Given';
|
|
15
|
+
|
|
16
|
+
for (const step of steps) {
|
|
17
|
+
const kw = step.keyword.trim();
|
|
18
|
+
if (kw === 'Given' || kw === 'When' || kw === 'Then') {
|
|
19
|
+
lastPrimaryKeyword = kw;
|
|
20
|
+
}
|
|
21
|
+
// Skip Then and any And/But that follow Then
|
|
22
|
+
if (lastPrimaryKeyword === 'Then') {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
result.push(step);
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
|
|
7
30
|
/**
|
|
8
31
|
* Extract auth role from tags
|
|
9
32
|
* @auth:admin → 'admin'
|
|
@@ -345,8 +368,9 @@ export class CodeGenerator {
|
|
|
345
368
|
if (scenario.extendsName) {
|
|
346
369
|
const baseScenario = this.stepsRegistry.get(scenario.extendsName);
|
|
347
370
|
if (baseScenario) {
|
|
348
|
-
// Prepend base steps inline —
|
|
349
|
-
|
|
371
|
+
// Prepend base steps inline — only Given→When (skip Then per @extend contract)
|
|
372
|
+
const filteredBaseSteps = filterBaseStepsForExtend(baseScenario.steps);
|
|
373
|
+
stepsToMap = [...filteredBaseSteps, ...scenario.steps];
|
|
350
374
|
// Auth precedence: extending @auth > base @auth > feature @auth
|
|
351
375
|
authFeatureTags = [...baseScenario.tags, ...featureTags];
|
|
352
376
|
} else {
|
|
@@ -370,9 +394,10 @@ export class CodeGenerator {
|
|
|
370
394
|
if (scenario.extendsName && this.stepsRegistry.has(scenario.extendsName)) {
|
|
371
395
|
const baseScenario = this.stepsRegistry.get(scenario.extendsName)!;
|
|
372
396
|
|
|
373
|
-
// Section header: base steps
|
|
397
|
+
// Section header: base steps (only Given→When, skip Then per @extend contract)
|
|
398
|
+
const filteredSteps = filterBaseStepsForExtend(baseScenario.steps);
|
|
374
399
|
steps.push({ code: ` // [from @steps:${scenario.extendsName}]` });
|
|
375
|
-
for (const step of
|
|
400
|
+
for (const step of filteredSteps) {
|
|
376
401
|
const mapped = await Promise.resolve(this.stepMapper.mapStep(step));
|
|
377
402
|
steps.push({ comment: mapped.comment, code: this.indentCode(mapped.code, 4) });
|
|
378
403
|
}
|
|
@@ -29,6 +29,7 @@ export const AI_RULES_FILE_MAPPING: [string, string][] = [
|
|
|
29
29
|
['claude-skill-selector-keys.md', '.claude/skills/sungen-selector-keys/SKILL.md'],
|
|
30
30
|
['claude-skill-error-mapping.md', '.claude/skills/sungen-error-mapping/SKILL.md'],
|
|
31
31
|
['claude-skill-tc-generation.md', '.claude/skills/sungen-tc-generation/SKILL.md'],
|
|
32
|
+
['claude-skill-test-design-techniques.md', '.claude/skills/sungen-test-design-techniques/SKILL.md'],
|
|
32
33
|
['claude-skill-selector-fix.md', '.claude/skills/sungen-selector-fix/SKILL.md'],
|
|
33
34
|
['claude-skill-tc-review.md', '.claude/skills/sungen-tc-review/SKILL.md'],
|
|
34
35
|
['claude-skill-viewpoint.md', '.claude/skills/sungen-viewpoint/SKILL.md'],
|
|
@@ -38,6 +39,7 @@ export const AI_RULES_FILE_MAPPING: [string, string][] = [
|
|
|
38
39
|
['github-skill-sungen-selector-keys.md', '.github/skills/sungen-selector-keys/SKILL.md'],
|
|
39
40
|
['github-skill-sungen-error-mapping.md', '.github/skills/sungen-error-mapping/SKILL.md'],
|
|
40
41
|
['github-skill-sungen-tc-generation.md', '.github/skills/sungen-tc-generation/SKILL.md'],
|
|
42
|
+
['github-skill-sungen-test-design-techniques.md', '.github/skills/sungen-test-design-techniques/SKILL.md'],
|
|
41
43
|
['github-skill-sungen-selector-fix.md', '.github/skills/sungen-selector-fix/SKILL.md'],
|
|
42
44
|
['github-skill-sungen-tc-review.md', '.github/skills/sungen-tc-review/SKILL.md'],
|
|
43
45
|
['github-skill-sungen-viewpoint.md', '.github/skills/sungen-viewpoint/SKILL.md'],
|
|
@@ -44,6 +44,9 @@ export class ProjectInitializer {
|
|
|
44
44
|
// Create playwright config if doesn't exist
|
|
45
45
|
this.createPlaywrightConfig();
|
|
46
46
|
|
|
47
|
+
// Create tsconfig.json if doesn't exist
|
|
48
|
+
this.createTsConfig();
|
|
49
|
+
|
|
47
50
|
// Create specs/base.ts for shared context
|
|
48
51
|
this.createSpecsBase();
|
|
49
52
|
|
|
@@ -115,6 +118,22 @@ export class ProjectInitializer {
|
|
|
115
118
|
this.createdItems.push('playwright.config.ts');
|
|
116
119
|
}
|
|
117
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Create TypeScript configuration
|
|
123
|
+
*/
|
|
124
|
+
private createTsConfig(): void {
|
|
125
|
+
const configPath = path.join(this.cwd, 'tsconfig.json');
|
|
126
|
+
|
|
127
|
+
if (fs.existsSync(configPath)) {
|
|
128
|
+
this.skippedItems.push('tsconfig.json');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const content = this.readTemplate('tsconfig.json');
|
|
133
|
+
fs.writeFileSync(configPath, content, 'utf-8');
|
|
134
|
+
this.createdItems.push('tsconfig.json');
|
|
135
|
+
}
|
|
136
|
+
|
|
118
137
|
/**
|
|
119
138
|
* Update .gitignore with Sungen-specific entries
|
|
120
139
|
*/
|
|
@@ -273,6 +292,7 @@ export class ProjectInitializer {
|
|
|
273
292
|
['claude-skill-selector-keys.md', '.claude/skills/sungen-selector-keys/SKILL.md'],
|
|
274
293
|
['claude-skill-error-mapping.md', '.claude/skills/sungen-error-mapping/SKILL.md'],
|
|
275
294
|
['claude-skill-tc-generation.md', '.claude/skills/sungen-tc-generation/SKILL.md'],
|
|
295
|
+
['claude-skill-test-design-techniques.md', '.claude/skills/sungen-test-design-techniques/SKILL.md'],
|
|
276
296
|
['claude-skill-selector-fix.md', '.claude/skills/sungen-selector-fix/SKILL.md'],
|
|
277
297
|
['claude-skill-tc-review.md', '.claude/skills/sungen-tc-review/SKILL.md'],
|
|
278
298
|
['claude-skill-viewpoint.md', '.claude/skills/sungen-viewpoint/SKILL.md'],
|
|
@@ -282,6 +302,7 @@ export class ProjectInitializer {
|
|
|
282
302
|
['github-skill-sungen-selector-keys.md', '.github/skills/sungen-selector-keys/SKILL.md'],
|
|
283
303
|
['github-skill-sungen-error-mapping.md', '.github/skills/sungen-error-mapping/SKILL.md'],
|
|
284
304
|
['github-skill-sungen-tc-generation.md', '.github/skills/sungen-tc-generation/SKILL.md'],
|
|
305
|
+
['github-skill-sungen-test-design-techniques.md', '.github/skills/sungen-test-design-techniques/SKILL.md'],
|
|
285
306
|
['github-skill-sungen-selector-fix.md', '.github/skills/sungen-selector-fix/SKILL.md'],
|
|
286
307
|
['github-skill-sungen-tc-review.md', '.github/skills/sungen-tc-review/SKILL.md'],
|
|
287
308
|
['github-skill-sungen-viewpoint.md', '.github/skills/sungen-viewpoint/SKILL.md'],
|
|
@@ -363,9 +384,9 @@ export class ProjectInitializer {
|
|
|
363
384
|
// package.json just created, proceed with install
|
|
364
385
|
}
|
|
365
386
|
|
|
366
|
-
// Install Playwright
|
|
367
|
-
console.log('📦 Installing @playwright/test...\n');
|
|
368
|
-
execSync('npm install -D @playwright/test', execOpts);
|
|
387
|
+
// Install Playwright and TypeScript types
|
|
388
|
+
console.log('📦 Installing @playwright/test and @types/node...\n');
|
|
389
|
+
execSync('npm install -D @playwright/test @types/node', execOpts);
|
|
369
390
|
|
|
370
391
|
console.log('\n🎭 Installing Playwright browsers...\n');
|
|
371
392
|
execSync('npx playwright install', execOpts);
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import * as path from 'path';
|
|
8
|
+
import { chromium } from '@playwright/test';
|
|
8
9
|
|
|
9
10
|
export interface ScreenOptions {
|
|
10
11
|
name: string;
|
|
@@ -120,6 +121,76 @@ export class ScreenManager {
|
|
|
120
121
|
fs.writeFileSync(specPath, this.generateSpecTemplate(options, screenName), 'utf-8');
|
|
121
122
|
}
|
|
122
123
|
|
|
124
|
+
// Generate requirements test-viewpoint.md (only on first screen creation)
|
|
125
|
+
const viewpointPath = path.join(requirementsDir, 'test-viewpoint.md');
|
|
126
|
+
if (!fs.existsSync(viewpointPath)) {
|
|
127
|
+
fs.writeFileSync(viewpointPath, [
|
|
128
|
+
`# ${options.name} — Test Viewpoints`,
|
|
129
|
+
'',
|
|
130
|
+
'## Edge Cases',
|
|
131
|
+
'',
|
|
132
|
+
'<!-- Sample — replace with actual edge cases for this screen:',
|
|
133
|
+
'- Paste emoji into text fields (e.g., 🎉👋) — should accept or sanitize?',
|
|
134
|
+
'- Input 10,000+ characters into free text — truncate or reject?',
|
|
135
|
+
'- Double-click Submit rapidly — should only create 1 record',
|
|
136
|
+
'- Browser back button after successful submit — should not re-submit',
|
|
137
|
+
'-->',
|
|
138
|
+
'',
|
|
139
|
+
'## Known Issues',
|
|
140
|
+
'',
|
|
141
|
+
'<!-- Sample — replace with actual known issues:',
|
|
142
|
+
'- [BUG-001] Date picker does not work on Safari < 16',
|
|
143
|
+
'- [BUG-002] File upload silently fails for files > 20MB (no error shown)',
|
|
144
|
+
'- Search does not handle Vietnamese accents correctly ("Nguyên" ≠ "Nguyen")',
|
|
145
|
+
'-->',
|
|
146
|
+
'',
|
|
147
|
+
'## Design Decisions',
|
|
148
|
+
'',
|
|
149
|
+
'<!-- Sample — replace with actual design decisions:',
|
|
150
|
+
'- Show all validation errors at once (not one at a time) — per UX review',
|
|
151
|
+
'- Cancel button resets form completely (no draft saving)',
|
|
152
|
+
'- Anonymous mode hides sender name but keeps department visible',
|
|
153
|
+
'-->',
|
|
154
|
+
'',
|
|
155
|
+
'## UI Patterns Identified',
|
|
156
|
+
'',
|
|
157
|
+
'<!-- Check applicable patterns for this screen (from sungen-viewpoint skill):',
|
|
158
|
+
'',
|
|
159
|
+
'| # | Pattern | Applicable? | Notes |',
|
|
160
|
+
'|---|---------|-------------|-------|',
|
|
161
|
+
'| 1 | Form & Inputs | ☐ | |',
|
|
162
|
+
'| 2 | Data Table | ☐ | |',
|
|
163
|
+
'| 3 | Create / Add | ☐ | |',
|
|
164
|
+
'| 4 | Update / Edit | ☐ | |',
|
|
165
|
+
'| 5 | Delete | ☐ | |',
|
|
166
|
+
'| 6 | Search | ☐ | |',
|
|
167
|
+
'| 7 | Filter | ☐ | |',
|
|
168
|
+
'| 8 | Pagination | ☐ | |',
|
|
169
|
+
'| 9 | Modal / Dialog | ☐ | |',
|
|
170
|
+
'| 10 | List / Card | ☐ | |',
|
|
171
|
+
'-->',
|
|
172
|
+
'',
|
|
173
|
+
'## Priority Viewpoints',
|
|
174
|
+
'',
|
|
175
|
+
'<!-- Rate importance for this screen (High / Medium / Low / Skip):',
|
|
176
|
+
'',
|
|
177
|
+
'| VP | Priority | Reason |',
|
|
178
|
+
'|---|----------|--------|',
|
|
179
|
+
'| VP-UI | High | Complex form with many states |',
|
|
180
|
+
'| VP-VAL | High | 14 validation rules from spec |',
|
|
181
|
+
'| VP-LOGIC | Medium | Standard CRUD, no complex business rules |',
|
|
182
|
+
'| VP-SEC | Low | Internal tool, basic auth only |',
|
|
183
|
+
'-->',
|
|
184
|
+
'',
|
|
185
|
+
].join('\n'), 'utf-8');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Auto-screenshot: capture page if --path is provided
|
|
189
|
+
// Use filename (not screenName) so each feature gets its own screenshots
|
|
190
|
+
if (options.path) {
|
|
191
|
+
await this.captureScreenshot(options.path, requirementsUiDir, filename);
|
|
192
|
+
}
|
|
193
|
+
|
|
123
194
|
// Display success
|
|
124
195
|
console.log(`Created files:`);
|
|
125
196
|
console.log(` ${path.relative(this.cwd, featurePath)}`);
|
|
@@ -127,6 +198,7 @@ export class ScreenManager {
|
|
|
127
198
|
console.log(` ${path.relative(this.cwd, testDataPath)}`);
|
|
128
199
|
if (isFirstFile) {
|
|
129
200
|
console.log(` ${path.relative(this.cwd, specPath)}`);
|
|
201
|
+
console.log(` ${path.relative(this.cwd, viewpointPath)}`);
|
|
130
202
|
console.log(` ${path.relative(this.cwd, requirementsUiDir)}/`);
|
|
131
203
|
}
|
|
132
204
|
console.log('');
|
|
@@ -139,6 +211,58 @@ export class ScreenManager {
|
|
|
139
211
|
console.log(` 4. Run: npx playwright test\n`);
|
|
140
212
|
}
|
|
141
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Capture full-page screenshot of the screen URL
|
|
216
|
+
*/
|
|
217
|
+
private async captureScreenshot(pagePath: string, uiDir: string, screenName: string): Promise<void> {
|
|
218
|
+
const baseURL = this.getBaseURL();
|
|
219
|
+
if (!baseURL) {
|
|
220
|
+
console.log(' ⊘ Screenshot skipped: no baseURL found in playwright.config.ts');
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const url = `${baseURL.replace(/\/+$/, '')}${pagePath}`;
|
|
225
|
+
console.log(`\n📸 Capturing screenshot: ${url}`);
|
|
226
|
+
|
|
227
|
+
let browser;
|
|
228
|
+
try {
|
|
229
|
+
browser = await chromium.launch();
|
|
230
|
+
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
|
|
231
|
+
await page.goto(url, { waitUntil: 'networkidle', timeout: 15000 });
|
|
232
|
+
|
|
233
|
+
// Full-page screenshot
|
|
234
|
+
const screenshotPath = path.join(uiDir, `${screenName}-full.png`);
|
|
235
|
+
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
236
|
+
console.log(` ✓ Saved: ${path.relative(this.cwd, screenshotPath)}`);
|
|
237
|
+
|
|
238
|
+
// Viewport screenshot (above the fold)
|
|
239
|
+
const viewportPath = path.join(uiDir, `${screenName}-viewport.png`);
|
|
240
|
+
await page.screenshot({ path: viewportPath, fullPage: false });
|
|
241
|
+
console.log(` ✓ Saved: ${path.relative(this.cwd, viewportPath)}`);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
244
|
+
if (msg.includes('net::ERR') || msg.includes('Timeout')) {
|
|
245
|
+
console.log(` ⊘ Screenshot skipped: page not reachable (${msg.split('\n')[0]})`);
|
|
246
|
+
} else {
|
|
247
|
+
console.log(` ⊘ Screenshot skipped: ${msg.split('\n')[0]}`);
|
|
248
|
+
}
|
|
249
|
+
} finally {
|
|
250
|
+
if (browser) await browser.close();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Read baseURL from playwright.config.ts
|
|
256
|
+
*/
|
|
257
|
+
private getBaseURL(): string | null {
|
|
258
|
+
const configPath = path.join(this.cwd, 'playwright.config.ts');
|
|
259
|
+
if (!fs.existsSync(configPath)) return null;
|
|
260
|
+
|
|
261
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
262
|
+
const match = content.match(/baseURL:\s*['"]([^'"]+)['"]/);
|
|
263
|
+
return match ? match[1] : null;
|
|
264
|
+
}
|
|
265
|
+
|
|
142
266
|
private validateScreenName(name: string): void {
|
|
143
267
|
if (!name || name.trim().length === 0) {
|
|
144
268
|
console.error('Error: Screen name cannot be empty');
|
|
@@ -33,20 +33,12 @@ Ask the user: "Would you like to fill in `requirements/spec.md` now? This helps
|
|
|
33
33
|
- If they have UI designs (screenshots, Figma exports, mockups) → suggest copying them to `requirements/ui/`.
|
|
34
34
|
- If no → proceed to step 3.
|
|
35
35
|
|
|
36
|
-
### 3.
|
|
36
|
+
### 3. Next steps
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
Tell the user what was created, then use `AskUserQuestion` to offer next steps:
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
- **`/sungen:create-test <screen>`** — Create test cases from requirements/designs (Recommended)
|
|
41
|
+
- **Fill `requirements/spec.md`** — Write screen specs first for better test quality
|
|
42
|
+
- **Done for now** — I'll come back later
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
Skill: create-test
|
|
44
|
-
Args: <screen>
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### 4. Confirm
|
|
48
|
-
|
|
49
|
-
If the user declined test case creation, tell them next steps:
|
|
50
|
-
- Fill `requirements/spec.md` with screen specs (if not done)
|
|
51
|
-
- Run `/sungen:create-test <screen>` to create test cases
|
|
52
|
-
- Run `/sungen:run-test <screen>` to generate selectors, compile, and run tests
|
|
44
|
+
If user picks `/sungen:create-test`, **you MUST use the Skill tool** to invoke it. Do NOT generate test cases yourself — the skill auto-loads `sungen-gherkin-syntax` and `sungen-tc-generation`.
|
|
@@ -20,14 +20,22 @@ Parse **screen** from `$ARGUMENTS`. If missing, ask the user.
|
|
|
20
20
|
3. **Read requirements** — check `qa/screens/<screen>/requirements/`:
|
|
21
21
|
- If `spec.md` exists → read it as PRIMARY source (sections, fields, validation rules, business rules, states).
|
|
22
22
|
- If `ui/` has images → read them for visual context (layout, element positions, states).
|
|
23
|
-
- If `
|
|
23
|
+
- If `test-viewpoint.md` exists → read it. If it only contains HTML comments (scaffold template), use `AskUserQuestion` to ask:
|
|
24
|
+
- **Fill test-viewpoint.md first** — I'll help you identify edge cases, known issues, and design decisions for this screen before generating tests
|
|
25
|
+
- **Continue without it** — generate tests from spec and other sources only
|
|
24
26
|
- Summarize what you found in requirements and present to the user.
|
|
25
27
|
4. **Screen input** (supplements requirements, or is primary source if no requirements):
|
|
26
28
|
- Use `AskUserQuestion` to ask: **Figma design** (provide Figma URL — recommended), **UI images** (screenshots/mockups in `requirements/ui/`), or **Live page scan** (optional, via Playwright MCP)?
|
|
27
29
|
- Recommend Figma or UI images first. Live page scan is optional — useful to verify specs vs actual UI or capture real data.
|
|
30
|
+
- If live page scan: `browser_navigate` → ONE `browser_snapshot`. If auth redirect → ask user to log in manually. Never use `browser_run_code` or `browser_evaluate` to inject cookies.
|
|
28
31
|
- If exploring, verify and supplement requirements — flag any discrepancies found.
|
|
29
32
|
5. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format. When requirements exist, use the "Requirements-Driven Generation" strategy.
|
|
30
33
|
6. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills.
|
|
31
|
-
7. Show summary
|
|
34
|
+
7. Show summary, then use `AskUserQuestion` to offer next steps:
|
|
35
|
+
|
|
36
|
+
- **`/sungen:review <screen>`** — Review syntax, coverage, viewpoint quality (Recommended)
|
|
37
|
+
- **`/sungen:run-test <screen>`** — Skip review, generate selectors and run tests now
|
|
38
|
+
- **`/sungen:create-test <screen>`** — Add more test cases for another section/viewpoint
|
|
39
|
+
- **Done for now** — I'll come back later
|
|
32
40
|
|
|
33
41
|
**No selectors.yaml** — selectors are generated during `/sungen:run-test`.
|
|
@@ -19,3 +19,8 @@ Parse **screen** from `$ARGUMENTS`. If missing, ask the user.
|
|
|
19
19
|
2. Follow the `sungen-tc-review` skill — score 3 dimensions: Syntax (30pts), Coverage (40pts), Viewpoint (30pts). Use `sungen-viewpoint` for pattern checklists.
|
|
20
20
|
3. Output review report per `sungen-tc-review` format. **>= 60%**: PASS. **< 60%**: FAIL with recommendations.
|
|
21
21
|
4. If FAIL and user confirms → update test cases following `sungen-gherkin-syntax` and `sungen-tc-generation` skills, then re-review.
|
|
22
|
+
5. After PASS (or user decides to proceed), use `AskUserQuestion` to offer next steps:
|
|
23
|
+
|
|
24
|
+
- **`/sungen:run-test <screen>`** — Generate selectors, compile, and run tests (Recommended)
|
|
25
|
+
- **`/sungen:create-test <screen>`** — Add more test cases before running
|
|
26
|
+
- **Done for now** — I'll come back later
|
|
@@ -13,20 +13,28 @@ You are a **Senior Developer**. Use `sungen-selector-fix`, `sungen-selector-keys
|
|
|
13
13
|
|
|
14
14
|
Parse **screen** from `$ARGUMENTS`. If missing, ask the user.
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## Compile
|
|
17
17
|
|
|
18
18
|
1. Verify `qa/screens/<screen>/` has `.feature` + `test-data.yaml`
|
|
19
19
|
2. Ensure `selectors.yaml` has page selector. If missing, ask user for URL path
|
|
20
20
|
3. `sungen generate --screen <screen>`
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
6.
|
|
27
|
-
7.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
|
|
22
|
+
## Run & Fix (phased — per `sungen-selector-fix` skill)
|
|
23
|
+
|
|
24
|
+
4. **Phase 1 — Smoke Check**: Run first 5 `@critical`/`@high` scenarios only. If failures → diagnose, fix fundamentals (page selector, auth, base @steps), re-run. Max 2 attempts. If still broken → ask user.
|
|
25
|
+
5. **Phase 2 — Priority Wave**: Run all `@critical` + `@high` scenarios. Fix only failures from this wave. Max 2 attempts. Shared selectors fixed here cascade to later phases.
|
|
26
|
+
6. **Phase 3 — Full Run**: Run all tests. Fix only **new** failures (elements unique to `@normal`/`@low`). Max 1 attempt. Don't loop on low-priority failures.
|
|
27
|
+
7. **Phase 4 — Regression**: One final full run. Report results. No more fix loops.
|
|
28
|
+
|
|
29
|
+
## Next steps
|
|
30
|
+
|
|
31
|
+
After showing results, use `AskUserQuestion` to offer next steps:
|
|
32
|
+
|
|
33
|
+
If all tests **passed**:
|
|
34
|
+
- **`/sungen:create-test <screen>`** — Add more test cases (Recommended)
|
|
35
|
+
- **Done** — All tests passed, I'm finished
|
|
36
|
+
|
|
37
|
+
If tests **failed** (after retries):
|
|
38
|
+
- **`/sungen:run-test <screen>`** — Re-run after manual fixes
|
|
39
|
+
- **`/sungen:create-test <screen>`** — Revise test cases
|
|
40
|
+
- **Done for now** — I'll fix manually later
|
|
@@ -10,8 +10,9 @@ You generate 3 files for sungen — a Gherkin compiler that produces Playwright
|
|
|
10
10
|
| `sungen-gherkin-syntax` | All 70+ step patterns, selector types, YAML key rules, tags, element types |
|
|
11
11
|
| `sungen-error-mapping` | Playwright & sungen error → fix mapping |
|
|
12
12
|
| `sungen-tc-generation` | Test case generation strategy, output format |
|
|
13
|
+
| `sungen-test-design-techniques` | EP, BVA, Decision Table, State Transition — systematic scenario generation |
|
|
13
14
|
| `sungen-tc-review` | Review scoring, quality rules, checklist |
|
|
14
|
-
| `sungen-viewpoint` |
|
|
15
|
+
| `sungen-viewpoint` | 10 UI patterns x 4 viewpoints — coverage checklists |
|
|
15
16
|
| `sungen-selector-keys` | YAML key generation from `[Reference]` names, suffixes, lookup priority |
|
|
16
17
|
| `sungen-selector-fix` | Selector generation from live page, auto-fix strategy |
|
|
17
18
|
|
|
@@ -26,6 +27,8 @@ You generate 3 files for sungen — a Gherkin compiler that produces Playwright
|
|
|
26
27
|
|
|
27
28
|
**Order:** add-screen → create-test → review → run-test.
|
|
28
29
|
|
|
30
|
+
After each command completes, use `AskUserQuestion` to present the next actions as selectable options. Never just print text — always give clickable choices so the user can continue the workflow seamlessly.
|
|
31
|
+
|
|
29
32
|
## File Structure
|
|
30
33
|
|
|
31
34
|
```
|
|
@@ -38,102 +41,6 @@ qa/screens/<screen-name>/
|
|
|
38
41
|
└── ui/ # Screenshots, mockups
|
|
39
42
|
```
|
|
40
43
|
|
|
41
|
-
## Complete Example
|
|
42
|
-
|
|
43
|
-
Given a login page, here are the 3 files you generate:
|
|
44
|
-
|
|
45
|
-
**qa/screens/login/features/login.feature**
|
|
46
|
-
```gherkin
|
|
47
|
-
@no-auth
|
|
48
|
-
Feature: Login Screen
|
|
49
|
-
|
|
50
|
-
Scenario: VP-LOGIC-001 Successful login
|
|
51
|
-
Given User is on [login] page
|
|
52
|
-
When User fill [Email] field with {{email}}
|
|
53
|
-
And User fill [Password] field with {{password}}
|
|
54
|
-
And User click [Submit] button
|
|
55
|
-
Then User see [Welcome] heading with {{welcome_text}}
|
|
56
|
-
And User see [Dashboard] link
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
**qa/screens/login/selectors/login.yaml**
|
|
60
|
-
```yaml
|
|
61
|
-
login:
|
|
62
|
-
type: 'page'
|
|
63
|
-
value: '/login'
|
|
64
|
-
|
|
65
|
-
email:
|
|
66
|
-
type: 'placeholder'
|
|
67
|
-
value: 'Enter your email'
|
|
68
|
-
|
|
69
|
-
password:
|
|
70
|
-
type: 'placeholder'
|
|
71
|
-
value: 'Enter your password'
|
|
72
|
-
|
|
73
|
-
submit:
|
|
74
|
-
type: 'role'
|
|
75
|
-
value: 'button'
|
|
76
|
-
name: 'Submit'
|
|
77
|
-
|
|
78
|
-
welcome:
|
|
79
|
-
type: 'role'
|
|
80
|
-
value: 'heading'
|
|
81
|
-
name: 'Welcome'
|
|
82
|
-
|
|
83
|
-
dashboard:
|
|
84
|
-
type: 'role'
|
|
85
|
-
value: 'link'
|
|
86
|
-
name: 'Dashboard'
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
**qa/screens/login/test-data/login.yaml**
|
|
90
|
-
```yaml
|
|
91
|
-
email: 'admin@example.com'
|
|
92
|
-
password: 'password123'
|
|
93
|
-
welcome_text: 'Welcome back'
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Critical Gherkin Rules (quick reference)
|
|
97
|
-
|
|
98
|
-
1. **NEVER write `is visible`** — `User see [T] type` already asserts visibility. Only use `is hidden` to assert absence.
|
|
99
|
-
2. **Use `@no-auth` for pre-login pages** (login, register, forgot-password). Use `@auth:role` for authenticated pages.
|
|
100
|
-
3. **Scenario names use VP prefix** — `VP-UI-001`, `VP-VAL-001`, `VP-LOGIC-001`, `VP-SEC-001`.
|
|
101
|
-
4. **Values use `{{snake_case}}`** — never hardcode data in `.feature`. All values go in `test-data.yaml`.
|
|
102
|
-
5. **Selectors are deferred** — `selectors.yaml` is generated during `/sungen-run-test` from the live page, NOT during `/sungen-create-test`.
|
|
103
|
-
6. **Every `{{variable}}` must exist in `test-data.yaml`** — missing variables cause compile failures.
|
|
104
|
-
7. **Assertion type is determined by element type** — input types (`field`, `textarea`, `search`, `dropdown`, `slider`, `date-picker`) → `toHaveValue()`. Everything else → `toHaveText()`. Wrong type = wrong assertion = test failure.
|
|
105
|
-
|
|
106
|
-
## Using Playwright MCP to explore pages
|
|
107
|
-
|
|
108
|
-
When exploring a page to generate test files:
|
|
109
|
-
1. Read `playwright.config.ts` for `baseURL`
|
|
110
|
-
2. Use `browser_navigate` to open `baseURL + /path`
|
|
111
|
-
3. Use `browser_snapshot` to see all elements
|
|
112
|
-
4. Generate the 3 files from the snapshot
|
|
113
|
-
|
|
114
|
-
### Allowed MCP tools
|
|
115
|
-
|
|
116
|
-
| Tool | Use for |
|
|
117
|
-
|---|---|
|
|
118
|
-
| `browser_navigate` | Open pages |
|
|
119
|
-
| `browser_snapshot` | Capture all accessible elements |
|
|
120
|
-
| `browser_click` | Interact with elements (open dialogs, dropdowns) |
|
|
121
|
-
| `browser_fill_form` | Fill form fields |
|
|
122
|
-
| `browser_evaluate` | **Read-only DOM queries only** (e.g., detect `data-testid` attributes) |
|
|
123
|
-
|
|
124
|
-
**NEVER use `browser_run_code`** — fails with `require is not defined`.
|
|
125
|
-
|
|
126
|
-
### Authentication via MCP
|
|
127
|
-
|
|
128
|
-
1. `browser_navigate` to `baseURL`
|
|
129
|
-
2. If redirected to login, ask the user to log in manually:
|
|
130
|
-
> "This page requires login. Please log in using the browser. Confirm when you're done."
|
|
131
|
-
3. Once confirmed, `browser_navigate` to the target page and take `browser_snapshot`
|
|
132
|
-
|
|
133
|
-
**Never use `sungen makeauth`.** Always let the user log in manually via the MCP browser.
|
|
134
|
-
**NEVER use `browser_evaluate` to inject cookies or localStorage** — causes size limit issues and server 500 errors. Use `browser_evaluate` ONLY for read-only DOM queries.
|
|
135
|
-
**Minimize navigations** — take one snapshot per page, do not navigate repeatedly.
|
|
136
|
-
|
|
137
44
|
## CLI Commands
|
|
138
45
|
|
|
139
46
|
```bash
|