@sun-asterisk/sungen 3.1.2-beta.93 → 3.1.2-beta.98

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.
Files changed (150) hide show
  1. package/README.md +4 -428
  2. package/dist/capabilities/builtins.d.ts +31 -0
  3. package/dist/capabilities/builtins.d.ts.map +1 -0
  4. package/dist/capabilities/builtins.js +84 -0
  5. package/dist/capabilities/builtins.js.map +1 -0
  6. package/dist/capabilities/context-router.d.ts +34 -0
  7. package/dist/capabilities/context-router.d.ts.map +1 -0
  8. package/dist/capabilities/context-router.js +49 -0
  9. package/dist/capabilities/context-router.js.map +1 -0
  10. package/dist/capabilities/context.d.ts +51 -0
  11. package/dist/capabilities/context.d.ts.map +1 -0
  12. package/dist/capabilities/context.js +17 -0
  13. package/dist/capabilities/context.js.map +1 -0
  14. package/dist/capabilities/discover.d.ts +2 -0
  15. package/dist/capabilities/discover.d.ts.map +1 -0
  16. package/dist/capabilities/discover.js +48 -0
  17. package/dist/capabilities/discover.js.map +1 -0
  18. package/dist/capabilities/registry.d.ts +90 -0
  19. package/dist/capabilities/registry.d.ts.map +1 -0
  20. package/dist/capabilities/registry.js +43 -0
  21. package/dist/capabilities/registry.js.map +1 -0
  22. package/dist/capabilities/sensor.d.ts +49 -0
  23. package/dist/capabilities/sensor.d.ts.map +1 -0
  24. package/dist/capabilities/sensor.js +3 -0
  25. package/dist/capabilities/sensor.js.map +1 -0
  26. package/dist/cli/commands/generate.d.ts.map +1 -1
  27. package/dist/cli/commands/generate.js +7 -3
  28. package/dist/cli/commands/generate.js.map +1 -1
  29. package/dist/cli/index.js +10 -1
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/generators/test-generator/adapters/adapter-interface.d.ts +1 -0
  32. package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
  33. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +1 -0
  34. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts.map +1 -1
  35. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js.map +1 -1
  36. package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
  37. package/dist/generators/test-generator/code-generator.d.ts +11 -9
  38. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  39. package/dist/generators/test-generator/code-generator.js +53 -76
  40. package/dist/generators/test-generator/code-generator.js.map +1 -1
  41. package/dist/generators/test-generator/patterns/index.d.ts +0 -10
  42. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  43. package/dist/generators/test-generator/patterns/index.js +10 -47
  44. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  45. package/dist/generators/test-generator/template-engine.d.ts +1 -0
  46. package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
  47. package/dist/generators/test-generator/template-engine.js +1 -1
  48. package/dist/generators/test-generator/template-engine.js.map +1 -1
  49. package/dist/harness/annotation-overrides.d.ts +9 -0
  50. package/dist/harness/annotation-overrides.d.ts.map +1 -0
  51. package/dist/harness/annotation-overrides.js +36 -0
  52. package/dist/harness/annotation-overrides.js.map +1 -0
  53. package/dist/harness/audit.d.ts.map +1 -1
  54. package/dist/harness/audit.js +35 -7
  55. package/dist/harness/audit.js.map +1 -1
  56. package/dist/harness/catalog/drivers.yaml +35 -12
  57. package/dist/harness/parse.d.ts +1 -0
  58. package/dist/harness/parse.d.ts.map +1 -1
  59. package/dist/harness/parse.js +13 -4
  60. package/dist/harness/parse.js.map +1 -1
  61. package/dist/index.d.ts +20 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +32 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +1 -0
  66. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +1 -0
  67. package/dist/orchestrator/templates/specs-api.d.ts +19 -0
  68. package/dist/orchestrator/templates/specs-api.d.ts.map +1 -0
  69. package/dist/orchestrator/templates/specs-api.js +128 -0
  70. package/dist/orchestrator/templates/specs-api.js.map +1 -0
  71. package/dist/orchestrator/templates/specs-api.ts +101 -0
  72. package/package.json +7 -30
  73. package/src/capabilities/builtins.ts +85 -0
  74. package/src/capabilities/context-router.ts +66 -0
  75. package/src/capabilities/context.ts +46 -0
  76. package/src/capabilities/discover.ts +42 -0
  77. package/src/capabilities/registry.ts +111 -0
  78. package/src/capabilities/sensor.ts +47 -0
  79. package/src/cli/commands/generate.ts +7 -3
  80. package/src/cli/index.ts +10 -1
  81. package/src/generators/test-generator/adapters/adapter-interface.ts +1 -1
  82. package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +1 -1
  83. package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
  84. package/src/generators/test-generator/code-generator.ts +51 -74
  85. package/src/generators/test-generator/patterns/index.ts +9 -35
  86. package/src/generators/test-generator/template-engine.ts +2 -2
  87. package/src/harness/annotation-overrides.ts +25 -0
  88. package/src/harness/audit.ts +37 -8
  89. package/src/harness/catalog/drivers.yaml +35 -12
  90. package/src/harness/parse.ts +7 -2
  91. package/src/index.ts +30 -0
  92. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +1 -0
  93. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +1 -0
  94. package/src/orchestrator/templates/specs-api.ts +101 -0
  95. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts +0 -7
  96. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +0 -1
  97. package/dist/generators/test-generator/patterns/assertion-patterns.js +0 -626
  98. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +0 -1
  99. package/dist/generators/test-generator/patterns/capture-patterns.d.ts +0 -21
  100. package/dist/generators/test-generator/patterns/capture-patterns.d.ts.map +0 -1
  101. package/dist/generators/test-generator/patterns/capture-patterns.js +0 -87
  102. package/dist/generators/test-generator/patterns/capture-patterns.js.map +0 -1
  103. package/dist/generators/test-generator/patterns/database-patterns.d.ts +0 -6
  104. package/dist/generators/test-generator/patterns/database-patterns.d.ts.map +0 -1
  105. package/dist/generators/test-generator/patterns/database-patterns.js +0 -95
  106. package/dist/generators/test-generator/patterns/database-patterns.js.map +0 -1
  107. package/dist/generators/test-generator/patterns/form-patterns.d.ts +0 -6
  108. package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +0 -1
  109. package/dist/generators/test-generator/patterns/form-patterns.js +0 -160
  110. package/dist/generators/test-generator/patterns/form-patterns.js.map +0 -1
  111. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts +0 -6
  112. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +0 -1
  113. package/dist/generators/test-generator/patterns/interaction-patterns.js +0 -433
  114. package/dist/generators/test-generator/patterns/interaction-patterns.js.map +0 -1
  115. package/dist/generators/test-generator/patterns/keyboard-patterns.d.ts +0 -7
  116. package/dist/generators/test-generator/patterns/keyboard-patterns.d.ts.map +0 -1
  117. package/dist/generators/test-generator/patterns/keyboard-patterns.js +0 -47
  118. package/dist/generators/test-generator/patterns/keyboard-patterns.js.map +0 -1
  119. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts +0 -6
  120. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +0 -1
  121. package/dist/generators/test-generator/patterns/navigation-patterns.js +0 -125
  122. package/dist/generators/test-generator/patterns/navigation-patterns.js.map +0 -1
  123. package/dist/generators/test-generator/patterns/scope-patterns.d.ts +0 -7
  124. package/dist/generators/test-generator/patterns/scope-patterns.d.ts.map +0 -1
  125. package/dist/generators/test-generator/patterns/scope-patterns.js +0 -36
  126. package/dist/generators/test-generator/patterns/scope-patterns.js.map +0 -1
  127. package/dist/generators/test-generator/patterns/scroll-patterns.d.ts +0 -7
  128. package/dist/generators/test-generator/patterns/scroll-patterns.d.ts.map +0 -1
  129. package/dist/generators/test-generator/patterns/scroll-patterns.js +0 -25
  130. package/dist/generators/test-generator/patterns/scroll-patterns.js.map +0 -1
  131. package/dist/generators/test-generator/patterns/setup-patterns.d.ts +0 -6
  132. package/dist/generators/test-generator/patterns/setup-patterns.d.ts.map +0 -1
  133. package/dist/generators/test-generator/patterns/setup-patterns.js +0 -72
  134. package/dist/generators/test-generator/patterns/setup-patterns.js.map +0 -1
  135. package/dist/generators/test-generator/patterns/table-patterns.d.ts +0 -19
  136. package/dist/generators/test-generator/patterns/table-patterns.d.ts.map +0 -1
  137. package/dist/generators/test-generator/patterns/table-patterns.js +0 -239
  138. package/dist/generators/test-generator/patterns/table-patterns.js.map +0 -1
  139. package/docs/orchestration-spec.md +0 -267
  140. package/src/generators/test-generator/patterns/assertion-patterns.ts +0 -691
  141. package/src/generators/test-generator/patterns/capture-patterns.ts +0 -97
  142. package/src/generators/test-generator/patterns/database-patterns.ts +0 -96
  143. package/src/generators/test-generator/patterns/form-patterns.ts +0 -167
  144. package/src/generators/test-generator/patterns/interaction-patterns.ts +0 -465
  145. package/src/generators/test-generator/patterns/keyboard-patterns.ts +0 -51
  146. package/src/generators/test-generator/patterns/navigation-patterns.ts +0 -140
  147. package/src/generators/test-generator/patterns/scope-patterns.ts +0 -40
  148. package/src/generators/test-generator/patterns/scroll-patterns.ts +0 -27
  149. package/src/generators/test-generator/patterns/setup-patterns.ts +0 -76
  150. package/src/generators/test-generator/patterns/table-patterns.ts +0 -279
@@ -1,97 +0,0 @@
1
- import { ParsedStep } from '../../gherkin-parser';
2
- import { StepPattern, StepTemplateData } from './types';
3
-
4
- /**
5
- * Capture & collection patterns (P5) — enable cross-screen data consistency and
6
- * filter-result correctness that plain single-element assertions can't express.
7
- *
8
- * 1. Capture: `User remember [Product Name] text as {{selected_product_name}}`
9
- * → stores the element's text/value into a runtime variable so a
10
- * later step (on another screen) can assert against it.
11
- * REQUIRES runtime data mode (default) — emits `testData.set(...)`.
12
- *
13
- * 2. List: `User see all [Product Name] contain {{selected_category}}`
14
- * → asserts EVERY matching element's text contains the value
15
- * (e.g. all products belong to the selected category/brand).
16
- *
17
- * 3. All-card: `User see all [Product Card] contain [Add To Cart] button`
18
- * → asserts EVERY container element holds the child element
19
- * (structural per-card proof: every card has name/price/action).
20
- * Roadmap Q2 — the fix for "each card exposes X" claims.
21
- */
22
- export const capturePatterns: StepPattern[] = [
23
- {
24
- name: 'capture-variable',
25
- matcher: (step: ParsedStep) =>
26
- /\bremember\b/i.test(step.text) && /\bas\b/i.test(step.text) && !!step.selectorRef && !!step.dataRef,
27
- resolver: (step, context): StepTemplateData => {
28
- const resolved = context.selectorResolver.resolveSelector(
29
- step.selectorRef!, undefined, step.elementType, step.nth,
30
- );
31
- const varName = step.dataRef!;
32
- const isValue = /\bvalue\b/i.test(step.text);
33
- // Register so later `{{varName}}` references resolve to testData.get(varName)
34
- // and skip YAML validation.
35
- context.dataResolver.registerCaptured(varName);
36
- return {
37
- templateName: 'capture-variable',
38
- data: { ...resolved, varName, capture: isValue ? 'inputValue' : 'textContent' },
39
- comment: `Remember ${step.selectorRef} ${isValue ? 'value' : 'text'} as ${varName}`,
40
- };
41
- },
42
- priority: 35,
43
- },
44
- {
45
- name: 'all-contain-assertion',
46
- matcher: (step: ParsedStep) =>
47
- /\b(see|sees)\b/i.test(step.text) &&
48
- /\ball\b/i.test(step.text) &&
49
- /(contain|contains|match|matches|belong)/i.test(step.text) &&
50
- !!step.selectorRef && !!(step.value || step.dataRef),
51
- resolver: (step, context): StepTemplateData => {
52
- const resolved = context.selectorResolver.resolveSelector(
53
- step.selectorRef!, undefined, step.elementType, step.nth,
54
- );
55
- const expectedText = step.value || context.dataResolver.resolveData(step.dataRef!, context.featureName);
56
- return {
57
- templateName: 'all-contain-assertion',
58
- data: { ...resolved, expectedText, selectorRef: step.selectorRef, stepCounter: context.stepCounter },
59
- comment: `Assert all ${step.selectorRef} contain "${step.value || step.dataRef}"`,
60
- };
61
- },
62
- priority: 34,
63
- },
64
- {
65
- // Q2 — all-card structural assertion: every container holds the child element.
66
- // Distinguished from `all-contain-assertion` by a second [ref] (childRef) and the
67
- // ABSENCE of a value/data (which would make it a text-contains assertion instead).
68
- name: 'all-contain-element',
69
- matcher: (step: ParsedStep) =>
70
- /\b(see|sees)\b/i.test(step.text) &&
71
- /\ball\b/i.test(step.text) &&
72
- /(contain|contains|include|includes)/i.test(step.text) &&
73
- !!step.selectorRef && !!step.childRef && !step.value && !step.dataRef,
74
- resolver: (step, context): StepTemplateData => {
75
- const container = context.selectorResolver.resolveSelector(
76
- step.selectorRef!, undefined, step.elementType, step.nth,
77
- );
78
- const child = context.selectorResolver.resolveSelector(
79
- step.childRef!, undefined, step.childElementType, 0,
80
- );
81
- const sc = context.stepCounter;
82
- return {
83
- templateName: 'all-contain-element',
84
- data: {
85
- ...container,
86
- selectorRef: step.selectorRef,
87
- childRef: step.childRef,
88
- stepCounter: sc,
89
- // Render the child RELATIVE to each container row via locator.hbs's parentLocator branch.
90
- child: { ...child, parentLocator: `__cards_${sc}.nth(__i_${sc})` },
91
- },
92
- comment: `Assert all ${step.selectorRef} contain [${step.childRef}]`,
93
- };
94
- },
95
- priority: 36,
96
- },
97
- ];
@@ -1,96 +0,0 @@
1
- import { ParsedStep } from '../../gherkin-parser';
2
- import { StepPattern, PatternContext } from './types';
3
- import { MappedStep } from '../step-mapper';
4
-
5
- /**
6
- * Database verification patterns (Data Driver v1) — declarative, no-SQL DB assertions
7
- * that compile to calls on the runtime `db` helper (specs/db.ts). Read-only.
8
- *
9
- * User see [users] row where [email] is {{reg_email}}
10
- * User see [users] row where [email] is {{reg_email}} has [status] = "active"
11
- * User see [users] no row where [email] is {{dup_email}}
12
- * User see [orders] where [buyer] is {{buyer}} count is {{expected_count}}
13
- *
14
- * Identifiers ([table]/[column]) are validated by the helper; values bind as parameters.
15
- */
16
-
17
- const TABLE = String.raw`\[([A-Za-z_][A-Za-z0-9_]*)\]`;
18
- const VALUE = String.raw`\{\{[^}]+\}\}|"[^"]*"|'[^']*'|-?\d+(?:\.\d+)?`;
19
-
20
- const reRow = new RegExp(`see\\s+${TABLE}\\s+row\\s+where\\b`, 'i');
21
- const reNoRow = new RegExp(`see\\s+${TABLE}\\s+no\\s+row\\s+where\\b`, 'i');
22
- const reCount = new RegExp(`see\\s+${TABLE}.*\\bcount\\s+is\\b`, 'i');
23
-
24
- /** True when a step is a declarative DB-verification step (used to wire the `db` import).
25
- * Named queries are invoked via the `@query:` annotation, not a step — see code-generator. */
26
- export function isDbStep(text: string): boolean {
27
- return reNoRow.test(text) || reRow.test(text) || reCount.test(text);
28
- }
29
-
30
- /** Render a value token (`{{var}}` | "literal" | 'literal' | number) as a JS expression. */
31
- function valueExpr(token: string): string {
32
- const t = token.trim();
33
- const v = t.match(/^\{\{\s*([^}]+?)\s*\}\}$/);
34
- if (v) return `testData.get(${JSON.stringify(v[1])})`;
35
- const q = t.match(/^["'](.*)["']$/);
36
- if (q) return JSON.stringify(q[1]);
37
- if (/^-?\d+(?:\.\d+)?$/.test(t)) return t;
38
- return JSON.stringify(t);
39
- }
40
-
41
- /** Parse a `[col] is VALUE [and [col2] is VALUE2]` segment into a JS object literal. */
42
- function parseFilter(segment: string): string {
43
- const re = new RegExp(`\\[([A-Za-z_][A-Za-z0-9_]*)\\]\\s+is\\s+(${VALUE})`, 'gi');
44
- const parts: string[] = [];
45
- let m: RegExpExecArray | null;
46
- while ((m = re.exec(segment))) parts.push(`${JSON.stringify(m[1])}: ${valueExpr(m[2])}`);
47
- return `{ ${parts.join(', ')} }`;
48
- }
49
-
50
- /** Parse a `has [col] = VALUE [and [col2] = VALUE2]` segment into a JS object literal. */
51
- function parseExpected(segment: string): string {
52
- const re = new RegExp(`\\[([A-Za-z_][A-Za-z0-9_]*)\\]\\s*=\\s*(${VALUE})`, 'gi');
53
- const parts: string[] = [];
54
- let m: RegExpExecArray | null;
55
- while ((m = re.exec(segment))) parts.push(`${JSON.stringify(m[1])}: ${valueExpr(m[2])}`);
56
- return parts.length ? `{ ${parts.join(', ')} }` : '';
57
- }
58
-
59
- export const databasePatterns: StepPattern[] = [
60
- {
61
- name: 'db-no-row',
62
- priority: 60, // above generic see-assertions
63
- matcher: (step: ParsedStep) => reNoRow.test(step.text),
64
- generator: (step: ParsedStep, _ctx: PatternContext): MappedStep => {
65
- const m = step.text.match(new RegExp(`${TABLE}\\s+no\\s+row\\s+where\\s+(.+)$`, 'i'))!;
66
- const table = m[1];
67
- const filter = parseFilter(m[2]);
68
- return { code: `await db.assertNoRow(${JSON.stringify(table)}, ${filter});`, comment: `DB: no row in ${table}` };
69
- },
70
- },
71
- {
72
- name: 'db-count',
73
- priority: 60,
74
- matcher: (step: ParsedStep) => reCount.test(step.text) && !reRow.test(step.text),
75
- generator: (step: ParsedStep, _ctx: PatternContext): MappedStep => {
76
- const m = step.text.match(new RegExp(`${TABLE}(?:\\s+where\\s+(.+?))?\\s+count\\s+is\\s+(${VALUE})`, 'i'))!;
77
- const table = m[1];
78
- const filter = m[2] ? parseFilter(m[2]) : '{}';
79
- return { code: `await db.assertCount(${JSON.stringify(table)}, ${filter}, Number(${valueExpr(m[3])}));`, comment: `DB: count rows in ${table}` };
80
- },
81
- },
82
- {
83
- name: 'db-row',
84
- priority: 60,
85
- matcher: (step: ParsedStep) => reRow.test(step.text),
86
- generator: (step: ParsedStep, _ctx: PatternContext): MappedStep => {
87
- // [table] row where <filter> [has <expected>]
88
- const m = step.text.match(new RegExp(`${TABLE}\\s+row\\s+where\\s+(.+?)(?:\\s+has\\s+(.+))?$`, 'i'))!;
89
- const table = m[1];
90
- const filter = parseFilter(m[2]);
91
- const expected = m[3] ? parseExpected(m[3]) : '';
92
- const args = expected ? `${JSON.stringify(table)}, ${filter}, ${expected}` : `${JSON.stringify(table)}, ${filter}`;
93
- return { code: `await db.assertRow(${args});`, comment: `DB: row in ${table}` };
94
- },
95
- },
96
- ];
@@ -1,167 +0,0 @@
1
- import { ParsedStep } from '../../gherkin-parser';
2
- import { StepPattern, StepTemplateData } from './types';
3
-
4
- /**
5
- * Form action patterns: fill, type, select, check, uncheck
6
- */
7
- export const formPatterns: StepPattern[] = [
8
- {
9
- name: 'upload-file',
10
- matcher: (step: ParsedStep) =>
11
- (step.text.includes('fill') || step.text.includes('fills')) &&
12
- step.elementType === 'uploader' &&
13
- !!step.selectorRef &&
14
- !!(step.dataRef || step.value),
15
- resolver: (step, context) => {
16
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
17
-
18
- let fileName: string;
19
- if (step.dataRef) {
20
- try {
21
- fileName = context.dataResolver.resolveData(step.dataRef, context.featureName);
22
- } catch (error) {
23
- fileName = `\${${step.dataRef}}`;
24
- }
25
- } else {
26
- fileName = step.value!;
27
- }
28
-
29
- return {
30
- templateName: 'upload-action',
31
- data: { ...resolved, selectorRef: step.selectorRef, fileName },
32
- comment: `Upload file ${fileName} via ${step.selectorRef}`,
33
- };
34
- },
35
- priority: 12,
36
- },
37
- {
38
- name: 'fill-input',
39
- matcher: (step: ParsedStep) =>
40
- (step.text.includes('fills') || step.text.includes('fill') || step.text.includes('inputs') || step.text.includes('input')) && !!step.selectorRef && !!(step.dataRef || step.value),
41
- resolver: (step, context) => {
42
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
43
-
44
- let value: string;
45
- if (step.dataRef) {
46
- try {
47
- value = context.dataResolver.resolveData(step.dataRef, context.featureName);
48
- } catch (error) {
49
- value = `\${${step.dataRef}}`;
50
- }
51
- } else {
52
- value = step.value!;
53
- }
54
-
55
- // Use pressSequentially template for contenteditable/rich-text editors (auto-detected by live-scan)
56
- // or when Gherkin explicitly uses 'editor' element type
57
- const isEditor = resolved.inputMethod === 'pressSequentially' || step.elementType === 'editor';
58
- const templateName = isEditor ? 'fill-editor-action' : 'fill-action';
59
-
60
- return {
61
- templateName,
62
- data: { ...resolved, selectorRef: step.selectorRef, fillValue: value },
63
- comment: isEditor
64
- ? `Fill rich text editor ${step.selectorRef} with ${step.dataRef || step.value}`
65
- : `Fill ${step.selectorRef} with ${step.dataRef || step.value}`,
66
- };
67
- },
68
- priority: 10,
69
- },
70
- {
71
- name: 'check-checkbox',
72
- matcher: (step: ParsedStep) =>
73
- (step.text.includes('checks') || step.text.match(/\bcheck\b/)) && !!step.selectorRef,
74
- resolver: (step, context) => {
75
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
76
- return {
77
- templateName: 'check-action',
78
- data: { ...resolved, selectorRef: step.selectorRef },
79
- comment: `Check ${step.selectorRef}`,
80
- };
81
- },
82
- priority: 8,
83
- },
84
- {
85
- name: 'uncheck-checkbox',
86
- matcher: (step: ParsedStep) =>
87
- (step.text.includes('unchecks') || step.text.match(/\buncheck\b/)) && !!step.selectorRef,
88
- resolver: (step, context) => {
89
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
90
- return {
91
- templateName: 'uncheck-action',
92
- data: { ...resolved, selectorRef: step.selectorRef },
93
- comment: `Uncheck ${step.selectorRef}`,
94
- };
95
- },
96
- priority: 8,
97
- },
98
- {
99
- name: 'select-dropdown',
100
- matcher: (step: ParsedStep) =>
101
- (step.text.includes('selects') || step.text.match(/\bselect\b/)) &&
102
- !!step.selectorRef &&
103
- !!(step.dataRef || step.value),
104
- resolver: (step, context): StepTemplateData => {
105
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
106
-
107
- let value: string;
108
- if (step.dataRef) {
109
- try {
110
- value = context.dataResolver.resolveData(step.dataRef, context.featureName);
111
- } catch (error) {
112
- value = `\${${step.dataRef}}`;
113
- }
114
- } else {
115
- value = step.value!;
116
- }
117
-
118
- const isRadio = resolved.role === 'radio' || step.text.includes('radio');
119
- const isCheckbox = resolved.role === 'checkbox' || step.text.includes('checkbox');
120
-
121
- let templateName: string;
122
- let data: Record<string, any>;
123
-
124
- if (isRadio) {
125
- templateName = 'radio-select-action';
126
- data = { ...resolved, selectorRef: step.selectorRef, selectValue: value };
127
- } else if (isCheckbox) {
128
- templateName = 'check-action';
129
- data = { ...resolved, selectorRef: step.selectorRef };
130
- } else {
131
- const nativeSelectRoles = ['combobox', 'listbox', 'select'];
132
- // A native <select> is detected by its ARIA role, or by an explicit `variant: native`
133
- // on the selector entry (lets a CSS/#id-located select opt into .selectOption()).
134
- const isNativeSelect = nativeSelectRoles.includes(resolved.role) || resolved.variant === 'native';
135
-
136
- if (isNativeSelect) {
137
- templateName = 'select-action';
138
- data = { ...resolved, selectorRef: step.selectorRef, selectValue: value };
139
- } else {
140
- templateName = 'click-select-action';
141
- data = { ...resolved, selectorRef: step.selectorRef, selectValue: value };
142
- }
143
- }
144
-
145
- return {
146
- templateName,
147
- data,
148
- comment: `Select ${step.dataRef || step.value} in ${step.selectorRef}`,
149
- };
150
- },
151
- priority: 8,
152
- },
153
- {
154
- name: 'clear-input',
155
- matcher: (step: ParsedStep) =>
156
- (step.text.includes('clears') || step.text.match(/\bclear\b/)) && !!step.selectorRef,
157
- resolver: (step, context) => {
158
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef!, undefined, step.elementType, step.nth);
159
- return {
160
- templateName: 'clear-action',
161
- data: { ...resolved, selectorRef: step.selectorRef },
162
- comment: `Clear ${step.selectorRef}`,
163
- };
164
- },
165
- priority: 7,
166
- },
167
- ];