@rancher/shell 3.0.12-rc.2 → 3.0.12-rc.4
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/apis/impl/apis.ts +6 -0
- package/apis/index.ts +26 -0
- package/apis/intf/resources-api/cluster-api.ts +18 -0
- package/apis/intf/resources-api/mgmt-api.ts +15 -0
- package/apis/intf/resources-api/resource-base.ts +107 -0
- package/apis/intf/resources-api/resource-constants.ts +147 -0
- package/apis/intf/resources-api/resources-api.ts +143 -0
- package/apis/intf/resources.ts +49 -0
- package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
- package/apis/intf/shell-api/proxy.ts +216 -0
- package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
- package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
- package/apis/intf/shell.ts +12 -6
- package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
- package/apis/resources/index.ts +22 -0
- package/apis/resources/resources-api-class.ts +187 -0
- package/apis/shell/__tests__/proxy.test.ts +369 -0
- package/apis/shell/index.ts +8 -1
- package/apis/shell/modal.ts +4 -1
- package/apis/shell/notifications.ts +9 -6
- package/apis/shell/proxy.ts +256 -0
- package/apis/shell/slide-in.ts +4 -1
- package/apis/vue-shim.d.ts +2 -1
- package/assets/data/aws-regions.json +4 -0
- package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/fonts/_fontstack.scss +132 -8
- package/assets/styles/global/_layout.scss +4 -0
- package/assets/translations/en-us.yaml +165 -45
- package/assets/translations/zh-hans.yaml +1 -7
- package/chart/monitoring/ClusterSelector.vue +0 -21
- package/chart/monitoring/index.vue +10 -1
- package/chart/monitoring/prometheus/index.vue +6 -3
- package/components/ActionDropdownShell.vue +2 -1
- package/components/CruResource.vue +161 -14
- package/components/CruResourceFooter.vue +9 -5
- package/components/ExplorerMembers.vue +8 -4
- package/components/ExplorerProjectsNamespaces.vue +11 -7
- package/components/GrowlManager.vue +4 -0
- package/components/InstallHelmCharts.vue +2 -2
- package/components/LandingPagePreference.vue +14 -5
- package/components/MgmtNodeList.vue +184 -0
- package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
- package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
- package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
- package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +17 -1
- package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
- package/components/Resource/Detail/Metadata/index.vue +6 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
- package/components/Resource/Detail/SpacedRow.vue +3 -1
- package/components/Resource/Detail/TitleBar/index.vue +10 -11
- package/components/ResourceDetail/index.vue +1 -1
- package/components/ResourceList/Masthead.vue +19 -9
- package/components/ResourceList/index.vue +82 -1
- package/components/RichTranslation.vue +5 -2
- package/components/SelectIconGrid.vue +0 -10
- package/components/Setting.vue +1 -0
- package/components/SingleClusterInfo.vue +1 -0
- package/components/SortableTable/__tests__/sorting.test.ts +126 -0
- package/components/SortableTable/index.vue +6 -9
- package/components/SortableTable/selection.js +23 -5
- package/components/SortableTable/sorting.js +6 -3
- package/components/SubtleLink.vue +31 -6
- package/components/Tabbed/Tab.vue +29 -3
- package/components/Tabbed/index.vue +25 -3
- package/components/TableOfContents/TableOfContents.vue +109 -0
- package/components/TableOfContents/composables.ts +258 -0
- package/components/Window/ContainerShell.vue +21 -11
- package/components/Window/__tests__/ContainerShell.test.ts +107 -37
- package/components/Wizard.vue +23 -17
- package/components/fleet/AppCoChartGrid.vue +401 -0
- package/components/fleet/AppCoEmptyState.vue +127 -0
- package/components/fleet/AppCoPageHeader.vue +119 -0
- package/components/fleet/AppCoVersionSelect.vue +70 -0
- package/components/fleet/FleetBundles.vue +100 -12
- package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
- package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
- package/components/fleet/FleetClusterTargets/index.vue +226 -161
- package/components/fleet/FleetIntro.vue +7 -3
- package/components/fleet/FleetNoWorkspaces.vue +7 -3
- package/components/fleet/FleetSecretSelector.vue +5 -3
- package/components/fleet/FleetValuesFrom.vue +8 -2
- package/components/fleet/GitRepoTargetTab.vue +0 -2
- package/components/fleet/HelmOpAdvancedTab.vue +19 -53
- package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
- package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
- package/components/fleet/HelmOpResourcesSection.vue +82 -0
- package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
- package/components/fleet/HelmOpTargetTab.vue +64 -60
- package/components/fleet/HelmOpValuesTab.vue +129 -105
- package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
- package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
- package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +402 -115
- package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
- package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
- package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
- package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
- package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
- package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
- package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
- package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
- package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
- package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
- package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
- package/components/fleet/dashboard/Empty.vue +8 -4
- package/components/fleet/dashboard/ResourceCard.vue +28 -0
- package/components/fleet/dashboard/ResourceDetails.vue +28 -0
- package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
- package/components/form/ArrayList.vue +61 -4
- package/components/form/KeyValue.vue +23 -2
- package/components/form/LabeledSelect.vue +59 -4
- package/components/form/Labels.vue +22 -3
- package/components/form/NameNsDescription.vue +24 -5
- package/components/form/ResourceTabs/index.vue +1 -0
- package/components/form/Security.vue +6 -2
- package/components/form/WorkloadPorts.vue +2 -7
- package/components/form/__tests__/NameNsDescription.test.ts +75 -0
- package/components/form/__tests__/Security.test.ts +76 -0
- package/components/formatter/Autoscaler.vue +4 -4
- package/components/formatter/ClusterKubeVersion.vue +27 -0
- package/components/formatter/ClusterLink.vue +1 -7
- package/components/formatter/ClusterProvider.vue +6 -10
- package/components/formatter/FleetSummaryGraph.vue +0 -3
- package/components/formatter/InternalExternalIP.vue +10 -4
- package/components/formatter/MachineSummaryGraph.vue +1 -1
- package/components/formatter/PodsUsage.vue +2 -2
- package/components/formatter/ServiceTargets.vue +26 -7
- package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
- package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
- package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
- package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
- package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
- package/components/nav/Header.vue +4 -0
- package/components/nav/NamespaceFilter.vue +2 -2
- package/components/nav/TopLevelMenu.helper.ts +15 -3
- package/components/nav/TopLevelMenu.vue +22 -6
- package/components/nav/__tests__/Header.test.ts +15 -0
- package/components/nav/__tests__/TopLevelMenu.test.ts +263 -21
- package/components/templates/default.vue +9 -4
- package/components/templates/home.vue +23 -0
- package/components/templates/plain.vue +23 -0
- package/components/templates/standalone.vue +17 -0
- package/composables/useFormValidation.ts +93 -0
- package/composables/useHelmOpResources.test.ts +56 -0
- package/composables/useHelmOpResources.ts +32 -0
- package/composables/useStateColor.test.ts +325 -0
- package/composables/useStateColor.ts +128 -0
- package/composables/useVeeValidateField.test.ts +159 -0
- package/composables/useVeeValidateField.ts +67 -0
- package/config/home-links.js +1 -1
- package/config/labels-annotations.js +1 -0
- package/config/pagination-table-headers.js +18 -1
- package/config/product/explorer.js +17 -4
- package/config/product/manager.js +84 -21
- package/config/router/index.js +16 -0
- package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
- package/config/router/navigation-guards/authentication.js +10 -4
- package/config/router/routes.js +26 -6
- package/config/settings.ts +0 -2
- package/config/table-headers.js +23 -5
- package/config/types.js +11 -1
- package/core/__tests__/plugin-products.test.ts +904 -20
- package/core/plugin-products-base.ts +110 -10
- package/core/plugin-products.ts +4 -0
- package/core/plugin-types.ts +194 -31
- package/core/plugin.ts +18 -7
- package/core/productDebugger.js +9 -4
- package/core/types-provisioning.ts +77 -31
- package/core/types.ts +72 -22
- package/detail/__tests__/pod.test.ts +41 -0
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
- package/detail/__tests__/workload.test.ts +3 -152
- package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
- package/detail/harvesterhci.io.management.cluster.vue +6 -2
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +34 -14
- package/detail/workload/index.vue +12 -55
- package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
- package/edit/auth/__tests__/azuread.test.ts +247 -39
- package/edit/auth/__tests__/github.test.ts +234 -0
- package/edit/auth/__tests__/oidc.test.ts +26 -6
- package/edit/auth/__tests__/saml.test.ts +196 -0
- package/edit/auth/azuread.vue +197 -56
- package/edit/auth/github.vue +72 -13
- package/edit/auth/ldap/__tests__/index.test.ts +206 -0
- package/edit/auth/ldap/config.vue +8 -0
- package/edit/auth/ldap/index.vue +75 -1
- package/edit/auth/oidc.vue +119 -73
- package/edit/auth/saml.vue +76 -12
- package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
- package/edit/fleet.cattle.io.helmop.vue +491 -136
- package/edit/management.cattle.io.user.vue +5 -2
- package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
- package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
- package/edit/networking.k8s.io.ingress/index.vue +75 -20
- package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
- package/edit/provisioning.cattle.io.cluster/rke2.vue +92 -14
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +22 -0
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
- package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
- package/edit/secret/__tests__/ssh.test.ts +5 -6
- package/edit/secret/basic.vue +31 -0
- package/edit/secret/index.vue +68 -17
- package/edit/secret/registry.vue +38 -0
- package/edit/secret/ssh.vue +29 -0
- package/edit/secret/tls.vue +30 -0
- package/edit/service.vue +4 -4
- package/edit/workload/Upgrading.vue +3 -3
- package/edit/workload/__tests__/Upgrading.test.ts +6 -9
- package/edit/workload/mixins/workload.js +2 -1
- package/list/fleet.cattle.io.bundle.vue +7 -104
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
- package/list/group.principal.vue +5 -4
- package/list/harvesterhci.io.management.cluster.vue +8 -9
- package/list/management.cattle.io.user.vue +12 -9
- package/list/provisioning.cattle.io.cluster.vue +268 -180
- package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
- package/mixins/__tests__/auth-config.test.ts +90 -0
- package/mixins/__tests__/chart.test.ts +206 -0
- package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
- package/mixins/auth-config.js +7 -0
- package/mixins/brand.js +2 -1
- package/mixins/chart.js +22 -9
- package/mixins/child-hook.js +12 -6
- package/mixins/create-edit-view/impl.js +5 -3
- package/mixins/resource-fetch-api-pagination.js +62 -6
- package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
- package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
- package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
- package/models/__tests__/fleet-application.test.ts +175 -0
- package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
- package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
- package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
- package/models/__tests__/management.cattle.io.node.ts +28 -5
- package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
- package/models/__tests__/namespace.test.ts +36 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +81 -11
- package/models/__tests__/workload.test.ts +401 -26
- package/models/base-cluster.x-k8s.io.js +26 -0
- package/models/catalog.cattle.io.clusterrepo.js +28 -4
- package/models/cluster.js +1 -1
- package/models/cluster.x-k8s.io.machine.js +4 -22
- package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
- package/models/cluster.x-k8s.io.machineset.js +2 -20
- package/models/compliance.cattle.io.clusterscan.js +165 -2
- package/models/ext.cattle.io.kubeconfig.ts +4 -7
- package/models/fleet-application.js +7 -1
- package/models/fleet.cattle.io.helmop.js +20 -1
- package/models/management.cattle.io.cluster.js +434 -41
- package/models/management.cattle.io.node.js +50 -7
- package/models/management.cattle.io.nodepool.js +1 -1
- package/models/namespace.js +1 -1
- package/models/networking.k8s.io.ingress.js +12 -4
- package/models/pod.js +33 -1
- package/models/provisioning.cattle.io.cluster.js +51 -334
- package/models/rke.cattle.io.etcdsnapshot.js +1 -2
- package/models/workload.js +108 -13
- package/models/workload.service.js +5 -0
- package/package.json +22 -39
- package/pages/__tests__/readme.test.ts +49 -0
- package/pages/about.vue +5 -6
- package/pages/auth/login.vue +0 -35
- package/pages/auth/setup.vue +13 -3
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
- package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
- package/pages/c/_cluster/apps/charts/chart.vue +62 -9
- package/pages/c/_cluster/apps/charts/index.vue +48 -10
- package/pages/c/_cluster/apps/charts/install.vue +122 -113
- package/pages/c/_cluster/auth/roles/index.vue +5 -4
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
- package/pages/c/_cluster/explorer/index.vue +5 -49
- package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
- package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
- package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
- package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
- package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
- package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
- package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
- package/pages/c/_cluster/fleet/application/create.vue +187 -136
- package/pages/c/_cluster/fleet/application/index.vue +5 -3
- package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
- package/pages/c/_cluster/fleet/index.vue +2 -2
- package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
- package/pages/c/_cluster/istio/index.vue +21 -6
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +815 -2
- package/pages/c/_cluster/uiplugins/index.vue +218 -197
- package/pages/diagnostic.vue +13 -17
- package/pages/fail-whale.vue +30 -7
- package/pages/home.vue +93 -306
- package/pages/readme.vue +88 -0
- package/plugins/clean-html.d.ts +9 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +181 -0
- package/plugins/dashboard-store/actions.js +40 -18
- package/plugins/dashboard-store/resource-class.js +67 -9
- package/plugins/steve/__tests__/actions.test.ts +212 -0
- package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
- package/plugins/steve/actions.js +96 -0
- package/plugins/steve/steve-pagination-utils.ts +12 -4
- package/plugins/steve/subscribe.js +35 -5
- package/rancher-components/Accordion/Accordion.vue +53 -9
- package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
- package/rancher-components/Form/Radio/RadioButton.vue +17 -1
- package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
- package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
- package/rancher-components/RcButton/RcButton.test.ts +140 -1
- package/rancher-components/RcButton/RcButton.vue +126 -17
- package/rancher-components/RcButton/types.ts +3 -0
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
- package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
- package/rancher-components/RcSection/RcSection.vue +28 -3
- package/scripts/extension/helm/package/Dockerfile +1 -1
- package/scripts/test-plugins-build.sh +2 -1
- package/store/__tests__/catalog.test.ts +115 -1
- package/store/__tests__/notifications.test.ts +434 -0
- package/store/__tests__/type-map.test.ts +556 -1
- package/store/action-menu.js +8 -3
- package/store/auth.js +1 -1
- package/store/aws.js +27 -16
- package/store/catalog.js +84 -3
- package/store/digitalocean.js +20 -38
- package/store/index.js +2 -0
- package/store/linode.js +25 -40
- package/store/plugins.js +7 -4
- package/store/pnap.js +1 -0
- package/store/type-map.js +111 -29
- package/tsconfig.paths.json +8 -8
- package/types/components/buttonGroup.ts +5 -0
- package/types/kube/kube-api.ts +14 -1
- package/types/rancher/steve.api.ts +12 -12
- package/types/resources/settings.d.ts +2 -1
- package/types/shell/index.d.ts +206 -72
- package/types/store/dashboard-store.types.ts +108 -11
- package/types/store/pagination.types.ts +6 -3
- package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
- package/utils/__tests__/async.test.ts +87 -0
- package/utils/__tests__/auth.test.ts +273 -0
- package/utils/__tests__/aws.test.ts +140 -0
- package/utils/__tests__/banners.test.ts +176 -0
- package/utils/__tests__/chart.test.ts +64 -1
- package/utils/__tests__/color.test.ts +226 -0
- package/utils/__tests__/computed.test.ts +193 -0
- package/utils/__tests__/cspAdaptor.test.ts +163 -0
- package/utils/__tests__/dom.test.ts +81 -0
- package/utils/__tests__/duration.test.ts +176 -0
- package/utils/__tests__/dynamic-importer.test.ts +102 -0
- package/utils/__tests__/fleet-appco.test.ts +312 -0
- package/utils/__tests__/fleet.test.ts +340 -0
- package/utils/__tests__/ingress.test.ts +553 -0
- package/utils/__tests__/kube.test.ts +68 -0
- package/utils/__tests__/monitoring.test.ts +130 -0
- package/utils/__tests__/namespace-filter.test.ts +109 -0
- package/utils/__tests__/object.test.ts +22 -0
- package/utils/__tests__/pagination-utils.test.ts +361 -0
- package/utils/__tests__/parse-externalid.test.ts +137 -0
- package/utils/__tests__/perf-setting.utils.test.ts +98 -0
- package/utils/__tests__/platform.test.ts +91 -0
- package/utils/__tests__/poller-sequential.test.ts +177 -0
- package/utils/__tests__/poller.test.ts +170 -0
- package/utils/__tests__/position.test.ts +237 -0
- package/utils/__tests__/promise.test.ts +346 -0
- package/utils/__tests__/provider.test.ts +51 -1
- package/utils/__tests__/queue.test.ts +232 -0
- package/utils/__tests__/release-notes.test.ts +221 -0
- package/utils/__tests__/router.test.js +254 -1
- package/utils/__tests__/select.test.ts +208 -0
- package/utils/__tests__/settings.test.ts +140 -0
- package/utils/__tests__/sort-utils.test.ts +301 -0
- package/utils/__tests__/string-utils.test.ts +798 -0
- package/utils/__tests__/string.test.ts +23 -1
- package/utils/__tests__/style.test.ts +154 -0
- package/utils/__tests__/svg-filter.test.ts +184 -0
- package/utils/__tests__/time.test.ts +265 -1
- package/utils/__tests__/title.test.ts +47 -0
- package/utils/__tests__/units.test.ts +417 -0
- package/utils/__tests__/versions.test.ts +128 -0
- package/utils/__tests__/width.test.ts +53 -0
- package/utils/__tests__/window.test.ts +158 -0
- package/utils/__tests__/xccdf.test.ts +511 -0
- package/utils/chart.js +36 -0
- package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
- package/utils/crypto/__tests__/index.test.ts +144 -0
- package/utils/duration.ts +104 -0
- package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
- package/utils/dynamic-content/info.ts +2 -1
- package/utils/error.js +13 -0
- package/utils/fleet-appco.ts +323 -0
- package/utils/fleet.ts +13 -3
- package/utils/gatekeeper/__tests__/util.test.ts +174 -0
- package/utils/gc/__tests__/gc-interval.test.ts +119 -0
- package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
- package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
- package/utils/gc/__tests__/gc.test.ts +487 -0
- package/utils/ingress.ts +9 -1
- package/utils/object.js +22 -2
- package/utils/pagination-utils.ts +2 -1
- package/utils/provider.ts +12 -0
- package/utils/string.js +25 -2
- package/utils/uiplugins.ts +5 -5
- package/utils/validators/__tests__/cluster-name.test.ts +110 -0
- package/utils/validators/__tests__/container-images.test.ts +104 -0
- package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
- package/utils/validators/__tests__/flow-output.test.ts +91 -0
- package/utils/validators/__tests__/index.test.ts +481 -0
- package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
- package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
- package/utils/validators/__tests__/misc-validators.test.ts +246 -0
- package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
- package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
- package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
- package/utils/validators/__tests__/role-template.test.ts +149 -0
- package/utils/validators/__tests__/service.test.ts +283 -0
- package/utils/validators/__tests__/setting.test.js +32 -0
- package/utils/validators/formRules/__tests__/index.test.ts +50 -0
- package/utils/validators/formRules/index.ts +5 -5
- package/utils/validators/machine-pool.ts +1 -1
- package/utils/validators/setting.js +18 -3
- package/utils/xccdf.ts +415 -0
- package/vue.config.js +1 -1
- package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
- package/pages/support/index.vue +0 -264
- package/utils/duration.js +0 -43
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import {
|
|
2
|
+
displayKeyFor,
|
|
3
|
+
validateLength,
|
|
4
|
+
validateChars,
|
|
5
|
+
validateHostname,
|
|
6
|
+
validateDnsLabel,
|
|
7
|
+
validateDnsLikeTypes,
|
|
8
|
+
validateBoolean,
|
|
9
|
+
} from '@shell/utils/validators';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Simple i18n getters mock: i18n/t returns a formatted key string,
|
|
13
|
+
* i18n/exists returns false by default (can be overridden per-test).
|
|
14
|
+
*/
|
|
15
|
+
function makeGetters(overrides: Record<string, unknown> = {}): Record<string, unknown> {
|
|
16
|
+
return {
|
|
17
|
+
'i18n/t': (key: string, args?: unknown) => (args ? `${ key }:${ JSON.stringify(args) }` : key),
|
|
18
|
+
'i18n/exists': () => false,
|
|
19
|
+
...overrides,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('displayKeyFor', () => {
|
|
24
|
+
it('returns i18n label when model label key exists', () => {
|
|
25
|
+
const getters = makeGetters({
|
|
26
|
+
'i18n/exists': (key: string) => key === 'model.Foo.bar.label',
|
|
27
|
+
'i18n/t': (key: string) => `translated:${ key }`,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
expect(displayKeyFor('Foo', 'bar', getters)).toBe('translated:model.Foo.bar.label');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('returns i18n prefix translation when prefix exists but not label', () => {
|
|
34
|
+
const getters = makeGetters({
|
|
35
|
+
'i18n/exists': (key: string) => key === 'model.Foo.bar',
|
|
36
|
+
'i18n/t': (key: string) => `translated:${ key }`,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(displayKeyFor('Foo', 'bar', getters)).toBe('translated:model.Foo.bar');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('strips Id suffix and camelToTitle when key ends with Id', () => {
|
|
43
|
+
const getters = makeGetters();
|
|
44
|
+
|
|
45
|
+
expect(displayKeyFor('Foo', 'clusterGroupId', getters)).toBe('Cluster Group');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('falls back to camelToTitle for plain keys', () => {
|
|
49
|
+
const getters = makeGetters();
|
|
50
|
+
|
|
51
|
+
expect(displayKeyFor('Foo', 'fooBar', getters)).toBe('Foo Bar');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('validateLength', () => {
|
|
56
|
+
describe('required field validation', () => {
|
|
57
|
+
it('pushes required error for undefined value when required=true and nullable=false', () => {
|
|
58
|
+
const getters = makeGetters();
|
|
59
|
+
const errors = validateLength(undefined, { required: true, nullable: false }, 'Name', getters);
|
|
60
|
+
|
|
61
|
+
expect(errors).toStrictEqual(['validation.required:{"key":"Name"}']);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('pushes required error for empty string when required=true', () => {
|
|
65
|
+
const getters = makeGetters();
|
|
66
|
+
const errors = validateLength('', { required: true, nullable: false }, 'Name', getters);
|
|
67
|
+
|
|
68
|
+
expect(errors).toStrictEqual(['validation.required:{"key":"Name"}']);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('pushes required error for empty object when required=true', () => {
|
|
72
|
+
const getters = makeGetters();
|
|
73
|
+
const errors = validateLength({}, { required: true, nullable: false }, 'Name', getters);
|
|
74
|
+
|
|
75
|
+
expect(errors).toStrictEqual(['validation.required:{"key":"Name"}']);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('does not push required error for 0 when required=true (0 is a valid falsy number)', () => {
|
|
79
|
+
const getters = makeGetters();
|
|
80
|
+
const errors = validateLength(0, { required: true, nullable: false }, 'Name', getters);
|
|
81
|
+
|
|
82
|
+
expect(errors).toStrictEqual([]);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('returns early without length check when required error is pushed', () => {
|
|
86
|
+
const getters = makeGetters();
|
|
87
|
+
const errors = validateLength(undefined, {
|
|
88
|
+
required: true, nullable: false, minLength: 5, maxLength: 10
|
|
89
|
+
}, 'Name', getters);
|
|
90
|
+
|
|
91
|
+
expect(errors).toHaveLength(1);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('does not push error for null when nullable=true', () => {
|
|
95
|
+
const getters = makeGetters();
|
|
96
|
+
const errors = validateLength(null, { required: true, nullable: true }, 'Name', getters);
|
|
97
|
+
|
|
98
|
+
expect(errors).toStrictEqual([]);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('string length validation', () => {
|
|
103
|
+
it.each([
|
|
104
|
+
{
|
|
105
|
+
desc: 'exactly error when min===max and string length is wrong',
|
|
106
|
+
val: 'ab',
|
|
107
|
+
field: { minLength: 5, maxLength: 5 },
|
|
108
|
+
key: 'Name',
|
|
109
|
+
expected: 'validation.stringLength.exactly',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
desc: 'between error when min!==max and string length is out of range',
|
|
113
|
+
val: 'ab',
|
|
114
|
+
field: { minLength: 5, maxLength: 10 },
|
|
115
|
+
key: 'Name',
|
|
116
|
+
expected: 'validation.stringLength.between',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
desc: 'min error when only min is set and string is too short',
|
|
120
|
+
val: 'ab',
|
|
121
|
+
field: { minLength: 5 },
|
|
122
|
+
key: 'Name',
|
|
123
|
+
expected: 'validation.stringLength.min',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
desc: 'max error when only max is set and string is too long',
|
|
127
|
+
val: 'abcdefghij',
|
|
128
|
+
field: { maxLength: 5 },
|
|
129
|
+
key: 'Name',
|
|
130
|
+
expected: 'validation.stringLength.max',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
desc: 'arrayLength key for array[ type',
|
|
134
|
+
val: [],
|
|
135
|
+
field: { type: 'array[string]', minLength: 1 },
|
|
136
|
+
key: 'Items',
|
|
137
|
+
expected: 'validation.arrayLength.min',
|
|
138
|
+
},
|
|
139
|
+
])('pushes $desc', ({
|
|
140
|
+
val, field, key, expected,
|
|
141
|
+
}) => {
|
|
142
|
+
const getters = makeGetters();
|
|
143
|
+
const errors = validateLength(val, field, key, getters);
|
|
144
|
+
|
|
145
|
+
expect(errors[0]).toContain(expected);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('returns no errors when string is within min/max bounds', () => {
|
|
149
|
+
const getters = makeGetters();
|
|
150
|
+
const errors = validateLength('abc', { minLength: 2, maxLength: 5 }, 'Name', getters);
|
|
151
|
+
|
|
152
|
+
expect(errors).toStrictEqual([]);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('number min/max validation', () => {
|
|
157
|
+
it.each([
|
|
158
|
+
{
|
|
159
|
+
desc: 'number.exactly when min===max and value is out of range',
|
|
160
|
+
val: 3,
|
|
161
|
+
field: { min: 5, max: 5 },
|
|
162
|
+
expected: 'validation.number.exactly',
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
desc: 'number.between when min!==max and value is out of range',
|
|
166
|
+
val: 3,
|
|
167
|
+
field: { min: 5, max: 10 },
|
|
168
|
+
expected: 'validation.number.between',
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
desc: 'number.min when only min is set and value is below min',
|
|
172
|
+
val: 2,
|
|
173
|
+
field: { min: 5 },
|
|
174
|
+
expected: 'validation.number.min',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
desc: 'number.max when only max is set and value exceeds max',
|
|
178
|
+
val: 20,
|
|
179
|
+
field: { max: 10 },
|
|
180
|
+
expected: 'validation.number.max',
|
|
181
|
+
},
|
|
182
|
+
])('pushes $desc', ({ val, field, expected }) => {
|
|
183
|
+
const getters = makeGetters();
|
|
184
|
+
const errors = validateLength(val, field, 'Count', getters);
|
|
185
|
+
|
|
186
|
+
expect(errors[0]).toContain(expected);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('returns no errors when number is within range', () => {
|
|
190
|
+
const getters = makeGetters();
|
|
191
|
+
const errors = validateLength(7, { min: 5, max: 10 }, 'Count', getters);
|
|
192
|
+
|
|
193
|
+
expect(errors).toStrictEqual([]);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe('validateChars', () => {
|
|
199
|
+
it('pushes chars error when value contains invalid chars defined by validChars', () => {
|
|
200
|
+
const getters = makeGetters();
|
|
201
|
+
const errors = validateChars('hello!', { validChars: 'a-z' }, 'Name', getters);
|
|
202
|
+
|
|
203
|
+
expect(errors[0]).toContain('validation.chars');
|
|
204
|
+
expect(errors[0]).toContain('!');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('pushes chars error when value contains chars in invalidChars', () => {
|
|
208
|
+
const getters = makeGetters();
|
|
209
|
+
const errors = validateChars('hello world', { invalidChars: ' ' }, 'Name', getters);
|
|
210
|
+
|
|
211
|
+
expect(errors[0]).toContain('validation.chars');
|
|
212
|
+
expect(errors[0]).toContain('[space]');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('converts space character to [space] in the error message', () => {
|
|
216
|
+
const getters = makeGetters();
|
|
217
|
+
const errors = validateChars('foo bar', { invalidChars: ' ' }, 'Name', getters);
|
|
218
|
+
|
|
219
|
+
expect(errors[0]).toContain('[space]');
|
|
220
|
+
expect(errors[0]).not.toContain('" "');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('deduplicates repeated invalid chars in error message', () => {
|
|
224
|
+
const getters = makeGetters();
|
|
225
|
+
const errors = validateChars('a!b!c!', { validChars: 'a-z' }, 'Name', getters);
|
|
226
|
+
|
|
227
|
+
// should only contain one unique '!' in the chars list
|
|
228
|
+
const charsArg = errors[0];
|
|
229
|
+
|
|
230
|
+
expect((charsArg.match(/!/g) || [])).toHaveLength(1);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('returns no errors when value matches validChars', () => {
|
|
234
|
+
const getters = makeGetters();
|
|
235
|
+
const errors = validateChars('hello', { validChars: 'a-z' }, 'Name', getters);
|
|
236
|
+
|
|
237
|
+
expect(errors).toStrictEqual([]);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('returns no errors when no validChars or invalidChars defined', () => {
|
|
241
|
+
const getters = makeGetters();
|
|
242
|
+
const errors = validateChars('anything!@#', {}, 'Name', getters);
|
|
243
|
+
|
|
244
|
+
expect(errors).toStrictEqual([]);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
describe('validateHostname', () => {
|
|
249
|
+
it.each([
|
|
250
|
+
{
|
|
251
|
+
desc: 'startDot when hostname starts with a dot',
|
|
252
|
+
val: '.example.com',
|
|
253
|
+
opts: {},
|
|
254
|
+
expected: 'startDot',
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
desc: 'empty when hostname is empty string',
|
|
258
|
+
val: '',
|
|
259
|
+
opts: {},
|
|
260
|
+
expected: 'empty',
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
desc: 'tooLong when hostname exceeds 253 characters',
|
|
264
|
+
val: 'a'.repeat(254),
|
|
265
|
+
opts: {},
|
|
266
|
+
expected: 'tooLong',
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
desc: 'tooLong with custom max length option',
|
|
270
|
+
val: 'abcdef',
|
|
271
|
+
opts: { max: 5 },
|
|
272
|
+
expected: 'tooLong',
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
desc: 'endDot in restricted mode when hostname ends with dot',
|
|
276
|
+
val: 'example.com.',
|
|
277
|
+
opts: { restricted: true },
|
|
278
|
+
expected: 'endDot',
|
|
279
|
+
},
|
|
280
|
+
])('pushes $desc', ({ val, opts, expected }) => {
|
|
281
|
+
const getters = makeGetters();
|
|
282
|
+
const errors = validateHostname(val, 'Host', getters, opts);
|
|
283
|
+
|
|
284
|
+
expect(errors.some((e: string) => e.includes(expected))).toBe(true);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it.each([
|
|
288
|
+
{
|
|
289
|
+
desc: 'trailing dot in non-restricted mode (FQDN notation)',
|
|
290
|
+
val: 'example.com.',
|
|
291
|
+
opts: { restricted: false },
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
desc: 'a valid simple hostname',
|
|
295
|
+
val: 'my-host',
|
|
296
|
+
opts: {},
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
desc: 'a valid FQDN',
|
|
300
|
+
val: 'my-host.example.com',
|
|
301
|
+
opts: {},
|
|
302
|
+
},
|
|
303
|
+
])('allows $desc', ({ val, opts }) => {
|
|
304
|
+
const getters = makeGetters();
|
|
305
|
+
const errors = validateHostname(val, 'Host', getters, opts);
|
|
306
|
+
|
|
307
|
+
expect(errors).toStrictEqual([]);
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
describe('validateDnsLabel', () => {
|
|
312
|
+
it.each([
|
|
313
|
+
{
|
|
314
|
+
desc: 'startHyphen when label starts with a hyphen',
|
|
315
|
+
label: '-bad',
|
|
316
|
+
opts: {},
|
|
317
|
+
expected: 'startHyphen',
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
desc: 'endHyphen when label ends with a hyphen',
|
|
321
|
+
label: 'bad-',
|
|
322
|
+
opts: {},
|
|
323
|
+
expected: 'endHyphen',
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
desc: 'emptyLabel when label is empty',
|
|
327
|
+
label: '',
|
|
328
|
+
opts: {},
|
|
329
|
+
expected: 'emptyLabel',
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
desc: 'tooLongLabel when label exceeds 63 characters',
|
|
333
|
+
label: 'a'.repeat(64),
|
|
334
|
+
opts: {},
|
|
335
|
+
expected: 'tooLongLabel',
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
desc: 'startNumber in restricted mode when label starts with a digit',
|
|
339
|
+
label: '1abc',
|
|
340
|
+
opts: { restricted: true },
|
|
341
|
+
expected: 'startNumber',
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
desc: 'doubleHyphen when label has -- at 3rd and 4th position (non-xn prefix)',
|
|
345
|
+
label: 'ab--cd',
|
|
346
|
+
opts: {},
|
|
347
|
+
expected: 'doubleHyphen',
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
desc: 'doubleHyphen for ianaServiceName with any consecutive hyphens',
|
|
351
|
+
label: 'xn--valid',
|
|
352
|
+
opts: { ianaServiceName: true },
|
|
353
|
+
expected: 'doubleHyphen',
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
desc: 'hostname.startHyphen when forHostname=true',
|
|
357
|
+
label: '-bad',
|
|
358
|
+
opts: { forHostname: true },
|
|
359
|
+
expected: 'hostname.startHyphen',
|
|
360
|
+
},
|
|
361
|
+
])('pushes $desc', ({ label, opts, expected }) => {
|
|
362
|
+
const getters = makeGetters();
|
|
363
|
+
const errors = validateDnsLabel(label, 'Label', getters, opts);
|
|
364
|
+
|
|
365
|
+
expect(errors.some((e: string) => e.includes(expected))).toBe(true);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it.each([
|
|
369
|
+
{
|
|
370
|
+
desc: 'a valid DNS label',
|
|
371
|
+
label: 'my-label',
|
|
372
|
+
opts: {},
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
desc: 'digit-start in non-restricted mode',
|
|
376
|
+
label: '1abc',
|
|
377
|
+
opts: { restricted: false },
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
desc: 'xn-- prefix (IDN string)',
|
|
381
|
+
label: 'xn--nxasmq6b',
|
|
382
|
+
opts: {},
|
|
383
|
+
},
|
|
384
|
+
])('allows $desc without errors', ({ label, opts }) => {
|
|
385
|
+
const getters = makeGetters();
|
|
386
|
+
const errors = validateDnsLabel(label, 'Label', getters, opts);
|
|
387
|
+
|
|
388
|
+
expect(errors).toStrictEqual([]);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe('validateDnsLikeTypes', () => {
|
|
393
|
+
it.each([
|
|
394
|
+
{
|
|
395
|
+
desc: 'dnsLabel type pushes error for empty label',
|
|
396
|
+
val: '',
|
|
397
|
+
type: 'dnsLabel',
|
|
398
|
+
expected: 'emptyLabel',
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
desc: 'dnsLabelRestricted type pushes startNumber for digit-start',
|
|
402
|
+
val: '1abc',
|
|
403
|
+
type: 'dnsLabelRestricted',
|
|
404
|
+
expected: 'startNumber',
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
desc: 'hostname type pushes startDot for dot-start',
|
|
408
|
+
val: '.bad',
|
|
409
|
+
type: 'hostname',
|
|
410
|
+
expected: 'startDot',
|
|
411
|
+
},
|
|
412
|
+
])('validates $desc', ({ val, type, expected }) => {
|
|
413
|
+
const getters = makeGetters();
|
|
414
|
+
const errors = validateDnsLikeTypes(val, type, 'Name', getters, {});
|
|
415
|
+
|
|
416
|
+
expect(errors.some((e: string) => e.includes(expected))).toBe(true);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('returns no errors for unknown type (no-op)', () => {
|
|
420
|
+
const getters = makeGetters();
|
|
421
|
+
const errors = validateDnsLikeTypes('anything', 'unknownType', 'Name', getters, {});
|
|
422
|
+
|
|
423
|
+
expect(errors).toStrictEqual([]);
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
describe('validateBoolean', () => {
|
|
428
|
+
it('pushes required error when field is required and value is undefined', () => {
|
|
429
|
+
const getters = makeGetters();
|
|
430
|
+
const errors: string[] = [];
|
|
431
|
+
|
|
432
|
+
validateBoolean(undefined, { required: true }, 'Flag', getters, errors);
|
|
433
|
+
|
|
434
|
+
expect(errors.some((e: string) => e.includes('validation.required'))).toBe(true);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('does not push required error when value is false (explicitly set)', () => {
|
|
438
|
+
const getters = makeGetters();
|
|
439
|
+
const errors: string[] = [];
|
|
440
|
+
|
|
441
|
+
validateBoolean(false, { required: true }, 'Flag', getters, errors);
|
|
442
|
+
|
|
443
|
+
expect(errors).toStrictEqual([]);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it('does not push required error when value is true', () => {
|
|
447
|
+
const getters = makeGetters();
|
|
448
|
+
const errors: string[] = [];
|
|
449
|
+
|
|
450
|
+
validateBoolean(true, { required: true }, 'Flag', getters, errors);
|
|
451
|
+
|
|
452
|
+
expect(errors).toStrictEqual([]);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('pushes boolean error when value is non-boolean truthy (e.g. string)', () => {
|
|
456
|
+
const getters = makeGetters();
|
|
457
|
+
const errors: string[] = [];
|
|
458
|
+
|
|
459
|
+
validateBoolean('yes', { required: false }, 'Flag', getters, errors);
|
|
460
|
+
|
|
461
|
+
expect(errors.some((e: string) => e.includes('validation.boolean'))).toBe(true);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it('does not push error when field is not required and value is undefined', () => {
|
|
465
|
+
const getters = makeGetters();
|
|
466
|
+
const errors: string[] = [];
|
|
467
|
+
|
|
468
|
+
validateBoolean(undefined, { required: false }, 'Flag', getters, errors);
|
|
469
|
+
|
|
470
|
+
expect(errors).toStrictEqual([]);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('does not push boolean error for numeric 0 when required=false and val is falsy', () => {
|
|
474
|
+
const getters = makeGetters();
|
|
475
|
+
const errors: string[] = [];
|
|
476
|
+
|
|
477
|
+
validateBoolean(0, { required: false }, 'Flag', getters, errors);
|
|
478
|
+
|
|
479
|
+
expect(errors).toStrictEqual([]);
|
|
480
|
+
});
|
|
481
|
+
});
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { validateKubernetesName } from '@shell/utils/validators/kubernetes-name';
|
|
2
|
+
|
|
3
|
+
function makeGetters(): Record<string, unknown> {
|
|
4
|
+
return {
|
|
5
|
+
'i18n/t': (key: string, args?: unknown) => (args ? `${ key }:${ JSON.stringify(args) }` : key),
|
|
6
|
+
'i18n/exists': () => false,
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe('validateKubernetesName', () => {
|
|
11
|
+
describe('valid names', () => {
|
|
12
|
+
it('accepts a simple lowercase name', () => {
|
|
13
|
+
const errors = validateKubernetesName('my-app', 'Name', makeGetters(), {});
|
|
14
|
+
|
|
15
|
+
expect(errors).toStrictEqual([]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('accepts alphanumeric name', () => {
|
|
19
|
+
const errors = validateKubernetesName('abc123', 'Name', makeGetters(), {});
|
|
20
|
+
|
|
21
|
+
expect(errors).toStrictEqual([]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('accepts single character', () => {
|
|
25
|
+
const errors = validateKubernetesName('a', 'Name', makeGetters(), {});
|
|
26
|
+
|
|
27
|
+
expect(errors).toStrictEqual([]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('accepts name at max length (63)', () => {
|
|
31
|
+
const name = 'a'.repeat(63);
|
|
32
|
+
const errors = validateKubernetesName(name, 'Name', makeGetters(), {});
|
|
33
|
+
|
|
34
|
+
expect(errors).toStrictEqual([]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('accepts uppercase letters by default validChars', () => {
|
|
38
|
+
const errors = validateKubernetesName('MyApp', 'Name', makeGetters(), {});
|
|
39
|
+
|
|
40
|
+
expect(errors).toStrictEqual([]);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('invalid characters', () => {
|
|
45
|
+
it('rejects underscores', () => {
|
|
46
|
+
const errors = validateKubernetesName('my_app', 'Name', makeGetters(), {});
|
|
47
|
+
|
|
48
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('rejects dots', () => {
|
|
52
|
+
const errors = validateKubernetesName('my.app', 'Name', makeGetters(), {});
|
|
53
|
+
|
|
54
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('rejects spaces', () => {
|
|
58
|
+
const errors = validateKubernetesName('my app', 'Name', makeGetters(), {});
|
|
59
|
+
|
|
60
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('hyphen position rules', () => {
|
|
65
|
+
it('rejects name starting with hyphen', () => {
|
|
66
|
+
const errors = validateKubernetesName('-myapp', 'Name', makeGetters(), {});
|
|
67
|
+
|
|
68
|
+
expect(errors).toContain('validation.dns.label.startHyphen:{"key":"Name"}');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('rejects name ending with hyphen', () => {
|
|
72
|
+
const errors = validateKubernetesName('myapp-', 'Name', makeGetters(), {});
|
|
73
|
+
|
|
74
|
+
expect(errors).toContain('validation.dns.label.endHyphen:{"key":"Name"}');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('rejects name starting and ending with hyphen', () => {
|
|
78
|
+
const errors = validateKubernetesName('-myapp-', 'Name', makeGetters(), {});
|
|
79
|
+
|
|
80
|
+
expect(errors).toContain('validation.dns.label.startHyphen:{"key":"Name"}');
|
|
81
|
+
expect(errors).toContain('validation.dns.label.endHyphen:{"key":"Name"}');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('allows hyphen in the middle', () => {
|
|
85
|
+
const errors = validateKubernetesName('my-app', 'Name', makeGetters(), {});
|
|
86
|
+
|
|
87
|
+
expect(errors).toStrictEqual([]);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('length rules', () => {
|
|
92
|
+
it('rejects empty string', () => {
|
|
93
|
+
const errors = validateKubernetesName('', 'Name', makeGetters(), {});
|
|
94
|
+
|
|
95
|
+
expect(errors).toContain('validation.dns.label.emptyLabel:{"key":"Name","min":1}');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('rejects name exceeding 63 characters', () => {
|
|
99
|
+
const name = 'a'.repeat(64);
|
|
100
|
+
const errors = validateKubernetesName(name, 'Name', makeGetters(), {});
|
|
101
|
+
|
|
102
|
+
expect(errors).toContain('validation.dns.label.tooLongLabel:{"key":"Name","max":63}');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('respects custom maxLength option', () => {
|
|
106
|
+
const errors = validateKubernetesName('ab', 'Name', makeGetters(), { maxLength: 1 });
|
|
107
|
+
|
|
108
|
+
expect(errors).toContain('validation.dns.label.tooLongLabel:{"key":"Name","max":1}');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('respects custom minLength option', () => {
|
|
112
|
+
const errors = validateKubernetesName('ab', 'Name', makeGetters(), { minLength: 5 });
|
|
113
|
+
|
|
114
|
+
expect(errors).toContain('validation.dns.label.emptyLabel:{"key":"Name","min":5}');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('hostname mode', () => {
|
|
119
|
+
it('uses hostname errorKey when forHostname is true', () => {
|
|
120
|
+
const errors = validateKubernetesName('-myapp', 'Name', makeGetters(), { forHostname: true });
|
|
121
|
+
|
|
122
|
+
expect(errors).toContain('validation.dns.hostname.startHyphen:{"key":"Name"}');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('uses label errorKey by default', () => {
|
|
126
|
+
const errors = validateKubernetesName('-myapp', 'Name', makeGetters(), {});
|
|
127
|
+
|
|
128
|
+
expect(errors).toContain('validation.dns.label.startHyphen:{"key":"Name"}');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('custom errorKey', () => {
|
|
133
|
+
it('uses custom errorKey when provided', () => {
|
|
134
|
+
const errors = validateKubernetesName('-myapp', 'Name', makeGetters(), { errorKey: 'custom' });
|
|
135
|
+
|
|
136
|
+
expect(errors).toContain('validation.dns.custom.startHyphen:{"key":"Name"}');
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('errors array accumulation', () => {
|
|
141
|
+
it('appends to existing errors array', () => {
|
|
142
|
+
const existing = ['existing-error'];
|
|
143
|
+
const errors = validateKubernetesName('', 'Name', makeGetters(), {}, existing);
|
|
144
|
+
|
|
145
|
+
expect(errors[0]).toBe('existing-error');
|
|
146
|
+
expect(errors.length).toBeGreaterThan(1);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('defaults to empty errors array', () => {
|
|
150
|
+
const errors = validateKubernetesName('valid', 'Name', makeGetters(), {});
|
|
151
|
+
|
|
152
|
+
expect(errors).toStrictEqual([]);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('invalidChars option', () => {
|
|
157
|
+
it('rejects characters matching invalidChars pattern', () => {
|
|
158
|
+
const errors = validateKubernetesName('my-App', 'Name', makeGetters(), { invalidChars: 'A-Z' });
|
|
159
|
+
|
|
160
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { logdna } from '@shell/utils/validators/logging-outputs';
|
|
2
|
+
|
|
3
|
+
const mockGetters = {
|
|
4
|
+
'i18n/t': (key: string, args?: object) => (args ? `${ key }:${ JSON.stringify(args) }` : key),
|
|
5
|
+
'i18n/exists': () => false,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
describe('validators/logging-outputs', () => {
|
|
9
|
+
describe('logdna', () => {
|
|
10
|
+
it('adds no error when value is empty object', () => {
|
|
11
|
+
const errors: string[] = [];
|
|
12
|
+
|
|
13
|
+
logdna({}, mockGetters, errors, []);
|
|
14
|
+
|
|
15
|
+
expect(errors).toStrictEqual([]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('adds no error when value is null', () => {
|
|
19
|
+
const errors: string[] = [];
|
|
20
|
+
|
|
21
|
+
logdna(null, mockGetters, errors, []);
|
|
22
|
+
|
|
23
|
+
expect(errors).toStrictEqual([]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('adds no error when value is undefined', () => {
|
|
27
|
+
const errors: string[] = [];
|
|
28
|
+
|
|
29
|
+
logdna(undefined, mockGetters, errors, []);
|
|
30
|
+
|
|
31
|
+
expect(errors).toStrictEqual([]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('adds no error when api_key is present', () => {
|
|
35
|
+
const errors: string[] = [];
|
|
36
|
+
|
|
37
|
+
logdna({ api_key: 'my-secret-key' }, mockGetters, errors, []);
|
|
38
|
+
|
|
39
|
+
expect(errors).toStrictEqual([]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('adds apiKey error when api_key is missing', () => {
|
|
43
|
+
const errors: string[] = [];
|
|
44
|
+
|
|
45
|
+
logdna({ host: 'logs.example.com' }, mockGetters, errors, []);
|
|
46
|
+
|
|
47
|
+
expect(errors).toStrictEqual(['validation.output.logdna.apiKey']);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('adds apiKey error when api_key is empty string', () => {
|
|
51
|
+
const errors: string[] = [];
|
|
52
|
+
|
|
53
|
+
logdna({ api_key: '' }, mockGetters, errors, []);
|
|
54
|
+
|
|
55
|
+
expect(errors).toStrictEqual(['validation.output.logdna.apiKey']);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|