@rancher/shell 3.0.12-rc.1 → 3.0.12-rc.3
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/images/providers/entraid-black.svg +4 -0
- package/assets/images/providers/entraid.svg +9 -0
- package/assets/images/vendor/entraid.svg +9 -0
- package/assets/styles/app.scss +0 -1
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/fonts/_fontstack.scss +132 -8
- package/assets/translations/en-us.yaml +41 -22
- package/assets/translations/zh-hans.yaml +4 -8
- package/chart/__tests__/S3.test.ts +10 -3
- package/chart/monitoring/index.vue +10 -1
- package/components/ActionDropdownShell.vue +2 -1
- package/components/CountBox.vue +20 -0
- package/components/CreateDriver.vue +0 -12
- package/components/CruResourceFooter.vue +9 -5
- package/components/DetailText.vue +12 -3
- package/components/ExplorerProjectsNamespaces.vue +1 -1
- package/components/InstallHelmCharts.vue +2 -2
- package/components/LandingPagePreference.vue +14 -5
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
- 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/ResourceList/Masthead.vue +12 -8
- package/components/SelectIconGrid.vue +5 -10
- 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/Wizard.vue +14 -13
- package/components/__tests__/CountBox.test.ts +72 -0
- package/components/__tests__/DetailText.test.ts +113 -0
- package/components/fleet/FleetBundles.vue +100 -12
- package/components/fleet/FleetClusterTargets/index.vue +54 -15
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
- package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
- package/components/form/InputWithSelect.vue +18 -10
- package/components/form/KeyValue.vue +17 -1
- package/components/form/LabeledSelect.vue +101 -26
- package/components/form/NameNsDescription.vue +11 -0
- package/components/form/Security.vue +6 -2
- package/components/form/Select.vue +73 -56
- package/components/form/ServiceNameSelect.vue +13 -11
- package/components/form/WorkloadPorts.vue +2 -7
- package/components/form/__tests__/KeyValue.test.ts +66 -0
- package/components/form/__tests__/NodeScheduling.test.ts +9 -0
- package/components/form/__tests__/Security.test.ts +76 -0
- package/components/form/labeled-select-utils/useLabeledSelectPagination.ts +138 -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/MachineSummaryGraph.vue +1 -1
- package/components/formatter/PodsUsage.vue +2 -2
- package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
- package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
- package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
- package/components/nav/Group.vue +7 -6
- package/components/nav/Header.vue +24 -3
- package/components/nav/NamespaceFilter.vue +2 -2
- package/components/nav/NotificationCenter/Notification.vue +4 -1
- package/components/nav/NotificationCenter/NotificationHeader.vue +20 -8
- package/components/nav/NotificationCenter/__tests__/NotificationHeader.test.ts +80 -0
- package/components/nav/TopLevelMenu.helper.ts +15 -3
- package/components/nav/TopLevelMenu.vue +16 -5
- package/components/nav/Type.vue +8 -7
- package/components/nav/WindowManager/index.vue +2 -1
- package/components/nav/WorkspaceSwitcher.vue +13 -0
- package/components/nav/__tests__/Group.test.ts +67 -0
- package/components/nav/__tests__/Header.test.ts +235 -0
- package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
- package/components/nav/__tests__/Type.test.ts +20 -3
- package/components/templates/default.vue +34 -4
- package/components/templates/home.vue +30 -25
- package/components/templates/plain.vue +31 -26
- package/components/templates/standalone.vue +17 -0
- package/composables/useFormValidation.ts +93 -0
- package/composables/useLabeledFormElement.ts +10 -2
- package/composables/useLabeledSelect.ts +60 -0
- package/composables/useUserRetentionValidation.ts +1 -49
- package/composables/useVeeValidateField.test.ts +159 -0
- package/composables/useVeeValidateField.ts +67 -0
- package/config/cookies.js +0 -1
- package/config/labels-annotations.js +1 -0
- package/config/pagination-table-headers.js +18 -1
- package/config/product/manager.js +82 -21
- package/config/query-params.js +1 -0
- package/config/router/routes.js +6 -8
- package/config/table-headers.js +20 -1
- package/config/types.js +2 -1
- package/core/__tests__/plugin-products.test.ts +1505 -30
- package/core/plugin-products-base.ts +137 -20
- package/core/plugin-products-helpers.ts +5 -4
- package/core/plugin-products.ts +4 -0
- package/core/plugin-types.ts +129 -4
- package/core/plugin.ts +15 -7
- package/core/productDebugger.js +9 -4
- package/core/types-provisioning.ts +43 -30
- package/core/types.ts +58 -19
- package/detail/__tests__/management.cattle.io.fleetworkspace.test.ts +128 -0
- package/detail/__tests__/pod.test.ts +41 -0
- package/detail/harvesterhci.io.management.cluster.vue +6 -2
- package/detail/management.cattle.io.fleetworkspace.vue +49 -0
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +4 -10
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +9 -0
- package/edit/__tests__/kontainerDriver.test.ts +0 -13
- package/edit/__tests__/nodeDriver.test.ts +5 -11
- package/edit/__tests__/resources.cattle.io.restore.test.ts +9 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
- package/edit/auth/__tests__/azuread.test.ts +217 -34
- package/edit/auth/__tests__/oidc.test.ts +54 -0
- package/edit/auth/azuread.vue +123 -15
- package/edit/auth/oidc.vue +10 -2
- package/edit/kontainerDriver.vue +1 -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/nodeDriver.vue +0 -2
- package/edit/provisioning.cattle.io.cluster/AgentEnv.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/AgentEnv.test.ts +25 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +81 -106
- package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -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/initialize/App.vue +29 -2
- package/initialize/install-plugins.js +0 -2
- package/list/__tests__/management.cattle.io.feature.test.ts +105 -0
- package/list/catalog.cattle.io.app.vue +25 -5
- package/list/fleet.cattle.io.bundle.vue +7 -104
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
- package/list/management.cattle.io.feature.vue +1 -1
- package/list/management.cattle.io.fleetworkspace.vue +8 -0
- package/list/provisioning.cattle.io.cluster.vue +262 -180
- package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
- package/machine-config/amazonec2.vue +1 -0
- package/mixins/__tests__/chart.test.ts +112 -0
- package/mixins/brand.js +2 -1
- package/mixins/chart.js +50 -15
- package/mixins/resource-fetch-api-pagination.js +41 -5
- package/models/__tests__/catalog.cattle.io.app.test.ts +15 -1
- package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +84 -0
- package/models/__tests__/chart.test.ts +99 -6
- package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
- package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
- package/models/__tests__/management.cattle.io.feature.test.ts +131 -0
- package/models/__tests__/management.cattle.io.node.ts +6 -5
- package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
- package/models/__tests__/monitoring.coreos.com.alertmanagerconfig.test.ts +98 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
- package/models/base-cluster.x-k8s.io.js +26 -0
- package/models/catalog.cattle.io.app.js +21 -17
- package/models/catalog.cattle.io.clusterrepo.js +39 -11
- package/models/chart.js +33 -19
- 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 +130 -2
- package/models/ext.cattle.io.kubeconfig.ts +4 -7
- package/models/fleet-application.js +4 -2
- package/models/fleet.cattle.io.bundle.js +1 -1
- package/models/kontainerdriver.js +11 -0
- package/models/management.cattle.io.authconfig.js +5 -1
- package/models/management.cattle.io.cluster.js +402 -78
- package/models/management.cattle.io.feature.js +3 -3
- package/models/management.cattle.io.kontainerdriver.js +1 -26
- package/models/management.cattle.io.node.js +6 -4
- package/models/management.cattle.io.nodepool.js +1 -1
- package/models/monitoring.coreos.com.alertmanagerconfig.js +31 -17
- package/models/networking.k8s.io.ingress.js +12 -4
- package/models/nodedriver.js +7 -0
- package/models/provisioning.cattle.io.cluster.js +47 -330
- package/models/rke.cattle.io.etcdsnapshot.js +1 -2
- package/package.json +20 -37
- package/pages/__tests__/readme.test.ts +49 -0
- package/pages/auth/setup.vue +2 -3
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +265 -0
- package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +55 -0
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +53 -0
- package/pages/c/_cluster/apps/charts/chart.vue +275 -39
- package/pages/c/_cluster/apps/charts/index.vue +2 -2
- package/pages/c/_cluster/apps/charts/install.vue +18 -10
- package/pages/c/_cluster/auth/user.retention/index.vue +55 -22
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
- package/pages/c/_cluster/explorer/index.vue +5 -49
- 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/manager/drivers/kontainerDriver/index.vue +5 -7
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +40 -2
- package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +61 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +735 -13
- package/pages/c/_cluster/uiplugins/index.vue +226 -222
- package/pages/diagnostic.vue +13 -17
- package/pages/fail-whale.vue +18 -0
- package/pages/home.vue +77 -260
- package/pages/readme.vue +88 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
- package/plugins/dashboard-store/actions.js +40 -18
- package/plugins/dashboard-store/resource-class.js +5 -2
- package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
- package/plugins/steve/steve-pagination-utils.ts +11 -3
- package/plugins/steve/subscribe.js +35 -5
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +211 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +37 -4
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +1 -1
- package/rancher-components/RcButton/RcButton.test.ts +37 -1
- package/rancher-components/RcButton/RcButton.vue +38 -8
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
- package/scripts/test-plugins-build.sh +5 -2
- package/server/server-middleware.js +2 -2
- package/static/humans.txt +1 -0
- package/static/robots.txt +34 -0
- package/static/welcome-cow.svg +18 -0
- package/store/__tests__/catalog.test.ts +276 -12
- package/store/__tests__/type-map.test.ts +556 -1
- package/store/action-menu.js +8 -3
- package/store/auth.js +1 -4
- package/store/aws.js +27 -16
- package/store/catalog.js +87 -11
- package/store/digitalocean.js +20 -38
- package/store/index.js +2 -0
- package/store/linode.js +25 -40
- package/store/pnap.js +1 -0
- package/store/type-map.js +111 -29
- package/tsconfig.paths.json +8 -8
- 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 +128 -24
- 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__/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__/duration.test.ts +140 -0
- package/utils/__tests__/fleet.test.ts +340 -0
- package/utils/__tests__/git.test.ts +270 -0
- package/utils/__tests__/inactivity.test.ts +316 -0
- package/utils/__tests__/ingress.test.ts +553 -0
- package/utils/__tests__/kube.test.ts +68 -0
- package/utils/__tests__/namespace-filter.test.ts +109 -0
- package/utils/__tests__/object.test.ts +77 -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__/poller-sequential.test.ts +177 -0
- package/utils/__tests__/poller.test.ts +170 -0
- package/utils/__tests__/promise.test.ts +346 -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 +14 -1
- package/utils/__tests__/units.test.ts +417 -0
- package/utils/__tests__/url.test.ts +246 -0
- package/utils/__tests__/versions.test.ts +128 -0
- package/utils/__tests__/xccdf.test.ts +391 -0
- package/utils/chart.js +36 -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 +33 -2
- package/utils/pagination-utils.ts +2 -1
- package/utils/string.js +25 -2
- package/utils/time.ts +5 -0
- package/utils/uiplugins.ts +5 -5
- package/utils/validators/__tests__/cluster-name.test.ts +110 -0
- package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
- package/utils/validators/__tests__/index.test.ts +481 -0
- package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
- package/utils/validators/__tests__/misc-validators.test.ts +246 -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 +418 -0
- package/vue.config.js +0 -9
- 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/assets/images/providers/azuread-black.svg +0 -22
- package/assets/images/providers/azuread.svg +0 -25
- package/assets/images/vendor/azuread.svg +0 -18
- package/assets/styles/fonts/_dots.scss +0 -18
- package/components/EmberPage.vue +0 -622
- package/components/EmberPageView.vue +0 -39
- package/components/form/labeled-select-utils/labeled-select-pagination.ts +0 -116
- package/mixins/labeled-form-element.ts +0 -225
- package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -28
- package/pages/c/_cluster/manager/pages/_page.vue +0 -22
- package/pages/c/_cluster/mcapps/pages/_page.vue +0 -22
- package/plugins/ember-cookie.js +0 -17
- package/utils/ember-page.js +0 -30
|
@@ -214,6 +214,72 @@ describe('chartMixin', () => {
|
|
|
214
214
|
id: 'custom-ns/custom-name'
|
|
215
215
|
});
|
|
216
216
|
});
|
|
217
|
+
|
|
218
|
+
it('should fall back to matchingInstalledApps when version namespace lookup fails', async() => {
|
|
219
|
+
const installedApp = {
|
|
220
|
+
id: 'other-ns/custom-name',
|
|
221
|
+
spec: { chart: { metadata: { version: '0.9.0' } } }
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const mockStore = {
|
|
225
|
+
dispatch: jest.fn((action, payload) => {
|
|
226
|
+
if (action === 'cluster/find') {
|
|
227
|
+
return Promise.reject(new Error('not found'));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return Promise.resolve();
|
|
231
|
+
}),
|
|
232
|
+
getters: {
|
|
233
|
+
currentCluster: () => {},
|
|
234
|
+
isRancher: () => true,
|
|
235
|
+
'catalog/repo': () => {
|
|
236
|
+
return () => 'repo';
|
|
237
|
+
},
|
|
238
|
+
'catalog/chart': () => {
|
|
239
|
+
return {
|
|
240
|
+
id: 'chart-id',
|
|
241
|
+
versions: [{ version: '1.0.0' }],
|
|
242
|
+
matchingInstalledApps: [installedApp]
|
|
243
|
+
};
|
|
244
|
+
},
|
|
245
|
+
'catalog/version': () => ({
|
|
246
|
+
version: '1.0.0',
|
|
247
|
+
annotations: {
|
|
248
|
+
[CATALOG_ANNOTATIONS.NAMESPACE]: 'custom-ns',
|
|
249
|
+
[CATALOG_ANNOTATIONS.RELEASE_NAME]: 'custom-name',
|
|
250
|
+
}
|
|
251
|
+
}),
|
|
252
|
+
'prefs/get': () => {},
|
|
253
|
+
'i18n/t': () => jest.fn()
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const DummyComponent = {
|
|
258
|
+
mixins: [ChartMixin],
|
|
259
|
+
template: '<div></div>',
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const wrapper = mount(
|
|
263
|
+
DummyComponent,
|
|
264
|
+
{
|
|
265
|
+
global: {
|
|
266
|
+
mocks: {
|
|
267
|
+
$store: mockStore,
|
|
268
|
+
$route: {
|
|
269
|
+
query: {
|
|
270
|
+
chart: 'chart_name',
|
|
271
|
+
versionName: '1.0.0'
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
await wrapper.vm.fetchChart();
|
|
279
|
+
|
|
280
|
+
expect(wrapper.vm.existing).toStrictEqual(installedApp);
|
|
281
|
+
expect(wrapper.vm.mode).toStrictEqual('edit');
|
|
282
|
+
});
|
|
217
283
|
});
|
|
218
284
|
|
|
219
285
|
describe('action', () => {
|
|
@@ -366,6 +432,52 @@ describe('chartMixin', () => {
|
|
|
366
432
|
});
|
|
367
433
|
});
|
|
368
434
|
|
|
435
|
+
describe('isChartTargeted', () => {
|
|
436
|
+
const DummyComponent = {
|
|
437
|
+
mixins: [ChartMixin],
|
|
438
|
+
template: '<div></div>',
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const mockStore = {
|
|
442
|
+
dispatch: jest.fn(() => Promise.resolve()),
|
|
443
|
+
getters: { 'i18n/t': (key: string) => key }
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
it('should return truthy when version annotations have both namespace and release-name', () => {
|
|
447
|
+
const wrapper = mount(DummyComponent, {
|
|
448
|
+
data: () => ({
|
|
449
|
+
version: {
|
|
450
|
+
annotations: {
|
|
451
|
+
[CATALOG_ANNOTATIONS.NAMESPACE]: 'custom-ns',
|
|
452
|
+
[CATALOG_ANNOTATIONS.RELEASE_NAME]: 'custom-name',
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}),
|
|
456
|
+
global: { mocks: { $store: mockStore } }
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
expect(wrapper.vm.isChartTargeted).toBeTruthy();
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it('should return falsy when version annotations are missing', () => {
|
|
463
|
+
const wrapper = mount(DummyComponent, {
|
|
464
|
+
data: () => ({ version: { annotations: {} } }),
|
|
465
|
+
global: { mocks: { $store: mockStore } }
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
expect(wrapper.vm.isChartTargeted).toBeFalsy();
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it('should return falsy when version is null', () => {
|
|
472
|
+
const wrapper = mount(DummyComponent, {
|
|
473
|
+
data: () => ({ version: null }),
|
|
474
|
+
global: { mocks: { $store: mockStore } }
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
expect(wrapper.vm.isChartTargeted).toBeFalsy();
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
|
|
369
481
|
describe('mappedVersions', () => {
|
|
370
482
|
it('should return versions sorted by semver (descending)', () => {
|
|
371
483
|
const versions = [
|
package/mixins/brand.js
CHANGED
|
@@ -183,7 +183,8 @@ export default {
|
|
|
183
183
|
},
|
|
184
184
|
setBodyClass() {
|
|
185
185
|
const body = document.getElementsByTagName('body')[0];
|
|
186
|
-
const
|
|
186
|
+
const isStandalone = this.$route?.meta?.standalone;
|
|
187
|
+
const cssClass = isStandalone ? 'dashboard-body' : 'overflow-hidden dashboard-body';
|
|
187
188
|
let bodyClass = `theme-${ this.theme } ${ cssClass }`;
|
|
188
189
|
|
|
189
190
|
if ( this.brand ) {
|
package/mixins/chart.js
CHANGED
|
@@ -13,7 +13,7 @@ import { CAPI, CATALOG } from '@shell/config/types';
|
|
|
13
13
|
import { isPrerelease } from '@shell/utils/version';
|
|
14
14
|
import { compareChartVersions } from '@shell/utils/chart';
|
|
15
15
|
import difference from 'lodash/difference';
|
|
16
|
-
import {
|
|
16
|
+
import { APP_UPGRADE_STATUS, isRancherRepo, getPermittedOSs } from '@shell/store/catalog';
|
|
17
17
|
import { clone } from '@shell/utils/object';
|
|
18
18
|
import { merge } from 'lodash';
|
|
19
19
|
|
|
@@ -28,6 +28,17 @@ export default {
|
|
|
28
28
|
ignoreWarning: false,
|
|
29
29
|
|
|
30
30
|
chart: null,
|
|
31
|
+
|
|
32
|
+
// Whether installing a new instance is allowed.
|
|
33
|
+
// This is false when the chart is targeted (has fixed namespace/name annotations)
|
|
34
|
+
// or when the URL query specifies a specific app to edit.
|
|
35
|
+
canInstallNew: true,
|
|
36
|
+
|
|
37
|
+
// Installed instances of this chart that can be selected for edit/upgrade
|
|
38
|
+
// on the chart detail page. When instances exist, `existing` is set to the
|
|
39
|
+
// first one by default, but the user can select a different instance or
|
|
40
|
+
// choose to install a new one.
|
|
41
|
+
installedInstances: [],
|
|
31
42
|
};
|
|
32
43
|
},
|
|
33
44
|
|
|
@@ -73,7 +84,8 @@ export default {
|
|
|
73
84
|
keywords: version.keywords
|
|
74
85
|
};
|
|
75
86
|
|
|
76
|
-
const
|
|
87
|
+
const isRancher = isRancherRepo(this.repo, this.chart);
|
|
88
|
+
const permittedSystems = getPermittedOSs(version?.annotations, isRancher);
|
|
77
89
|
|
|
78
90
|
if (permittedSystems.length > 0 && difference(OSs, permittedSystems).length > 0) {
|
|
79
91
|
nue.disabled = true;
|
|
@@ -260,7 +272,7 @@ export default {
|
|
|
260
272
|
},
|
|
261
273
|
|
|
262
274
|
isChartTargeted() {
|
|
263
|
-
return this.
|
|
275
|
+
return this.version?.annotations?.[CATALOG_ANNOTATIONS.NAMESPACE] && this.version?.annotations?.[CATALOG_ANNOTATIONS.RELEASE_NAME];
|
|
264
276
|
},
|
|
265
277
|
|
|
266
278
|
hasQuestions() {
|
|
@@ -376,6 +388,9 @@ export default {
|
|
|
376
388
|
// If found, set the form to edit mode. If not, set the
|
|
377
389
|
// form to create mode.
|
|
378
390
|
|
|
391
|
+
// This is a hard blocker - installing a new instance is NOT allowed.
|
|
392
|
+
this.canInstallNew = false;
|
|
393
|
+
|
|
379
394
|
try {
|
|
380
395
|
this.existing = await this.$store.dispatch('cluster/find', {
|
|
381
396
|
type: CATALOG.APP,
|
|
@@ -396,10 +411,12 @@ export default {
|
|
|
396
411
|
if ( targetNamespace && targetName ) {
|
|
397
412
|
// If the app name and namespace values are not provided in the
|
|
398
413
|
// query, fall back on target values defined in the Helm chart itself.
|
|
399
|
-
|
|
400
414
|
// Ask to install a special chart with fixed namespace/name
|
|
401
415
|
// or edit it if there's an existing install.
|
|
402
416
|
|
|
417
|
+
// This is a hard blocker - installing a new instance is NOT allowed.
|
|
418
|
+
this.canInstallNew = false;
|
|
419
|
+
|
|
403
420
|
try {
|
|
404
421
|
this.existing = await this.$store.dispatch('cluster/find', {
|
|
405
422
|
type: CATALOG.APP,
|
|
@@ -407,22 +424,40 @@ export default {
|
|
|
407
424
|
});
|
|
408
425
|
this.mode = _EDIT;
|
|
409
426
|
} catch (e) {
|
|
410
|
-
|
|
411
|
-
|
|
427
|
+
// Version targets a different namespace than where the app is installed.
|
|
428
|
+
// Fall back to matching installed apps (e.g. chart detail page).
|
|
429
|
+
const matching = this.chart?.matchingInstalledApps || [];
|
|
430
|
+
|
|
431
|
+
this.existing = matching[0] || null;
|
|
432
|
+
this.mode = this.existing ? _EDIT : _CREATE;
|
|
412
433
|
}
|
|
413
|
-
} else
|
|
414
|
-
|
|
434
|
+
} else {
|
|
435
|
+
// Regular chart (not targeted) - check if there are installed instances.
|
|
436
|
+
// Installing new instances IS allowed (canInstallNew remains true).
|
|
437
|
+
const isChartDetailPage = this.$route.name === 'c-cluster-apps-charts-chart';
|
|
415
438
|
|
|
416
|
-
if (
|
|
417
|
-
this.
|
|
418
|
-
|
|
439
|
+
if (isChartDetailPage) {
|
|
440
|
+
const matching = this.chart?.matchingInstalledApps || [];
|
|
441
|
+
|
|
442
|
+
// Always refresh the available instances so stale values are removed.
|
|
443
|
+
this.installedInstances = [];
|
|
444
|
+
|
|
445
|
+
if (matching.length >= 1) {
|
|
446
|
+
// Populate the instance selector and preserve the current selection
|
|
447
|
+
// when it is still one of the matching installed apps.
|
|
448
|
+
this.installedInstances = matching;
|
|
449
|
+
const hasExistingMatch = this.existing?.id && matching.some((instance) => instance.id === this.existing.id);
|
|
450
|
+
|
|
451
|
+
if (!hasExistingMatch) {
|
|
452
|
+
this.existing = matching[0];
|
|
453
|
+
}
|
|
454
|
+
} else {
|
|
455
|
+
this.existing = null;
|
|
456
|
+
}
|
|
419
457
|
} else {
|
|
458
|
+
// Regular create
|
|
420
459
|
this.mode = _CREATE;
|
|
421
460
|
}
|
|
422
|
-
} else {
|
|
423
|
-
// Regular create
|
|
424
|
-
|
|
425
|
-
this.mode = _CREATE;
|
|
426
461
|
}
|
|
427
462
|
}
|
|
428
463
|
}, // End of fetchChart
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NAMESPACE_FILTER_NAMESPACED_YES, NAMESPACE_FILTER_NAMESPACED_NO, NAMESPACE_FILTER_ALL } from '@shell/utils/namespace-filter';
|
|
2
|
-
import { NAMESPACE } from '@shell/config/types';
|
|
2
|
+
import { MANAGEMENT, NAMESPACE } from '@shell/config/types';
|
|
3
3
|
import { ALL_NAMESPACES } from '@shell/store/prefs';
|
|
4
4
|
import { mapGetters } from 'vuex';
|
|
5
5
|
import { ResourceListComponentName } from '../components/ResourceList/resource-list.config';
|
|
@@ -248,7 +248,7 @@ export default {
|
|
|
248
248
|
namespaceFilters: {
|
|
249
249
|
immediate: true,
|
|
250
250
|
async handler(neu, old) {
|
|
251
|
-
if (!this.canPaginate || !this.isNamespaced) {
|
|
251
|
+
if (!this.canPaginate || !this.isNamespaced || !this.currentProduct?.showNamespaceFilter) {
|
|
252
252
|
return;
|
|
253
253
|
}
|
|
254
254
|
|
|
@@ -283,7 +283,7 @@ export default {
|
|
|
283
283
|
allNamespaces: this.$store.getters[`${ this.currentProduct?.inStore }/all`](NAMESPACE),
|
|
284
284
|
selection: neu,
|
|
285
285
|
isAllNamespaces: this.isAllNamespaces,
|
|
286
|
-
isLocalCluster: this.$store.getters['currentCluster']
|
|
286
|
+
isLocalCluster: this.$store.getters['currentCluster']?.isLocal,
|
|
287
287
|
showReservedRancherNamespaces: this.showDynamicRancherNamespaces,
|
|
288
288
|
productHidesSystemNamespaces: this.productHidesSystemNamespaces,
|
|
289
289
|
});
|
|
@@ -354,15 +354,51 @@ export default {
|
|
|
354
354
|
|
|
355
355
|
async beforeUnmount() {
|
|
356
356
|
if (this.havePaginated) {
|
|
357
|
+
const store = this.overrideInStore || this.inStore;
|
|
357
358
|
// of type @STEVE_WATCH_PARAMS
|
|
358
359
|
const watchArgs = {
|
|
359
360
|
type: this.resource,
|
|
360
361
|
mode: STEVE_WATCH_MODE.RESOURCE_CHANGES,
|
|
361
362
|
};
|
|
362
363
|
|
|
363
|
-
|
|
364
|
-
|
|
364
|
+
// Ensure that a resource remains in the store... and it's the same reference as before
|
|
365
|
+
// We'll do this for the mgmt cluster if there's a currentCluster set, otherwise we become unstuck
|
|
366
|
+
// (on nav we ensure currentCluster exists, but beforeUnmount fires afterwards, removing the cluster from pages that are using it)
|
|
367
|
+
const retainResource = this.resource === MANAGEMENT.CLUSTER && this.currentCluster ? this.currentCluster : null;
|
|
368
|
+
|
|
369
|
+
// Forget (unwatch, clear from store) the list's resource
|
|
370
|
+
await this.$store.dispatch(`${ store }/forgetType`, {
|
|
371
|
+
type: this.resource,
|
|
372
|
+
compareWatches: (watchParams) => {
|
|
373
|
+
return watchParams.type === watchArgs.type && watchParams.mode === watchArgs.mode;
|
|
374
|
+
},
|
|
375
|
+
unwatch: true,
|
|
376
|
+
forget: !retainResource
|
|
365
377
|
});
|
|
378
|
+
|
|
379
|
+
if (!!retainResource) {
|
|
380
|
+
// The end state should be just like we've dispatched `${store}/find`
|
|
381
|
+
|
|
382
|
+
try {
|
|
383
|
+
// Clone to ensure we can load each property of A into B without worrying about them being the same reference
|
|
384
|
+
const clone = await this.$store.dispatch(`${ store }/clone`, { resource: retainResource });
|
|
385
|
+
|
|
386
|
+
// Load the resource. It's already in the store but this makes sure any legacy state (like havePage) is reset
|
|
387
|
+
await this.$store.dispatch(`${ store }/load`, {
|
|
388
|
+
data: clone,
|
|
389
|
+
existing: retainResource,
|
|
390
|
+
invalidatePageCache: true,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// Watch the resource.
|
|
394
|
+
await this.$store.dispatch(`${ store }/watch`, {
|
|
395
|
+
type: this.resource,
|
|
396
|
+
id: retainResource.id,
|
|
397
|
+
});
|
|
398
|
+
} catch (error) {
|
|
399
|
+
console.error('Error occurred whilst trying to retain the management cluster in cache and correctly watched', error); // eslint-disable-line no-console
|
|
400
|
+
}
|
|
401
|
+
}
|
|
366
402
|
}
|
|
367
403
|
}
|
|
368
404
|
};
|
|
@@ -87,6 +87,19 @@ const certManagerOfficialMatchingChart2 = {
|
|
|
87
87
|
}]
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
+
// Simulates the chart data fetched from the repository where the home URL has changed in newer versions
|
|
91
|
+
// AND the older installed version has been dropped/replaced from the repository index.
|
|
92
|
+
const certManagerOfficialMatchingChartNewHomeOnly = {
|
|
93
|
+
name: chartName,
|
|
94
|
+
repoName: certManagerOfficial.repoName,
|
|
95
|
+
versions: [{
|
|
96
|
+
version: latestVersion,
|
|
97
|
+
home: certManagerOfficial.home,
|
|
98
|
+
repoName: certManagerOfficial.repoName,
|
|
99
|
+
annotations: {},
|
|
100
|
+
}]
|
|
101
|
+
};
|
|
102
|
+
|
|
90
103
|
const installedCertManagerAppCoFromRancherUI = {
|
|
91
104
|
metadata: {
|
|
92
105
|
annotations: { [CATALOG_ANNOTATIONS.SOURCE_REPO_NAME]: appCo.repoName },
|
|
@@ -130,7 +143,8 @@ describe('class CatalogApp', () => {
|
|
|
130
143
|
[installedCertManagerOfficialFromRancherUI, [], APP_UPGRADE_STATUS.NO_UPGRADE],
|
|
131
144
|
[installedCertManagerOfficialFromRancherUI, [certManagerOfficialMatchingChart1], APP_UPGRADE_STATUS.SINGLE_UPGRADE],
|
|
132
145
|
[installedCertManagerOfficialFromRancherUI, [certManagerOfficialMatchingChart1, appCoMatchingChart1], APP_UPGRADE_STATUS.SINGLE_UPGRADE],
|
|
133
|
-
[installedCertManagerOfficialFromRancherUI, [certManagerOfficialMatchingChart1, certManagerOfficialMatchingChart2], APP_UPGRADE_STATUS.MULTIPLE_UPGRADES]
|
|
146
|
+
[installedCertManagerOfficialFromRancherUI, [certManagerOfficialMatchingChart1, certManagerOfficialMatchingChart2], APP_UPGRADE_STATUS.MULTIPLE_UPGRADES],
|
|
147
|
+
[installedCertManagerOfficialFromRancherUI, [certManagerOfficialMatchingChartNewHomeOnly], APP_UPGRADE_STATUS.SINGLE_UPGRADE]
|
|
134
148
|
];
|
|
135
149
|
|
|
136
150
|
it.each(testCases)('should return the correct upgrade status', (installedChart: Object, matchingCharts: any, expected: any) => {
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import ClusterRepo from '../catalog.cattle.io.clusterrepo';
|
|
2
|
+
|
|
3
|
+
describe('clusterRepo', () => {
|
|
4
|
+
let model: any;
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
model = new ClusterRepo({
|
|
8
|
+
metadata: { name: 'test-repo' },
|
|
9
|
+
spec: { url: 'https://test-url.com' }
|
|
10
|
+
}, {
|
|
11
|
+
getters: {},
|
|
12
|
+
dispatch: jest.fn(),
|
|
13
|
+
rootGetters: {}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
jest.spyOn(model, '_key', 'get').mockReturnValue('test-key');
|
|
17
|
+
jest.spyOn(model, 'save').mockImplementation().mockResolvedValue(true);
|
|
18
|
+
jest.spyOn(model, 'waitForState').mockImplementation().mockResolvedValue(true);
|
|
19
|
+
jest.spyOn(model, '$dispatch', 'get').mockReturnValue(jest.fn());
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('refresh', () => {
|
|
23
|
+
it('updates forceUpdate, saves, waits for active state, and dispatches load by default', async() => {
|
|
24
|
+
// Mock Date to ensure deterministic forceUpdate value
|
|
25
|
+
const mockDate = new Date('2023-01-01T12:00:00.000Z');
|
|
26
|
+
const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate as any);
|
|
27
|
+
|
|
28
|
+
await model.refresh();
|
|
29
|
+
|
|
30
|
+
expect(model.spec.forceUpdate).toBe('2023-01-01T12:00:00Z');
|
|
31
|
+
expect(model.save).toHaveBeenCalledWith();
|
|
32
|
+
expect(model.waitForState).toHaveBeenCalledWith('active', 10000, 1000);
|
|
33
|
+
expect(model.$dispatch).toHaveBeenCalledWith('catalog/load', { force: true, repoKeys: [model._key] }, { root: true });
|
|
34
|
+
|
|
35
|
+
spy.mockRestore();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('updates forceUpdate, saves, waits for active state, but DOES NOT dispatch load if dispatchLoad is false', async() => {
|
|
39
|
+
await model.refresh(false);
|
|
40
|
+
|
|
41
|
+
expect(model.save).toHaveBeenCalledWith();
|
|
42
|
+
expect(model.waitForState).toHaveBeenCalledWith('active', 10000, 1000);
|
|
43
|
+
expect(model.$dispatch).not.toHaveBeenCalled();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('dispatches error to growl if save or waitForState fails', async() => {
|
|
47
|
+
const error = new Error('waitForState timeout');
|
|
48
|
+
|
|
49
|
+
model.waitForState.mockRejectedValue(error);
|
|
50
|
+
jest.spyOn(model, 't', 'get').mockReturnValue(jest.fn().mockReturnValue('Error refreshing repository'));
|
|
51
|
+
jest.spyOn(model, 'nameDisplay', 'get').mockReturnValue('Test Repo');
|
|
52
|
+
|
|
53
|
+
await model.refresh();
|
|
54
|
+
|
|
55
|
+
expect(model.$dispatch).toHaveBeenCalledWith('growl/fromError', {
|
|
56
|
+
title: 'Error refreshing repository',
|
|
57
|
+
err: error
|
|
58
|
+
}, { root: true });
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('refreshBulk', () => {
|
|
63
|
+
it('calls refresh(false) on all items and then dispatches a single catalog/load with all repoKeys', async() => {
|
|
64
|
+
const mockItem1 = {
|
|
65
|
+
_key: 'repo-1',
|
|
66
|
+
refresh: jest.fn().mockResolvedValue(true)
|
|
67
|
+
};
|
|
68
|
+
const mockItem2 = {
|
|
69
|
+
_key: 'repo-2',
|
|
70
|
+
refresh: jest.fn().mockResolvedValue(true)
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
await model.refreshBulk([mockItem1, mockItem2]);
|
|
74
|
+
|
|
75
|
+
expect(mockItem1.refresh).toHaveBeenCalledWith(false);
|
|
76
|
+
expect(mockItem2.refresh).toHaveBeenCalledWith(false);
|
|
77
|
+
|
|
78
|
+
expect(model.$dispatch).toHaveBeenCalledWith('catalog/load', {
|
|
79
|
+
force: true,
|
|
80
|
+
repoKeys: ['repo-1', 'repo-2']
|
|
81
|
+
}, { root: true });
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -17,7 +17,7 @@ type MockChartContext = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
interface CardContent {
|
|
20
|
-
subHeaderItems: { label: string, labelTooltip?: string}[];
|
|
20
|
+
subHeaderItems: { label: string, labelTooltip?: string, icon?: string, iconTooltip?: string }[];
|
|
21
21
|
footerItems: { labels: string[]; icon?: string }[];
|
|
22
22
|
statuses: { tooltip: { key?: string; text?: string }; color: string }[];
|
|
23
23
|
}
|
|
@@ -166,7 +166,7 @@ describe('class Chart', () => {
|
|
|
166
166
|
expect(chart.isInstalled).toBe(false);
|
|
167
167
|
});
|
|
168
168
|
|
|
169
|
-
it('is
|
|
169
|
+
it('is true when multiple apps match', () => {
|
|
170
170
|
const app = makeInstalledApp();
|
|
171
171
|
|
|
172
172
|
app.spec.chart.metadata.version = '1.2.3';
|
|
@@ -174,7 +174,7 @@ describe('class Chart', () => {
|
|
|
174
174
|
|
|
175
175
|
const chart = new Chart(base, ctx);
|
|
176
176
|
|
|
177
|
-
expect(chart.isInstalled).toBe(
|
|
177
|
+
expect(chart.isInstalled).toBe(true);
|
|
178
178
|
});
|
|
179
179
|
});
|
|
180
180
|
|
|
@@ -206,6 +206,58 @@ describe('class Chart', () => {
|
|
|
206
206
|
|
|
207
207
|
expect(chart.upgradeable).toBe(false);
|
|
208
208
|
});
|
|
209
|
+
|
|
210
|
+
it('is true when at least one of multiple installed apps is upgradeable', () => {
|
|
211
|
+
const app1 = makeInstalledApp(APP_UPGRADE_STATUS.NO_UPGRADE);
|
|
212
|
+
const app2 = makeInstalledApp(APP_UPGRADE_STATUS.SINGLE_UPGRADE);
|
|
213
|
+
|
|
214
|
+
ctx.rootGetters['cluster/all'] = () => [app1, app2];
|
|
215
|
+
|
|
216
|
+
const chart = new Chart(base, ctx);
|
|
217
|
+
|
|
218
|
+
expect(chart.upgradeable).toBe(true);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('is false when none of multiple installed apps are upgradeable', () => {
|
|
222
|
+
const app1 = makeInstalledApp(APP_UPGRADE_STATUS.NO_UPGRADE);
|
|
223
|
+
const app2 = makeInstalledApp(APP_UPGRADE_STATUS.NO_UPGRADE);
|
|
224
|
+
|
|
225
|
+
ctx.rootGetters['cluster/all'] = () => [app1, app2];
|
|
226
|
+
|
|
227
|
+
const chart = new Chart(base, ctx);
|
|
228
|
+
|
|
229
|
+
expect(chart.upgradeable).toBe(false);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
describe('installedCount', () => {
|
|
234
|
+
it('returns 0 when no apps are installed', () => {
|
|
235
|
+
const chart = new Chart(base, ctx);
|
|
236
|
+
|
|
237
|
+
expect(chart.installedCount).toBe(0);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('returns 1 when one app is installed', () => {
|
|
241
|
+
const installedApp = makeInstalledApp();
|
|
242
|
+
|
|
243
|
+
ctx.rootGetters['cluster/all'] = () => [installedApp];
|
|
244
|
+
|
|
245
|
+
const chart = new Chart(base, ctx);
|
|
246
|
+
|
|
247
|
+
expect(chart.installedCount).toBe(1);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('returns correct count when multiple apps are installed', () => {
|
|
251
|
+
const app1 = makeInstalledApp();
|
|
252
|
+
const app2 = makeInstalledApp();
|
|
253
|
+
const app3 = makeInstalledApp();
|
|
254
|
+
|
|
255
|
+
ctx.rootGetters['cluster/all'] = () => [app1, app2, app3];
|
|
256
|
+
|
|
257
|
+
const chart = new Chart(base, ctx);
|
|
258
|
+
|
|
259
|
+
expect(chart.installedCount).toBe(3);
|
|
260
|
+
});
|
|
209
261
|
});
|
|
210
262
|
|
|
211
263
|
describe('cardContent', () => {
|
|
@@ -274,6 +326,25 @@ describe('class Chart', () => {
|
|
|
274
326
|
expect(installedStatus?.tooltip?.text).toContain(installedApp.spec.chart.metadata.version);
|
|
275
327
|
});
|
|
276
328
|
|
|
329
|
+
it('does not include version in installed tooltip when multiple instances exist', () => {
|
|
330
|
+
const app1 = makeInstalledApp();
|
|
331
|
+
const app2 = makeInstalledApp();
|
|
332
|
+
|
|
333
|
+
app2.spec.chart.metadata.version = '1.2.0';
|
|
334
|
+
ctx.rootGetters['cluster/all'] = () => [app1, app2];
|
|
335
|
+
|
|
336
|
+
const chart = new Chart(base, ctx);
|
|
337
|
+
|
|
338
|
+
const result = chart.cardContent as CardContent;
|
|
339
|
+
|
|
340
|
+
const installedStatus = result.statuses.find((s) => s.tooltip?.text?.startsWith('generic.installedMultiple'));
|
|
341
|
+
|
|
342
|
+
expect(installedStatus).toBeDefined();
|
|
343
|
+
expect(installedStatus?.color).toBe('success');
|
|
344
|
+
// Should not contain version number when multiple instances
|
|
345
|
+
expect(installedStatus?.tooltip?.text).toBe('generic.installedMultiple');
|
|
346
|
+
});
|
|
347
|
+
|
|
277
348
|
it('includes upgradeable status when upgrade is available', () => {
|
|
278
349
|
const installedApp = makeInstalledApp(APP_UPGRADE_STATUS.SINGLE_UPGRADE);
|
|
279
350
|
|
|
@@ -332,10 +403,32 @@ describe('class Chart', () => {
|
|
|
332
403
|
});
|
|
333
404
|
|
|
334
405
|
const result = chart.cardContent as CardContent;
|
|
335
|
-
const lastUpdatedItem = result.subHeaderItems[1];
|
|
336
406
|
|
|
337
|
-
expect(
|
|
338
|
-
expect(
|
|
407
|
+
expect(result.subHeaderItems).toHaveLength(1);
|
|
408
|
+
expect(result.subHeaderItems[0].icon).toBe('icon-version-alt');
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it('handles falsy time for last updated date', () => {
|
|
412
|
+
const chartWithFalsyTime = {
|
|
413
|
+
...base,
|
|
414
|
+
versions: [{
|
|
415
|
+
...base.versions[0],
|
|
416
|
+
created: '',
|
|
417
|
+
}]
|
|
418
|
+
};
|
|
419
|
+
const chart = new Chart(chartWithFalsyTime, {
|
|
420
|
+
rootGetters: {
|
|
421
|
+
'cluster/all': () => [],
|
|
422
|
+
'i18n/t': (key: string) => key,
|
|
423
|
+
currentCluster: { workerOSs: [] },
|
|
424
|
+
'prefs/get': () => false,
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
const result = chart.cardContent as CardContent;
|
|
429
|
+
|
|
430
|
+
expect(result.subHeaderItems).toHaveLength(1);
|
|
431
|
+
expect(result.subHeaderItems[0].icon).toBe('icon-version-alt');
|
|
339
432
|
});
|
|
340
433
|
});
|
|
341
434
|
});
|