@sun-asterisk/sungen 3.1.1 → 3.1.2-beta.101

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 (162) 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/exporters/spec-parser.d.ts.map +1 -1
  32. package/dist/exporters/spec-parser.js +4 -1
  33. package/dist/exporters/spec-parser.js.map +1 -1
  34. package/dist/generators/test-generator/adapters/adapter-interface.d.ts +1 -0
  35. package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
  36. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +1 -0
  37. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts.map +1 -1
  38. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js.map +1 -1
  39. package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
  40. package/dist/generators/test-generator/code-generator.d.ts +18 -9
  41. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  42. package/dist/generators/test-generator/code-generator.js +76 -119
  43. package/dist/generators/test-generator/code-generator.js.map +1 -1
  44. package/dist/generators/test-generator/patterns/index.d.ts +0 -10
  45. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  46. package/dist/generators/test-generator/patterns/index.js +10 -47
  47. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  48. package/dist/generators/test-generator/template-engine.d.ts +1 -0
  49. package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
  50. package/dist/generators/test-generator/template-engine.js +1 -1
  51. package/dist/generators/test-generator/template-engine.js.map +1 -1
  52. package/dist/harness/annotation-overrides.d.ts +9 -0
  53. package/dist/harness/annotation-overrides.d.ts.map +1 -0
  54. package/dist/harness/annotation-overrides.js +36 -0
  55. package/dist/harness/annotation-overrides.js.map +1 -0
  56. package/dist/harness/audit.d.ts.map +1 -1
  57. package/dist/harness/audit.js +35 -7
  58. package/dist/harness/audit.js.map +1 -1
  59. package/dist/harness/catalog/drivers.yaml +35 -12
  60. package/dist/harness/parse.d.ts +1 -0
  61. package/dist/harness/parse.d.ts.map +1 -1
  62. package/dist/harness/parse.js +13 -4
  63. package/dist/harness/parse.js.map +1 -1
  64. package/dist/index.d.ts +20 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +32 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +4 -3
  69. package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +1 -0
  70. package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +3 -0
  71. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +4 -3
  72. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +1 -0
  73. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +3 -0
  74. package/dist/orchestrator/templates/specs-api.d.ts +19 -0
  75. package/dist/orchestrator/templates/specs-api.d.ts.map +1 -0
  76. package/dist/orchestrator/templates/specs-api.js +128 -0
  77. package/dist/orchestrator/templates/specs-api.js.map +1 -0
  78. package/dist/orchestrator/templates/specs-api.ts +101 -0
  79. package/package.json +7 -30
  80. package/src/capabilities/builtins.ts +85 -0
  81. package/src/capabilities/context-router.ts +66 -0
  82. package/src/capabilities/context.ts +46 -0
  83. package/src/capabilities/discover.ts +42 -0
  84. package/src/capabilities/registry.ts +111 -0
  85. package/src/capabilities/sensor.ts +47 -0
  86. package/src/cli/commands/generate.ts +7 -3
  87. package/src/cli/index.ts +10 -1
  88. package/src/exporters/spec-parser.ts +4 -1
  89. package/src/generators/test-generator/adapters/adapter-interface.ts +1 -1
  90. package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +1 -1
  91. package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
  92. package/src/generators/test-generator/code-generator.ts +71 -118
  93. package/src/generators/test-generator/patterns/index.ts +9 -35
  94. package/src/generators/test-generator/template-engine.ts +2 -2
  95. package/src/harness/annotation-overrides.ts +25 -0
  96. package/src/harness/audit.ts +37 -8
  97. package/src/harness/catalog/drivers.yaml +35 -12
  98. package/src/harness/parse.ts +7 -2
  99. package/src/index.ts +30 -0
  100. package/src/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +4 -3
  101. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +1 -0
  102. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +3 -0
  103. package/src/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +4 -3
  104. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +1 -0
  105. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +3 -0
  106. package/src/orchestrator/templates/specs-api.ts +101 -0
  107. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts +0 -7
  108. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +0 -1
  109. package/dist/generators/test-generator/patterns/assertion-patterns.js +0 -626
  110. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +0 -1
  111. package/dist/generators/test-generator/patterns/capture-patterns.d.ts +0 -21
  112. package/dist/generators/test-generator/patterns/capture-patterns.d.ts.map +0 -1
  113. package/dist/generators/test-generator/patterns/capture-patterns.js +0 -87
  114. package/dist/generators/test-generator/patterns/capture-patterns.js.map +0 -1
  115. package/dist/generators/test-generator/patterns/database-patterns.d.ts +0 -6
  116. package/dist/generators/test-generator/patterns/database-patterns.d.ts.map +0 -1
  117. package/dist/generators/test-generator/patterns/database-patterns.js +0 -95
  118. package/dist/generators/test-generator/patterns/database-patterns.js.map +0 -1
  119. package/dist/generators/test-generator/patterns/form-patterns.d.ts +0 -6
  120. package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +0 -1
  121. package/dist/generators/test-generator/patterns/form-patterns.js +0 -160
  122. package/dist/generators/test-generator/patterns/form-patterns.js.map +0 -1
  123. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts +0 -6
  124. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +0 -1
  125. package/dist/generators/test-generator/patterns/interaction-patterns.js +0 -433
  126. package/dist/generators/test-generator/patterns/interaction-patterns.js.map +0 -1
  127. package/dist/generators/test-generator/patterns/keyboard-patterns.d.ts +0 -7
  128. package/dist/generators/test-generator/patterns/keyboard-patterns.d.ts.map +0 -1
  129. package/dist/generators/test-generator/patterns/keyboard-patterns.js +0 -47
  130. package/dist/generators/test-generator/patterns/keyboard-patterns.js.map +0 -1
  131. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts +0 -6
  132. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +0 -1
  133. package/dist/generators/test-generator/patterns/navigation-patterns.js +0 -125
  134. package/dist/generators/test-generator/patterns/navigation-patterns.js.map +0 -1
  135. package/dist/generators/test-generator/patterns/scope-patterns.d.ts +0 -7
  136. package/dist/generators/test-generator/patterns/scope-patterns.d.ts.map +0 -1
  137. package/dist/generators/test-generator/patterns/scope-patterns.js +0 -36
  138. package/dist/generators/test-generator/patterns/scope-patterns.js.map +0 -1
  139. package/dist/generators/test-generator/patterns/scroll-patterns.d.ts +0 -7
  140. package/dist/generators/test-generator/patterns/scroll-patterns.d.ts.map +0 -1
  141. package/dist/generators/test-generator/patterns/scroll-patterns.js +0 -25
  142. package/dist/generators/test-generator/patterns/scroll-patterns.js.map +0 -1
  143. package/dist/generators/test-generator/patterns/setup-patterns.d.ts +0 -6
  144. package/dist/generators/test-generator/patterns/setup-patterns.d.ts.map +0 -1
  145. package/dist/generators/test-generator/patterns/setup-patterns.js +0 -72
  146. package/dist/generators/test-generator/patterns/setup-patterns.js.map +0 -1
  147. package/dist/generators/test-generator/patterns/table-patterns.d.ts +0 -19
  148. package/dist/generators/test-generator/patterns/table-patterns.d.ts.map +0 -1
  149. package/dist/generators/test-generator/patterns/table-patterns.js +0 -239
  150. package/dist/generators/test-generator/patterns/table-patterns.js.map +0 -1
  151. package/docs/orchestration-spec.md +0 -267
  152. package/src/generators/test-generator/patterns/assertion-patterns.ts +0 -691
  153. package/src/generators/test-generator/patterns/capture-patterns.ts +0 -97
  154. package/src/generators/test-generator/patterns/database-patterns.ts +0 -96
  155. package/src/generators/test-generator/patterns/form-patterns.ts +0 -167
  156. package/src/generators/test-generator/patterns/interaction-patterns.ts +0 -465
  157. package/src/generators/test-generator/patterns/keyboard-patterns.ts +0 -51
  158. package/src/generators/test-generator/patterns/navigation-patterns.ts +0 -140
  159. package/src/generators/test-generator/patterns/scope-patterns.ts +0 -40
  160. package/src/generators/test-generator/patterns/scroll-patterns.ts +0 -27
  161. package/src/generators/test-generator/patterns/setup-patterns.ts +0 -76
  162. package/src/generators/test-generator/patterns/table-patterns.ts +0 -279
@@ -1,626 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.assertionPatterns = void 0;
4
- /**
5
- * Assertion patterns: visibility, text content, state, attributes
6
- * Uses template engine for framework-agnostic code generation
7
- */
8
- exports.assertionPatterns = [
9
- // Browser alert text assertion: "see [Are you sure?] alert"
10
- {
11
- name: 'alert-text-assertion',
12
- matcher: (step) => (step.text.includes('see') || step.text.includes('sees')) &&
13
- step.elementType === 'alert' &&
14
- !!step.selectorRef,
15
- resolver: (step, context) => {
16
- let dataValue = step.selectorRef || '';
17
- if (step.dataRef) {
18
- try {
19
- dataValue = context.dataResolver.resolveData(step.dataRef, context.featureName);
20
- }
21
- catch {
22
- dataValue = `\${${step.dataRef}}`;
23
- }
24
- }
25
- return {
26
- templateName: 'alert-text-assertion',
27
- data: { dataValue, stepCounter: context.stepCounter },
28
- comment: `Assert browser alert contains "${step.selectorRef}"`,
29
- };
30
- },
31
- priority: 21,
32
- },
33
- // Column cell assertion: "see [Department] column 1 with {{value}}" -> check table cell text
34
- {
35
- name: 'column-cell-assertion',
36
- matcher: (step) => (step.text.includes('should see') || step.text.match(/\b(see|sees)\s+\[/)) &&
37
- !!step.selectorRef &&
38
- !!step.dataRef &&
39
- (step.elementType === 'column' || step.elementType === 'columnheader'),
40
- resolver: (step, context) => {
41
- let dataValue;
42
- try {
43
- dataValue = context.dataResolver.resolveData(step.dataRef, context.featureName);
44
- }
45
- catch (error) {
46
- dataValue = `\${${step.dataRef}}`;
47
- }
48
- // getByRole('row') includes header row as nth(0), so Gherkin nth=1 maps to locator nth(1)
49
- const rowNth = step.nth || 1;
50
- // Create a safe variable name from column name
51
- const columnIndexVar = `colIndex${step.selectorRef.replace(/[^a-zA-Z0-9]/g, '')}`;
52
- return {
53
- templateName: 'column-cell-assertion',
54
- data: {
55
- columnName: step.selectorRef,
56
- rowNth,
57
- columnIndexVar,
58
- dataValue,
59
- },
60
- comment: `Assert ${step.selectorRef} column row ${step.nth || 1} has text ${step.dataRef}`,
61
- };
62
- },
63
- priority: 15, // Higher than all other assertion patterns
64
- },
65
- // Page assertion pattern: "see [home] page" -> check URL matches selector value
66
- {
67
- name: 'page-assertion',
68
- matcher: (step) => (step.text.includes('should see') ||
69
- step.text.includes('see') ||
70
- step.text.includes('sees')) &&
71
- step.elementType === 'page',
72
- resolver: (step, context) => {
73
- let path = step.featurePath || '/';
74
- // If selector is present, extract path from selector's value attribute
75
- if (step.selectorRef) {
76
- try {
77
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, context.featureName, step.elementType, step.nth);
78
- // Use selector's value as the path for URL assertion
79
- path = resolved.value || path;
80
- }
81
- catch (error) {
82
- // Fallback to feature path if selector not found
83
- path = step.featurePath || '/';
84
- }
85
- }
86
- // Convert path to regex-safe string
87
- const pathRegex = path.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
88
- return {
89
- templateName: 'page-assertion',
90
- data: { pathRegex },
91
- comment: step.selectorRef ? `Assert on ${step.selectorRef} page` : `Assert on page`,
92
- };
93
- },
94
- priority: 13, // Higher priority than general visibility
95
- },
96
- // Pattern: Then User see [panel] dialog with {{kudo.title}} hidden -> toBeHidden()
97
- {
98
- name: 'see-with-variable-hidden',
99
- matcher: (step) => (step.text.includes('should see') || step.text.match(/\b(see|sees)\s+\[/)) &&
100
- !!step.selectorRef &&
101
- !!step.dataRef &&
102
- step.text.includes('with') &&
103
- /\bis hidden\b/.test(step.text),
104
- generator: (step, context) => {
105
- let resolved = {};
106
- try {
107
- resolved = context.selectorResolver.resolveSelector(step.selectorRef, context.featureName, step.elementType, step.nth);
108
- }
109
- catch (error) { }
110
- let dataValue;
111
- try {
112
- dataValue = context.dataResolver.resolveData(step.dataRef, context.featureName);
113
- }
114
- catch (error) {
115
- dataValue = `\${${step.dataRef}}`;
116
- }
117
- const escapedVariable = dataValue.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
118
- if (resolved.strategy === 'locator') {
119
- const code = context.templateEngine.renderStep('hidden-with-filter-assertion', {
120
- ...resolved, dataValue, nth: resolved.nth,
121
- });
122
- return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
123
- }
124
- if (resolved.strategy === 'role' && resolved.role) {
125
- // Dialog role: check heading inside dialog instead of filter on full text content
126
- if (resolved.role === 'dialog') {
127
- const code = context.templateEngine.renderStep('hidden-dialog-heading-assertion', {
128
- dataValue,
129
- });
130
- return { code, comment: `Assert ${step.selectorRef} dialog hidden with heading ${step.dataRef}` };
131
- }
132
- const code = context.templateEngine.renderStep('hidden-with-role-variable-assertion', {
133
- role: resolved.role,
134
- name: resolved.name && resolved.name.trim() ? resolved.name : undefined,
135
- dataValue,
136
- nth: resolved.nth,
137
- });
138
- return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
139
- }
140
- const selectorValue = resolved.value || '';
141
- if (selectorValue && selectorValue.trim()) {
142
- const code = context.templateEngine.renderStep('hidden-with-filter-assertion', {
143
- ...resolved, dataValue, nth: resolved.nth,
144
- });
145
- return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
146
- }
147
- const code = context.templateEngine.renderStep('hidden-with-variable-assertion', {
148
- selectorRef: step.selectorRef,
149
- selectorValue: resolved.value || '',
150
- dataValue,
151
- nth: resolved.nth,
152
- });
153
- return { code, comment: `Assert ${step.selectorRef} hidden with ${step.dataRef}` };
154
- },
155
- priority: 14,
156
- },
157
- // Pattern: Then User see [submit] button with {{label}} disabled -> toBeDisabled()
158
- {
159
- name: 'see-with-variable-disabled',
160
- matcher: (step) => (step.text.includes('should see') || step.text.match(/\b(see|sees)\s+\[/)) &&
161
- !!step.selectorRef &&
162
- !!step.dataRef &&
163
- step.text.includes('with') &&
164
- /\bis disabled\b/.test(step.text),
165
- generator: (step, context) => {
166
- let resolved = {};
167
- try {
168
- resolved = context.selectorResolver.resolveSelector(step.selectorRef, context.featureName, step.elementType, step.nth);
169
- }
170
- catch (error) { }
171
- let dataValue;
172
- try {
173
- dataValue = context.dataResolver.resolveData(step.dataRef, context.featureName);
174
- }
175
- catch (error) {
176
- dataValue = `\${${step.dataRef}}`;
177
- }
178
- const escapedVariable = dataValue.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
179
- if (resolved.strategy === 'locator') {
180
- const code = context.templateEngine.renderStep('disabled-with-filter-assertion', {
181
- ...resolved, dataValue, nth: resolved.nth,
182
- });
183
- return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
184
- }
185
- if (resolved.strategy === 'role' && resolved.role) {
186
- const code = context.templateEngine.renderStep('disabled-with-role-variable-assertion', {
187
- role: resolved.role,
188
- name: resolved.name && resolved.name.trim() ? resolved.name : undefined,
189
- dataValue,
190
- nth: resolved.nth,
191
- });
192
- return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
193
- }
194
- const selectorValue = resolved.value || '';
195
- if (selectorValue && selectorValue.trim()) {
196
- const code = context.templateEngine.renderStep('disabled-with-filter-assertion', {
197
- ...resolved, dataValue, nth: resolved.nth,
198
- });
199
- return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
200
- }
201
- const code = context.templateEngine.renderStep('disabled-with-variable-assertion', {
202
- selectorRef: step.selectorRef,
203
- selectorValue: resolved.value || '',
204
- dataValue,
205
- nth: resolved.nth,
206
- });
207
- return { code, comment: `Assert ${step.selectorRef} disabled with ${step.dataRef}` };
208
- },
209
- priority: 14,
210
- },
211
- // NEW: Assertion with variable (handles both empty selector and static + variable)
212
- // Pattern: Then User see [error] message with {{fail_message}}
213
- // If selector YAML has empty value, uses variable-only matching
214
- // If selector has value, combines both (static text + variable)
215
- // Input types → toHaveValue, everything else → toHaveText
216
- {
217
- name: 'see-with-variable',
218
- matcher: (step) => (step.text.includes('should see') ||
219
- step.text.match(/\b(see|sees)\s+\[/)) &&
220
- !!step.selectorRef &&
221
- !!step.dataRef &&
222
- step.text.includes('with'),
223
- generator: (step, context) => {
224
- // Input element types use toHaveValue instead of toHaveText
225
- const INPUT_TYPES = new Set([
226
- 'field', 'textarea', 'search', 'dropdown', 'slider', 'date-picker',
227
- 'input', 'textbox', 'editor', 'select', 'combobox',
228
- ]);
229
- // Resolve data variable value
230
- let dataValue;
231
- try {
232
- dataValue = context.dataResolver.resolveData(step.dataRef, context.featureName);
233
- }
234
- catch (error) {
235
- dataValue = `\${${step.dataRef}}`;
236
- }
237
- // Resolve selector from YAML with feature context
238
- let resolved = {};
239
- try {
240
- resolved = context.selectorResolver.resolveSelector(step.selectorRef, context.featureName, step.elementType, step.nth);
241
- }
242
- catch (error) {
243
- // Selector not in YAML or context issue - will use variable-only
244
- }
245
- // --- Attribute assertion: toHaveAttribute ---
246
- // When selector YAML has `attribute` field (e.g., attribute: 'src', 'href')
247
- if (resolved.attribute) {
248
- const code = context.templateEngine.renderStep('attribute-assertion', {
249
- ...resolved,
250
- dataValue,
251
- });
252
- return {
253
- code,
254
- comment: `Assert ${step.selectorRef} ${resolved.attribute} matches ${step.dataRef}`,
255
- };
256
- }
257
- // --- Input types: toHaveValue ---
258
- if (step.elementType && INPUT_TYPES.has(step.elementType)) {
259
- const code = context.templateEngine.renderStep('have-value-assertion', {
260
- ...resolved,
261
- dataValue,
262
- });
263
- return {
264
- code,
265
- comment: `Assert ${step.selectorRef} has value ${step.dataRef}`,
266
- };
267
- }
268
- // --- Label-value pattern: "User see [X] label with {{Y}}" ---
269
- if (step.elementType === 'label' && step.dataRef) {
270
- let label = step.selectorRef;
271
- try {
272
- const labelResolved = context.selectorResolver.resolveSelector(step.selectorRef, context.featureName, step.elementType, step.nth);
273
- if (labelResolved.value !== undefined && labelResolved.value.trim() === '') {
274
- label = undefined;
275
- }
276
- }
277
- catch {
278
- // No selector entry — use label from selectorRef
279
- }
280
- const code = context.templateEngine.renderStep('label-value-assertion', {
281
- label,
282
- dataValue,
283
- });
284
- return {
285
- code,
286
- comment: `Assert ${step.selectorRef} label with value ${step.dataRef}`,
287
- };
288
- }
289
- // --- Dialog role: check heading inside dialog ---
290
- if (resolved.strategy === 'role' && resolved.role === 'dialog') {
291
- const code = context.templateEngine.renderStep('visible-dialog-heading-assertion', {
292
- dataValue,
293
- });
294
- return {
295
- code,
296
- comment: `Assert ${step.selectorRef} dialog with heading ${step.dataRef}`,
297
- };
298
- }
299
- // --- Image role: images have no text, use name ---
300
- if (resolved.strategy === 'role' && resolved.role === 'img') {
301
- const hasName = resolved.name && resolved.name.trim();
302
- const code = context.templateEngine.renderStep('visible-with-role-variable-assertion', {
303
- role: resolved.role,
304
- name: hasName ? resolved.name : dataValue,
305
- exact: resolved.exact || false,
306
- nth: resolved.nth,
307
- });
308
- return {
309
- code,
310
- comment: `Assert ${step.selectorRef} image with name ${step.dataRef}`,
311
- };
312
- }
313
- // --- Everything else: toHaveText (exact full match) ---
314
- // Locator strategy
315
- if (resolved.strategy === 'locator') {
316
- const code = context.templateEngine.renderStep('have-text-assertion', {
317
- ...resolved,
318
- expectedText: dataValue,
319
- });
320
- return {
321
- code,
322
- comment: `Assert ${step.selectorRef} has text ${step.dataRef}`,
323
- };
324
- }
325
- // Role-based selector
326
- if (resolved.strategy === 'role' && resolved.role) {
327
- const hasName = resolved.name && resolved.name.trim();
328
- const code = context.templateEngine.renderStep('have-text-assertion', {
329
- ...resolved,
330
- expectedText: dataValue,
331
- });
332
- return {
333
- code,
334
- comment: `Assert ${step.selectorRef} has text ${step.dataRef}`,
335
- };
336
- }
337
- // Text/placeholder/testid/other strategies
338
- const code = context.templateEngine.renderStep('have-text-assertion', {
339
- ...resolved,
340
- expectedText: dataValue,
341
- });
342
- return {
343
- code,
344
- comment: `Assert ${step.selectorRef} has text ${step.dataRef}`,
345
- };
346
- },
347
- priority: 13,
348
- },
349
- {
350
- name: 'should-be-visible',
351
- matcher: (step) => (step.text.includes('should be visible') ||
352
- step.text.includes('should be displayed') ||
353
- step.text.includes('should see') ||
354
- step.text.match(/\b(see|sees)\s+\[/)) &&
355
- !!step.selectorRef,
356
- resolver: (step, context) => {
357
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
358
- return {
359
- templateName: 'visible-assertion',
360
- data: { ...resolved, selectorRef: step.selectorRef },
361
- comment: `Assert ${step.selectorRef} is visible`,
362
- };
363
- },
364
- priority: 10,
365
- },
366
- {
367
- name: 'is-hidden',
368
- // "see [target] is hidden" — no variable version; with-variable handled by see-with-variable-hidden
369
- matcher: (step) => /\b(see|sees)\s+\[/.test(step.text) && /\bis hidden\b/.test(step.text) &&
370
- !step.dataRef && !!step.selectorRef,
371
- resolver: (step, context) => {
372
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
373
- return {
374
- templateName: 'is-hidden-assertion',
375
- data: { ...resolved, selectorRef: step.selectorRef },
376
- comment: `Assert ${step.selectorRef} is not visible`,
377
- };
378
- },
379
- priority: 11,
380
- },
381
- {
382
- name: 'should-be-enabled',
383
- matcher: (step) => /\b(see|sees)\s+\[/.test(step.text) && /\bis enabled\b/.test(step.text) && !!step.selectorRef,
384
- resolver: (step, context) => {
385
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
386
- return {
387
- templateName: 'enabled-assertion',
388
- data: { ...resolved, selectorRef: step.selectorRef },
389
- comment: `Assert ${step.selectorRef} is enabled`,
390
- };
391
- },
392
- priority: 11,
393
- },
394
- {
395
- name: 'should-be-disabled',
396
- matcher: (step) => /\b(see|sees)\s+\[/.test(step.text) && /\bis disabled\b/.test(step.text) && !!step.selectorRef,
397
- resolver: (step, context) => {
398
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
399
- return {
400
- templateName: 'disabled-assertion',
401
- data: { ...resolved, selectorRef: step.selectorRef },
402
- comment: `Assert ${step.selectorRef} is disabled`,
403
- };
404
- },
405
- priority: 11,
406
- },
407
- {
408
- name: 'should-contain-text',
409
- matcher: (step) => (step.text.includes('should contain') || step.text.includes('contains')) && !!step.selectorRef && !!(step.value || step.dataRef),
410
- resolver: (step, context) => {
411
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
412
- const expectedText = step.value || context.dataResolver.resolveData(step.dataRef, context.featureName);
413
- return {
414
- templateName: 'contain-text-assertion',
415
- data: { ...resolved, expectedText },
416
- comment: `Assert ${step.selectorRef} contains "${step.value || step.dataRef}"`,
417
- };
418
- },
419
- priority: 12,
420
- },
421
- {
422
- name: 'should-have-text',
423
- matcher: (step) => (step.text.includes('should have text') || step.text.includes('has text')) && !!step.selectorRef && !!(step.value || step.dataRef),
424
- resolver: (step, context) => {
425
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
426
- const expectedText = step.value || context.dataResolver.resolveData(step.dataRef, context.featureName);
427
- return {
428
- templateName: 'have-text-assertion',
429
- data: { ...resolved, expectedText },
430
- comment: `Assert ${step.selectorRef} has text "${step.value || step.dataRef}"`,
431
- };
432
- },
433
- priority: 12,
434
- },
435
- {
436
- name: 'is-empty',
437
- matcher: (step) => /\b(see|sees)\s+\[/.test(step.text) && /\bis empty\b/.test(step.text) && !!step.selectorRef,
438
- resolver: (step, context) => {
439
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
440
- return {
441
- templateName: 'empty-assertion',
442
- data: { ...resolved, selectorRef: step.selectorRef },
443
- comment: `Assert ${step.selectorRef} is empty`,
444
- };
445
- },
446
- priority: 11,
447
- },
448
- {
449
- name: 'is-checked',
450
- matcher: (step) => /\b(see|sees)\s+\[/.test(step.text) && /\bis checked\b/.test(step.text) && !!step.selectorRef,
451
- resolver: (step, context) => {
452
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
453
- return {
454
- templateName: 'checked-assertion',
455
- data: { ...resolved, selectorRef: step.selectorRef },
456
- comment: `Assert ${step.selectorRef} is checked`,
457
- };
458
- },
459
- priority: 11,
460
- },
461
- {
462
- name: 'is-unchecked',
463
- matcher: (step) => /\b(see|sees)\s+\[/.test(step.text) && /\bis unchecked\b/.test(step.text) && !!step.selectorRef,
464
- resolver: (step, context) => {
465
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
466
- return {
467
- templateName: 'not-checked-assertion',
468
- data: { ...resolved, selectorRef: step.selectorRef },
469
- comment: `Assert ${step.selectorRef} is unchecked`,
470
- };
471
- },
472
- priority: 11,
473
- },
474
- {
475
- name: 'is-focused',
476
- matcher: (step) => /\b(see|sees)\s+\[/.test(step.text) && /\bis focused\b/.test(step.text) && !!step.selectorRef,
477
- resolver: (step, context) => {
478
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
479
- return {
480
- templateName: 'focused-assertion',
481
- data: { ...resolved, selectorRef: step.selectorRef },
482
- comment: `Assert ${step.selectorRef} is focused`,
483
- };
484
- },
485
- priority: 11,
486
- },
487
- {
488
- name: 'see-data-text',
489
- matcher: (step) => (step.text.includes('should see') ||
490
- step.text.match(/\b(see|sees)\b/)) &&
491
- !step.selectorRef &&
492
- !!step.dataRef,
493
- resolver: (step, context) => {
494
- // Resolve data reference to actual value
495
- let dataValue;
496
- try {
497
- dataValue = context.dataResolver.resolveData(step.dataRef, context.featureName);
498
- }
499
- catch (error) {
500
- dataValue = `\${${step.dataRef}}`;
501
- }
502
- return {
503
- templateName: 'visible-assertion',
504
- data: {
505
- strategy: 'text',
506
- value: dataValue,
507
- },
508
- comment: `Assert ${step.dataRef} is visible`,
509
- };
510
- },
511
- priority: 5, // Lower than selector-based assertions
512
- },
513
- {
514
- name: 'list-item-count',
515
- matcher: (step) => step.text.includes('should have') && step.text.includes('count') &&
516
- !!step.selectorRef && step.text.includes('items') &&
517
- (step.elementType === 'list' || step.elementType === 'list-item' || step.elementType === 'listitem'),
518
- resolver: (step, context) => {
519
- let expectedCount = 1;
520
- const match = step.text.match(/count\s+(\d+)/);
521
- if (match) {
522
- expectedCount = parseInt(match[1]);
523
- }
524
- else if (step.dataRef) {
525
- try {
526
- const resolvedData = context.dataResolver.resolveData(step.dataRef, context.featureName);
527
- const parsed = parseInt(resolvedData);
528
- expectedCount = isNaN(parsed) ? 1 : parsed;
529
- }
530
- catch {
531
- expectedCount = `\${${step.dataRef}}`;
532
- }
533
- }
534
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
535
- return {
536
- templateName: 'list-item-count-assertion',
537
- data: { ...resolved, expectedCount },
538
- comment: `Assert ${step.selectorRef} list has ${expectedCount} items`,
539
- };
540
- },
541
- priority: 9, // Higher than should-have-count (8)
542
- },
543
- {
544
- name: 'should-have-count',
545
- matcher: (step) => step.text.includes('should have') && step.text.includes('count') && !!step.selectorRef,
546
- resolver: (step, context) => {
547
- let expectedCount = 1;
548
- // Try literal digit first: "count 10"
549
- const match = step.text.match(/count\s+(\d+)/);
550
- if (match) {
551
- expectedCount = parseInt(match[1]);
552
- }
553
- else if (step.dataRef) {
554
- // Resolve data reference: "count {{number_items}}"
555
- try {
556
- const resolvedData = context.dataResolver.resolveData(step.dataRef, context.featureName);
557
- const parsed = parseInt(resolvedData);
558
- expectedCount = isNaN(parsed) ? 1 : parsed;
559
- }
560
- catch {
561
- expectedCount = `\${${step.dataRef}}`;
562
- }
563
- }
564
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
565
- return {
566
- templateName: 'count-assertion',
567
- data: { ...resolved, expectedCount },
568
- comment: `Assert ${step.selectorRef} has count ${expectedCount}`,
569
- };
570
- },
571
- priority: 8,
572
- },
573
- {
574
- name: 'is-loading',
575
- matcher: (step) => /\b(see|sees)\s+\[/.test(step.text) && /\bis loading\b/.test(step.text) && !!step.selectorRef,
576
- resolver: (step, context) => {
577
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
578
- return {
579
- templateName: 'loading-assertion',
580
- data: { ...resolved, selectorRef: step.selectorRef },
581
- comment: `Assert ${step.selectorRef} is loading`,
582
- };
583
- },
584
- priority: 11,
585
- },
586
- {
587
- name: 'is-selected',
588
- matcher: (step) => /\b(see|sees)\s+\[/.test(step.text) && /\bis selected\b/.test(step.text) && !!step.selectorRef,
589
- resolver: (step, context) => {
590
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
591
- return {
592
- templateName: 'selected-assertion',
593
- data: { ...resolved, selectorRef: step.selectorRef },
594
- comment: `Assert ${step.selectorRef} is selected`,
595
- };
596
- },
597
- priority: 11,
598
- },
599
- {
600
- name: 'is-sorted-ascending',
601
- matcher: (step) => /\b(see|sees)\s+\[/.test(step.text) && /\bsorted ascending\b/.test(step.text) && !!step.selectorRef,
602
- resolver: (step, context) => {
603
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
604
- return {
605
- templateName: 'sorted-assertion',
606
- data: { ...resolved, selectorRef: step.selectorRef, sortDirection: 'ascending' },
607
- comment: `Assert ${step.selectorRef} is sorted ascending`,
608
- };
609
- },
610
- priority: 12,
611
- },
612
- {
613
- name: 'is-sorted-descending',
614
- matcher: (step) => /\b(see|sees)\s+\[/.test(step.text) && /\bsorted descending\b/.test(step.text) && !!step.selectorRef,
615
- resolver: (step, context) => {
616
- const resolved = context.selectorResolver.resolveSelector(step.selectorRef, undefined, step.elementType, step.nth);
617
- return {
618
- templateName: 'sorted-assertion',
619
- data: { ...resolved, selectorRef: step.selectorRef, sortDirection: 'descending' },
620
- comment: `Assert ${step.selectorRef} is sorted descending`,
621
- };
622
- },
623
- priority: 12,
624
- },
625
- ];
626
- //# sourceMappingURL=assertion-patterns.js.map