@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.
- package/package.json +2 -6
- package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +3 -0
- package/skills/automation-sandbox-post-copy-configure/SKILL.md +4 -0
- package/skills/design-systems-slds-validate/SKILL.md +2 -2
- package/skills/dx-app-analytics-query/SKILL.md +1 -3
- package/skills/dx-devops-conflict-resolve/SKILL.md +205 -0
- package/skills/dx-devops-conflict-resolve/examples/conflict-workflows.md +165 -0
- package/skills/dx-devops-conflict-resolve/references/deploy-failure-resolution.md +75 -0
- package/skills/dx-devops-conflict-resolve/references/git-conflict-resolution.md +124 -0
- package/skills/dx-devops-conflict-resolve/scripts/detect-conflicts.sh +91 -0
- package/skills/dx-devops-conflict-resolve/scripts/diagnose-deploy-failure.sh +156 -0
- package/skills/dx-devops-request-status/SKILL.md +160 -0
- package/skills/dx-devops-request-status/examples/polling-workflows.md +101 -0
- package/skills/dx-devops-request-status/references/cli-commands.md +176 -0
- package/skills/dx-devops-request-status/scripts/poll-status.sh +134 -0
- package/skills/dx-devops-test-failures-analyze/SKILL.md +6 -6
- package/skills/dx-devops-test-pipeline-configure/SKILL.md +7 -7
- package/skills/dx-devops-test-suite-assignments-configure/SKILL.md +6 -6
- package/skills/dx-devops-test-suite-run/SKILL.md +6 -6
- package/skills/dx-devops-work-item-manage/SKILL.md +2 -0
- package/skills/dx-org-manage/references/creating-scratch-org.md +5 -2
- package/skills/dx-org-manage/references/creating-snapshot.md +1 -0
- package/skills/dx-org-shape-manage/SKILL.md +152 -0
- package/skills/dx-org-shape-manage/examples/create_error_output.json +9 -0
- package/skills/dx-org-shape-manage/examples/create_success_output.json +9 -0
- package/skills/dx-org-shape-manage/examples/delete_output.json +11 -0
- package/skills/dx-org-shape-manage/examples/list_inactive_output.json +32 -0
- package/skills/dx-org-shape-manage/examples/list_output.json +24 -0
- package/skills/dx-org-shape-manage/references/cli_flags.md +135 -0
- package/skills/dx-org-switch/SKILL.md +2 -2
- package/skills/dx-org-trial-expiration-check/SKILL.md +2 -0
- package/skills/dx-pkg-post-install-configure/SKILL.md +3 -0
- package/skills/education-cloud-multi-campus-configure/SKILL.md +211 -0
- package/skills/education-cloud-multi-campus-configure/examples/hierarchy_visualization.md +174 -0
- package/skills/education-cloud-multi-campus-configure/examples/output_examples.md +54 -0
- package/skills/education-cloud-multi-campus-configure/examples/sample_hierarchy_input.csv +29 -0
- package/skills/education-cloud-multi-campus-configure/examples/sample_hierarchy_input_edgecases.csv +16 -0
- package/skills/education-cloud-multi-campus-configure/references/account_recordtype_prerequisite.md +44 -0
- package/skills/education-cloud-multi-campus-configure/references/delta_computation.md +33 -0
- package/skills/education-cloud-multi-campus-configure/references/error_handling.md +198 -0
- package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +54 -0
- package/skills/education-cloud-multi-campus-configure/references/gotchas.md +16 -0
- package/skills/education-cloud-multi-campus-configure/references/hierarchy_parsing_rules.md +133 -0
- package/skills/education-cloud-multi-campus-configure/references/mcp-invocation.md +75 -0
- package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/SKILL.md +1 -1
- package/skills/experience-cms-brand-apply/SKILL.md +4 -1
- package/skills/experience-cms-brand-create/SKILL.md +197 -0
- package/skills/experience-cms-brand-create/assets/brand-template.json +329 -0
- package/skills/experience-cms-brand-create/references/brand-anatomy.md +120 -0
- package/skills/experience-cms-brand-create/references/disk-contract.md +84 -0
- package/skills/experience-cms-content-generate/SKILL.md +190 -0
- package/skills/experience-cms-content-generate/assets/display-formats.md +84 -0
- package/skills/experience-cms-content-generate/assets/payloads/create-content-bulk.json +11 -0
- package/skills/experience-cms-content-generate/assets/payloads/create-content-single.json +11 -0
- package/skills/experience-cms-content-generate/assets/payloads/publish-content.json +4 -0
- package/skills/experience-cms-content-generate/assets/payloads/update-content.json +9 -0
- package/skills/experience-cms-content-generate/assets/questions.md +241 -0
- package/skills/experience-cms-content-generate/examples/create-content-call.md +81 -0
- package/skills/experience-cms-content-generate/references/bulk-batching.md +56 -0
- package/skills/experience-cms-content-generate/references/content-type-classification.md +88 -0
- package/skills/experience-cms-content-generate/references/content-write-tool.md +95 -0
- package/skills/experience-cms-content-generate/references/delegation-protocol.md +37 -0
- package/skills/experience-cms-content-generate/references/edit-publish-workflow.md +63 -0
- package/skills/experience-cms-content-generate/references/error-recovery.md +72 -0
- package/skills/experience-cms-content-generate/references/identifier-resolution.md +26 -0
- package/skills/experience-cms-content-generate/references/intent-routing.md +45 -0
- package/skills/experience-cms-content-generate/references/principles.md +19 -0
- package/skills/experience-cms-content-generate/references/ux-rules.md +68 -0
- package/skills/experience-cms-content-generate/references/workspace-resolution.md +33 -0
- package/skills/experience-cms-content-type-generate/SKILL.md +449 -0
- package/skills/experience-cms-content-type-generate/assets/discovery-prompts.md +79 -0
- package/skills/experience-cms-content-type-generate/assets/schema-example.json +70 -0
- package/skills/experience-cms-content-type-generate/references/agent-checklist.md +50 -0
- package/skills/experience-cms-content-type-generate/references/anti-patterns.md +39 -0
- package/skills/experience-cms-content-type-generate/references/deployment-errors.md +27 -0
- package/skills/experience-cms-content-type-generate/references/discovery-details.md +106 -0
- package/skills/experience-cms-content-type-generate/references/discovery-query-rules.md +85 -0
- package/skills/experience-cms-content-type-generate/references/edit-fields-loop.md +48 -0
- package/skills/experience-cms-content-type-generate/references/pre-deploy-checklist.md +21 -0
- package/skills/experience-cms-content-type-generate/references/retrieve-and-reconcile.md +105 -0
- package/skills/experience-cms-content-type-generate/references/schema-rules.md +51 -0
- package/skills/experience-cms-content-type-generate/references/schema-summary-format.md +60 -0
- package/skills/experience-lds-graphql-generate/SKILL.md +4 -0
- package/skills/experience-lwc-accessibility-jest-run/SKILL.md +79 -0
- package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +205 -0
- package/skills/experience-lwc-design-generate/SKILL.md +5 -4
- package/skills/experience-lwc-generate/SKILL.md +10 -9
- package/skills/experience-ui-bundle-deploy/references/dev-preview.md +21 -0
- package/skills/experience-ui-bundle-deploy/references/social-login.md +1 -0
- package/skills/experience-ui-bundle-file-upload-generate/SKILL.md +6 -6
- package/skills/experience-ui-bundle-localize/SKILL.md +30 -37
- package/skills/experience-ui-bundle-localize/references/gotchas.md +23 -7
- package/skills/experience-ui-bundle-localize/references/i18n-setup.md +48 -4
- package/skills/experience-ui-bundle-localize/references/label-xml.md +22 -8
- package/skills/experience-ui-bundle-localize/references/verifying.md +39 -11
- package/skills/experience-ui-bundle-localize/scripts/detect-bundle-type.sh +118 -15
- package/skills/experience-ui-bundle-localize/scripts/tests/test-detect-bundle-type.sh +187 -0
- package/skills/experience-ui-bundle-mfa-configure/SKILL.md +13 -2
- package/skills/experience-ui-bundle-mfa-configure/references/social-login.md +1 -0
- package/skills/experience-ui-bundle-project-generate/SKILL.md +2 -2
- package/skills/integration-connectivity-generate/SKILL.md +11 -10
- package/skills/integration-eventing-cdc-configure/SKILL.md +6 -6
- package/skills/integration-eventing-subscription-configure/SKILL.md +6 -5
- package/skills/mobile-platform-native-capabilities-integrate/SKILL.md +2 -2
- package/skills/mobile-platform-offline-validate/scripts/package.json +1 -1
- package/skills/platform-agentexchange-partner-offers-configure/SKILL.md +8 -8
- package/skills/platform-custom-application-generate/SKILL.md +2 -0
- package/skills/platform-custom-field-generate/SKILL.md +8 -6
- package/skills/platform-custom-metadata-type-generate/SKILL.md +460 -0
- package/skills/platform-custom-metadata-type-generate/references/cmdt-records.md +251 -0
- package/skills/platform-custom-metadata-type-generate/scripts/sanitize-developer-name.sh +46 -0
- package/skills/platform-custom-setting-generate/SKILL.md +419 -0
- package/skills/platform-dataspace-access-configure/SKILL.md +3 -3
- package/skills/platform-sharing-owd-configure/SKILL.md +3 -0
- package/skills/platform-value-set-generate/SKILL.md +2 -0
- package/skills/service-agentforce-channel-configure/SKILL.md +29 -20
- package/skills/service-agentforce-channel-configure/assets/BotEmailDefinition.botEmailDefinition-meta.xml +36 -0
- package/skills/service-agentforce-channel-configure/assets/email/unfiled$public/AgentforceForServiceEmailTemplate.email +17 -0
- package/skills/service-agentforce-channel-configure/assets/email/unfiled$public/AgentforceForServiceEmailTemplate.email-meta.xml +29 -0
- package/skills/service-agentforce-channel-configure/assets/mdapi-package.xml +25 -0
- package/skills/service-agentforce-channel-configure/assets/settings-mdapi-package.xml +30 -0
- package/skills/service-agentforce-channel-configure/references/agent-wiring.md +20 -0
- package/skills/service-agentforce-channel-configure/references/botemaildefinition.md +107 -0
- package/skills/service-agentforce-channel-configure/references/channel-branch-email.md +203 -50
- package/skills/service-agentforce-channel-configure/references/channel-types.md +30 -3
- package/skills/service-agentforce-channel-configure/references/queue-resolution.md +17 -8
- package/skills/service-agentforce-channel-configure/references/routing-flow.md +14 -3
- package/skills/service-agentforce-channel-configure/scripts/validate-botemaildefinition.py +126 -0
- package/skills/service-agentforce-channel-configure/scripts/validate-emailtemplate.py +133 -0
- package/skills/service-de-channel-activate/SKILL.md +271 -0
- package/skills/service-de-channel-activate/references/gotchas.md +31 -0
- package/skills/service-de-channel-activate/references/phone-verification.md +96 -0
- package/skills/service-de-channel-activate/references/worked-examples.md +14 -0
- package/skills/service-de-channel-consent-configure/SKILL.md +230 -0
- package/skills/service-de-channel-consent-configure/references/gotchas.md +41 -0
- package/skills/service-de-channel-consent-configure/references/language-keywords.md +84 -0
- package/skills/service-de-channel-consent-configure/references/worked-examples.md +134 -0
- package/skills/service-de-channel-create/SKILL.md +369 -0
- package/skills/service-de-channel-create/references/apple.md +59 -0
- package/skills/service-de-channel-create/references/connect-insert.md +84 -0
- package/skills/service-de-channel-create/references/facebook.md +154 -0
- package/skills/service-de-channel-create/references/line.md +66 -0
- package/skills/service-de-channel-create/references/sms.md +98 -0
- package/skills/service-de-channel-create/references/whatsapp.md +98 -0
- package/skills/service-de-channel-create/references/worked-examples.md +70 -0
- package/skills/service-de-channel-routing-configure/SKILL.md +345 -0
- package/skills/service-de-channel-routing-configure/references/asa-routing.md +90 -0
- package/skills/service-de-channel-routing-configure/references/gotchas.md +27 -0
- package/skills/service-de-channel-routing-configure/references/queue-creation.md +123 -0
- package/skills/service-de-channel-routing-configure/references/target-locate.md +98 -0
- package/skills/service-de-channel-routing-configure/references/worked-examples.md +174 -0
- package/skills/service-de-headless-channel-configure/SKILL.md +305 -0
- package/skills/service-de-headless-channel-configure/references/gotchas.md +27 -0
- package/skills/service-de-headless-channel-configure/references/inputs.md +49 -0
- package/skills/service-de-headless-channel-configure/references/output-envelopes.md +53 -0
- package/skills/service-de-headless-channel-configure/references/partial-success.md +24 -0
- package/skills/service-de-headless-channel-configure/references/terms-and-conditions.md +54 -0
- package/skills/service-de-headless-channel-configure/references/worked-examples.md +108 -0
- package/skills/service-de-waba-integrate/SKILL.md +227 -0
- package/skills/service-digital-engagement-channel-configure/scripts/check-api-version.sh +29 -0
- package/skills/service-digital-engagement-messaging-site-integrate/SKILL.md +10 -9
- package/skills/service-digital-engagement-messaging-site-integrate/references/lwr_patch.md +39 -21
- package/skills/service-digital-engagement-messaging-site-integrate/scripts/patch_lwr_bundle.sh +157 -96
- package/skills/service-email-to-case-configure/SKILL.md +190 -0
- package/skills/service-email-to-case-configure/assets/CaseSettings.settings-meta.xml +45 -0
- package/skills/service-email-to-case-configure/examples/CaseSettings-two-addresses.settings-meta.xml +36 -0
- package/skills/service-email-to-case-configure/references/apply-mechanics.md +73 -0
- package/skills/service-email-to-case-configure/references/routing_address_reference.md +73 -0
- package/skills/service-email-to-case-configure/references/troubleshooting.md +25 -0
- package/skills/service-email-to-case-configure/scripts/apply-casesettings.py +1315 -0
- package/skills/service-email-to-case-configure/scripts/check-agent-email-capability.sh +26 -0
- package/skills/service-email-to-case-configure/scripts/tests/__init__.py +0 -0
- package/skills/service-email-to-case-configure/scripts/tests/_bootstrap.py +45 -0
- package/skills/service-email-to-case-configure/scripts/tests/_fakeorg.py +366 -0
- package/skills/service-email-to-case-configure/scripts/tests/_run.py +84 -0
- package/skills/service-email-to-case-configure/scripts/tests/test_apply_org_scenarios.py +655 -0
- package/skills/service-email-to-case-configure/scripts/tests/test_get_session.py +134 -0
- package/skills/service-email-to-case-configure/scripts/tests/test_new_capabilities.py +193 -0
- package/skills/service-email-to-case-configure/scripts/tests/test_validate_casesettings.py +170 -0
- package/skills/service-email-to-case-configure/scripts/validate-casesettings.py +229 -0
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +33 -15
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -47
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/SKILL.md +17 -12
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/references/cli-invocation.md +5 -3
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/classify-enable-plan.mjs +11 -3
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/SKILL.md +8 -6
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/references/cli-invocation.md +3 -3
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/scripts/classify-readiness.mjs +11 -3
- package/skills/service-itsm-agentic-setup-configure/SKILL.md +5 -5
- package/skills/service-itsm-agentic-setup-configure/examples/output-templates.md +5 -5
- package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +2 -2
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +11 -7
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +20 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +10 -6
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/action-availability.md +7 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +11 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/build-create-body.mjs +24 -17
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-action-availability.mjs +98 -58
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +20 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/strip-release-management.mjs +176 -0
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/SKILL.md +19 -19
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/cli-invocation.md +3 -4
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/helper-contracts.md +6 -6
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/permset-topology.md +3 -10
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-permset-availability.mjs +7 -9
- package/skills/service-itsm-incident-mgmt-configure/SKILL.md +2 -0
- package/skills/service-itsm-incident-priority-configure/SKILL.md +15 -12
- package/skills/service-itsm-incident-priority-configure/examples/matrix-operations.md +27 -1
- package/skills/service-itsm-incident-priority-configure/references/sf-cli-invocation.md +37 -6
- package/skills/service-native-voice-recording-transcription-configure/SKILL.md +146 -0
- package/skills/service-native-voice-recording-transcription-configure/assets/confirmation-output.json +16 -0
- package/skills/service-native-voice-recording-transcription-configure/assets/package.xml +22 -0
- package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +101 -0
- package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +181 -0
- package/skills/data360-activate/README.md +0 -38
- package/skills/data360-activate/SKILL.md +0 -127
- package/skills/data360-connect/README.md +0 -57
- package/skills/data360-connect/SKILL.md +0 -163
- package/skills/data360-connect/examples/connections/heroku-postgres.json +0 -15
- package/skills/data360-connect/examples/connections/ingest-api-connection.json +0 -5
- package/skills/data360-connect/examples/connections/ingest-api-schema.json +0 -31
- package/skills/data360-connect/examples/connections/redshift.json +0 -16
- package/skills/data360-connect/examples/connections/sharepoint-unstructured.json +0 -20
- package/skills/data360-connect/examples/connections/snowflake-connection.json +0 -42
- package/skills/data360-harmonize/README.md +0 -31
- package/skills/data360-harmonize/SKILL.md +0 -126
- package/skills/data360-orchestrate/README.md +0 -120
- package/skills/data360-orchestrate/SKILL.md +0 -263
- package/skills/data360-orchestrate/assets/definitions/activation-target.template.json +0 -5
- package/skills/data360-orchestrate/assets/definitions/activation.template.json +0 -7
- package/skills/data360-orchestrate/assets/definitions/calculated-insight.template.json +0 -7
- package/skills/data360-orchestrate/assets/definitions/data-action-target.template.json +0 -5
- package/skills/data360-orchestrate/assets/definitions/data-action.template.json +0 -5
- package/skills/data360-orchestrate/assets/definitions/data-graph.template.json +0 -21
- package/skills/data360-orchestrate/assets/definitions/data-stream.template.json +0 -55
- package/skills/data360-orchestrate/assets/definitions/dmo.template.json +0 -17
- package/skills/data360-orchestrate/assets/definitions/identity-resolution.template.json +0 -30
- package/skills/data360-orchestrate/assets/definitions/mapping.template.json +0 -14
- package/skills/data360-orchestrate/assets/definitions/relationship.template.json +0 -12
- package/skills/data360-orchestrate/assets/definitions/search-index.template.json +0 -9
- package/skills/data360-orchestrate/assets/definitions/segment.template.json +0 -16
- package/skills/data360-orchestrate/references/feature-readiness.md +0 -157
- package/skills/data360-orchestrate/references/plugin-setup.md +0 -138
- package/skills/data360-orchestrate/scripts/bootstrap-plugin.sh +0 -53
- package/skills/data360-orchestrate/scripts/diagnose-org.mjs +0 -511
- package/skills/data360-orchestrate/scripts/generate-manifest.mjs +0 -68
- package/skills/data360-orchestrate/scripts/verify-plugin.sh +0 -58
- package/skills/data360-prepare/README.md +0 -50
- package/skills/data360-prepare/SKILL.md +0 -203
- package/skills/data360-prepare/examples/ingestion-api/.env.example +0 -8
- package/skills/data360-prepare/examples/ingestion-api/README.md +0 -48
- package/skills/data360-prepare/examples/ingestion-api/send-data.py +0 -144
- package/skills/data360-query/README.md +0 -43
- package/skills/data360-query/SKILL.md +0 -128
- package/skills/data360-query/examples/search-indexes/hybrid-structured.json +0 -44
- package/skills/data360-query/examples/search-indexes/vector-knowledge.json +0 -43
- package/skills/data360-segment/README.md +0 -35
- package/skills/data360-segment/SKILL.md +0 -123
- package/skills/service-helpagent-coordinate/references/channel-help-portal.md +0 -22
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-1-1-non-text-content.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-i-lists.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-ii-tables.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-iii-form-labels.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-iv-regions.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-v-groups.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-5-identify-input.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-4-3-contrast.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-1-1-keyboard.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-4-4-link-purpose.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-4-6-headings-labels.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-3-label-in-name.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-7-dragging-movement.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-2-1-on-focus.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-2-2-on-input.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-1-error-identification.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-2-labels-instructions.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-3-error-suggestion.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-i-name.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-ii-role.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-iii-value.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-1-1-non-text-content.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-1-use-of-color.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-10-resize-reflow.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-11-non-text-contrast.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-3-contrast.md +0 -0
- /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()
|