@sun-asterisk/sungen 3.1.2 → 3.2.0-beta.142

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