@rancher/shell 3.0.12-rc.2 → 3.0.12-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apis/impl/apis.ts +6 -0
- package/apis/index.ts +26 -0
- package/apis/intf/resources-api/cluster-api.ts +18 -0
- package/apis/intf/resources-api/mgmt-api.ts +15 -0
- package/apis/intf/resources-api/resource-base.ts +107 -0
- package/apis/intf/resources-api/resource-constants.ts +147 -0
- package/apis/intf/resources-api/resources-api.ts +143 -0
- package/apis/intf/resources.ts +49 -0
- package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
- package/apis/intf/shell-api/proxy.ts +216 -0
- package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
- package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
- package/apis/intf/shell.ts +12 -6
- package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
- package/apis/resources/index.ts +22 -0
- package/apis/resources/resources-api-class.ts +187 -0
- package/apis/shell/__tests__/proxy.test.ts +369 -0
- package/apis/shell/index.ts +8 -1
- package/apis/shell/modal.ts +4 -1
- package/apis/shell/notifications.ts +9 -6
- package/apis/shell/proxy.ts +256 -0
- package/apis/shell/slide-in.ts +4 -1
- package/apis/vue-shim.d.ts +2 -1
- package/assets/data/aws-regions.json +4 -0
- package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/fonts/_fontstack.scss +132 -8
- package/assets/translations/en-us.yaml +22 -5
- package/chart/monitoring/index.vue +10 -1
- package/components/ActionDropdownShell.vue +2 -1
- package/components/CruResourceFooter.vue +9 -5
- package/components/ExplorerProjectsNamespaces.vue +1 -1
- package/components/InstallHelmCharts.vue +2 -2
- package/components/LandingPagePreference.vue +14 -5
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
- package/components/Resource/Detail/Metadata/index.vue +6 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
- package/components/Resource/Detail/SpacedRow.vue +3 -1
- package/components/Resource/Detail/TitleBar/index.vue +10 -11
- package/components/ResourceList/Masthead.vue +12 -8
- package/components/SelectIconGrid.vue +0 -10
- package/components/SingleClusterInfo.vue +1 -0
- package/components/SortableTable/__tests__/sorting.test.ts +126 -0
- package/components/SortableTable/index.vue +6 -9
- package/components/SortableTable/selection.js +23 -5
- package/components/SortableTable/sorting.js +6 -3
- package/components/Wizard.vue +14 -13
- package/components/fleet/FleetBundles.vue +100 -12
- package/components/fleet/FleetClusterTargets/index.vue +37 -15
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
- package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
- package/components/form/LabeledSelect.vue +20 -3
- package/components/form/NameNsDescription.vue +11 -0
- package/components/form/Security.vue +6 -2
- package/components/form/WorkloadPorts.vue +2 -7
- package/components/form/__tests__/Security.test.ts +76 -0
- package/components/formatter/Autoscaler.vue +4 -4
- package/components/formatter/ClusterKubeVersion.vue +27 -0
- package/components/formatter/ClusterLink.vue +1 -7
- package/components/formatter/ClusterProvider.vue +6 -10
- package/components/formatter/FleetSummaryGraph.vue +0 -3
- package/components/formatter/MachineSummaryGraph.vue +1 -1
- package/components/formatter/PodsUsage.vue +2 -2
- package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
- package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
- package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
- package/components/nav/NamespaceFilter.vue +2 -2
- package/components/nav/TopLevelMenu.helper.ts +15 -3
- package/components/nav/TopLevelMenu.vue +16 -5
- package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
- package/components/templates/home.vue +18 -0
- package/components/templates/plain.vue +18 -0
- package/components/templates/standalone.vue +17 -0
- package/composables/useFormValidation.ts +93 -0
- package/composables/useVeeValidateField.test.ts +159 -0
- package/composables/useVeeValidateField.ts +67 -0
- package/config/pagination-table-headers.js +18 -1
- package/config/product/manager.js +82 -21
- package/config/router/routes.js +6 -0
- package/config/table-headers.js +20 -1
- package/config/types.js +2 -1
- package/core/__tests__/plugin-products.test.ts +904 -20
- package/core/plugin-products-base.ts +107 -7
- package/core/plugin-products.ts +4 -0
- package/core/plugin-types.ts +111 -1
- package/core/plugin.ts +15 -7
- package/core/productDebugger.js +9 -4
- package/core/types-provisioning.ts +43 -30
- package/core/types.ts +57 -20
- package/detail/__tests__/pod.test.ts +41 -0
- package/detail/harvesterhci.io.management.cluster.vue +6 -2
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +4 -10
- package/edit/auth/__tests__/azuread.test.ts +217 -34
- package/edit/auth/azuread.vue +122 -14
- package/edit/auth/oidc.vue +2 -2
- package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
- package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
- package/edit/networking.k8s.io.ingress/index.vue +75 -20
- package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
- package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
- package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
- package/edit/secret/__tests__/ssh.test.ts +5 -6
- package/edit/secret/basic.vue +31 -0
- package/edit/secret/index.vue +68 -17
- package/edit/secret/registry.vue +38 -0
- package/edit/secret/ssh.vue +29 -0
- package/edit/secret/tls.vue +30 -0
- package/edit/service.vue +4 -4
- package/edit/workload/Upgrading.vue +3 -3
- package/edit/workload/__tests__/Upgrading.test.ts +6 -9
- package/edit/workload/mixins/workload.js +2 -1
- package/list/fleet.cattle.io.bundle.vue +7 -104
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
- package/list/provisioning.cattle.io.cluster.vue +262 -180
- package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
- package/mixins/__tests__/chart.test.ts +112 -0
- package/mixins/brand.js +2 -1
- package/mixins/chart.js +12 -8
- package/mixins/resource-fetch-api-pagination.js +41 -5
- package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
- package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
- package/models/__tests__/management.cattle.io.node.ts +6 -5
- package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
- package/models/base-cluster.x-k8s.io.js +26 -0
- package/models/cluster.js +1 -1
- package/models/cluster.x-k8s.io.machine.js +4 -22
- package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
- package/models/cluster.x-k8s.io.machineset.js +2 -20
- package/models/compliance.cattle.io.clusterscan.js +130 -2
- package/models/ext.cattle.io.kubeconfig.ts +4 -7
- package/models/fleet-application.js +3 -1
- package/models/management.cattle.io.cluster.js +417 -40
- package/models/management.cattle.io.node.js +6 -4
- package/models/management.cattle.io.nodepool.js +1 -1
- package/models/networking.k8s.io.ingress.js +12 -4
- package/models/provisioning.cattle.io.cluster.js +47 -330
- package/models/rke.cattle.io.etcdsnapshot.js +1 -2
- package/package.json +11 -29
- package/pages/__tests__/readme.test.ts +49 -0
- package/pages/auth/setup.vue +2 -3
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
- package/pages/c/_cluster/apps/charts/chart.vue +60 -8
- package/pages/c/_cluster/apps/charts/install.vue +10 -7
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
- package/pages/c/_cluster/explorer/index.vue +5 -49
- package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
- package/pages/c/_cluster/istio/index.vue +21 -6
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +719 -2
- package/pages/c/_cluster/uiplugins/index.vue +203 -197
- package/pages/diagnostic.vue +13 -17
- package/pages/fail-whale.vue +18 -0
- package/pages/home.vue +77 -260
- package/pages/readme.vue +88 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
- package/plugins/dashboard-store/actions.js +40 -18
- package/plugins/dashboard-store/resource-class.js +5 -2
- package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
- package/plugins/steve/steve-pagination-utils.ts +11 -3
- package/plugins/steve/subscribe.js +35 -5
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
- package/rancher-components/RcButton/RcButton.test.ts +37 -1
- package/rancher-components/RcButton/RcButton.vue +38 -8
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
- package/store/__tests__/catalog.test.ts +115 -1
- package/store/__tests__/type-map.test.ts +556 -1
- package/store/action-menu.js +8 -3
- package/store/auth.js +1 -1
- package/store/aws.js +27 -16
- package/store/catalog.js +27 -3
- package/store/digitalocean.js +20 -38
- package/store/index.js +2 -0
- package/store/linode.js +25 -40
- package/store/pnap.js +1 -0
- package/store/type-map.js +111 -29
- package/tsconfig.paths.json +8 -8
- package/types/kube/kube-api.ts +14 -1
- package/types/rancher/steve.api.ts +12 -12
- package/types/resources/settings.d.ts +2 -1
- package/types/shell/index.d.ts +102 -2
- package/types/store/dashboard-store.types.ts +108 -11
- package/types/store/pagination.types.ts +6 -3
- package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
- package/utils/__tests__/async.test.ts +87 -0
- package/utils/__tests__/aws.test.ts +140 -0
- package/utils/__tests__/banners.test.ts +176 -0
- package/utils/__tests__/chart.test.ts +64 -1
- package/utils/__tests__/color.test.ts +226 -0
- package/utils/__tests__/duration.test.ts +140 -0
- package/utils/__tests__/fleet.test.ts +340 -0
- package/utils/__tests__/ingress.test.ts +553 -0
- package/utils/__tests__/kube.test.ts +68 -0
- package/utils/__tests__/namespace-filter.test.ts +109 -0
- package/utils/__tests__/pagination-utils.test.ts +361 -0
- package/utils/__tests__/parse-externalid.test.ts +137 -0
- package/utils/__tests__/perf-setting.utils.test.ts +98 -0
- package/utils/__tests__/poller-sequential.test.ts +177 -0
- package/utils/__tests__/poller.test.ts +170 -0
- package/utils/__tests__/promise.test.ts +346 -0
- package/utils/__tests__/settings.test.ts +140 -0
- package/utils/__tests__/sort-utils.test.ts +301 -0
- package/utils/__tests__/string-utils.test.ts +798 -0
- package/utils/__tests__/string.test.ts +23 -1
- package/utils/__tests__/style.test.ts +154 -0
- package/utils/__tests__/svg-filter.test.ts +184 -0
- package/utils/__tests__/units.test.ts +417 -0
- package/utils/__tests__/versions.test.ts +128 -0
- package/utils/__tests__/xccdf.test.ts +391 -0
- package/utils/chart.js +36 -0
- package/utils/fleet.ts +13 -3
- package/utils/gatekeeper/__tests__/util.test.ts +174 -0
- package/utils/gc/__tests__/gc-interval.test.ts +119 -0
- package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
- package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
- package/utils/gc/__tests__/gc.test.ts +487 -0
- package/utils/ingress.ts +9 -1
- package/utils/pagination-utils.ts +2 -1
- package/utils/string.js +25 -2
- package/utils/uiplugins.ts +5 -5
- package/utils/validators/__tests__/cluster-name.test.ts +110 -0
- package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
- package/utils/validators/__tests__/index.test.ts +481 -0
- package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
- package/utils/validators/__tests__/misc-validators.test.ts +246 -0
- package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
- package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
- package/utils/validators/__tests__/role-template.test.ts +149 -0
- package/utils/validators/__tests__/service.test.ts +283 -0
- package/utils/validators/__tests__/setting.test.js +32 -0
- package/utils/validators/formRules/__tests__/index.test.ts +50 -0
- package/utils/validators/formRules/index.ts +5 -5
- package/utils/validators/machine-pool.ts +1 -1
- package/utils/validators/setting.js +18 -3
- package/utils/xccdf.ts +418 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PluginProduct } from '@shell/core/plugin-products';
|
|
2
|
+
import { Plugin } from '@shell/core/plugin';
|
|
2
3
|
import {
|
|
3
4
|
ProductMetadata, ProductSinglePage, ProductChildPage,
|
|
4
5
|
ProductChildGroup, ProductChildCustomPage, ProductChildResourcePage,
|
|
@@ -57,9 +58,10 @@ jest.mock('@shell/core/productDebugger', () => ({
|
|
|
57
58
|
// Create mock factories
|
|
58
59
|
function createMockPlugin(): IExtension {
|
|
59
60
|
return {
|
|
60
|
-
_registerTopLevelProduct:
|
|
61
|
-
addRoute:
|
|
62
|
-
|
|
61
|
+
_registerTopLevelProduct: jest.fn(),
|
|
62
|
+
addRoute: jest.fn(),
|
|
63
|
+
enableServerSidePagination: jest.fn(),
|
|
64
|
+
DSL: jest.fn((store, productName) => ({
|
|
63
65
|
basicType: jest.fn(),
|
|
64
66
|
labelGroup: jest.fn(),
|
|
65
67
|
setGroupDefaultType: jest.fn(),
|
|
@@ -68,12 +70,24 @@ function createMockPlugin(): IExtension {
|
|
|
68
70
|
configureType: jest.fn(),
|
|
69
71
|
weightType: jest.fn(),
|
|
70
72
|
product: jest.fn(),
|
|
73
|
+
headers: jest.fn(),
|
|
74
|
+
hideBulkActions: jest.fn(),
|
|
75
|
+
mapGroup: jest.fn(),
|
|
76
|
+
ignoreGroup: jest.fn(),
|
|
77
|
+
mapType: jest.fn(),
|
|
78
|
+
ignoreType: jest.fn(),
|
|
79
|
+
moveType: jest.fn(),
|
|
71
80
|
})),
|
|
72
81
|
} as any;
|
|
73
82
|
}
|
|
74
83
|
|
|
75
|
-
function createMockStore(extendableProducts: string[] = Object.values(StandardProductNames)): any {
|
|
76
|
-
return {
|
|
84
|
+
function createMockStore(extendableProducts: string[] = Object.values(StandardProductNames), managementSchemas: string[] = []): any {
|
|
85
|
+
return {
|
|
86
|
+
getters: {
|
|
87
|
+
'type-map/productByName': (productName: string) => (extendableProducts.includes(productName) ? { name: productName, extendable: true } : undefined),
|
|
88
|
+
'management/schemaFor': (type: string) => (managementSchemas.includes(type) ? { id: type } : undefined),
|
|
89
|
+
}
|
|
90
|
+
};
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
describe('pluginProduct', () => {
|
|
@@ -3134,11 +3148,11 @@ describe('pluginProduct', () => {
|
|
|
3134
3148
|
weightType: jest.fn(),
|
|
3135
3149
|
headers: jest.fn(),
|
|
3136
3150
|
hideBulkActions: jest.fn(),
|
|
3137
|
-
|
|
3138
|
-
mapGroup:
|
|
3139
|
-
ignoreGroup:
|
|
3140
|
-
mapType:
|
|
3141
|
-
ignoreType:
|
|
3151
|
+
|
|
3152
|
+
mapGroup: jest.fn(),
|
|
3153
|
+
ignoreGroup: jest.fn(),
|
|
3154
|
+
mapType: jest.fn(),
|
|
3155
|
+
ignoreType: jest.fn(),
|
|
3142
3156
|
};
|
|
3143
3157
|
|
|
3144
3158
|
jest.spyOn(mockPlugin, 'DSL').mockReturnValue(mockDSL);
|
|
@@ -3169,11 +3183,11 @@ describe('pluginProduct', () => {
|
|
|
3169
3183
|
weightType: jest.fn(),
|
|
3170
3184
|
headers: jest.fn(),
|
|
3171
3185
|
hideBulkActions: jest.fn(),
|
|
3172
|
-
|
|
3173
|
-
mapGroup:
|
|
3174
|
-
ignoreGroup:
|
|
3175
|
-
mapType:
|
|
3176
|
-
ignoreType:
|
|
3186
|
+
|
|
3187
|
+
mapGroup: jest.fn(),
|
|
3188
|
+
ignoreGroup: jest.fn(),
|
|
3189
|
+
mapType: jest.fn(),
|
|
3190
|
+
ignoreType: jest.fn(),
|
|
3177
3191
|
};
|
|
3178
3192
|
|
|
3179
3193
|
jest.spyOn(mockPlugin, 'DSL').mockReturnValue(mockDSL);
|
|
@@ -3200,11 +3214,11 @@ describe('pluginProduct', () => {
|
|
|
3200
3214
|
weightType: jest.fn(),
|
|
3201
3215
|
headers: jest.fn(),
|
|
3202
3216
|
hideBulkActions: jest.fn(),
|
|
3203
|
-
|
|
3204
|
-
mapGroup:
|
|
3205
|
-
ignoreGroup:
|
|
3206
|
-
mapType:
|
|
3207
|
-
ignoreType:
|
|
3217
|
+
|
|
3218
|
+
mapGroup: jest.fn(),
|
|
3219
|
+
ignoreGroup: jest.fn(),
|
|
3220
|
+
mapType: jest.fn(),
|
|
3221
|
+
ignoreType: jest.fn(),
|
|
3208
3222
|
};
|
|
3209
3223
|
|
|
3210
3224
|
jest.spyOn(mockPlugin, 'DSL').mockReturnValue(mockDSL);
|
|
@@ -3805,6 +3819,876 @@ describe('pluginProduct', () => {
|
|
|
3805
3819
|
// It should NOT be a generic route without the group name
|
|
3806
3820
|
expect(groupVirtualType[0].route.name).not.toBe('myapp-c-cluster');
|
|
3807
3821
|
});
|
|
3822
|
+
describe('resource page: headers support', () => {
|
|
3823
|
+
it('should call DSL headers method when resource page has headers configured', () => {
|
|
3824
|
+
const mockPlugin = createMockPlugin();
|
|
3825
|
+
const mockStore = createMockStore();
|
|
3826
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
3827
|
+
|
|
3828
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
3829
|
+
|
|
3830
|
+
const productMetadata: ProductMetadata = {
|
|
3831
|
+
name: 'headers-test',
|
|
3832
|
+
label: 'Headers Test',
|
|
3833
|
+
};
|
|
3834
|
+
|
|
3835
|
+
const testHeaders = [
|
|
3836
|
+
{
|
|
3837
|
+
name: 'name', label: 'Name', value: 'metadata.name'
|
|
3838
|
+
},
|
|
3839
|
+
{
|
|
3840
|
+
name: 'status', labelKey: 'tableHeaders.status', value: 'status'
|
|
3841
|
+
},
|
|
3842
|
+
];
|
|
3843
|
+
|
|
3844
|
+
const config: ProductChildPage[] = [
|
|
3845
|
+
{ type: 'some.resource.type', headers: testHeaders },
|
|
3846
|
+
];
|
|
3847
|
+
|
|
3848
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
3849
|
+
|
|
3850
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
3851
|
+
|
|
3852
|
+
expect(mockDSL.headers).toHaveBeenCalledWith('some.resource.type', testHeaders, undefined);
|
|
3853
|
+
});
|
|
3854
|
+
|
|
3855
|
+
it('should call DSL headers method with sspHeaders when configured', () => {
|
|
3856
|
+
const mockPlugin = createMockPlugin();
|
|
3857
|
+
const mockStore = createMockStore();
|
|
3858
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
3859
|
+
|
|
3860
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
3861
|
+
|
|
3862
|
+
const productMetadata: ProductMetadata = {
|
|
3863
|
+
name: 'ssp-headers-test',
|
|
3864
|
+
label: 'SSP Headers Test',
|
|
3865
|
+
};
|
|
3866
|
+
|
|
3867
|
+
const testSspHeaders = [
|
|
3868
|
+
{
|
|
3869
|
+
name: 'name', label: 'Name', value: 'metadata.name'
|
|
3870
|
+
},
|
|
3871
|
+
{
|
|
3872
|
+
name: 'namespace', labelKey: 'tableHeaders.namespace', value: 'metadata.namespace'
|
|
3873
|
+
},
|
|
3874
|
+
];
|
|
3875
|
+
|
|
3876
|
+
const config: ProductChildPage[] = [
|
|
3877
|
+
{ type: 'some.resource.type', sspHeaders: testSspHeaders },
|
|
3878
|
+
];
|
|
3879
|
+
|
|
3880
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
3881
|
+
|
|
3882
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
3883
|
+
|
|
3884
|
+
expect(mockDSL.headers).toHaveBeenCalledWith('some.resource.type', undefined, testSspHeaders);
|
|
3885
|
+
});
|
|
3886
|
+
|
|
3887
|
+
it('should call DSL headers method with both headers and sspHeaders when both are configured', () => {
|
|
3888
|
+
const mockPlugin = createMockPlugin();
|
|
3889
|
+
const mockStore = createMockStore();
|
|
3890
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
3891
|
+
|
|
3892
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
3893
|
+
|
|
3894
|
+
const productMetadata: ProductMetadata = {
|
|
3895
|
+
name: 'both-headers-test',
|
|
3896
|
+
label: 'Both Headers Test',
|
|
3897
|
+
};
|
|
3898
|
+
|
|
3899
|
+
const testHeaders = [
|
|
3900
|
+
{
|
|
3901
|
+
name: 'name', label: 'Name', value: 'metadata.name'
|
|
3902
|
+
},
|
|
3903
|
+
];
|
|
3904
|
+
|
|
3905
|
+
const testSspHeaders = [
|
|
3906
|
+
{
|
|
3907
|
+
name: 'name', label: 'Name', value: 'metadata.name'
|
|
3908
|
+
},
|
|
3909
|
+
];
|
|
3910
|
+
|
|
3911
|
+
const config: ProductChildPage[] = [
|
|
3912
|
+
{
|
|
3913
|
+
type: 'some.resource.type',
|
|
3914
|
+
headers: testHeaders,
|
|
3915
|
+
sspHeaders: testSspHeaders,
|
|
3916
|
+
},
|
|
3917
|
+
];
|
|
3918
|
+
|
|
3919
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
3920
|
+
|
|
3921
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
3922
|
+
|
|
3923
|
+
expect(mockDSL.headers).toHaveBeenCalledWith('some.resource.type', testHeaders, testSspHeaders);
|
|
3924
|
+
});
|
|
3925
|
+
|
|
3926
|
+
it('should not call DSL headers method when resource page has no headers or sspHeaders', () => {
|
|
3927
|
+
const mockPlugin = createMockPlugin();
|
|
3928
|
+
const mockStore = createMockStore();
|
|
3929
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
3930
|
+
|
|
3931
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
3932
|
+
|
|
3933
|
+
const productMetadata: ProductMetadata = {
|
|
3934
|
+
name: 'no-headers-test',
|
|
3935
|
+
label: 'No Headers Test',
|
|
3936
|
+
};
|
|
3937
|
+
|
|
3938
|
+
const config: ProductChildPage[] = [{ type: 'some.resource.type' }];
|
|
3939
|
+
|
|
3940
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
3941
|
+
|
|
3942
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
3943
|
+
|
|
3944
|
+
expect(mockDSL.headers).not.toHaveBeenCalled();
|
|
3945
|
+
});
|
|
3946
|
+
});
|
|
3947
|
+
|
|
3948
|
+
describe('resource page: overrideListResourceName (mapType) support', () => {
|
|
3949
|
+
it('should call DSL mapType when resource page has overrideListResourceName', () => {
|
|
3950
|
+
const mockPlugin = createMockPlugin();
|
|
3951
|
+
const mockStore = createMockStore();
|
|
3952
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
3953
|
+
|
|
3954
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
3955
|
+
|
|
3956
|
+
const productMetadata: ProductMetadata = {
|
|
3957
|
+
name: 'maptype-test',
|
|
3958
|
+
label: 'MapType Test',
|
|
3959
|
+
};
|
|
3960
|
+
|
|
3961
|
+
const config: ProductChildPage[] = [
|
|
3962
|
+
{ type: 'apps.deployment', overrideListResourceName: 'Deployments' },
|
|
3963
|
+
];
|
|
3964
|
+
|
|
3965
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
3966
|
+
|
|
3967
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
3968
|
+
|
|
3969
|
+
expect(mockDSL.mapType).toHaveBeenCalledWith('apps.deployment', 'Deployments');
|
|
3970
|
+
});
|
|
3971
|
+
|
|
3972
|
+
it('should not call mapType when overrideListResourceName is not set', () => {
|
|
3973
|
+
const mockPlugin = createMockPlugin();
|
|
3974
|
+
const mockStore = createMockStore();
|
|
3975
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
3976
|
+
|
|
3977
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
3978
|
+
|
|
3979
|
+
const productMetadata: ProductMetadata = {
|
|
3980
|
+
name: 'no-maptype',
|
|
3981
|
+
label: 'No MapType',
|
|
3982
|
+
};
|
|
3983
|
+
|
|
3984
|
+
const config: ProductChildPage[] = [{ type: 'apps.deployment' }];
|
|
3985
|
+
|
|
3986
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
3987
|
+
|
|
3988
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
3989
|
+
|
|
3990
|
+
expect(mockDSL.mapType).not.toHaveBeenCalled();
|
|
3991
|
+
});
|
|
3992
|
+
});
|
|
3993
|
+
|
|
3994
|
+
describe('resource page: hideFromNav (ignoreType) support', () => {
|
|
3995
|
+
it('should call DSL ignoreType when resource page has hideFromNav set', () => {
|
|
3996
|
+
const mockPlugin = createMockPlugin();
|
|
3997
|
+
const mockStore = createMockStore();
|
|
3998
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
3999
|
+
|
|
4000
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4001
|
+
|
|
4002
|
+
const productMetadata: ProductMetadata = {
|
|
4003
|
+
name: 'ignoretype-test',
|
|
4004
|
+
label: 'IgnoreType Test',
|
|
4005
|
+
};
|
|
4006
|
+
|
|
4007
|
+
const config: ProductChildPage[] = [
|
|
4008
|
+
{ type: 'secret.type', hideFromNav: true },
|
|
4009
|
+
];
|
|
4010
|
+
|
|
4011
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4012
|
+
|
|
4013
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4014
|
+
|
|
4015
|
+
expect(mockDSL.ignoreType).toHaveBeenCalledWith('secret.type');
|
|
4016
|
+
});
|
|
4017
|
+
|
|
4018
|
+
it('should not call ignoreType when hideFromNav is not set', () => {
|
|
4019
|
+
const mockPlugin = createMockPlugin();
|
|
4020
|
+
const mockStore = createMockStore();
|
|
4021
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4022
|
+
|
|
4023
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4024
|
+
|
|
4025
|
+
const productMetadata: ProductMetadata = {
|
|
4026
|
+
name: 'no-ignoretype',
|
|
4027
|
+
label: 'No IgnoreType',
|
|
4028
|
+
};
|
|
4029
|
+
|
|
4030
|
+
const config: ProductChildPage[] = [{ type: 'apps.deployment' }];
|
|
4031
|
+
|
|
4032
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4033
|
+
|
|
4034
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4035
|
+
|
|
4036
|
+
expect(mockDSL.ignoreType).not.toHaveBeenCalled();
|
|
4037
|
+
});
|
|
4038
|
+
});
|
|
4039
|
+
|
|
4040
|
+
describe('resource page: hideBulkActions support', () => {
|
|
4041
|
+
it('should call DSL hideBulkActions when resource page has hideBulkActions set', () => {
|
|
4042
|
+
const mockPlugin = createMockPlugin();
|
|
4043
|
+
const mockStore = createMockStore();
|
|
4044
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4045
|
+
|
|
4046
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4047
|
+
|
|
4048
|
+
const productMetadata: ProductMetadata = {
|
|
4049
|
+
name: 'bulk-actions-test',
|
|
4050
|
+
label: 'Bulk Actions Test',
|
|
4051
|
+
};
|
|
4052
|
+
|
|
4053
|
+
const config: ProductChildPage[] = [
|
|
4054
|
+
{ type: 'management.cattle.io.feature', hideBulkActions: true },
|
|
4055
|
+
];
|
|
4056
|
+
|
|
4057
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4058
|
+
|
|
4059
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4060
|
+
|
|
4061
|
+
expect(mockDSL.hideBulkActions).toHaveBeenCalledWith('management.cattle.io.feature', true);
|
|
4062
|
+
});
|
|
4063
|
+
});
|
|
4064
|
+
|
|
4065
|
+
describe('resource page: ordering - weight is set last', () => {
|
|
4066
|
+
it('should call weightType after configureType for resource pages', () => {
|
|
4067
|
+
const mockPlugin = createMockPlugin();
|
|
4068
|
+
const mockStore = createMockStore();
|
|
4069
|
+
const callOrder: string[] = [];
|
|
4070
|
+
const mockDSL = {
|
|
4071
|
+
product: jest.fn(),
|
|
4072
|
+
basicType: jest.fn(),
|
|
4073
|
+
labelGroup: jest.fn(),
|
|
4074
|
+
setGroupDefaultType: jest.fn(),
|
|
4075
|
+
weightGroup: jest.fn(),
|
|
4076
|
+
virtualType: jest.fn(),
|
|
4077
|
+
configureType: jest.fn(() => callOrder.push('configureType')),
|
|
4078
|
+
weightType: jest.fn(() => callOrder.push('weightType')),
|
|
4079
|
+
headers: jest.fn(() => callOrder.push('headers')),
|
|
4080
|
+
hideBulkActions: jest.fn(() => callOrder.push('hideBulkActions')),
|
|
4081
|
+
|
|
4082
|
+
mapGroup: jest.fn(),
|
|
4083
|
+
ignoreGroup: jest.fn(),
|
|
4084
|
+
mapType: jest.fn(() => callOrder.push('mapType')),
|
|
4085
|
+
ignoreType: jest.fn(() => callOrder.push('ignoreType')),
|
|
4086
|
+
};
|
|
4087
|
+
|
|
4088
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4089
|
+
|
|
4090
|
+
const productMetadata: ProductMetadata = {
|
|
4091
|
+
name: 'order-test',
|
|
4092
|
+
label: 'Order Test',
|
|
4093
|
+
};
|
|
4094
|
+
|
|
4095
|
+
const config: ProductChildPage[] = [
|
|
4096
|
+
{
|
|
4097
|
+
type: 'apps.deployment',
|
|
4098
|
+
weight: 10,
|
|
4099
|
+
headers: [{ name: 'col1', label: 'Col1' }],
|
|
4100
|
+
hideBulkActions: true,
|
|
4101
|
+
overrideListResourceName: 'Deployments',
|
|
4102
|
+
hideFromNav: true,
|
|
4103
|
+
},
|
|
4104
|
+
];
|
|
4105
|
+
|
|
4106
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4107
|
+
|
|
4108
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4109
|
+
|
|
4110
|
+
// weight must be the last thing set
|
|
4111
|
+
const weightIndex = callOrder.indexOf('weightType');
|
|
4112
|
+
const configureIndex = callOrder.indexOf('configureType');
|
|
4113
|
+
|
|
4114
|
+
expect(weightIndex).toBeGreaterThan(configureIndex);
|
|
4115
|
+
expect(callOrder[callOrder.length - 1]).toBe('weightType');
|
|
4116
|
+
});
|
|
4117
|
+
});
|
|
4118
|
+
|
|
4119
|
+
describe('product-level: renameGroups support', () => {
|
|
4120
|
+
it('should call DSL mapGroup for each renameGroups entry in product metadata', () => {
|
|
4121
|
+
const mockPlugin = createMockPlugin();
|
|
4122
|
+
const mockStore = createMockStore();
|
|
4123
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4124
|
+
|
|
4125
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4126
|
+
|
|
4127
|
+
const productMetadata: ProductMetadata = {
|
|
4128
|
+
name: 'maptogroup-test',
|
|
4129
|
+
label: 'MapToGroup Test',
|
|
4130
|
+
renameGroups: [
|
|
4131
|
+
{ groupSelector: /some\.regex/, newName: 'my-group' },
|
|
4132
|
+
{ groupSelector: 'exact.match', newName: 'other-group' },
|
|
4133
|
+
],
|
|
4134
|
+
};
|
|
4135
|
+
|
|
4136
|
+
const config: ProductChildPage[] = [
|
|
4137
|
+
{
|
|
4138
|
+
name: 'overview', label: 'Overview', component: { name: 'OverviewPage' }
|
|
4139
|
+
},
|
|
4140
|
+
];
|
|
4141
|
+
|
|
4142
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4143
|
+
|
|
4144
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4145
|
+
|
|
4146
|
+
expect(mockDSL.mapGroup).toHaveBeenCalledTimes(2);
|
|
4147
|
+
expect(mockDSL.mapGroup).toHaveBeenCalledWith(/some\.regex/, 'my-group');
|
|
4148
|
+
expect(mockDSL.mapGroup).toHaveBeenCalledWith('exact.match', 'other-group');
|
|
4149
|
+
});
|
|
4150
|
+
|
|
4151
|
+
it('should not call mapGroup when no renameGroups entries exist', () => {
|
|
4152
|
+
const mockPlugin = createMockPlugin();
|
|
4153
|
+
const mockStore = createMockStore();
|
|
4154
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4155
|
+
|
|
4156
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4157
|
+
|
|
4158
|
+
const productMetadata: ProductMetadata = {
|
|
4159
|
+
name: 'no-maptogroup',
|
|
4160
|
+
label: 'No MapToGroup',
|
|
4161
|
+
};
|
|
4162
|
+
|
|
4163
|
+
const config: ProductChildPage[] = [
|
|
4164
|
+
{
|
|
4165
|
+
name: 'overview', label: 'Overview', component: { name: 'OverviewPage' }
|
|
4166
|
+
},
|
|
4167
|
+
];
|
|
4168
|
+
|
|
4169
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4170
|
+
|
|
4171
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4172
|
+
|
|
4173
|
+
expect(mockDSL.mapGroup).not.toHaveBeenCalled();
|
|
4174
|
+
});
|
|
4175
|
+
});
|
|
4176
|
+
|
|
4177
|
+
describe('product-level: ignoreGroups support', () => {
|
|
4178
|
+
it('should call DSL ignoreGroup with callback when condition is provided', () => {
|
|
4179
|
+
const mockPlugin = createMockPlugin();
|
|
4180
|
+
const mockStore = createMockStore();
|
|
4181
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4182
|
+
|
|
4183
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4184
|
+
|
|
4185
|
+
const cbFn = jest.fn(() => true);
|
|
4186
|
+
|
|
4187
|
+
const productMetadata: ProductMetadata = {
|
|
4188
|
+
name: 'ignoregroups-test',
|
|
4189
|
+
label: 'IgnoreGroups Test',
|
|
4190
|
+
ignoreGroups: [
|
|
4191
|
+
{ groupSelector: 'hidden-group', condition: cbFn },
|
|
4192
|
+
],
|
|
4193
|
+
};
|
|
4194
|
+
|
|
4195
|
+
const config: ProductChildPage[] = [
|
|
4196
|
+
{
|
|
4197
|
+
name: 'overview', label: 'Overview', component: { name: 'OverviewPage' }
|
|
4198
|
+
},
|
|
4199
|
+
];
|
|
4200
|
+
|
|
4201
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4202
|
+
|
|
4203
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4204
|
+
|
|
4205
|
+
expect(mockDSL.ignoreGroup).toHaveBeenCalledTimes(1);
|
|
4206
|
+
expect(mockDSL.ignoreGroup).toHaveBeenCalledWith('hidden-group', cbFn);
|
|
4207
|
+
});
|
|
4208
|
+
|
|
4209
|
+
it('should call DSL ignoreGroup without callback when condition is not provided (unconditional hide)', () => {
|
|
4210
|
+
const mockPlugin = createMockPlugin();
|
|
4211
|
+
const mockStore = createMockStore();
|
|
4212
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4213
|
+
|
|
4214
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4215
|
+
|
|
4216
|
+
const productMetadata: ProductMetadata = {
|
|
4217
|
+
name: 'ignoregroups-unconditional',
|
|
4218
|
+
label: 'IgnoreGroups Unconditional',
|
|
4219
|
+
ignoreGroups: [
|
|
4220
|
+
{ groupSelector: 'always-hidden' },
|
|
4221
|
+
],
|
|
4222
|
+
};
|
|
4223
|
+
|
|
4224
|
+
const config: ProductChildPage[] = [
|
|
4225
|
+
{
|
|
4226
|
+
name: 'overview', label: 'Overview', component: { name: 'OverviewPage' }
|
|
4227
|
+
},
|
|
4228
|
+
];
|
|
4229
|
+
|
|
4230
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4231
|
+
|
|
4232
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4233
|
+
|
|
4234
|
+
expect(mockDSL.ignoreGroup).toHaveBeenCalledTimes(1);
|
|
4235
|
+
expect(mockDSL.ignoreGroup).toHaveBeenCalledWith('always-hidden');
|
|
4236
|
+
});
|
|
4237
|
+
|
|
4238
|
+
it('should support regex patterns in ignoreGroups', () => {
|
|
4239
|
+
const mockPlugin = createMockPlugin();
|
|
4240
|
+
const mockStore = createMockStore();
|
|
4241
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4242
|
+
|
|
4243
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4244
|
+
|
|
4245
|
+
const productMetadata: ProductMetadata = {
|
|
4246
|
+
name: 'ignoregroups-regex',
|
|
4247
|
+
label: 'IgnoreGroups Regex',
|
|
4248
|
+
ignoreGroups: [
|
|
4249
|
+
{ groupSelector: /^internal-.*/ },
|
|
4250
|
+
],
|
|
4251
|
+
};
|
|
4252
|
+
|
|
4253
|
+
const config: ProductChildPage[] = [
|
|
4254
|
+
{
|
|
4255
|
+
name: 'overview', label: 'Overview', component: { name: 'OverviewPage' }
|
|
4256
|
+
},
|
|
4257
|
+
];
|
|
4258
|
+
|
|
4259
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4260
|
+
|
|
4261
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4262
|
+
|
|
4263
|
+
expect(mockDSL.ignoreGroup).toHaveBeenCalledTimes(1);
|
|
4264
|
+
expect(mockDSL.ignoreGroup).toHaveBeenCalledWith(/^internal-.*/);
|
|
4265
|
+
});
|
|
4266
|
+
|
|
4267
|
+
it('should not call ignoreGroup when no ignoreGroups entries exist', () => {
|
|
4268
|
+
const mockPlugin = createMockPlugin();
|
|
4269
|
+
const mockStore = createMockStore();
|
|
4270
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4271
|
+
|
|
4272
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4273
|
+
|
|
4274
|
+
const productMetadata: ProductMetadata = {
|
|
4275
|
+
name: 'no-ignoregroups',
|
|
4276
|
+
label: 'No IgnoreGroups',
|
|
4277
|
+
};
|
|
4278
|
+
|
|
4279
|
+
const config: ProductChildPage[] = [
|
|
4280
|
+
{
|
|
4281
|
+
name: 'overview', label: 'Overview', component: { name: 'OverviewPage' }
|
|
4282
|
+
},
|
|
4283
|
+
];
|
|
4284
|
+
|
|
4285
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4286
|
+
|
|
4287
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4288
|
+
|
|
4289
|
+
expect(mockDSL.ignoreGroup).not.toHaveBeenCalled();
|
|
4290
|
+
});
|
|
4291
|
+
});
|
|
4292
|
+
|
|
4293
|
+
describe('product-level DSL options are not called when extending', () => {
|
|
4294
|
+
it('should not call mapGroup, ignoreGroup, or moveType when extending an existing product', () => {
|
|
4295
|
+
const mockPlugin = createMockPlugin();
|
|
4296
|
+
const mockStore = createMockStore();
|
|
4297
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4298
|
+
|
|
4299
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4300
|
+
|
|
4301
|
+
const config: ProductChildPage[] = [
|
|
4302
|
+
{
|
|
4303
|
+
name: 'overview', label: 'Overview', component: { name: 'OverviewPage' }
|
|
4304
|
+
},
|
|
4305
|
+
];
|
|
4306
|
+
|
|
4307
|
+
const pluginProduct = new PluginProduct(mockPlugin, StandardProductNames.EXPLORER, config);
|
|
4308
|
+
|
|
4309
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4310
|
+
|
|
4311
|
+
expect(mockDSL.mapGroup).not.toHaveBeenCalled();
|
|
4312
|
+
expect(mockDSL.ignoreGroup).not.toHaveBeenCalled();
|
|
4313
|
+
expect(mockDSL.moveType).not.toHaveBeenCalled();
|
|
4314
|
+
});
|
|
4315
|
+
});
|
|
4316
|
+
|
|
4317
|
+
describe('product-level: moveToGroup support', () => {
|
|
4318
|
+
it('should call basicType and moveType to move a resource type into a group', () => {
|
|
4319
|
+
const mockPlugin = createMockPlugin();
|
|
4320
|
+
const mockStore = createMockStore();
|
|
4321
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4322
|
+
|
|
4323
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4324
|
+
|
|
4325
|
+
const monitoringGroup: ProductChildGroup = {
|
|
4326
|
+
name: 'monitoring',
|
|
4327
|
+
label: 'Monitoring',
|
|
4328
|
+
children: [
|
|
4329
|
+
{
|
|
4330
|
+
name: 'alerts', label: 'Alerts', component: { name: 'AlertsPage' }
|
|
4331
|
+
},
|
|
4332
|
+
],
|
|
4333
|
+
};
|
|
4334
|
+
|
|
4335
|
+
const productMetadata: ProductMetadata = {
|
|
4336
|
+
name: 'my-app',
|
|
4337
|
+
label: 'My App',
|
|
4338
|
+
moveToGroup: [
|
|
4339
|
+
{ entryId: 'pod', groupName: 'monitoring' },
|
|
4340
|
+
],
|
|
4341
|
+
};
|
|
4342
|
+
|
|
4343
|
+
const config: ProductChild[] = [
|
|
4344
|
+
monitoringGroup,
|
|
4345
|
+
{ type: 'pod' },
|
|
4346
|
+
];
|
|
4347
|
+
|
|
4348
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4349
|
+
|
|
4350
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4351
|
+
|
|
4352
|
+
// basicType re-registers the page under the resolved group for the nav tree
|
|
4353
|
+
expect(mockDSL.basicType).toHaveBeenCalledWith(['pod'], 'myapp-monitoring');
|
|
4354
|
+
// moveType also called for resource types (non-basic view modes)
|
|
4355
|
+
expect(mockDSL.moveType).toHaveBeenCalledTimes(1);
|
|
4356
|
+
expect(mockDSL.moveType).toHaveBeenCalledWith('pod', 'myapp-monitoring', undefined);
|
|
4357
|
+
});
|
|
4358
|
+
|
|
4359
|
+
it('should call basicType but NOT moveType when moving a custom page into a group', () => {
|
|
4360
|
+
const mockPlugin = createMockPlugin();
|
|
4361
|
+
const mockStore = createMockStore();
|
|
4362
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4363
|
+
|
|
4364
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4365
|
+
|
|
4366
|
+
const monitoringGroup: ProductChildGroup = {
|
|
4367
|
+
name: 'monitoring',
|
|
4368
|
+
label: 'Monitoring',
|
|
4369
|
+
children: [
|
|
4370
|
+
{
|
|
4371
|
+
name: 'alerts', label: 'Alerts', component: { name: 'AlertsPage' }
|
|
4372
|
+
},
|
|
4373
|
+
],
|
|
4374
|
+
};
|
|
4375
|
+
|
|
4376
|
+
const customPage: ProductChildCustomPage = {
|
|
4377
|
+
name: 'dashboard', label: 'Dashboard', component: { name: 'DashboardPage' }
|
|
4378
|
+
};
|
|
4379
|
+
|
|
4380
|
+
const productMetadata: ProductMetadata = {
|
|
4381
|
+
name: 'my-app',
|
|
4382
|
+
label: 'My App',
|
|
4383
|
+
moveToGroup: [
|
|
4384
|
+
{ entryId: 'dashboard', groupName: 'monitoring' },
|
|
4385
|
+
],
|
|
4386
|
+
};
|
|
4387
|
+
|
|
4388
|
+
const config: ProductChild[] = [monitoringGroup, customPage];
|
|
4389
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4390
|
+
|
|
4391
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4392
|
+
|
|
4393
|
+
// basicType re-registers the custom page under the resolved group
|
|
4394
|
+
expect(mockDSL.basicType).toHaveBeenCalledWith(['myapp-dashboard'], 'myapp-monitoring');
|
|
4395
|
+
// moveType is NOT called for custom pages (no schema to match against)
|
|
4396
|
+
expect(mockDSL.moveType).not.toHaveBeenCalled();
|
|
4397
|
+
});
|
|
4398
|
+
|
|
4399
|
+
it('should pass weight to DSL moveType when specified', () => {
|
|
4400
|
+
const mockPlugin = createMockPlugin();
|
|
4401
|
+
const mockStore = createMockStore();
|
|
4402
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4403
|
+
|
|
4404
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4405
|
+
|
|
4406
|
+
const myGroup: ProductChildGroup = {
|
|
4407
|
+
name: 'resources',
|
|
4408
|
+
label: 'Resources',
|
|
4409
|
+
children: [
|
|
4410
|
+
{
|
|
4411
|
+
name: 'overview', label: 'Overview', component: { name: 'OverviewPage' }
|
|
4412
|
+
},
|
|
4413
|
+
],
|
|
4414
|
+
};
|
|
4415
|
+
|
|
4416
|
+
const productMetadata: ProductMetadata = {
|
|
4417
|
+
name: 'my-app',
|
|
4418
|
+
label: 'My App',
|
|
4419
|
+
moveToGroup: [
|
|
4420
|
+
{
|
|
4421
|
+
entryId: 'apps.deployment', groupName: 'resources', weight: 10
|
|
4422
|
+
},
|
|
4423
|
+
],
|
|
4424
|
+
};
|
|
4425
|
+
|
|
4426
|
+
const config: ProductChild[] = [myGroup, { type: 'apps.deployment' }];
|
|
4427
|
+
|
|
4428
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4429
|
+
|
|
4430
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4431
|
+
|
|
4432
|
+
expect(mockDSL.basicType).toHaveBeenCalledWith(['apps.deployment'], 'myapp-resources');
|
|
4433
|
+
expect(mockDSL.moveType).toHaveBeenCalledWith('apps.deployment', 'myapp-resources', 10);
|
|
4434
|
+
});
|
|
4435
|
+
|
|
4436
|
+
it('should throw when moveToGroup references a groupName that does not exist in the config', () => {
|
|
4437
|
+
const mockPlugin = createMockPlugin();
|
|
4438
|
+
const mockStore = createMockStore();
|
|
4439
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4440
|
+
|
|
4441
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4442
|
+
|
|
4443
|
+
const productMetadata: ProductMetadata = {
|
|
4444
|
+
name: 'my-app',
|
|
4445
|
+
label: 'My App',
|
|
4446
|
+
moveToGroup: [
|
|
4447
|
+
{ entryId: 'pod', groupName: 'nonexistent-group' },
|
|
4448
|
+
],
|
|
4449
|
+
};
|
|
4450
|
+
|
|
4451
|
+
const config: ProductChildPage[] = [
|
|
4452
|
+
{ type: 'pod' },
|
|
4453
|
+
];
|
|
4454
|
+
|
|
4455
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4456
|
+
|
|
4457
|
+
expect(() => {
|
|
4458
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4459
|
+
}).toThrow('moveToGroup target group "nonexistent-group" not found');
|
|
4460
|
+
});
|
|
4461
|
+
|
|
4462
|
+
it('should throw when moveToGroup entryId does not match any registered page', () => {
|
|
4463
|
+
const mockPlugin = createMockPlugin();
|
|
4464
|
+
const mockStore = createMockStore();
|
|
4465
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4466
|
+
|
|
4467
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4468
|
+
|
|
4469
|
+
const myGroup: ProductChildGroup = {
|
|
4470
|
+
name: 'monitoring',
|
|
4471
|
+
label: 'Monitoring',
|
|
4472
|
+
children: [
|
|
4473
|
+
{
|
|
4474
|
+
name: 'alerts', label: 'Alerts', component: { name: 'AlertsPage' }
|
|
4475
|
+
},
|
|
4476
|
+
],
|
|
4477
|
+
};
|
|
4478
|
+
|
|
4479
|
+
const productMetadata: ProductMetadata = {
|
|
4480
|
+
name: 'my-app',
|
|
4481
|
+
label: 'My App',
|
|
4482
|
+
moveToGroup: [
|
|
4483
|
+
{ entryId: 'nonexistent-page', groupName: 'monitoring' },
|
|
4484
|
+
],
|
|
4485
|
+
};
|
|
4486
|
+
|
|
4487
|
+
const config: ProductChild[] = [myGroup];
|
|
4488
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4489
|
+
|
|
4490
|
+
expect(() => {
|
|
4491
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4492
|
+
}).toThrow('moveToGroup entryId "nonexistent-page" not found');
|
|
4493
|
+
});
|
|
4494
|
+
|
|
4495
|
+
it('should not call moveType when no moveToGroup entries exist', () => {
|
|
4496
|
+
const mockPlugin = createMockPlugin();
|
|
4497
|
+
const mockStore = createMockStore();
|
|
4498
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4499
|
+
|
|
4500
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4501
|
+
|
|
4502
|
+
const productMetadata: ProductMetadata = {
|
|
4503
|
+
name: 'no-move',
|
|
4504
|
+
label: 'No Move',
|
|
4505
|
+
};
|
|
4506
|
+
|
|
4507
|
+
const config: ProductChildPage[] = [
|
|
4508
|
+
{
|
|
4509
|
+
name: 'overview', label: 'Overview', component: { name: 'OverviewPage' }
|
|
4510
|
+
},
|
|
4511
|
+
];
|
|
4512
|
+
|
|
4513
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4514
|
+
|
|
4515
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4516
|
+
|
|
4517
|
+
expect(mockDSL.moveType).not.toHaveBeenCalled();
|
|
4518
|
+
});
|
|
4519
|
+
|
|
4520
|
+
it('should support multiple moveToGroup entries targeting different groups', () => {
|
|
4521
|
+
const mockPlugin = createMockPlugin();
|
|
4522
|
+
const mockStore = createMockStore();
|
|
4523
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4524
|
+
|
|
4525
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4526
|
+
|
|
4527
|
+
const networkingGroup: ProductChildGroup = {
|
|
4528
|
+
name: 'networking',
|
|
4529
|
+
label: 'Networking',
|
|
4530
|
+
children: [
|
|
4531
|
+
{
|
|
4532
|
+
name: 'net-overview', label: 'Overview', component: { name: 'NetOverview' }
|
|
4533
|
+
},
|
|
4534
|
+
],
|
|
4535
|
+
};
|
|
4536
|
+
|
|
4537
|
+
const storageGroup: ProductChildGroup = {
|
|
4538
|
+
name: 'storage',
|
|
4539
|
+
label: 'Storage',
|
|
4540
|
+
children: [
|
|
4541
|
+
{
|
|
4542
|
+
name: 'storage-overview', label: 'Overview', component: { name: 'StorageOverview' }
|
|
4543
|
+
},
|
|
4544
|
+
],
|
|
4545
|
+
};
|
|
4546
|
+
|
|
4547
|
+
const productMetadata: ProductMetadata = {
|
|
4548
|
+
name: 'my-app',
|
|
4549
|
+
label: 'My App',
|
|
4550
|
+
moveToGroup: [
|
|
4551
|
+
{ entryId: 'networking.ingress', groupName: 'networking' },
|
|
4552
|
+
{ entryId: 'storage.pvc', groupName: 'storage' },
|
|
4553
|
+
],
|
|
4554
|
+
};
|
|
4555
|
+
|
|
4556
|
+
const config: ProductChild[] = [
|
|
4557
|
+
networkingGroup,
|
|
4558
|
+
storageGroup,
|
|
4559
|
+
{ type: 'networking.ingress' },
|
|
4560
|
+
{ type: 'storage.pvc' },
|
|
4561
|
+
];
|
|
4562
|
+
|
|
4563
|
+
const pluginProduct = new PluginProduct(mockPlugin, productMetadata, config);
|
|
4564
|
+
|
|
4565
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4566
|
+
|
|
4567
|
+
expect(mockDSL.basicType).toHaveBeenCalledWith(['networking.ingress'], 'myapp-networking');
|
|
4568
|
+
expect(mockDSL.basicType).toHaveBeenCalledWith(['storage.pvc'], 'myapp-storage');
|
|
4569
|
+
expect(mockDSL.moveType).toHaveBeenCalledTimes(2);
|
|
4570
|
+
expect(mockDSL.moveType).toHaveBeenCalledWith('networking.ingress', 'myapp-networking', undefined);
|
|
4571
|
+
expect(mockDSL.moveType).toHaveBeenCalledWith('storage.pvc', 'myapp-storage', undefined);
|
|
4572
|
+
});
|
|
4573
|
+
});
|
|
4574
|
+
|
|
4575
|
+
describe('resource page DSL options work when extending a product', () => {
|
|
4576
|
+
it('should support headers, hideBulkActions, overrideListResourceName, hideFromNav on resource pages when extending', () => {
|
|
4577
|
+
const mockPlugin = createMockPlugin();
|
|
4578
|
+
const mockStore = createMockStore();
|
|
4579
|
+
const mockDSL = (mockPlugin.DSL as jest.Mock)();
|
|
4580
|
+
|
|
4581
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
4582
|
+
|
|
4583
|
+
const testHeaders = [{ name: 'col1', label: 'Column 1' }];
|
|
4584
|
+
|
|
4585
|
+
const config: ProductChildPage[] = [
|
|
4586
|
+
{
|
|
4587
|
+
type: 'custom.resource.type',
|
|
4588
|
+
headers: testHeaders,
|
|
4589
|
+
hideBulkActions: true,
|
|
4590
|
+
overrideListResourceName: 'Custom Name',
|
|
4591
|
+
hideFromNav: true,
|
|
4592
|
+
},
|
|
4593
|
+
];
|
|
4594
|
+
|
|
4595
|
+
const pluginProduct = new PluginProduct(mockPlugin, StandardProductNames.EXPLORER, config);
|
|
4596
|
+
|
|
4597
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
4598
|
+
|
|
4599
|
+
expect(mockDSL.headers).toHaveBeenCalledWith('custom.resource.type', testHeaders, undefined);
|
|
4600
|
+
expect(mockDSL.hideBulkActions).toHaveBeenCalledWith('custom.resource.type', true);
|
|
4601
|
+
expect(mockDSL.mapType).toHaveBeenCalledWith('custom.resource.type', 'Custom Name');
|
|
4602
|
+
expect(mockDSL.ignoreType).toHaveBeenCalledWith('custom.resource.type');
|
|
4603
|
+
});
|
|
4604
|
+
});
|
|
4605
|
+
});
|
|
4606
|
+
});
|
|
4607
|
+
|
|
4608
|
+
describe('addProduct duplicate guard', () => {
|
|
4609
|
+
it('should throw when addProduct is called twice with the same product name (object form)', () => {
|
|
4610
|
+
const plugin = new Plugin('test-extension');
|
|
4611
|
+
|
|
4612
|
+
const product: ProductMetadata = {
|
|
4613
|
+
name: 'my-product',
|
|
4614
|
+
label: 'My Product',
|
|
4615
|
+
};
|
|
4616
|
+
|
|
4617
|
+
const config: ProductChildPage[] = [
|
|
4618
|
+
{
|
|
4619
|
+
name: 'page-a', label: 'Page A', component: { name: 'PageA' }
|
|
4620
|
+
},
|
|
4621
|
+
];
|
|
4622
|
+
|
|
4623
|
+
plugin.addProduct(product, config);
|
|
4624
|
+
|
|
4625
|
+
expect(() => {
|
|
4626
|
+
plugin.addProduct(product, [{
|
|
4627
|
+
name: 'page-b', label: 'Page B', component: { name: 'PageB' }
|
|
4628
|
+
}]);
|
|
4629
|
+
}).toThrow('addProduct can only be called once per product');
|
|
4630
|
+
});
|
|
4631
|
+
|
|
4632
|
+
it('should throw when addProduct is called twice with the same product name (string form)', () => {
|
|
4633
|
+
const plugin = new Plugin('test-extension');
|
|
4634
|
+
|
|
4635
|
+
plugin.addProduct('my-product');
|
|
4636
|
+
|
|
4637
|
+
expect(() => {
|
|
4638
|
+
plugin.addProduct('my-product');
|
|
4639
|
+
}).toThrow('addProduct can only be called once per product');
|
|
4640
|
+
});
|
|
4641
|
+
|
|
4642
|
+
it('should throw when addProduct is called twice mixing string and object form for the same name', () => {
|
|
4643
|
+
const plugin = new Plugin('test-extension');
|
|
4644
|
+
|
|
4645
|
+
plugin.addProduct('my-product');
|
|
4646
|
+
|
|
4647
|
+
expect(() => {
|
|
4648
|
+
plugin.addProduct({ name: 'my-product', label: 'My Product' }, []);
|
|
4649
|
+
}).toThrow('addProduct can only be called once per product');
|
|
4650
|
+
});
|
|
4651
|
+
|
|
4652
|
+
it('should allow addProduct for different product names', () => {
|
|
4653
|
+
const plugin = new Plugin('test-extension');
|
|
4654
|
+
|
|
4655
|
+
plugin.addProduct('product-a');
|
|
4656
|
+
|
|
4657
|
+
expect(() => {
|
|
4658
|
+
plugin.addProduct('product-b');
|
|
4659
|
+
}).not.toThrow();
|
|
4660
|
+
|
|
4661
|
+
expect(plugin.productConfigs).toHaveLength(2);
|
|
4662
|
+
});
|
|
4663
|
+
|
|
4664
|
+
it('should allow addProduct and extendProduct for the same name (extending is separate)', () => {
|
|
4665
|
+
const plugin = new Plugin('test-extension');
|
|
4666
|
+
|
|
4667
|
+
plugin.addProduct('my-product');
|
|
4668
|
+
|
|
4669
|
+
expect(() => {
|
|
4670
|
+
plugin.extendProduct('explorer', [{
|
|
4671
|
+
name: 'extra-page', label: 'Extra', component: { name: 'Extra' }
|
|
4672
|
+
}]);
|
|
4673
|
+
}).not.toThrow();
|
|
4674
|
+
|
|
4675
|
+
expect(plugin.productConfigs).toHaveLength(2);
|
|
4676
|
+
});
|
|
4677
|
+
|
|
4678
|
+
it('should throw when addProduct is called twice with single page product form', () => {
|
|
4679
|
+
const plugin = new Plugin('test-extension');
|
|
4680
|
+
|
|
4681
|
+
const singlePage: ProductSinglePage = {
|
|
4682
|
+
name: 'my-dashboard',
|
|
4683
|
+
label: 'My Dashboard',
|
|
4684
|
+
component: { name: 'DashboardPage' },
|
|
4685
|
+
};
|
|
4686
|
+
|
|
4687
|
+
plugin.addProduct(singlePage);
|
|
4688
|
+
|
|
4689
|
+
expect(() => {
|
|
4690
|
+
plugin.addProduct(singlePage);
|
|
4691
|
+
}).toThrow('addProduct can only be called once per product');
|
|
3808
4692
|
});
|
|
3809
4693
|
});
|
|
3810
4694
|
});
|