@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
|
@@ -32,8 +32,6 @@ export interface TitleBarProps {
|
|
|
32
32
|
actionMenuResource?: any;
|
|
33
33
|
onShowConfiguration?: (returnFocusSelector: string) => void;
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
const showConfigurationIcon = require(`@shell/assets/images/icons/document.svg`);
|
|
37
35
|
</script>
|
|
38
36
|
|
|
39
37
|
<script setup lang="ts">
|
|
@@ -84,6 +82,7 @@ watch(
|
|
|
84
82
|
</span>
|
|
85
83
|
<BadgeState
|
|
86
84
|
v-if="badge"
|
|
85
|
+
v-ui-context="{ store: store, icon: 'icon-folder', hookable: true, value: resource, tag: '__details-state', description: 'Details' }"
|
|
87
86
|
class="badge-state"
|
|
88
87
|
:color="badge.color"
|
|
89
88
|
:label="badge.label"
|
|
@@ -99,11 +98,10 @@ watch(
|
|
|
99
98
|
:aria-label="i18n.t('component.resource.detail.titleBar.ariaLabel.showConfiguration', { resource: resourceName })"
|
|
100
99
|
@click="() => emit('show-configuration', showConfigurationReturnFocusSelector)"
|
|
101
100
|
>
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
class="mmr-3"
|
|
101
|
+
<i
|
|
102
|
+
class="icon icon-document"
|
|
105
103
|
aria-hidden="true"
|
|
106
|
-
|
|
104
|
+
/>
|
|
107
105
|
{{ i18n.t('component.resource.detail.titleBar.showConfiguration') }}
|
|
108
106
|
</RcButton>
|
|
109
107
|
<ActionMenu
|
|
@@ -139,6 +137,12 @@ watch(
|
|
|
139
137
|
position: relative;
|
|
140
138
|
}
|
|
141
139
|
|
|
140
|
+
.icon-document {
|
|
141
|
+
width: 15px;
|
|
142
|
+
font-size: 16px;
|
|
143
|
+
margin-right: 10px;
|
|
144
|
+
}
|
|
145
|
+
|
|
142
146
|
.show-configuration {
|
|
143
147
|
margin-left: 16px;
|
|
144
148
|
}
|
|
@@ -2,6 +2,7 @@ import { computed, Ref, toValue } from 'vue';
|
|
|
2
2
|
import { useStore } from 'vuex';
|
|
3
3
|
import { Props as BannerProps } from '@components/Banner/Banner.vue';
|
|
4
4
|
import { useI18n } from '@shell/composables/useI18n';
|
|
5
|
+
import ResourceClass from '@shell/plugins/dashboard-store/resource-class';
|
|
5
6
|
|
|
6
7
|
export const useResourceDetailBannerProps = (resource: any): Ref<BannerProps | undefined> => {
|
|
7
8
|
const store = useStore();
|
|
@@ -43,3 +44,14 @@ export const useResourceDetailBannerProps = (resource: any): Ref<BannerProps | u
|
|
|
43
44
|
return undefined;
|
|
44
45
|
});
|
|
45
46
|
};
|
|
47
|
+
|
|
48
|
+
export const useOnShowConfiguration = (resource: any) => {
|
|
49
|
+
return (returnFocusSelector?: string, defaultTab?: string) => {
|
|
50
|
+
const resourceValue = toValue(resource);
|
|
51
|
+
// Because extensions can make a copy of the resource-class it's possible that an extension will have a resource-class which predates the inclusion of showConfiguration
|
|
52
|
+
// to still the rest of shell to consume
|
|
53
|
+
const showConfiguration = resourceValue.showConfiguration ? resourceValue.showConfiguration.bind(resourceValue) : ResourceClass.prototype.showConfiguration.bind(resourceValue);
|
|
54
|
+
|
|
55
|
+
showConfiguration(returnFocusSelector, defaultTab);
|
|
56
|
+
};
|
|
57
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
/* eslint-disable */
|
|
2
3
|
import { Banner } from '@components/Banner';
|
|
3
4
|
import TitleBar from '@shell/components/Resource/Detail/TitleBar/index.vue';
|
|
4
5
|
import { useDefaultTitleBarProps } from '@shell/components/Resource/Detail/TitleBar/composables';
|
|
@@ -7,6 +8,7 @@ import { useDefaultMetadataForLegacyPagesProps } from '@shell/components/Resourc
|
|
|
7
8
|
import { useResourceDetailBannerProps } from '@shell/components/Resource/Detail/composables';
|
|
8
9
|
import { computed } from 'vue';
|
|
9
10
|
|
|
11
|
+
// We are disabling eslint for this script to allow the use of the Props interface
|
|
10
12
|
export interface Props {
|
|
11
13
|
value?: Object;
|
|
12
14
|
resourceSubtype?: string;
|
|
@@ -15,18 +17,45 @@ export interface Props {
|
|
|
15
17
|
</script>
|
|
16
18
|
|
|
17
19
|
<script lang="ts" setup>
|
|
20
|
+
import { useStore } from 'vuex';
|
|
21
|
+
|
|
18
22
|
const props = withDefaults(defineProps<Props>(), { value: () => ({}), resourceSubtype: undefined });
|
|
19
23
|
|
|
24
|
+
const uiCtxResource = computed(() => {
|
|
25
|
+
const {
|
|
26
|
+
name, metadata, kind, state
|
|
27
|
+
} = (props.value || {}) as any;
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
name,
|
|
31
|
+
namespace: metadata?.namespace,
|
|
32
|
+
kind,
|
|
33
|
+
state,
|
|
34
|
+
};
|
|
35
|
+
});
|
|
20
36
|
const resourceSubtype = computed(() => props.resourceSubtype);
|
|
21
37
|
const titleBarProps = useDefaultTitleBarProps(props.value, resourceSubtype);
|
|
22
38
|
const metadataProps = useDefaultMetadataForLegacyPagesProps(props.value);
|
|
23
39
|
const bannerProps = useResourceDetailBannerProps(props.value);
|
|
40
|
+
|
|
41
|
+
const store = useStore();
|
|
24
42
|
</script>
|
|
25
43
|
|
|
26
44
|
<template>
|
|
27
45
|
<TitleBar v-bind="titleBarProps" />
|
|
28
46
|
<Banner
|
|
29
47
|
v-if="bannerProps"
|
|
48
|
+
v-ui-context="{
|
|
49
|
+
store: store,
|
|
50
|
+
icon: 'icon-info',
|
|
51
|
+
hookable: true,
|
|
52
|
+
value: {
|
|
53
|
+
bannerProps,
|
|
54
|
+
resource: uiCtxResource
|
|
55
|
+
},
|
|
56
|
+
tag: '__details-state-banner',
|
|
57
|
+
description: 'Status Message'
|
|
58
|
+
}"
|
|
30
59
|
class="new state-banner"
|
|
31
60
|
v-bind="bannerProps"
|
|
32
61
|
/>
|
|
@@ -310,7 +310,7 @@ export default {
|
|
|
310
310
|
}), {});
|
|
311
311
|
},
|
|
312
312
|
isFullPageOverride() {
|
|
313
|
-
return this.isView && this.value.fullDetailPageOverride;
|
|
313
|
+
return this.isView && this.value.fullDetailPageOverride && !this.isYaml;
|
|
314
314
|
}
|
|
315
315
|
},
|
|
316
316
|
|
|
@@ -430,6 +430,7 @@ export default {
|
|
|
430
430
|
:is="showComponent"
|
|
431
431
|
v-else-if="isFullPageOverride"
|
|
432
432
|
v-model:value="value"
|
|
433
|
+
v-ui-context="{ icon: 'icon-folder', value: value.name, tag: value.kind?.toLowerCase(), description: value.kind }"
|
|
433
434
|
v-bind="$data"
|
|
434
435
|
:done-params="doneParams"
|
|
435
436
|
:done-route="doneRoute"
|
|
@@ -446,6 +447,7 @@ export default {
|
|
|
446
447
|
<div v-else>
|
|
447
448
|
<Masthead
|
|
448
449
|
v-if="showMasthead"
|
|
450
|
+
v-ui-context="{ icon: 'icon-folder', value: liveModel.name, tag: liveModel.kind?.toLowerCase(), description: liveModel.kind }"
|
|
449
451
|
:resource="resourceType"
|
|
450
452
|
:value="liveModel"
|
|
451
453
|
:mode="mode"
|
|
@@ -481,7 +483,7 @@ export default {
|
|
|
481
483
|
</div>
|
|
482
484
|
|
|
483
485
|
<ResourceYaml
|
|
484
|
-
v-
|
|
486
|
+
v-if="isYaml"
|
|
485
487
|
ref="resourceyaml"
|
|
486
488
|
:value="value"
|
|
487
489
|
:mode="mode"
|
|
@@ -499,6 +501,7 @@ export default {
|
|
|
499
501
|
v-else
|
|
500
502
|
ref="comp"
|
|
501
503
|
v-model:value="value"
|
|
504
|
+
v-ui-context="{ icon: 'icon-folder', value: value.name, tag: value.kind?.toLowerCase(), description: value.kind }"
|
|
502
505
|
v-bind="$data"
|
|
503
506
|
:done-params="doneParams"
|
|
504
507
|
:done-route="doneRoute"
|
|
@@ -141,7 +141,7 @@ export default {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// blocked-post means you can post through norman, but not through steve.
|
|
144
|
-
if ( this.schema && !this.schema?.collectionMethods.find((x) => ['blocked-post', 'post'].includes(x.toLowerCase())) ) {
|
|
144
|
+
if ( this.schema && this.schema?.collectionMethods && !this.schema?.collectionMethods.find((x) => ['blocked-post', 'post'].includes(x.toLowerCase())) ) {
|
|
145
145
|
return false;
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -1090,7 +1090,7 @@ export default {
|
|
|
1090
1090
|
<div
|
|
1091
1091
|
v-if="showHeaderRow"
|
|
1092
1092
|
class="fixed-header-actions"
|
|
1093
|
-
:class="{button: !!$slots['header-button'], 'advanced-filtering': hasAdvancedFiltering}"
|
|
1093
|
+
:class="{button: !!$slots['header-button'], 'with-sub-header': !!$slots['sub-header-row'], 'advanced-filtering': hasAdvancedFiltering}"
|
|
1094
1094
|
>
|
|
1095
1095
|
<div
|
|
1096
1096
|
:class="bulkActionsClass"
|
|
@@ -1296,6 +1296,12 @@ export default {
|
|
|
1296
1296
|
<slot name="header-button" />
|
|
1297
1297
|
</div>
|
|
1298
1298
|
</div>
|
|
1299
|
+
<div
|
|
1300
|
+
v-if="!!$slots['sub-header-row']"
|
|
1301
|
+
class="sub-header-row"
|
|
1302
|
+
>
|
|
1303
|
+
<slot name="sub-header-row" />
|
|
1304
|
+
</div>
|
|
1299
1305
|
</div>
|
|
1300
1306
|
<table
|
|
1301
1307
|
ref="table"
|
|
@@ -1468,6 +1474,7 @@ export default {
|
|
|
1468
1474
|
<td
|
|
1469
1475
|
v-show="!hasAdvancedFiltering || (hasAdvancedFiltering && col.col.isColVisible)"
|
|
1470
1476
|
:key="col.col.name"
|
|
1477
|
+
v-ui-context="col.col.name === 'state' ? { icon: 'icon-folder', hookable: true, value: row.row, tag: '__sortable-table-row', description: 'Row' } : undefined"
|
|
1471
1478
|
:data-title="col.col.label"
|
|
1472
1479
|
:data-testid="`sortable-cell-${ i }-${ j }`"
|
|
1473
1480
|
:align="col.col.align || 'left'"
|
|
@@ -2047,8 +2054,17 @@ export default {
|
|
|
2047
2054
|
grid-template-columns: [bulk] auto [middle] min-content [search] minmax(min-content, 350px);
|
|
2048
2055
|
}
|
|
2049
2056
|
|
|
2057
|
+
$header-padding: 20px;
|
|
2058
|
+
.sub-header-row {
|
|
2059
|
+
padding: 0 0 $header-padding / 2 0;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2050
2062
|
.fixed-header-actions {
|
|
2051
|
-
padding: 0 0
|
|
2063
|
+
padding: 0 0 $header-padding 0;
|
|
2064
|
+
&.with-sub-header {
|
|
2065
|
+
padding: 0 0 $header-padding / 4 0;
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2052
2068
|
width: 100%;
|
|
2053
2069
|
z-index: z-index('fixedTableHeader');
|
|
2054
2070
|
background: transparent;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { mount, VueWrapper } from '@vue/test-utils';
|
|
2
|
+
import Tabbed from '@shell/components/Tabbed/index.vue';
|
|
3
|
+
import Tab from '@shell/components/Tabbed/Tab.vue';
|
|
4
|
+
|
|
5
|
+
jest.mock('@shell/components/form/ResourceTabs/composable', () => ({ useTabCountWatcher: () => ({}) }));
|
|
6
|
+
|
|
7
|
+
const mockT = (key: string) => key;
|
|
8
|
+
|
|
9
|
+
const defaultGlobalMountOptions = {
|
|
10
|
+
components: { Tab },
|
|
11
|
+
mocks: {
|
|
12
|
+
$router: {
|
|
13
|
+
replace: jest.fn(),
|
|
14
|
+
currentRoute: { _value: { hash: '' } }
|
|
15
|
+
},
|
|
16
|
+
$route: { hash: '' },
|
|
17
|
+
t: mockT,
|
|
18
|
+
store: { getters: { 'i18n/t': mockT } }
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe('component: Tabbed', () => {
|
|
23
|
+
const findTabNav = (wrapper: VueWrapper<any>) => wrapper.find('[data-testid="tabbed-block"]');
|
|
24
|
+
|
|
25
|
+
it('should display tab navigation for a single tab when hideSingleTab is false (default)', async() => {
|
|
26
|
+
const wrapper = mount(Tabbed, {
|
|
27
|
+
slots: { default: { components: { Tab }, template: '<Tab name="tab1" label="Tab 1" />' } },
|
|
28
|
+
global: { ...defaultGlobalMountOptions },
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
await wrapper.vm.$nextTick();
|
|
32
|
+
|
|
33
|
+
expect(findTabNav(wrapper).exists()).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should display tab navigation for multiple tabs when hideSingleTab is false (default)', async() => {
|
|
37
|
+
const wrapper = mount(Tabbed, {
|
|
38
|
+
slots: {
|
|
39
|
+
default: {
|
|
40
|
+
components: { Tab },
|
|
41
|
+
template: `
|
|
42
|
+
<Tab name="tab1" label="Tab 1" />
|
|
43
|
+
<Tab name="tab2" label="Tab 2" />
|
|
44
|
+
`,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
global: { ...defaultGlobalMountOptions },
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
await wrapper.vm.$nextTick();
|
|
51
|
+
|
|
52
|
+
expect(findTabNav(wrapper).exists()).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should NOT display tab navigation for a single tab when hideSingleTab is true', async() => {
|
|
56
|
+
const wrapper = mount(Tabbed, {
|
|
57
|
+
props: { hideSingleTab: true },
|
|
58
|
+
slots: { default: { components: { Tab }, template: '<Tab name="tab1" label="Tab 1" />' } },
|
|
59
|
+
global: { ...defaultGlobalMountOptions },
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await wrapper.vm.$nextTick();
|
|
63
|
+
|
|
64
|
+
expect(findTabNav(wrapper).exists()).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should display tab navigation for multiple tabs when hideSingleTab is true', async() => {
|
|
68
|
+
const wrapper = mount(Tabbed, {
|
|
69
|
+
props: { hideSingleTab: true },
|
|
70
|
+
slots: {
|
|
71
|
+
default: {
|
|
72
|
+
components: { Tab },
|
|
73
|
+
template: `
|
|
74
|
+
<Tab name="tab1" label="Tab 1" />
|
|
75
|
+
<Tab name="tab2" label="Tab 2" />
|
|
76
|
+
`,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
global: { ...defaultGlobalMountOptions },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await wrapper.vm.$nextTick();
|
|
83
|
+
|
|
84
|
+
expect(findTabNav(wrapper).exists()).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -10,7 +10,7 @@ import AsyncButton from '@shell/components/AsyncButton';
|
|
|
10
10
|
import Select from '@shell/components/form/Select';
|
|
11
11
|
import VirtualList from 'vue3-virtual-scroll-list';
|
|
12
12
|
import LogItem from '@shell/components/LogItem';
|
|
13
|
-
import ContainerLogsActions from '@shell/components/
|
|
13
|
+
import ContainerLogsActions from '@shell/components/Window/ContainerLogsActions.vue';
|
|
14
14
|
import { shallowRef } from 'vue';
|
|
15
15
|
import { useStore } from 'vuex';
|
|
16
16
|
import { debounce } from 'lodash';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nextTick } from 'vue';
|
|
2
2
|
import { shallowMount } from '@vue/test-utils';
|
|
3
|
-
import ContainerLogs from '@shell/components/
|
|
3
|
+
import ContainerLogs from '@shell/components/Window/ContainerLogs.vue';
|
|
4
4
|
import { base64Encode } from '@shell/utils/crypto';
|
|
5
5
|
import { Buffer } from 'buffer';
|
|
6
6
|
import { addEventListener } from '@shell/utils/socket';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { flushPromises, mount, Wrapper } from '@vue/test-utils';
|
|
2
|
-
import ContainerShell from '@shell/components/
|
|
2
|
+
import ContainerShell from '@shell/components/Window/ContainerShell.vue';
|
|
3
3
|
import Socket, {
|
|
4
4
|
addEventListener, EVENT_CONNECTED, EVENT_CONNECTING, EVENT_DISCONNECTED, EVENT_MESSAGE, EVENT_CONNECT_ERROR
|
|
5
5
|
} from '@shell/utils/socket';
|
|
6
|
-
import Window from '@shell/components/
|
|
6
|
+
import Window from '@shell/components/Window/Window.vue';
|
|
7
7
|
|
|
8
8
|
jest.mock('@shell/utils/socket');
|
|
9
9
|
jest.mock('@shell/utils/crypto', () => {
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import AutoscalerCard from '@shell/components/AutoscalerCard.vue';
|
|
4
|
+
import { ref } from 'vue';
|
|
5
|
+
import { createStore } from 'vuex';
|
|
6
|
+
|
|
7
|
+
const mockUseFetch = jest.fn();
|
|
8
|
+
|
|
9
|
+
jest.mock('@shell/components/Resource/Detail/FetchLoader/composables', () => ({ useFetch: (...args: any[]) => mockUseFetch(...args) }));
|
|
10
|
+
|
|
11
|
+
const mockUseInterval = jest.fn();
|
|
12
|
+
|
|
13
|
+
jest.mock('@shell/composables/useInterval', () => ({ useInterval: (...args: any[]) => mockUseInterval(...args) }));
|
|
14
|
+
|
|
15
|
+
describe('component: AutoscalerCard.vue', () => {
|
|
16
|
+
const mockLoadDetails = jest.fn();
|
|
17
|
+
const mockRefresh = jest.fn();
|
|
18
|
+
|
|
19
|
+
const createWrapper = (props: any, useFetchState: any) => {
|
|
20
|
+
// Reset and configure the useFetch mock for each test
|
|
21
|
+
mockUseFetch.mockImplementation(() => {
|
|
22
|
+
return ref({
|
|
23
|
+
loading: false,
|
|
24
|
+
refreshing: false,
|
|
25
|
+
data: null,
|
|
26
|
+
refresh: mockRefresh,
|
|
27
|
+
...useFetchState,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return mount(AutoscalerCard, {
|
|
32
|
+
props: {
|
|
33
|
+
value: { loadAutoscalerDetails: mockLoadDetails },
|
|
34
|
+
...props,
|
|
35
|
+
},
|
|
36
|
+
global: { plugins: [createStore({})] },
|
|
37
|
+
// Shallow mount to avoid rendering child components like the dynamic ones
|
|
38
|
+
shallow: true,
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
jest.clearAllMocks();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should call useFetch with the correct loader function', () => {
|
|
47
|
+
createWrapper({}, {});
|
|
48
|
+
// The first argument to useFetch is the loader function
|
|
49
|
+
expect(mockUseFetch).toHaveBeenCalledWith(expect.any(Function));
|
|
50
|
+
// We can invoke the loader to ensure it calls the prop method
|
|
51
|
+
const loader = mockUseFetch.mock.calls[0][0];
|
|
52
|
+
|
|
53
|
+
loader();
|
|
54
|
+
expect(mockLoadDetails).toHaveBeenCalledWith();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should setup a polling interval to refresh data', () => {
|
|
58
|
+
createWrapper({}, {});
|
|
59
|
+
expect(mockUseInterval).toHaveBeenCalledWith(expect.any(Function), 10000);
|
|
60
|
+
|
|
61
|
+
// Invoke the interval function to ensure it calls refresh
|
|
62
|
+
const intervalFn = mockUseInterval.mock.calls[0][0];
|
|
63
|
+
|
|
64
|
+
intervalFn();
|
|
65
|
+
expect(mockRefresh).toHaveBeenCalledWith();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('uI States', () => {
|
|
69
|
+
it('should display a loading spinner on initial load', () => {
|
|
70
|
+
const wrapper = createWrapper({}, { loading: true, refreshing: false });
|
|
71
|
+
|
|
72
|
+
expect(wrapper.find('.loading').exists()).toBe(true);
|
|
73
|
+
expect(wrapper.find('.icon-spinner').exists()).toBe(true);
|
|
74
|
+
expect(wrapper.find('.details').exists()).toBe(false);
|
|
75
|
+
expect(wrapper.find('.text-warning').exists()).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should NOT display the main loading spinner during a background refresh', () => {
|
|
79
|
+
const wrapper = createWrapper({}, {
|
|
80
|
+
loading: true, refreshing: true, data: []
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(wrapper.find('.loading').exists()).toBe(false);
|
|
84
|
+
// Data should still be visible
|
|
85
|
+
expect(wrapper.find('.details').exists()).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should display an error message if loading fails', () => {
|
|
89
|
+
const wrapper = createWrapper({}, { loading: false, data: null });
|
|
90
|
+
|
|
91
|
+
expect(wrapper.find('.text-warning').exists()).toBe(true);
|
|
92
|
+
expect(wrapper.find('.text-warning').text()).toBe('autoscaler.card.loadingError');
|
|
93
|
+
expect(wrapper.find('.loading').exists()).toBe(false);
|
|
94
|
+
expect(wrapper.find('.details').exists()).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should display details when data is loaded', () => {
|
|
98
|
+
const mockData = [
|
|
99
|
+
{ label: 'Status', value: 'Active' },
|
|
100
|
+
{ label: 'Nodes', value: '3' },
|
|
101
|
+
];
|
|
102
|
+
const wrapper = createWrapper({}, { loading: false, data: mockData });
|
|
103
|
+
|
|
104
|
+
expect(wrapper.find('.details').exists()).toBe(true);
|
|
105
|
+
const details = wrapper.findAll('.detail');
|
|
106
|
+
|
|
107
|
+
expect(details).toHaveLength(2);
|
|
108
|
+
expect(details[0].text()).toContain('Status');
|
|
109
|
+
expect(details[0].text()).toContain('Active');
|
|
110
|
+
expect(details[1].text()).toContain('Nodes');
|
|
111
|
+
expect(details[1].text()).toContain('3');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('detail Rendering', () => {
|
|
116
|
+
it('should render a string value', () => {
|
|
117
|
+
const mockData = [{ label: 'My Label', value: 'My Value' }];
|
|
118
|
+
const wrapper = createWrapper({}, { data: mockData });
|
|
119
|
+
const valueDiv = wrapper.find('.value');
|
|
120
|
+
|
|
121
|
+
expect(valueDiv.find('span').exists()).toBe(true);
|
|
122
|
+
expect(valueDiv.text()).toBe('My Value');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should render a dynamic component value', () => {
|
|
126
|
+
const DynamicComponent = {
|
|
127
|
+
name: 'DynamicComponent',
|
|
128
|
+
props: ['text'],
|
|
129
|
+
template: '<div>{{ text }}</div>'
|
|
130
|
+
};
|
|
131
|
+
const mockData = [{
|
|
132
|
+
label: 'My Component',
|
|
133
|
+
value: { component: DynamicComponent, props: { text: 'Dynamic Text' } }
|
|
134
|
+
}];
|
|
135
|
+
const wrapper = createWrapper({}, { data: mockData });
|
|
136
|
+
const valueDiv = wrapper.find('.value');
|
|
137
|
+
const renderedComponent = valueDiv.findComponent(DynamicComponent);
|
|
138
|
+
|
|
139
|
+
expect(renderedComponent.exists()).toBe(true);
|
|
140
|
+
expect(renderedComponent.props('text')).toBe('Dynamic Text');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should render a heading for details without a value', () => {
|
|
144
|
+
const mockData = [{ label: 'Section Header' }];
|
|
145
|
+
const wrapper = createWrapper({}, { data: mockData });
|
|
146
|
+
|
|
147
|
+
expect(wrapper.find('h5').exists()).toBe(true);
|
|
148
|
+
expect(wrapper.find('h5').text()).toBe('Section Header');
|
|
149
|
+
// Label and value should not be rendered
|
|
150
|
+
expect(wrapper.find('label').exists()).toBe(false);
|
|
151
|
+
expect(wrapper.find('.value').exists()).toBe(false);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import AutoscalerTab from '@shell/components/AutoscalerTab.vue';
|
|
4
|
+
import { ref } from 'vue';
|
|
5
|
+
import { createStore } from 'vuex';
|
|
6
|
+
|
|
7
|
+
const mockUseFetch = jest.fn();
|
|
8
|
+
|
|
9
|
+
jest.mock('@shell/components/Resource/Detail/FetchLoader/composables', () => ({ useFetch: (...args: any[]) => mockUseFetch(...args) }));
|
|
10
|
+
|
|
11
|
+
const mockUseInterval = jest.fn();
|
|
12
|
+
|
|
13
|
+
jest.mock('@shell/composables/useInterval', () => ({ useInterval: (...args: any[]) => mockUseInterval(...args) }));
|
|
14
|
+
|
|
15
|
+
describe('component: AutoscalerTab.vue', () => {
|
|
16
|
+
const mockLoadEvents = jest.fn();
|
|
17
|
+
const mockRefresh = jest.fn();
|
|
18
|
+
const mockChangeSort = jest.fn();
|
|
19
|
+
|
|
20
|
+
const SortableTableStub = {
|
|
21
|
+
name: 'SortableTable',
|
|
22
|
+
template: '<div></div>',
|
|
23
|
+
setup() {
|
|
24
|
+
return { changeSort: mockChangeSort };
|
|
25
|
+
},
|
|
26
|
+
props: ['namespaced', 'rowActions', 'defaultSortBy', 'headers', 'rows']
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const createWrapper = (props: any, useFetchState: any) => {
|
|
30
|
+
mockUseFetch.mockImplementation(() => {
|
|
31
|
+
return ref({
|
|
32
|
+
data: null,
|
|
33
|
+
refresh: mockRefresh,
|
|
34
|
+
...useFetchState,
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return mount(AutoscalerTab, {
|
|
39
|
+
props: {
|
|
40
|
+
value: { loadAutoscalerEvents: mockLoadEvents },
|
|
41
|
+
...props,
|
|
42
|
+
},
|
|
43
|
+
global: {
|
|
44
|
+
plugins: [createStore({})],
|
|
45
|
+
stubs: {
|
|
46
|
+
Tab: {
|
|
47
|
+
name: 'Tab',
|
|
48
|
+
template: '<div><slot/></div>',
|
|
49
|
+
props: ['label']
|
|
50
|
+
},
|
|
51
|
+
SortableTable: SortableTableStub,
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
jest.clearAllMocks();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('initialization and Data Fetching', () => {
|
|
62
|
+
it('should call useFetch with the correct loader function', () => {
|
|
63
|
+
createWrapper({}, {});
|
|
64
|
+
expect(mockUseFetch).toHaveBeenCalledWith(expect.any(Function));
|
|
65
|
+
|
|
66
|
+
const loader = mockUseFetch.mock.calls[0][0];
|
|
67
|
+
|
|
68
|
+
loader();
|
|
69
|
+
expect(mockLoadEvents).toHaveBeenCalledWith();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should setup a polling interval to refresh data', () => {
|
|
73
|
+
createWrapper({}, {});
|
|
74
|
+
expect(mockUseInterval).toHaveBeenCalledWith(expect.any(Function), 20000);
|
|
75
|
+
|
|
76
|
+
const intervalFn = mockUseInterval.mock.calls[0][0];
|
|
77
|
+
|
|
78
|
+
intervalFn();
|
|
79
|
+
expect(mockRefresh).toHaveBeenCalledWith();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should call changeSort on the table on mounted', () => {
|
|
83
|
+
createWrapper({}, {});
|
|
84
|
+
expect(mockChangeSort).toHaveBeenCalledWith('date', true);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('sortableTable props', () => {
|
|
89
|
+
it('should pass static props to the table', () => {
|
|
90
|
+
const wrapper = createWrapper({}, {});
|
|
91
|
+
const table = wrapper.findComponent(SortableTableStub);
|
|
92
|
+
|
|
93
|
+
expect(table.props('namespaced')).toBe(false);
|
|
94
|
+
expect(table.props('rowActions')).toBe(false);
|
|
95
|
+
expect(table.props('defaultSortBy')).toBe('date');
|
|
96
|
+
expect(table.props('headers')).toHaveLength(4);
|
|
97
|
+
expect(table.props('headers')[0].name).toBe('type');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should pass an empty array to rows when data is null', () => {
|
|
101
|
+
const wrapper = createWrapper({}, { data: null });
|
|
102
|
+
const table = wrapper.findComponent(SortableTableStub);
|
|
103
|
+
|
|
104
|
+
expect(table.props('rows')).toStrictEqual([]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should pass fetched data to the rows prop', () => {
|
|
108
|
+
const mockEvents = [
|
|
109
|
+
{ id: 1, message: 'Event 1' },
|
|
110
|
+
{ id: 2, message: 'Event 2' },
|
|
111
|
+
];
|
|
112
|
+
const wrapper = createWrapper({}, { data: mockEvents });
|
|
113
|
+
const table = wrapper.findComponent(SortableTableStub);
|
|
114
|
+
|
|
115
|
+
expect(table.props('rows')).toStrictEqual(mockEvents);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should pass correct label to Tab component', () => {
|
|
120
|
+
const wrapper = createWrapper({}, {});
|
|
121
|
+
const tab = wrapper.findComponent({ name: 'Tab' });
|
|
122
|
+
|
|
123
|
+
expect(tab.props('label')).toBe('autoscaler.tab.title');
|
|
124
|
+
});
|
|
125
|
+
});
|