@salesforce/afv-skills 1.41.0 → 1.43.0

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 (288) hide show
  1. package/package.json +2 -6
  2. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +3 -0
  3. package/skills/automation-sandbox-post-copy-configure/SKILL.md +4 -0
  4. package/skills/design-systems-slds-validate/SKILL.md +2 -2
  5. package/skills/dx-app-analytics-query/SKILL.md +1 -3
  6. package/skills/dx-devops-conflict-resolve/SKILL.md +205 -0
  7. package/skills/dx-devops-conflict-resolve/examples/conflict-workflows.md +165 -0
  8. package/skills/dx-devops-conflict-resolve/references/deploy-failure-resolution.md +75 -0
  9. package/skills/dx-devops-conflict-resolve/references/git-conflict-resolution.md +124 -0
  10. package/skills/dx-devops-conflict-resolve/scripts/detect-conflicts.sh +91 -0
  11. package/skills/dx-devops-conflict-resolve/scripts/diagnose-deploy-failure.sh +156 -0
  12. package/skills/dx-devops-request-status/SKILL.md +160 -0
  13. package/skills/dx-devops-request-status/examples/polling-workflows.md +101 -0
  14. package/skills/dx-devops-request-status/references/cli-commands.md +176 -0
  15. package/skills/dx-devops-request-status/scripts/poll-status.sh +134 -0
  16. package/skills/dx-devops-test-failures-analyze/SKILL.md +6 -6
  17. package/skills/dx-devops-test-pipeline-configure/SKILL.md +7 -7
  18. package/skills/dx-devops-test-suite-assignments-configure/SKILL.md +6 -6
  19. package/skills/dx-devops-test-suite-run/SKILL.md +6 -6
  20. package/skills/dx-devops-work-item-manage/SKILL.md +2 -0
  21. package/skills/dx-org-manage/references/creating-scratch-org.md +5 -2
  22. package/skills/dx-org-manage/references/creating-snapshot.md +1 -0
  23. package/skills/dx-org-shape-manage/SKILL.md +152 -0
  24. package/skills/dx-org-shape-manage/examples/create_error_output.json +9 -0
  25. package/skills/dx-org-shape-manage/examples/create_success_output.json +9 -0
  26. package/skills/dx-org-shape-manage/examples/delete_output.json +11 -0
  27. package/skills/dx-org-shape-manage/examples/list_inactive_output.json +32 -0
  28. package/skills/dx-org-shape-manage/examples/list_output.json +24 -0
  29. package/skills/dx-org-shape-manage/references/cli_flags.md +135 -0
  30. package/skills/dx-org-switch/SKILL.md +2 -2
  31. package/skills/dx-org-trial-expiration-check/SKILL.md +2 -0
  32. package/skills/dx-pkg-post-install-configure/SKILL.md +3 -0
  33. package/skills/education-cloud-multi-campus-configure/SKILL.md +211 -0
  34. package/skills/education-cloud-multi-campus-configure/examples/hierarchy_visualization.md +174 -0
  35. package/skills/education-cloud-multi-campus-configure/examples/output_examples.md +54 -0
  36. package/skills/education-cloud-multi-campus-configure/examples/sample_hierarchy_input.csv +29 -0
  37. package/skills/education-cloud-multi-campus-configure/examples/sample_hierarchy_input_edgecases.csv +16 -0
  38. package/skills/education-cloud-multi-campus-configure/references/account_recordtype_prerequisite.md +44 -0
  39. package/skills/education-cloud-multi-campus-configure/references/delta_computation.md +33 -0
  40. package/skills/education-cloud-multi-campus-configure/references/error_handling.md +198 -0
  41. package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +54 -0
  42. package/skills/education-cloud-multi-campus-configure/references/gotchas.md +16 -0
  43. package/skills/education-cloud-multi-campus-configure/references/hierarchy_parsing_rules.md +133 -0
  44. package/skills/education-cloud-multi-campus-configure/references/mcp-invocation.md +75 -0
  45. package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/SKILL.md +1 -1
  46. package/skills/experience-cms-brand-apply/SKILL.md +4 -1
  47. package/skills/experience-cms-brand-create/SKILL.md +197 -0
  48. package/skills/experience-cms-brand-create/assets/brand-template.json +329 -0
  49. package/skills/experience-cms-brand-create/references/brand-anatomy.md +120 -0
  50. package/skills/experience-cms-brand-create/references/disk-contract.md +84 -0
  51. package/skills/experience-cms-content-generate/SKILL.md +190 -0
  52. package/skills/experience-cms-content-generate/assets/display-formats.md +84 -0
  53. package/skills/experience-cms-content-generate/assets/payloads/create-content-bulk.json +11 -0
  54. package/skills/experience-cms-content-generate/assets/payloads/create-content-single.json +11 -0
  55. package/skills/experience-cms-content-generate/assets/payloads/publish-content.json +4 -0
  56. package/skills/experience-cms-content-generate/assets/payloads/update-content.json +9 -0
  57. package/skills/experience-cms-content-generate/assets/questions.md +241 -0
  58. package/skills/experience-cms-content-generate/examples/create-content-call.md +81 -0
  59. package/skills/experience-cms-content-generate/references/bulk-batching.md +56 -0
  60. package/skills/experience-cms-content-generate/references/content-type-classification.md +88 -0
  61. package/skills/experience-cms-content-generate/references/content-write-tool.md +95 -0
  62. package/skills/experience-cms-content-generate/references/delegation-protocol.md +37 -0
  63. package/skills/experience-cms-content-generate/references/edit-publish-workflow.md +63 -0
  64. package/skills/experience-cms-content-generate/references/error-recovery.md +72 -0
  65. package/skills/experience-cms-content-generate/references/identifier-resolution.md +26 -0
  66. package/skills/experience-cms-content-generate/references/intent-routing.md +45 -0
  67. package/skills/experience-cms-content-generate/references/principles.md +19 -0
  68. package/skills/experience-cms-content-generate/references/ux-rules.md +68 -0
  69. package/skills/experience-cms-content-generate/references/workspace-resolution.md +33 -0
  70. package/skills/experience-cms-content-type-generate/SKILL.md +449 -0
  71. package/skills/experience-cms-content-type-generate/assets/discovery-prompts.md +79 -0
  72. package/skills/experience-cms-content-type-generate/assets/schema-example.json +70 -0
  73. package/skills/experience-cms-content-type-generate/references/agent-checklist.md +50 -0
  74. package/skills/experience-cms-content-type-generate/references/anti-patterns.md +39 -0
  75. package/skills/experience-cms-content-type-generate/references/deployment-errors.md +27 -0
  76. package/skills/experience-cms-content-type-generate/references/discovery-details.md +106 -0
  77. package/skills/experience-cms-content-type-generate/references/discovery-query-rules.md +85 -0
  78. package/skills/experience-cms-content-type-generate/references/edit-fields-loop.md +48 -0
  79. package/skills/experience-cms-content-type-generate/references/pre-deploy-checklist.md +21 -0
  80. package/skills/experience-cms-content-type-generate/references/retrieve-and-reconcile.md +105 -0
  81. package/skills/experience-cms-content-type-generate/references/schema-rules.md +51 -0
  82. package/skills/experience-cms-content-type-generate/references/schema-summary-format.md +60 -0
  83. package/skills/experience-lds-graphql-generate/SKILL.md +4 -0
  84. package/skills/experience-lwc-accessibility-jest-run/SKILL.md +79 -0
  85. package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +205 -0
  86. package/skills/experience-lwc-design-generate/SKILL.md +5 -4
  87. package/skills/experience-lwc-generate/SKILL.md +10 -9
  88. package/skills/experience-ui-bundle-deploy/references/dev-preview.md +21 -0
  89. package/skills/experience-ui-bundle-deploy/references/social-login.md +1 -0
  90. package/skills/experience-ui-bundle-file-upload-generate/SKILL.md +6 -6
  91. package/skills/experience-ui-bundle-localize/SKILL.md +30 -37
  92. package/skills/experience-ui-bundle-localize/references/gotchas.md +23 -7
  93. package/skills/experience-ui-bundle-localize/references/i18n-setup.md +48 -4
  94. package/skills/experience-ui-bundle-localize/references/label-xml.md +22 -8
  95. package/skills/experience-ui-bundle-localize/references/verifying.md +39 -11
  96. package/skills/experience-ui-bundle-localize/scripts/detect-bundle-type.sh +118 -15
  97. package/skills/experience-ui-bundle-localize/scripts/tests/test-detect-bundle-type.sh +187 -0
  98. package/skills/experience-ui-bundle-mfa-configure/SKILL.md +13 -2
  99. package/skills/experience-ui-bundle-mfa-configure/references/social-login.md +1 -0
  100. package/skills/experience-ui-bundle-project-generate/SKILL.md +2 -2
  101. package/skills/integration-connectivity-generate/SKILL.md +11 -10
  102. package/skills/integration-eventing-cdc-configure/SKILL.md +6 -6
  103. package/skills/integration-eventing-subscription-configure/SKILL.md +6 -5
  104. package/skills/mobile-platform-native-capabilities-integrate/SKILL.md +2 -2
  105. package/skills/mobile-platform-offline-validate/scripts/package.json +1 -1
  106. package/skills/platform-agentexchange-partner-offers-configure/SKILL.md +8 -8
  107. package/skills/platform-custom-application-generate/SKILL.md +2 -0
  108. package/skills/platform-custom-field-generate/SKILL.md +8 -6
  109. package/skills/platform-custom-metadata-type-generate/SKILL.md +460 -0
  110. package/skills/platform-custom-metadata-type-generate/references/cmdt-records.md +251 -0
  111. package/skills/platform-custom-metadata-type-generate/scripts/sanitize-developer-name.sh +46 -0
  112. package/skills/platform-custom-setting-generate/SKILL.md +419 -0
  113. package/skills/platform-dataspace-access-configure/SKILL.md +3 -3
  114. package/skills/platform-sharing-owd-configure/SKILL.md +3 -0
  115. package/skills/platform-value-set-generate/SKILL.md +2 -0
  116. package/skills/service-agentforce-channel-configure/SKILL.md +29 -20
  117. package/skills/service-agentforce-channel-configure/assets/BotEmailDefinition.botEmailDefinition-meta.xml +36 -0
  118. package/skills/service-agentforce-channel-configure/assets/email/unfiled$public/AgentforceForServiceEmailTemplate.email +17 -0
  119. package/skills/service-agentforce-channel-configure/assets/email/unfiled$public/AgentforceForServiceEmailTemplate.email-meta.xml +29 -0
  120. package/skills/service-agentforce-channel-configure/assets/mdapi-package.xml +25 -0
  121. package/skills/service-agentforce-channel-configure/assets/settings-mdapi-package.xml +30 -0
  122. package/skills/service-agentforce-channel-configure/references/agent-wiring.md +20 -0
  123. package/skills/service-agentforce-channel-configure/references/botemaildefinition.md +107 -0
  124. package/skills/service-agentforce-channel-configure/references/channel-branch-email.md +203 -50
  125. package/skills/service-agentforce-channel-configure/references/channel-types.md +30 -3
  126. package/skills/service-agentforce-channel-configure/references/queue-resolution.md +17 -8
  127. package/skills/service-agentforce-channel-configure/references/routing-flow.md +14 -3
  128. package/skills/service-agentforce-channel-configure/scripts/validate-botemaildefinition.py +126 -0
  129. package/skills/service-agentforce-channel-configure/scripts/validate-emailtemplate.py +133 -0
  130. package/skills/service-de-channel-activate/SKILL.md +271 -0
  131. package/skills/service-de-channel-activate/references/gotchas.md +31 -0
  132. package/skills/service-de-channel-activate/references/phone-verification.md +96 -0
  133. package/skills/service-de-channel-activate/references/worked-examples.md +14 -0
  134. package/skills/service-de-channel-consent-configure/SKILL.md +230 -0
  135. package/skills/service-de-channel-consent-configure/references/gotchas.md +41 -0
  136. package/skills/service-de-channel-consent-configure/references/language-keywords.md +84 -0
  137. package/skills/service-de-channel-consent-configure/references/worked-examples.md +134 -0
  138. package/skills/service-de-channel-create/SKILL.md +369 -0
  139. package/skills/service-de-channel-create/references/apple.md +59 -0
  140. package/skills/service-de-channel-create/references/connect-insert.md +84 -0
  141. package/skills/service-de-channel-create/references/facebook.md +154 -0
  142. package/skills/service-de-channel-create/references/line.md +66 -0
  143. package/skills/service-de-channel-create/references/sms.md +98 -0
  144. package/skills/service-de-channel-create/references/whatsapp.md +98 -0
  145. package/skills/service-de-channel-create/references/worked-examples.md +70 -0
  146. package/skills/service-de-channel-routing-configure/SKILL.md +345 -0
  147. package/skills/service-de-channel-routing-configure/references/asa-routing.md +90 -0
  148. package/skills/service-de-channel-routing-configure/references/gotchas.md +27 -0
  149. package/skills/service-de-channel-routing-configure/references/queue-creation.md +123 -0
  150. package/skills/service-de-channel-routing-configure/references/target-locate.md +98 -0
  151. package/skills/service-de-channel-routing-configure/references/worked-examples.md +174 -0
  152. package/skills/service-de-headless-channel-configure/SKILL.md +305 -0
  153. package/skills/service-de-headless-channel-configure/references/gotchas.md +27 -0
  154. package/skills/service-de-headless-channel-configure/references/inputs.md +49 -0
  155. package/skills/service-de-headless-channel-configure/references/output-envelopes.md +53 -0
  156. package/skills/service-de-headless-channel-configure/references/partial-success.md +24 -0
  157. package/skills/service-de-headless-channel-configure/references/terms-and-conditions.md +54 -0
  158. package/skills/service-de-headless-channel-configure/references/worked-examples.md +108 -0
  159. package/skills/service-de-waba-integrate/SKILL.md +227 -0
  160. package/skills/service-digital-engagement-channel-configure/scripts/check-api-version.sh +29 -0
  161. package/skills/service-digital-engagement-messaging-site-integrate/SKILL.md +10 -9
  162. package/skills/service-digital-engagement-messaging-site-integrate/references/lwr_patch.md +39 -21
  163. package/skills/service-digital-engagement-messaging-site-integrate/scripts/patch_lwr_bundle.sh +157 -96
  164. package/skills/service-email-to-case-configure/SKILL.md +190 -0
  165. package/skills/service-email-to-case-configure/assets/CaseSettings.settings-meta.xml +45 -0
  166. package/skills/service-email-to-case-configure/examples/CaseSettings-two-addresses.settings-meta.xml +36 -0
  167. package/skills/service-email-to-case-configure/references/apply-mechanics.md +73 -0
  168. package/skills/service-email-to-case-configure/references/routing_address_reference.md +73 -0
  169. package/skills/service-email-to-case-configure/references/troubleshooting.md +25 -0
  170. package/skills/service-email-to-case-configure/scripts/apply-casesettings.py +1315 -0
  171. package/skills/service-email-to-case-configure/scripts/check-agent-email-capability.sh +26 -0
  172. package/skills/service-email-to-case-configure/scripts/tests/__init__.py +0 -0
  173. package/skills/service-email-to-case-configure/scripts/tests/_bootstrap.py +45 -0
  174. package/skills/service-email-to-case-configure/scripts/tests/_fakeorg.py +366 -0
  175. package/skills/service-email-to-case-configure/scripts/tests/_run.py +84 -0
  176. package/skills/service-email-to-case-configure/scripts/tests/test_apply_org_scenarios.py +655 -0
  177. package/skills/service-email-to-case-configure/scripts/tests/test_get_session.py +134 -0
  178. package/skills/service-email-to-case-configure/scripts/tests/test_new_capabilities.py +193 -0
  179. package/skills/service-email-to-case-configure/scripts/tests/test_validate_casesettings.py +170 -0
  180. package/skills/service-email-to-case-configure/scripts/validate-casesettings.py +229 -0
  181. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +33 -15
  182. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -47
  183. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/SKILL.md +17 -12
  184. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/references/cli-invocation.md +5 -3
  185. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/classify-enable-plan.mjs +11 -3
  186. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/SKILL.md +8 -6
  187. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/references/cli-invocation.md +3 -3
  188. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/scripts/classify-readiness.mjs +11 -3
  189. package/skills/service-itsm-agentic-setup-configure/SKILL.md +5 -5
  190. package/skills/service-itsm-agentic-setup-configure/examples/output-templates.md +5 -5
  191. package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +2 -2
  192. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +11 -7
  193. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +20 -7
  194. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +10 -6
  195. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/action-availability.md +7 -7
  196. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +11 -7
  197. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/build-create-body.mjs +24 -17
  198. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-action-availability.mjs +98 -58
  199. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +20 -7
  200. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/strip-release-management.mjs +176 -0
  201. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/SKILL.md +19 -19
  202. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/cli-invocation.md +3 -4
  203. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/helper-contracts.md +6 -6
  204. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/permset-topology.md +3 -10
  205. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-permset-availability.mjs +7 -9
  206. package/skills/service-itsm-incident-mgmt-configure/SKILL.md +2 -0
  207. package/skills/service-itsm-incident-priority-configure/SKILL.md +15 -12
  208. package/skills/service-itsm-incident-priority-configure/examples/matrix-operations.md +27 -1
  209. package/skills/service-itsm-incident-priority-configure/references/sf-cli-invocation.md +37 -6
  210. package/skills/service-native-voice-recording-transcription-configure/SKILL.md +146 -0
  211. package/skills/service-native-voice-recording-transcription-configure/assets/confirmation-output.json +16 -0
  212. package/skills/service-native-voice-recording-transcription-configure/assets/package.xml +22 -0
  213. package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +101 -0
  214. package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +181 -0
  215. package/skills/data360-activate/README.md +0 -38
  216. package/skills/data360-activate/SKILL.md +0 -127
  217. package/skills/data360-connect/README.md +0 -57
  218. package/skills/data360-connect/SKILL.md +0 -163
  219. package/skills/data360-connect/examples/connections/heroku-postgres.json +0 -15
  220. package/skills/data360-connect/examples/connections/ingest-api-connection.json +0 -5
  221. package/skills/data360-connect/examples/connections/ingest-api-schema.json +0 -31
  222. package/skills/data360-connect/examples/connections/redshift.json +0 -16
  223. package/skills/data360-connect/examples/connections/sharepoint-unstructured.json +0 -20
  224. package/skills/data360-connect/examples/connections/snowflake-connection.json +0 -42
  225. package/skills/data360-harmonize/README.md +0 -31
  226. package/skills/data360-harmonize/SKILL.md +0 -126
  227. package/skills/data360-orchestrate/README.md +0 -120
  228. package/skills/data360-orchestrate/SKILL.md +0 -263
  229. package/skills/data360-orchestrate/assets/definitions/activation-target.template.json +0 -5
  230. package/skills/data360-orchestrate/assets/definitions/activation.template.json +0 -7
  231. package/skills/data360-orchestrate/assets/definitions/calculated-insight.template.json +0 -7
  232. package/skills/data360-orchestrate/assets/definitions/data-action-target.template.json +0 -5
  233. package/skills/data360-orchestrate/assets/definitions/data-action.template.json +0 -5
  234. package/skills/data360-orchestrate/assets/definitions/data-graph.template.json +0 -21
  235. package/skills/data360-orchestrate/assets/definitions/data-stream.template.json +0 -55
  236. package/skills/data360-orchestrate/assets/definitions/dmo.template.json +0 -17
  237. package/skills/data360-orchestrate/assets/definitions/identity-resolution.template.json +0 -30
  238. package/skills/data360-orchestrate/assets/definitions/mapping.template.json +0 -14
  239. package/skills/data360-orchestrate/assets/definitions/relationship.template.json +0 -12
  240. package/skills/data360-orchestrate/assets/definitions/search-index.template.json +0 -9
  241. package/skills/data360-orchestrate/assets/definitions/segment.template.json +0 -16
  242. package/skills/data360-orchestrate/references/feature-readiness.md +0 -157
  243. package/skills/data360-orchestrate/references/plugin-setup.md +0 -138
  244. package/skills/data360-orchestrate/scripts/bootstrap-plugin.sh +0 -53
  245. package/skills/data360-orchestrate/scripts/diagnose-org.mjs +0 -511
  246. package/skills/data360-orchestrate/scripts/generate-manifest.mjs +0 -68
  247. package/skills/data360-orchestrate/scripts/verify-plugin.sh +0 -58
  248. package/skills/data360-prepare/README.md +0 -50
  249. package/skills/data360-prepare/SKILL.md +0 -203
  250. package/skills/data360-prepare/examples/ingestion-api/.env.example +0 -8
  251. package/skills/data360-prepare/examples/ingestion-api/README.md +0 -48
  252. package/skills/data360-prepare/examples/ingestion-api/send-data.py +0 -144
  253. package/skills/data360-query/README.md +0 -43
  254. package/skills/data360-query/SKILL.md +0 -128
  255. package/skills/data360-query/examples/search-indexes/hybrid-structured.json +0 -44
  256. package/skills/data360-query/examples/search-indexes/vector-knowledge.json +0 -43
  257. package/skills/data360-segment/README.md +0 -35
  258. package/skills/data360-segment/SKILL.md +0 -123
  259. package/skills/service-helpagent-coordinate/references/channel-help-portal.md +0 -22
  260. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-1-1-non-text-content.md +0 -0
  261. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-i-lists.md +0 -0
  262. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-ii-tables.md +0 -0
  263. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-iii-form-labels.md +0 -0
  264. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-iv-regions.md +0 -0
  265. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-v-groups.md +0 -0
  266. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-5-identify-input.md +0 -0
  267. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-4-3-contrast.md +0 -0
  268. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-1-1-keyboard.md +0 -0
  269. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-4-4-link-purpose.md +0 -0
  270. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-4-6-headings-labels.md +0 -0
  271. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -0
  272. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -0
  273. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-3-label-in-name.md +0 -0
  274. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-7-dragging-movement.md +0 -0
  275. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-2-1-on-focus.md +0 -0
  276. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-2-2-on-input.md +0 -0
  277. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-1-error-identification.md +0 -0
  278. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-2-labels-instructions.md +0 -0
  279. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-3-error-suggestion.md +0 -0
  280. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-i-name.md +0 -0
  281. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-ii-role.md +0 -0
  282. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-iii-value.md +0 -0
  283. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-1-1-non-text-content.md +0 -0
  284. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-1-use-of-color.md +0 -0
  285. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-10-resize-reflow.md +0 -0
  286. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-11-non-text-contrast.md +0 -0
  287. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-3-contrast.md +0 -0
  288. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/scripts/contrast-ratio.py +0 -0
@@ -0,0 +1,134 @@
1
+ """Tests for get_session's access-token acquisition and _usable_access_token.
2
+
3
+ The FakeOrg-based suites patch get_session wholesale, so they never exercise the
4
+ token-selection logic. This module covers it directly:
5
+
6
+ * _usable_access_token — accept a live `<orgId>!<secret>` token, reject the
7
+ `[REDACTED ...]` marker recent CLIs emit, and reject None/empty/non-string.
8
+ * get_session — prefer `sf org auth show-access-token`; on a redacted or
9
+ missing primary token, fall back to a `--verbose` `sf org display`; hard-fail
10
+ when neither yields a usable token.
11
+
12
+ All `sf` invocations are mocked at the sf_json / sf_json_optional layer — no
13
+ live org required.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import unittest
18
+ from unittest import mock
19
+
20
+ from . import _bootstrap
21
+
22
+ apply_mod = _bootstrap.load_apply()
23
+
24
+ # A value shaped like a live Salesforce session token: <orgId>!<secret>.
25
+ REAL_TOKEN = "00D000000000001AAA!AQ" + "x" * 90
26
+ # The marker recent CLIs return from `sf org display` in place of the token.
27
+ REDACTED = "[REDACTED] Use 'sf org auth show-access-token' to view the token"
28
+
29
+ DISPLAY = {
30
+ "instanceUrl": "https://example.my.salesforce.com",
31
+ "username": "auth@example.com",
32
+ "apiVersion": "68.0",
33
+ }
34
+
35
+
36
+ def _dispatch(show_at, verbose):
37
+ """Build an sf_json_optional stand-in that returns ``show_at`` for the
38
+ show-access-token call and ``verbose`` for the `--verbose` display call."""
39
+ def _impl(args):
40
+ if "show-access-token" in args:
41
+ return show_at
42
+ if "--verbose" in args:
43
+ return verbose
44
+ return None
45
+ return _impl
46
+
47
+
48
+ class UsableAccessToken(unittest.TestCase):
49
+ """_usable_access_token: live token has '!' and is not the redaction marker."""
50
+
51
+ def test_real_token(self):
52
+ self.assertTrue(apply_mod._usable_access_token(REAL_TOKEN))
53
+
54
+ def test_redaction_marker_rejected(self):
55
+ self.assertFalse(apply_mod._usable_access_token(REDACTED))
56
+
57
+ def test_none_rejected(self):
58
+ self.assertFalse(apply_mod._usable_access_token(None))
59
+
60
+ def test_empty_rejected(self):
61
+ self.assertFalse(apply_mod._usable_access_token(""))
62
+
63
+ def test_token_without_separator_rejected(self):
64
+ self.assertFalse(apply_mod._usable_access_token("noseparator" + "x" * 40))
65
+
66
+ def test_non_string_rejected(self):
67
+ self.assertFalse(apply_mod._usable_access_token(12345))
68
+
69
+
70
+ class GetSessionTokenAcquisition(unittest.TestCase):
71
+ """get_session: primary show-access-token, verbose fallback, hard-fail."""
72
+
73
+ def setUp(self):
74
+ # get_session derives API_VERSION from the org unless forced; keep the
75
+ # derivation path deterministic regardless of test ordering.
76
+ apply_mod._API_VERSION_EXPLICIT = False
77
+
78
+ def _run(self, show_at, verbose):
79
+ opt = mock.MagicMock(side_effect=_dispatch(show_at, verbose))
80
+ with mock.patch.object(apply_mod, "sf_json", return_value=dict(DISPLAY)), \
81
+ mock.patch.object(apply_mod, "sf_json_optional", opt):
82
+ result = apply_mod.get_session("myorg")
83
+ return result, opt
84
+
85
+ @staticmethod
86
+ def _verbose_calls(opt):
87
+ return [c for c in opt.call_args_list if "--verbose" in c.args[0]]
88
+
89
+ def test_primary_path_uses_show_access_token(self):
90
+ (token, md_url, inst, user), opt = self._run(
91
+ show_at={"accessToken": REAL_TOKEN},
92
+ verbose={"accessToken": "should-not-be-used!"})
93
+ self.assertEqual(token, REAL_TOKEN)
94
+ self.assertEqual(inst, DISPLAY["instanceUrl"])
95
+ self.assertEqual(user, DISPLAY["username"])
96
+ self.assertEqual(
97
+ md_url, f"{DISPLAY['instanceUrl']}/services/Soap/m/68.0")
98
+ # Happy path must not issue the verbose fallback display.
99
+ self.assertEqual(self._verbose_calls(opt), [])
100
+
101
+ def test_primary_path_accepts_bare_string_result(self):
102
+ # Defensive: if a CLI version returns the token as a bare string in
103
+ # `result` (not an {accessToken} object), get_session must still use it.
104
+ (token, *_), opt = self._run(
105
+ show_at=REAL_TOKEN,
106
+ verbose={"accessToken": "should-not-be-used!"})
107
+ self.assertEqual(token, REAL_TOKEN)
108
+ self.assertEqual(self._verbose_calls(opt), [])
109
+
110
+ def test_redacted_primary_falls_back_to_verbose_display(self):
111
+ (token, *_), opt = self._run(
112
+ show_at={"accessToken": REDACTED},
113
+ verbose={"accessToken": REAL_TOKEN})
114
+ self.assertEqual(token, REAL_TOKEN)
115
+ self.assertEqual(len(self._verbose_calls(opt)), 1)
116
+
117
+ def test_missing_subcommand_falls_back_to_verbose_display(self):
118
+ (token, *_), opt = self._run(
119
+ show_at=None, verbose={"accessToken": REAL_TOKEN})
120
+ self.assertEqual(token, REAL_TOKEN)
121
+ self.assertEqual(len(self._verbose_calls(opt)), 1)
122
+
123
+ def test_redacted_everywhere_exits(self):
124
+ with self.assertRaises(SystemExit):
125
+ self._run(show_at={"accessToken": REDACTED},
126
+ verbose={"accessToken": REDACTED})
127
+
128
+ def test_no_token_anywhere_exits(self):
129
+ with self.assertRaises(SystemExit):
130
+ self._run(show_at=None, verbose=None)
131
+
132
+
133
+ if __name__ == "__main__":
134
+ unittest.main()
@@ -0,0 +1,193 @@
1
+ """Tests for the three capabilities added for the Email-to-Case steel thread:
2
+
3
+ 1. Production-org safety gate (enforce_production_gate / --confirm-production).
4
+ 2. API-version derivation with a 67.0 floor (resolve_api_version).
5
+ 3. Act-3 verify-cases proof step (verify_cases / --verify-cases).
6
+
7
+ All org I/O is mocked via FakeOrg — no live org required.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import tempfile
12
+ import unittest
13
+
14
+ from . import _bootstrap
15
+ from ._fakeorg import FakeOrg
16
+ from ._run import Input, run_main, write_input
17
+
18
+ apply_mod = _bootstrap.load_apply()
19
+
20
+ ADMIN = "admin@acme.com"
21
+ AUTH = "authuser@fake.example.com"
22
+
23
+
24
+ def _fresh(**kw):
25
+ kw.setdefault("case_settings",
26
+ {"fullName": "Case",
27
+ "defaultCaseOwner": ADMIN,
28
+ "defaultCaseOwnerType": "User",
29
+ "defaultCaseUser": ADMIN,
30
+ "emailToCase": {"enableEmailToCase": False}})
31
+ kw.setdefault("active_users", [ADMIN, AUTH])
32
+ return FakeOrg(**kw)
33
+
34
+
35
+ def _apply(org, extra_argv, addr=True):
36
+ b = Input().with_default_toggles()
37
+ if addr:
38
+ b = b.address(addressType="EmailToCase", routingName="Support",
39
+ caseOrigin="Email", casePriority="Medium")
40
+ src = b.build()
41
+ with tempfile.TemporaryDirectory() as d, org.patch(apply_mod):
42
+ path = write_input(d, src)
43
+ argv = ["--target-org", "x", "--input", path]
44
+ if addr:
45
+ argv += ["--routing-email", "support@acme.com"]
46
+ return run_main(apply_mod, argv + extra_argv)
47
+
48
+
49
+ class ApiVersionDerivation(unittest.TestCase):
50
+ """resolve_api_version: floor at DEFAULT_API_VERSION, ride newer orgs."""
51
+
52
+ def test_floors_below_default(self):
53
+ self.assertEqual(apply_mod.resolve_api_version("62.0"), "67.0")
54
+
55
+ def test_equal_to_floor(self):
56
+ self.assertEqual(apply_mod.resolve_api_version("67.0"), "67.0")
57
+
58
+ def test_rides_newer_org(self):
59
+ self.assertEqual(apply_mod.resolve_api_version("68.0"), "68.0")
60
+
61
+ def test_rides_much_newer_org(self):
62
+ self.assertEqual(apply_mod.resolve_api_version("70.0"), "70.0")
63
+
64
+ def test_none_falls_back_to_floor(self):
65
+ self.assertEqual(apply_mod.resolve_api_version(None), "67.0")
66
+
67
+ def test_garbage_falls_back_to_floor(self):
68
+ self.assertEqual(apply_mod.resolve_api_version("not-a-version"), "67.0")
69
+
70
+ def test_summary_reports_derived_version(self):
71
+ org = _fresh(org_api_version="70.0")
72
+ summary = _apply(org, [])
73
+ self.assertEqual(summary["apiVersion"], "70.0")
74
+
75
+ def test_summary_reports_floored_version(self):
76
+ org = _fresh(org_api_version="60.0")
77
+ summary = _apply(org, [])
78
+ self.assertEqual(summary["apiVersion"], "67.0")
79
+
80
+
81
+ class ProductionGate(unittest.TestCase):
82
+ """enforce_production_gate: block prod writes unless --confirm-production."""
83
+
84
+ def test_sandbox_writes_without_confirmation(self):
85
+ org = _fresh(is_sandbox=True)
86
+ summary = _apply(org, [])
87
+ self.assertFalse(summary["orgInfo"]["isProduction"])
88
+ self.assertEqual(summary["verified"]["routingAddressCount"], 1)
89
+
90
+ def test_trial_writes_without_confirmation(self):
91
+ org = _fresh(is_sandbox=False, is_trial=True, org_type="Trial")
92
+ summary = _apply(org, [])
93
+ self.assertFalse(summary["orgInfo"]["isProduction"])
94
+ self.assertEqual(summary["verified"]["routingAddressCount"], 1)
95
+
96
+ def test_production_blocked_without_confirmation(self):
97
+ org = _fresh(is_sandbox=False, is_trial=False,
98
+ org_type="Professional Edition")
99
+ with self.assertRaises(SystemExit):
100
+ _apply(org, [])
101
+ # nothing was written
102
+ self.assertEqual(org.writes, [])
103
+
104
+ def test_production_writes_with_confirmation(self):
105
+ org = _fresh(is_sandbox=False, is_trial=False,
106
+ org_type="Professional Edition")
107
+ summary = _apply(org, ["--confirm-production"])
108
+ self.assertTrue(summary["orgInfo"]["isProduction"])
109
+ self.assertEqual(summary["verified"]["routingAddressCount"], 1)
110
+
111
+ def test_unreadable_org_row_blocked_without_confirmation(self):
112
+ org = _fresh(is_sandbox=None)
113
+ with self.assertRaises(SystemExit):
114
+ _apply(org, [])
115
+ self.assertEqual(org.writes, [])
116
+
117
+ def test_unreadable_org_row_writes_with_confirmation(self):
118
+ org = _fresh(is_sandbox=None)
119
+ summary = _apply(org, ["--confirm-production"])
120
+ self.assertEqual(summary["verified"]["routingAddressCount"], 1)
121
+
122
+
123
+ class VerifyCasesMode(unittest.TestCase):
124
+ """verify_cases / --verify-cases: proof requires an Email-origin Case with a
125
+ linked incoming EmailMessage."""
126
+
127
+ def _run_verify(self, org, extra=None):
128
+ # --verify-cases needs no input write, but main() requires --input; a
129
+ # throwaway file satisfies argparse (it isn't parsed in this mode).
130
+ src = Input().with_default_toggles().build()
131
+ with tempfile.TemporaryDirectory() as d, org.patch(apply_mod):
132
+ path = write_input(d, src)
133
+ return run_main(apply_mod, ["--target-org", "x", "--input", path,
134
+ "--verify-cases", *(extra or [])])
135
+
136
+ def test_proven_when_case_and_incoming_message(self):
137
+ org = _fresh(
138
+ cases=[{"Id": "500x1", "CaseNumber": "00001001", "Origin": "Email",
139
+ "SuppliedEmail": "ext@customer.com", "Subject": "Help",
140
+ "Status": "New", "CreatedDate": "2026-08-18T10:00:00Z"}],
141
+ email_messages=[{"Id": "02sx1", "ParentId": "500x1",
142
+ "Incoming": True, "FromAddress": "ext@customer.com",
143
+ "Subject": "Help",
144
+ "MessageDate": "2026-08-18T10:00:00Z"}])
145
+ summary = self._run_verify(org)
146
+ self.assertTrue(summary["proven"])
147
+ self.assertEqual(summary["caseCount"], 1)
148
+ self.assertEqual(summary["incomingEmailMessageCount"], 1)
149
+ self.assertTrue(summary["cases"][0]["hasIncomingEmailMessage"])
150
+
151
+ def test_no_cases_fails_closed(self):
152
+ org = _fresh(cases=[], email_messages=[])
153
+ with self.assertRaises(SystemExit):
154
+ self._run_verify(org)
155
+
156
+ def test_case_without_incoming_message_fails_closed(self):
157
+ org = _fresh(
158
+ cases=[{"Id": "500x1", "CaseNumber": "00001001", "Origin": "Email",
159
+ "SuppliedEmail": "ext@customer.com", "Subject": "Help",
160
+ "Status": "New", "CreatedDate": "2026-08-18T10:00:00Z"}],
161
+ email_messages=[]) # no linked incoming email
162
+ with self.assertRaises(SystemExit):
163
+ self._run_verify(org)
164
+
165
+ def test_supplied_email_filter_narrows_cases(self):
166
+ org = _fresh(
167
+ cases=[
168
+ {"Id": "500x1", "CaseNumber": "1", "Origin": "Email",
169
+ "SuppliedEmail": "ext@customer.com", "Subject": "A",
170
+ "Status": "New", "CreatedDate": "2026-08-18T10:00:00Z"},
171
+ {"Id": "500x2", "CaseNumber": "2", "Origin": "Email",
172
+ "SuppliedEmail": "other@somewhere.com", "Subject": "B",
173
+ "Status": "New", "CreatedDate": "2026-08-18T11:00:00Z"}],
174
+ email_messages=[{"Id": "02sx1", "ParentId": "500x1",
175
+ "Incoming": True, "MessageDate": "x"}])
176
+ summary = self._run_verify(org, ["--supplied-email", "ext@customer.com"])
177
+ self.assertEqual(summary["caseCount"], 1)
178
+ self.assertEqual(summary["cases"][0]["suppliedEmail"], "ext@customer.com")
179
+ self.assertTrue(summary["proven"])
180
+
181
+ def test_verify_cases_does_not_write(self):
182
+ org = _fresh(
183
+ cases=[{"Id": "500x1", "CaseNumber": "1", "Origin": "Email",
184
+ "SuppliedEmail": "ext@customer.com", "Subject": "A",
185
+ "Status": "New", "CreatedDate": "x"}],
186
+ email_messages=[{"Id": "02sx1", "ParentId": "500x1",
187
+ "Incoming": True, "MessageDate": "x"}])
188
+ self._run_verify(org)
189
+ self.assertEqual(org.writes, [])
190
+
191
+
192
+ if __name__ == "__main__":
193
+ unittest.main()
@@ -0,0 +1,170 @@
1
+ """Unit tests for validate-casesettings.py.
2
+
3
+ The validator checks a CaseSettings SOURCE file (the input to
4
+ apply-casesettings.py) for structural rules that must hold BEFORE applying —
5
+ independent of any org. These are pure, deterministic checks, so they are unit
6
+ tested here rather than covered by evals (evals score the LLM-authored artifact
7
+ against a gold, they do not assert on the validator's fail-closed behavior).
8
+
9
+ ``validate(path)`` returns a list of human-readable error strings — empty means
10
+ the file passed. Each test writes a source file to a temp dir and asserts the
11
+ error list contains (or lacks) the expected message.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import tempfile
16
+ import unittest
17
+
18
+ from . import _bootstrap
19
+ from ._run import Input, write_input
20
+
21
+ validate_mod = _bootstrap.load_validate()
22
+
23
+
24
+ def _errors(xml: str) -> list:
25
+ """Write xml to a temp file, run validate(), return the error list."""
26
+ with tempfile.TemporaryDirectory() as d:
27
+ path = write_input(d, xml)
28
+ return validate_mod.validate(path)
29
+
30
+
31
+ def _has(errors, needle) -> bool:
32
+ return any(needle in e for e in errors)
33
+
34
+
35
+ # A minimal source file that should PASS: E2C on, on-demand on, one well-formed
36
+ # EmailToCase routing address, no owner fields, no emailAddress.
37
+ def _valid_src(**addr_over) -> str:
38
+ addr = dict(addressType="EmailToCase", routingName="Support",
39
+ caseOrigin="Email", casePriority="Medium")
40
+ addr.update(addr_over)
41
+ return Input().address(**addr).build()
42
+
43
+
44
+ class ValidSourceFile(unittest.TestCase):
45
+ def test_minimal_valid_file_passes(self):
46
+ self.assertEqual(_errors(_valid_src()), [])
47
+
48
+ def test_valid_with_optional_toggles_passes(self):
49
+ xml = (Input().with_default_toggles()
50
+ .address(addressType="EmailToCase", routingName="Support",
51
+ caseOrigin="Email", casePriority="Medium").build())
52
+ self.assertEqual(_errors(xml), [])
53
+
54
+ def test_valid_with_user_case_owner_passes(self):
55
+ self.assertEqual(
56
+ _errors(_valid_src(caseOwner="agent@acme.com", caseOwnerType="User")),
57
+ [])
58
+
59
+
60
+ class StructuralRules(unittest.TestCase):
61
+ def test_not_well_formed_xml(self):
62
+ errors = _errors("<CaseSettings>not closed")
63
+ self.assertTrue(_has(errors, "not well-formed"))
64
+
65
+ def test_wrong_root_element(self):
66
+ xml = ('<?xml version="1.0" encoding="UTF-8"?>\n'
67
+ '<Foo xmlns="http://soap.sforce.com/2006/04/metadata"></Foo>\n')
68
+ self.assertTrue(_has(_errors(xml), "Root element must be CaseSettings"))
69
+
70
+ def test_missing_email_to_case_block(self):
71
+ xml = ('<?xml version="1.0" encoding="UTF-8"?>\n'
72
+ '<CaseSettings xmlns="http://soap.sforce.com/2006/04/metadata">\n'
73
+ '</CaseSettings>\n')
74
+ self.assertTrue(_has(_errors(xml), "Missing <emailToCase> block"))
75
+
76
+
77
+ class RequiredToggles(unittest.TestCase):
78
+ def test_on_demand_must_be_true(self):
79
+ xml = (Input().e2c(enableOnDemandEmailToCase="false")
80
+ .address(addressType="EmailToCase", routingName="Support",
81
+ caseOrigin="Email", casePriority="Medium").build())
82
+ self.assertTrue(_has(_errors(xml), "enableOnDemandEmailToCase must be 'true'"))
83
+
84
+ def test_enable_email_to_case_must_be_true(self):
85
+ xml = (Input().e2c(enableEmailToCase="false")
86
+ .address(addressType="EmailToCase", routingName="Support",
87
+ caseOrigin="Email", casePriority="Medium").build())
88
+ self.assertTrue(_has(_errors(xml), "enableEmailToCase must be 'true'"))
89
+
90
+ def test_non_boolean_toggle_flagged(self):
91
+ xml = (Input().e2c(enableHtmlEmail="yes")
92
+ .address(addressType="EmailToCase", routingName="Support",
93
+ caseOrigin="Email", casePriority="Medium").build())
94
+ self.assertTrue(_has(_errors(xml),
95
+ "emailToCase.enableHtmlEmail must be 'true' or 'false'"))
96
+
97
+
98
+ class RoutingAddressRules(unittest.TestCase):
99
+ def test_no_routing_address_flagged(self):
100
+ self.assertTrue(_has(_errors(Input().build()), "No <routingAddresses> found"))
101
+
102
+ def test_missing_required_address_field(self):
103
+ # routingName omitted
104
+ xml = Input().address(addressType="EmailToCase", caseOrigin="Email",
105
+ casePriority="Medium").build()
106
+ self.assertTrue(_has(_errors(xml), "missing required field 'routingName'"))
107
+
108
+ def test_email_address_in_source_rejected(self):
109
+ # emailAddress must never be in the source file — supplied at apply time.
110
+ xml = _valid_src(emailAddress="support@acme.com")
111
+ self.assertTrue(_has(_errors(xml), "sets 'emailAddress'"))
112
+
113
+ def test_readonly_field_rejected(self):
114
+ xml = _valid_src(isVerified="true")
115
+ self.assertTrue(_has(_errors(xml), "read-only field 'isVerified'"))
116
+
117
+ def test_e2ceasy_address_type_rejected(self):
118
+ xml = _valid_src(addressType="E2cEasy")
119
+ self.assertTrue(_has(_errors(xml), "E2cEasy"))
120
+
121
+ def test_invalid_address_type_rejected(self):
122
+ xml = _valid_src(addressType="Bogus")
123
+ self.assertTrue(_has(_errors(xml), "addressType 'Bogus' invalid"))
124
+
125
+ def test_duplicate_routing_name_flagged(self):
126
+ xml = (Input()
127
+ .address(addressType="EmailToCase", routingName="Dup",
128
+ caseOrigin="Email", casePriority="Medium")
129
+ .address(addressType="EmailToCase", routingName="Dup",
130
+ caseOrigin="Email", casePriority="High").build())
131
+ self.assertTrue(_has(_errors(xml), "duplicate routingName 'Dup'"))
132
+
133
+
134
+ class PerAddressCaseOwnerRules(unittest.TestCase):
135
+ def test_case_owner_without_type_rejected(self):
136
+ xml = _valid_src(caseOwner="agent@acme.com") # no caseOwnerType
137
+ self.assertTrue(_has(_errors(xml), "sets caseOwner without caseOwnerType"))
138
+
139
+ def test_case_owner_type_without_owner_rejected(self):
140
+ xml = _valid_src(caseOwnerType="User") # no caseOwner
141
+ self.assertTrue(_has(_errors(xml), "sets caseOwnerType without caseOwner"))
142
+
143
+ def test_placeholder_case_owner_rejected(self):
144
+ xml = _valid_src(caseOwner="{CASE_OWNER_USERNAME}", caseOwnerType="User")
145
+ self.assertTrue(_has(_errors(xml), "still the placeholder"))
146
+
147
+ def test_invalid_case_owner_type_rejected(self):
148
+ xml = _valid_src(caseOwner="agent@acme.com", caseOwnerType="Group")
149
+ self.assertTrue(_has(_errors(xml),
150
+ "caseOwnerType must be 'User' or 'Queue'"))
151
+
152
+
153
+ class SupportSettingsRules(unittest.TestCase):
154
+ def test_system_and_named_user_mutually_exclusive(self):
155
+ xml = (Input().top(useSystemUserAsDefaultCaseUser="true",
156
+ defaultCaseUser="admin@acme.com")
157
+ .address(addressType="EmailToCase", routingName="Support",
158
+ caseOrigin="Email", casePriority="Medium").build())
159
+ self.assertTrue(_has(_errors(xml), "mutually exclusive"))
160
+
161
+ def test_invalid_default_owner_type_rejected(self):
162
+ xml = (Input().top(defaultCaseOwnerType="Group")
163
+ .address(addressType="EmailToCase", routingName="Support",
164
+ caseOrigin="Email", casePriority="Medium").build())
165
+ self.assertTrue(_has(_errors(xml),
166
+ "defaultCaseOwnerType must be 'User' or 'Queue'"))
167
+
168
+
169
+ if __name__ == "__main__":
170
+ unittest.main()