@rancher/shell 3.0.7 → 3.0.8-rc.10
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/assets/brand/suse/banner.svg +1 -0
- package/assets/brand/suse/dark/banner.svg +1 -0
- package/assets/brand/suse/dark/login-landscape.svg +1 -0
- package/assets/brand/suse/dark/rancher-logo.svg +1 -1
- package/assets/brand/suse/favicon.png +0 -0
- package/assets/brand/suse/login-landscape.svg +1 -0
- package/assets/brand/suse/metadata.json +11 -1
- package/assets/brand/suse/rancher-logo.svg +1 -1
- package/assets/fonts/suse/suse-v2-latin-300.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-300.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-600.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-600.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-700.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-700.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-800.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-800.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-regular.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-regular.woff2 +0 -0
- package/assets/images/content/README.md +5 -0
- package/assets/images/content/cloud-native.svg +84 -0
- package/assets/images/content/dark/cloud-native.svg +21 -0
- package/assets/images/content/dark/shield.svg +59 -0
- package/assets/images/content/dark/suse.svg +10 -0
- package/assets/images/content/shield.svg +59 -0
- package/assets/images/content/suse.svg +10 -0
- package/assets/images/vendor/githubapp.svg +13 -0
- package/assets/styles/base/_typography.scss +2 -1
- package/assets/styles/fonts/_fontstack.scss +53 -1
- package/assets/styles/global/_cards.scss +0 -3
- package/assets/styles/global/_layout.scss +21 -35
- package/assets/styles/themes/_dark.scss +1 -1
- package/assets/styles/themes/_light.scss +1 -1
- package/assets/styles/themes/_modern.scss +16 -8
- package/assets/styles/themes/_suse.scss +116 -24
- package/assets/translations/en-us.yaml +185 -21
- package/assets/translations/zh-hans.yaml +0 -4
- package/components/AutoscalerCard.vue +113 -0
- package/components/AutoscalerTab.vue +94 -0
- package/components/BackLink.vue +8 -0
- package/components/BannerGraphic.vue +36 -21
- package/components/BrandImage.vue +17 -6
- package/components/ClusterIconMenu.vue +1 -1
- package/components/ClusterProviderIcon.vue +1 -1
- package/components/Cron/CronExpressionEditor.vue +1 -1
- package/components/Cron/CronExpressionEditorModal.vue +1 -1
- package/components/Drawer/Chrome.vue +2 -6
- package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +4 -9
- package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +3 -8
- package/components/Drawer/ResourceDetailDrawer/composables.ts +3 -4
- package/components/Drawer/ResourceDetailDrawer/index.vue +4 -9
- package/components/Drawer/ResourceDetailDrawer/types.ts +17 -0
- package/components/Drawer/types.ts +3 -0
- package/components/DynamicContent/DynamicContentBanner.vue +102 -0
- package/components/DynamicContent/DynamicContentCloseButton.vue +42 -0
- package/components/DynamicContent/DynamicContentIcon.vue +132 -0
- package/components/DynamicContent/DynamicContentPanel.vue +112 -0
- package/components/DynamicContent/content.ts +78 -0
- package/components/EmberPage.vue +1 -1
- package/components/IconOrSvg.vue +2 -2
- package/components/Inactivity.vue +222 -106
- package/components/InstallHelmCharts.vue +2 -2
- package/components/PaginatedResourceTable.vue +2 -6
- package/components/PopoverCard.vue +192 -0
- package/components/Questions/__tests__/index.test.ts +159 -0
- package/components/Resource/Detail/CopyToClipboard.vue +4 -1
- package/components/Resource/Detail/FetchLoader/composables.ts +18 -4
- package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +1 -1
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +4 -0
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +1 -1
- package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
- package/components/Resource/Detail/Metadata/composables.ts +9 -9
- package/components/Resource/Detail/Metadata/index.vue +3 -3
- package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -19
- package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +0 -29
- package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +132 -150
- package/components/Resource/Detail/ResourcePopover/index.vue +54 -159
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
- package/components/Resource/Detail/TitleBar/composables.ts +2 -1
- package/components/Resource/Detail/TitleBar/index.vue +10 -6
- package/components/Resource/Detail/composables.ts +12 -0
- package/components/ResourceDetail/Masthead/latest.vue +29 -0
- package/components/ResourceDetail/index.vue +5 -2
- package/components/ResourceList/Masthead.vue +1 -1
- package/components/SortableTable/index.vue +18 -2
- package/components/Tabbed/__tests__/index.test.ts +86 -0
- package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
- package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
- package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
- package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
- package/components/__tests__/AutoscalerCard.test.ts +154 -0
- package/components/__tests__/AutoscalerTab.test.ts +125 -0
- package/components/__tests__/PopoverCard.test.ts +204 -0
- package/components/auth/SelectPrincipal.vue +24 -6
- package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
- package/components/fleet/FleetConfigMapSelector.vue +117 -0
- package/components/fleet/FleetSecretSelector.vue +127 -0
- package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
- package/components/form/FileImageSelector.vue +13 -4
- package/components/form/FileSelector.vue +11 -2
- package/components/form/ResourceLabeledSelect.vue +1 -0
- package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
- package/components/formatter/Autoscaler.vue +97 -0
- package/components/formatter/InternalExternalIP.vue +198 -24
- package/components/formatter/__tests__/Autoscaler.test.ts +156 -0
- package/components/formatter/__tests__/InternalExternalIP.test.ts +133 -0
- package/components/google/util/__tests__/formatter.test.ts +47 -0
- package/components/google/util/formatter.ts +5 -2
- package/components/nav/Group.vue +12 -3
- package/components/nav/Header.vue +37 -16
- package/components/nav/NamespaceFilter.vue +13 -1
- package/components/nav/NotificationCenter/index.vue +2 -1
- package/components/nav/TopLevelMenu.helper.ts +16 -6
- package/components/nav/TopLevelMenu.vue +4 -2
- package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
- package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
- package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
- package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
- package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
- package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
- package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
- package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
- package/components/nav/WindowManager/constants.ts +23 -0
- package/components/nav/WindowManager/index.vue +61 -575
- package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
- package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
- package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
- package/components/templates/default.vue +4 -40
- package/components/templates/home.vue +31 -5
- package/components/templates/plain.vue +30 -4
- package/components/templates/standalone.vue +1 -1
- package/composables/useI18n.ts +10 -1
- package/composables/useInterval.ts +15 -0
- package/config/__test__/uiplugins.test.ts +309 -0
- package/config/labels-annotations.js +9 -1
- package/config/product/auth.js +1 -0
- package/config/product/explorer.js +3 -1
- package/config/product/manager.js +20 -9
- package/config/query-params.js +1 -0
- package/config/router/routes.js +10 -2
- package/config/settings.ts +10 -2
- package/config/store.js +4 -2
- package/config/table-headers.js +8 -0
- package/config/types.js +11 -0
- package/config/uiplugins.js +46 -2
- package/config/version.js +1 -1
- package/core/__test__/extension-manager-impl.test.js +236 -0
- package/core/extension-manager-impl.js +23 -6
- package/core/plugin-helpers.ts +2 -0
- package/core/types-provisioning.ts +4 -1
- package/detail/pod.vue +1 -0
- package/detail/provisioning.cattle.io.cluster.vue +13 -1
- package/dialog/AddonConfigConfirmationDialog.vue +45 -1
- package/dialog/DeveloperLoadExtensionDialog.vue +12 -3
- package/dialog/RollbackWorkloadDialog.vue +2 -5
- package/directives/ui-context.ts +103 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
- package/edit/auth/AuthProviderWarningBanners.vue +14 -1
- package/edit/auth/__tests__/oidc.test.ts +26 -0
- package/edit/auth/github-app-steps.vue +97 -0
- package/edit/auth/github-steps.vue +75 -0
- package/edit/auth/github.vue +99 -65
- package/edit/auth/oidc.vue +5 -1
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
- package/edit/cloudcredential.vue +1 -1
- package/edit/configmap.vue +1 -0
- package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
- package/edit/fleet.cattle.io.gitrepo.vue +0 -10
- package/edit/fleet.cattle.io.helmop.vue +51 -2
- package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
- package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
- package/edit/logging-flow/index.vue +1 -0
- package/edit/logging.banzaicloud.io.output/index.vue +1 -0
- package/edit/management.cattle.io.fleetworkspace.vue +1 -1
- package/edit/management.cattle.io.project.vue +1 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
- package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
- package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
- package/edit/monitoring.coreos.com.route.vue +1 -1
- package/edit/namespace.vue +1 -0
- package/edit/networking.istio.io.destinationrule/index.vue +1 -0
- package/edit/networking.k8s.io.ingress/index.vue +1 -0
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
- package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
- package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
- package/edit/node.vue +1 -0
- package/edit/persistentvolume/index.vue +27 -22
- package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
- package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
- package/edit/persistentvolume/plugins/azureFile.vue +15 -14
- package/edit/persistentvolume/plugins/cephfs.vue +15 -14
- package/edit/persistentvolume/plugins/cinder.vue +15 -14
- package/edit/persistentvolume/plugins/csi.vue +18 -16
- package/edit/persistentvolume/plugins/fc.vue +13 -14
- package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
- package/edit/persistentvolume/plugins/flocker.vue +1 -3
- package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
- package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
- package/edit/persistentvolume/plugins/hostPath.vue +40 -39
- package/edit/persistentvolume/plugins/iscsi.vue +13 -14
- package/edit/persistentvolume/plugins/local.vue +1 -3
- package/edit/persistentvolume/plugins/longhorn.vue +23 -22
- package/edit/persistentvolume/plugins/nfs.vue +15 -14
- package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
- package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
- package/edit/persistentvolume/plugins/quobyte.vue +15 -14
- package/edit/persistentvolume/plugins/rbd.vue +15 -14
- package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
- package/edit/persistentvolume/plugins/storageos.vue +15 -14
- package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +32 -5
- package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +35 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +155 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
- package/edit/provisioning.cattle.io.cluster/index.vue +25 -15
- package/edit/provisioning.cattle.io.cluster/rke2.vue +98 -17
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +107 -5
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +92 -4
- package/edit/secret/index.vue +1 -1
- package/edit/service.vue +9 -4
- package/edit/serviceaccount.vue +1 -0
- package/edit/storage.k8s.io.storageclass/index.vue +1 -0
- package/edit/workload/index.vue +2 -1
- package/edit/workload/mixins/workload.js +1 -1
- package/initialize/App.vue +4 -4
- package/initialize/install-directives.js +2 -0
- package/initialize/install-plugins.js +19 -2
- package/list/projectsecret.vue +1 -1
- package/list/provisioning.cattle.io.cluster.vue +15 -2
- package/machine-config/amazonec2.vue +42 -135
- package/machine-config/azure.vue +1 -1
- package/machine-config/components/EC2Networking.vue +490 -0
- package/machine-config/components/__tests__/EC2Networking.test.ts +148 -0
- package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +294 -0
- package/machine-config/digitalocean.vue +11 -0
- package/machine-config/google.vue +1 -1
- package/mixins/__tests__/brand.spec.ts +2 -2
- package/mixins/__tests__/chart.test.ts +21 -0
- package/mixins/brand.js +1 -7
- package/mixins/chart.js +8 -2
- package/mixins/create-edit-view/index.js +5 -0
- package/models/__tests__/chart.test.ts +49 -12
- package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +112 -5
- package/models/catalog.cattle.io.app.js +1 -1
- package/models/chart.js +28 -14
- package/models/cluster/node.js +13 -6
- package/models/cluster.x-k8s.io.machine.js +10 -20
- package/models/cluster.x-k8s.io.machinedeployment.js +5 -1
- package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
- package/models/management.cattle.io.authconfig.js +1 -0
- package/models/management.cattle.io.cluster.js +21 -3
- package/models/management.cattle.io.kontainerdriver.js +1 -0
- package/models/provisioning.cattle.io.cluster.js +249 -33
- package/package.json +6 -5
- package/pages/auth/login.vue +43 -4
- package/pages/auth/verify.vue +1 -1
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
- package/pages/c/_cluster/apps/charts/chart.vue +35 -17
- package/pages/c/_cluster/apps/charts/index.vue +11 -13
- package/pages/c/_cluster/apps/charts/install.vue +1 -1
- package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
- package/pages/c/_cluster/explorer/index.vue +8 -6
- package/pages/c/_cluster/explorer/tools/index.vue +3 -3
- package/pages/c/_cluster/manager/hostedprovider/index.vue +220 -0
- package/pages/c/_cluster/settings/brand.vue +1 -1
- package/pages/c/_cluster/settings/performance.vue +12 -25
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
- package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
- package/pages/c/_cluster/uiplugins/index.vue +126 -184
- package/pages/home.vue +327 -16
- package/pkg/dynamic-importer.lib.js +4 -0
- package/plugins/axios.js +2 -1
- package/plugins/dashboard-client-init.js +3 -0
- package/plugins/dashboard-store/actions.js +1 -1
- package/plugins/dashboard-store/getters.js +18 -1
- package/plugins/dashboard-store/resource-class.js +21 -6
- package/plugins/dynamic-content.js +13 -0
- package/plugins/i18n.js +8 -0
- package/plugins/steve/__tests__/steve-pagination-utils.test.ts +333 -0
- package/plugins/steve/steve-pagination-utils.ts +41 -22
- package/plugins/steve/subscribe.js +17 -9
- package/plugins/subscribe-events.ts +4 -2
- package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -34
- package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
- package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
- package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
- package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
- package/rancher-components/Pill/types.ts +0 -1
- package/rancher-components/RcDropdown/RcDropdownItem.vue +1 -0
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
- package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
- package/rancher-components/RcIcon/RcIcon.vue +46 -0
- package/rancher-components/RcIcon/index.ts +1 -0
- package/rancher-components/RcIcon/types.ts +160 -0
- package/rancher-components/utils/status.test.ts +67 -0
- package/rancher-components/utils/status.ts +77 -0
- package/scripts/extension/publish +1 -1
- package/scripts/typegen.sh +1 -0
- package/store/action-menu.js +8 -0
- package/store/auth.js +11 -6
- package/store/aws.js +8 -6
- package/store/catalog.js +6 -0
- package/store/features.js +2 -0
- package/store/index.js +45 -20
- package/store/notifications.ts +51 -4
- package/store/plugins.js +7 -3
- package/store/prefs.js +12 -6
- package/store/type-map.js +3 -3
- package/store/ui-context.ts +86 -0
- package/store/wm.ts +244 -0
- package/types/kube/kube-api.ts +2 -1
- package/types/notifications/index.ts +27 -3
- package/types/rancher/index.d.ts +1 -0
- package/types/resources/settings.d.ts +29 -7
- package/types/shell/index.d.ts +138 -4
- package/types/store/__tests__/pagination.types.spec.ts +137 -0
- package/types/store/pagination.types.ts +157 -9
- package/types/store/subscribe-events.types.ts +8 -1
- package/types/store/subscribe.types.ts +1 -0
- package/types/window-manager.ts +24 -0
- package/utils/__tests__/cluster.test.ts +379 -1
- package/utils/__tests__/object.test.ts +19 -0
- package/utils/__tests__/provider.test.ts +98 -0
- package/utils/__tests__/selector-typed.test.ts +263 -0
- package/utils/__tests__/version.test.ts +19 -1
- package/utils/autoscaler-utils.ts +7 -0
- package/utils/back-off.ts +3 -3
- package/utils/brand.ts +29 -0
- package/utils/chart.js +18 -0
- package/utils/cluster.js +157 -3
- package/utils/color.js +1 -1
- package/utils/dynamic-content/__tests__/announcement.test.ts +498 -0
- package/utils/dynamic-content/__tests__/config.test.ts +187 -0
- package/utils/dynamic-content/__tests__/index.test.ts +390 -0
- package/utils/dynamic-content/__tests__/info.test.ts +275 -0
- package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
- package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
- package/utils/dynamic-content/__tests__/util.test.ts +235 -0
- package/utils/dynamic-content/announcement.ts +142 -0
- package/utils/dynamic-content/config.ts +55 -0
- package/utils/dynamic-content/example.json +40 -0
- package/utils/dynamic-content/index.ts +277 -0
- package/utils/dynamic-content/info.ts +261 -0
- package/utils/dynamic-content/new-release.ts +126 -0
- package/utils/dynamic-content/notification-handler.ts +48 -0
- package/utils/dynamic-content/support-notice.ts +169 -0
- package/utils/dynamic-content/types.d.ts +153 -0
- package/utils/dynamic-content/util.ts +122 -0
- package/utils/dynamic-importer.js +2 -2
- package/utils/favicon.js +4 -4
- package/utils/inactivity.ts +104 -0
- package/utils/object.js +20 -2
- package/utils/pagination-utils.ts +19 -4
- package/utils/pagination-wrapper.ts +12 -8
- package/utils/provider.ts +14 -0
- package/utils/release-notes.ts +1 -1
- package/utils/scroll.js +7 -0
- package/utils/selector-typed.ts +6 -2
- package/utils/settings.ts +15 -0
- package/utils/validators/machine-pool.ts +13 -3
- package/utils/version.js +15 -0
- package/assets/images/icons/document.svg +0 -3
- package/plugins/nuxt-client-init.js +0 -3
- package/store/wm.js +0 -95
- /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
- /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
- /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { fetchAndProcessDynamicContent, fetchDynamicContent, UPDATE_DATE_FORMAT } from '../index';
|
|
2
|
+
import * as config from '../config';
|
|
3
|
+
import * as util from '../util';
|
|
4
|
+
import * as newRelease from '../new-release';
|
|
5
|
+
import * as supportNotice from '../support-notice';
|
|
6
|
+
import * as info from '../info';
|
|
7
|
+
import * as typeMap from '@shell/store/type-map';
|
|
8
|
+
import * as version from '@shell/config/version';
|
|
9
|
+
import * as jsyaml from 'js-yaml';
|
|
10
|
+
import dayjs from 'dayjs';
|
|
11
|
+
import { Context } from '../types';
|
|
12
|
+
|
|
13
|
+
// Mock dependencies
|
|
14
|
+
jest.mock('../config');
|
|
15
|
+
jest.mock('../util');
|
|
16
|
+
jest.mock('../new-release');
|
|
17
|
+
jest.mock('../support-notice');
|
|
18
|
+
jest.mock('../info');
|
|
19
|
+
jest.mock('@shell/store/type-map');
|
|
20
|
+
jest.mock('@shell/config/version');
|
|
21
|
+
jest.mock('js-yaml');
|
|
22
|
+
|
|
23
|
+
// Mock dayjs to control time
|
|
24
|
+
const mockDayInstance = {
|
|
25
|
+
format: jest.fn(() => '2023-01-01'),
|
|
26
|
+
add: jest.fn((amount, unit) => {
|
|
27
|
+
return dayjs('2023-01-01').add(amount, unit);
|
|
28
|
+
}),
|
|
29
|
+
diff: jest.fn(() => 100), // a value > 30 for concurrent check
|
|
30
|
+
toString: jest.fn(() => new Date('2023-01-01T12:00:00Z').toString()),
|
|
31
|
+
isAfter: jest.fn(),
|
|
32
|
+
isValid: jest.fn(() => true),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const mockGetConfig = config.getConfig as jest.Mock;
|
|
36
|
+
const mockCreateLogger = util.createLogger as jest.Mock;
|
|
37
|
+
const mockProcessReleaseVersion = newRelease.processReleaseVersion as jest.Mock;
|
|
38
|
+
const mockProcessSupportNotices = supportNotice.processSupportNotices as jest.Mock;
|
|
39
|
+
const mockIsAdminUser = typeMap.isAdminUser as jest.Mock;
|
|
40
|
+
const mockGetVersionData = version.getVersionData as jest.Mock;
|
|
41
|
+
const mockYamlLoad = jsyaml.load as jest.Mock;
|
|
42
|
+
const mockSystemInfoProvider = info.SystemInfoProvider as jest.Mock;
|
|
43
|
+
|
|
44
|
+
describe('dynamic content', () => {
|
|
45
|
+
let mockDispatch: jest.Mock;
|
|
46
|
+
let mockGetters: any;
|
|
47
|
+
let mockAxios: jest.Mock;
|
|
48
|
+
let mockLogger: any;
|
|
49
|
+
let mockConfig: any;
|
|
50
|
+
let localStorageMock: any;
|
|
51
|
+
let consoleLogSpy: jest.SpyInstance;
|
|
52
|
+
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
mockDispatch = jest.fn();
|
|
55
|
+
mockGetters = { isSingleProduct: false };
|
|
56
|
+
mockAxios = jest.fn();
|
|
57
|
+
mockLogger = {
|
|
58
|
+
debug: jest.fn(),
|
|
59
|
+
info: jest.fn(),
|
|
60
|
+
error: jest.fn()
|
|
61
|
+
};
|
|
62
|
+
mockConfig = {
|
|
63
|
+
enabled: true,
|
|
64
|
+
debug: false,
|
|
65
|
+
log: false,
|
|
66
|
+
prime: false,
|
|
67
|
+
endpoint: '$dist'
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
mockGetConfig.mockReturnValue(mockConfig);
|
|
71
|
+
mockCreateLogger.mockReturnValue(mockLogger);
|
|
72
|
+
mockIsAdminUser.mockReturnValue(true);
|
|
73
|
+
mockGetVersionData.mockReturnValue({ Version: '2.9.0', RancherPrime: 'false' });
|
|
74
|
+
mockSystemInfoProvider.mockImplementation(() => ({ buildQueryString: () => 'qs=1' }));
|
|
75
|
+
|
|
76
|
+
// Mock localStorage
|
|
77
|
+
let store: { [key: string]: string } = {};
|
|
78
|
+
|
|
79
|
+
localStorageMock = {
|
|
80
|
+
getItem: (key: string) => store[key] || null,
|
|
81
|
+
setItem: (key: string, value: string) => {
|
|
82
|
+
store[key] = value.toString();
|
|
83
|
+
},
|
|
84
|
+
removeItem: (key: string) => {
|
|
85
|
+
delete store[key];
|
|
86
|
+
},
|
|
87
|
+
clear: () => {
|
|
88
|
+
store = {};
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
Object.defineProperty(window, 'localStorage', { value: localStorageMock });
|
|
92
|
+
|
|
93
|
+
jest.useFakeTimers();
|
|
94
|
+
|
|
95
|
+
// Mock console
|
|
96
|
+
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
afterEach(() => {
|
|
100
|
+
jest.clearAllMocks();
|
|
101
|
+
jest.useRealTimers();
|
|
102
|
+
localStorageMock.clear();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('fetchAndProcessDynamicContent', () => {
|
|
106
|
+
it('should exit early if in single product mode', async() => {
|
|
107
|
+
mockGetters.isSingleProduct = true;
|
|
108
|
+
|
|
109
|
+
const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
|
|
110
|
+
|
|
111
|
+
jest.runAllTimers();
|
|
112
|
+
|
|
113
|
+
await ret;
|
|
114
|
+
expect(mockGetConfig).not.toHaveBeenCalled();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should exit early if dynamic content is disabled', async() => {
|
|
118
|
+
mockConfig.enabled = false;
|
|
119
|
+
await fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
|
|
120
|
+
|
|
121
|
+
expect(mockLogger.debug).not.toHaveBeenCalled();
|
|
122
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('Dynamic content disabled through configuration'); // eslint-disable-line no-console
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should exit early if version is invalid', async() => {
|
|
126
|
+
mockGetVersionData.mockReturnValue({ Version: 'invalid' });
|
|
127
|
+
const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
|
|
128
|
+
|
|
129
|
+
jest.runAllTimers();
|
|
130
|
+
|
|
131
|
+
await ret;
|
|
132
|
+
expect(mockProcessReleaseVersion).not.toHaveBeenCalled();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should process content for community users', async() => {
|
|
136
|
+
const content = { releases: [], support: {} };
|
|
137
|
+
|
|
138
|
+
mockAxios.mockResolvedValue({ data: 'yaml: data' });
|
|
139
|
+
mockYamlLoad.mockReturnValue(content);
|
|
140
|
+
|
|
141
|
+
const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
|
|
142
|
+
|
|
143
|
+
jest.runAllTimers();
|
|
144
|
+
await ret;
|
|
145
|
+
|
|
146
|
+
expect(mockProcessReleaseVersion).toHaveBeenCalledTimes(1);
|
|
147
|
+
expect(mockProcessSupportNotices).toHaveBeenCalledTimes(1);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should process content for prime admin users', async() => {
|
|
151
|
+
mockConfig.prime = true;
|
|
152
|
+
mockIsAdminUser.mockReturnValue(true);
|
|
153
|
+
const content = { releases: [], support: {} };
|
|
154
|
+
|
|
155
|
+
mockAxios.mockResolvedValue({ data: 'yaml: data' });
|
|
156
|
+
mockYamlLoad.mockReturnValue(content);
|
|
157
|
+
|
|
158
|
+
const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
|
|
159
|
+
|
|
160
|
+
jest.runAllTimers();
|
|
161
|
+
await ret;
|
|
162
|
+
|
|
163
|
+
expect(mockProcessReleaseVersion).toHaveBeenCalledTimes(1);
|
|
164
|
+
expect(mockProcessSupportNotices).toHaveBeenCalledTimes(1);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should NOT process content for prime non-admin users', async() => {
|
|
168
|
+
mockConfig.prime = true;
|
|
169
|
+
mockIsAdminUser.mockReturnValue(false);
|
|
170
|
+
const content = { releases: [], support: {} };
|
|
171
|
+
|
|
172
|
+
mockAxios.mockResolvedValue({ data: 'yaml: data' });
|
|
173
|
+
mockYamlLoad.mockReturnValue(content);
|
|
174
|
+
|
|
175
|
+
const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
|
|
176
|
+
|
|
177
|
+
jest.runAllTimers();
|
|
178
|
+
await ret;
|
|
179
|
+
|
|
180
|
+
expect(mockProcessReleaseVersion).not.toHaveBeenCalled();
|
|
181
|
+
expect(mockProcessSupportNotices).not.toHaveBeenCalled();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should override version if debugVersion is set', async() => {
|
|
185
|
+
const content = { settings: { debugVersion: '2.10.0' } };
|
|
186
|
+
|
|
187
|
+
mockAxios.mockResolvedValue({ data: content });
|
|
188
|
+
mockYamlLoad.mockReturnValue(content);
|
|
189
|
+
|
|
190
|
+
const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
|
|
191
|
+
|
|
192
|
+
jest.runAllTimers();
|
|
193
|
+
await ret;
|
|
194
|
+
|
|
195
|
+
expect(mockLogger.debug).toHaveBeenCalledWith('Read configuration', {
|
|
196
|
+
debug: false, enabled: true, endpoint: '$dist', log: false, prime: false
|
|
197
|
+
});
|
|
198
|
+
expect(mockLogger.debug).toHaveBeenCalledWith('Overriding version number to 2.10.0');
|
|
199
|
+
expect(mockProcessReleaseVersion).toHaveBeenCalledWith(
|
|
200
|
+
expect.any(Object),
|
|
201
|
+
undefined,
|
|
202
|
+
expect.objectContaining({ version: expect.objectContaining({ version: '2.10.0' }) })
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should clear logs if logging is disabled', async() => {
|
|
207
|
+
mockConfig.log = false;
|
|
208
|
+
localStorageMock.setItem('rancher-updates-debug-log', '[]');
|
|
209
|
+
mockAxios.mockResolvedValue({ data: '' });
|
|
210
|
+
|
|
211
|
+
const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
|
|
212
|
+
|
|
213
|
+
jest.runAllTimers();
|
|
214
|
+
await ret;
|
|
215
|
+
|
|
216
|
+
expect(localStorageMock.getItem('rancher-updates-debug-log')).toBeNull();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should handle errors during processing', async() => {
|
|
220
|
+
const error = new Error('Processing failed');
|
|
221
|
+
|
|
222
|
+
mockProcessReleaseVersion.mockImplementation(() => {
|
|
223
|
+
throw error;
|
|
224
|
+
});
|
|
225
|
+
mockAxios.mockResolvedValue({ data: '' });
|
|
226
|
+
|
|
227
|
+
const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
|
|
228
|
+
|
|
229
|
+
jest.runAllTimers();
|
|
230
|
+
await ret;
|
|
231
|
+
|
|
232
|
+
expect(mockLogger.error).toHaveBeenCalledWith('Error reading or processing dynamic content', error);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe('fetchDynamicContent', () => {
|
|
237
|
+
let context: Context;
|
|
238
|
+
|
|
239
|
+
beforeEach(() => {
|
|
240
|
+
context = {
|
|
241
|
+
dispatch: mockDispatch,
|
|
242
|
+
getters: mockGetters,
|
|
243
|
+
axios: mockAxios,
|
|
244
|
+
logger: mockLogger,
|
|
245
|
+
config: mockConfig,
|
|
246
|
+
isAdmin: true,
|
|
247
|
+
settings: { releaseNotesUrl: '', suseExtensions: [] },
|
|
248
|
+
};
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should fetch and parse content when needed', async() => {
|
|
252
|
+
const content = { version: '1.0' };
|
|
253
|
+
|
|
254
|
+
mockAxios.mockResolvedValue({ data: 'yaml: data' });
|
|
255
|
+
mockYamlLoad.mockReturnValue(content);
|
|
256
|
+
|
|
257
|
+
const fetchPromise = fetchDynamicContent(context);
|
|
258
|
+
|
|
259
|
+
jest.runAllTimers();
|
|
260
|
+
const result = await fetchPromise;
|
|
261
|
+
|
|
262
|
+
expect(mockAxios).toHaveBeenCalledTimes(1);
|
|
263
|
+
expect(mockYamlLoad).toHaveBeenCalledWith('yaml: data');
|
|
264
|
+
expect(result).toStrictEqual(content);
|
|
265
|
+
|
|
266
|
+
const tomorrow = dayjs().add(1, 'day').format(UPDATE_DATE_FORMAT);
|
|
267
|
+
|
|
268
|
+
expect(localStorageMock.getItem('rancher-updates-last-content')).toBe(JSON.stringify(content));
|
|
269
|
+
// Check updateFetchInfo(false) was called
|
|
270
|
+
expect(localStorageMock.getItem('rancher-updates-fetch-next')).toBe(tomorrow);
|
|
271
|
+
expect(localStorageMock.getItem('rancher-updates-fetch-errors')).toBeNull();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('should skip fetch if not due', async() => {
|
|
275
|
+
const today = dayjs().format(UPDATE_DATE_FORMAT);
|
|
276
|
+
const tomorrow = dayjs().add(1, 'day').format(UPDATE_DATE_FORMAT);
|
|
277
|
+
|
|
278
|
+
localStorageMock.setItem('rancher-updates-fetch-next', tomorrow);
|
|
279
|
+
const fetchPromise = fetchDynamicContent(context);
|
|
280
|
+
|
|
281
|
+
jest.runAllTimers();
|
|
282
|
+
await fetchPromise;
|
|
283
|
+
|
|
284
|
+
expect(mockAxios).not.toHaveBeenCalled();
|
|
285
|
+
expect(mockLogger.info).toHaveBeenCalledWith(`Skipping update check for dynamic content - next check due on ${ tomorrow } (today is ${ today })`);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('should skip fetch if another is in progress', async() => {
|
|
289
|
+
const today = dayjs().format(UPDATE_DATE_FORMAT);
|
|
290
|
+
const later = dayjs().subtract(10, 'second').toString();
|
|
291
|
+
|
|
292
|
+
localStorageMock.setItem('rancher-updates-fetch-next', today);
|
|
293
|
+
localStorageMock.setItem('rancher-updates-fetching', later);
|
|
294
|
+
|
|
295
|
+
const fetchPromise = fetchDynamicContent(context);
|
|
296
|
+
|
|
297
|
+
jest.runAllTimers();
|
|
298
|
+
await fetchPromise;
|
|
299
|
+
|
|
300
|
+
expect(mockAxios).not.toHaveBeenCalled();
|
|
301
|
+
expect(mockLogger.debug).toHaveBeenCalledWith('Already fetching dynamic content in another tab (or previous tab closed while fetching) - skipping');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('should proceed with fetch if a stale fetch was in progress', async() => {
|
|
305
|
+
(mockDayInstance.diff as jest.Mock).mockReturnValue(100); // more than 30s
|
|
306
|
+
localStorageMock.setItem('rancher-updates-fetching', new Date('2023-01-01T11:00:00Z').toString());
|
|
307
|
+
mockAxios.mockResolvedValue({ data: '' });
|
|
308
|
+
|
|
309
|
+
const fetchPromise = fetchDynamicContent(context);
|
|
310
|
+
|
|
311
|
+
jest.runAllTimers();
|
|
312
|
+
await fetchPromise;
|
|
313
|
+
|
|
314
|
+
expect(mockAxios).toHaveBeenCalledTimes(1);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should handle axios fetch error and backoff', async() => {
|
|
318
|
+
const error = new Error('Network Error');
|
|
319
|
+
|
|
320
|
+
mockAxios.mockRejectedValue(error);
|
|
321
|
+
|
|
322
|
+
const fetchPromise = fetchDynamicContent(context);
|
|
323
|
+
|
|
324
|
+
jest.runAllTimers();
|
|
325
|
+
await fetchPromise;
|
|
326
|
+
|
|
327
|
+
const tomorrow = dayjs().add(1, 'day').format(UPDATE_DATE_FORMAT);
|
|
328
|
+
|
|
329
|
+
expect(mockLogger.error).toHaveBeenCalledWith('Error occurred reading dynamic content', error);
|
|
330
|
+
// Check updateFetchInfo(true) was called
|
|
331
|
+
expect(localStorageMock.getItem('rancher-updates-fetch-errors')).toBe('1');
|
|
332
|
+
expect(localStorageMock.getItem('rancher-updates-fetch-next')).toBe(tomorrow); // 1 day backoff
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should handle YAML parsing error', async() => {
|
|
336
|
+
const error = new Error('YAML Error');
|
|
337
|
+
|
|
338
|
+
mockAxios.mockResolvedValue({ data: 'invalid yaml' });
|
|
339
|
+
mockYamlLoad.mockImplementation(() => {
|
|
340
|
+
throw error;
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
const fetchPromise = fetchDynamicContent(context);
|
|
344
|
+
|
|
345
|
+
jest.runAllTimers();
|
|
346
|
+
await fetchPromise;
|
|
347
|
+
|
|
348
|
+
expect(mockLogger.error).toHaveBeenCalledWith('Failed to parse YAML/JSON from dynamic content package', error);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('should handle axios fetch error with unexpected data', async() => {
|
|
352
|
+
mockAxios.mockResolvedValue({ data: null });
|
|
353
|
+
|
|
354
|
+
const fetchPromise = fetchDynamicContent(context);
|
|
355
|
+
|
|
356
|
+
jest.runAllTimers();
|
|
357
|
+
await fetchPromise;
|
|
358
|
+
|
|
359
|
+
expect(mockLogger.error).toHaveBeenCalledWith('Error fetching dynamic content package (unexpected data)');
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('should increment backoff on multiple consecutive errors', async() => {
|
|
363
|
+
const error = new Error('Network Error');
|
|
364
|
+
|
|
365
|
+
mockAxios.mockRejectedValue(error);
|
|
366
|
+
localStorageMock.setItem('rancher-updates-fetch-errors', '2'); // 2 previous errors
|
|
367
|
+
|
|
368
|
+
const fetchPromise = fetchDynamicContent(context);
|
|
369
|
+
|
|
370
|
+
jest.runAllTimers();
|
|
371
|
+
await fetchPromise;
|
|
372
|
+
|
|
373
|
+
expect(localStorageMock.getItem('rancher-updates-fetch-errors')).toBe('3');
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('should cap backoff at max value', async() => {
|
|
377
|
+
const error = new Error('Network Error');
|
|
378
|
+
|
|
379
|
+
mockAxios.mockRejectedValue(error);
|
|
380
|
+
localStorageMock.setItem('rancher-updates-fetch-errors', '10');
|
|
381
|
+
|
|
382
|
+
const fetchPromise = fetchDynamicContent(context);
|
|
383
|
+
|
|
384
|
+
jest.runAllTimers();
|
|
385
|
+
await fetchPromise;
|
|
386
|
+
|
|
387
|
+
expect(localStorageMock.getItem('rancher-updates-fetch-errors')).toBe('6');
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
});
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { SystemInfoProvider } from '../info';
|
|
2
|
+
import { MANAGEMENT, COUNT } from '@shell/config/types';
|
|
3
|
+
import { SETTING } from '@shell/config/settings';
|
|
4
|
+
import * as version from '@shell/config/version';
|
|
5
|
+
import { sha256 } from '@shell/utils/crypto';
|
|
6
|
+
|
|
7
|
+
// Mock dependencies from @shell
|
|
8
|
+
jest.mock('@shell/config/version', () => ({ getVersionData: jest.fn(), isRancherPrime: jest.fn() }));
|
|
9
|
+
|
|
10
|
+
jest.mock('@shell/utils/crypto', () => ({ sha256: jest.fn((val: string) => `hashed_${ val }`) }));
|
|
11
|
+
|
|
12
|
+
describe('systemInfoProvider', () => {
|
|
13
|
+
let mockGetters: any;
|
|
14
|
+
let mockSettings: any[];
|
|
15
|
+
let mockClusters: any[];
|
|
16
|
+
let mockCounts: any;
|
|
17
|
+
let mockPlugins: any[];
|
|
18
|
+
let originalWindowLocation: Location;
|
|
19
|
+
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
originalWindowLocation = window.location;
|
|
22
|
+
// Mock window.location
|
|
23
|
+
delete (window as any).location;
|
|
24
|
+
(window as any).location = { host: 'fallback.host' };
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterAll(() => {
|
|
28
|
+
(window as any).location = originalWindowLocation;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
// Reset mocks
|
|
33
|
+
(version.getVersionData as jest.Mock).mockClear();
|
|
34
|
+
(version.isRancherPrime as jest.Mock).mockClear();
|
|
35
|
+
(sha256 as jest.Mock).mockClear();
|
|
36
|
+
|
|
37
|
+
// Mock window properties
|
|
38
|
+
Object.defineProperty(window, 'screen', {
|
|
39
|
+
value: { width: 1920, height: 1080 },
|
|
40
|
+
writable: true
|
|
41
|
+
});
|
|
42
|
+
Object.defineProperty(window, 'innerWidth', { value: 1024, writable: true });
|
|
43
|
+
Object.defineProperty(window, 'innerHeight', { value: 768, writable: true });
|
|
44
|
+
Object.defineProperty(window.navigator, 'language', { value: 'en-US', writable: true });
|
|
45
|
+
|
|
46
|
+
mockSettings = [
|
|
47
|
+
{ id: SETTING.SERVER_URL, value: 'https://rancher.test' },
|
|
48
|
+
{ id: 'install-uuid', value: 'test-uuid' },
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
mockClusters = [
|
|
52
|
+
{
|
|
53
|
+
id: 'local',
|
|
54
|
+
isLocal: true,
|
|
55
|
+
kubernetesVersionBase: '1.25',
|
|
56
|
+
provisioner: 'k3s',
|
|
57
|
+
status: { nodeCount: 3 },
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'c-12345',
|
|
61
|
+
isLocal: false,
|
|
62
|
+
}
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
mockCounts = [{ counts: { [MANAGEMENT.CLUSTER]: { summary: { count: 2 } } } }];
|
|
66
|
+
|
|
67
|
+
mockPlugins = [
|
|
68
|
+
{ name: 'harvester', builtin: false },
|
|
69
|
+
{ name: 'some-custom-ext', builtin: false },
|
|
70
|
+
{ name: 'core', builtin: true },
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
mockGetters = {
|
|
74
|
+
'management/typeRegistered': jest.fn().mockReturnValue(true),
|
|
75
|
+
'management/all': jest.fn((type: string) => {
|
|
76
|
+
if (type === MANAGEMENT.SETTING) {
|
|
77
|
+
return mockSettings;
|
|
78
|
+
}
|
|
79
|
+
if (type === MANAGEMENT.CLUSTER) {
|
|
80
|
+
return mockClusters;
|
|
81
|
+
}
|
|
82
|
+
if (type === COUNT) {
|
|
83
|
+
return mockCounts;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return [];
|
|
87
|
+
}),
|
|
88
|
+
'auth/principalId': 'user-123',
|
|
89
|
+
'uiplugins/plugins': mockPlugins,
|
|
90
|
+
isSingleProduct: false,
|
|
91
|
+
'management/byId': jest.fn((type: string, id: string) => {
|
|
92
|
+
if (type === MANAGEMENT.SETTING) {
|
|
93
|
+
return mockSettings.find((s) => s.id === id) || null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return undefined;
|
|
97
|
+
}),
|
|
98
|
+
'management/schemaFor': jest.fn(),
|
|
99
|
+
localCluster: mockClusters.find((c) => c.id === 'local') || null,
|
|
100
|
+
'features/get': jest.fn(() => 'abc'),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
(version.getVersionData as jest.Mock).mockReturnValue({
|
|
104
|
+
Version: '2.8.0-rc1',
|
|
105
|
+
RancherPrime: 'false',
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
(version.isRancherPrime as jest.Mock).mockReturnValue(false);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should build a complete query string with all available data', () => {
|
|
112
|
+
const infoProvider = new SystemInfoProvider(mockGetters, {});
|
|
113
|
+
const qs = infoProvider.buildQueryString();
|
|
114
|
+
|
|
115
|
+
expect(qs).toContain('dcv=v1');
|
|
116
|
+
expect(qs).toContain('s=hashed_https://rancher.test');
|
|
117
|
+
expect(qs).toContain('u=hashed_user-123');
|
|
118
|
+
expect(qs).toContain('uuid=test-uuid');
|
|
119
|
+
expect(qs).toContain('v=2.8.0');
|
|
120
|
+
expect(qs).toContain('dev=true');
|
|
121
|
+
expect(qs).toContain('p=false');
|
|
122
|
+
// Add back when LTS is added
|
|
123
|
+
// expect(qs).toContain('lts=false');
|
|
124
|
+
expect(qs).toContain('cc=2');
|
|
125
|
+
expect(qs).toContain('lkv=1.25');
|
|
126
|
+
expect(qs).toContain('lcp=k3s');
|
|
127
|
+
expect(qs).toContain('lnc=3');
|
|
128
|
+
expect(qs).toContain('xkn=harvester');
|
|
129
|
+
expect(qs).toContain('xcc=1');
|
|
130
|
+
expect(qs).toContain('bl=en-US');
|
|
131
|
+
expect(qs).toContain('bs=1024x768');
|
|
132
|
+
expect(qs).toContain('ss=1920x1080');
|
|
133
|
+
expect(qs).toContain('ff-usc=abc');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should handle missing or partial data gracefully', () => {
|
|
137
|
+
// Override mocks for this test
|
|
138
|
+
(version.getVersionData as jest.Mock).mockReturnValue({
|
|
139
|
+
Version: '2.9.0', // Not a dev version
|
|
140
|
+
RancherPrime: 'true',
|
|
141
|
+
});
|
|
142
|
+
(version.isRancherPrime as jest.Mock).mockReturnValue(true);
|
|
143
|
+
|
|
144
|
+
mockGetters['management/all'].mockImplementation((type: string) => {
|
|
145
|
+
if (type === MANAGEMENT.SETTING) {
|
|
146
|
+
return [{ id: 'install-uuid', value: 'only-uuid' }]; // No server-url
|
|
147
|
+
}
|
|
148
|
+
if (type === COUNT) {
|
|
149
|
+
return [{ counts: { [MANAGEMENT.CLUSTER]: { summary: { count: 27 } } } }];
|
|
150
|
+
}
|
|
151
|
+
if (type === MANAGEMENT.CLUSTER) {
|
|
152
|
+
return []; // No clusters
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return [];
|
|
156
|
+
});
|
|
157
|
+
mockGetters['management/byId'].mockImplementation((type: string, id: string) => {
|
|
158
|
+
if (type === MANAGEMENT.SETTING && id === 'install-uuid') {
|
|
159
|
+
return { id: 'install-uuid', value: 'only-uuid' }; // No server-url
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
mockGetters['uiplugins/plugins'] = null; // No plugins
|
|
164
|
+
mockGetters['auth/principalId'] = null; // No user
|
|
165
|
+
mockGetters['localCluster'] = null; // No clusters
|
|
166
|
+
mockGetters['features/get'] = () => {
|
|
167
|
+
throw new Error('unknown feature');
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const infoProvider = new SystemInfoProvider(mockGetters, {});
|
|
171
|
+
const qs = infoProvider.buildQueryString();
|
|
172
|
+
|
|
173
|
+
expect(qs).toContain('s=hashed_fallback.host');
|
|
174
|
+
expect(qs).toContain('u=hashed_unknown');
|
|
175
|
+
expect(qs).toContain('uuid=only-uuid');
|
|
176
|
+
expect(qs).toContain('v=2.9.0');
|
|
177
|
+
expect(qs).toContain('dev=false');
|
|
178
|
+
expect(qs).toContain('p=true');
|
|
179
|
+
expect(qs).toContain('cc=27');
|
|
180
|
+
expect(qs).not.toContain('lkv=');
|
|
181
|
+
expect(qs).not.toContain('lcp=');
|
|
182
|
+
expect(qs).not.toContain('lnc=');
|
|
183
|
+
expect(qs).not.toContain('xkn=');
|
|
184
|
+
expect(qs).not.toContain('xcc=');
|
|
185
|
+
expect(qs).not.toContain('ff-usc=');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should handle getAll returning undefined when types are not registered', () => {
|
|
189
|
+
// Override mocks for this test
|
|
190
|
+
(version.getVersionData as jest.Mock).mockReturnValue({
|
|
191
|
+
Version: '2.9.1',
|
|
192
|
+
RancherPrime: 'false',
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Simulate that the management types are not registered in the store
|
|
196
|
+
mockGetters['management/typeRegistered'].mockReturnValue(false);
|
|
197
|
+
mockGetters['management/all'].mockImplementation();
|
|
198
|
+
mockGetters['management/byId'].mockImplementation();
|
|
199
|
+
|
|
200
|
+
mockGetters['auth/principalId'] = 'user-456';
|
|
201
|
+
mockGetters['uiplugins/plugins'] = []; // No plugins
|
|
202
|
+
mockGetters['localCluster'] = null; // No clusters
|
|
203
|
+
|
|
204
|
+
const infoProvider = new SystemInfoProvider(mockGetters, {});
|
|
205
|
+
const qs = infoProvider.buildQueryString();
|
|
206
|
+
|
|
207
|
+
expect(mockGetters['management/byId']).toHaveBeenCalledWith(MANAGEMENT.SETTING, 'server-url');
|
|
208
|
+
expect(mockGetters['management/byId']).toHaveBeenCalledWith(MANAGEMENT.SETTING, 'install-uuid');
|
|
209
|
+
expect(mockGetters['management/byId']).toHaveBeenCalledWith(MANAGEMENT.SETTING, 'server-version-type');
|
|
210
|
+
expect(mockGetters['management/typeRegistered']).toHaveBeenCalledWith(COUNT);
|
|
211
|
+
expect(mockGetters['management/all']).not.toHaveBeenCalled();
|
|
212
|
+
|
|
213
|
+
// Verify the query string is built with fallback or empty values
|
|
214
|
+
expect(qs).toContain('s=hashed_fallback.host');
|
|
215
|
+
expect(qs).toContain('u=hashed_user-456');
|
|
216
|
+
expect(qs).toContain('v=2.9.1');
|
|
217
|
+
expect(qs).toContain('p=false');
|
|
218
|
+
expect(qs).toContain('xcc=0');
|
|
219
|
+
expect(qs).not.toContain('uuid=');
|
|
220
|
+
expect(qs).not.toContain('lkv=');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('should use UNKNOWN for missing system properties', () => {
|
|
224
|
+
// Override mocks for this test
|
|
225
|
+
(version.getVersionData as jest.Mock).mockReturnValue({
|
|
226
|
+
Version: '2.9.0',
|
|
227
|
+
RancherPrime: 'false',
|
|
228
|
+
});
|
|
229
|
+
(version.isRancherPrime as jest.Mock).mockReturnValue(false);
|
|
230
|
+
|
|
231
|
+
mockGetters['management/byId'].mockImplementation((type: string, id: string) => {
|
|
232
|
+
if (type === MANAGEMENT.SETTING) {
|
|
233
|
+
return { id, value: '' }; // Empty values for all settings
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// local cluster with missing properties
|
|
238
|
+
const localCluster = {
|
|
239
|
+
id: 'local',
|
|
240
|
+
isLocal: true,
|
|
241
|
+
status: { nodeCount: 1 },
|
|
242
|
+
// kubernetesVersionBase is missing
|
|
243
|
+
// provisioner is missing
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
mockGetters['management/all'].mockImplementation((type: string) => {
|
|
247
|
+
if (type === MANAGEMENT.SETTING) {
|
|
248
|
+
// Return settings, but with empty values
|
|
249
|
+
return [
|
|
250
|
+
{ id: SETTING.SERVER_URL, value: '' },
|
|
251
|
+
{ id: 'install-uuid', value: '' },
|
|
252
|
+
];
|
|
253
|
+
}
|
|
254
|
+
if (type === COUNT) {
|
|
255
|
+
return [{ counts: { [MANAGEMENT.CLUSTER]: { summary: { count: 1 } } } }];
|
|
256
|
+
}
|
|
257
|
+
if (type === MANAGEMENT.CLUSTER) {
|
|
258
|
+
return [localCluster];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return [];
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
mockGetters['auth/principalId'] = null; // No user
|
|
265
|
+
mockGetters['localCluster'] = localCluster;
|
|
266
|
+
|
|
267
|
+
const infoProvider = new SystemInfoProvider(mockGetters, {});
|
|
268
|
+
const qs = infoProvider.buildQueryString();
|
|
269
|
+
|
|
270
|
+
expect(qs).toContain('u=hashed_unknown');
|
|
271
|
+
expect(qs).toContain('lkv=unknown');
|
|
272
|
+
expect(qs).toContain('lcp=unknown');
|
|
273
|
+
expect(qs).not.toContain('uuid='); // systemUUID is UNKNOWN, so it's skipped
|
|
274
|
+
});
|
|
275
|
+
});
|