@provartesting/provardx-cli 1.5.0-dev.2 → 1.5.1

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 (147) hide show
  1. package/README.md +163 -12
  2. package/bin/mcp-start.js +74 -0
  3. package/lib/commands/provar/auth/clear.d.ts +7 -0
  4. package/lib/commands/provar/auth/clear.js +36 -0
  5. package/lib/commands/provar/auth/clear.js.map +1 -0
  6. package/lib/commands/provar/auth/login.d.ts +10 -0
  7. package/lib/commands/provar/auth/login.js +90 -0
  8. package/lib/commands/provar/auth/login.js.map +1 -0
  9. package/lib/commands/provar/auth/rotate.d.ts +7 -0
  10. package/lib/commands/provar/auth/rotate.js +42 -0
  11. package/lib/commands/provar/auth/rotate.js.map +1 -0
  12. package/lib/commands/provar/auth/status.d.ts +7 -0
  13. package/lib/commands/provar/auth/status.js +107 -0
  14. package/lib/commands/provar/auth/status.js.map +1 -0
  15. package/lib/commands/provar/mcp/start.d.ts +2 -0
  16. package/lib/commands/provar/mcp/start.js +14 -1
  17. package/lib/commands/provar/mcp/start.js.map +1 -1
  18. package/lib/mcp/docs/NITROX_CATALOG_SOURCE.json +6 -0
  19. package/lib/mcp/docs/NITROX_COMPONENT_CATALOG.md +2001 -0
  20. package/lib/mcp/docs/PROVAR_TEST_STEP_REFERENCE.md +1430 -0
  21. package/lib/mcp/docs/PROVAR_TOOL_GUIDE.md +187 -0
  22. package/lib/mcp/licensing/algasClient.js +14 -5
  23. package/lib/mcp/licensing/algasClient.js.map +1 -1
  24. package/lib/mcp/licensing/ideDetection.d.ts +0 -12
  25. package/lib/mcp/licensing/ideDetection.js +1 -73
  26. package/lib/mcp/licensing/ideDetection.js.map +1 -1
  27. package/lib/mcp/licensing/licenseCache.js +7 -1
  28. package/lib/mcp/licensing/licenseCache.js.map +1 -1
  29. package/lib/mcp/licensing/licenseValidator.d.ts +3 -3
  30. package/lib/mcp/licensing/licenseValidator.js +11 -4
  31. package/lib/mcp/licensing/licenseValidator.js.map +1 -1
  32. package/lib/mcp/prompts/guidePrompts.d.ts +4 -0
  33. package/lib/mcp/prompts/guidePrompts.js +334 -0
  34. package/lib/mcp/prompts/guidePrompts.js.map +1 -0
  35. package/lib/mcp/prompts/index.d.ts +2 -0
  36. package/lib/mcp/prompts/index.js +23 -0
  37. package/lib/mcp/prompts/index.js.map +1 -0
  38. package/lib/mcp/prompts/loopPrompts.d.ts +6 -0
  39. package/lib/mcp/prompts/loopPrompts.js +435 -0
  40. package/lib/mcp/prompts/loopPrompts.js.map +1 -0
  41. package/lib/mcp/prompts/migrationPrompts.d.ts +4 -0
  42. package/lib/mcp/prompts/migrationPrompts.js +207 -0
  43. package/lib/mcp/prompts/migrationPrompts.js.map +1 -0
  44. package/lib/mcp/rules/provar_best_practices_rules.json +256 -544
  45. package/lib/mcp/security/pathPolicy.d.ts +5 -0
  46. package/lib/mcp/security/pathPolicy.js +58 -3
  47. package/lib/mcp/security/pathPolicy.js.map +1 -1
  48. package/lib/mcp/server.d.ts +18 -0
  49. package/lib/mcp/server.js +232 -19
  50. package/lib/mcp/server.js.map +1 -1
  51. package/lib/mcp/tools/antTools.d.ts +15 -0
  52. package/lib/mcp/tools/antTools.js +369 -170
  53. package/lib/mcp/tools/antTools.js.map +1 -1
  54. package/lib/mcp/tools/automationTools.d.ts +18 -8
  55. package/lib/mcp/tools/automationTools.js +333 -176
  56. package/lib/mcp/tools/automationTools.js.map +1 -1
  57. package/lib/mcp/tools/bestPracticesEngine.js +161 -23
  58. package/lib/mcp/tools/bestPracticesEngine.js.map +1 -1
  59. package/lib/mcp/tools/connectionTools.d.ts +4 -0
  60. package/lib/mcp/tools/connectionTools.js +242 -0
  61. package/lib/mcp/tools/connectionTools.js.map +1 -0
  62. package/lib/mcp/tools/defectTools.d.ts +1 -1
  63. package/lib/mcp/tools/defectTools.js +61 -50
  64. package/lib/mcp/tools/defectTools.js.map +1 -1
  65. package/lib/mcp/tools/descHelper.d.ts +5 -0
  66. package/lib/mcp/tools/descHelper.js +14 -0
  67. package/lib/mcp/tools/descHelper.js.map +1 -0
  68. package/lib/mcp/tools/hierarchyValidate.d.ts +1 -1
  69. package/lib/mcp/tools/hierarchyValidate.js +127 -42
  70. package/lib/mcp/tools/hierarchyValidate.js.map +1 -1
  71. package/lib/mcp/tools/nitroXTools.d.ts +23 -0
  72. package/lib/mcp/tools/nitroXTools.js +863 -0
  73. package/lib/mcp/tools/nitroXTools.js.map +1 -0
  74. package/lib/mcp/tools/pageObjectGenerate.js +150 -57
  75. package/lib/mcp/tools/pageObjectGenerate.js.map +1 -1
  76. package/lib/mcp/tools/pageObjectValidate.js +143 -46
  77. package/lib/mcp/tools/pageObjectValidate.js.map +1 -1
  78. package/lib/mcp/tools/projectInspect.js +79 -32
  79. package/lib/mcp/tools/projectInspect.js.map +1 -1
  80. package/lib/mcp/tools/projectValidateFromPath.js +185 -58
  81. package/lib/mcp/tools/projectValidateFromPath.js.map +1 -1
  82. package/lib/mcp/tools/propertiesTools.d.ts +2 -0
  83. package/lib/mcp/tools/propertiesTools.js +358 -78
  84. package/lib/mcp/tools/propertiesTools.js.map +1 -1
  85. package/lib/mcp/tools/qualityHubApiTools.d.ts +3 -0
  86. package/lib/mcp/tools/qualityHubApiTools.js +139 -0
  87. package/lib/mcp/tools/qualityHubApiTools.js.map +1 -0
  88. package/lib/mcp/tools/qualityHubTools.js +292 -72
  89. package/lib/mcp/tools/qualityHubTools.js.map +1 -1
  90. package/lib/mcp/tools/rcaTools.d.ts +3 -2
  91. package/lib/mcp/tools/rcaTools.js +194 -56
  92. package/lib/mcp/tools/rcaTools.js.map +1 -1
  93. package/lib/mcp/tools/sfSpawn.d.ts +25 -3
  94. package/lib/mcp/tools/sfSpawn.js +154 -6
  95. package/lib/mcp/tools/sfSpawn.js.map +1 -1
  96. package/lib/mcp/tools/testCaseGenerate.js +285 -78
  97. package/lib/mcp/tools/testCaseGenerate.js.map +1 -1
  98. package/lib/mcp/tools/testCaseStepTools.d.ts +4 -0
  99. package/lib/mcp/tools/testCaseStepTools.js +244 -0
  100. package/lib/mcp/tools/testCaseStepTools.js.map +1 -0
  101. package/lib/mcp/tools/testCaseValidate.d.ts +11 -0
  102. package/lib/mcp/tools/testCaseValidate.js +381 -46
  103. package/lib/mcp/tools/testCaseValidate.js.map +1 -1
  104. package/lib/mcp/tools/testPlanTools.d.ts +1 -0
  105. package/lib/mcp/tools/testPlanTools.js +316 -59
  106. package/lib/mcp/tools/testPlanTools.js.map +1 -1
  107. package/lib/mcp/tools/testPlanValidate.js +114 -23
  108. package/lib/mcp/tools/testPlanValidate.js.map +1 -1
  109. package/lib/mcp/tools/testSuiteValidate.js +130 -15
  110. package/lib/mcp/tools/testSuiteValidate.js.map +1 -1
  111. package/lib/mcp/update/updateChecker.d.ts +14 -0
  112. package/lib/mcp/update/updateChecker.js +228 -0
  113. package/lib/mcp/update/updateChecker.js.map +1 -0
  114. package/lib/mcp/utils/detailLevel.d.ts +9 -0
  115. package/lib/mcp/utils/detailLevel.js +20 -0
  116. package/lib/mcp/utils/detailLevel.js.map +1 -0
  117. package/lib/mcp/utils/fieldMask.d.ts +17 -0
  118. package/lib/mcp/utils/fieldMask.js +75 -0
  119. package/lib/mcp/utils/fieldMask.js.map +1 -0
  120. package/lib/mcp/utils/tokenMeta.d.ts +40 -0
  121. package/lib/mcp/utils/tokenMeta.js +90 -0
  122. package/lib/mcp/utils/tokenMeta.js.map +1 -0
  123. package/lib/mcp/utils/validationDiff.d.ts +57 -0
  124. package/lib/mcp/utils/validationDiff.js +191 -0
  125. package/lib/mcp/utils/validationDiff.js.map +1 -0
  126. package/lib/mcp/utils/validationScore.d.ts +15 -0
  127. package/lib/mcp/utils/validationScore.js +31 -0
  128. package/lib/mcp/utils/validationScore.js.map +1 -0
  129. package/lib/services/auth/credentials.d.ts +21 -0
  130. package/lib/services/auth/credentials.js +75 -0
  131. package/lib/services/auth/credentials.js.map +1 -0
  132. package/lib/services/auth/loginFlow.d.ts +68 -0
  133. package/lib/services/auth/loginFlow.js +216 -0
  134. package/lib/services/auth/loginFlow.js.map +1 -0
  135. package/lib/services/projectValidation.d.ts +5 -2
  136. package/lib/services/projectValidation.js +83 -31
  137. package/lib/services/projectValidation.js.map +1 -1
  138. package/lib/services/qualityHub/client.d.ts +161 -0
  139. package/lib/services/qualityHub/client.js +226 -0
  140. package/lib/services/qualityHub/client.js.map +1 -0
  141. package/messages/sf.provar.auth.clear.md +16 -0
  142. package/messages/sf.provar.auth.login.md +31 -0
  143. package/messages/sf.provar.auth.rotate.md +23 -0
  144. package/messages/sf.provar.auth.status.md +16 -0
  145. package/messages/sf.provar.mcp.start.md +83 -48
  146. package/oclif.manifest.json +325 -28
  147. package/package.json +35 -12
@@ -0,0 +1,1430 @@
1
+ # Provar Test Step Reference
2
+
3
+ > **Source of truth** for AI-assisted test generation in the provardx-cli / Quality Hub MCP toolchain.
4
+ > All examples are sourced from the SalesCloud corpus. The `provar_qualityhub_examples_retrieve` tool returns real
5
+ > examples for additional grounding; `provar_testcase_validate` enforces all rules documented here.
6
+
7
+ ---
8
+
9
+ ## API ID Reference
10
+
11
+ | Step Type | API ID |
12
+ | --------------------- | ------------------------------------------------------------------------ |
13
+ | **Connection** | |
14
+ | ApexConnect | `com.provar.plugins.forcedotcom.core.testapis.ApexConnect` |
15
+ | UiConnect | `com.provar.plugins.forcedotcom.core.ui.UiConnect` |
16
+ | **Apex CRUD** | |
17
+ | ApexCreateObject | `com.provar.plugins.forcedotcom.core.testapis.ApexCreateObject` |
18
+ | ApexReadObject | `com.provar.plugins.forcedotcom.core.testapis.ApexReadObject` |
19
+ | ApexUpdateObject | `com.provar.plugins.forcedotcom.core.testapis.ApexUpdateObject` |
20
+ | ApexDeleteObject | `com.provar.plugins.forcedotcom.core.testapis.ApexDeleteObject` |
21
+ | ApexSoqlQuery | `com.provar.plugins.forcedotcom.core.testapis.ApexSoqlQuery` |
22
+ | **Apex Advanced** | |
23
+ | ApexBulk | `com.provar.plugins.forcedotcom.core.testapis.ApexBulk` |
24
+ | ApexExecute | `com.provar.plugins.forcedotcom.core.testapis.ApexExecute` |
25
+ | ApexConvertLead | `com.provar.plugins.forcedotcom.core.testapis.ApexConvertLead` |
26
+ | ApexExtractLayout | `com.provar.plugins.forcedotcom.core.testapis.ApexExtractLayout` |
27
+ | ApexAssertLayout | `com.provar.plugins.forcedotcom.core.testapis.ApexAssertLayout` |
28
+ | ApexApproveWorkItem | `com.provar.plugins.forcedotcom.core.testapis.ApexApproveWorkItem` |
29
+ | ApexSubmitForApproval | `com.provar.plugins.forcedotcom.core.testapis.ApexSubmitForApproval` |
30
+ | ApexLogForCleanup | `com.provar.plugins.forcedotcom.core.testapis.ApexLogForCleanup` |
31
+ | **UI Steps** | |
32
+ | UiWithScreen | `com.provar.plugins.forcedotcom.core.ui.UiWithScreen` |
33
+ | UiDoAction | `com.provar.plugins.forcedotcom.core.ui.UiDoAction` |
34
+ | UiAssert | `com.provar.plugins.forcedotcom.core.ui.UiAssert` |
35
+ | UiWithRow | `com.provar.plugins.forcedotcom.core.ui.UiWithRow` |
36
+ | UiHandleAlert | `com.provar.plugins.forcedotcom.core.ui.UiHandleAlert` |
37
+ | UiNavigate | `com.provar.plugins.forcedotcom.core.ui.UiNavigate` |
38
+ | **Control Flow** | |
39
+ | SetValues | `com.provar.plugins.bundled.apis.control.SetValues` |
40
+ | StepGroup | `com.provar.plugins.bundled.apis.control.StepGroup` |
41
+ | If | `com.provar.plugins.bundled.apis.If` |
42
+ | ForEach | `com.provar.plugins.bundled.apis.control.ForEach` |
43
+ | DoWhile | `com.provar.plugins.bundled.apis.control.DoWhile` |
44
+ | WaitFor | `com.provar.plugins.bundled.apis.control.WaitFor` |
45
+ | TryCatchFinally | `com.provar.plugins.bundled.apis.control.TryCatchFinally` |
46
+ | Switch | `com.provar.plugins.bundled.apis.Switch` |
47
+ | Sleep | `com.provar.plugins.bundled.apis.control.Sleep` |
48
+ | Fail | `com.provar.plugins.bundled.apis.control.Fail` |
49
+ | CallTest | `com.provar.plugins.bundled.apis.control.CallTest` |
50
+ | **Assertions** | |
51
+ | AssertValues | `com.provar.plugins.bundled.apis.AssertValues` |
52
+ | **BDD** | |
53
+ | Given | `com.provar.plugins.bundled.apis.bdd.Given` |
54
+ | When | `com.provar.plugins.bundled.apis.bdd.When` |
55
+ | Then | `com.provar.plugins.bundled.apis.bdd.Then` |
56
+ | And | `com.provar.plugins.bundled.apis.bdd.And` |
57
+ | But | `com.provar.plugins.bundled.apis.bdd.But` |
58
+ | **Design** | |
59
+ | ActualResult | `com.provar.plugins.bundled.apis.control.ActualResult` |
60
+ | DesignStep | `com.provar.plugins.bundled.apis.control.DesignStep` |
61
+ | **Database** | |
62
+ | DbConnect | `com.provar.plugins.bundled.apis.db.DbConnect` |
63
+ | DbRead | `com.provar.plugins.bundled.apis.db.DbRead` |
64
+ | DbInsert | `com.provar.plugins.bundled.apis.db.DbInsert` |
65
+ | DbUpdate | `com.provar.plugins.bundled.apis.db.DbUpdate` |
66
+ | DbDelete | `com.provar.plugins.bundled.apis.db.DbDelete` |
67
+ | SqlQuery | `com.provar.plugins.bundled.apis.db.SqlQuery` |
68
+ | **Web Service** | |
69
+ | WebConnect | `com.provar.plugins.bundled.apis.restservice.WebConnect` |
70
+ | RestRequest | `com.provar.plugins.bundled.apis.restservice.RestRequest` |
71
+ | SoapRequest | `com.provar.plugins.bundled.apis.restservice.SoapRequest` |
72
+ | **Messaging** | |
73
+ | PublishMessage | `com.provar.plugins.bundled.apis.messaging.PublishMessage` |
74
+ | Subscribe | `com.provar.plugins.bundled.apis.messaging.Subscribe` |
75
+ | ReceiveMessage | `com.provar.plugins.bundled.apis.messaging.ReceiveMessage` |
76
+ | SendMessage | `com.provar.plugins.bundled.apis.messaging.SendMessage` |
77
+ | **Utility** | |
78
+ | ListCompare | `com.provar.plugins.bundled.apis.list.ListCompare` |
79
+ | Match | `com.provar.plugins.bundled.apis.string.Match` |
80
+ | Read | `com.provar.plugins.bundled.apis.io.Read` |
81
+ | Write | `com.provar.plugins.bundled.apis.io.Write` |
82
+ | Split | `com.provar.plugins.bundled.apis.string.Split` |
83
+ | Replace | `com.provar.plugins.bundled.apis.string.Replace` |
84
+ | **ProvarAI / Labs** | |
85
+ | GenerateTestData | `com.provar.plugins.forcedotcom.core.testapis.generate.GenerateTestData` |
86
+ | GenerateTestCase | `com.provar.plugins.forcedotcom.core.testapis.GenerateTestCase` |
87
+ | PageObjectCleaner | `com.provar.plugins.bundled.apis.provarlabs.PageObjectCleaner` |
88
+
89
+ ---
90
+
91
+ ## Common AI Hallucinations — Do Not Use
92
+
93
+ The following argument IDs look plausible but are invalid and will fail validation:
94
+
95
+ | Invalid argument | Correct alternative |
96
+ | ----------------------------- | ---------------------------------------------------------- |
97
+ | `autoPopulateRequiredFields` | Not supported — populate fields explicitly |
98
+ | `assertObjectFieldsPopulated` | Use `UiAssert` or `ApexReadObject` instead |
99
+ | `commandTimeout` | Not a valid argument on any step |
100
+ | `waitForPageLoad` | Use `beforeWait`/`afterWait` with `uiWait` URI |
101
+ | `screenshotOnFailure` | Use `captureBefore`/`captureAfter` (string "true"/"false") |
102
+ | `closeAllOtherTabs` | The correct id is `closeAllPrimaryTabs` (ApexConnect only) |
103
+ | `connectionType` | Not a step argument; connection type is implied by apiId |
104
+
105
+ ---
106
+
107
+ ## Connection Steps
108
+
109
+ ### ApexConnect
110
+
111
+ Establishes a Salesforce connection (API + optional UI). Usually the first step in every test. Use `autoCleanup="true"` to automatically log out after the test; omit it only when you want the connection to persist into a parent scope.
112
+
113
+ > **`connectionId` must use `valueClass="id"`** (a GUID string), not `valueClass="string"`. The validator
114
+ > enforces this (rule APEX-CONNECT-CONNID-001).
115
+
116
+ > **ApexConnect vs UiConnect:** ApexConnect opens both an API connection and (optionally) a UI browser session.
117
+ > UiConnect opens only a browser session, using an existing ApexConnect result as its Salesforce credential.
118
+ > In most test cases you need only ApexConnect.
119
+
120
+ ```xml
121
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.testapis.ApexConnect"
122
+ name="ApexConnect" testItemId="1"
123
+ title="Salesforce Connect: SalesUser (Test)">
124
+ <arguments>
125
+ <argument id="connectionName">
126
+ <value class="value" valueClass="string">SalesUser</value>
127
+ </argument>
128
+ <argument id="connectionId">
129
+ <value class="value" valueClass="id">74c34c63-ad34-43d9-bb12-cd783bd9bcdd</value>
130
+ </argument>
131
+ <argument id="resultName">
132
+ <value class="value" valueClass="string">DemoOrg</value>
133
+ </argument>
134
+ <argument id="resultScope">
135
+ <value class="value" valueClass="string">Test</value>
136
+ </argument>
137
+ <argument id="uiApplicationName">
138
+ <value class="value" valueClass="string">LightningSales</value>
139
+ </argument>
140
+ <argument id="quickUiLogin">
141
+ <value class="value" valueClass="boolean">true</value>
142
+ </argument>
143
+ <argument id="closeAllPrimaryTabs">
144
+ <value class="value" valueClass="boolean">true</value>
145
+ </argument>
146
+ <argument id="reuseConnectionName"/>
147
+ <argument id="alreadyOpenBehaviour">
148
+ <value class="value" valueClass="string">Fail</value>
149
+ </argument>
150
+ <argument id="privateBrowsingMode"/>
151
+ <argument id="enableObjectIdLogging">
152
+ <value class="value" valueClass="boolean">true</value>
153
+ </argument>
154
+ <argument id="autoCleanup">
155
+ <value class="value" valueClass="boolean">true</value>
156
+ </argument>
157
+ <argument id="cleanupConnectionName"/>
158
+ <argument id="lightningMode">
159
+ <value class="value" valueClass="string">enable</value>
160
+ </argument>
161
+ <argument id="webBrowser"/>
162
+ </arguments>
163
+ </apiCall>
164
+ ```
165
+
166
+ **Valid `alreadyOpenBehaviour` values:** `Reuse` | `Fail`
167
+ **Valid `lightningMode` values:** `enable` | `default`
168
+ **Valid `resultScope` values:** `Test` | `Global` | `Folder`
169
+
170
+ ### UiConnect
171
+
172
+ Opens a browser-only UI session. Use this when you have a separate ApexConnect for API operations and need an independent browser window. Fewer arguments than ApexConnect — it does not manage Salesforce API credentials.
173
+
174
+ > **UiConnect does NOT accept:** `autoCleanup`, `enableObjectIdLogging`, `quickUiLogin`, `closeAllPrimaryTabs`,
175
+ > `alreadyOpenBehaviour`, `lightningMode`, `uiApplicationName`, `cleanupConnectionName`.
176
+ > Using these on UiConnect will fail validation (rule UI-CONNECT-ARGS-001).
177
+
178
+ ```xml
179
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.ui.UiConnect"
180
+ name="UiConnect" testItemId="1"
181
+ title="UI Connect: DemoOrg">
182
+ <arguments>
183
+ <argument id="connectionName">
184
+ <value class="value" valueClass="string">DemoOrg</value>
185
+ </argument>
186
+ <argument id="resultName">
187
+ <value class="value" valueClass="string">DemoOrg</value>
188
+ </argument>
189
+ <argument id="resultScope">
190
+ <value class="value" valueClass="string">Test</value>
191
+ </argument>
192
+ <argument id="reuseConnectionName"/>
193
+ <argument id="privateBrowsingMode"/>
194
+ <argument id="webBrowser"/>
195
+ </arguments>
196
+ </apiCall>
197
+ ```
198
+
199
+ ---
200
+
201
+ ## UI Steps
202
+
203
+ UI steps must always be nested inside a `UiWithScreen` block. Never place `UiDoAction` or `UiAssert` directly in the top-level `<steps>` list.
204
+
205
+ ### UiWithScreen
206
+
207
+ Navigates to (or asserts presence of) a Salesforce screen, then runs substeps against it. All UI actions and assertions go inside the `<clause name="substeps">` block.
208
+
209
+ **Navigation rules:**
210
+
211
+ - The first `UiWithScreen` in a test **must** use `navigate="Always"` or `navigate="IfNecessary"`. Never `"Dont"` for the first screen.
212
+ - Subsequent screens in the same flow should use `navigate="Dont"` when Salesforce has already navigated there (e.g., after clicking Save the record view is already open).
213
+ - `navigate="Always"` on an `Edit` or `View` action **requires** `sfUiTargetObjectId` (the record ID). Without it, Provar cannot navigate to the correct record.
214
+ - `sfUiTargetResultName` captures the record ID that Salesforce creates (useful on `New` action screens after save).
215
+
216
+ **Target URI format:** `sf:ui:target?object=OBJECT&action=ACTION`
217
+
218
+ Valid `action` values: `ObjectHome` | `New` | `View` | `Edit` | `Delete` | `Clone`
219
+
220
+ ```xml
221
+ <!-- First screen: navigate=Always required -->
222
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.ui.UiWithScreen"
223
+ name="UiWithScreen" testItemId="2"
224
+ title="On SF Opportunity Home screen">
225
+ <arguments>
226
+ <argument id="uiConnectionName">
227
+ <value class="value" valueClass="string">DemoOrg</value>
228
+ </argument>
229
+ <argument id="target">
230
+ <value class="uiTarget" uri="sf:ui:target?object=Opportunity&amp;action=ObjectHome"/>
231
+ </argument>
232
+ <argument id="navigate">
233
+ <value class="value" valueClass="string">Always</value>
234
+ </argument>
235
+ <argument id="targetDescription">
236
+ <value class="value" valueClass="string">On SF Opportunity Home screen</value>
237
+ </argument>
238
+ <argument id="windowSelection">
239
+ <value class="value" valueClass="string">Default</value>
240
+ </argument>
241
+ <argument id="windowSize">
242
+ <value class="value" valueClass="string">Default</value>
243
+ </argument>
244
+ <argument id="closeWindow"/>
245
+ <argument id="captureBefore">
246
+ <value class="value" valueClass="string">false</value>
247
+ </argument>
248
+ <argument id="captureAfter">
249
+ <value class="value" valueClass="string">false</value>
250
+ </argument>
251
+ </arguments>
252
+ <clauses>
253
+ <clause name="substeps" testItemId="3">
254
+ <steps>
255
+ <!-- UiDoAction and UiAssert steps go here -->
256
+ </steps>
257
+ </clause>
258
+ </clauses>
259
+ </apiCall>
260
+
261
+ <!-- New screen: navigate=Dont (already arrived via New button click), capture resulting record ID -->
262
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.ui.UiWithScreen"
263
+ name="UiWithScreen" testItemId="5"
264
+ title="On SF Opportunity New screen">
265
+ <arguments>
266
+ <argument id="uiConnectionName">
267
+ <value class="value" valueClass="string">DemoOrg</value>
268
+ </argument>
269
+ <argument id="target">
270
+ <value class="uiTarget" uri="sf:ui:target?object=Opportunity&amp;noOverride=true&amp;action=New"/>
271
+ </argument>
272
+ <argument id="navigate">
273
+ <value class="value" valueClass="string">Dont</value>
274
+ </argument>
275
+ <argument id="targetDescription">
276
+ <value class="value" valueClass="string">On SF Opportunity New screen</value>
277
+ </argument>
278
+ <argument id="windowSelection">
279
+ <value class="value" valueClass="string">Default</value>
280
+ </argument>
281
+ <argument id="windowSize">
282
+ <value class="value" valueClass="string">Default</value>
283
+ </argument>
284
+ <argument id="closeWindow"/>
285
+ <argument id="captureBefore">
286
+ <value class="value" valueClass="string">false</value>
287
+ </argument>
288
+ <argument id="captureAfter">
289
+ <value class="value" valueClass="string">false</value>
290
+ </argument>
291
+ <argument id="sfUiTargetResultName">
292
+ <value class="value" valueClass="string">opportunityId</value>
293
+ </argument>
294
+ <argument id="sfUiTargetResultScope">
295
+ <value class="value" valueClass="string">Test</value>
296
+ </argument>
297
+ </arguments>
298
+ <clauses>
299
+ <clause name="substeps" testItemId="6">
300
+ <steps>
301
+ <!-- fill fields, then Save -->
302
+ </steps>
303
+ </clause>
304
+ </clauses>
305
+ </apiCall>
306
+
307
+ <!-- View screen: navigate=Always requires sfUiTargetObjectId -->
308
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.ui.UiWithScreen"
309
+ name="UiWithScreen" testItemId="12"
310
+ title="On SF Opportunity View screen">
311
+ <arguments>
312
+ <argument id="uiConnectionName">
313
+ <value class="value" valueClass="string">DemoOrg</value>
314
+ </argument>
315
+ <argument id="target">
316
+ <value class="uiTarget" uri="sf:ui:target?object=Opportunity&amp;noOverride=true&amp;action=View"/>
317
+ </argument>
318
+ <argument id="navigate">
319
+ <value class="value" valueClass="string">Always</value>
320
+ </argument>
321
+ <argument id="targetDescription">
322
+ <value class="value" valueClass="string">On SF Opportunity View screen</value>
323
+ </argument>
324
+ <argument id="windowSelection">
325
+ <value class="value" valueClass="string">Default</value>
326
+ </argument>
327
+ <argument id="windowSize">
328
+ <value class="value" valueClass="string">Default</value>
329
+ </argument>
330
+ <argument id="closeWindow"/>
331
+ <argument id="captureBefore">
332
+ <value class="value" valueClass="string">false</value>
333
+ </argument>
334
+ <argument id="captureAfter">
335
+ <value class="value" valueClass="string">false</value>
336
+ </argument>
337
+ <argument id="sfUiTargetObjectId">
338
+ <value class="variable">
339
+ <path element="opportunityId"/>
340
+ </value>
341
+ </argument>
342
+ </arguments>
343
+ <clauses>
344
+ <clause name="substeps" testItemId="13">
345
+ <steps>
346
+ <!-- UiAssert steps go here -->
347
+ </steps>
348
+ </clause>
349
+ </clauses>
350
+ </apiCall>
351
+ ```
352
+
353
+ ### UiDoAction
354
+
355
+ Performs a single UI interaction on a field or button. The `interaction` URI determines the action type.
356
+
357
+ **Interaction types:**
358
+
359
+ - `ui:interaction?name=action` — click a button or link
360
+ - `ui:interaction?name=set` — fill/type into a field or select a picklist value
361
+ - `ui:interaction?name=file` — upload a file (uses `fileLocation` instead of `value`)
362
+
363
+ **Locator URI format:** `ui:locator?name=FIELD_OR_BUTTON_NAME&binding=ENCODED_BINDING`
364
+
365
+ The binding component URL-encodes `sf:ui:binding:object?object=OBJECT&field=FIELD` for field locators, or `sf:ui:binding:object?object=OBJECT&action=ACTION` for action locators.
366
+
367
+ ```xml
368
+ <!-- Click a button (interaction=action) -->
369
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.ui.UiDoAction"
370
+ name="UiDoAction" testItemId="4"
371
+ title="Click the New button">
372
+ <arguments>
373
+ <argument id="locator">
374
+ <value class="uiLocator" uri="ui:locator?name=New&amp;binding=sf%3Aui%3Abinding%3Aobject%3Fobject%3DOpportunity%26action%3DNew"/>
375
+ </argument>
376
+ <argument id="interaction">
377
+ <value class="uiInteraction" uri="ui:interaction?name=action"/>
378
+ </argument>
379
+ <argument id="hover">
380
+ <value class="value" valueClass="boolean">false</value>
381
+ </argument>
382
+ <argument id="captureBefore">
383
+ <value class="value" valueClass="string">false</value>
384
+ </argument>
385
+ <argument id="captureAfter">
386
+ <value class="value" valueClass="string">false</value>
387
+ </argument>
388
+ <argument id="beforeWait">
389
+ <value class="uiWait" uri="default"/>
390
+ </argument>
391
+ <argument id="afterWait">
392
+ <value class="uiWait" uri="default"/>
393
+ </argument>
394
+ <argument id="interactionDescription">
395
+ <value class="value" valueClass="string">Click the New button</value>
396
+ </argument>
397
+ <argument id="autoRetry"/>
398
+ </arguments>
399
+ </apiCall>
400
+
401
+ <!-- Set a text/picklist field (interaction=set) -->
402
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.ui.UiDoAction"
403
+ name="UiDoAction" testItemId="7"
404
+ title="Set the Opportunity Name field to Test">
405
+ <arguments>
406
+ <argument id="locator">
407
+ <value class="uiLocator" uri="ui:locator?name=Name&amp;binding=sf%3Aui%3Abinding%3Aobject%3Fobject%3DOpportunity%26field%3DName"/>
408
+ </argument>
409
+ <argument id="interaction">
410
+ <value class="uiInteraction" uri="ui:interaction?name=set"/>
411
+ </argument>
412
+ <argument id="value">
413
+ <value class="value" valueClass="string">Test</value>
414
+ </argument>
415
+ <argument id="blur">
416
+ <value class="value" valueClass="boolean">false</value>
417
+ </argument>
418
+ <argument id="pressEnter">
419
+ <value class="value" valueClass="boolean">false</value>
420
+ </argument>
421
+ <argument id="captureBefore">
422
+ <value class="value" valueClass="string">false</value>
423
+ </argument>
424
+ <argument id="captureAfter">
425
+ <value class="value" valueClass="string">false</value>
426
+ </argument>
427
+ <argument id="beforeWait">
428
+ <value class="uiWait" uri="default"/>
429
+ </argument>
430
+ <argument id="afterWait">
431
+ <value class="uiWait" uri="default"/>
432
+ </argument>
433
+ <argument id="interactionDescription">
434
+ <value class="value" valueClass="string">Set the Opportunity Name field to Test</value>
435
+ </argument>
436
+ <argument id="autoRetry"/>
437
+ </arguments>
438
+ </apiCall>
439
+
440
+ <!-- Upload a file (interaction=file) -->
441
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.ui.UiDoAction"
442
+ name="UiDoAction" testItemId="15"
443
+ title="Upload attachment file">
444
+ <arguments>
445
+ <argument id="locator">
446
+ <value class="uiLocator" uri="ui:locator?name=AttachFile&amp;binding=sf%3Aui%3Abinding%3Aobject%3Fobject%3DOpportunity%26action%3DAttachFile%26relationship%3DCombinedAttachments"/>
447
+ </argument>
448
+ <argument id="interaction">
449
+ <value class="uiInteraction" uri="ui:interaction?name=file"/>
450
+ </argument>
451
+ <argument id="fileLocation">
452
+ <value class="value" valueClass="string">templates/TestData.xlsx</value>
453
+ </argument>
454
+ <argument id="fileContents"/>
455
+ <argument id="captureBefore">
456
+ <value class="value" valueClass="string">false</value>
457
+ </argument>
458
+ <argument id="captureAfter">
459
+ <value class="value" valueClass="string">false</value>
460
+ </argument>
461
+ <argument id="beforeWait">
462
+ <value class="uiWait" uri="default"/>
463
+ </argument>
464
+ <argument id="afterWait">
465
+ <value class="uiWait" uri="default"/>
466
+ </argument>
467
+ <argument id="interactionDescription">
468
+ <value class="value" valueClass="string">Upload attachment file</value>
469
+ </argument>
470
+ <argument id="autoRetry">
471
+ <value class="uiWait" uri="ui:wait:autoRetry:timeout=10"/>
472
+ </argument>
473
+ </arguments>
474
+ </apiCall>
475
+ ```
476
+
477
+ > **Rule UI-DOACTION-VALUE-001:** When `interaction` is `set`, the `value` argument is required.
478
+ > When `interaction` is `action`, the `value` argument must be absent.
479
+
480
+ ### UiAssert
481
+
482
+ Verifies field values or element state on the current screen. Must always include `columnAssertions`, `pageAssertions`, `resultScope`, `captureAfter`, `beforeWait`, and `autoRetry` — even when empty.
483
+
484
+ > **Rule UI-ASSERT-STRUCT-001:** All six arguments above are required. Omitting any will fail validation.
485
+ >
486
+ > **Rule UI-ASSERT-STRUCT-002:** Do NOT include `generatedParameters` on `UiAssert`. Unlike `UiDoAction`,
487
+ > `UiAssert` does not use `generatedParameters` for its field assertions.
488
+ >
489
+ > **Rule UI-ASSERT-FIELDLOCATOR-002:** The `uiFieldAssertion` uses a bare `<fieldLocator uri="..."/>` element,
490
+ > NOT a `<value class="uiLocator">` wrapper.
491
+
492
+ ```xml
493
+ <!-- Assert a field value -->
494
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.ui.UiAssert"
495
+ name="UiAssert" testItemId="14"
496
+ title="UI Assert: Name">
497
+ <arguments>
498
+ <argument id="resultName">
499
+ <value class="value" valueClass="string">Values</value>
500
+ </argument>
501
+ <argument id="resultScope">
502
+ <value class="value" valueClass="string">Test</value>
503
+ </argument>
504
+ <argument id="fieldAssertions">
505
+ <value class="valueList" mutable="Mutable">
506
+ <uiFieldAssertion resultName="Name">
507
+ <fieldLocator uri="ui:locator?name=Name&amp;binding=sf%3Aui%3Abinding%3Aobject%3Fobject%3DOpportunity%26field%3DName"/>
508
+ <attributeAssertions>
509
+ <uiAttributeAssertion attributeName="value" comparisonType="EqualTo" normalize="true">
510
+ <value class="value" valueClass="string">Test</value>
511
+ </uiAttributeAssertion>
512
+ </attributeAssertions>
513
+ </uiFieldAssertion>
514
+ </value>
515
+ </argument>
516
+ <argument id="captureAfter">
517
+ <value class="value" valueClass="string">false</value>
518
+ </argument>
519
+ <argument id="columnAssertions">
520
+ <value class="valueList" mutable="Mutable"/>
521
+ </argument>
522
+ <argument id="pageAssertions">
523
+ <value class="valueList" mutable="Mutable"/>
524
+ </argument>
525
+ <argument id="beforeWait"/>
526
+ <argument id="autoRetry"/>
527
+ </arguments>
528
+ </apiCall>
529
+
530
+ <!-- Assert a related list row count -->
531
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.ui.UiAssert"
532
+ name="UiAssert" testItemId="16"
533
+ title="UI Assert: Attachment count">
534
+ <arguments>
535
+ <argument id="resultName">
536
+ <value class="value" valueClass="string">Values</value>
537
+ </argument>
538
+ <argument id="resultScope">
539
+ <value class="value" valueClass="string">Test</value>
540
+ </argument>
541
+ <argument id="fieldAssertions">
542
+ <value class="valueList" mutable="Mutable">
543
+ <uiFieldAssertion resultName="CombinedAttachments">
544
+ <fieldLocator uri="ui:locator?name=CombinedAttachments&amp;binding=sf%3Aui%3Abinding%3Aobject%3Fobject%3DOpportunity%26relationship%3DCombinedAttachments"/>
545
+ <attributeAssertions>
546
+ <uiAttributeAssertion attributeName="totalRowCount" comparisonType="EqualTo">
547
+ <value class="value" valueClass="string">1</value>
548
+ </uiAttributeAssertion>
549
+ <uiAttributeAssertion attributeName="value" comparisonType="None"/>
550
+ </attributeAssertions>
551
+ </uiFieldAssertion>
552
+ </value>
553
+ </argument>
554
+ <argument id="captureAfter">
555
+ <value class="value" valueClass="string">false</value>
556
+ </argument>
557
+ <argument id="columnAssertions">
558
+ <value class="valueList" mutable="Mutable"/>
559
+ </argument>
560
+ <argument id="pageAssertions">
561
+ <value class="valueList" mutable="Mutable"/>
562
+ </argument>
563
+ <argument id="beforeWait"/>
564
+ <argument id="autoRetry"/>
565
+ </arguments>
566
+ </apiCall>
567
+ ```
568
+
569
+ **Valid `comparisonType` values:** `EqualTo` | `NotEqualTo` | `Contains` | `NotContains` | `StartsWith` | `EndsWith` | `None`
570
+
571
+ ### UiWithRow
572
+
573
+ Targets a specific row in a related list or table. Substeps inside the clause operate in the row context (locators can use `rowLocator` parameter for scoping).
574
+
575
+ ```xml
576
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.ui.UiWithRow"
577
+ name="UiWithRow" testItemId="22"
578
+ title="With Roles rows 1">
579
+ <arguments>
580
+ <argument id="uiConnectionName">
581
+ <value class="value" valueClass="string">DemoOrg</value>
582
+ </argument>
583
+ <argument id="locator">
584
+ <value class="uiLocator" uri="sf:ui:locator:row?table=roleBlock%2Froles"/>
585
+ </argument>
586
+ <argument id="uiRowLocator">
587
+ <value class="value" valueClass="string">1</value>
588
+ </argument>
589
+ <argument id="locatorDescription">
590
+ <value class="value" valueClass="string">With Roles rows 1</value>
591
+ </argument>
592
+ <argument id="failIfNotFound">
593
+ <value class="value" valueClass="boolean">true</value>
594
+ </argument>
595
+ <argument id="debugRowLocator">
596
+ <value class="value" valueClass="boolean">true</value>
597
+ </argument>
598
+ <argument id="resultName">
599
+ <value class="value" valueClass="string">Row</value>
600
+ </argument>
601
+ <argument id="resultScope">
602
+ <value class="value" valueClass="string">Local</value>
603
+ </argument>
604
+ </arguments>
605
+ <clauses>
606
+ <clause name="substeps" testItemId="23">
607
+ <steps>
608
+ <!-- UiDoAction steps scoped to this row -->
609
+ </steps>
610
+ </clause>
611
+ </clauses>
612
+ </apiCall>
613
+ ```
614
+
615
+ **Row locator URI format:** `sf:ui:locator:row?table=RELATED_LIST_NAME`
616
+
617
+ The `uiRowLocator` can be a row number (1-based) or a field-value match expression.
618
+
619
+ ### UiHandleAlert
620
+
621
+ Handles browser-level alert/confirm dialogs. Place this immediately after the step that triggers the dialog (e.g., a Delete button click).
622
+
623
+ ```xml
624
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.ui.UiHandleAlert"
625
+ name="UiHandleAlert" testItemId="46"
626
+ title="Handle UI Alerts">
627
+ <arguments>
628
+ <argument id="alerts">
629
+ <value class="valueList" mutable="Mutable">
630
+ <namedValues mutable="Mutable">
631
+ <namedValue name="expectedMessage"/>
632
+ <namedValue name="response">
633
+ <value class="value" valueClass="string">OK</value>
634
+ </namedValue>
635
+ <namedValue name="beforeWait"/>
636
+ <namedValue name="afterWait"/>
637
+ <namedValue name="autoRetry"/>
638
+ </namedValues>
639
+ </value>
640
+ </argument>
641
+ <argument id="captureBefore">
642
+ <value class="value" valueClass="string">false</value>
643
+ </argument>
644
+ <argument id="captureAfter">
645
+ <value class="value" valueClass="string">false</value>
646
+ </argument>
647
+ </arguments>
648
+ </apiCall>
649
+ ```
650
+
651
+ **Valid `response` values:** `OK` | `Cancel`
652
+
653
+ Set `expectedMessage` to a string value to assert the alert text matches before responding.
654
+
655
+ ---
656
+
657
+ ## Apex CRUD Operations
658
+
659
+ ### ApexCreateObject
660
+
661
+ Creates a Salesforce record via the API. Requires `parameterGeneratorUri`, `parameterGeneratorProperties`, and `generatedParameters` — these are always present on Apex CRUD steps and the validator checks for them.
662
+
663
+ > **`parameterGeneratorProperties` key format:** The full command class name prefix is required, e.g.
664
+ > `com.provar.plugins.forcedotcom.ui.commands.CreateCustomObjectTestStepCommand.ConnectionName`
665
+ > (not just `.ConnectionName`).
666
+
667
+ ```xml
668
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.testapis.ApexCreateObject"
669
+ name="ApexCreateObject"
670
+ parameterGeneratorUri="command:com.provar.plugins.forcedotcom.ui.commands.CreateCustomObjectTestStepCommand"
671
+ testItemId="3"
672
+ title="Create Object: Lead=&gt;LeadId">
673
+ <arguments>
674
+ <argument id="objectType">
675
+ <value class="value" valueClass="string">Lead</value>
676
+ </argument>
677
+ <argument id="resultIdName">
678
+ <value class="value" valueClass="string">LeadId</value>
679
+ </argument>
680
+ <argument id="apexConnectionName">
681
+ <value class="value" valueClass="string">Admin</value>
682
+ </argument>
683
+ <argument id="resultScope">
684
+ <value class="value" valueClass="string">Test</value>
685
+ </argument>
686
+ <argument id="LastName">
687
+ <value class="value" valueClass="string">Smith</value>
688
+ </argument>
689
+ <argument id="Company">
690
+ <value class="value" valueClass="string">Acme</value>
691
+ </argument>
692
+ </arguments>
693
+ <parameterGeneratorProperties>
694
+ <propertyValue name="com.provar.plugins.forcedotcom.ui.commands.CreateCustomObjectTestStepCommand.ConnectionName">Admin</propertyValue>
695
+ <propertyValue name="com.provar.plugins.forcedotcom.ui.commands.CreateCustomObjectTestStepCommand.CustomObjectName">Lead</propertyValue>
696
+ </parameterGeneratorProperties>
697
+ <generatedParameters>
698
+ <apiParam group="fields" modelBinding="sf:ui:binding:object?object=Lead&amp;field=LastName" name="LastName" title="LastName"/>
699
+ <apiParam group="fields" modelBinding="sf:ui:binding:object?object=Lead&amp;field=Company" name="Company" title="Company"/>
700
+ </generatedParameters>
701
+ </apiCall>
702
+ ```
703
+
704
+ ### ApexReadObject
705
+
706
+ Reads fields from an existing record by ID. `objectId` takes a variable reference to an ID stored from a prior `ApexCreateObject` or `sfUiTargetResultName` capture.
707
+
708
+ ```xml
709
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.testapis.ApexReadObject"
710
+ name="ApexReadObject"
711
+ parameterGeneratorUri="command:com.provar.plugins.forcedotcom.ui.commands.ReadCustomObjectTestStepCommand"
712
+ testItemId="1"
713
+ title="Read Object: Account = {AccountId}">
714
+ <arguments>
715
+ <argument id="apexConnectionName">
716
+ <value class="value" valueClass="string">Admin</value>
717
+ </argument>
718
+ <argument id="objectType">
719
+ <value class="value" valueClass="string">Account</value>
720
+ </argument>
721
+ <argument id="objectId">
722
+ <value class="variable">
723
+ <path element="AccountId"/>
724
+ </value>
725
+ </argument>
726
+ <argument id="resultName">
727
+ <value class="value" valueClass="string">AccountRecord</value>
728
+ </argument>
729
+ </arguments>
730
+ <parameterGeneratorProperties>
731
+ <propertyValue name="com.provar.plugins.forcedotcom.ui.commands.ReadCustomObjectTestStepCommand.ConnectionName">Admin</propertyValue>
732
+ <propertyValue name="com.provar.plugins.forcedotcom.ui.commands.ReadCustomObjectTestStepCommand.CustomObjectName">Account</propertyValue>
733
+ </parameterGeneratorProperties>
734
+ <generatedParameters>
735
+ <apiParam group="fields" modelBinding="sf:ui:binding:object?object=Account&amp;field=Name" name="Name" title="Name"/>
736
+ <apiParam group="fields" modelBinding="sf:ui:binding:object?object=Account&amp;field=Phone" name="Phone" title="Phone"/>
737
+ </generatedParameters>
738
+ </apiCall>
739
+ ```
740
+
741
+ ### ApexUpdateObject
742
+
743
+ Updates fields on an existing record. `objectId` is required and must reference a variable.
744
+
745
+ ```xml
746
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.testapis.ApexUpdateObject"
747
+ name="ApexUpdateObject"
748
+ parameterGeneratorUri="command:com.provar.plugins.forcedotcom.ui.commands.UpdateCustomObjectTestStepCommand"
749
+ testItemId="1"
750
+ title="Update Object: Account = {AccountId}">
751
+ <arguments>
752
+ <argument id="apexConnectionName">
753
+ <value class="value" valueClass="string">Admin</value>
754
+ </argument>
755
+ <argument id="objectType">
756
+ <value class="value" valueClass="string">Account</value>
757
+ </argument>
758
+ <argument id="objectId">
759
+ <value class="variable">
760
+ <path element="AccountId"/>
761
+ </value>
762
+ </argument>
763
+ <argument id="Industry">
764
+ <value class="value" valueClass="string">Technology</value>
765
+ </argument>
766
+ </arguments>
767
+ <parameterGeneratorProperties>
768
+ <propertyValue name="com.provar.plugins.forcedotcom.ui.commands.UpdateCustomObjectTestStepCommand.ConnectionName">Admin</propertyValue>
769
+ <propertyValue name="com.provar.plugins.forcedotcom.ui.commands.UpdateCustomObjectTestStepCommand.CustomObjectName">Account</propertyValue>
770
+ </parameterGeneratorProperties>
771
+ <generatedParameters>
772
+ <apiParam group="fields" modelBinding="sf:ui:binding:object?object=Account&amp;field=Industry" name="Industry" title="Industry"/>
773
+ </generatedParameters>
774
+ </apiCall>
775
+ ```
776
+
777
+ ### ApexDeleteObject
778
+
779
+ Deletes a record by ID. No `parameterGeneratorUri` required.
780
+
781
+ ```xml
782
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.testapis.ApexDeleteObject"
783
+ name="ApexDeleteObject" testItemId="1"
784
+ title="Delete Object: {AccountId}">
785
+ <arguments>
786
+ <argument id="apexConnectionName">
787
+ <value class="value" valueClass="string">Admin</value>
788
+ </argument>
789
+ <argument id="objectId">
790
+ <value class="variable">
791
+ <path element="AccountId"/>
792
+ </value>
793
+ </argument>
794
+ </arguments>
795
+ </apiCall>
796
+ ```
797
+
798
+ ### ApexSoqlQuery
799
+
800
+ Runs a SOQL query and stores results as a list variable. Always include `Id` and `Name` in the SELECT clause. Always specify `resultListName` and `resultScope`.
801
+
802
+ ```xml
803
+ <apiCall apiId="com.provar.plugins.forcedotcom.core.testapis.ApexSoqlQuery"
804
+ name="ApexSoqlQuery" testItemId="1"
805
+ title="SOQL Query: SELECT Id, Name FROM Account=&gt;AccountRows">
806
+ <arguments>
807
+ <argument id="apexConnectionName">
808
+ <value class="value" valueClass="string">Admin</value>
809
+ </argument>
810
+ <argument id="soqlQuery">
811
+ <value class="value" valueClass="string">SELECT Id, Name FROM Account WHERE Name = 'Test Account'</value>
812
+ </argument>
813
+ <argument id="resultListName">
814
+ <value class="value" valueClass="string">AccountRows</value>
815
+ </argument>
816
+ <argument id="resultScope">
817
+ <value class="value" valueClass="string">Test</value>
818
+ </argument>
819
+ </arguments>
820
+ </apiCall>
821
+ ```
822
+
823
+ ---
824
+
825
+ ## Control Flow
826
+
827
+ ### SetValues
828
+
829
+ Sets one or more test variables. Use this for all test data instead of hardcoding values across multiple steps. Variable names must match `^[A-Za-z_][A-Za-z0-9_]*$`.
830
+
831
+ > **`dataTable` binding is ignored by the CLI.** Always use `SetValues` for parameterized data when tests
832
+ > run via `sf provar automation testrun` or Quality Hub.
833
+
834
+ ```xml
835
+ <apiCall apiId="com.provar.plugins.bundled.apis.control.SetValues"
836
+ name="SetValues" testItemId="1"
837
+ title="Set Test Data Variables">
838
+ <arguments>
839
+ <argument id="values">
840
+ <value class="valueList" mutable="Mutable">
841
+ <namedValues mutable="Mutable">
842
+ <namedValue name="AccountName">
843
+ <value class="value" valueClass="string">Acme Corporation</value>
844
+ </namedValue>
845
+ <namedValue name="PhoneNumber">
846
+ <value class="value" valueClass="string">555-123-4567</value>
847
+ </namedValue>
848
+ <namedValue name="IsActive">
849
+ <value class="value" valueClass="boolean">true</value>
850
+ </namedValue>
851
+ </namedValues>
852
+ </value>
853
+ </argument>
854
+ </arguments>
855
+ </apiCall>
856
+ ```
857
+
858
+ ### StepGroup
859
+
860
+ Groups steps with a shared label. Substeps go in `<clause name="hidden">`.
861
+
862
+ ```xml
863
+ <apiCall apiId="com.provar.plugins.bundled.apis.control.StepGroup"
864
+ name="StepGroup" testItemId="1"
865
+ title="Setup - Create Connection">
866
+ <arguments>
867
+ <argument id="description">
868
+ <value class="value" valueClass="string">Establish connection to Salesforce</value>
869
+ </argument>
870
+ </arguments>
871
+ <clauses>
872
+ <clause name="hidden" testItemId="2">
873
+ <steps>
874
+ <!-- Steps go here -->
875
+ </steps>
876
+ </clause>
877
+ </clauses>
878
+ </apiCall>
879
+ ```
880
+
881
+ ### If
882
+
883
+ ```xml
884
+ <apiCall apiId="com.provar.plugins.bundled.apis.If"
885
+ name="If" testItemId="1"
886
+ title="If: {IsActive}">
887
+ <arguments>
888
+ <argument id="condition">
889
+ <value class="value" valueClass="string">{IsActive} == true</value>
890
+ </argument>
891
+ </arguments>
892
+ <clauses>
893
+ <clause name="then" testItemId="2">
894
+ <steps>
895
+ <!-- Steps if condition is true -->
896
+ </steps>
897
+ </clause>
898
+ <clause name="else" testItemId="3">
899
+ <steps>
900
+ <!-- Steps if condition is false -->
901
+ </steps>
902
+ </clause>
903
+ </clauses>
904
+ </apiCall>
905
+ ```
906
+
907
+ ### ForEach
908
+
909
+ Iterates over a list variable. `list` must reference an existing variable. `valueName` must be unique within scope.
910
+
911
+ ```xml
912
+ <apiCall apiId="com.provar.plugins.bundled.apis.control.ForEach"
913
+ name="ForEach" testItemId="1"
914
+ title="For Each: {AccountRows}=&gt;CurrentRow">
915
+ <arguments>
916
+ <argument id="list">
917
+ <value class="variable">
918
+ <path element="AccountRows"/>
919
+ </value>
920
+ </argument>
921
+ <argument id="fromItem">
922
+ <value class="value" valueClass="decimal">1</value>
923
+ </argument>
924
+ <argument id="valueName">
925
+ <value class="value" valueClass="string">CurrentRow</value>
926
+ </argument>
927
+ <argument id="continueOnFailure">
928
+ <value class="value" valueClass="boolean">false</value>
929
+ </argument>
930
+ </arguments>
931
+ <clauses>
932
+ <clause name="substeps" testItemId="2">
933
+ <steps>
934
+ <!-- Loop body -->
935
+ </steps>
936
+ </clause>
937
+ </clauses>
938
+ </apiCall>
939
+ ```
940
+
941
+ ### TryCatchFinally
942
+
943
+ Wraps steps with error handling. The `finally` clause always runs, making it the right place for cleanup steps (e.g., delete records created during the test).
944
+
945
+ ```xml
946
+ <apiCall apiId="com.provar.plugins.bundled.apis.control.TryCatchFinally"
947
+ name="TryCatchFinally" testItemId="1"
948
+ title="Try/Catch/Finally">
949
+ <clauses>
950
+ <clause name="try" testItemId="2">
951
+ <steps>
952
+ <!-- Main test steps -->
953
+ </steps>
954
+ </clause>
955
+ <clause name="catch" testItemId="3">
956
+ <steps>
957
+ <!-- Steps to run on failure (optional) -->
958
+ </steps>
959
+ </clause>
960
+ <clause name="finally" testItemId="4">
961
+ <steps>
962
+ <!-- Cleanup: always runs, even on failure -->
963
+ </steps>
964
+ </clause>
965
+ </clauses>
966
+ </apiCall>
967
+ ```
968
+
969
+ ### WaitFor
970
+
971
+ Polls a condition at intervals until true or max iterations are reached.
972
+
973
+ ```xml
974
+ <apiCall apiId="com.provar.plugins.bundled.apis.control.WaitFor"
975
+ name="WaitFor" testItemId="1"
976
+ title="Wait For: {IsComplete}">
977
+ <arguments>
978
+ <argument id="condition">
979
+ <value class="value" valueClass="string">{IsComplete} == true</value>
980
+ </argument>
981
+ <argument id="testAtStart">
982
+ <value class="value" valueClass="boolean">true</value>
983
+ </argument>
984
+ <argument id="maxIterations">
985
+ <value class="value" valueClass="decimal">10</value>
986
+ </argument>
987
+ <argument id="sleepSecs">
988
+ <value class="value" valueClass="decimal">2</value>
989
+ </argument>
990
+ <argument id="continueOnFailure">
991
+ <value class="value" valueClass="boolean">false</value>
992
+ </argument>
993
+ </arguments>
994
+ <clauses>
995
+ <clause name="substeps" testItemId="2">
996
+ <steps>
997
+ <!-- Steps to check the condition -->
998
+ </steps>
999
+ </clause>
1000
+ </clauses>
1001
+ </apiCall>
1002
+ ```
1003
+
1004
+ > **Rule CONTROL-WAITFOR-002:** `maxIterations` is required — omitting it produces an infinite loop.
1005
+
1006
+ ### Sleep
1007
+
1008
+ ```xml
1009
+ <apiCall apiId="com.provar.plugins.bundled.apis.control.Sleep"
1010
+ name="Sleep" testItemId="1"
1011
+ title="Sleep for 5 seconds">
1012
+ <arguments>
1013
+ <argument id="sleepSecs">
1014
+ <value class="value" valueClass="decimal">5</value>
1015
+ </argument>
1016
+ </arguments>
1017
+ </apiCall>
1018
+ ```
1019
+
1020
+ ### CallTest
1021
+
1022
+ Calls another test case as a step (callable tests have `visibility="Internal"`). Use this to share setup logic across test cases.
1023
+
1024
+ ```xml
1025
+ <apiCall apiId="com.provar.plugins.bundled.apis.control.CallTest"
1026
+ name="CallTest" testItemId="1"
1027
+ title="Call: Create Lead API">
1028
+ <arguments>
1029
+ <argument id="testCase">
1030
+ <value class="value" valueClass="string">Callables/Create Lead API</value>
1031
+ </argument>
1032
+ <argument id="LastName">
1033
+ <value class="value" valueClass="string">Smith</value>
1034
+ </argument>
1035
+ <argument id="Company">
1036
+ <value class="value" valueClass="string">Acme</value>
1037
+ </argument>
1038
+ </arguments>
1039
+ </apiCall>
1040
+ ```
1041
+
1042
+ ---
1043
+
1044
+ ## Assertions
1045
+
1046
+ ### AssertValues
1047
+
1048
+ Variable-to-variable or variable-to-literal comparison. For UI field assertions use `UiAssert` instead.
1049
+
1050
+ ```xml
1051
+ <apiCall apiId="com.provar.plugins.bundled.apis.AssertValues"
1052
+ name="AssertValues" testItemId="1"
1053
+ title="Assert: {Expected} EqualTo {Actual}">
1054
+ <arguments>
1055
+ <argument id="expectedValue">
1056
+ <value class="variable">
1057
+ <path element="ExpectedValue"/>
1058
+ </value>
1059
+ </argument>
1060
+ <argument id="comparisonType">
1061
+ <value class="value" valueClass="string">EqualTo</value>
1062
+ </argument>
1063
+ <argument id="actualValue">
1064
+ <value class="variable">
1065
+ <path element="ActualValue"/>
1066
+ </value>
1067
+ </argument>
1068
+ <argument id="caseSensitive">
1069
+ <value class="value" valueClass="boolean">false</value>
1070
+ </argument>
1071
+ <argument id="numeric">
1072
+ <value class="value" valueClass="boolean">false</value>
1073
+ </argument>
1074
+ <argument id="failureMessage">
1075
+ <value class="value" valueClass="string">Values do not match</value>
1076
+ </argument>
1077
+ </arguments>
1078
+ </apiCall>
1079
+ ```
1080
+
1081
+ ---
1082
+
1083
+ ## BDD Steps
1084
+
1085
+ `Given`, `When`, `Then`, `And`, `But` all share the same structure: a `description` argument and a `hidden` clause for substeps. Use them to add a BDD narrative layer over standard steps.
1086
+
1087
+ ```xml
1088
+ <apiCall apiId="com.provar.plugins.bundled.apis.bdd.Given"
1089
+ name="Given" testItemId="1"
1090
+ title="Given: The user is logged in to Salesforce">
1091
+ <arguments>
1092
+ <argument id="description">
1093
+ <value class="value" valueClass="string">The user is logged in to Salesforce</value>
1094
+ </argument>
1095
+ </arguments>
1096
+ <clauses>
1097
+ <clause name="hidden" testItemId="2">
1098
+ <steps>
1099
+ <!-- ApexConnect or UiConnect steps go here -->
1100
+ </steps>
1101
+ </clause>
1102
+ </clauses>
1103
+ </apiCall>
1104
+
1105
+ <!-- And / But follow the same structure -->
1106
+ <apiCall apiId="com.provar.plugins.bundled.apis.bdd.And"
1107
+ name="And" testItemId="5"
1108
+ title="And: The opportunity stage is set to Closed Won">
1109
+ <arguments>
1110
+ <argument id="description">
1111
+ <value class="value" valueClass="string">The opportunity stage is set to Closed Won</value>
1112
+ </argument>
1113
+ </arguments>
1114
+ <clauses>
1115
+ <clause name="hidden" testItemId="6">
1116
+ <steps>
1117
+ <!-- Steps -->
1118
+ </steps>
1119
+ </clause>
1120
+ </clauses>
1121
+ </apiCall>
1122
+ ```
1123
+
1124
+ ---
1125
+
1126
+ ## Database Steps
1127
+
1128
+ > **Critical constraints — read before generating any Database step XML:**
1129
+ >
1130
+ > 1. **`connectionId` must use `valueClass="id"`** — not `"string"`. Using `"string"` causes a runtime type error.
1131
+ > 2. **`DbConnect.resultName` must exactly equal `SqlQuery.dbConnectionName`** — these two values are the coupling point between the steps. A mismatch causes "connection not found" at runtime.
1132
+ > 3. **Never use `{Count(Var)}` or `{Var[0].Field}` string expressions in SetValues/AssertValues** — these are stored verbatim and never evaluated. Use `<value class="funcCall">` for Count and the structured `<value class="variable"><path>` form for indexed field access. See the examples below.
1133
+
1134
+ ### DbConnect
1135
+
1136
+ ```xml
1137
+ <apiCall apiId="com.provar.plugins.bundled.apis.db.DbConnect"
1138
+ name="DbConnect" testItemId="1"
1139
+ title="DB Connect: DbConnection">
1140
+ <arguments>
1141
+ <argument id="connectionName">
1142
+ <value class="value" valueClass="string">MyDatabase</value>
1143
+ </argument>
1144
+ <argument id="connectionId">
1145
+ <!-- MUST be valueClass="id" — not "string" -->
1146
+ <value class="value" valueClass="id">database-connection-uuid</value>
1147
+ </argument>
1148
+ <argument id="autoCommit">
1149
+ <value class="value" valueClass="boolean">true</value>
1150
+ </argument>
1151
+ <argument id="resultName">
1152
+ <!-- This value must exactly match dbConnectionName on every SqlQuery that uses this connection -->
1153
+ <value class="value" valueClass="string">DbConnection</value>
1154
+ </argument>
1155
+ <argument id="resultScope">
1156
+ <value class="value" valueClass="string">Test</value>
1157
+ </argument>
1158
+ </arguments>
1159
+ </apiCall>
1160
+ ```
1161
+
1162
+ ### SqlQuery
1163
+
1164
+ ```xml
1165
+ <apiCall apiId="com.provar.plugins.bundled.apis.db.SqlQuery"
1166
+ name="SqlQuery" testItemId="1"
1167
+ title="SQL Query: DbConnection=&gt;DbResults">
1168
+ <arguments>
1169
+ <argument id="dbConnectionName">
1170
+ <!-- Must exactly equal the resultName on the DbConnect step above -->
1171
+ <value class="value" valueClass="string">DbConnection</value>
1172
+ </argument>
1173
+ <argument id="query">
1174
+ <value class="value" valueClass="string">SELECT Id, Name, Status FROM Users WHERE Status = 'Active'</value>
1175
+ </argument>
1176
+ <argument id="resultName">
1177
+ <value class="value" valueClass="string">DbResults</value>
1178
+ </argument>
1179
+ <argument id="resultScope">
1180
+ <value class="value" valueClass="string">Test</value>
1181
+ </argument>
1182
+ </arguments>
1183
+ </apiCall>
1184
+ ```
1185
+
1186
+ ### Accessing Database Results — funcCall and structured variable paths
1187
+
1188
+ After `SqlQuery`, the result variable (e.g. `DbResults`) is a list of rows. To use the results in `SetValues` or `AssertValues`, you must use structured XML — not `{...}` string expressions.
1189
+
1190
+ #### Count the result rows (funcCall)
1191
+
1192
+ ```xml
1193
+ <!-- ✅ CORRECT — funcCall element evaluates at runtime -->
1194
+ <argument id="value">
1195
+ <value class="funcCall" id="Count">
1196
+ <argument id="value">
1197
+ <value class="variable">
1198
+ <path element="DbResults"/>
1199
+ </value>
1200
+ </argument>
1201
+ </value>
1202
+ </argument>
1203
+
1204
+ <!-- ❌ WRONG — string expression stored verbatim, never evaluated -->
1205
+ <argument id="value">
1206
+ <value class="value" valueClass="string">{Count(DbResults)}</value>
1207
+ </argument>
1208
+ ```
1209
+
1210
+ #### Access a field from a specific row (structured variable path)
1211
+
1212
+ ```xml
1213
+ <!-- ✅ CORRECT — structured path with index filter (0-based) -->
1214
+ <argument id="value">
1215
+ <value class="variable">
1216
+ <path element="DbResults">
1217
+ <filter class="index">
1218
+ <index valueClass="decimal">0</index>
1219
+ </filter>
1220
+ </path>
1221
+ <path element="Status"/>
1222
+ </value>
1223
+ </argument>
1224
+
1225
+ <!-- ❌ WRONG — string expression stored verbatim, never evaluated -->
1226
+ <argument id="value">
1227
+ <value class="value" valueClass="string">{DbResults[0].Status}</value>
1228
+ </argument>
1229
+ ```
1230
+
1231
+ #### Full SetValues example — extract row count and first-row field
1232
+
1233
+ ```xml
1234
+ <apiCall apiId="com.provar.plugins.bundled.apis.control.SetValues"
1235
+ name="SetValues" testItemId="3"
1236
+ title="Extract DB result values">
1237
+ <arguments>
1238
+ <argument id="values">
1239
+ <value class="list">
1240
+ <!-- Row count into RowCount variable -->
1241
+ <value class="namedValue" name="RowCount">
1242
+ <value class="funcCall" id="Count">
1243
+ <argument id="value">
1244
+ <value class="variable">
1245
+ <path element="DbResults"/>
1246
+ </value>
1247
+ </argument>
1248
+ </value>
1249
+ </value>
1250
+ <!-- First row Status field into FirstStatus variable -->
1251
+ <value class="namedValue" name="FirstStatus">
1252
+ <value class="variable">
1253
+ <path element="DbResults">
1254
+ <filter class="index">
1255
+ <index valueClass="decimal">0</index>
1256
+ </filter>
1257
+ </path>
1258
+ <path element="Status"/>
1259
+ </value>
1260
+ </value>
1261
+ </value>
1262
+ </argument>
1263
+ <argument id="resultScope">
1264
+ <value class="value" valueClass="string">Test</value>
1265
+ </argument>
1266
+ </arguments>
1267
+ </apiCall>
1268
+ ```
1269
+
1270
+ ---
1271
+
1272
+ ## Web Service Steps
1273
+
1274
+ ### WebConnect
1275
+
1276
+ ```xml
1277
+ <apiCall apiId="com.provar.plugins.bundled.apis.restservice.WebConnect"
1278
+ name="WebConnect" testItemId="1"
1279
+ title="Web Connect: WebServiceConnection">
1280
+ <arguments>
1281
+ <argument id="connectionName">
1282
+ <value class="value" valueClass="string">MyWebService</value>
1283
+ </argument>
1284
+ <argument id="connectionId">
1285
+ <value class="value" valueClass="string">web-service-connection-id</value>
1286
+ </argument>
1287
+ <argument id="resultName">
1288
+ <value class="value" valueClass="string">WebServiceConnection</value>
1289
+ </argument>
1290
+ <argument id="resultScope">
1291
+ <value class="value" valueClass="string">Test</value>
1292
+ </argument>
1293
+ </arguments>
1294
+ </apiCall>
1295
+ ```
1296
+
1297
+ ### RestRequest
1298
+
1299
+ ```xml
1300
+ <apiCall apiId="com.provar.plugins.bundled.apis.restservice.RestRequest"
1301
+ name="RestRequest" testItemId="1"
1302
+ title="Web Request (REST): GET /api/users=&gt;RestResponse">
1303
+ <arguments>
1304
+ <argument id="connectionName">
1305
+ <value class="value" valueClass="string">WebServiceConnection</value>
1306
+ </argument>
1307
+ <argument id="targetValue">
1308
+ <value class="value" valueClass="string">GET</value>
1309
+ </argument>
1310
+ <argument id="resultName">
1311
+ <value class="value" valueClass="string">RestResponse</value>
1312
+ </argument>
1313
+ <argument id="resultScope">
1314
+ <value class="value" valueClass="string">Test</value>
1315
+ </argument>
1316
+ <argument id="statusResultName">
1317
+ <value class="value" valueClass="string">RestStatus</value>
1318
+ </argument>
1319
+ <argument id="restResourceUrl">
1320
+ <value class="value" valueClass="string">/api/users</value>
1321
+ </argument>
1322
+ </arguments>
1323
+ </apiCall>
1324
+ ```
1325
+
1326
+ ---
1327
+
1328
+ ## Value Types Reference
1329
+
1330
+ ```xml
1331
+ <!-- String literal -->
1332
+ <value class="value" valueClass="string">Text content</value>
1333
+
1334
+ <!-- Boolean -->
1335
+ <value class="value" valueClass="boolean">true</value>
1336
+
1337
+ <!-- Number -->
1338
+ <value class="value" valueClass="decimal">123.45</value>
1339
+
1340
+ <!-- GUID / record ID — required for connectionId on ApexConnect -->
1341
+ <value class="value" valueClass="id">74c34c63-ad34-43d9-bb12-cd783bd9bcdd</value>
1342
+
1343
+ <!-- Variable reference -->
1344
+ <value class="variable">
1345
+ <path element="VariableName"/>
1346
+ </value>
1347
+
1348
+ <!-- Nested object field access -->
1349
+ <value class="variable">
1350
+ <path element="ObjectName"/>
1351
+ <path element="FieldName"/>
1352
+ </value>
1353
+
1354
+ <!-- String concatenation (compound) -->
1355
+ <value class="compound">
1356
+ <parts>
1357
+ <value class="value" valueClass="string">Prefix </value>
1358
+ <value class="variable">
1359
+ <path element="DynamicPart"/>
1360
+ </value>
1361
+ <value class="value" valueClass="string"> suffix</value>
1362
+ </parts>
1363
+ </value>
1364
+
1365
+ <!-- UI wait (use on beforeWait / afterWait) -->
1366
+ <value class="uiWait" uri="default"/>
1367
+
1368
+ <!-- UI wait with custom timeout (use on autoRetry) -->
1369
+ <value class="uiWait" uri="ui:wait:autoRetry:timeout=10"/>
1370
+ ```
1371
+
1372
+ ---
1373
+
1374
+ ## Callable Test Structure
1375
+
1376
+ A callable test (used by `CallTest`) declares its interface via `<params>`, `<outputParams>`, `<args>`, and `<outputArgs>` elements. Set `visibility="Internal"` on the `<testCase>` root.
1377
+
1378
+ ```xml
1379
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1380
+ <testCase guid="..." visibility="Internal">
1381
+ <summary/>
1382
+ <steps>
1383
+ <!-- Steps that use input params as variables -->
1384
+ </steps>
1385
+ <params>
1386
+ <param name="LastName" title="Last Name" passwordVariableAllowed="true">
1387
+ <summary/>
1388
+ </param>
1389
+ </params>
1390
+ <outputParams>
1391
+ <param name="RecordId" title="Record Id" passwordVariableAllowed="true">
1392
+ <summary/>
1393
+ <sourceValue class="variable">
1394
+ <path element="RecordId"/>
1395
+ </sourceValue>
1396
+ </param>
1397
+ </outputParams>
1398
+ <args>
1399
+ <argument id="LastName"/>
1400
+ </args>
1401
+ <outputArgs>
1402
+ <outputArgument id="RecordId">
1403
+ <name class="value" valueClass="string">RecordId</name>
1404
+ </outputArgument>
1405
+ </outputArgs>
1406
+ </testCase>
1407
+ ```
1408
+
1409
+ ---
1410
+
1411
+ ## Validation Checklist
1412
+
1413
+ - [ ] **API IDs** — exact match from the reference table above (no variations, no `<UI4SF:*>` elements)
1414
+ - [ ] **Connections** — first step is `ApexConnect` or `UiConnect`; `resultName` is referenced consistently by all subsequent steps
1415
+ - [ ] **`connectionId`** — uses `valueClass="id"` (GUID), not `valueClass="string"`
1416
+ - [ ] **UI nesting** — `UiDoAction` and `UiAssert` are always inside `<clause name="substeps">` of a `UiWithScreen`
1417
+ - [ ] **First UiWithScreen** — `navigate` is `Always` or `IfNecessary`, never `Dont`
1418
+ - [ ] **UiWithScreen on Edit/View with `navigate=Always`** — includes `sfUiTargetObjectId` referencing a variable
1419
+ - [ ] **UiAssert required arguments** — `columnAssertions`, `pageAssertions`, `resultScope`, `captureAfter`, `beforeWait`, `autoRetry` all present (may be empty)
1420
+ - [ ] **UiAssert no generatedParameters** — do not add a `<generatedParameters>` block to `UiAssert`
1421
+ - [ ] **UiConnect arguments** — does NOT include `autoCleanup`, `quickUiLogin`, `closeAllPrimaryTabs`, `alreadyOpenBehaviour`, `lightningMode`, `uiApplicationName`, `cleanupConnectionName`
1422
+ - [ ] **SOQL** — queries include `SELECT` and `FROM`; include `Id` and `Name`; use `resultListName`
1423
+ - [ ] **CRUD result capture** — `ApexCreateObject` has `resultIdName`; `ApexReadObject` has `objectId` (variable)
1424
+ - [ ] **CRUD generator metadata** — `ApexCreateObject`, `ApexReadObject`, `ApexUpdateObject` include `parameterGeneratorUri`, `parameterGeneratorProperties`, and `generatedParameters`
1425
+ - [ ] **Variable definition** — variables used as `<value class="variable">` are defined by a prior step in scope
1426
+ - [ ] **Control flow** — `If` has `condition`; `ForEach` has `list` and `valueName`; `WaitFor` has `maxIterations`
1427
+ - [ ] **SetValues structure** — `values` argument contains `<value class="valueList">` wrapping `<namedValues>` wrapping `<namedValue>` elements
1428
+ - [ ] **Data types** — booleans are string `"true"`/`"false"` inside `valueClass="boolean"`; numbers use `valueClass="decimal"`
1429
+ - [ ] **Cleanup** — either `autoCleanup="true"` on the connection, or explicit `ApexDeleteObject` / `TryCatchFinally` finally block
1430
+ - [ ] **No hallucinated arguments** — see the Common AI Hallucinations table at the top of this doc