@sun-asterisk/sungen 1.0.14 → 1.0.15
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/generators/cli.js +1 -1
- package/dist/generators/gherkin-parser/index.d.ts +1 -0
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +8 -0
- package/dist/generators/gherkin-parser/index.js.map +1 -1
- package/dist/generators/scaffold-generator/index.d.ts +4 -1
- package/dist/generators/scaffold-generator/index.d.ts.map +1 -1
- package/dist/generators/scaffold-generator/index.js +39 -16
- package/dist/generators/scaffold-generator/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-element-with-text.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/upload-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-filter-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +10 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/label.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/locator.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/placeholder.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/role.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/testid.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/text.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +10 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.js +119 -16
- package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/form-patterns.js +37 -5
- package/dist/generators/test-generator/patterns/form-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.js +108 -9
- package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/navigation-patterns.js +1 -1
- package/dist/generators/test-generator/patterns/navigation-patterns.js.map +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +1 -0
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +15 -0
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/template-engine.d.ts +5 -0
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
- package/dist/generators/test-generator/template-engine.js +61 -1
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +14 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +28 -3
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/input/cli-adapter.js +1 -1
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +2 -1
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/package.json +1 -1
- package/src/generators/cli.ts +1 -1
- package/src/generators/gherkin-parser/index.ts +10 -0
- package/src/generators/scaffold-generator/index.ts +43 -20
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-element-with-text.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/upload-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-filter-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +10 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/label.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/locator.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/placeholder.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/role.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/testid.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/text.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +10 -1
- package/src/generators/test-generator/patterns/assertion-patterns.ts +142 -16
- package/src/generators/test-generator/patterns/form-patterns.ts +39 -5
- package/src/generators/test-generator/patterns/interaction-patterns.ts +118 -11
- package/src/generators/test-generator/patterns/navigation-patterns.ts +2 -1
- package/src/generators/test-generator/step-mapper.ts +18 -2
- package/src/generators/test-generator/template-engine.ts +70 -1
- package/src/generators/test-generator/utils/selector-resolver.ts +30 -3
- package/src/input/cli-adapter.ts +1 -1
- package/src/orchestrator/project-initializer.ts +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-initializer.js","sourceRoot":"","sources":["../../src/orchestrator/project-initializer.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,uCAAyB;AACzB,2CAA6B;AAE7B,MAAa,kBAAkB;IAK7B;QAHQ,iBAAY,GAAa,EAAE,CAAC;QAC5B,iBAAY,GAAa,EAAE,CAAC;QAGlC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU;QACd,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QAEnD,qBAAqB;QACrB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEzB,4CAA4C;QAC5C,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAE9B,2BAA2B;QAC3B,IAAI,CAAC,eAAe,EAAE,CAAC;QAEvB,gBAAgB;QAChB,IAAI,CAAC,YAAY,EAAE,CAAC;QAEpB,kBAAkB;QAClB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IAED;;OAEG;IACK,iBAAiB;QACvB,MAAM,IAAI,GAAG;YACX,IAAI;YACJ,YAAY;YACZ,OAAO;YACP,iBAAiB;
|
|
1
|
+
{"version":3,"file":"project-initializer.js","sourceRoot":"","sources":["../../src/orchestrator/project-initializer.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/generators/cli.ts
CHANGED
|
@@ -52,7 +52,7 @@ const program = new Command();
|
|
|
52
52
|
program
|
|
53
53
|
.name('qa-generator')
|
|
54
54
|
.description('AI-Native QA Selector DSL Generator')
|
|
55
|
-
.version('1.0.
|
|
55
|
+
.version('1.0.15');
|
|
56
56
|
|
|
57
57
|
// ============================================================================
|
|
58
58
|
// Command: discover
|
|
@@ -3,6 +3,7 @@ import * as Gherkin from '@cucumber/gherkin';
|
|
|
3
3
|
import * as Messages from '@cucumber/messages';
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
|
+
import { SelectorResolver } from '../test-generator/utils/selector-resolver';
|
|
6
7
|
|
|
7
8
|
export interface ParsedStep {
|
|
8
9
|
keyword: string; // Given, When, Then, And, But
|
|
@@ -11,6 +12,7 @@ export interface ParsedStep {
|
|
|
11
12
|
dataRef?: string; // Extracted data reference like <valid_user.email>
|
|
12
13
|
value?: string; // Static value if any
|
|
13
14
|
elementType?: string; // Element type after [Selector] e.g., "button", "text", "link", "field"
|
|
15
|
+
nth?: number; // Positional index from "[Element] field 3" → 3 (0 = not specified)
|
|
14
16
|
featurePath?: string; // NEW: Feature path for page type (e.g., "/", "/dashboard")
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -141,6 +143,13 @@ export class GherkinParser {
|
|
|
141
143
|
elementType = finalWordMatch ? finalWordMatch[1].toLowerCase() : undefined;
|
|
142
144
|
}
|
|
143
145
|
|
|
146
|
+
// Extract positional index from "[Element] field 3", "[Element] button 2", etc.
|
|
147
|
+
let nth = 0;
|
|
148
|
+
if (selectorRef && selectorMatch) {
|
|
149
|
+
const afterElement = text.slice(selectorMatch.index + selectorMatch[0].length);
|
|
150
|
+
nth = SelectorResolver.extractNthFromStep(afterElement);
|
|
151
|
+
}
|
|
152
|
+
|
|
144
153
|
return {
|
|
145
154
|
keyword: step.keyword.trim(),
|
|
146
155
|
text,
|
|
@@ -148,6 +157,7 @@ export class GherkinParser {
|
|
|
148
157
|
dataRef,
|
|
149
158
|
value,
|
|
150
159
|
elementType,
|
|
160
|
+
nth,
|
|
151
161
|
};
|
|
152
162
|
}
|
|
153
163
|
|
|
@@ -95,14 +95,18 @@ export class ScaffoldGenerator {
|
|
|
95
95
|
let match: RegExpExecArray | null;
|
|
96
96
|
while ((match = elementRegex.exec(trimmedLine)) !== null) {
|
|
97
97
|
const rawText = match[1];
|
|
98
|
-
const
|
|
98
|
+
const baseKey = SelectorResolver.generateKey(rawText);
|
|
99
99
|
|
|
100
100
|
// Extract nth index and target type from text after the element reference
|
|
101
101
|
// Matches patterns like: [Email] field 1, [Submit] button 2, [Name] 3
|
|
102
102
|
const afterElement = trimmedLine.slice(match.index + match[0].length);
|
|
103
|
-
const nth =
|
|
103
|
+
const nth = SelectorResolver.extractNthFromStep(afterElement);
|
|
104
104
|
const targetType = this.extractTargetType(afterElement, action);
|
|
105
105
|
|
|
106
|
+
// Append nth to key so elements at different positions get distinct keys
|
|
107
|
+
// e.g., [Hãy gửi lời cảm ơn] field 3 → "hay.gui.loi.cam.on--3"
|
|
108
|
+
const key = nth > 0 ? `${baseKey}--${nth}` : baseKey;
|
|
109
|
+
|
|
106
110
|
// Check if element starts with "target" (semantic indicator for dynamic content)
|
|
107
111
|
// e.g., [target order], [Target Item] → element should have empty value
|
|
108
112
|
const isTargetElement = /^target\s/i.test(rawText);
|
|
@@ -153,12 +157,21 @@ export class ScaffoldGenerator {
|
|
|
153
157
|
if (/^field\b/.test(lowerText) || /^\d*\s*field\b/.test(lowerText) || /^input\b/.test(lowerText)) {
|
|
154
158
|
return 'field';
|
|
155
159
|
}
|
|
160
|
+
if (/^textbox\b/.test(lowerText) || /^\d*\s*textbox\b/.test(lowerText)) {
|
|
161
|
+
return 'textbox';
|
|
162
|
+
}
|
|
163
|
+
if (/^textarea\b/.test(lowerText) || /^\d*\s*textarea\b/.test(lowerText)) {
|
|
164
|
+
return 'textarea';
|
|
165
|
+
}
|
|
156
166
|
if (/^text\b/.test(lowerText) || /^\d*\s*text\b/.test(lowerText)) {
|
|
157
167
|
return 'text';
|
|
158
168
|
}
|
|
159
169
|
if (/^label\b/.test(lowerText) || /^\d*\s*label\b/.test(lowerText)) {
|
|
160
170
|
return 'label';
|
|
161
171
|
}
|
|
172
|
+
if (/^uploader\b/.test(lowerText) || /^\d*\s*uploader\b/.test(lowerText)) {
|
|
173
|
+
return 'uploader';
|
|
174
|
+
}
|
|
162
175
|
if (/^element\b/.test(lowerText) || /^\d*\s*element\b/.test(lowerText)) {
|
|
163
176
|
return 'element';
|
|
164
177
|
}
|
|
@@ -188,19 +201,6 @@ export class ScaffoldGenerator {
|
|
|
188
201
|
* Extract nth index from text after element reference
|
|
189
202
|
* e.g., " field 1 with" -> 1, " button 2" -> 2, " row 1" -> 1, " text" -> 0
|
|
190
203
|
*/
|
|
191
|
-
private extractNthIndex(text: string): number {
|
|
192
|
-
// Match patterns like "field 1", "button 2", "row 1", "text 3", or just a number
|
|
193
|
-
// The number should come right after optional keywords (field, button, text, link, row, etc.)
|
|
194
|
-
const nthRegex = /^\s*(?:field|button|text|link|input|element|item|logo|image|img|icon|raw|table|columnheader|list|listitem|row)?\s*(\d+)\b/i;
|
|
195
|
-
const match = nthRegex.exec(text);
|
|
196
|
-
|
|
197
|
-
if (match) {
|
|
198
|
-
return parseInt(match[1], 10);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return 0;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
204
|
/**
|
|
205
205
|
* Detect the action type from a Gherkin step line
|
|
206
206
|
*/
|
|
@@ -337,6 +337,16 @@ export class ScaffoldGenerator {
|
|
|
337
337
|
|
|
338
338
|
switch (element.action) {
|
|
339
339
|
case 'fill':
|
|
340
|
+
// For fill action with uploader type, use label type (file input triggered by label)
|
|
341
|
+
if (targetType === 'uploader') {
|
|
342
|
+
return {
|
|
343
|
+
locator: '',
|
|
344
|
+
type: 'label',
|
|
345
|
+
value: element.rawText,
|
|
346
|
+
nth,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
340
350
|
// For fill action with label type, use label type
|
|
341
351
|
if (targetType === 'label') {
|
|
342
352
|
return {
|
|
@@ -346,13 +356,13 @@ export class ScaffoldGenerator {
|
|
|
346
356
|
nth,
|
|
347
357
|
};
|
|
348
358
|
}
|
|
349
|
-
|
|
350
|
-
// For fill action, use
|
|
351
|
-
// User should update 'value' with actual placeholder text from inspection
|
|
359
|
+
|
|
360
|
+
// For fill action with field/textbox/textarea types, use role-based locator
|
|
352
361
|
return {
|
|
353
362
|
locator: '',
|
|
354
|
-
type: '
|
|
355
|
-
value:
|
|
363
|
+
type: 'role',
|
|
364
|
+
value: this.getFillRoleValue(targetType),
|
|
365
|
+
name: element.rawText,
|
|
356
366
|
nth,
|
|
357
367
|
};
|
|
358
368
|
|
|
@@ -479,6 +489,19 @@ export class ScaffoldGenerator {
|
|
|
479
489
|
}
|
|
480
490
|
}
|
|
481
491
|
|
|
492
|
+
/**
|
|
493
|
+
* Get ARIA role value for fill actions — all fillable inputs use 'textbox'
|
|
494
|
+
*/
|
|
495
|
+
private getFillRoleValue(targetType: string): string {
|
|
496
|
+
switch (targetType) {
|
|
497
|
+
case 'field':
|
|
498
|
+
case 'textbox':
|
|
499
|
+
case 'textarea':
|
|
500
|
+
default:
|
|
501
|
+
return 'textbox';
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
482
505
|
/**
|
|
483
506
|
* Generate YAML content from scaffold result
|
|
484
507
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
await {{> locator}}.filter({ hasText: '{{escapeQuotes dataValue}}' }).click();
|
|
1
|
+
await {{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}.click();
|
package/src/generators/test-generator/adapters/playwright/templates/steps/actions/upload-action.hbs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await {{> locator}}.setInputFiles('specs/storage/{{fileName}}');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await expect({{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}).toBeDisabled();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{#if name}}await expect(page.getByRole('{{role}}', { name: '{{name}}'{{#if (shouldUseExact selectorRef)}}, exact: true{{/if}} }).filter({ hasText: '{{dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeDisabled();{{else}}await expect(page.getByRole('{{role}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}}).filter({ hasText: '{{dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeDisabled();{{/if}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await expect(page.getByText(/.*{{variable}}/{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}})).toBeDisabled();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await expect({{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}).toBeHidden();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{#if name}}await expect(page.getByRole('{{role}}', { name: '{{name}}'{{#if (shouldUseExact selectorRef)}}, exact: true{{/if}} }).filter({ hasText: '{{dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeHidden();{{else}}await expect(page.getByRole('{{role}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}}).filter({ hasText: '{{dataValue}}' }){{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}).toBeHidden();{{/if}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await expect(page.getByText(/.*{{variable}}/{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}})).toBeHidden();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
await expect({{> locator}}.filter({ hasText: '{{escapeQuotes dataValue}}' })).toBeVisible();
|
|
1
|
+
await expect({{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}).toBeVisible();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
await expect({{> locator}}.filter({ hasText: '{{dataValue}}' })).toBeVisible();
|
|
1
|
+
await expect({{> locator-base}}.filter({ hasText: '{{dataValue}}' }){{> locator-nth}}).toBeVisible();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await {{> locator-base}}.filter({ hasText: '{{escapeQuotes dataValue}}' }){{> locator-nth}}.waitFor({ state: '{{state}}' });
|
package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{{#switch strategy~}}
|
|
2
|
+
{{~#case 'testid'}}{{> locator-strategies/testid}}{{/case~}}
|
|
3
|
+
{{~#case 'role'}}{{> locator-strategies/role}}{{/case~}}
|
|
4
|
+
{{~#case 'placeholder'}}{{> locator-strategies/placeholder}}{{/case~}}
|
|
5
|
+
{{~#case 'label'}}{{> locator-strategies/label}}{{/case~}}
|
|
6
|
+
{{~#case 'text'}}{{> locator-strategies/text}}{{/case~}}
|
|
7
|
+
{{~#case 'locator'}}{{> locator-strategies/locator}}{{/case~}}
|
|
8
|
+
{{~#case 'id'}}{{> locator-strategies/id}}{{/case~}}
|
|
9
|
+
{{~#default}}{{> locator-strategies/default}}{{/default~}}
|
|
10
|
+
{{~/switch}}
|
package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{#if (gt nth 0)}}.nth({{subtract nth 1}}){{/if}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
page.locator('{{escapeQuotes selector}}')
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
page.locator('{{selector}}')
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{pageRoot}}.getByLabel('{{escapeQuotes value}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
page.locator('{{escapeQuotes value}}')
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{pageRoot}}.getByPlaceholder('{{escapeQuotes value}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{#if name}}{{pageRoot}}.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if (shouldUseExact selectorRef)}}, exact: true{{/if}} }){{else}}{{pageRoot}}.getByRole('{{role}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}}){{/if}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{pageRoot}}.getByTestId('{{value}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{pageRoot}}.getByText('{{escapeQuotes value}}'{{#if (shouldUseExact selectorRef)}}, { exact: true }{{/if}})
|
package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs
CHANGED
|
@@ -1 +1,10 @@
|
|
|
1
|
-
{{#
|
|
1
|
+
{{#switch strategy~}}
|
|
2
|
+
{{~#case 'testid'}}{{> locator-strategies/testid}}{{/case~}}
|
|
3
|
+
{{~#case 'role'}}{{> locator-strategies/role}}{{/case~}}
|
|
4
|
+
{{~#case 'placeholder'}}{{> locator-strategies/placeholder}}{{/case~}}
|
|
5
|
+
{{~#case 'label'}}{{> locator-strategies/label}}{{/case~}}
|
|
6
|
+
{{~#case 'text'}}{{> locator-strategies/text}}{{/case~}}
|
|
7
|
+
{{~#case 'locator'}}{{> locator-strategies/locator}}{{/case~}}
|
|
8
|
+
{{~#case 'id'}}{{> locator-strategies/id}}{{/case~}}
|
|
9
|
+
{{~#default}}{{> locator-strategies/default}}{{/default~}}
|
|
10
|
+
{{~/switch}}{{> locator-nth}}
|
|
@@ -23,7 +23,8 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
23
23
|
const resolved = context.selectorResolver.resolveSelector(
|
|
24
24
|
step.selectorRef,
|
|
25
25
|
context.featureName,
|
|
26
|
-
step.elementType
|
|
26
|
+
step.elementType,
|
|
27
|
+
step.nth
|
|
27
28
|
);
|
|
28
29
|
// Use selector's value as the path for URL assertion
|
|
29
30
|
path = resolved.value || path;
|
|
@@ -42,6 +43,130 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
42
43
|
};
|
|
43
44
|
},
|
|
44
45
|
priority: 13, // Higher priority than general visibility
|
|
46
|
+
},
|
|
47
|
+
// Pattern: Then User see [panel] dialog with {{kudo.title}} hidden -> toBeHidden()
|
|
48
|
+
{
|
|
49
|
+
name: 'see-with-variable-hidden',
|
|
50
|
+
matcher: (step: ParsedStep) =>
|
|
51
|
+
(step.text.includes('should see') || step.text.match(/\b(see|sees)\s+\[/)) &&
|
|
52
|
+
!!step.selectorRef &&
|
|
53
|
+
!!step.dataRef &&
|
|
54
|
+
step.text.includes('with') &&
|
|
55
|
+
/\bhidden\b/.test(step.text),
|
|
56
|
+
generator: (step, context) => {
|
|
57
|
+
let resolved: any = {};
|
|
58
|
+
try {
|
|
59
|
+
resolved = context.selectorResolver.resolveSelector(
|
|
60
|
+
step.selectorRef!,
|
|
61
|
+
context.featureName,
|
|
62
|
+
step.elementType,
|
|
63
|
+
step.nth
|
|
64
|
+
);
|
|
65
|
+
} catch (error) {}
|
|
66
|
+
|
|
67
|
+
let dataValue: string;
|
|
68
|
+
try {
|
|
69
|
+
dataValue = context.dataResolver.resolveData(step.dataRef!, context.featureName);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
dataValue = `\${${step.dataRef}}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const escapedVariable = dataValue.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
|
|
75
|
+
|
|
76
|
+
if (resolved.strategy === 'locator') {
|
|
77
|
+
const code = context.templateEngine.renderStep('hidden-with-filter-assertion', {
|
|
78
|
+
...resolved, dataValue, nth: resolved.nth || 0,
|
|
79
|
+
});
|
|
80
|
+
return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (resolved.strategy === 'role' && resolved.role) {
|
|
84
|
+
const code = context.templateEngine.renderStep('hidden-with-role-variable-assertion', {
|
|
85
|
+
role: resolved.role,
|
|
86
|
+
name: resolved.name && resolved.name.trim() ? resolved.name : undefined,
|
|
87
|
+
dataValue,
|
|
88
|
+
nth: resolved.nth || 0,
|
|
89
|
+
});
|
|
90
|
+
return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const selectorValue = resolved.value || '';
|
|
94
|
+
if (selectorValue && selectorValue.trim()) {
|
|
95
|
+
const code = context.templateEngine.renderStep('hidden-with-filter-assertion', {
|
|
96
|
+
...resolved, dataValue, nth: resolved.nth || 0,
|
|
97
|
+
});
|
|
98
|
+
return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const code = context.templateEngine.renderStep('hidden-with-variable-assertion', {
|
|
102
|
+
variable: escapedVariable,
|
|
103
|
+
selectorRef: step.selectorRef,
|
|
104
|
+
});
|
|
105
|
+
return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
|
|
106
|
+
},
|
|
107
|
+
priority: 14,
|
|
108
|
+
},
|
|
109
|
+
// Pattern: Then User see [submit] button with {{label}} disabled -> toBeDisabled()
|
|
110
|
+
{
|
|
111
|
+
name: 'see-with-variable-disabled',
|
|
112
|
+
matcher: (step: ParsedStep) =>
|
|
113
|
+
(step.text.includes('should see') || step.text.match(/\b(see|sees)\s+\[/)) &&
|
|
114
|
+
!!step.selectorRef &&
|
|
115
|
+
!!step.dataRef &&
|
|
116
|
+
step.text.includes('with') &&
|
|
117
|
+
/\bdisabled\b/.test(step.text),
|
|
118
|
+
generator: (step, context) => {
|
|
119
|
+
let resolved: any = {};
|
|
120
|
+
try {
|
|
121
|
+
resolved = context.selectorResolver.resolveSelector(
|
|
122
|
+
step.selectorRef!,
|
|
123
|
+
context.featureName,
|
|
124
|
+
step.elementType,
|
|
125
|
+
step.nth
|
|
126
|
+
);
|
|
127
|
+
} catch (error) {}
|
|
128
|
+
|
|
129
|
+
let dataValue: string;
|
|
130
|
+
try {
|
|
131
|
+
dataValue = context.dataResolver.resolveData(step.dataRef!, context.featureName);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
dataValue = `\${${step.dataRef}}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const escapedVariable = dataValue.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
|
|
137
|
+
|
|
138
|
+
if (resolved.strategy === 'locator') {
|
|
139
|
+
const code = context.templateEngine.renderStep('disabled-with-filter-assertion', {
|
|
140
|
+
...resolved, dataValue, nth: resolved.nth || 0,
|
|
141
|
+
});
|
|
142
|
+
return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (resolved.strategy === 'role' && resolved.role) {
|
|
146
|
+
const code = context.templateEngine.renderStep('disabled-with-role-variable-assertion', {
|
|
147
|
+
role: resolved.role,
|
|
148
|
+
name: resolved.name && resolved.name.trim() ? resolved.name : undefined,
|
|
149
|
+
dataValue,
|
|
150
|
+
nth: resolved.nth || 0,
|
|
151
|
+
});
|
|
152
|
+
return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const selectorValue = resolved.value || '';
|
|
156
|
+
if (selectorValue && selectorValue.trim()) {
|
|
157
|
+
const code = context.templateEngine.renderStep('disabled-with-filter-assertion', {
|
|
158
|
+
...resolved, dataValue, nth: resolved.nth || 0,
|
|
159
|
+
});
|
|
160
|
+
return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const code = context.templateEngine.renderStep('disabled-with-variable-assertion', {
|
|
164
|
+
variable: escapedVariable,
|
|
165
|
+
selectorRef: step.selectorRef,
|
|
166
|
+
});
|
|
167
|
+
return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
|
|
168
|
+
},
|
|
169
|
+
priority: 14,
|
|
45
170
|
},
|
|
46
171
|
// NEW: Assertion with variable (handles both empty selector and static + variable)
|
|
47
172
|
// Pattern: Then User see [error] message with {{fail_message}}
|
|
@@ -61,8 +186,9 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
61
186
|
try {
|
|
62
187
|
resolved = context.selectorResolver.resolveSelector(
|
|
63
188
|
step.selectorRef!,
|
|
64
|
-
context.featureName,
|
|
65
|
-
step.elementType
|
|
189
|
+
context.featureName,
|
|
190
|
+
step.elementType,
|
|
191
|
+
step.nth
|
|
66
192
|
);
|
|
67
193
|
} catch (error) {
|
|
68
194
|
// Selector not in YAML or context issue - will use variable-only
|
|
@@ -143,7 +269,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
143
269
|
!!step.dataRef &&
|
|
144
270
|
step.text.includes('with'),
|
|
145
271
|
generator: (step, context) => {
|
|
146
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
272
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
147
273
|
|
|
148
274
|
// Resolve the data value
|
|
149
275
|
let dataValue: string;
|
|
@@ -182,7 +308,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
182
308
|
step.text.match(/\b(see|sees)\s+\[/)) &&
|
|
183
309
|
!!step.selectorRef,
|
|
184
310
|
generator: (step, context) => {
|
|
185
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
311
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
186
312
|
const code = context.templateEngine.renderStep('visible-assertion', { ...resolved, selectorRef: step.selectorRef });
|
|
187
313
|
return {
|
|
188
314
|
code,
|
|
@@ -199,7 +325,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
199
325
|
step.text.includes('should not see')) &&
|
|
200
326
|
!!step.selectorRef,
|
|
201
327
|
generator: (step, context) => {
|
|
202
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
328
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
203
329
|
const code = context.templateEngine.renderStep('not-visible-assertion', { ...resolved, selectorRef: step.selectorRef });
|
|
204
330
|
return {
|
|
205
331
|
code,
|
|
@@ -213,7 +339,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
213
339
|
matcher: (step: ParsedStep) =>
|
|
214
340
|
step.text.includes('should be enabled') && !!step.selectorRef,
|
|
215
341
|
generator: (step, context) => {
|
|
216
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
342
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
217
343
|
const code = context.templateEngine.renderStep('enabled-assertion', { ...resolved, selectorRef: step.selectorRef });
|
|
218
344
|
return {
|
|
219
345
|
code,
|
|
@@ -227,7 +353,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
227
353
|
matcher: (step: ParsedStep) =>
|
|
228
354
|
step.text.includes('should be disabled') && !!step.selectorRef,
|
|
229
355
|
generator: (step, context) => {
|
|
230
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
356
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
231
357
|
const code = context.templateEngine.renderStep('disabled-assertion', { ...resolved, selectorRef: step.selectorRef });
|
|
232
358
|
return {
|
|
233
359
|
code,
|
|
@@ -241,7 +367,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
241
367
|
matcher: (step: ParsedStep) =>
|
|
242
368
|
step.text.includes('should contain') && !!step.selectorRef && !!(step.value || step.dataRef),
|
|
243
369
|
generator: (step, context) => {
|
|
244
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
370
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
245
371
|
const expectedText = step.value || `\${${step.dataRef}}`;
|
|
246
372
|
const code = context.templateEngine.renderStep('contain-text-assertion', {
|
|
247
373
|
...resolved,
|
|
@@ -259,7 +385,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
259
385
|
matcher: (step: ParsedStep) =>
|
|
260
386
|
step.text.includes('should have text') && !!step.selectorRef && !!(step.value || step.dataRef),
|
|
261
387
|
generator: (step, context) => {
|
|
262
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
388
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
263
389
|
const expectedText = step.value || `\${${step.dataRef}}`;
|
|
264
390
|
const code = context.templateEngine.renderStep('have-text-assertion', {
|
|
265
391
|
...resolved,
|
|
@@ -277,7 +403,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
277
403
|
matcher: (step: ParsedStep) =>
|
|
278
404
|
step.text.includes('should be empty') && !!step.selectorRef,
|
|
279
405
|
generator: (step, context) => {
|
|
280
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
406
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
281
407
|
const code = context.templateEngine.renderStep('empty-assertion', { ...resolved, selectorRef: step.selectorRef });
|
|
282
408
|
return {
|
|
283
409
|
code,
|
|
@@ -291,7 +417,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
291
417
|
matcher: (step: ParsedStep) =>
|
|
292
418
|
step.text.includes('should be checked') && !!step.selectorRef,
|
|
293
419
|
generator: (step, context) => {
|
|
294
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
420
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
295
421
|
const code = context.templateEngine.renderStep('checked-assertion', { ...resolved, selectorRef: step.selectorRef });
|
|
296
422
|
return {
|
|
297
423
|
code,
|
|
@@ -305,7 +431,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
305
431
|
matcher: (step: ParsedStep) =>
|
|
306
432
|
step.text.includes('should be unchecked') && !!step.selectorRef,
|
|
307
433
|
generator: (step, context) => {
|
|
308
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
434
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
309
435
|
const code = context.templateEngine.renderStep('not-checked-assertion', { ...resolved, selectorRef: step.selectorRef });
|
|
310
436
|
return {
|
|
311
437
|
code,
|
|
@@ -319,7 +445,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
319
445
|
matcher: (step: ParsedStep) =>
|
|
320
446
|
step.text.includes('should be focused') && !!step.selectorRef,
|
|
321
447
|
generator: (step, context) => {
|
|
322
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
448
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
323
449
|
const code = context.templateEngine.renderStep('focused-assertion', { ...resolved, selectorRef: step.selectorRef });
|
|
324
450
|
return {
|
|
325
451
|
code,
|
|
@@ -379,7 +505,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
379
505
|
}
|
|
380
506
|
}
|
|
381
507
|
|
|
382
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
508
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
383
509
|
const code = context.templateEngine.renderStep('list-item-count-assertion', {
|
|
384
510
|
...resolved,
|
|
385
511
|
expectedCount,
|
|
@@ -413,7 +539,7 @@ export const assertionPatterns: StepPattern[] = [
|
|
|
413
539
|
}
|
|
414
540
|
}
|
|
415
541
|
|
|
416
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
542
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
417
543
|
const code = context.templateEngine.renderStep('count-assertion', {
|
|
418
544
|
...resolved,
|
|
419
545
|
expectedCount,
|
|
@@ -7,12 +7,46 @@ import { StepPattern } from './types';
|
|
|
7
7
|
* Resolvers return metadata, templates handle framework syntax
|
|
8
8
|
*/
|
|
9
9
|
export const formPatterns: StepPattern[] = [
|
|
10
|
+
{
|
|
11
|
+
name: 'upload-file',
|
|
12
|
+
matcher: (step: ParsedStep) =>
|
|
13
|
+
(step.text.includes('fill') || step.text.includes('fills')) &&
|
|
14
|
+
step.elementType === 'uploader' &&
|
|
15
|
+
!!step.selectorRef &&
|
|
16
|
+
!!(step.dataRef || step.value),
|
|
17
|
+
generator: (step, context) => {
|
|
18
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
19
|
+
|
|
20
|
+
let fileName: string;
|
|
21
|
+
if (step.dataRef) {
|
|
22
|
+
try {
|
|
23
|
+
fileName = context.dataResolver.resolveData(step.dataRef, context.featureName);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
fileName = `\${${step.dataRef}}`;
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
fileName = step.value!;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const code = context.templateEngine.renderStep('upload-action', {
|
|
32
|
+
...resolved,
|
|
33
|
+
selectorRef: step.selectorRef,
|
|
34
|
+
fileName,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
code,
|
|
39
|
+
comment: `Upload file ${fileName} via ${step.selectorRef}`,
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
priority: 12,
|
|
43
|
+
},
|
|
10
44
|
{
|
|
11
45
|
name: 'fill-input',
|
|
12
46
|
matcher: (step: ParsedStep) =>
|
|
13
47
|
(step.text.includes('fills') || step.text.includes('fill') || step.text.includes('inputs') || step.text.includes('input')) && !!step.selectorRef && !!(step.dataRef || step.value),
|
|
14
48
|
generator: (step, context) => {
|
|
15
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
49
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
16
50
|
|
|
17
51
|
// Resolve data reference to actual value
|
|
18
52
|
let value: string;
|
|
@@ -46,7 +80,7 @@ export const formPatterns: StepPattern[] = [
|
|
|
46
80
|
matcher: (step: ParsedStep) =>
|
|
47
81
|
step.text.includes('checks') && !!step.selectorRef,
|
|
48
82
|
generator: (step, context) => {
|
|
49
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
83
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
50
84
|
|
|
51
85
|
const code = context.templateEngine.renderStep('check-action', { ...resolved, selectorRef: step.selectorRef });
|
|
52
86
|
|
|
@@ -62,7 +96,7 @@ export const formPatterns: StepPattern[] = [
|
|
|
62
96
|
matcher: (step: ParsedStep) =>
|
|
63
97
|
step.text.includes('unchecks') && !!step.selectorRef,
|
|
64
98
|
generator: (step, context) => {
|
|
65
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
99
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
66
100
|
|
|
67
101
|
const code = context.templateEngine.renderStep('uncheck-action', { ...resolved, selectorRef: step.selectorRef });
|
|
68
102
|
|
|
@@ -80,7 +114,7 @@ export const formPatterns: StepPattern[] = [
|
|
|
80
114
|
!!step.selectorRef &&
|
|
81
115
|
!!(step.dataRef || step.value),
|
|
82
116
|
generator: (step, context) => {
|
|
83
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
117
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
84
118
|
|
|
85
119
|
// Resolve data reference to actual value
|
|
86
120
|
let value: string;
|
|
@@ -130,7 +164,7 @@ export const formPatterns: StepPattern[] = [
|
|
|
130
164
|
matcher: (step: ParsedStep) =>
|
|
131
165
|
step.text.includes('clears') && !!step.selectorRef,
|
|
132
166
|
generator: (step, context) => {
|
|
133
|
-
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType);
|
|
167
|
+
const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
|
|
134
168
|
|
|
135
169
|
const code = context.templateEngine.renderStep('clear-action', { ...resolved, selectorRef: step.selectorRef });
|
|
136
170
|
|