@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.4
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/_layout.scss +4 -0
- package/assets/translations/en-us.yaml +144 -41
- 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/CruResource.vue +161 -14
- 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/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/RichTranslation.vue +5 -2
- package/components/Setting.vue +1 -0
- 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 +9 -4
- 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/GitRepoTargetTab.vue +0 -2
- package/components/fleet/HelmOpAdvancedTab.vue +19 -53
- package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
- package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -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/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/ResourceTabs/index.vue +1 -0
- package/components/form/__tests__/NameNsDescription.test.ts +75 -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 +4 -0
- 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 +9 -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/home-links.js +1 -1
- package/config/labels-annotations.js +1 -0
- package/config/product/explorer.js +17 -4
- package/config/product/manager.js +2 -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/settings.ts +0 -2
- package/config/table-headers.js +3 -4
- package/config/types.js +9 -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 +30 -4
- package/detail/workload/index.vue +12 -55
- package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
- 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/fleet.cattle.io.helmop.vue +491 -136
- package/edit/management.cattle.io.user.vue +5 -2
- package/edit/provisioning.cattle.io.cluster/rke2.vue +84 -10
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
- 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 +49 -0
- 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 +18 -2
- package/models/management.cattle.io.node.js +44 -3
- package/models/namespace.js +1 -1
- package/models/pod.js +33 -1
- package/models/provisioning.cattle.io.cluster.js +5 -5
- package/models/workload.js +108 -13
- 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/chart.vue +2 -1
- package/pages/c/_cluster/apps/charts/index.vue +48 -10
- package/pages/c/_cluster/apps/charts/install.vue +122 -116
- 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/plugins/clean-html.d.ts +9 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +93 -0
- package/plugins/dashboard-store/resource-class.js +62 -7
- 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/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/types.ts +3 -0
- 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__/notifications.test.ts +434 -0
- package/store/catalog.js +57 -0
- package/store/plugins.js +7 -4
- package/types/components/buttonGroup.ts +5 -0
- package/types/shell/index.d.ts +104 -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__/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/provider.ts +12 -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/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,234 @@
|
|
|
1
|
+
import { nextTick } from 'vue';
|
|
2
|
+
import { mount, type VueWrapper, flushPromises } from '@vue/test-utils';
|
|
3
|
+
import { _EDIT } from '@shell/config/query-params';
|
|
4
|
+
|
|
5
|
+
import GitHub from '@shell/edit/auth/github.vue';
|
|
6
|
+
|
|
7
|
+
jest.mock('@shell/utils/clipboard', () => {
|
|
8
|
+
return { copyTextToClipboard: jest.fn(() => Promise.resolve({})) };
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const validClientId = 'github-client-id';
|
|
12
|
+
const validClientSecret = 'github-client-secret';
|
|
13
|
+
const validAppId = '12345';
|
|
14
|
+
const validPrivateKey = '-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----';
|
|
15
|
+
const validTargetUrl = 'https://github.mycompany.com';
|
|
16
|
+
|
|
17
|
+
const mockGitHubModel = {
|
|
18
|
+
enabled: false,
|
|
19
|
+
id: 'github',
|
|
20
|
+
clientId: validClientId,
|
|
21
|
+
clientSecret: validClientSecret,
|
|
22
|
+
hostname: 'github.com',
|
|
23
|
+
tls: true,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const mockGitHubAppModel = {
|
|
27
|
+
...mockGitHubModel,
|
|
28
|
+
id: 'githubapp',
|
|
29
|
+
appId: validAppId,
|
|
30
|
+
privateKey: validPrivateKey,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const requiredSetup = (modelOverrides = {}, dataOverrides = {}) => ({
|
|
34
|
+
data() {
|
|
35
|
+
return {
|
|
36
|
+
isEnabling: false,
|
|
37
|
+
editConfig: false,
|
|
38
|
+
model: { ...mockGitHubModel, ...modelOverrides },
|
|
39
|
+
serverSetting: null,
|
|
40
|
+
errors: [],
|
|
41
|
+
originalModel: null,
|
|
42
|
+
principals: [],
|
|
43
|
+
authConfigName: 'github',
|
|
44
|
+
targetType: 'public',
|
|
45
|
+
targetUrl: '',
|
|
46
|
+
...dataOverrides,
|
|
47
|
+
} as any;
|
|
48
|
+
},
|
|
49
|
+
global: {
|
|
50
|
+
mocks: {
|
|
51
|
+
$fetchState: { pending: false },
|
|
52
|
+
$store: {
|
|
53
|
+
getters: {
|
|
54
|
+
currentStore: () => 'current_store',
|
|
55
|
+
'current_store/schemaFor': jest.fn(),
|
|
56
|
+
'current_store/all': jest.fn(),
|
|
57
|
+
'i18n/t': (val: string) => val,
|
|
58
|
+
'i18n/exists': jest.fn(),
|
|
59
|
+
},
|
|
60
|
+
dispatch: jest.fn(),
|
|
61
|
+
},
|
|
62
|
+
$route: { query: { AS: '' }, params: { id: 'github' } },
|
|
63
|
+
$router: { applyQuery: jest.fn() },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
props: {
|
|
67
|
+
value: {},
|
|
68
|
+
mode: _EDIT,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('github.vue', () => {
|
|
73
|
+
describe('GitHub provider', () => {
|
|
74
|
+
let wrapper: VueWrapper<any, any>;
|
|
75
|
+
|
|
76
|
+
beforeEach(() => {
|
|
77
|
+
wrapper = mount(GitHub, { ...requiredSetup() });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
afterEach(() => {
|
|
81
|
+
wrapper.unmount();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('save button disabled', () => {
|
|
85
|
+
it('when clientId is empty', async() => {
|
|
86
|
+
wrapper.setData({ model: { clientId: '' } });
|
|
87
|
+
await wrapper.vm.validateAllFields();
|
|
88
|
+
await flushPromises();
|
|
89
|
+
|
|
90
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
91
|
+
|
|
92
|
+
expect(saveButton.disabled).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('when clientSecret is empty', async() => {
|
|
96
|
+
wrapper.setData({ model: { clientSecret: '' } });
|
|
97
|
+
await wrapper.vm.validateAllFields();
|
|
98
|
+
await flushPromises();
|
|
99
|
+
|
|
100
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
101
|
+
|
|
102
|
+
expect(saveButton.disabled).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('when both clientId and clientSecret are empty', async() => {
|
|
106
|
+
wrapper.setData({ model: { clientId: '' } });
|
|
107
|
+
wrapper.setData({ model: { clientSecret: '' } });
|
|
108
|
+
await wrapper.vm.validateAllFields();
|
|
109
|
+
await flushPromises();
|
|
110
|
+
|
|
111
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
112
|
+
|
|
113
|
+
expect(saveButton.disabled).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('save button enabled', () => {
|
|
118
|
+
it('when clientId and clientSecret are filled', async() => {
|
|
119
|
+
await nextTick();
|
|
120
|
+
|
|
121
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
122
|
+
|
|
123
|
+
expect(saveButton.disabled).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('when provider is already enabled and not editing config', async() => {
|
|
127
|
+
wrapper.setData({ model: { ...mockGitHubModel, enabled: true }, editConfig: false });
|
|
128
|
+
await nextTick();
|
|
129
|
+
|
|
130
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
131
|
+
|
|
132
|
+
expect(saveButton.disabled).toBe(false);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('GitHub App provider', () => {
|
|
138
|
+
let wrapper: VueWrapper<any, any>;
|
|
139
|
+
|
|
140
|
+
beforeEach(() => {
|
|
141
|
+
wrapper = mount(GitHub, { ...requiredSetup(mockGitHubAppModel) });
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
afterEach(() => {
|
|
145
|
+
wrapper.unmount();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe('save button disabled', () => {
|
|
149
|
+
it('when appId is empty', async() => {
|
|
150
|
+
wrapper.setData({ model: { appId: '' } });
|
|
151
|
+
await wrapper.vm.validateAllFields();
|
|
152
|
+
await flushPromises();
|
|
153
|
+
|
|
154
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
155
|
+
|
|
156
|
+
expect(saveButton.disabled).toBe(true);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('when privateKey is empty', async() => {
|
|
160
|
+
wrapper.vm.model.privateKey = '';
|
|
161
|
+
wrapper.setData({ model: { privateKey: '' } });
|
|
162
|
+
await wrapper.vm.validateAllFields();
|
|
163
|
+
await flushPromises();
|
|
164
|
+
|
|
165
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
166
|
+
|
|
167
|
+
expect(saveButton.disabled).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('save button enabled', () => {
|
|
172
|
+
it('when all required fields are filled', async() => {
|
|
173
|
+
await nextTick();
|
|
174
|
+
|
|
175
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
176
|
+
|
|
177
|
+
expect(saveButton.disabled).toBe(false);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('GitHub App fields not required for GitHub provider', () => {
|
|
183
|
+
let wrapper: VueWrapper<any, any>;
|
|
184
|
+
|
|
185
|
+
beforeEach(() => {
|
|
186
|
+
wrapper = mount(GitHub, { ...requiredSetup() });
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
afterEach(() => {
|
|
190
|
+
wrapper.unmount();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('save button is enabled even when appId and privateKey are absent', async() => {
|
|
194
|
+
await nextTick();
|
|
195
|
+
|
|
196
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
197
|
+
|
|
198
|
+
expect(saveButton.disabled).toBe(false);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('private GitHub Enterprise target', () => {
|
|
203
|
+
let wrapper: VueWrapper<any, any>;
|
|
204
|
+
|
|
205
|
+
beforeEach(() => {
|
|
206
|
+
wrapper = mount(
|
|
207
|
+
GitHub,
|
|
208
|
+
{ ...requiredSetup({}, { targetType: 'private', targetUrl: validTargetUrl }) }
|
|
209
|
+
);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
afterEach(() => {
|
|
213
|
+
wrapper.unmount();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('save button is disabled when targetUrl is empty', async() => {
|
|
217
|
+
wrapper.setData({ targetType: 'private', targetUrl: '' });
|
|
218
|
+
await wrapper.vm.validateAllFields();
|
|
219
|
+
await flushPromises();
|
|
220
|
+
|
|
221
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
222
|
+
|
|
223
|
+
expect(saveButton.disabled).toBe(true);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('save button is enabled when targetUrl is filled', async() => {
|
|
227
|
+
await nextTick();
|
|
228
|
+
|
|
229
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
230
|
+
|
|
231
|
+
expect(saveButton.disabled).toBe(false);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nextTick } from 'vue';
|
|
2
2
|
/* eslint-disable jest/no-hooks */
|
|
3
|
-
import { mount, type VueWrapper } from '@vue/test-utils';
|
|
3
|
+
import { mount, type VueWrapper, flushPromises } from '@vue/test-utils';
|
|
4
4
|
import { _EDIT } from '@shell/config/query-params';
|
|
5
5
|
|
|
6
6
|
import oidc from '@shell/edit/auth/oidc.vue';
|
|
@@ -9,6 +9,18 @@ jest.mock('@shell/utils/clipboard', () => {
|
|
|
9
9
|
return { copyTextToClipboard: jest.fn(() => Promise.resolve({})) };
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
+
const mockStore = {
|
|
13
|
+
getters: {
|
|
14
|
+
'i18n/t': (key: string) => key,
|
|
15
|
+
'i18n/exists': () => false,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
jest.mock('vuex', () => ({
|
|
20
|
+
...jest.requireActual('vuex'),
|
|
21
|
+
useStore: () => mockStore,
|
|
22
|
+
}));
|
|
23
|
+
|
|
12
24
|
const validClientId = 'rancheroidc';
|
|
13
25
|
const validClientSecret = 'TOkUxg0P67m1UXWNkJLHDPkUZFIKOWSq';
|
|
14
26
|
const validUrl = 'https://localhost:8080';
|
|
@@ -79,20 +91,26 @@ describe('oidc.vue', () => {
|
|
|
79
91
|
});
|
|
80
92
|
|
|
81
93
|
describe('have "Create" button disabled', () => {
|
|
82
|
-
|
|
94
|
+
// validateAllFields() replicates what happens on blur in a real browser: it runs
|
|
95
|
+
// the toTypedSchema validators for every registered field and flushes async errors.
|
|
96
|
+
it('given missing Auth endpoint URL', async() => {
|
|
83
97
|
wrapper.vm.model.authEndpoint = '';
|
|
84
98
|
wrapper.vm.model.scopes = 'openid profile email'; // set scope to be sure
|
|
85
99
|
wrapper.vm.oidcScope = ['openid', 'profile', 'email']; // TODO #13457: this is duplicated due wrong format of scopes
|
|
100
|
+
await wrapper.vm.validateAllFields();
|
|
101
|
+
await flushPromises();
|
|
86
102
|
|
|
87
103
|
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
88
104
|
|
|
89
105
|
expect(saveButton.disabled).toBe(true);
|
|
90
106
|
});
|
|
91
107
|
|
|
92
|
-
it('given missing required basic scopes', () => {
|
|
93
|
-
wrapper.vm.model.authEndpoint =
|
|
108
|
+
it('given missing required basic scopes', async() => {
|
|
109
|
+
wrapper.vm.model.authEndpoint = validAuthEndpoint; // set auth endpoint to be sure
|
|
94
110
|
wrapper.vm.model.scopes = 'something else'; // set wrong scope
|
|
95
111
|
wrapper.vm.oidcScope = ['something', 'else']; // TODO #13457: this is duplicated due wrong format of scopes
|
|
112
|
+
await wrapper.vm.validateAllFields();
|
|
113
|
+
await flushPromises();
|
|
96
114
|
|
|
97
115
|
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
98
116
|
|
|
@@ -101,7 +119,8 @@ describe('oidc.vue', () => {
|
|
|
101
119
|
|
|
102
120
|
it('when provider is disabled and editing config before fields are filled in', async() => {
|
|
103
121
|
wrapper.setData({ model: {}, editConfig: true });
|
|
104
|
-
await
|
|
122
|
+
await wrapper.vm.validateAllFields();
|
|
123
|
+
await flushPromises();
|
|
105
124
|
|
|
106
125
|
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
107
126
|
|
|
@@ -110,7 +129,8 @@ describe('oidc.vue', () => {
|
|
|
110
129
|
|
|
111
130
|
it('when provider is disabled and editing config after required fields and scope is missing openid', async() => {
|
|
112
131
|
wrapper.setData({ oidcUrls: { url: validUrl, realm: validRealm } });
|
|
113
|
-
await
|
|
132
|
+
await wrapper.vm.validateAllFields();
|
|
133
|
+
await flushPromises();
|
|
114
134
|
|
|
115
135
|
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
116
136
|
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { nextTick } from 'vue';
|
|
2
|
+
import { mount, type VueWrapper, flushPromises } from '@vue/test-utils';
|
|
3
|
+
import { _EDIT } from '@shell/config/query-params';
|
|
4
|
+
import Saml from '@shell/edit/auth/saml.vue';
|
|
5
|
+
|
|
6
|
+
const REQUIRED_FIELDS = [
|
|
7
|
+
'displayNameField',
|
|
8
|
+
'userNameField',
|
|
9
|
+
'uidField',
|
|
10
|
+
'groupsField',
|
|
11
|
+
'rancherApiHost',
|
|
12
|
+
'spKey',
|
|
13
|
+
'spCert',
|
|
14
|
+
'idpMetadataContent',
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
type RequiredField = typeof REQUIRED_FIELDS[number];
|
|
18
|
+
|
|
19
|
+
const validModel = {
|
|
20
|
+
enabled: false,
|
|
21
|
+
id: 'shibboleth',
|
|
22
|
+
displayNameField: 'givenName',
|
|
23
|
+
userNameField: 'uid',
|
|
24
|
+
uidField: 'uid',
|
|
25
|
+
groupsField: 'memberOf',
|
|
26
|
+
rancherApiHost: 'https://rancher.example.com',
|
|
27
|
+
spKey: '-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----',
|
|
28
|
+
spCert: '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----',
|
|
29
|
+
idpMetadataContent: '<EntityDescriptor/>',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const emptyRequiredFields: Record<RequiredField, string> = {
|
|
33
|
+
displayNameField: '',
|
|
34
|
+
userNameField: '',
|
|
35
|
+
uidField: '',
|
|
36
|
+
groupsField: '',
|
|
37
|
+
rancherApiHost: '',
|
|
38
|
+
spKey: '',
|
|
39
|
+
spCert: '',
|
|
40
|
+
idpMetadataContent: '',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const mountOptions = (model: object) => ({
|
|
44
|
+
data() {
|
|
45
|
+
return {
|
|
46
|
+
isEnabling: false,
|
|
47
|
+
editConfig: false,
|
|
48
|
+
model: { ...model },
|
|
49
|
+
serverSetting: null,
|
|
50
|
+
errors: [],
|
|
51
|
+
originalModel: null,
|
|
52
|
+
principals: [],
|
|
53
|
+
authConfigName: 'shibboleth',
|
|
54
|
+
} as any;
|
|
55
|
+
},
|
|
56
|
+
global: {
|
|
57
|
+
mocks: {
|
|
58
|
+
$fetchState: { pending: false },
|
|
59
|
+
$store: {
|
|
60
|
+
getters: {
|
|
61
|
+
currentStore: () => 'current_store',
|
|
62
|
+
'current_store/schemaFor': jest.fn(),
|
|
63
|
+
'current_store/all': jest.fn(),
|
|
64
|
+
'i18n/t': (val: string) => val,
|
|
65
|
+
'i18n/exists': jest.fn(),
|
|
66
|
+
},
|
|
67
|
+
dispatch: jest.fn(),
|
|
68
|
+
},
|
|
69
|
+
$route: { query: { AS: '' }, params: { id: 'shibboleth' } },
|
|
70
|
+
$router: { applyQuery: jest.fn() },
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
props: {
|
|
74
|
+
value: {},
|
|
75
|
+
mode: _EDIT,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('saml.vue', () => {
|
|
80
|
+
describe('validationPassed computed', () => {
|
|
81
|
+
let wrapper: VueWrapper<any, any>;
|
|
82
|
+
|
|
83
|
+
afterEach(() => {
|
|
84
|
+
wrapper.unmount();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('returns true when all required fields are filled', async() => {
|
|
88
|
+
wrapper = mount(Saml, mountOptions(validModel));
|
|
89
|
+
await flushPromises();
|
|
90
|
+
|
|
91
|
+
expect(wrapper.vm.validationPassed).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('returns false when all required fields are empty', async() => {
|
|
95
|
+
wrapper = mount(Saml, mountOptions({ ...validModel, ...emptyRequiredFields }));
|
|
96
|
+
await wrapper.vm.validateAllFields();
|
|
97
|
+
await flushPromises();
|
|
98
|
+
|
|
99
|
+
expect(wrapper.vm.validationPassed).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('returns true when provider is enabled and not editing config, regardless of field state', async() => {
|
|
103
|
+
wrapper = mount(Saml, mountOptions({
|
|
104
|
+
...validModel, ...emptyRequiredFields, enabled: true
|
|
105
|
+
}));
|
|
106
|
+
await wrapper.vm.validateAllFields();
|
|
107
|
+
await flushPromises();
|
|
108
|
+
|
|
109
|
+
expect(wrapper.vm.validationPassed).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('returns false when provider is enabled but editConfig is true and required fields are empty', async() => {
|
|
113
|
+
wrapper = mount(Saml, {
|
|
114
|
+
...mountOptions({
|
|
115
|
+
...validModel, ...emptyRequiredFields, enabled: true
|
|
116
|
+
}),
|
|
117
|
+
data() {
|
|
118
|
+
return {
|
|
119
|
+
isEnabling: false,
|
|
120
|
+
editConfig: true,
|
|
121
|
+
model: {
|
|
122
|
+
...validModel, ...emptyRequiredFields, enabled: true
|
|
123
|
+
},
|
|
124
|
+
serverSetting: null,
|
|
125
|
+
errors: [],
|
|
126
|
+
originalModel: null,
|
|
127
|
+
principals: [],
|
|
128
|
+
authConfigName: 'shibboleth',
|
|
129
|
+
} as any;
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
await wrapper.vm.validateAllFields();
|
|
133
|
+
await flushPromises();
|
|
134
|
+
|
|
135
|
+
expect(wrapper.vm.validationPassed).toBe(false);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('Enable button', () => {
|
|
140
|
+
describe('is disabled', () => {
|
|
141
|
+
let wrapper: VueWrapper<any, any>;
|
|
142
|
+
|
|
143
|
+
afterEach(() => {
|
|
144
|
+
wrapper.unmount();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('when all required fields are empty', async() => {
|
|
148
|
+
wrapper = mount(Saml, mountOptions({ ...validModel, ...emptyRequiredFields }));
|
|
149
|
+
await wrapper.vm.validateAllFields();
|
|
150
|
+
await flushPromises();
|
|
151
|
+
|
|
152
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
153
|
+
|
|
154
|
+
expect(saveButton.disabled).toBe(true);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it.each([...REQUIRED_FIELDS])('when only %s is empty', async(field: RequiredField) => {
|
|
158
|
+
wrapper = mount(Saml, mountOptions({ ...validModel, [field]: '' }));
|
|
159
|
+
await wrapper.vm.validateAllFields();
|
|
160
|
+
await flushPromises();
|
|
161
|
+
|
|
162
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
163
|
+
|
|
164
|
+
expect(saveButton.disabled).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe('is enabled', () => {
|
|
169
|
+
let wrapper: VueWrapper<any, any>;
|
|
170
|
+
|
|
171
|
+
afterEach(() => {
|
|
172
|
+
wrapper.unmount();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('when all required fields are filled', async() => {
|
|
176
|
+
wrapper = mount(Saml, mountOptions(validModel));
|
|
177
|
+
await flushPromises();
|
|
178
|
+
|
|
179
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
180
|
+
|
|
181
|
+
expect(saveButton.disabled).toBe(false);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('when provider is already enabled and not editing config', async() => {
|
|
185
|
+
wrapper = mount(Saml, mountOptions({
|
|
186
|
+
...validModel, ...emptyRequiredFields, enabled: true
|
|
187
|
+
}));
|
|
188
|
+
await nextTick();
|
|
189
|
+
|
|
190
|
+
const saveButton = wrapper.find('[data-testid="form-save"]').element as HTMLInputElement;
|
|
191
|
+
|
|
192
|
+
expect(saveButton.disabled).toBe(false);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
});
|