@rancher/shell 0.3.0 → 0.3.2
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/styles/global/_button.scss +5 -1
- package/assets/styles/global/_columns.scss +4 -0
- package/assets/styles/global/_gauges.scss +1 -1
- package/assets/styles/global/_layout.scss +5 -2
- package/assets/styles/global/_select.scss +1 -4
- package/assets/styles/themes/_dark.scss +5 -4
- package/assets/styles/themes/_light.scss +4 -3
- package/assets/styles/themes/_suse.scss +1 -1
- package/assets/styles/vendor/vue-select.scss +4 -3
- package/assets/translations/en-us.yaml +673 -73
- package/assets/translations/zh-hans.yaml +720 -207
- package/chart/monitoring/steps/uninstall-v1.vue +2 -2
- package/cloud-credential/azure.vue +23 -0
- package/cloud-credential/harvester.vue +25 -62
- package/cloud-credential/pnap.vue +80 -0
- package/components/.DS_Store +0 -0
- package/components/ActionMenu.vue +28 -7
- package/components/AdvancedSection.vue +9 -2
- package/components/Alert.vue +2 -2
- package/components/ButtonDropdown.vue +0 -2
- package/components/ButtonGroup.vue +1 -0
- package/components/CollapsibleCard.vue +0 -1
- package/components/CruResource.vue +41 -4
- package/components/DetailTop.vue +72 -4
- package/components/DisableAuthProviderModal.vue +106 -0
- package/{rancher-components/components/Utils/DraggableZone → components}/DraggableZone.vue +0 -0
- package/components/ExplorerMembers.vue +253 -30
- package/components/ExplorerProjectsNamespaces.vue +77 -33
- package/components/ExtensionPanel.vue +42 -0
- package/components/GrowlManager.vue +3 -3
- package/components/IconOrSvg.vue +178 -0
- package/components/LogItem.vue +69 -0
- package/components/PodSecurityAdmission.vue +302 -0
- package/components/PromptModal.vue +1 -0
- package/components/ResourceDetail/Masthead.vue +69 -4
- package/components/ResourceDetail/index.vue +12 -5
- package/components/ResourceList/Masthead.vue +11 -1
- package/components/ResourceList/ResourceLoadingIndicator.vue +12 -2
- package/components/ResourceList/index.vue +66 -12
- package/components/ResourceList/resource-list.config.js +7 -0
- package/components/ResourceTable.vue +33 -6
- package/components/SimpleBox.vue +1 -1
- package/components/SortableTable/THead.vue +21 -14
- package/components/SortableTable/filtering.js +1 -1
- package/components/SortableTable/index.vue +21 -10
- package/components/SortableTable/selection.js +15 -3
- package/components/Tabbed/Tab.vue +1 -1
- package/components/Tabbed/index.vue +20 -15
- package/components/__tests__/.DS_Store +0 -0
- package/components/__tests__/AsyncButton.test.ts +140 -0
- package/components/__tests__/BackLink.test.ts +33 -0
- package/components/__tests__/ButtonGroup.test.ts +124 -0
- package/components/__tests__/ClusterBadge.test.ts +32 -0
- package/components/__tests__/CollapsibleCard.test.ts +64 -0
- package/components/__tests__/ConsumptionGauge.test.ts +88 -0
- package/components/__tests__/CruResource.test.ts +3 -2
- package/components/__tests__/FixedBanner.test.ts +129 -0
- package/components/__tests__/GrowlManager.test.ts +147 -0
- package/components/__tests__/NamespaceFilter.test.ts +33 -25
- package/components/__tests__/PercentageBar.test.ts +32 -0
- package/components/__tests__/PodSecurityAdmission.test.ts +398 -0
- package/components/auth/AuthBanner.vue +20 -10
- package/components/auth/RoleDetailEdit.vue +26 -17
- package/components/auth/SelectPrincipal.vue +36 -5
- package/components/form/ArrayList.vue +3 -35
- package/components/form/ArrayListGrouped.vue +13 -4
- package/components/form/ArrayListSelect.vue +5 -5
- package/components/form/Error.vue +8 -0
- package/components/form/KeyValue.vue +39 -7
- package/components/form/LabeledSelect.vue +5 -2
- package/components/form/Labels.vue +46 -16
- package/components/form/Members/ClusterPermissionsEditor.vue +17 -17
- package/components/form/Members/MembershipEditor.vue +12 -12
- package/components/form/NameNsDescription.vue +1 -1
- package/components/form/NodeScheduling.vue +1 -1
- package/components/form/Probe.vue +3 -3
- package/components/form/ResourceQuota/Project.vue +6 -6
- package/components/form/ResourceTabs/index.vue +24 -6
- package/components/form/Security.vue +7 -6
- package/components/form/Select.vue +3 -2
- package/components/form/SelectOrCreateAuthSecret.vue +22 -29
- package/components/form/ServicePorts.vue +8 -0
- package/components/form/WorkloadPorts.vue +7 -1
- package/components/form/__tests__/ArrayList.test.ts +74 -0
- package/components/form/__tests__/ArrayListGrouped.test.ts +6 -4
- package/components/formatter/Checked.vue +1 -1
- package/components/formatter/ClusterLink.vue +5 -0
- package/components/formatter/IconIsDefault.vue +2 -2
- package/components/formatter/InternalExternalIP.vue +11 -8
- package/components/formatter/LiveDuration.vue +78 -0
- package/components/formatter/WorkloadHealthScale.vue +5 -3
- package/components/nav/Header.vue +74 -7
- package/components/nav/NamespaceFilter.vue +146 -63
- package/components/nav/TopLevelMenu.vue +22 -19
- package/components/nav/WindowManager/ContainerLogs.vue +83 -126
- package/components/nav/WindowManager/ContainerShell.vue +9 -7
- package/components/nav/WindowManager/Window.vue +2 -0
- package/components/nav/WindowManager/index.vue +10 -0
- package/config/elemental-types.js +9 -0
- package/config/features.js +2 -0
- package/config/home-links.js +4 -1
- package/config/pod-security-admission.ts +82 -0
- package/config/product/apps.js +1 -1
- package/config/product/auth.js +6 -5
- package/config/product/backup.js +1 -1
- package/config/product/explorer.js +6 -6
- package/config/product/fleet.js +1 -1
- package/config/product/manager.js +6 -2
- package/config/query-params.js +1 -0
- package/config/secret.js +0 -1
- package/config/settings.ts +26 -9
- package/config/table-headers.js +22 -11
- package/config/types.js +4 -1
- package/config/uiplugins.js +3 -3
- package/content/docs/zh-hans/getting-started.md +113 -137
- package/content/docs/zh-hans/whats-new.md +8 -46
- package/core/plugin-helpers.js +171 -0
- package/core/plugin.ts +61 -1
- package/core/plugins.js +33 -0
- package/core/types.ts +128 -2
- package/creators/pkg/package-lock.json +37 -0
- package/creators/pkg/package.json +1 -1
- package/detail/catalog.cattle.io.app.vue +1 -1
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +35 -9
- package/detail/service.vue +2 -9
- package/detail/workload/index.vue +0 -1
- package/dialog/AddClusterMemberDialog.vue +22 -28
- package/dialog/AddProjectMemberDialog.vue +53 -9
- package/dialog/DiagnosticTimingsDialog.vue +8 -7
- package/dialog/DrainNode.vue +44 -48
- package/dialog/ForceMachineRemoveDialog.vue +5 -7
- package/dialog/GenericPrompt.vue +15 -20
- package/dialog/RollbackWorkloadDialog.vue +15 -46
- package/dialog/RotateCertificatesDialog.vue +5 -7
- package/dialog/RotateEncryptionKeyDialog.vue +5 -9
- package/dialog/SaveAsRKETemplateDialog.vue +5 -13
- package/dialog/ScaleMachineDownDialog.vue +1 -1
- package/dialog/ScalePoolDownDialog.vue +121 -0
- package/edit/__tests__/management.cattle.io.setting.test.ts +3 -3
- package/edit/auth/azuread.vue +16 -16
- package/edit/auth/github.vue +8 -0
- package/edit/auth/googleoauth.vue +10 -1
- package/edit/auth/ldap/index.vue +10 -0
- package/edit/auth/oidc.vue +10 -0
- package/edit/auth/saml.vue +10 -0
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -1
- package/edit/catalog.cattle.io.clusterrepo.vue +3 -0
- package/edit/cloudcredential.vue +3 -7
- package/edit/logging-flow/Match.vue +39 -8
- package/edit/logging-flow/index.vue +27 -4
- package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +107 -0
- package/edit/management.cattle.io.project.vue +8 -1
- package/edit/management.cattle.io.setting.vue +5 -2
- package/edit/management.cattle.io.user.vue +7 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +36 -8
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/email.vue +2 -2
- package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +14 -6
- package/edit/namespace.vue +18 -4
- package/edit/networking.k8s.io.ingress/Certificate.vue +1 -0
- package/edit/networking.k8s.io.ingress/IngressClass.vue +8 -6
- package/edit/networking.k8s.io.ingress/RulePath.vue +12 -6
- package/edit/networking.k8s.io.ingress/index.vue +8 -6
- package/edit/persistentvolume/index.vue +30 -27
- package/edit/persistentvolume/plugins/cephfs.vue +29 -29
- package/edit/persistentvolume/plugins/csi.vue +102 -62
- package/edit/persistentvolume/plugins/fc.vue +19 -19
- package/edit/persistentvolume/plugins/iscsi.vue +45 -45
- package/edit/persistentvolume/plugins/rbd.vue +39 -39
- package/edit/persistentvolumeclaim.vue +78 -75
- package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -7
- package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +10 -1
- package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +87 -27
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -6
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +96 -0
- package/edit/provisioning.cattle.io.cluster/import.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/index.vue +29 -6
- package/edit/provisioning.cattle.io.cluster/rke2.vue +445 -154
- package/edit/secret/index.vue +3 -7
- package/edit/service.vue +3 -1
- package/edit/storage.k8s.io.storageclass/index.vue +100 -16
- package/edit/storage.k8s.io.storageclass/provisioners/driver.harvesterhci.io.vue +114 -0
- package/edit/workload/__tests__/index.test.ts +98 -0
- package/edit/workload/index.vue +58 -8
- package/edit/workload/mixins/workload.js +107 -70
- package/edit/workload/storage/ContainerMountPaths.vue +0 -10
- package/edit/workload/storage/emptyDir.vue +88 -0
- package/edit/workload/storage/ephemeralVolume/index.vue +1 -1
- package/edit/workload/storage/index.vue +8 -0
- package/edit/workload/storage/persistentVolumeClaim/index.vue +1 -1
- package/layouts/default.vue +57 -44
- package/list/__tests__/workload.test.ts +5 -2
- package/list/catalog.cattle.io.app.vue +1 -0
- package/list/cis.cattle.io.clusterscan.vue +1 -0
- package/list/fleet.cattle.io.bundle.vue +5 -6
- package/list/fleet.cattle.io.cluster.vue +6 -3
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +5 -6
- package/list/fleet.cattle.io.gitrepo.vue +4 -9
- package/list/helm.cattle.io.projecthelmchart.vue +1 -5
- package/list/logging.banzaicloud.io.clusterflow.vue +4 -1
- package/list/logging.banzaicloud.io.flow.vue +6 -5
- package/list/management.cattle.io.cluster.vue +1 -0
- package/list/management.cattle.io.feature.vue +3 -4
- package/list/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +47 -0
- package/list/management.cattle.io.setting.vue +2 -2
- package/list/management.cattle.io.user.vue +4 -10
- package/list/monitoring.coreos.com.alertmanagerconfig.vue +2 -7
- package/list/node.vue +8 -5
- package/list/persistentvolume.vue +3 -3
- package/list/persistentvolumeclaim.vue +3 -4
- package/list/provisioning.cattle.io.cluster.vue +18 -19
- package/list/service.vue +6 -14
- package/list/workload.vue +43 -38
- package/machine-config/azure.vue +429 -60
- package/machine-config/pnap.vue +288 -0
- package/mixins/auth-config.js +1 -3
- package/mixins/browser-tab-visibility.js +8 -14
- package/mixins/chart.js +1 -1
- package/mixins/create-edit-view/impl.js +4 -0
- package/mixins/create-edit-view/index.js +4 -2
- package/mixins/resource-fetch-namespaced.js +98 -0
- package/mixins/resource-fetch.js +79 -45
- package/mixins/resource-manager.js +1 -23
- package/models/apps.controllerrevision.js +7 -0
- package/models/apps.daemonset.js +18 -0
- package/models/apps.deployment.js +44 -0
- package/models/apps.replicaset.js +7 -0
- package/models/apps.statefulset.js +18 -0
- package/models/batch.job.js +7 -14
- package/models/cluster/node.js +10 -2
- package/models/cluster.x-k8s.io.machine.js +26 -4
- package/models/cluster.x-k8s.io.machinedeployment.js +12 -2
- package/models/event.js +7 -0
- package/models/logging.banzaicloud.io.flow.js +4 -0
- package/models/management.cattle.io.cluster.js +1 -1
- package/models/management.cattle.io.clusterroletemplatebinding.js +1 -1
- package/models/management.cattle.io.globalrole.js +2 -2
- package/models/management.cattle.io.node.js +37 -2
- package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.ts +4 -0
- package/models/management.cattle.io.project.js +30 -11
- package/models/management.cattle.io.setting.js +1 -1
- package/models/management.cattle.io.user.js +37 -1
- package/models/namespace.js +42 -5
- package/models/persistentvolume.js +14 -2
- package/models/pod.js +15 -0
- package/models/projectroletemplatebinding.js +7 -0
- package/models/provisioning.cattle.io.cluster.js +61 -10
- package/models/rke-machine.cattle.io.pnapmachinetemplate.js +15 -0
- package/models/service.js +14 -13
- package/models/storage.k8s.io.storageclass.js +33 -18
- package/models/workload.js +38 -7
- package/nuxt.config.js +27 -17
- package/package.json +7 -7
- package/pages/about.vue +14 -2
- package/pages/c/_cluster/apps/charts/index.vue +21 -3
- package/pages/c/_cluster/apps/charts/install.vue +59 -22
- package/pages/c/_cluster/auth/config/_id.vue +6 -0
- package/pages/c/_cluster/auth/config/index.vue +8 -6
- package/pages/c/_cluster/auth/group.principal/assign-edit.vue +1 -1
- package/pages/c/_cluster/auth/roles/index.vue +1 -1
- package/pages/c/_cluster/explorer/index.vue +51 -6
- package/pages/c/_cluster/longhorn/index.vue +1 -1
- package/pages/c/_cluster/monitoring/alertmanagerconfig/_alertmanagerconfigid/receiver.vue +15 -4
- package/pages/c/_cluster/monitoring/index.vue +1 -1
- package/pages/c/_cluster/neuvector/index.vue +1 -1
- package/pages/c/_cluster/settings/performance.vue +48 -2
- package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +2 -0
- package/pages/c/_cluster/uiplugins/InstallDialog.vue +3 -0
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +42 -2
- package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +2 -0
- package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +1 -0
- package/pages/c/_cluster/uiplugins/UninstallDialog.vue +2 -0
- package/pages/c/_cluster/uiplugins/index.vue +42 -3
- package/pages/diagnostic.vue +5 -4
- package/pages/home.vue +105 -30
- package/pages/prefs.vue +23 -12
- package/pages/rio/mesh.vue +1 -1
- package/pkg/dynamic-importer.lib.js +8 -0
- package/pkg/vue.config.js +4 -0
- package/plugins/dashboard-store/__tests__/mutations.spec.js +406 -0
- package/plugins/dashboard-store/actions.js +32 -25
- package/plugins/dashboard-store/getters.js +50 -33
- package/plugins/dashboard-store/mutations.js +134 -28
- package/plugins/dashboard-store/resource-class.js +37 -42
- package/plugins/steve/actions.js +30 -0
- package/plugins/steve/caches/resourceCache.js +60 -0
- package/plugins/steve/getters.js +44 -1
- package/plugins/steve/mutations.js +97 -36
- package/plugins/steve/resourceWatcher.js +277 -0
- package/plugins/steve/schema.utils.js +25 -0
- package/plugins/steve/subscribe.js +288 -115
- package/plugins/steve/worker/index.js +17 -0
- package/plugins/steve/worker/web-worker.advanced.js +302 -0
- package/plugins/steve/{web-worker.steve-sub-worker.js → worker/web-worker.basic.js} +3 -44
- package/rancher-components/Card/Card.vue +3 -3
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +1 -0
- package/rancher-components/StringList/StringList.test.ts +45 -420
- package/rancher-components/StringList/StringList.vue +1 -10
- package/rancher-components/components/Banner/Banner.test.ts +44 -0
- package/rancher-components/components/Banner/Banner.vue +130 -61
- package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +13 -22
- package/rancher-components/components/Form/Checkbox/Checkbox.vue +8 -6
- package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +9 -9
- package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -1
- package/rancher-components/components/StringList/StringList.test.ts +7 -7
- package/rancher-components/components/StringList/StringList.vue +21 -15
- package/scripts/test-plugins-build.sh +8 -0
- package/static/loading-indicator.html +1 -1
- package/store/action-menu.js +4 -3
- package/store/index.js +54 -3
- package/store/plugins.js +0 -17
- package/store/pnap.js +128 -0
- package/store/prefs.js +4 -2
- package/store/type-map.js +81 -13
- package/types/pod-security-admission.ts +36 -0
- package/types/shell/index.d.ts +497 -396
- package/utils/__tests__/object.test.ts +17 -1
- package/utils/__tests__/pod-security-admission.test.ts +61 -0
- package/utils/async.ts +36 -0
- package/utils/color.js +45 -0
- package/utils/crypto/browserHashUtils.js +18 -0
- package/utils/dynamic-importer.js +8 -0
- package/utils/install-redirect.js +1 -1
- package/utils/object.js +24 -0
- package/utils/pod-security-admission.ts +39 -0
- package/utils/socket.js +61 -24
- package/utils/string.js +2 -0
- package/utils/svg-filter.js +301 -0
- package/utils/time.js +49 -0
- package/utils/validators/cidr.js +4 -0
- package/utils/validators/formRules/__tests__/index.test.ts +23 -3
- package/utils/validators/formRules/index.ts +14 -0
- package/config/product/harvester-manager.js +0 -162
- package/edit/harvesterhci.io.management.cluster.vue +0 -153
- package/list/harvesterhci.io.management.cluster.vue +0 -241
- package/machine-config/harvester.vue +0 -693
- package/models/harvesterhci.io.management.cluster.js +0 -228
- package/pages/c/_cluster/harvesterManager/index.vue +0 -24
- package/rancher-components/Card/Card.test.ts +0 -39
- package/rancher-components/Utils/DraggableZone/DraggableZone.vue +0 -181
- package/rancher-components/Utils/DraggableZone/index.ts +0 -1
- package/rancher-components/components/Utils/DraggableZone/index.ts +0 -1
|
@@ -304,6 +304,13 @@ export default {
|
|
|
304
304
|
useQueryParamsForSimpleFiltering: {
|
|
305
305
|
type: Boolean,
|
|
306
306
|
default: false
|
|
307
|
+
},
|
|
308
|
+
/**
|
|
309
|
+
* Manaul force the update of live and delayed cells. Change this number to kick off the update
|
|
310
|
+
*/
|
|
311
|
+
forceUpdateLiveAndDelayed: {
|
|
312
|
+
type: Number,
|
|
313
|
+
default: 0
|
|
307
314
|
}
|
|
308
315
|
},
|
|
309
316
|
|
|
@@ -389,6 +396,9 @@ export default {
|
|
|
389
396
|
page(neu, old) {
|
|
390
397
|
this.watcherUpdateLiveAndDelayed(neu, old);
|
|
391
398
|
},
|
|
399
|
+
forceUpdateLiveAndDelayed(neu, old) {
|
|
400
|
+
this.watcherUpdateLiveAndDelayed(neu, old);
|
|
401
|
+
},
|
|
392
402
|
|
|
393
403
|
// Ensure we update live and delayed columns on first load
|
|
394
404
|
initalLoad: {
|
|
@@ -401,16 +411,14 @@ export default {
|
|
|
401
411
|
immediate: true
|
|
402
412
|
},
|
|
403
413
|
|
|
404
|
-
|
|
414
|
+
// this is the flag that indicates that manual refresh data has been loaded
|
|
415
|
+
// and we should update the deferred cols
|
|
416
|
+
manualRefreshLoadingFinished: {
|
|
405
417
|
handler(neu, old) {
|
|
406
|
-
this
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
if (old && !neu) {
|
|
411
|
-
this.manualRefreshTimer = setTimeout(() => {
|
|
412
|
-
this.watcherUpdateLiveAndDelayed(neu, old);
|
|
413
|
-
}, 1000);
|
|
418
|
+
// this is merely to update the manual refresh button status
|
|
419
|
+
this.currentPhase = !neu ? ASYNC_BUTTON_STATES.WAITING : ASYNC_BUTTON_STATES.ACTION;
|
|
420
|
+
if (neu && neu !== old) {
|
|
421
|
+
this.$nextTick(() => this.updateLiveAndDelayed());
|
|
414
422
|
}
|
|
415
423
|
},
|
|
416
424
|
immediate: true
|
|
@@ -432,6 +440,10 @@ export default {
|
|
|
432
440
|
return !!(!this.loading && !this._didinit && this.rows?.length);
|
|
433
441
|
},
|
|
434
442
|
|
|
443
|
+
manualRefreshLoadingFinished() {
|
|
444
|
+
return !!(!this.loading && this._didinit && this.rows?.length && !this.isManualRefreshLoading);
|
|
445
|
+
},
|
|
446
|
+
|
|
435
447
|
fullColspan() {
|
|
436
448
|
let span = 0;
|
|
437
449
|
|
|
@@ -1530,7 +1542,6 @@ export default {
|
|
|
1530
1542
|
.actions.role-multi-action {
|
|
1531
1543
|
background-color: transparent;
|
|
1532
1544
|
border: none;
|
|
1533
|
-
font-size: 18px;
|
|
1534
1545
|
&:hover, &:focus {
|
|
1535
1546
|
background-color: var(--accent-btn);
|
|
1536
1547
|
box-shadow: none;
|
|
@@ -504,7 +504,7 @@ export default {
|
|
|
504
504
|
},
|
|
505
505
|
|
|
506
506
|
applyTableAction(action, args, event) {
|
|
507
|
-
const opts = { alt: event && isAlternate(event) };
|
|
507
|
+
const opts = { alt: event && isAlternate(event), event };
|
|
508
508
|
|
|
509
509
|
// Go through the table selection and filter out those actions that can't run the chosen action
|
|
510
510
|
const executableSelection = this.selectedRows.filter((row) => {
|
|
@@ -513,7 +513,7 @@ export default {
|
|
|
513
513
|
return matchingResourceAction?.enabled;
|
|
514
514
|
});
|
|
515
515
|
|
|
516
|
-
_execute(executableSelection, action, args, opts);
|
|
516
|
+
_execute(executableSelection, action, args, opts, this);
|
|
517
517
|
|
|
518
518
|
this.actionOfInterest = null;
|
|
519
519
|
},
|
|
@@ -575,8 +575,20 @@ function _filter(map, disableAll = false) {
|
|
|
575
575
|
return out;
|
|
576
576
|
}
|
|
577
577
|
|
|
578
|
-
function _execute(resources, action, args, opts = {}) {
|
|
578
|
+
function _execute(resources, action, args, opts = {}, ctx) {
|
|
579
579
|
args = args || [];
|
|
580
|
+
|
|
581
|
+
// New pattern for extensions - always call invoke
|
|
582
|
+
if (action.invoke) {
|
|
583
|
+
const actionOpts = {
|
|
584
|
+
action,
|
|
585
|
+
event: opts.event,
|
|
586
|
+
isAlt: !!opts.alt,
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
return action.invoke.apply(ctx, [actionOpts, resources || [], args]);
|
|
590
|
+
}
|
|
591
|
+
|
|
580
592
|
if ( resources.length > 1 && action.bulkAction && !opts.alt ) {
|
|
581
593
|
const fn = resources[0][action.bulkAction];
|
|
582
594
|
|
|
@@ -92,12 +92,12 @@ export default {
|
|
|
92
92
|
sortedTabs(tabs) {
|
|
93
93
|
const {
|
|
94
94
|
defaultTab,
|
|
95
|
-
useHash
|
|
96
|
-
$route: { hash }
|
|
95
|
+
useHash
|
|
97
96
|
} = this;
|
|
98
97
|
const activeTab = tabs.find(t => t.active);
|
|
99
98
|
|
|
100
|
-
const
|
|
99
|
+
const hash = useHash ? this.$route.hash : undefined;
|
|
100
|
+
const windowHash = useHash ? hash.slice(1) : undefined;
|
|
101
101
|
const windowHashTabMatch = tabs.find(t => t.name === windowHash && !t.active);
|
|
102
102
|
const firstTab = head(tabs) || null;
|
|
103
103
|
|
|
@@ -148,11 +148,7 @@ export default {
|
|
|
148
148
|
},
|
|
149
149
|
|
|
150
150
|
select(name/* , event */) {
|
|
151
|
-
const {
|
|
152
|
-
sortedTabs,
|
|
153
|
-
$route: { hash: routeHash },
|
|
154
|
-
$router: { currentRoute },
|
|
155
|
-
} = this;
|
|
151
|
+
const { sortedTabs } = this;
|
|
156
152
|
|
|
157
153
|
const selected = this.find(name);
|
|
158
154
|
const hashName = `#${ name }`;
|
|
@@ -160,13 +156,22 @@ export default {
|
|
|
160
156
|
if ( !selected || selected.disabled) {
|
|
161
157
|
return;
|
|
162
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Exclude logic with URL anchor (hash) for projects without routing logic (vue-router)
|
|
161
|
+
*/
|
|
162
|
+
if ( this.useHash ) {
|
|
163
|
+
const {
|
|
164
|
+
$route: { hash: routeHash },
|
|
165
|
+
$router: { currentRoute },
|
|
166
|
+
} = this;
|
|
163
167
|
|
|
164
|
-
|
|
165
|
-
|
|
168
|
+
if (this.useHash && routeHash !== hashName) {
|
|
169
|
+
const kurrentRoute = { ...currentRoute };
|
|
166
170
|
|
|
167
|
-
|
|
171
|
+
kurrentRoute.hash = hashName;
|
|
168
172
|
|
|
169
|
-
|
|
173
|
+
this.$router.replace(kurrentRoute);
|
|
174
|
+
}
|
|
170
175
|
}
|
|
171
176
|
|
|
172
177
|
for ( const tab of sortedTabs ) {
|
|
@@ -251,7 +256,7 @@ export default {
|
|
|
251
256
|
<i
|
|
252
257
|
v-if="hasIcon(tab)"
|
|
253
258
|
v-tooltip="t('validation.tab')"
|
|
254
|
-
class="conditions-alert-icon icon-error
|
|
259
|
+
class="conditions-alert-icon icon-error"
|
|
255
260
|
/>
|
|
256
261
|
</a>
|
|
257
262
|
</li>
|
|
@@ -274,7 +279,7 @@ export default {
|
|
|
274
279
|
class="btn bg-transparent"
|
|
275
280
|
@click="tabAddClicked"
|
|
276
281
|
>
|
|
277
|
-
<i class="icon icon-plus
|
|
282
|
+
<i class="icon icon-plus" />
|
|
278
283
|
</button>
|
|
279
284
|
<button
|
|
280
285
|
type="button"
|
|
@@ -282,7 +287,7 @@ export default {
|
|
|
282
287
|
:disabled="!sortedTabs.length"
|
|
283
288
|
@click="tabRemoveClicked"
|
|
284
289
|
>
|
|
285
|
-
<i class="icon icon-minus
|
|
290
|
+
<i class="icon icon-minus" />
|
|
286
291
|
</button>
|
|
287
292
|
</li>
|
|
288
293
|
</ul>
|
|
Binary file
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { mount, Wrapper } from '@vue/test-utils';
|
|
2
|
+
import AsyncButton, { ASYNC_BUTTON_STATES } from '@shell/components/AsyncButton.vue';
|
|
3
|
+
|
|
4
|
+
describe('component: AsyncButton', () => {
|
|
5
|
+
it('should render appropriately with default config', () => {
|
|
6
|
+
const mockExists = jest.fn().mockReturnValue(true);
|
|
7
|
+
const mockT = jest.fn().mockReturnValue('some-string');
|
|
8
|
+
|
|
9
|
+
const wrapper: Wrapper<InstanceType<typeof AsyncButton> & { [key: string]: any }> = mount(AsyncButton, {
|
|
10
|
+
mocks: {
|
|
11
|
+
$store: {
|
|
12
|
+
getters: {
|
|
13
|
+
'i18n/exists': mockExists,
|
|
14
|
+
'i18n/t': mockT
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const button = wrapper.find('button');
|
|
21
|
+
const icon = wrapper.find('i');
|
|
22
|
+
const span = wrapper.find('span');
|
|
23
|
+
|
|
24
|
+
expect(wrapper.props().currentPhase).toBe(ASYNC_BUTTON_STATES.ACTION);
|
|
25
|
+
|
|
26
|
+
expect(button.exists()).toBe(true);
|
|
27
|
+
expect(button.classes()).toContain('btn');
|
|
28
|
+
expect(button.classes()).toContain('role-primary');
|
|
29
|
+
expect(button.props().name).toBeNull();
|
|
30
|
+
expect(button.props().type).toBe('button');
|
|
31
|
+
expect(button.props().disabled).toBe(false);
|
|
32
|
+
expect(button.props().tabIndex).toBeNull();
|
|
33
|
+
// we are mocking the getters, so it's to expect to find an icon
|
|
34
|
+
expect(icon.exists()).toBe(true);
|
|
35
|
+
expect(icon.classes()).toContain('icon');
|
|
36
|
+
expect(icon.classes()).toContain('icon-lg');
|
|
37
|
+
expect(icon.classes()).toContain('icon-some-string');
|
|
38
|
+
// we are mocking the getters, so it's to expect to find a label
|
|
39
|
+
expect(span.exists()).toBe(true);
|
|
40
|
+
expect(span.text()).toBe('some-string');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('click on async button should emit click with a proper state of waiting, disabled and spinning ::: CB true', () => {
|
|
44
|
+
jest.useFakeTimers();
|
|
45
|
+
|
|
46
|
+
const wrapper: Wrapper<InstanceType<typeof AsyncButton> & { [key: string]: any }> = mount(AsyncButton, {
|
|
47
|
+
mocks: {
|
|
48
|
+
$store: {
|
|
49
|
+
getters: {
|
|
50
|
+
'i18n/exists': jest.fn(),
|
|
51
|
+
'i18n/t': jest.fn()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const spyDone = jest.spyOn(wrapper.vm, 'done');
|
|
58
|
+
|
|
59
|
+
wrapper.find('button').trigger('click');
|
|
60
|
+
|
|
61
|
+
expect(wrapper.emitted('click')).toHaveLength(1);
|
|
62
|
+
expect(wrapper.vm.phase).toBe(ASYNC_BUTTON_STATES.WAITING);
|
|
63
|
+
expect(wrapper.vm.isSpinning).toBe(true);
|
|
64
|
+
expect(wrapper.vm.isDisabled).toBe(true);
|
|
65
|
+
// testing cb function has been emitted
|
|
66
|
+
expect(typeof wrapper.emitted('click')![0][0]).toBe('function');
|
|
67
|
+
|
|
68
|
+
// trigger the cb function so that we test state changes on AsyncButton
|
|
69
|
+
wrapper.emitted('click')![0][0](true);
|
|
70
|
+
|
|
71
|
+
expect(spyDone).toHaveBeenCalledWith(true);
|
|
72
|
+
expect(wrapper.vm.phase).toBe(ASYNC_BUTTON_STATES.SUCCESS);
|
|
73
|
+
|
|
74
|
+
// wait for button delay to be completed
|
|
75
|
+
jest.runAllTimers();
|
|
76
|
+
|
|
77
|
+
expect(wrapper.vm.phase).toBe(ASYNC_BUTTON_STATES.ACTION);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('click on async button should emit click and update state properly ::: CB false', () => {
|
|
81
|
+
jest.useFakeTimers();
|
|
82
|
+
|
|
83
|
+
const wrapper: Wrapper<InstanceType<typeof AsyncButton> & { [key: string]: any }> = mount(AsyncButton, {
|
|
84
|
+
mocks: {
|
|
85
|
+
$store: {
|
|
86
|
+
getters: {
|
|
87
|
+
'i18n/exists': jest.fn(),
|
|
88
|
+
'i18n/t': jest.fn()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const spyDone = jest.spyOn(wrapper.vm, 'done');
|
|
95
|
+
|
|
96
|
+
wrapper.find('button').trigger('click');
|
|
97
|
+
|
|
98
|
+
expect(wrapper.emitted('click')).toHaveLength(1);
|
|
99
|
+
// testing cb function has been emitted
|
|
100
|
+
expect(typeof wrapper.emitted('click')![0][0]).toBe('function');
|
|
101
|
+
|
|
102
|
+
// trigger the cb function so that we test state changes on AsyncButton
|
|
103
|
+
wrapper.emitted('click')![0][0](false);
|
|
104
|
+
|
|
105
|
+
expect(spyDone).toHaveBeenCalledWith(false);
|
|
106
|
+
expect(wrapper.vm.phase).toBe(ASYNC_BUTTON_STATES.ERROR);
|
|
107
|
+
|
|
108
|
+
// wait for button delay to be completed
|
|
109
|
+
jest.runAllTimers();
|
|
110
|
+
|
|
111
|
+
expect(wrapper.vm.phase).toBe(ASYNC_BUTTON_STATES.ACTION);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('click on async button should emit click and update state properly ::: CB "cancelled"', () => {
|
|
115
|
+
const wrapper: Wrapper<InstanceType<typeof AsyncButton> & { [key: string]: any }> = mount(AsyncButton, {
|
|
116
|
+
mocks: {
|
|
117
|
+
$store: {
|
|
118
|
+
getters: {
|
|
119
|
+
'i18n/exists': jest.fn(),
|
|
120
|
+
'i18n/t': jest.fn()
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const spyDone = jest.spyOn(wrapper.vm, 'done');
|
|
127
|
+
|
|
128
|
+
wrapper.find('button').trigger('click');
|
|
129
|
+
|
|
130
|
+
expect(wrapper.emitted('click')).toHaveLength(1);
|
|
131
|
+
// testing cb function has been emitted
|
|
132
|
+
expect(typeof wrapper.emitted('click')![0][0]).toBe('function');
|
|
133
|
+
|
|
134
|
+
// trigger the cb function so that we test state changes on AsyncButton
|
|
135
|
+
wrapper.emitted('click')![0][0]('cancelled');
|
|
136
|
+
|
|
137
|
+
expect(spyDone).toHaveBeenCalledWith('cancelled');
|
|
138
|
+
expect(wrapper.vm.phase).toBe(ASYNC_BUTTON_STATES.ACTION);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
|
|
2
|
+
import BackLink from '@shell/components/BackLink.vue';
|
|
3
|
+
|
|
4
|
+
describe('component: BackLink', () => {
|
|
5
|
+
it('should render component with the correct data applied', () => {
|
|
6
|
+
const linkRoute = {
|
|
7
|
+
name: 'some-route-name',
|
|
8
|
+
params: { param1: 'paramval1' },
|
|
9
|
+
query: {
|
|
10
|
+
query1: 'queryval1',
|
|
11
|
+
query2: 'queryval2'
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const wrapper = shallowMount(BackLink, {
|
|
16
|
+
propsData: { link: linkRoute },
|
|
17
|
+
stubs: { NuxtLink: RouterLinkStub }
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const link = wrapper.findComponent(RouterLinkStub);
|
|
21
|
+
const icon = wrapper.find('i');
|
|
22
|
+
|
|
23
|
+
expect(link.exists()).toBe(true);
|
|
24
|
+
// assertion regarding the text will have to be pointed to whatever t() returns on the test
|
|
25
|
+
expect(link.text()).toBe('%generic.back%');
|
|
26
|
+
expect(link.classes()).toContain('back-link');
|
|
27
|
+
expect(link.props().to).toBe(linkRoute);
|
|
28
|
+
|
|
29
|
+
expect(icon.exists()).toBe(true);
|
|
30
|
+
expect(icon.classes()).toContain('icon');
|
|
31
|
+
expect(icon.classes()).toContain('icon-chevron-left');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import ButtonGroup from '@shell/components/ButtonGroup.vue';
|
|
3
|
+
|
|
4
|
+
describe('component: ButtonGroup', () => {
|
|
5
|
+
it('should render component with the default props correctly', () => {
|
|
6
|
+
const options = [
|
|
7
|
+
{
|
|
8
|
+
label: 'label1',
|
|
9
|
+
value: 'val1'
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
label: 'label2',
|
|
13
|
+
value: 'val2'
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
label: 'label3',
|
|
17
|
+
value: 'val3'
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const wrapper = shallowMount(ButtonGroup, {
|
|
22
|
+
propsData: {
|
|
23
|
+
options,
|
|
24
|
+
value: 'val1'
|
|
25
|
+
},
|
|
26
|
+
directives: { 'trim-whitespace': jest.fn() },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const firstBtn = wrapper.find('[data-testid="button-group-child-0"]');
|
|
30
|
+
const firstBtnLabel = wrapper.find('[data-testid="button-group-child-0"] span');
|
|
31
|
+
const secondBtn = wrapper.find('[data-testid="button-group-child-1"]');
|
|
32
|
+
const secondBtnLabel = wrapper.find('[data-testid="button-group-child-1"] span');
|
|
33
|
+
const thirdBtn = wrapper.find('[data-testid="button-group-child-2"]');
|
|
34
|
+
|
|
35
|
+
expect(wrapper.findAll('button')).toHaveLength(3);
|
|
36
|
+
expect(firstBtn.exists()).toBe(true);
|
|
37
|
+
expect(firstBtn.attributes().type).toBe('button');
|
|
38
|
+
expect(firstBtn.classes()).toContain('btn');
|
|
39
|
+
expect(firstBtn.classes()).toContain('bg-primary');
|
|
40
|
+
expect(secondBtn.classes()).toContain('bg-disabled');
|
|
41
|
+
expect(thirdBtn.classes()).toContain('bg-disabled');
|
|
42
|
+
|
|
43
|
+
expect(firstBtnLabel.text()).toBe('label1');
|
|
44
|
+
expect(secondBtnLabel.text()).toBe('label2');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should render component with icon correctly', () => {
|
|
48
|
+
const options = [
|
|
49
|
+
{
|
|
50
|
+
label: 'label1',
|
|
51
|
+
value: 'val1',
|
|
52
|
+
icon: 'some-icon'
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: 'label2',
|
|
56
|
+
value: 'val2'
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: 'label3',
|
|
60
|
+
value: 'val3'
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const wrapper = shallowMount(ButtonGroup, {
|
|
65
|
+
propsData: {
|
|
66
|
+
options,
|
|
67
|
+
activeClass: 'bg-another-active-class',
|
|
68
|
+
inactiveClass: 'bg-some-inactive-class',
|
|
69
|
+
iconSize: 'xxxxl',
|
|
70
|
+
value: 'val1'
|
|
71
|
+
},
|
|
72
|
+
directives: { 'trim-whitespace': jest.fn() },
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const firstBtn = wrapper.find('[data-testid="button-group-child-0"]');
|
|
76
|
+
const firstBtnIcon = wrapper.find('[data-testid="button-group-child-0"] i');
|
|
77
|
+
const firstBtnLabel = wrapper.find('[data-testid="button-group-child-0"] span');
|
|
78
|
+
const secondBtn = wrapper.find('[data-testid="button-group-child-1"]');
|
|
79
|
+
|
|
80
|
+
expect(wrapper.findAll('button')).toHaveLength(3);
|
|
81
|
+
|
|
82
|
+
expect(firstBtn.classes()).toContain('bg-another-active-class');
|
|
83
|
+
expect(secondBtn.classes()).toContain('bg-some-inactive-class');
|
|
84
|
+
|
|
85
|
+
expect(firstBtnLabel.exists()).toBe(true);
|
|
86
|
+
expect(firstBtnIcon.exists()).toBe(true);
|
|
87
|
+
|
|
88
|
+
expect(firstBtnIcon.classes()).toContain('icon');
|
|
89
|
+
expect(firstBtnIcon.classes()).toContain('some-icon');
|
|
90
|
+
expect(firstBtnIcon.classes()).toContain('icon-xxxxl');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('clicking on button should emit event "input"', () => {
|
|
94
|
+
const options = [
|
|
95
|
+
{
|
|
96
|
+
label: 'label1',
|
|
97
|
+
value: 'val1'
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
label: 'label2',
|
|
101
|
+
value: 'val2'
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
label: 'label3',
|
|
105
|
+
value: 'val3'
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
const wrapper = shallowMount(ButtonGroup, {
|
|
110
|
+
propsData: {
|
|
111
|
+
options,
|
|
112
|
+
value: 'val1'
|
|
113
|
+
},
|
|
114
|
+
directives: { 'trim-whitespace': jest.fn() },
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const firstBtn = wrapper.find('[data-testid="button-group-child-0"]');
|
|
118
|
+
|
|
119
|
+
firstBtn.trigger('click');
|
|
120
|
+
|
|
121
|
+
expect(wrapper.emitted('input')).toHaveLength(1);
|
|
122
|
+
expect(wrapper.emitted('input')![0][0]).toBe('val1');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import ClusterBadge from '@shell/components/ClusterBadge.vue';
|
|
3
|
+
|
|
4
|
+
describe('component: ClusterBadge', () => {
|
|
5
|
+
it('should render component with the correct data applied', () => {
|
|
6
|
+
const clusterProp = {
|
|
7
|
+
badge: {
|
|
8
|
+
color: 'red',
|
|
9
|
+
textColor: 'blue',
|
|
10
|
+
text: 'some-text',
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const wrapper = shallowMount(ClusterBadge, { propsData: { cluster: clusterProp } });
|
|
15
|
+
|
|
16
|
+
const elem = wrapper.find('div');
|
|
17
|
+
|
|
18
|
+
expect(elem.classes()).toContain('cluster-badge');
|
|
19
|
+
expect(elem.attributes().style).toBe('background-color: red; color: blue;');
|
|
20
|
+
expect(elem.text()).toBe('some-text');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should NOT render component if there is no badge property in object', () => {
|
|
24
|
+
const clusterProp = {};
|
|
25
|
+
|
|
26
|
+
const wrapper = shallowMount(ClusterBadge, { propsData: { cluster: clusterProp } });
|
|
27
|
+
|
|
28
|
+
const elem = wrapper.find('div');
|
|
29
|
+
|
|
30
|
+
expect(elem.exists()).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import CollapsibleCard from '@shell/components/CollapsibleCard.vue';
|
|
3
|
+
|
|
4
|
+
describe('component: CollapsibleCard', () => {
|
|
5
|
+
it('should render component with the correct data applied', () => {
|
|
6
|
+
const wrapper = shallowMount(CollapsibleCard, {
|
|
7
|
+
propsData: { title: 'some-card-title' },
|
|
8
|
+
slots: {
|
|
9
|
+
'header-right': '<p>some stuff for header</p>',
|
|
10
|
+
content: '<p>some content</p>'
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const cardWrapper = wrapper.find('.collapsible-card');
|
|
15
|
+
const cardHeader = wrapper.find('.collapsible-card-header');
|
|
16
|
+
const cardBody = wrapper.find('.collapsible-card-body');
|
|
17
|
+
|
|
18
|
+
expect(cardWrapper.exists()).toBe(true);
|
|
19
|
+
|
|
20
|
+
expect(cardHeader.exists()).toBe(true);
|
|
21
|
+
expect(cardHeader.find('h2 span').text()).toBe('some-card-title');
|
|
22
|
+
expect(cardHeader.find('p').exists()).toBe(true);
|
|
23
|
+
expect(cardHeader.find('p').text()).toBe('some stuff for header');
|
|
24
|
+
expect(cardHeader.find('i').exists()).toBe(true);
|
|
25
|
+
expect(cardHeader.find('i').classes()).toContain('icon-chevron-up');
|
|
26
|
+
|
|
27
|
+
expect(cardBody.exists()).toBe(true);
|
|
28
|
+
expect(cardBody.find('p').exists()).toBe(true);
|
|
29
|
+
expect(cardBody.find('p').text()).toBe('some content');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('clicking on card header should emit event', () => {
|
|
33
|
+
const wrapper = shallowMount(CollapsibleCard, {
|
|
34
|
+
propsData: { title: 'some-card-title' },
|
|
35
|
+
slots: {
|
|
36
|
+
'header-right': '<p>some stuff for header</p>',
|
|
37
|
+
content: '<p>some content</p>'
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const cardHeader = wrapper.find('.collapsible-card-header');
|
|
42
|
+
|
|
43
|
+
cardHeader.trigger('click');
|
|
44
|
+
|
|
45
|
+
expect(wrapper.emitted('toggleCollapse')).toHaveLength(1);
|
|
46
|
+
expect(wrapper.emitted('toggleCollapse')![0][0]).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('clicking on card title should emit event', () => {
|
|
50
|
+
const wrapper = shallowMount(CollapsibleCard, {
|
|
51
|
+
propsData: { title: 'some-card-title', isTitleClickable: true },
|
|
52
|
+
slots: {
|
|
53
|
+
'header-right': '<p>some stuff for header</p>',
|
|
54
|
+
content: '<p>some content</p>'
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const cardHeaderTitle = wrapper.find('.collapsible-card-header h2 span');
|
|
59
|
+
|
|
60
|
+
cardHeaderTitle.trigger('click');
|
|
61
|
+
|
|
62
|
+
expect(wrapper.emitted('titleClick')).toHaveLength(1);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import ConsumptionGauge from '@shell/components/ConsumptionGauge.vue';
|
|
3
|
+
import PercentageBar from '@shell/components/PercentageBar.vue';
|
|
4
|
+
|
|
5
|
+
describe('component: ConsumptionGauge', () => {
|
|
6
|
+
it('should render component with the correct data applied', () => {
|
|
7
|
+
const colorStops = {
|
|
8
|
+
0: '--success', 30: '--warning', 70: '--error'
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const wrapper = mount(ConsumptionGauge, {
|
|
12
|
+
propsData: {
|
|
13
|
+
resourceName: 'some-resource-name',
|
|
14
|
+
capacity: 1000,
|
|
15
|
+
used: 200,
|
|
16
|
+
units: 'cores',
|
|
17
|
+
colorStops
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const mainWrapper = wrapper.find('.consumption-gauge');
|
|
22
|
+
const title = wrapper.find('.consumption-gauge h3');
|
|
23
|
+
const usedSpan = wrapper.find('.consumption-gauge .numbers span:nth-child(1)');
|
|
24
|
+
const percentageSpan = wrapper.find('.consumption-gauge .percentage');
|
|
25
|
+
const percentageBar = wrapper.findComponent(PercentageBar);
|
|
26
|
+
|
|
27
|
+
expect(mainWrapper.exists()).toBe(true);
|
|
28
|
+
expect(title.exists()).toBe(true);
|
|
29
|
+
expect(title.text()).toBe('some-resource-name');
|
|
30
|
+
expect(usedSpan.exists()).toBe(true);
|
|
31
|
+
// check translation key as for translation are not applied
|
|
32
|
+
expect(usedSpan.text()).toBe('%node.detail.glance.consumptionGauge.used%');
|
|
33
|
+
|
|
34
|
+
expect(percentageSpan.exists()).toBe(true);
|
|
35
|
+
expect(percentageSpan.text()).toContain('20%');
|
|
36
|
+
|
|
37
|
+
// checking PercentageBar component render
|
|
38
|
+
expect(percentageBar.exists()).toBe(true);
|
|
39
|
+
expect(percentageBar.props().value).toBe(20);
|
|
40
|
+
expect(percentageBar.props().colorStops).toBe(colorStops);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('usedAsResourceName should render secondary title instead of main h3 title', () => {
|
|
44
|
+
const colorStops = {
|
|
45
|
+
0: '--success', 30: '--warning', 70: '--error'
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const wrapper = mount(ConsumptionGauge, {
|
|
49
|
+
propsData: {
|
|
50
|
+
resourceName: 'some-resource-name',
|
|
51
|
+
capacity: 1000,
|
|
52
|
+
used: 200,
|
|
53
|
+
units: 'cores',
|
|
54
|
+
colorStops,
|
|
55
|
+
usedAsResourceName: true
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const mainTitle = wrapper.find('.consumption-gauge h3');
|
|
60
|
+
const slotTitle = wrapper.find('.consumption-gauge h4');
|
|
61
|
+
|
|
62
|
+
expect(mainTitle.exists()).toBe(false);
|
|
63
|
+
expect(slotTitle.exists()).toBe(true);
|
|
64
|
+
expect(slotTitle.text()).toBe('some-resource-name');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('passing slot TITLE should render correctly', () => {
|
|
68
|
+
const colorStops = {
|
|
69
|
+
0: '--success', 30: '--warning', 70: '--error'
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const wrapper = mount(ConsumptionGauge, {
|
|
73
|
+
propsData: {
|
|
74
|
+
resourceName: 'some-resource-name',
|
|
75
|
+
capacity: 1000,
|
|
76
|
+
used: 200,
|
|
77
|
+
units: 'cores',
|
|
78
|
+
colorStops
|
|
79
|
+
},
|
|
80
|
+
slots: { title: '<p class="slot-class">another title</p>' }
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const slotElem = wrapper.find('.consumption-gauge .slot-class');
|
|
84
|
+
|
|
85
|
+
expect(slotElem.exists()).toBe(true);
|
|
86
|
+
expect(slotElem.text()).toBe('another title');
|
|
87
|
+
});
|
|
88
|
+
});
|