@sun-asterisk/sungen 2.1.1 → 2.2.1
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/playwright/templates/imports.hbs +1 -1
- 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/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 +0 -1
- 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 +0 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/sorted-assertion.hbs +0 -1
- 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/code-generator.d.ts +4 -0
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +19 -0
- package/dist/generators/test-generator/code-generator.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/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 +8 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +68 -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 +29 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +2 -2
- 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/github-skill-sungen-error-mapping.md +56 -0
- package/{src/orchestrator/templates/ai-instructions/copilot-skill-gherkin-syntax.md → dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md} +5 -5
- 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 +257 -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/dist/orchestrator/templates/specs-base.d.ts +4 -0
- package/dist/orchestrator/templates/specs-base.d.ts.map +1 -0
- package/dist/orchestrator/templates/specs-base.js +70 -0
- package/dist/orchestrator/templates/specs-base.js.map +1 -0
- package/dist/orchestrator/templates/specs-base.ts +73 -0
- package/package.json +1 -1
- package/src/cli/index.ts +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +1 -1
- 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/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 +0 -1
- 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 +0 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/sorted-assertion.hbs +0 -1
- 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/code-generator.ts +21 -0
- package/src/generators/test-generator/patterns/navigation-patterns.ts +2 -2
- package/src/generators/test-generator/utils/selector-resolver.ts +27 -204
- package/src/orchestrator/project-initializer.ts +84 -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 +29 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +2 -2
- 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/github-skill-sungen-error-mapping.md +56 -0
- package/{dist/orchestrator/templates/ai-instructions/copilot-skill-gherkin-syntax.md → src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md} +5 -5
- 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 +257 -0
- package/src/orchestrator/templates/playwright.config.ts +3 -1
- package/src/orchestrator/templates/readme.md +78 -101
- package/src/orchestrator/templates/specs-base.ts +73 -0
- package/dist/orchestrator/templates/ai-instructions/copilot-skill-error-mapping.md +0 -27
- package/src/orchestrator/templates/ai-instructions/copilot-skill-error-mapping.md +0 -27
|
@@ -179,6 +179,24 @@ export class CodeGenerator {
|
|
|
179
179
|
fs.writeFileSync(generatedTest.filePath, generatedTest.code, 'utf-8');
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Ensure specs/base.ts exists in the output directory
|
|
184
|
+
*/
|
|
185
|
+
ensureBaseFile(outputDir: string): void {
|
|
186
|
+
const basePath = path.join(outputDir, 'base.ts');
|
|
187
|
+
if (fs.existsSync(basePath)) return;
|
|
188
|
+
|
|
189
|
+
const templatePath = path.join(__dirname, '..', '..', '..', 'orchestrator', 'templates', 'specs-base.ts');
|
|
190
|
+
if (fs.existsSync(templatePath)) {
|
|
191
|
+
const baseDir = path.dirname(basePath);
|
|
192
|
+
if (!fs.existsSync(baseDir)) {
|
|
193
|
+
fs.mkdirSync(baseDir, { recursive: true });
|
|
194
|
+
}
|
|
195
|
+
fs.copyFileSync(templatePath, basePath);
|
|
196
|
+
console.log('✓ Created: specs/base.ts');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
182
200
|
private async generateTestCode(feature: ParsedFeature): Promise<string> {
|
|
183
201
|
// Extract feature name from source file if available, otherwise derive from feature name
|
|
184
202
|
let featureName: string;
|
|
@@ -415,6 +433,9 @@ export class CodeGenerator {
|
|
|
415
433
|
const { GherkinParser } = require('../gherkin-parser');
|
|
416
434
|
const parser = new GherkinParser();
|
|
417
435
|
|
|
436
|
+
// Ensure specs/base.ts exists
|
|
437
|
+
this.ensureBaseFile(outputDir);
|
|
438
|
+
|
|
418
439
|
// Parse all features first
|
|
419
440
|
const features: ParsedFeature[] = [];
|
|
420
441
|
const parseErrors: Array<{ file: string; error: any }> = [];
|
|
@@ -9,7 +9,7 @@ export const navigationPatterns: StepPattern[] = [
|
|
|
9
9
|
{
|
|
10
10
|
name: 'open-page-type',
|
|
11
11
|
matcher: (step: ParsedStep) =>
|
|
12
|
-
(step.text.includes('open') || step.text.includes('opens') || step.text.includes('is on')) &&
|
|
12
|
+
(step.text.includes('open') || step.text.includes('opens') || step.text.includes('is on') || step.text.includes('navigate to')) &&
|
|
13
13
|
step.elementType === 'page',
|
|
14
14
|
resolver: (step, context) => {
|
|
15
15
|
let path = context.featurePath || '/';
|
|
@@ -64,7 +64,7 @@ export const navigationPatterns: StepPattern[] = [
|
|
|
64
64
|
{
|
|
65
65
|
name: 'navigate-to-route',
|
|
66
66
|
matcher: (step: ParsedStep) =>
|
|
67
|
-
(step.text.includes('navigates to') || step.text.includes('is on')) &&
|
|
67
|
+
(step.text.includes('navigate to') || step.text.includes('navigates to') || step.text.includes('is on')) &&
|
|
68
68
|
!!(step.selectorRef || step.dataRef),
|
|
69
69
|
resolver: (step, context) => {
|
|
70
70
|
const route = step.selectorRef || step.dataRef;
|
|
@@ -3,191 +3,6 @@ import * as path from 'path';
|
|
|
3
3
|
import yaml from 'yaml';
|
|
4
4
|
import { readYaml, readYamlIfExists } from '../../../utils/yaml-io';
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* Remove Vietnamese diacritics (accents) from text
|
|
8
|
-
* Example: "Thời gian" → "Thoi gian", "Địa điểm" → "Dia diem"
|
|
9
|
-
*/
|
|
10
|
-
function removeVietnameseTones(text: string): string {
|
|
11
|
-
const vietnameseTones: { [key: string]: string } = {
|
|
12
|
-
'à': 'a', 'á': 'a', 'ả': 'a', 'ã': 'a', 'ạ': 'a',
|
|
13
|
-
'ă': 'a', 'ằ': 'a', 'ắ': 'a', 'ẳ': 'a', 'ẵ': 'a', 'ặ': 'a',
|
|
14
|
-
'â': 'a', 'ầ': 'a', 'ấ': 'a', 'ẩ': 'a', 'ẫ': 'a', 'ậ': 'a',
|
|
15
|
-
'đ': 'd',
|
|
16
|
-
'è': 'e', 'é': 'e', 'ẻ': 'e', 'ẽ': 'e', 'ẹ': 'e',
|
|
17
|
-
'ê': 'e', 'ề': 'e', 'ế': 'e', 'ể': 'e', 'ễ': 'e', 'ệ': 'e',
|
|
18
|
-
'ì': 'i', 'í': 'i', 'ỉ': 'i', 'ĩ': 'i', 'ị': 'i',
|
|
19
|
-
'ò': 'o', 'ó': 'o', 'ỏ': 'o', 'õ': 'o', 'ọ': 'o',
|
|
20
|
-
'ô': 'o', 'ồ': 'o', 'ố': 'o', 'ổ': 'o', 'ỗ': 'o', 'ộ': 'o',
|
|
21
|
-
'ơ': 'o', 'ờ': 'o', 'ớ': 'o', 'ở': 'o', 'ỡ': 'o', 'ợ': 'o',
|
|
22
|
-
'ù': 'u', 'ú': 'u', 'ủ': 'u', 'ũ': 'u', 'ụ': 'u',
|
|
23
|
-
'ư': 'u', 'ừ': 'u', 'ứ': 'u', 'ử': 'u', 'ữ': 'u', 'ự': 'u',
|
|
24
|
-
'ỳ': 'y', 'ý': 'y', 'ỷ': 'y', 'ỹ': 'y', 'ỵ': 'y',
|
|
25
|
-
// Uppercase
|
|
26
|
-
'À': 'A', 'Á': 'A', 'Ả': 'A', 'Ã': 'A', 'Ạ': 'A',
|
|
27
|
-
'Ă': 'A', 'Ằ': 'A', 'Ắ': 'A', 'Ẳ': 'A', 'Ẵ': 'A', 'Ặ': 'A',
|
|
28
|
-
'Â': 'A', 'Ầ': 'A', 'Ấ': 'A', 'Ẩ': 'A', 'Ẫ': 'A', 'Ậ': 'A',
|
|
29
|
-
'Đ': 'D',
|
|
30
|
-
'È': 'E', 'É': 'E', 'Ẻ': 'E', 'Ẽ': 'E', 'Ẹ': 'E',
|
|
31
|
-
'Ê': 'E', 'Ề': 'E', 'Ế': 'E', 'Ể': 'E', 'Ễ': 'E', 'Ệ': 'E',
|
|
32
|
-
'Ì': 'I', 'Í': 'I', 'Ỉ': 'I', 'Ĩ': 'I', 'Ị': 'I',
|
|
33
|
-
'Ò': 'O', 'Ó': 'O', 'Ỏ': 'O', 'Õ': 'O', 'Ọ': 'O',
|
|
34
|
-
'Ô': 'O', 'Ồ': 'O', 'Ố': 'O', 'Ổ': 'O', 'Ỗ': 'O', 'Ộ': 'O',
|
|
35
|
-
'Ơ': 'O', 'Ờ': 'O', 'Ớ': 'O', 'Ở': 'O', 'Ỡ': 'O', 'Ợ': 'O',
|
|
36
|
-
'Ù': 'U', 'Ú': 'U', 'Ủ': 'U', 'Ũ': 'U', 'Ụ': 'U',
|
|
37
|
-
'Ư': 'U', 'Ừ': 'U', 'Ứ': 'U', 'Ử': 'U', 'Ữ': 'U', 'Ự': 'U',
|
|
38
|
-
'Ỳ': 'Y', 'Ý': 'Y', 'Ỷ': 'Y', 'Ỹ': 'Y', 'Ỵ': 'Y',
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
return text.split('').map(char => vietnameseTones[char] || char).join('');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Convert Japanese Hiragana and Katakana to Romaji
|
|
46
|
-
* Example: "ひらがな" → "hiragana", "カタカナ" → "katakana"
|
|
47
|
-
*/
|
|
48
|
-
function convertJapaneseToRomaji(text: string): string {
|
|
49
|
-
// Hiragana to Romaji mapping
|
|
50
|
-
const hiraganaMap: { [key: string]: string } = {
|
|
51
|
-
// Basic vowels
|
|
52
|
-
'あ': 'a', 'い': 'i', 'う': 'u', 'え': 'e', 'お': 'o',
|
|
53
|
-
// K-row
|
|
54
|
-
'か': 'ka', 'き': 'ki', 'く': 'ku', 'け': 'ke', 'こ': 'ko',
|
|
55
|
-
'きゃ': 'kya', 'きゅ': 'kyu', 'きょ': 'kyo',
|
|
56
|
-
// G-row
|
|
57
|
-
'が': 'ga', 'ぎ': 'gi', 'ぐ': 'gu', 'げ': 'ge', 'ご': 'go',
|
|
58
|
-
'ぎゃ': 'gya', 'ぎゅ': 'gyu', 'ぎょ': 'gyo',
|
|
59
|
-
// S-row
|
|
60
|
-
'さ': 'sa', 'し': 'shi', 'す': 'su', 'せ': 'se', 'そ': 'so',
|
|
61
|
-
'しゃ': 'sha', 'しゅ': 'shu', 'しょ': 'sho',
|
|
62
|
-
// Z-row
|
|
63
|
-
'ざ': 'za', 'じ': 'ji', 'ず': 'zu', 'ぜ': 'ze', 'ぞ': 'zo',
|
|
64
|
-
'じゃ': 'ja', 'じゅ': 'ju', 'じょ': 'jo',
|
|
65
|
-
// T-row
|
|
66
|
-
'た': 'ta', 'ち': 'chi', 'つ': 'tsu', 'て': 'te', 'と': 'to',
|
|
67
|
-
'ちゃ': 'cha', 'ちゅ': 'chu', 'ちょ': 'cho',
|
|
68
|
-
// D-row
|
|
69
|
-
'だ': 'da', 'ぢ': 'ji', 'づ': 'zu', 'で': 'de', 'ど': 'do',
|
|
70
|
-
// N-row
|
|
71
|
-
'な': 'na', 'に': 'ni', 'ぬ': 'nu', 'ね': 'ne', 'の': 'no',
|
|
72
|
-
'にゃ': 'nya', 'にゅ': 'nyu', 'にょ': 'nyo',
|
|
73
|
-
// H-row
|
|
74
|
-
'は': 'ha', 'ひ': 'hi', 'ふ': 'fu', 'へ': 'he', 'ほ': 'ho',
|
|
75
|
-
'ひゃ': 'hya', 'ひゅ': 'hyu', 'ひょ': 'hyo',
|
|
76
|
-
// B-row
|
|
77
|
-
'ば': 'ba', 'び': 'bi', 'ぶ': 'bu', 'べ': 'be', 'ぼ': 'bo',
|
|
78
|
-
'びゃ': 'bya', 'びゅ': 'byu', 'びょ': 'byo',
|
|
79
|
-
// P-row
|
|
80
|
-
'ぱ': 'pa', 'ぴ': 'pi', 'ぷ': 'pu', 'ぺ': 'pe', 'ぽ': 'po',
|
|
81
|
-
'ぴゃ': 'pya', 'ぴゅ': 'pyu', 'ぴょ': 'pyo',
|
|
82
|
-
// M-row
|
|
83
|
-
'ま': 'ma', 'み': 'mi', 'む': 'mu', 'め': 'me', 'も': 'mo',
|
|
84
|
-
'みゃ': 'mya', 'みゅ': 'myu', 'みょ': 'myo',
|
|
85
|
-
// Y-row
|
|
86
|
-
'や': 'ya', 'ゆ': 'yu', 'よ': 'yo',
|
|
87
|
-
// R-row
|
|
88
|
-
'ら': 'ra', 'り': 'ri', 'る': 'ru', 'れ': 're', 'ろ': 'ro',
|
|
89
|
-
'りゃ': 'rya', 'りゅ': 'ryu', 'りょ': 'ryo',
|
|
90
|
-
// W-row
|
|
91
|
-
'わ': 'wa', 'ゐ': 'wi', 'ゑ': 'we', 'を': 'wo', 'ん': 'n',
|
|
92
|
-
// Small tsu (doubles next consonant)
|
|
93
|
-
'っ': '',
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// Katakana to Romaji mapping
|
|
97
|
-
const katakanaMap: { [key: string]: string } = {
|
|
98
|
-
// Basic vowels
|
|
99
|
-
'ア': 'a', 'イ': 'i', 'ウ': 'u', 'エ': 'e', 'オ': 'o',
|
|
100
|
-
// K-row
|
|
101
|
-
'カ': 'ka', 'キ': 'ki', 'ク': 'ku', 'ケ': 'ke', 'コ': 'ko',
|
|
102
|
-
'キャ': 'kya', 'キュ': 'kyu', 'キョ': 'kyo',
|
|
103
|
-
// G-row
|
|
104
|
-
'ガ': 'ga', 'ギ': 'gi', 'グ': 'gu', 'ゲ': 'ge', 'ゴ': 'go',
|
|
105
|
-
'ギャ': 'gya', 'ギュ': 'gyu', 'ギョ': 'gyo',
|
|
106
|
-
// S-row
|
|
107
|
-
'サ': 'sa', 'シ': 'shi', 'ス': 'su', 'セ': 'se', 'ソ': 'so',
|
|
108
|
-
'シャ': 'sha', 'シュ': 'shu', 'ショ': 'sho',
|
|
109
|
-
// Z-row
|
|
110
|
-
'ザ': 'za', 'ジ': 'ji', 'ズ': 'zu', 'ゼ': 'ze', 'ゾ': 'zo',
|
|
111
|
-
'ジャ': 'ja', 'ジュ': 'ju', 'ジョ': 'jo',
|
|
112
|
-
// T-row
|
|
113
|
-
'タ': 'ta', 'チ': 'chi', 'ツ': 'tsu', 'テ': 'te', 'ト': 'to',
|
|
114
|
-
'チャ': 'cha', 'チュ': 'chu', 'チョ': 'cho',
|
|
115
|
-
// D-row
|
|
116
|
-
'ダ': 'da', 'ヂ': 'ji', 'ヅ': 'zu', 'デ': 'de', 'ド': 'do',
|
|
117
|
-
// N-row
|
|
118
|
-
'ナ': 'na', 'ニ': 'ni', 'ヌ': 'nu', 'ネ': 'ne', 'ノ': 'no',
|
|
119
|
-
'ニャ': 'nya', 'ニュ': 'nyu', 'ニョ': 'nyo',
|
|
120
|
-
// H-row
|
|
121
|
-
'ハ': 'ha', 'ヒ': 'hi', 'フ': 'fu', 'ヘ': 'he', 'ホ': 'ho',
|
|
122
|
-
'ヒャ': 'hya', 'ヒュ': 'hyu', 'ヒョ': 'hyo',
|
|
123
|
-
// B-row
|
|
124
|
-
'バ': 'ba', 'ビ': 'bi', 'ブ': 'bu', 'ベ': 'be', 'ボ': 'bo',
|
|
125
|
-
'ビャ': 'bya', 'ビュ': 'byu', 'ビョ': 'byo',
|
|
126
|
-
// P-row
|
|
127
|
-
'パ': 'pa', 'ピ': 'pi', 'プ': 'pu', 'ペ': 'pe', 'ポ': 'po',
|
|
128
|
-
'ピャ': 'pya', 'ピュ': 'pyu', 'ピョ': 'pyo',
|
|
129
|
-
// M-row
|
|
130
|
-
'マ': 'ma', 'ミ': 'mi', 'ム': 'mu', 'メ': 'me', 'モ': 'mo',
|
|
131
|
-
'ミャ': 'mya', 'ミュ': 'myu', 'ミョ': 'myo',
|
|
132
|
-
// Y-row
|
|
133
|
-
'ヤ': 'ya', 'ユ': 'yu', 'ヨ': 'yo',
|
|
134
|
-
// R-row
|
|
135
|
-
'ラ': 'ra', 'リ': 'ri', 'ル': 'ru', 'レ': 're', 'ロ': 'ro',
|
|
136
|
-
'リャ': 'rya', 'リュ': 'ryu', 'リョ': 'ryo',
|
|
137
|
-
// W-row
|
|
138
|
-
'ワ': 'wa', 'ヰ': 'wi', 'ヱ': 'we', 'ヲ': 'wo', 'ン': 'n',
|
|
139
|
-
// Small tsu (doubles next consonant)
|
|
140
|
-
'ッ': '',
|
|
141
|
-
// Extended katakana for foreign words
|
|
142
|
-
'ヴ': 'vu',
|
|
143
|
-
'ファ': 'fa', 'フィ': 'fi', 'フェ': 'fe', 'フォ': 'fo',
|
|
144
|
-
'ウィ': 'wi', 'ウェ': 'we', 'ウォ': 'wo',
|
|
145
|
-
'ティ': 'ti', 'ディ': 'di',
|
|
146
|
-
'トゥ': 'tu', 'ドゥ': 'du',
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const allMaps = { ...hiraganaMap, ...katakanaMap };
|
|
150
|
-
|
|
151
|
-
let result = '';
|
|
152
|
-
let i = 0;
|
|
153
|
-
|
|
154
|
-
while (i < text.length) {
|
|
155
|
-
// Try to match 2-character combinations first (like きゃ, シャ)
|
|
156
|
-
if (i < text.length - 1) {
|
|
157
|
-
const twoChar = text.substring(i, i + 2);
|
|
158
|
-
if (allMaps[twoChar]) {
|
|
159
|
-
result += allMaps[twoChar];
|
|
160
|
-
i += 2;
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Then try single character
|
|
166
|
-
const oneChar = text[i];
|
|
167
|
-
if (allMaps[oneChar]) {
|
|
168
|
-
// Handle small tsu (っ/ッ) - double next consonant
|
|
169
|
-
if (oneChar === 'っ' || oneChar === 'ッ') {
|
|
170
|
-
if (i < text.length - 1) {
|
|
171
|
-
const nextChar = text[i + 1];
|
|
172
|
-
const nextRomaji = allMaps[nextChar];
|
|
173
|
-
if (nextRomaji && nextRomaji.length > 0) {
|
|
174
|
-
result += nextRomaji[0]; // Add first letter of next romaji
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
} else {
|
|
178
|
-
result += allMaps[oneChar];
|
|
179
|
-
}
|
|
180
|
-
i++;
|
|
181
|
-
} else {
|
|
182
|
-
// Not Japanese character, keep as is
|
|
183
|
-
result += oneChar;
|
|
184
|
-
i++;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return result;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
6
|
import { SelectorType } from '../../../utils/selector-types';
|
|
192
7
|
|
|
193
8
|
// Structured selector format v2
|
|
@@ -288,23 +103,31 @@ export class SelectorResolver {
|
|
|
288
103
|
}
|
|
289
104
|
|
|
290
105
|
/**
|
|
291
|
-
* Generate selector key from natural language label
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
* "
|
|
295
|
-
* "
|
|
296
|
-
* "
|
|
297
|
-
* "
|
|
298
|
-
* "
|
|
106
|
+
* Generate selector key from natural language label using Unicode NFC normalization.
|
|
107
|
+
* Preserves all Unicode characters (Vietnamese, Japanese, etc.) as-is.
|
|
108
|
+
*
|
|
109
|
+
* "Email Address" → "email address"
|
|
110
|
+
* "Submit Button" → "submit button"
|
|
111
|
+
* "User's Profile" → "user's profile"
|
|
112
|
+
* "Giới thiệu" → "giới thiệu"
|
|
113
|
+
* "Thời gian" → "thời gian"
|
|
114
|
+
* "ログイン" → "ログイン"
|
|
115
|
+
* "書類一覧" → "書類一覧"
|
|
299
116
|
*/
|
|
300
117
|
static generateKey(label: string): string {
|
|
301
|
-
return
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
118
|
+
return label.normalize('NFC').toLowerCase().trim().replace(/\s+/g, ' ');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Normalize all keys in a SelectorFile through generateKey()
|
|
123
|
+
* so that YAML keys are case-insensitive and Unicode-normalized on lookup.
|
|
124
|
+
*/
|
|
125
|
+
private static normalizeKeys(file: SelectorFile): SelectorFile {
|
|
126
|
+
const normalized: SelectorFile = {};
|
|
127
|
+
for (const [key, value] of Object.entries(file)) {
|
|
128
|
+
normalized[SelectorResolver.generateKey(key)] = value;
|
|
129
|
+
}
|
|
130
|
+
return normalized;
|
|
308
131
|
}
|
|
309
132
|
|
|
310
133
|
/**
|
|
@@ -742,7 +565,7 @@ export class SelectorResolver {
|
|
|
742
565
|
if (basePath) {
|
|
743
566
|
const baseSelectors = readYamlIfExists<SelectorFile>(basePath);
|
|
744
567
|
if (baseSelectors) {
|
|
745
|
-
Object.assign(selectors, baseSelectors);
|
|
568
|
+
Object.assign(selectors, SelectorResolver.normalizeKeys(baseSelectors));
|
|
746
569
|
}
|
|
747
570
|
}
|
|
748
571
|
|
|
@@ -751,7 +574,7 @@ export class SelectorResolver {
|
|
|
751
574
|
if (baseOverridePath) {
|
|
752
575
|
const overrideSelectors = readYamlIfExists<SelectorFile>(baseOverridePath);
|
|
753
576
|
if (overrideSelectors) {
|
|
754
|
-
Object.assign(selectors, overrideSelectors);
|
|
577
|
+
Object.assign(selectors, SelectorResolver.normalizeKeys(overrideSelectors));
|
|
755
578
|
}
|
|
756
579
|
}
|
|
757
580
|
}
|
|
@@ -769,7 +592,7 @@ export class SelectorResolver {
|
|
|
769
592
|
} else {
|
|
770
593
|
const featureSelectors = readYamlIfExists<SelectorFile>(featurePath);
|
|
771
594
|
if (featureSelectors) {
|
|
772
|
-
Object.assign(selectors, featureSelectors);
|
|
595
|
+
Object.assign(selectors, SelectorResolver.normalizeKeys(featureSelectors));
|
|
773
596
|
}
|
|
774
597
|
}
|
|
775
598
|
|
|
@@ -778,7 +601,7 @@ export class SelectorResolver {
|
|
|
778
601
|
if (featureOverridePath) {
|
|
779
602
|
const overrideSelectors = readYamlIfExists<SelectorFile>(featureOverridePath);
|
|
780
603
|
if (overrideSelectors) {
|
|
781
|
-
Object.assign(selectors, overrideSelectors);
|
|
604
|
+
Object.assign(selectors, SelectorResolver.normalizeKeys(overrideSelectors));
|
|
782
605
|
}
|
|
783
606
|
}
|
|
784
607
|
|
|
@@ -44,6 +44,9 @@ export class ProjectInitializer {
|
|
|
44
44
|
// Create playwright config if doesn't exist
|
|
45
45
|
this.createPlaywrightConfig();
|
|
46
46
|
|
|
47
|
+
// Create specs/base.ts for shared context
|
|
48
|
+
this.createSpecsBase();
|
|
49
|
+
|
|
47
50
|
// Create/update .gitignore
|
|
48
51
|
this.updateGitignore();
|
|
49
52
|
|
|
@@ -53,6 +56,9 @@ export class ProjectInitializer {
|
|
|
53
56
|
// Create AI rules files
|
|
54
57
|
this.createAIRules();
|
|
55
58
|
|
|
59
|
+
// Create VS Code settings for Copilot auto-attach
|
|
60
|
+
this.createVSCodeSettings();
|
|
61
|
+
|
|
56
62
|
// Display summary
|
|
57
63
|
this.displaySummary(normalizedProjectName);
|
|
58
64
|
}
|
|
@@ -159,6 +165,54 @@ export class ProjectInitializer {
|
|
|
159
165
|
this.createdItems.push('README.md');
|
|
160
166
|
}
|
|
161
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Create VS Code settings for Copilot auto-attach
|
|
170
|
+
*/
|
|
171
|
+
private createVSCodeSettings(): void {
|
|
172
|
+
const settingsPath = path.join(this.cwd, '.vscode', 'settings.json');
|
|
173
|
+
|
|
174
|
+
if (fs.existsSync(settingsPath)) {
|
|
175
|
+
this.skippedItems.push('.vscode/settings.json');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const settingsDir = path.dirname(settingsPath);
|
|
180
|
+
if (!fs.existsSync(settingsDir)) {
|
|
181
|
+
fs.mkdirSync(settingsDir, { recursive: true });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const settings = {
|
|
185
|
+
'github.copilot.chat.agent.runTasks': true,
|
|
186
|
+
'chat.tools.terminal.autoApprove': {
|
|
187
|
+
sungen: true,
|
|
188
|
+
'npx playwright': true,
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
193
|
+
this.createdItems.push('.vscode/settings.json');
|
|
194
|
+
|
|
195
|
+
// Create MCP config for Playwright browser tools
|
|
196
|
+
const mcpPath = path.join(this.cwd, '.vscode', 'mcp.json');
|
|
197
|
+
|
|
198
|
+
if (fs.existsSync(mcpPath)) {
|
|
199
|
+
this.skippedItems.push('.vscode/mcp.json');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const mcpConfig = {
|
|
204
|
+
servers: {
|
|
205
|
+
playwright: {
|
|
206
|
+
command: 'npx',
|
|
207
|
+
args: ['@playwright/mcp@latest'],
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2) + '\n', 'utf-8');
|
|
213
|
+
this.createdItems.push('.vscode/mcp.json');
|
|
214
|
+
}
|
|
215
|
+
|
|
162
216
|
/**
|
|
163
217
|
* Display initialization summary
|
|
164
218
|
*/
|
|
@@ -216,11 +270,15 @@ export class ProjectInitializer {
|
|
|
216
270
|
['claude-skill-gherkin-syntax.md', '.claude/skills/sungen-gherkin-syntax/SKILL.md'],
|
|
217
271
|
['claude-skill-selector-keys.md', '.claude/skills/sungen-selector-keys/SKILL.md'],
|
|
218
272
|
['claude-skill-error-mapping.md', '.claude/skills/sungen-error-mapping/SKILL.md'],
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
['
|
|
273
|
+
['claude-skill-tc-generation.md', '.claude/skills/sungen-tc-generation/SKILL.md'],
|
|
274
|
+
['claude-skill-selector-fix.md', '.claude/skills/sungen-selector-fix/SKILL.md'],
|
|
275
|
+
|
|
276
|
+
// Skills — GitHub Copilot (separate copies with Copilot-friendly descriptions)
|
|
277
|
+
['github-skill-sungen-gherkin-syntax.md', '.github/skills/sungen-gherkin-syntax/SKILL.md'],
|
|
278
|
+
['github-skill-sungen-selector-keys.md', '.github/skills/sungen-selector-keys/SKILL.md'],
|
|
279
|
+
['github-skill-sungen-error-mapping.md', '.github/skills/sungen-error-mapping/SKILL.md'],
|
|
280
|
+
['github-skill-sungen-tc-generation.md', '.github/skills/sungen-tc-generation/SKILL.md'],
|
|
281
|
+
['github-skill-sungen-selector-fix.md', '.github/skills/sungen-selector-fix/SKILL.md'],
|
|
224
282
|
];
|
|
225
283
|
|
|
226
284
|
for (const [templateFile, outputRelPath] of fileMapping) {
|
|
@@ -244,6 +302,27 @@ export class ProjectInitializer {
|
|
|
244
302
|
|
|
245
303
|
}
|
|
246
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Create specs/base.ts for shared browser context
|
|
307
|
+
*/
|
|
308
|
+
private createSpecsBase(): void {
|
|
309
|
+
const basePath = path.join(this.cwd, 'specs', 'generated', 'base.ts');
|
|
310
|
+
|
|
311
|
+
if (fs.existsSync(basePath)) {
|
|
312
|
+
this.skippedItems.push('specs/generated/base.ts');
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const baseDir = path.dirname(basePath);
|
|
317
|
+
if (!fs.existsSync(baseDir)) {
|
|
318
|
+
fs.mkdirSync(baseDir, { recursive: true });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const content = this.readTemplate('specs-base.ts');
|
|
322
|
+
fs.writeFileSync(basePath, content, 'utf-8');
|
|
323
|
+
this.createdItems.push('specs/generated/base.ts');
|
|
324
|
+
}
|
|
325
|
+
|
|
247
326
|
/**
|
|
248
327
|
* Read a template file from the templates directory
|
|
249
328
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: add-screen
|
|
3
3
|
description: 'Add a new Sungen screen — scaffolds directories and delegates to make-tc for test case creation'
|
|
4
4
|
argument-hint: [screen-name] [url-path]
|
|
5
|
-
allowed-tools: Read, Grep, Bash, Glob
|
|
5
|
+
allowed-tools: Read, Grep, Bash, Glob, AskUserQuestion
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
You are adding a new Sungen screen for test generation.
|
|
@@ -37,6 +37,7 @@ If yes, delegate to `/sungen:make-tc <screen>` to handle:
|
|
|
37
37
|
### 3. Confirm
|
|
38
38
|
|
|
39
39
|
Tell the user what was created and next steps:
|
|
40
|
-
- If the page requires authentication
|
|
40
|
+
- If the page requires authentication, the user will be asked to log in via the MCP browser during `/sungen:make-tc`
|
|
41
41
|
- Edit the generated files as needed
|
|
42
|
-
- Run
|
|
42
|
+
- Run `/sungen:make-tc <screen>` to create test cases
|
|
43
|
+
- Run `/sungen:make-test <screen>` to generate selectors, compile, and run tests
|
|
@@ -1,60 +1,25 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: make-tc
|
|
3
|
-
description: 'Create test cases for a Sungen screen —
|
|
3
|
+
description: 'Create or update test cases for a Sungen screen — generates feature + test-data files (20+ scenarios per viewpoint)'
|
|
4
4
|
argument-hint: [screen-name]
|
|
5
|
-
allowed-tools: Read, Grep, Bash, Glob
|
|
5
|
+
allowed-tools: Read, Grep, Bash, Glob, AskUserQuestion
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## Role
|
|
9
9
|
|
|
10
|
-
You are a **Senior QA Engineer** specialized in test case design. You structure test cases by viewpoint categories and translate UI into Gherkin test cases following the `sungen-gherkin-syntax` and `sungen-
|
|
10
|
+
You are a **Senior QA Engineer** specialized in exhaustive test case design. You structure test cases by viewpoint categories and translate UI into comprehensive Gherkin test cases following the `sungen-gherkin-syntax` and `sungen-tc-generation` skills. Your focus is on **Gherkin scenarios and test data only** — selectors are handled later during `/sungen:make-test`.
|
|
11
11
|
|
|
12
12
|
## Parameters
|
|
13
13
|
|
|
14
|
-
Parse from `$ARGUMENTS
|
|
15
|
-
- **screen** — screen name (e.g., `login`, `dashboard`)
|
|
16
|
-
|
|
17
|
-
If missing, ask the user.
|
|
14
|
+
Parse **screen** from `$ARGUMENTS`. If missing, ask the user.
|
|
18
15
|
|
|
19
16
|
## Steps
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
Ask: "Do you have a **live page** or **static designs** (screenshots, Figma, images)?"
|
|
28
|
-
|
|
29
|
-
**Live page →** explore via browser (see CLAUDE.md for Playwright MCP rules). If auth needed, check `specs/.auth/<role>.json` exists — if not, read `baseURL` from `playwright.config.ts` and tell the user to manually run: `sungen makeauth <role> --url <baseURL>` (e.g., `sungen makeauth admin --url http://localhost:3000`). **Do NOT execute `sungen makeauth` yourself.** Wait for the user to confirm auth is ready before proceeding.
|
|
30
|
-
|
|
31
|
-
**Static designs →** ask user to provide screenshot paths, Figma exports, or UI descriptions. Note: selectors will be best-guess until live page is available.
|
|
32
|
-
|
|
33
|
-
Present discovered elements to user, then proceed.
|
|
34
|
-
|
|
35
|
-
### 3. Gather test viewpoints
|
|
36
|
-
|
|
37
|
-
| VP Category | Description |
|
|
38
|
-
|---|---|
|
|
39
|
-
| **UI/UX** | Default screen appearance, static elements, default states |
|
|
40
|
-
| **Validation** | Field/form validation, error messages |
|
|
41
|
-
| **Logic** | Business logic, happy paths, redirects |
|
|
42
|
-
| **Security** | Auth guards, permission checks |
|
|
43
|
-
|
|
44
|
-
For each viewpoint, gather: **UI Target**, **Test Viewpoint**, **Expected Result**.
|
|
45
|
-
|
|
46
|
-
User can provide all at once as a table:
|
|
47
|
-
```
|
|
48
|
-
| VP Category | UI Target | Test Viewpoint | Expected Result |
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### 4. Generate files
|
|
52
|
-
|
|
53
|
-
Generate the 3 files following `sungen-gherkin-syntax` and `sungen-selector-keys` skill rules:
|
|
54
|
-
- `qa/screens/<screen>/features/<screen>.feature` — one Scenario per viewpoint
|
|
55
|
-
- `qa/screens/<screen>/selectors/<screen>.yaml`
|
|
56
|
-
- `qa/screens/<screen>/test-data/<screen>.yaml`
|
|
57
|
-
|
|
58
|
-
### 5. Confirm
|
|
18
|
+
1. Verify `qa/screens/<screen>/` exists. If not → `/sungen:add-screen` first.
|
|
19
|
+
2. Check if `.feature` file already has scenarios. If yes → use `AskUserQuestion` to ask the update mode (see `sungen-tc-generation` skill for details). If no → fresh creation.
|
|
20
|
+
3. Use `AskUserQuestion` to ask: **Live page** (explore via Playwright MCP) or **Static designs** (screenshots, Figma)? Explore accordingly (see CLAUDE.md for MCP rules).
|
|
21
|
+
4. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format.
|
|
22
|
+
5. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills.
|
|
23
|
+
6. Show summary → next: `/sungen:make-test <screen>`
|
|
59
24
|
|
|
60
|
-
|
|
25
|
+
**No selectors.yaml** — selectors are generated during `/sungen:make-test`.
|
|
@@ -1,59 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: make-test
|
|
3
|
-
description: '
|
|
3
|
+
description: 'Generate selectors, compile, and run Playwright tests — auto-fixes selectors on failure'
|
|
4
4
|
argument-hint: [screen-name]
|
|
5
|
-
allowed-tools: Read, Grep, Bash, Glob, Edit
|
|
5
|
+
allowed-tools: Read, Grep, Bash, Glob, Edit, AskUserQuestion
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## Role
|
|
9
9
|
|
|
10
|
-
You are a **Senior Developer** specialized in Playwright test debugging. You diagnose test failures, fix selectors/test-data
|
|
10
|
+
You are a **Senior Developer** specialized in Playwright test debugging. You generate selectors from live pages, diagnose test failures, and fix selectors/test-data using the `sungen-selector-fix`, `sungen-selector-keys`, and `sungen-error-mapping` skills.
|
|
11
11
|
|
|
12
12
|
## Parameters
|
|
13
13
|
|
|
14
|
-
Parse from `$ARGUMENTS
|
|
15
|
-
- **screen** — screen name (e.g., `login`, `dashboard`)
|
|
16
|
-
|
|
17
|
-
If missing, ask the user.
|
|
14
|
+
Parse **screen** from `$ARGUMENTS`. If missing, ask the user.
|
|
18
15
|
|
|
19
16
|
## Steps
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
sungen generate --screen <screen>
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
If fails, fix source files per `sungen-error-mapping` skill and retry.
|
|
32
|
-
|
|
33
|
-
### 3. Run tests
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
npx playwright test specs/screens/<screen>/<screen>.spec.ts
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
If pass → Step 5. If fail → Step 4.
|
|
40
|
-
|
|
41
|
-
### 4. Fix and retry (max 5 attempts)
|
|
42
|
-
|
|
43
|
-
Per attempt:
|
|
44
|
-
1. Read Playwright error output
|
|
45
|
-
2. Map error to fix using `sungen-error-mapping` skill
|
|
46
|
-
3. Fix `selectors.yaml` or `test-data.yaml`
|
|
47
|
-
4. Recompile: `sungen generate --screen <screen>`
|
|
48
|
-
5. Retest
|
|
49
|
-
|
|
50
|
-
### 5. Fallback — AI fix .spec.ts
|
|
51
|
-
|
|
52
|
-
After 5 failed attempts, ask user:
|
|
53
|
-
> Tests still failing. Would you like me to directly fix the `.spec.ts` file?
|
|
54
|
-
|
|
55
|
-
If yes: read `.spec.ts`, fix locators/assertions, mark with `// AI-fixed: <reason>`
|
|
56
|
-
|
|
57
|
-
### 6. Confirm
|
|
58
|
-
|
|
59
|
-
Show: pass/fail, attempt count, files changed.
|
|
18
|
+
1. Verify `qa/screens/<screen>/` has `.feature` + `test-data.yaml`. If not → `/sungen:make-tc` first.
|
|
19
|
+
2. Generate `selectors.yaml` from live page using `sungen-selector-fix` and `sungen-selector-keys` skills.
|
|
20
|
+
3. Compile: `sungen generate --screen <screen>`
|
|
21
|
+
4. Run: `npx playwright test specs/screens/<screen>/<screen>.spec.ts`
|
|
22
|
+
5. If fail → fix selectors/test-data per `sungen-selector-fix` + `sungen-error-mapping` skills, retry (max 5).
|
|
23
|
+
6. After 5 fails → ask user about direct `.spec.ts` fix.
|
|
24
|
+
7. Show: pass/fail, attempt count, files changed.
|
|
@@ -12,7 +12,7 @@ Given a login page, here are the 3 files you generate:
|
|
|
12
12
|
|
|
13
13
|
**qa/screens/login/features/login.feature**
|
|
14
14
|
```gherkin
|
|
15
|
-
@auth
|
|
15
|
+
@no-auth
|
|
16
16
|
Feature: Login Screen
|
|
17
17
|
|
|
18
18
|
Scenario: Successful login
|
|
@@ -73,12 +73,14 @@ When exploring a page to generate test files:
|
|
|
73
73
|
Only use: `browser_navigate`, `browser_snapshot`, `browser_click`, `browser_fill_form`, `browser_evaluate`.
|
|
74
74
|
|
|
75
75
|
To browse authenticated pages via MCP:
|
|
76
|
-
1.
|
|
77
|
-
2. If
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
1. `browser_navigate` to `baseURL`
|
|
77
|
+
2. If redirected to login, ask the user to log in manually:
|
|
78
|
+
> "This page requires login. Please log in using the browser. Confirm when you're done."
|
|
79
|
+
3. Once confirmed, `browser_navigate` to the target page and take `browser_snapshot`
|
|
80
|
+
|
|
81
|
+
**Never use `sungen makeauth`.** Always let the user log in manually via the MCP browser.
|
|
82
|
+
**NEVER use `browser_evaluate` to inject cookies or localStorage.** It causes size limit issues and server 500 errors.
|
|
83
|
+
**Minimize navigations** — take one snapshot per page, do not navigate repeatedly.
|
|
82
84
|
|
|
83
85
|
## Commands
|
|
84
86
|
|
|
@@ -86,5 +88,4 @@ To browse authenticated pages via MCP:
|
|
|
86
88
|
sungen add --screen <name> --path <url-path> # Create screen
|
|
87
89
|
sungen generate --screen <name> # Compile to .spec.ts
|
|
88
90
|
sungen generate --all # Compile all
|
|
89
|
-
sungen makeauth <role> # Capture auth state
|
|
90
91
|
```
|