@sun-asterisk/sungen 2.3.1 → 2.4.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 +10 -11
- package/dist/cli/index.js +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-column-exists.hbs +5 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +7 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +5 -1
- package/dist/generators/test-generator/patterns/table-patterns.d.ts +9 -0
- package/dist/generators/test-generator/patterns/table-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/table-patterns.js +94 -98
- package/dist/generators/test-generator/patterns/table-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts +2 -0
- package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +13 -0
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +80 -0
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
- package/dist/orchestrator/ai-rules-updater.js +8 -6
- package/dist/orchestrator/ai-rules-updater.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +33 -2
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/screen-manager.js +1 -1
- package/dist/orchestrator/screen-manager.js.map +1 -1
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +5 -5
- package/{src/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md → dist/orchestrator/templates/ai-instructions/claude-cmd-create-test.md} +7 -7
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-review.md +21 -0
- package/{src/orchestrator/templates/ai-instructions/claude-cmd-make-test.md → dist/orchestrator/templates/ai-instructions/claude-cmd-run-test.md} +2 -2
- package/dist/orchestrator/templates/ai-instructions/claude-config.md +65 -12
- package/dist/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +128 -52
- package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +9 -40
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +67 -239
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +40 -208
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-review.md +104 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-viewpoint.md +186 -0
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +4 -4
- package/dist/orchestrator/templates/ai-instructions/{copilot-cmd-make-tc.md → copilot-cmd-create-test.md} +8 -8
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-review.md +24 -0
- package/{src/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md → dist/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md} +3 -3
- package/dist/orchestrator/templates/ai-instructions/copilot-config.md +66 -13
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +128 -52
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +9 -40
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +67 -214
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +40 -208
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-review.md +104 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint.md +186 -0
- package/dist/orchestrator/templates/readme.md +4 -4
- package/package.json +1 -1
- package/src/cli/index.ts +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-column-exists.hbs +5 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +7 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +5 -1
- package/src/generators/test-generator/patterns/table-patterns.ts +98 -111
- package/src/generators/test-generator/patterns/types.ts +2 -0
- package/src/generators/test-generator/step-mapper.ts +87 -1
- package/src/orchestrator/ai-rules-updater.ts +8 -6
- package/src/orchestrator/project-initializer.ts +38 -2
- package/src/orchestrator/screen-manager.ts +1 -1
- package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +5 -5
- package/{dist/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md → src/orchestrator/templates/ai-instructions/claude-cmd-create-test.md} +7 -7
- package/src/orchestrator/templates/ai-instructions/claude-cmd-review.md +21 -0
- package/{dist/orchestrator/templates/ai-instructions/claude-cmd-make-test.md → src/orchestrator/templates/ai-instructions/claude-cmd-run-test.md} +2 -2
- package/src/orchestrator/templates/ai-instructions/claude-config.md +65 -12
- package/src/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +128 -52
- package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +9 -40
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +67 -239
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +40 -208
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-review.md +104 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint.md +186 -0
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +4 -4
- package/src/orchestrator/templates/ai-instructions/{copilot-cmd-make-tc.md → copilot-cmd-create-test.md} +8 -8
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-review.md +24 -0
- package/{dist/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md → src/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md} +3 -3
- package/src/orchestrator/templates/ai-instructions/copilot-config.md +66 -13
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +128 -52
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +9 -40
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +67 -214
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +40 -208
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-review.md +104 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint.md +186 -0
- package/src/orchestrator/templates/readme.md +4 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-filter.hbs +0 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-index.hbs +0 -2
- package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-review.md +0 -228
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-review.md +0 -228
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-filter.hbs +0 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-index.hbs +0 -2
- package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-review.md +0 -228
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-review.md +0 -228
package/README.md
CHANGED
|
@@ -218,13 +218,12 @@ Feature: Login Screen
|
|
|
218
218
|
| Pattern | Example |
|
|
219
219
|
|---|---|
|
|
220
220
|
| Column exists | `User see [Email] column in [Users] table` |
|
|
221
|
-
| Row exists | `User see [Users] table
|
|
222
|
-
|
|
|
223
|
-
| Row count | `User see [Users] table with {{count}}
|
|
224
|
-
| Cell by filter | `User see [Users] table row with {{name}} has [Status] with {{status}}` |
|
|
225
|
-
| Cell by index | `User see [Users] table row 1 [Name] cell with {{name}}` |
|
|
226
|
-
| Action in row | `User click [Edit] in [Users] table row with {{name}}` |
|
|
221
|
+
| Row exists | `User see [Username] row in [Users] table with {{name}}` |
|
|
222
|
+
| Row hidden | `User see [Username] row in [Users] table with {{name}} is hidden` |
|
|
223
|
+
| Row count | `User see [Users] table with {{count}}` |
|
|
227
224
|
| Empty table | `User see [Users] table is empty` |
|
|
225
|
+
| Cell value | `User see [Status] column with {{status}}` (row scoped) |
|
|
226
|
+
| Action in row | `User click [Edit] button in [Users] table with {{name}}` |
|
|
228
227
|
|
|
229
228
|
### Scope
|
|
230
229
|
|
|
@@ -336,11 +335,11 @@ specs/generated/<name>/
|
|
|
336
335
|
|
|
337
336
|
Full documentation at **[sungen.sun-asterisk.vn](https://sungen.sun-asterisk.vn)**
|
|
338
337
|
|
|
339
|
-
- **[Installation Guide](https://
|
|
340
|
-
- **[Quick Start](https://
|
|
341
|
-
- **[Selector YAML Guide](https://
|
|
342
|
-
- **[Tips & Troubleshooting](https://
|
|
343
|
-
- **[Gherkin
|
|
338
|
+
- **[Installation Guide](https://sun-asterisk.github.io/sungen/docs/guides/installation)** — Windows (Git Bash + VS Code + Copilot), macOS, Linux
|
|
339
|
+
- **[Quick Start](https://sun-asterisk.github.io/sungen/docs/guides/quick-start)** — From zero to running tests
|
|
340
|
+
- **[Selector YAML Guide](https://sun-asterisk.github.io/sungen/docs/guides/selector-override)** — NFC keys, auto-infer, Japanese/Vietnamese support
|
|
341
|
+
- **[Tips & Troubleshooting](https://sun-asterisk.github.io/sungen/docs/guides/tips)** — When AI gets stuck fixing tests
|
|
342
|
+
- **[Gherkin Patterns](https://sun-asterisk.github.io/sungen/docs/gherkin/patterns)** — Full grammar, all patterns, compiler rules
|
|
344
343
|
|
|
345
344
|
---
|
|
346
345
|
|
package/dist/cli/index.js
CHANGED
|
@@ -16,7 +16,7 @@ async function main() {
|
|
|
16
16
|
program
|
|
17
17
|
.name('sungen')
|
|
18
18
|
.description('Deterministic E2E Test Compiler — Gherkin + Selectors → Playwright')
|
|
19
|
-
.version('2.
|
|
19
|
+
.version('2.4.0');
|
|
20
20
|
// Global options
|
|
21
21
|
program
|
|
22
22
|
.option('-v, --verbose', 'Enable verbose logging');
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
{ const tableRow = {{> locator}}.getByRole('row').filter({ hasText: '{{escapeQuotes filterValue}}' });
|
|
2
|
-
await tableRow.getByRole('
|
|
2
|
+
await tableRow.getByRole('{{elementRole}}', { name: '{{escapeQuotes elementName}}' }).{{action}}(); }
|
|
@@ -1 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
{{~#if isGiven}}
|
|
2
|
+
await {{> locator}}.getByRole('columnheader', { name: '{{escapeQuotes columnName}}' }).waitFor();
|
|
3
|
+
{{~else}}
|
|
4
|
+
await expect({{> locator}}.getByRole('columnheader', { name: '{{escapeQuotes columnName}}' })).toBeVisible();
|
|
5
|
+
{{~/if}}
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
{{~#if isGiven}}
|
|
2
|
+
const tableRow = {{> locator}}.getByRole('row').filter({ hasText: '{{escapeQuotes filterValue}}' });
|
|
3
|
+
await tableRow.waitFor();
|
|
4
|
+
{{~else}}
|
|
5
|
+
const tableRow = {{> locator}}.getByRole('row').filter({ hasText: '{{escapeQuotes filterValue}}' });
|
|
6
|
+
await expect(tableRow).toBeVisible();
|
|
7
|
+
{{~/if}}
|
|
@@ -1 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
{{~#if isGiven}}
|
|
2
|
+
await {{> locator}}.getByRole('row').filter({ hasText: '{{escapeQuotes filterValue}}' }).waitFor({ state: 'hidden' });
|
|
3
|
+
{{~else}}
|
|
4
|
+
await expect({{> locator}}.getByRole('row').filter({ hasText: '{{escapeQuotes filterValue}}' })).toHaveCount(0);
|
|
5
|
+
{{~/if}}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Table Patterns
|
|
3
3
|
* Handles: table row assertions, table cell lookups, actions in table rows
|
|
4
|
+
*
|
|
5
|
+
* Syntax (v2.3 final):
|
|
6
|
+
* - User see [Col] column in [Table] table # column exists
|
|
7
|
+
* - User see [Ref] row in [Table] table with {{value}} # row exists (enters row scope)
|
|
8
|
+
* - User see [Ref] row in [Table] table with {{value}} is hidden # row hidden
|
|
9
|
+
* - User see [Table] table with {{count}} # row count
|
|
10
|
+
* - User see [Table] table is empty # empty table
|
|
11
|
+
* - User see [Col] column with {{value}} # cell value (row scoped — handled in step-mapper)
|
|
12
|
+
* - User click [Act] button in [Table] table with {{filter}} # action in row
|
|
4
13
|
*/
|
|
5
14
|
import { StepPattern } from './types';
|
|
6
15
|
export declare const tablePatterns: StepPattern[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table-patterns.d.ts","sourceRoot":"","sources":["../../../../src/generators/test-generator/patterns/table-patterns.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"table-patterns.d.ts","sourceRoot":"","sources":["../../../../src/generators/test-generator/patterns/table-patterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,WAAW,EAAkB,MAAM,SAAS,CAAC;AAkBtD,eAAO,MAAM,aAAa,EAAE,WAAW,EA6LtC,CAAC"}
|
|
@@ -2,54 +2,82 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Table Patterns
|
|
4
4
|
* Handles: table row assertions, table cell lookups, actions in table rows
|
|
5
|
+
*
|
|
6
|
+
* Syntax (v2.3 final):
|
|
7
|
+
* - User see [Col] column in [Table] table # column exists
|
|
8
|
+
* - User see [Ref] row in [Table] table with {{value}} # row exists (enters row scope)
|
|
9
|
+
* - User see [Ref] row in [Table] table with {{value}} is hidden # row hidden
|
|
10
|
+
* - User see [Table] table with {{count}} # row count
|
|
11
|
+
* - User see [Table] table is empty # empty table
|
|
12
|
+
* - User see [Col] column with {{value}} # cell value (row scoped — handled in step-mapper)
|
|
13
|
+
* - User click [Act] button in [Table] table with {{filter}} # action in row
|
|
5
14
|
*/
|
|
6
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
16
|
exports.tablePatterns = void 0;
|
|
17
|
+
/** Helper: check if current step is in a Given context */
|
|
18
|
+
function isGivenContext(context) {
|
|
19
|
+
return context.effectiveKeyword === 'Given';
|
|
20
|
+
}
|
|
21
|
+
/** Map Gherkin element type to Playwright ARIA role */
|
|
22
|
+
const typeToRole = {
|
|
23
|
+
button: 'button',
|
|
24
|
+
link: 'link',
|
|
25
|
+
checkbox: 'checkbox',
|
|
26
|
+
icon: 'img',
|
|
27
|
+
image: 'img',
|
|
28
|
+
radio: 'radio',
|
|
29
|
+
switch: 'switch',
|
|
30
|
+
};
|
|
8
31
|
exports.tablePatterns = [
|
|
9
|
-
// "User see [
|
|
10
|
-
// Also accepts: "User see [Users] table has {{count}} rows" (backward compat)
|
|
32
|
+
// "User see [Table] table with {{count}}" — row count (no "rows" keyword needed)
|
|
11
33
|
{
|
|
12
34
|
name: 'table-row-count',
|
|
13
35
|
matcher: (step) => {
|
|
14
|
-
return
|
|
15
|
-
/\
|
|
16
|
-
!!step.dataRef
|
|
36
|
+
return step.elementType === 'table' &&
|
|
37
|
+
/\btable\s+with\b/i.test(step.text) &&
|
|
38
|
+
!!step.dataRef &&
|
|
39
|
+
!/\brow\b/i.test(step.text) &&
|
|
40
|
+
!/\bcolumn\b/i.test(step.text) &&
|
|
41
|
+
!/\bempty\b/i.test(step.text);
|
|
17
42
|
},
|
|
18
43
|
resolver: (step, context) => {
|
|
19
44
|
const resolved = context.selectorResolver.resolveSelector(step.selectorRef, context.featureName, 'table', step.nth);
|
|
20
45
|
const count = context.dataResolver.resolveData(step.dataRef, context.featureName);
|
|
46
|
+
const isGiven = isGivenContext(context);
|
|
21
47
|
return {
|
|
22
48
|
templateName: 'table-row-count',
|
|
23
|
-
data: { ...resolved, expectedCount: count },
|
|
24
|
-
comment:
|
|
49
|
+
data: { ...resolved, expectedCount: count, isGiven },
|
|
50
|
+
comment: `${isGiven ? 'Wait' : 'Assert'} ${step.selectorRef} table has ${count} rows`,
|
|
25
51
|
};
|
|
26
52
|
},
|
|
27
53
|
priority: 16,
|
|
28
54
|
},
|
|
29
|
-
// "User see [
|
|
30
|
-
// Preferred: "User see [Email] column in [Users] table" (parent scoping)
|
|
55
|
+
// "User see [Col] column in [Table] table"
|
|
31
56
|
{
|
|
32
57
|
name: 'table-column-exists',
|
|
33
58
|
matcher: (step) => {
|
|
34
|
-
return /\
|
|
35
|
-
/\
|
|
59
|
+
return /\bcolumn\s+in\b/i.test(step.text) &&
|
|
60
|
+
/\btable\b/i.test(step.text) &&
|
|
61
|
+
!step.dataRef;
|
|
36
62
|
},
|
|
37
63
|
resolver: (step, context) => {
|
|
38
|
-
//
|
|
64
|
+
// brackets[0] = Col, brackets[1] = Table
|
|
39
65
|
const brackets = step.text.match(/\[([^\]]+)\]/g) || [];
|
|
40
|
-
const columnName = brackets
|
|
66
|
+
const columnName = brackets[0]?.replace(/[\[\]]/g, '') || 'Unknown';
|
|
67
|
+
const tableName = brackets.length >= 2
|
|
41
68
|
? brackets[1].replace(/[\[\]]/g, '')
|
|
42
|
-
: '
|
|
43
|
-
const resolved = context.selectorResolver.resolveSelector(
|
|
69
|
+
: brackets[0]?.replace(/[\[\]]/g, '') || '';
|
|
70
|
+
const resolved = context.selectorResolver.resolveSelector(tableName, context.featureName, 'table', step.nth);
|
|
71
|
+
const isGiven = isGivenContext(context);
|
|
44
72
|
return {
|
|
45
73
|
templateName: 'table-column-exists',
|
|
46
|
-
data: { ...resolved, columnName },
|
|
47
|
-
comment:
|
|
74
|
+
data: { ...resolved, columnName, isGiven },
|
|
75
|
+
comment: `${isGiven ? 'Wait' : 'Assert'} ${columnName} column in ${tableName} table`,
|
|
48
76
|
};
|
|
49
77
|
},
|
|
50
78
|
priority: 16,
|
|
51
79
|
},
|
|
52
|
-
// "User see [
|
|
80
|
+
// "User see [Table] table is empty"
|
|
53
81
|
{
|
|
54
82
|
name: 'table-is-empty',
|
|
55
83
|
matcher: (step) => {
|
|
@@ -57,136 +85,104 @@ exports.tablePatterns = [
|
|
|
57
85
|
},
|
|
58
86
|
resolver: (step, context) => {
|
|
59
87
|
const resolved = context.selectorResolver.resolveSelector(step.selectorRef, context.featureName, 'table', step.nth);
|
|
88
|
+
const isGiven = isGivenContext(context);
|
|
60
89
|
return {
|
|
61
90
|
templateName: 'table-empty',
|
|
62
|
-
data: { ...resolved },
|
|
63
|
-
comment:
|
|
91
|
+
data: { ...resolved, isGiven },
|
|
92
|
+
comment: `${isGiven ? 'Wait' : 'Assert'} ${step.selectorRef} table is empty`,
|
|
64
93
|
};
|
|
65
94
|
},
|
|
66
95
|
priority: 16,
|
|
67
96
|
},
|
|
68
|
-
// "User see [
|
|
97
|
+
// "User see [Ref] row in [Table] table with {{value}}" — row exists (enters row scope)
|
|
69
98
|
{
|
|
70
|
-
name: 'table-
|
|
99
|
+
name: 'table-row-exists',
|
|
71
100
|
matcher: (step) => {
|
|
72
|
-
return
|
|
73
|
-
/\
|
|
101
|
+
return step.elementType === 'row' &&
|
|
102
|
+
/\bin\b.*\btable\b/i.test(step.text) &&
|
|
103
|
+
/\bwith\b/i.test(step.text) &&
|
|
104
|
+
!!step.dataRef &&
|
|
105
|
+
!/\bis\s+hidden\b/i.test(step.text);
|
|
74
106
|
},
|
|
75
107
|
resolver: (step, context) => {
|
|
76
|
-
//
|
|
108
|
+
// brackets[0] = Ref (row label), brackets[1] = Table
|
|
77
109
|
const brackets = step.text.match(/\[([^\]]+)\]/g) || [];
|
|
78
|
-
const
|
|
79
|
-
const
|
|
110
|
+
const rowLabel = brackets[0]?.replace(/[\[\]]/g, '') || '';
|
|
111
|
+
const tableName = brackets.length >= 2
|
|
80
112
|
? brackets[1].replace(/[\[\]]/g, '')
|
|
81
113
|
: '';
|
|
82
|
-
// Extract data refs: first is filter, second is cell value
|
|
83
|
-
const dataRefs = step.text.match(/\{\{([^}]+)\}\}/g) || [];
|
|
84
|
-
const filterRef = dataRefs[0]?.replace(/\{\{|\}\}/g, '') || '';
|
|
85
|
-
const valueRef = dataRefs.length >= 2
|
|
86
|
-
? dataRefs[1].replace(/\{\{|\}\}/g, '')
|
|
87
|
-
: '';
|
|
88
|
-
const filterValue = filterRef
|
|
89
|
-
? context.dataResolver.resolveData(filterRef, context.featureName)
|
|
90
|
-
: '';
|
|
91
|
-
const cellValue = valueRef
|
|
92
|
-
? context.dataResolver.resolveData(valueRef, context.featureName)
|
|
93
|
-
: '';
|
|
94
114
|
const resolved = context.selectorResolver.resolveSelector(tableName, context.featureName, 'table', step.nth);
|
|
115
|
+
const filterValue = context.dataResolver.resolveData(step.dataRef, context.featureName);
|
|
116
|
+
const isGiven = isGivenContext(context);
|
|
95
117
|
return {
|
|
96
|
-
templateName: 'table-
|
|
97
|
-
data: { ...resolved, filterValue,
|
|
98
|
-
comment:
|
|
118
|
+
templateName: 'table-row-exists',
|
|
119
|
+
data: { ...resolved, filterValue, isGiven },
|
|
120
|
+
comment: `${isGiven ? 'Wait' : 'Assert'} ${rowLabel} row in ${tableName} table with ${step.dataRef}`,
|
|
99
121
|
};
|
|
100
122
|
},
|
|
101
123
|
priority: 17,
|
|
102
124
|
},
|
|
103
|
-
// "User see [
|
|
125
|
+
// "User see [Ref] row in [Table] table with {{value}} is hidden"
|
|
104
126
|
{
|
|
105
|
-
name: 'table-
|
|
127
|
+
name: 'table-row-hidden',
|
|
106
128
|
matcher: (step) => {
|
|
107
|
-
return
|
|
108
|
-
/\
|
|
129
|
+
return step.elementType === 'row' &&
|
|
130
|
+
/\bin\b.*\btable\b/i.test(step.text) &&
|
|
131
|
+
/\bwith\b/i.test(step.text) &&
|
|
132
|
+
!!step.dataRef &&
|
|
133
|
+
/\bis\s+hidden\b/i.test(step.text);
|
|
109
134
|
},
|
|
110
135
|
resolver: (step, context) => {
|
|
136
|
+
// brackets[0] = Ref (row label), brackets[1] = Table
|
|
111
137
|
const brackets = step.text.match(/\[([^\]]+)\]/g) || [];
|
|
112
|
-
const
|
|
113
|
-
const
|
|
138
|
+
const rowLabel = brackets[0]?.replace(/[\[\]]/g, '') || '';
|
|
139
|
+
const tableName = brackets.length >= 2
|
|
114
140
|
? brackets[1].replace(/[\[\]]/g, '')
|
|
115
141
|
: '';
|
|
116
|
-
const rowMatch = step.text.match(/\brow\s+(\d+)\b/i);
|
|
117
|
-
const rowIndex = rowMatch ? parseInt(rowMatch[1], 10) : 1;
|
|
118
|
-
const cellValue = step.dataRef
|
|
119
|
-
? context.dataResolver.resolveData(step.dataRef, context.featureName)
|
|
120
|
-
: '';
|
|
121
142
|
const resolved = context.selectorResolver.resolveSelector(tableName, context.featureName, 'table', step.nth);
|
|
122
|
-
return {
|
|
123
|
-
templateName: 'table-cell-by-index',
|
|
124
|
-
data: { ...resolved, rowIndex, columnName, cellValue },
|
|
125
|
-
comment: `Assert ${tableName} table row ${rowIndex} ${columnName} cell = ${step.dataRef}`,
|
|
126
|
-
};
|
|
127
|
-
},
|
|
128
|
-
priority: 17,
|
|
129
|
-
},
|
|
130
|
-
// "User see [Users] table row with {{name}}" (preferred)
|
|
131
|
-
// Also accepts: "User see [Users] table has row with {{name}}" (backward compat)
|
|
132
|
-
{
|
|
133
|
-
name: 'table-row-exists',
|
|
134
|
-
matcher: (step) => {
|
|
135
|
-
return /\btable\s+(?:has\s+)?row\s+with\b/i.test(step.text) && !!step.dataRef;
|
|
136
|
-
},
|
|
137
|
-
resolver: (step, context) => {
|
|
138
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef, context.featureName, 'table', step.nth);
|
|
139
|
-
const filterValue = context.dataResolver.resolveData(step.dataRef, context.featureName);
|
|
140
|
-
return {
|
|
141
|
-
templateName: 'table-row-exists',
|
|
142
|
-
data: { ...resolved, filterValue },
|
|
143
|
-
comment: `Assert ${step.selectorRef} table has row with ${step.dataRef}`,
|
|
144
|
-
};
|
|
145
|
-
},
|
|
146
|
-
priority: 18,
|
|
147
|
-
},
|
|
148
|
-
// "User see [Users] table has no row with {{name}}"
|
|
149
|
-
{
|
|
150
|
-
name: 'table-row-not-exists',
|
|
151
|
-
matcher: (step) => {
|
|
152
|
-
return /\btable\s+has\s+no\s+row\s+with\b/i.test(step.text) && !!step.dataRef;
|
|
153
|
-
},
|
|
154
|
-
resolver: (step, context) => {
|
|
155
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef, context.featureName, 'table', step.nth);
|
|
156
143
|
const filterValue = context.dataResolver.resolveData(step.dataRef, context.featureName);
|
|
144
|
+
const isGiven = isGivenContext(context);
|
|
157
145
|
return {
|
|
158
146
|
templateName: 'table-row-not-exists',
|
|
159
|
-
data: { ...resolved, filterValue },
|
|
160
|
-
comment:
|
|
147
|
+
data: { ...resolved, filterValue, isGiven },
|
|
148
|
+
comment: `${isGiven ? 'Wait' : 'Assert'} ${rowLabel} row in ${tableName} table is hidden`,
|
|
161
149
|
};
|
|
162
150
|
},
|
|
163
151
|
priority: 19,
|
|
164
152
|
},
|
|
165
|
-
// "User click [
|
|
153
|
+
// "User click [Act] button in [Table] table with {{filter}}" — action in row
|
|
166
154
|
{
|
|
167
155
|
name: 'table-action-in-row',
|
|
168
156
|
matcher: (step) => {
|
|
169
|
-
return /\bin\b.*\btable\
|
|
157
|
+
return /\bin\b.*\btable\b/i.test(step.text) &&
|
|
158
|
+
/\bwith\b/i.test(step.text) &&
|
|
159
|
+
!!step.dataRef &&
|
|
160
|
+
step.elementType !== 'column' &&
|
|
161
|
+
step.elementType !== 'row' &&
|
|
162
|
+
step.elementType !== 'table';
|
|
170
163
|
},
|
|
171
164
|
resolver: (step, context) => {
|
|
165
|
+
// brackets[0] = Act (element name), brackets[1] = Table
|
|
172
166
|
const brackets = step.text.match(/\[([^\]]+)\]/g) || [];
|
|
173
167
|
const elementName = brackets[0]?.replace(/[\[\]]/g, '') || '';
|
|
174
168
|
const tableName = brackets.length >= 2
|
|
175
169
|
? brackets[1].replace(/[\[\]]/g, '')
|
|
176
170
|
: '';
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
const filterValue = filterRef
|
|
180
|
-
? context.dataResolver.resolveData(filterRef, context.featureName)
|
|
171
|
+
const filterValue = step.dataRef
|
|
172
|
+
? context.dataResolver.resolveData(step.dataRef, context.featureName)
|
|
181
173
|
: '';
|
|
182
174
|
const tableResolved = context.selectorResolver.resolveSelector(tableName, context.featureName, 'table', 0);
|
|
183
|
-
// Determine action
|
|
175
|
+
// Determine action from step text
|
|
184
176
|
const actionMatch = step.text.match(/\b(click|check|uncheck)\b/i);
|
|
185
177
|
const action = actionMatch ? actionMatch[1].toLowerCase() : 'click';
|
|
178
|
+
// Extract element type (word after [Act]) and map to ARIA role
|
|
179
|
+
const elementTypeMatch = step.text.match(/\]\s+([\w-]+)\s+in\b/i);
|
|
180
|
+
const elementType = elementTypeMatch ? elementTypeMatch[1].toLowerCase() : 'button';
|
|
181
|
+
const elementRole = typeToRole[elementType] || elementType;
|
|
186
182
|
return {
|
|
187
183
|
templateName: 'table-action-in-row',
|
|
188
|
-
data: { ...tableResolved, elementName, filterValue, action },
|
|
189
|
-
comment: `${action} ${elementName} in ${tableName} table row with ${
|
|
184
|
+
data: { ...tableResolved, elementName, filterValue, action, elementRole },
|
|
185
|
+
comment: `${action} ${elementName} in ${tableName} table row with ${step.dataRef}`,
|
|
190
186
|
};
|
|
191
187
|
},
|
|
192
188
|
priority: 17,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table-patterns.js","sourceRoot":"","sources":["../../../../src/generators/test-generator/patterns/table-patterns.ts"],"names":[],"mappings":";AAAA;;;
|
|
1
|
+
{"version":3,"file":"table-patterns.js","sourceRoot":"","sources":["../../../../src/generators/test-generator/patterns/table-patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -37,6 +37,8 @@ export interface PatternContext {
|
|
|
37
37
|
screenName?: string;
|
|
38
38
|
featurePath?: string;
|
|
39
39
|
scenarioSteps?: ParsedStep[];
|
|
40
|
+
/** Effective Gherkin keyword (Given/When/Then), resolved from And/But */
|
|
41
|
+
effectiveKeyword?: string;
|
|
40
42
|
}
|
|
41
43
|
/**
|
|
42
44
|
* Step pattern definition
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/generators/test-generator/patterns/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtD;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,KAAK,UAAU,CAAC;AAEzF;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,KAAK,gBAAgB,CAAC;AAE9F;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC;IACjD,cAAc,EAAE,GAAG,CAAC;IACpB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,MAAM,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,UAAU,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/generators/test-generator/patterns/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtD;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,KAAK,UAAU,CAAC;AAEzF;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,KAAK,gBAAgB,CAAC;AAE9F;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC;IACjD,cAAc,EAAE,GAAG,CAAC;IACpB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,MAAM,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,UAAU,EAAE,CAAC;IAC7B,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -22,6 +22,11 @@ export declare class StepMapper {
|
|
|
22
22
|
private currentScenarioSteps?;
|
|
23
23
|
private inDialogScope;
|
|
24
24
|
private inFrameScope;
|
|
25
|
+
/** Tracks the last primary keyword (Given/When/Then) for resolving And/But */
|
|
26
|
+
private lastPrimaryKeyword;
|
|
27
|
+
/** Row scope: when active, column assertions are scoped to this row */
|
|
28
|
+
private inRowScope;
|
|
29
|
+
private rowScopeTable;
|
|
25
30
|
constructor(options?: {
|
|
26
31
|
verbose?: boolean;
|
|
27
32
|
baseURL?: string;
|
|
@@ -46,6 +51,14 @@ export declare class StepMapper {
|
|
|
46
51
|
* Uses pattern registry first, falls back to AI if enabled
|
|
47
52
|
*/
|
|
48
53
|
mapStep(step: ParsedStep): MappedStep | Promise<MappedStep>;
|
|
54
|
+
/**
|
|
55
|
+
* Generate column assertion scoped to the current row.
|
|
56
|
+
* Uses columns config for exact cell targeting (nth), or filter fallback.
|
|
57
|
+
*
|
|
58
|
+
* With columns: await expect(tableRow.getByRole('cell').nth(index)).toHaveText('value');
|
|
59
|
+
* Without: await expect(tableRow.getByRole('cell').filter({ hasText: 'value' })).toBeVisible();
|
|
60
|
+
*/
|
|
61
|
+
private generateRowScopedColumnAssertion;
|
|
49
62
|
/**
|
|
50
63
|
* Create pattern context for generators
|
|
51
64
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step-mapper.d.ts","sourceRoot":"","sources":["../../../src/generators/test-generator/step-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAO/C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,oBAAoB,CAAC,CAAe;IAC5C,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,YAAY,CAAkB;
|
|
1
|
+
{"version":3,"file":"step-mapper.d.ts","sourceRoot":"","sources":["../../../src/generators/test-generator/step-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAO/C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,oBAAoB,CAAC,CAAe;IAC5C,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,YAAY,CAAkB;IACtC,8EAA8E;IAC9E,OAAO,CAAC,kBAAkB,CAAmB;IAC7C,uEAAuE;IACvE,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,aAAa,CAAc;gBAEvB,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAO;IAkBlI;;OAEG;IACH,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAMlE;;OAEG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAM1C;;OAEG;IACH,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI;IAW7C;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAmH3D;;;;;;OAMG;IACH,OAAO,CAAC,gCAAgC;IA8CxC;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAoB7B"}
|
|
@@ -49,6 +49,11 @@ class StepMapper {
|
|
|
49
49
|
this.verbose = false;
|
|
50
50
|
this.inDialogScope = false;
|
|
51
51
|
this.inFrameScope = false;
|
|
52
|
+
/** Tracks the last primary keyword (Given/When/Then) for resolving And/But */
|
|
53
|
+
this.lastPrimaryKeyword = 'Given';
|
|
54
|
+
/** Row scope: when active, column assertions are scoped to this row */
|
|
55
|
+
this.inRowScope = false;
|
|
56
|
+
this.rowScopeTable = '';
|
|
52
57
|
this.verbose = options.verbose ?? false;
|
|
53
58
|
this.baseURL = options.baseURL || null; // null means path-only navigation
|
|
54
59
|
this.featureName = options.featureName;
|
|
@@ -88,6 +93,9 @@ class StepMapper {
|
|
|
88
93
|
// Reset dialog and frame scope at the start of each new scenario
|
|
89
94
|
this.inDialogScope = false;
|
|
90
95
|
this.inFrameScope = false;
|
|
96
|
+
this.inRowScope = false;
|
|
97
|
+
this.rowScopeTable = '';
|
|
98
|
+
this.lastPrimaryKeyword = 'Given';
|
|
91
99
|
this.templateEngine.resetBaseContext();
|
|
92
100
|
}
|
|
93
101
|
/**
|
|
@@ -96,6 +104,11 @@ class StepMapper {
|
|
|
96
104
|
*/
|
|
97
105
|
mapStep(step) {
|
|
98
106
|
this.stepCounter++;
|
|
107
|
+
// Track effective keyword: And/But inherit from previous Given/When/Then
|
|
108
|
+
const keyword = step.keyword.trim();
|
|
109
|
+
if (keyword === 'Given' || keyword === 'When' || keyword === 'Then') {
|
|
110
|
+
this.lastPrimaryKeyword = keyword;
|
|
111
|
+
}
|
|
99
112
|
// Frame scope directives — intercept before pattern matching
|
|
100
113
|
if (/\bswitch(?:es)?\s+to\b/i.test(step.text) &&
|
|
101
114
|
(step.elementType === 'frame' || step.elementType === 'iframe')) {
|
|
@@ -164,10 +177,26 @@ class StepMapper {
|
|
|
164
177
|
this.templateEngine.setBaseContext(contextVars);
|
|
165
178
|
return { code: '', comment: `Enter dialog scope${step.selectorRef ? ': ' + step.selectorRef : ''}` };
|
|
166
179
|
}
|
|
180
|
+
// Row-scoped column assertion: "User see [Col] column with {{value}}"
|
|
181
|
+
// Must be checked BEFORE pattern matching (like dialog/frame scope)
|
|
182
|
+
if (this.inRowScope && step.elementType === 'column' &&
|
|
183
|
+
!!step.dataRef && /\bwith\b/i.test(step.text) &&
|
|
184
|
+
!/\btable\b/i.test(step.text)) {
|
|
185
|
+
return this.generateRowScopedColumnAssertion(step);
|
|
186
|
+
}
|
|
167
187
|
// Try pattern registry first
|
|
168
188
|
const context = this.createPatternContext();
|
|
169
189
|
const mappedStep = this.patternRegistry.generateStep(step, context);
|
|
170
190
|
if (mappedStep) {
|
|
191
|
+
// Enter row scope when a row-in-table step is matched
|
|
192
|
+
if (step.elementType === 'row' && /\bin\b.*\btable\b/i.test(step.text) &&
|
|
193
|
+
/\bwith\b/i.test(step.text) && !/\bis\s+hidden\b/i.test(step.text)) {
|
|
194
|
+
const brackets = step.text.match(/\[([^\]]+)\]/g) || [];
|
|
195
|
+
this.inRowScope = true;
|
|
196
|
+
this.rowScopeTable = brackets.length >= 2
|
|
197
|
+
? brackets[1].replace(/[\[\]]/g, '')
|
|
198
|
+
: '';
|
|
199
|
+
}
|
|
171
200
|
if (this.verbose) {
|
|
172
201
|
console.log(` ✓ Pattern matched: ${step.text.substring(0, 50)}...`);
|
|
173
202
|
}
|
|
@@ -179,6 +208,56 @@ class StepMapper {
|
|
|
179
208
|
comment: `Unrecognized step pattern: ${step.text}`,
|
|
180
209
|
};
|
|
181
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* Generate column assertion scoped to the current row.
|
|
213
|
+
* Uses columns config for exact cell targeting (nth), or filter fallback.
|
|
214
|
+
*
|
|
215
|
+
* With columns: await expect(tableRow.getByRole('cell').nth(index)).toHaveText('value');
|
|
216
|
+
* Without: await expect(tableRow.getByRole('cell').filter({ hasText: 'value' })).toBeVisible();
|
|
217
|
+
*/
|
|
218
|
+
generateRowScopedColumnAssertion(step) {
|
|
219
|
+
const columnRef = step.selectorRef || '';
|
|
220
|
+
const cellValue = step.dataRef
|
|
221
|
+
? this.dataResolver.resolveData(step.dataRef, this.featureName)
|
|
222
|
+
: '';
|
|
223
|
+
// Try to find column index from table's columns config
|
|
224
|
+
let columnIndex;
|
|
225
|
+
if (this.rowScopeTable) {
|
|
226
|
+
try {
|
|
227
|
+
const tableResolved = this.selectorResolver.resolveSelector(this.rowScopeTable, this.featureName, 'table', 0);
|
|
228
|
+
const columnKey = columnRef.toLowerCase().normalize('NFC');
|
|
229
|
+
columnIndex = tableResolved.columns?.[columnKey]?.index;
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// Table not found — fallback to filter
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const isGiven = this.lastPrimaryKeyword === 'Given';
|
|
236
|
+
const escapedValue = cellValue.replace(/'/g, "\\'");
|
|
237
|
+
let code;
|
|
238
|
+
if (columnIndex !== undefined) {
|
|
239
|
+
// Exact cell: nth(index) + toHaveText
|
|
240
|
+
if (isGiven) {
|
|
241
|
+
code = `await expect(tableRow.getByRole('cell').nth(${columnIndex})).toHaveText('${escapedValue}');`;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
code = `await expect(tableRow.getByRole('cell').nth(${columnIndex})).toHaveText('${escapedValue}');`;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
// Fallback: filter by text
|
|
249
|
+
if (isGiven) {
|
|
250
|
+
code = `await tableRow.getByRole('cell').filter({ hasText: '${escapedValue}' }).waitFor();`;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
code = `await expect(tableRow.getByRole('cell').filter({ hasText: '${escapedValue}' })).toBeVisible();`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
code,
|
|
258
|
+
comment: `${isGiven ? 'Wait' : 'Assert'} ${columnRef} column = ${step.dataRef}`,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
182
261
|
/**
|
|
183
262
|
* Create pattern context for generators
|
|
184
263
|
*/
|
|
@@ -198,6 +277,7 @@ class StepMapper {
|
|
|
198
277
|
screenName: this.screenName,
|
|
199
278
|
featurePath: this.featurePath, // Pass feature path to patterns
|
|
200
279
|
scenarioSteps: this.currentScenarioSteps || [], // Pass scenario steps for path variables
|
|
280
|
+
effectiveKeyword: this.lastPrimaryKeyword,
|
|
201
281
|
};
|
|
202
282
|
}
|
|
203
283
|
}
|