@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.5
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 +1 -1
- package/assets/styles/global/_layout.scss +4 -0
- package/assets/translations/en-us.yaml +183 -51
- package/assets/translations/zh-hans.yaml +1 -7
- package/chart/monitoring/ClusterSelector.vue +0 -21
- package/chart/monitoring/prometheus/index.vue +6 -3
- package/components/ActionDropdownShell.vue +5 -3
- package/components/ButtonGroup.vue +26 -1
- package/components/CruResource.vue +212 -16
- package/components/ExplorerMembers.vue +8 -4
- package/components/ExplorerProjectsNamespaces.vue +10 -6
- package/components/GrowlManager.vue +4 -0
- package/components/MgmtNodeList.vue +184 -0
- package/components/PromptRestore.vue +93 -32
- package/components/Questions/index.vue +1 -0
- package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
- package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
- package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
- package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
- package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
- package/components/ResourceDetail/index.vue +1 -1
- package/components/ResourceList/Masthead.vue +7 -1
- package/components/ResourceList/index.vue +82 -1
- package/components/ResourceTable.vue +1 -0
- package/components/RichTranslation.vue +5 -2
- package/components/Setting.vue +1 -0
- package/components/SortableTable/index.vue +4 -3
- package/components/SubtleLink.vue +31 -6
- package/components/Tabbed/Tab.vue +29 -3
- package/components/Tabbed/index.vue +25 -3
- package/components/TableOfContents/TableOfContents.vue +109 -0
- package/components/TableOfContents/composables.ts +258 -0
- package/components/Window/ContainerShell.vue +21 -11
- package/components/Window/__tests__/ContainerShell.test.ts +107 -37
- package/components/Wizard.vue +23 -5
- package/components/__tests__/ButtonGroup.test.ts +56 -0
- package/components/__tests__/PromptRestore.test.ts +169 -19
- package/components/fleet/AppCoChartGrid.vue +401 -0
- package/components/fleet/AppCoEmptyState.vue +127 -0
- package/components/fleet/AppCoPageHeader.vue +119 -0
- package/components/fleet/AppCoVersionSelect.vue +70 -0
- package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
- package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
- package/components/fleet/FleetClusterTargets/index.vue +189 -146
- package/components/fleet/FleetIntro.vue +7 -3
- package/components/fleet/FleetNoWorkspaces.vue +7 -3
- package/components/fleet/FleetSecretSelector.vue +5 -3
- package/components/fleet/FleetValuesFrom.vue +8 -2
- package/components/fleet/GitRepoAdvancedTab.vue +1 -0
- package/components/fleet/GitRepoMetadataTab.vue +5 -0
- package/components/fleet/GitRepoTargetTab.vue +0 -2
- package/components/fleet/HelmOpAdvancedTab.vue +19 -53
- package/components/fleet/HelmOpAppCoConfigTab.vue +597 -0
- package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
- package/components/fleet/HelmOpMetadataTab.vue +5 -0
- package/components/fleet/HelmOpResourcesSection.vue +82 -0
- package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
- package/components/fleet/HelmOpTargetTab.vue +64 -60
- package/components/fleet/HelmOpValuesTab.vue +129 -105
- package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
- package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
- package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
- package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
- package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
- package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
- package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
- package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
- package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
- package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
- package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
- package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
- package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
- package/components/fleet/dashboard/Empty.vue +8 -4
- package/components/fleet/dashboard/ResourceCard.vue +28 -0
- package/components/fleet/dashboard/ResourceDetails.vue +28 -0
- package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
- package/components/form/ArrayList.vue +61 -4
- package/components/form/FileSelector.vue +39 -1
- package/components/form/KeyValue.vue +23 -2
- package/components/form/LabeledSelect.vue +39 -1
- package/components/form/Labels.vue +22 -3
- package/components/form/NameNsDescription.vue +13 -5
- package/components/form/PrivateRegistry.constants.ts +7 -0
- package/components/form/PrivateRegistry.vue +253 -18
- package/components/form/ResourceTabs/index.vue +1 -0
- package/components/form/SelectOrCreateAuthSecret.vue +140 -17
- package/components/form/__tests__/FileSelector.test.ts +23 -0
- package/components/form/__tests__/NameNsDescription.test.ts +75 -0
- package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
- package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
- package/components/formatter/EtcdSnapshotName.vue +73 -0
- package/components/formatter/InternalExternalIP.vue +10 -4
- package/components/formatter/ServiceTargets.vue +26 -7
- package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
- package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
- package/components/nav/Header.vue +12 -1
- package/components/nav/TopLevelMenu.vue +7 -2
- package/components/nav/__tests__/Header.test.ts +15 -0
- package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
- package/components/templates/default.vue +16 -4
- package/components/templates/home.vue +9 -4
- package/components/templates/plain.vue +9 -4
- package/composables/useHelmOpResources.test.ts +56 -0
- package/composables/useHelmOpResources.ts +32 -0
- package/composables/useStateColor.test.ts +325 -0
- package/composables/useStateColor.ts +128 -0
- package/config/features.js +1 -0
- package/config/home-links.js +1 -1
- package/config/labels-annotations.js +3 -0
- package/config/product/explorer.js +17 -4
- package/config/product/manager.js +8 -0
- package/config/router/index.js +16 -0
- package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
- package/config/router/navigation-guards/authentication.js +10 -4
- package/config/router/routes.js +20 -6
- package/config/secret.ts +10 -0
- package/config/settings.ts +6 -4
- package/config/table-headers.js +3 -4
- package/config/types.js +16 -0
- package/core/plugin-products-base.ts +3 -3
- package/core/plugin-types.ts +83 -30
- package/core/plugin.ts +3 -0
- package/core/types-provisioning.ts +34 -1
- package/core/types.ts +15 -2
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
- package/detail/__tests__/workload.test.ts +3 -152
- package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +109 -7
- package/detail/workload/index.vue +12 -55
- package/dialog/RotateEncryptionKeyDialog.vue +33 -9
- package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
- package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +206 -0
- package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
- package/edit/auth/__tests__/azuread.test.ts +34 -9
- package/edit/auth/__tests__/github.test.ts +234 -0
- package/edit/auth/__tests__/oidc.test.ts +26 -6
- package/edit/auth/__tests__/saml.test.ts +196 -0
- package/edit/auth/azuread.vue +128 -95
- package/edit/auth/github.vue +72 -13
- package/edit/auth/ldap/__tests__/index.test.ts +206 -0
- package/edit/auth/ldap/config.vue +8 -0
- package/edit/auth/ldap/index.vue +75 -1
- package/edit/auth/oidc.vue +119 -73
- package/edit/auth/saml.vue +76 -12
- package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
- package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
- package/edit/fleet.cattle.io.gitrepo.vue +70 -16
- package/edit/fleet.cattle.io.helmop.vue +542 -141
- package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
- package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
- package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
- package/edit/management.cattle.io.user.vue +5 -2
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +89 -11
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
- package/list/group.principal.vue +5 -4
- package/list/harvesterhci.io.management.cluster.vue +8 -9
- package/list/management.cattle.io.user.vue +12 -9
- package/list/provisioning.cattle.io.cluster.vue +16 -10
- package/mixins/__tests__/auth-config.test.ts +90 -0
- package/mixins/__tests__/chart.test.ts +94 -0
- package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
- package/mixins/auth-config.js +7 -0
- package/mixins/chart.js +11 -2
- package/mixins/child-hook.js +12 -6
- package/mixins/create-edit-view/impl.js +5 -3
- package/mixins/resource-fetch-api-pagination.js +21 -1
- package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
- package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
- package/models/__tests__/fleet-application.test.ts +175 -0
- package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
- package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
- package/models/__tests__/management.cattle.io.node.ts +22 -0
- package/models/__tests__/namespace.test.ts +36 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +205 -0
- package/models/__tests__/secret.test.ts +68 -1
- package/models/__tests__/workload.test.ts +401 -26
- package/models/catalog.cattle.io.clusterrepo.js +28 -4
- package/models/compliance.cattle.io.clusterscan.js +39 -4
- package/models/fleet-application.js +4 -0
- package/models/fleet.cattle.io.helmop.js +20 -1
- package/models/management.cattle.io.cluster.js +39 -5
- package/models/management.cattle.io.node.js +44 -3
- package/models/namespace.js +1 -1
- package/models/pod.js +46 -3
- package/models/provisioning.cattle.io.cluster.js +64 -14
- package/models/rke.cattle.io.etcdsnapshot.js +17 -9
- package/models/secret.js +19 -0
- package/models/workload.js +120 -20
- package/models/workload.service.js +5 -0
- package/package.json +14 -13
- package/pages/about.vue +5 -6
- package/pages/auth/login.vue +0 -35
- package/pages/auth/setup.vue +11 -0
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
- package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +485 -107
- package/pages/c/_cluster/apps/charts/chart.vue +2 -1
- package/pages/c/_cluster/apps/charts/index.vue +48 -10
- package/pages/c/_cluster/apps/charts/install.vue +236 -144
- package/pages/c/_cluster/auth/roles/index.vue +5 -4
- package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
- package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
- package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
- package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
- package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
- package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
- package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
- package/pages/c/_cluster/fleet/application/create.vue +187 -136
- package/pages/c/_cluster/fleet/application/index.vue +5 -3
- package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
- package/pages/c/_cluster/fleet/index.vue +2 -2
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
- package/pages/c/_cluster/uiplugins/index.vue +15 -0
- package/pages/fail-whale.vue +16 -11
- package/pages/home.vue +16 -46
- package/pkg/require-asset.lib.js +25 -0
- package/pkg/vue.config.js +7 -0
- package/plugins/clean-html.d.ts +9 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +177 -0
- package/plugins/dashboard-store/getters.js +0 -1
- package/plugins/dashboard-store/resource-class.js +114 -19
- package/plugins/steve/__tests__/actions.test.ts +212 -0
- package/plugins/steve/actions.js +96 -0
- package/plugins/steve/steve-pagination-utils.ts +1 -1
- package/rancher-components/Accordion/Accordion.vue +53 -9
- package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
- package/rancher-components/Form/Radio/RadioButton.vue +17 -1
- package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
- package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
- package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
- package/rancher-components/RcButton/RcButton.test.ts +103 -0
- package/rancher-components/RcButton/RcButton.vue +94 -15
- package/rancher-components/RcButton/index.ts +1 -1
- package/rancher-components/RcButton/types.ts +3 -0
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
- package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
- package/rancher-components/RcSection/RcSection.vue +28 -3
- package/scripts/extension/helm/package/Dockerfile +1 -1
- package/scripts/test-plugins-build.sh +2 -1
- package/store/__tests__/features.test.ts +131 -0
- package/store/__tests__/growl.test.ts +374 -0
- package/store/__tests__/modal.test.ts +131 -0
- package/store/__tests__/notifications.test.ts +434 -0
- package/store/__tests__/slideInPanel.test.ts +88 -0
- package/store/__tests__/type-map.utils.test.ts +433 -0
- package/store/catalog.js +57 -0
- package/store/features.js +4 -0
- package/store/plugins.js +7 -4
- package/types/components/buttonGroup.ts +5 -0
- package/types/shell/index.d.ts +166 -70
- package/utils/__tests__/auth.test.ts +273 -0
- package/utils/__tests__/computed.test.ts +193 -0
- package/utils/__tests__/cspAdaptor.test.ts +163 -0
- package/utils/__tests__/dom.test.ts +81 -0
- package/utils/__tests__/duration.test.ts +37 -1
- package/utils/__tests__/dynamic-importer.test.ts +102 -0
- package/utils/__tests__/fleet-appco.test.ts +312 -0
- package/utils/__tests__/monitoring.test.ts +130 -0
- package/utils/__tests__/object.test.ts +22 -0
- package/utils/__tests__/operation-cr.test.ts +34 -0
- package/utils/__tests__/platform.test.ts +91 -0
- package/utils/__tests__/position.test.ts +237 -0
- package/utils/__tests__/provider.test.ts +51 -1
- package/utils/__tests__/queue.test.ts +232 -0
- package/utils/__tests__/release-notes.test.ts +221 -0
- package/utils/__tests__/router.test.js +254 -1
- package/utils/__tests__/select.test.ts +208 -0
- package/utils/__tests__/time.test.ts +265 -1
- package/utils/__tests__/title.test.ts +47 -0
- package/utils/__tests__/width.test.ts +53 -0
- package/utils/__tests__/window.test.ts +158 -0
- package/utils/__tests__/xccdf.test.ts +126 -6
- package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
- package/utils/crypto/__tests__/index.test.ts +144 -0
- package/utils/duration.ts +104 -0
- package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
- package/utils/dynamic-content/info.ts +2 -1
- package/utils/error.js +13 -0
- package/utils/fleet-appco.ts +323 -0
- package/utils/object.js +22 -2
- package/utils/operation-cr.js +19 -0
- package/utils/provider.ts +12 -0
- package/utils/require-asset.ts +7 -0
- package/utils/validators/__tests__/container-images.test.ts +104 -0
- package/utils/validators/__tests__/flow-output.test.ts +91 -0
- package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
- package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
- package/utils/validators/__tests__/private-registry.test.ts +27 -15
- package/utils/validators/private-registry.ts +15 -4
- package/utils/xccdf.ts +39 -42
- package/vue.config.js +1 -1
- package/pages/support/index.vue +0 -264
- package/utils/duration.js +0 -43
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import {
|
|
2
|
+
checkPermissions,
|
|
3
|
+
parseAuthProvidersInfo,
|
|
4
|
+
returnTo,
|
|
5
|
+
} from '@shell/utils/auth';
|
|
6
|
+
|
|
7
|
+
describe('parseAuthProvidersInfo', () => {
|
|
8
|
+
it.each([
|
|
9
|
+
{
|
|
10
|
+
desc: 'empty rows',
|
|
11
|
+
rows: [],
|
|
12
|
+
expected: {
|
|
13
|
+
nonLocal: [],
|
|
14
|
+
enabled: [],
|
|
15
|
+
enabledLocation: null,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
desc: 'only local provider',
|
|
20
|
+
rows: [{
|
|
21
|
+
name: 'local', id: 'local', enabled: true
|
|
22
|
+
}],
|
|
23
|
+
expected: {
|
|
24
|
+
nonLocal: [],
|
|
25
|
+
enabled: [],
|
|
26
|
+
enabledLocation: null,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
desc: 'one disabled non-local provider',
|
|
31
|
+
rows: [{
|
|
32
|
+
name: 'github', id: 'github', enabled: false
|
|
33
|
+
}],
|
|
34
|
+
expected: {
|
|
35
|
+
nonLocal: [{
|
|
36
|
+
name: 'github', id: 'github', enabled: false
|
|
37
|
+
}],
|
|
38
|
+
enabled: [],
|
|
39
|
+
enabledLocation: null,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
desc: 'one enabled non-local provider',
|
|
44
|
+
rows: [{
|
|
45
|
+
name: 'github', id: 'github', enabled: true
|
|
46
|
+
}],
|
|
47
|
+
expected: {
|
|
48
|
+
nonLocal: [{
|
|
49
|
+
name: 'github', id: 'github', enabled: true
|
|
50
|
+
}],
|
|
51
|
+
enabled: [{
|
|
52
|
+
name: 'github', id: 'github', enabled: true
|
|
53
|
+
}],
|
|
54
|
+
enabledLocation: {
|
|
55
|
+
name: 'c-cluster-auth-config-id',
|
|
56
|
+
params: { id: 'github' },
|
|
57
|
+
query: { mode: 'edit' },
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
desc: 'oidc provider excluded from nonLocal but included in enabled',
|
|
63
|
+
rows: [{
|
|
64
|
+
name: 'oidc', id: 'oidc', enabled: true
|
|
65
|
+
}],
|
|
66
|
+
expected: {
|
|
67
|
+
nonLocal: [],
|
|
68
|
+
enabled: [{
|
|
69
|
+
name: 'oidc', id: 'oidc', enabled: true
|
|
70
|
+
}],
|
|
71
|
+
enabledLocation: {
|
|
72
|
+
name: 'c-cluster-auth-config-id',
|
|
73
|
+
params: { id: 'oidc' },
|
|
74
|
+
query: { mode: 'edit' },
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
desc: 'two enabled non-local providers gives null enabledLocation',
|
|
80
|
+
rows: [
|
|
81
|
+
{
|
|
82
|
+
name: 'github', id: 'github', enabled: true
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'activedirectory', id: 'activedirectory', enabled: true
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
expected: {
|
|
89
|
+
nonLocal: [
|
|
90
|
+
{
|
|
91
|
+
name: 'github', id: 'github', enabled: true
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'activedirectory', id: 'activedirectory', enabled: true
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
enabled: [
|
|
98
|
+
{
|
|
99
|
+
name: 'github', id: 'github', enabled: true
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'activedirectory', id: 'activedirectory', enabled: true
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
enabledLocation: null,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
desc: 'local provider excluded while non-local disabled provider is retained',
|
|
110
|
+
rows: [
|
|
111
|
+
{
|
|
112
|
+
name: 'local', id: 'local', enabled: true
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'github', id: 'github', enabled: false
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
expected: {
|
|
119
|
+
nonLocal: [{
|
|
120
|
+
name: 'github', id: 'github', enabled: false
|
|
121
|
+
}],
|
|
122
|
+
enabled: [],
|
|
123
|
+
enabledLocation: null,
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
])('returns provider info for $desc', ({ rows, expected }) => {
|
|
127
|
+
expect(parseAuthProvidersInfo(rows)).toStrictEqual(expected);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('checkPermissions', () => {
|
|
132
|
+
it('returns empty object for empty types', async() => {
|
|
133
|
+
const getters = { 'management/schemaFor': jest.fn() };
|
|
134
|
+
const result = await checkPermissions({}, getters);
|
|
135
|
+
|
|
136
|
+
expect(result).toStrictEqual({});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('returns false when schema is not found', async() => {
|
|
140
|
+
const getters = { 'management/schemaFor': jest.fn().mockReturnValue(null) };
|
|
141
|
+
const result = await checkPermissions({ pods: { type: 'pod' } }, getters);
|
|
142
|
+
|
|
143
|
+
expect(result).toStrictEqual({ pods: false });
|
|
144
|
+
expect(getters['management/schemaFor']).toHaveBeenCalledWith('pod');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('returns true when schema exists with no method constraints', async() => {
|
|
148
|
+
const mockSchema = { resourceMethods: ['GET', 'PUT'] };
|
|
149
|
+
const getters = { 'management/schemaFor': jest.fn().mockReturnValue(mockSchema) };
|
|
150
|
+
const result = await checkPermissions({ pods: { type: 'pod' } }, getters);
|
|
151
|
+
|
|
152
|
+
expect(result).toStrictEqual({ pods: true });
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('uses schemaValidator result when provided', async() => {
|
|
156
|
+
const mockSchema = { resourceMethods: ['GET'] };
|
|
157
|
+
const schemaValidator = jest.fn().mockReturnValue(false);
|
|
158
|
+
const getters = { 'management/schemaFor': jest.fn().mockReturnValue(mockSchema) };
|
|
159
|
+
const result = await checkPermissions({ pods: { type: 'pod', schemaValidator } }, getters);
|
|
160
|
+
|
|
161
|
+
expect(result).toStrictEqual({ pods: false });
|
|
162
|
+
expect(schemaValidator).toHaveBeenCalledWith(mockSchema);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('returns true when all resourceMethods are available', async() => {
|
|
166
|
+
const mockSchema = { resourceMethods: ['GET', 'PUT', 'DELETE'] };
|
|
167
|
+
const getters = { 'management/schemaFor': jest.fn().mockReturnValue(mockSchema) };
|
|
168
|
+
const types = { pods: { type: 'pod', resourceMethods: ['GET', 'PUT'] } };
|
|
169
|
+
const result = await checkPermissions(types, getters);
|
|
170
|
+
|
|
171
|
+
expect(result).toStrictEqual({ pods: true });
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('returns false when a resourceMethod is not in schema', async() => {
|
|
175
|
+
const mockSchema = { resourceMethods: ['GET'] };
|
|
176
|
+
const getters = { 'management/schemaFor': jest.fn().mockReturnValue(mockSchema) };
|
|
177
|
+
const types = { pods: { type: 'pod', resourceMethods: ['GET', 'DELETE'] } };
|
|
178
|
+
const result = await checkPermissions(types, getters);
|
|
179
|
+
|
|
180
|
+
expect(result).toStrictEqual({ pods: false });
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('returns false when schema has no resourceMethods and type requires them', async() => {
|
|
184
|
+
const mockSchema = {};
|
|
185
|
+
const getters = { 'management/schemaFor': jest.fn().mockReturnValue(mockSchema) };
|
|
186
|
+
const types = { pods: { type: 'pod', resourceMethods: ['GET'] } };
|
|
187
|
+
const result = await checkPermissions(types, getters);
|
|
188
|
+
|
|
189
|
+
expect(result).toStrictEqual({ pods: false });
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('returns true when all collectionMethods are available', async() => {
|
|
193
|
+
const mockSchema = { collectionMethods: ['GET', 'POST'] };
|
|
194
|
+
const getters = { 'management/schemaFor': jest.fn().mockReturnValue(mockSchema) };
|
|
195
|
+
const types = { pods: { type: 'pod', collectionMethods: ['GET'] } };
|
|
196
|
+
const result = await checkPermissions(types, getters);
|
|
197
|
+
|
|
198
|
+
expect(result).toStrictEqual({ pods: true });
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('returns false when a collectionMethod is not in schema', async() => {
|
|
202
|
+
const mockSchema = { collectionMethods: ['GET'] };
|
|
203
|
+
const getters = { 'management/schemaFor': jest.fn().mockReturnValue(mockSchema) };
|
|
204
|
+
const types = { pods: { type: 'pod', collectionMethods: ['GET', 'DELETE'] } };
|
|
205
|
+
const result = await checkPermissions(types, getters);
|
|
206
|
+
|
|
207
|
+
expect(result).toStrictEqual({ pods: false });
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('handles multiple types independently', async() => {
|
|
211
|
+
const getters = {
|
|
212
|
+
'management/schemaFor': jest.fn()
|
|
213
|
+
.mockReturnValueOnce({ resourceMethods: ['GET'] })
|
|
214
|
+
.mockReturnValueOnce(null),
|
|
215
|
+
};
|
|
216
|
+
const types = {
|
|
217
|
+
pods: { type: 'pod' },
|
|
218
|
+
nodes: { type: 'node' },
|
|
219
|
+
};
|
|
220
|
+
const result = await checkPermissions(types, getters);
|
|
221
|
+
|
|
222
|
+
expect(result).toStrictEqual({ pods: true, nodes: false });
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe('returnTo', () => {
|
|
227
|
+
it.each([
|
|
228
|
+
{
|
|
229
|
+
desc: 'default route when no route in opt',
|
|
230
|
+
opt: {},
|
|
231
|
+
vm: { $router: {} },
|
|
232
|
+
expected: 'http://localhost/auth/verify',
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
desc: 'custom route from opt',
|
|
236
|
+
opt: { route: '/my/page' },
|
|
237
|
+
vm: { $router: {} },
|
|
238
|
+
expected: 'http://localhost/my/page',
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
desc: 'router base prepended to route',
|
|
242
|
+
opt: {},
|
|
243
|
+
vm: { $router: { options: { base: '/ui' } } },
|
|
244
|
+
expected: 'http://localhost/ui/auth/verify',
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
desc: 'router base of "/" does not alter route',
|
|
248
|
+
opt: {},
|
|
249
|
+
vm: { $router: { options: { base: '/' } } },
|
|
250
|
+
expected: 'http://localhost/auth/verify',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
desc: 'backTo option adds back-to query param',
|
|
254
|
+
opt: { backTo: 'dashboard' },
|
|
255
|
+
vm: { $router: {} },
|
|
256
|
+
expected: 'http://localhost/auth/verify?back-to=dashboard',
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
desc: 'config option adds config query param',
|
|
260
|
+
opt: { config: 'github' },
|
|
261
|
+
vm: { $router: {} },
|
|
262
|
+
expected: 'http://localhost/auth/verify?config=github',
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
desc: 'isSlo option adds is-slo and logged-out params',
|
|
266
|
+
opt: { isSlo: true },
|
|
267
|
+
vm: { $router: {} },
|
|
268
|
+
expected: 'http://localhost/auth/verify?is-slo&logged-out',
|
|
269
|
+
},
|
|
270
|
+
])('builds return URL for $desc', ({ opt, vm, expected }) => {
|
|
271
|
+
expect(returnTo(opt, vm)).toStrictEqual(expected);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { integerString, keyValueStrings } from '@shell/utils/computed';
|
|
2
|
+
|
|
3
|
+
describe('integerString', () => {
|
|
4
|
+
describe('get', () => {
|
|
5
|
+
it.each([
|
|
6
|
+
{
|
|
7
|
+
desc: 'integer string',
|
|
8
|
+
initial: '42',
|
|
9
|
+
expected: 42,
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
desc: 'float string',
|
|
13
|
+
initial: '3.14',
|
|
14
|
+
expected: 3.14,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
desc: 'zero string',
|
|
18
|
+
initial: '0',
|
|
19
|
+
expected: 0,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
desc: 'negative integer string',
|
|
23
|
+
initial: '-7',
|
|
24
|
+
expected: -7,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
desc: 'undefined value',
|
|
28
|
+
initial: undefined as unknown as string,
|
|
29
|
+
expected: NaN,
|
|
30
|
+
},
|
|
31
|
+
])('returns a number for $desc', ({ initial, expected }) => {
|
|
32
|
+
const computed = integerString('value');
|
|
33
|
+
const ctx = { value: initial };
|
|
34
|
+
|
|
35
|
+
expect(computed.get.call(ctx)).toStrictEqual(expected);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('reads from a nested dot-notation path', () => {
|
|
39
|
+
const computed = integerString('a.b');
|
|
40
|
+
const ctx = { a: { b: '99' } };
|
|
41
|
+
|
|
42
|
+
expect(computed.get.call(ctx)).toStrictEqual(99);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('set', () => {
|
|
47
|
+
it.each([
|
|
48
|
+
{
|
|
49
|
+
desc: 'positive integer',
|
|
50
|
+
input: 42,
|
|
51
|
+
expected: '42',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
desc: 'float',
|
|
55
|
+
input: 3.14,
|
|
56
|
+
expected: '3.14',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
desc: 'zero',
|
|
60
|
+
input: 0,
|
|
61
|
+
expected: '0',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
desc: 'negative integer',
|
|
65
|
+
input: -7,
|
|
66
|
+
expected: '-7',
|
|
67
|
+
},
|
|
68
|
+
])('stores a string representation for $desc', ({ input, expected }) => {
|
|
69
|
+
const computed = integerString('value');
|
|
70
|
+
const ctx = { value: '' };
|
|
71
|
+
|
|
72
|
+
computed.set.call(ctx, input);
|
|
73
|
+
|
|
74
|
+
expect(ctx.value).toStrictEqual(expected);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('writes to a nested dot-notation path', () => {
|
|
78
|
+
const computed = integerString('a.b');
|
|
79
|
+
const ctx = { a: { b: '' } };
|
|
80
|
+
|
|
81
|
+
computed.set.call(ctx, 5);
|
|
82
|
+
|
|
83
|
+
expect((ctx.a as { b: string }).b).toStrictEqual('5');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('keyValueStrings', () => {
|
|
89
|
+
describe('get', () => {
|
|
90
|
+
it('returns an empty object for an empty array', () => {
|
|
91
|
+
const computed = keyValueStrings('items');
|
|
92
|
+
const ctx = { items: [] as string[] };
|
|
93
|
+
|
|
94
|
+
expect(computed.get.call(ctx)).toStrictEqual({});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('returns an empty object when path value is undefined', () => {
|
|
98
|
+
const computed = keyValueStrings('items');
|
|
99
|
+
const ctx = { items: undefined as unknown as string[] };
|
|
100
|
+
|
|
101
|
+
expect(computed.get.call(ctx)).toStrictEqual({});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it.each([
|
|
105
|
+
{
|
|
106
|
+
desc: 'single entry with default delimiter',
|
|
107
|
+
entries: ['key=value'],
|
|
108
|
+
delimiter: '=',
|
|
109
|
+
expected: { key: 'value' },
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
desc: 'multiple entries with default delimiter',
|
|
113
|
+
entries: ['foo=bar', 'baz=qux'],
|
|
114
|
+
delimiter: '=',
|
|
115
|
+
expected: {
|
|
116
|
+
foo: 'bar',
|
|
117
|
+
baz: 'qux',
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
desc: 'single entry with custom colon delimiter',
|
|
122
|
+
entries: ['key:value'],
|
|
123
|
+
delimiter: ':',
|
|
124
|
+
expected: { key: 'value' },
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
desc: 'multiple entries with custom colon delimiter',
|
|
128
|
+
entries: ['a:1', 'b:2'],
|
|
129
|
+
delimiter: ':',
|
|
130
|
+
expected: {
|
|
131
|
+
a: '1',
|
|
132
|
+
b: '2',
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
])('returns an object for $desc', ({ entries, delimiter, expected }) => {
|
|
136
|
+
const computed = keyValueStrings('items', delimiter);
|
|
137
|
+
const ctx = { items: entries };
|
|
138
|
+
|
|
139
|
+
expect(computed.get.call(ctx)).toStrictEqual(expected);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('set', () => {
|
|
144
|
+
it('stores an empty array for an empty object', () => {
|
|
145
|
+
const computed = keyValueStrings('items');
|
|
146
|
+
const ctx = { items: ['existing=val'] };
|
|
147
|
+
|
|
148
|
+
computed.set.call(ctx, {});
|
|
149
|
+
|
|
150
|
+
expect(ctx.items).toStrictEqual([]);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it.each([
|
|
154
|
+
{
|
|
155
|
+
desc: 'single key-value pair with default delimiter',
|
|
156
|
+
input: { key: 'value' },
|
|
157
|
+
delimiter: '=',
|
|
158
|
+
expected: ['key=value'],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
desc: 'multiple key-value pairs with default delimiter',
|
|
162
|
+
input: {
|
|
163
|
+
foo: 'bar',
|
|
164
|
+
baz: 'qux',
|
|
165
|
+
},
|
|
166
|
+
delimiter: '=',
|
|
167
|
+
expected: ['foo=bar', 'baz=qux'],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
desc: 'single pair with custom colon delimiter',
|
|
171
|
+
input: { key: 'value' },
|
|
172
|
+
delimiter: ':',
|
|
173
|
+
expected: ['key:value'],
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
desc: 'multiple pairs with custom colon delimiter',
|
|
177
|
+
input: {
|
|
178
|
+
a: '1',
|
|
179
|
+
b: '2',
|
|
180
|
+
},
|
|
181
|
+
delimiter: ':',
|
|
182
|
+
expected: ['a:1', 'b:2'],
|
|
183
|
+
},
|
|
184
|
+
])('stores delimited strings for $desc', ({ input, delimiter, expected }) => {
|
|
185
|
+
const computed = keyValueStrings('items', delimiter);
|
|
186
|
+
const ctx = { items: [] as string[] };
|
|
187
|
+
|
|
188
|
+
computed.set.call(ctx, input);
|
|
189
|
+
|
|
190
|
+
expect(ctx.items).toStrictEqual(expected);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
});
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import CspAdapterUtils from '@shell/utils/cspAdaptor';
|
|
2
|
+
import { CATALOG } from '@shell/config/types';
|
|
3
|
+
|
|
4
|
+
type App = { metadata?: { name?: string } };
|
|
5
|
+
|
|
6
|
+
function makeStore({
|
|
7
|
+
canList = true,
|
|
8
|
+
paginationEnabled = false,
|
|
9
|
+
findAllResult = [] as App[],
|
|
10
|
+
findPageData = [] as App[],
|
|
11
|
+
} = {}) {
|
|
12
|
+
return {
|
|
13
|
+
getters: {
|
|
14
|
+
'management/canList': jest.fn().mockReturnValue(canList),
|
|
15
|
+
'management/paginationEnabled': jest.fn().mockReturnValue(paginationEnabled),
|
|
16
|
+
},
|
|
17
|
+
dispatch: jest.fn((action: string) => {
|
|
18
|
+
if (action === 'management/findAll') {
|
|
19
|
+
return Promise.resolve(findAllResult);
|
|
20
|
+
}
|
|
21
|
+
if (action === 'management/findPage') {
|
|
22
|
+
return Promise.resolve({ data: findPageData });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return Promise.resolve(null);
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('cspAdaptor', () => {
|
|
31
|
+
describe('hasCspAdapter', () => {
|
|
32
|
+
it.each([
|
|
33
|
+
{
|
|
34
|
+
desc: 'returns the matching app for rancher-csp-adapter',
|
|
35
|
+
apps: [{ metadata: { name: 'rancher-csp-adapter' } }, { metadata: { name: 'other-app' } }],
|
|
36
|
+
expected: { metadata: { name: 'rancher-csp-adapter' } },
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
desc: 'returns the matching app for rancher-csp-billing-adapter',
|
|
40
|
+
apps: [{ metadata: { name: 'rancher-csp-billing-adapter' } }],
|
|
41
|
+
expected: { metadata: { name: 'rancher-csp-billing-adapter' } },
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
desc: 'returns first match when multiple csp apps are present',
|
|
45
|
+
apps: [{ metadata: { name: 'rancher-csp-billing-adapter' } }, { metadata: { name: 'rancher-csp-adapter' } }],
|
|
46
|
+
expected: { metadata: { name: 'rancher-csp-billing-adapter' } },
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
desc: 'returns undefined when no app name matches',
|
|
50
|
+
apps: [{ metadata: { name: 'unrelated-app' } }],
|
|
51
|
+
expected: undefined,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
desc: 'returns undefined for apps without metadata',
|
|
55
|
+
apps: [{}],
|
|
56
|
+
expected: undefined,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
desc: 'returns undefined when apps is an empty array',
|
|
60
|
+
apps: [],
|
|
61
|
+
expected: undefined,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
desc: 'returns undefined when apps is null',
|
|
65
|
+
apps: null as unknown as App[],
|
|
66
|
+
expected: undefined,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
desc: 'returns undefined when apps is undefined',
|
|
70
|
+
apps: undefined as unknown as App[],
|
|
71
|
+
expected: undefined,
|
|
72
|
+
},
|
|
73
|
+
])('$desc', ({ apps, expected }) => {
|
|
74
|
+
const result = CspAdapterUtils.hasCspAdapter({ $store: {} as any, apps });
|
|
75
|
+
|
|
76
|
+
expect(result).toStrictEqual(expected);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('fetchCspAdaptorApp', () => {
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
CspAdapterUtils.resetState();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('returns empty array when canList returns false', async() => {
|
|
86
|
+
const store = makeStore({ canList: false });
|
|
87
|
+
const result = await CspAdapterUtils.fetchCspAdaptorApp(store as any);
|
|
88
|
+
|
|
89
|
+
expect(result).toStrictEqual([]);
|
|
90
|
+
expect(store.dispatch).not.toHaveBeenCalled();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('uses findAll when canList is true and pagination is disabled', async() => {
|
|
94
|
+
const findAllResult: App[] = [{ metadata: { name: 'rancher-csp-adapter' } }];
|
|
95
|
+
const store = makeStore({
|
|
96
|
+
canList: true, paginationEnabled: false, findAllResult
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const result = await CspAdapterUtils.fetchCspAdaptorApp(store as any);
|
|
100
|
+
|
|
101
|
+
expect(result).toStrictEqual(findAllResult);
|
|
102
|
+
expect(store.dispatch).toHaveBeenCalledWith('management/findAll', { type: CATALOG.APP });
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('uses findPage when canList is true and pagination is enabled', async() => {
|
|
106
|
+
const findPageData: App[] = [{ metadata: { name: 'rancher-csp-billing-adapter' } }];
|
|
107
|
+
const store = makeStore({
|
|
108
|
+
canList: true, paginationEnabled: true, findPageData
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const result = await CspAdapterUtils.fetchCspAdaptorApp(store as any);
|
|
112
|
+
|
|
113
|
+
expect(result).toStrictEqual(findPageData);
|
|
114
|
+
expect(store.dispatch).toHaveBeenCalledWith('management/findPage', expect.objectContaining({ type: CATALOG.APP }));
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('caches the result and does not dispatch again on a second call', async() => {
|
|
118
|
+
const findAllResult: App[] = [{ metadata: { name: 'rancher-csp-adapter' } }];
|
|
119
|
+
const store = makeStore({
|
|
120
|
+
canList: true, paginationEnabled: false, findAllResult
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await CspAdapterUtils.fetchCspAdaptorApp(store as any);
|
|
124
|
+
const result = await CspAdapterUtils.fetchCspAdaptorApp(store as any);
|
|
125
|
+
|
|
126
|
+
expect(result).toStrictEqual(findAllResult);
|
|
127
|
+
expect(store.dispatch).toHaveBeenCalledTimes(1);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('fetches fresh data after resetState clears the cache', async() => {
|
|
131
|
+
const findAllResult: App[] = [{ metadata: { name: 'rancher-csp-adapter' } }];
|
|
132
|
+
const store = makeStore({
|
|
133
|
+
canList: true, paginationEnabled: false, findAllResult
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
await CspAdapterUtils.fetchCspAdaptorApp(store as any);
|
|
137
|
+
|
|
138
|
+
CspAdapterUtils.resetState();
|
|
139
|
+
|
|
140
|
+
const freshStore = makeStore({
|
|
141
|
+
canList: true, paginationEnabled: false, findAllResult: []
|
|
142
|
+
});
|
|
143
|
+
const result = await CspAdapterUtils.fetchCspAdaptorApp(freshStore as any);
|
|
144
|
+
|
|
145
|
+
expect(result).toStrictEqual([]);
|
|
146
|
+
expect(freshStore.dispatch).toHaveBeenCalledTimes(1);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('passes pagination filters for known csp adapter app names when using findPage', async() => {
|
|
150
|
+
const store = makeStore({
|
|
151
|
+
canList: true, paginationEnabled: true, findPageData: []
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
await CspAdapterUtils.fetchCspAdaptorApp(store as any);
|
|
155
|
+
|
|
156
|
+
const [, args] = store.dispatch.mock.calls[0];
|
|
157
|
+
|
|
158
|
+
expect(args.type).toStrictEqual(CATALOG.APP);
|
|
159
|
+
expect(args.opt.watch).toStrictEqual(false);
|
|
160
|
+
expect(args.opt.transient).toStrictEqual(true);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { getParent } from '@shell/utils/dom';
|
|
2
|
+
|
|
3
|
+
describe('getParent', () => {
|
|
4
|
+
it('returns null when element has no parent', () => {
|
|
5
|
+
const el = document.createElement('div');
|
|
6
|
+
|
|
7
|
+
expect(getParent(el, 'div')).toBeNull();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('returns null when el is null', () => {
|
|
11
|
+
expect(getParent(null, 'div')).toBeNull();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('returns null when el is undefined', () => {
|
|
15
|
+
expect(getParent(undefined, 'div')).toBeNull();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('returns the direct parent matching the selector', () => {
|
|
19
|
+
const parent = document.createElement('section');
|
|
20
|
+
const child = document.createElement('div');
|
|
21
|
+
|
|
22
|
+
parent.appendChild(child);
|
|
23
|
+
|
|
24
|
+
expect(getParent(child, 'section')).toBe(parent);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('returns an ancestor matching the selector when direct parent does not match', () => {
|
|
28
|
+
const grandparent = document.createElement('article');
|
|
29
|
+
const parent = document.createElement('div');
|
|
30
|
+
const child = document.createElement('span');
|
|
31
|
+
|
|
32
|
+
grandparent.appendChild(parent);
|
|
33
|
+
parent.appendChild(child);
|
|
34
|
+
|
|
35
|
+
expect(getParent(child, 'article')).toBe(grandparent);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('returns null when no ancestor matches the selector', () => {
|
|
39
|
+
const parent = document.createElement('div');
|
|
40
|
+
const child = document.createElement('span');
|
|
41
|
+
|
|
42
|
+
parent.appendChild(child);
|
|
43
|
+
|
|
44
|
+
expect(getParent(child, 'article')).toBeNull();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('matches by class selector', () => {
|
|
48
|
+
const parent = document.createElement('div');
|
|
49
|
+
|
|
50
|
+
parent.className = 'my-container';
|
|
51
|
+
const child = document.createElement('span');
|
|
52
|
+
|
|
53
|
+
parent.appendChild(child);
|
|
54
|
+
|
|
55
|
+
expect(getParent(child, '.my-container')).toBe(parent);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('matches by id selector', () => {
|
|
59
|
+
const parent = document.createElement('div');
|
|
60
|
+
|
|
61
|
+
parent.id = 'root';
|
|
62
|
+
const child = document.createElement('span');
|
|
63
|
+
|
|
64
|
+
parent.appendChild(child);
|
|
65
|
+
|
|
66
|
+
expect(getParent(child, '#root')).toBe(parent);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('skips intermediate non-matching ancestors and finds the correct one', () => {
|
|
70
|
+
const great = document.createElement('nav');
|
|
71
|
+
const grandparent = document.createElement('section');
|
|
72
|
+
const parent = document.createElement('div');
|
|
73
|
+
const child = document.createElement('span');
|
|
74
|
+
|
|
75
|
+
great.appendChild(grandparent);
|
|
76
|
+
grandparent.appendChild(parent);
|
|
77
|
+
parent.appendChild(child);
|
|
78
|
+
|
|
79
|
+
expect(getParent(child, 'nav')).toBe(great);
|
|
80
|
+
});
|
|
81
|
+
});
|