@rancher/shell 3.0.8 → 3.0.9-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apis/intf/modal.ts +38 -0
- package/apis/intf/slide-in.ts +3 -1
- package/apis/shell/__tests__/slide-in.test.ts +36 -0
- package/apis/shell/slide-in.ts +5 -1
- package/assets/styles/base/_color.scss +1 -0
- package/assets/styles/base/_typography.scss +14 -5
- package/assets/styles/themes/_light.scss +1 -1
- package/assets/styles/themes/_modern.scss +1 -1
- package/assets/translations/en-us.yaml +94 -33
- package/assets/translations/zh-hans.yaml +0 -2
- package/components/ActionMenuShell.vue +4 -4
- package/components/CodeMirror.vue +4 -3
- package/components/DetailText.vue +54 -7
- package/components/Drawer/Chrome.vue +11 -4
- package/components/Drawer/DrawerCard.vue +19 -0
- package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +3 -11
- package/components/Drawer/ResourceDetailDrawer/__tests__/ConfigTab.test.ts +2 -2
- package/components/Drawer/ResourceDetailDrawer/index.vue +3 -20
- package/components/Drawer/types.ts +1 -0
- package/components/DynamicContent/DynamicContentCloseButton.vue +2 -2
- package/components/LocaleSelector.vue +1 -1
- package/components/Markdown.vue +1 -1
- package/components/PopoverCard.vue +3 -3
- package/components/Resource/Detail/Card/ExtrasCard.vue +39 -0
- package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +142 -0
- package/components/Resource/Detail/Card/StateCard/composables.ts +41 -11
- package/components/Resource/Detail/Card/StateCard/index.vue +3 -9
- package/components/Resource/Detail/Card/StateCard/types.ts +6 -0
- package/components/Resource/Detail/Card/{PodsCard → StatusCard}/index.vue +11 -10
- package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +24 -25
- package/components/Resource/Detail/Cards.vue +27 -0
- package/components/Resource/Detail/Masthead/__tests__/index.test.ts +70 -0
- package/components/Resource/Detail/Masthead/index.vue +5 -0
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +4 -2
- package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -2
- package/components/Resource/Detail/ResourceRow.types.ts +14 -0
- package/components/Resource/Detail/ResourceRow.vue +23 -35
- package/components/Resource/Detail/StatusRow.vue +5 -2
- package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +38 -7
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +106 -2
- package/components/Resource/Detail/TitleBar/composables.ts +2 -1
- package/components/Resource/Detail/TitleBar/index.vue +41 -6
- package/components/ResourceDetail/Masthead/__tests__/index.test.ts +49 -1
- package/components/ResourceDetail/Masthead/__tests__/latest.test.ts +85 -0
- package/components/ResourceDetail/Masthead/index.vue +1 -0
- package/components/ResourceDetail/Masthead/latest.vue +8 -1
- package/components/ResourceDetail/Masthead/legacy.vue +1 -1
- package/components/Setting.vue +1 -1
- package/components/SortableTable/index.vue +25 -0
- package/components/SortableTable/selection.js +25 -12
- package/components/SortableTable/sorting.js +1 -1
- package/components/Tabbed/Tab.vue +1 -0
- package/components/Tabbed/index.vue +29 -6
- package/components/Window/ContainerShell.vue +10 -13
- package/components/fleet/FleetClusterTargets/TargetsList.vue +47 -29
- package/components/fleet/FleetClusterTargets/index.vue +82 -29
- package/components/fleet/FleetClusters.vue +26 -12
- package/components/fleet/FleetGitRepoPaths.vue +2 -2
- package/components/fleet/FleetResources.vue +14 -0
- package/components/fleet/FleetValuesFrom.vue +2 -2
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +531 -0
- package/components/fleet/__tests__/FleetClusters.test.ts +576 -0
- package/components/fleet/dashboard/ResourceDetails.vue +96 -123
- package/components/form/Conditions.vue +1 -15
- package/components/form/HookOption.vue +5 -0
- package/components/form/LabeledSelect.vue +1 -1
- package/components/form/LifecycleHooks.vue +2 -6
- package/components/form/ResourceLabeledSelect.vue +12 -1
- package/components/form/SeccompProfile.vue +113 -0
- package/components/form/Security.vue +244 -133
- package/components/form/__tests__/LabeledSelect.test.ts +1 -1
- package/components/form/__tests__/SeccompProfile.test.js +124 -0
- package/components/form/__tests__/Security.test.ts +125 -37
- package/components/formatter/Autoscaler.vue +2 -2
- package/components/formatter/FleetSummaryGraph.vue +4 -1
- package/components/nav/Group.vue +5 -0
- package/components/nav/Header.vue +3 -3
- package/components/nav/HeaderPageActionMenu.vue +1 -1
- package/components/nav/NamespaceFilter.vue +6 -6
- package/components/nav/NotificationCenter/index.vue +1 -1
- package/components/nav/TopLevelMenu.helper.ts +41 -16
- package/components/nav/TopLevelMenu.vue +45 -25
- package/components/nav/WorkspaceSwitcher.vue +1 -1
- package/components/nav/__tests__/TopLevelMenu.helper.test.ts +277 -0
- package/components/nav/__tests__/TopLevelMenu.test.ts +160 -4
- package/components/templates/default.vue +0 -3
- package/components/templates/home.vue +0 -3
- package/components/templates/plain.vue +0 -3
- package/composables/useClickOutside.ts +1 -1
- package/config/product/explorer.js +1 -2
- package/config/types.js +41 -8
- package/detail/__tests__/workload.test.ts +8 -16
- package/detail/catalog.cattle.io.app.vue +6 -0
- package/detail/fleet.cattle.io.cluster.vue +6 -0
- package/detail/workload/index.vue +7 -109
- package/edit/__tests__/projectsecret.test.ts +42 -0
- package/edit/auth/__tests__/oidc.test.ts +50 -0
- package/edit/auth/oidc.vue +68 -44
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +140 -59
- package/edit/autoscaling.horizontalpodautoscaler/metrics-row.vue +41 -5
- package/edit/projectsecret.vue +29 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +89 -200
- package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +58 -17
- package/edit/provisioning.cattle.io.cluster/rke2.vue +11 -0
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +3 -63
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +82 -14
- package/edit/workload/__tests__/index.test.ts +122 -85
- package/edit/workload/index.vue +48 -29
- package/edit/workload/mixins/workload.js +85 -32
- package/list/catalog.cattle.io.clusterrepo.vue +1 -1
- package/list/projectsecret.vue +2 -2
- package/machine-config/__tests__/vmwarevsphere.test.ts +64 -0
- package/machine-config/amazonec2.vue +2 -2
- package/machine-config/vmwarevsphere.vue +58 -4
- package/mixins/__tests__/brand.spec.ts +18 -13
- package/mixins/__tests__/chart.test.ts +63 -0
- package/mixins/chart.js +56 -51
- package/models/__tests__/catalog.cattle.io.app.test.ts +33 -0
- package/models/__tests__/workload.test.ts +333 -0
- package/models/catalog.cattle.io.app.js +8 -0
- package/models/pod.js +14 -0
- package/models/secret.js +1 -1
- package/models/workload.js +93 -27
- package/package.json +4 -4
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +91 -0
- package/pages/c/_cluster/apps/charts/install.vue +4 -4
- package/pages/c/_cluster/explorer/EventsTable.vue +2 -2
- package/pages/c/_cluster/fleet/index.vue +18 -12
- package/pages/c/_cluster/manager/hostedprovider/index.vue +1 -19
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
- package/pages/c/_cluster/uiplugins/index.vue +1 -1
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +234 -0
- package/plugins/dashboard-store/actions.js +9 -8
- package/plugins/dashboard-store/resource-class.js +97 -1
- package/plugins/steve/__tests__/revision.test.ts +84 -0
- package/plugins/steve/__tests__/steve-pagination-utils.test.ts +30 -0
- package/plugins/steve/__tests__/subscribe.spec.ts +134 -0
- package/plugins/steve/mutations.js +9 -0
- package/plugins/steve/revision.ts +26 -0
- package/plugins/steve/steve-pagination-utils.ts +6 -5
- package/plugins/steve/subscribe.js +211 -51
- package/plugins/subscribe-events.ts +2 -2
- package/rancher-components/Form/Checkbox/Checkbox.vue +13 -0
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -1
- package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +1 -1
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +3 -1
- package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +3 -1
- package/rancher-components/Pill/RcTag/RcTag.vue +1 -1
- package/rancher-components/Pill/index.ts +4 -0
- package/rancher-components/RcButton/RcButton.test.ts +53 -9
- package/rancher-components/RcButton/RcButton.vue +217 -25
- package/rancher-components/RcButton/types.ts +27 -1
- package/rancher-components/RcDropdown/RcDropdownMenu.vue +4 -4
- package/rancher-components/RcDropdown/types.ts +3 -3
- package/rancher-components/RcIcon/RcIcon.test.ts +42 -0
- package/rancher-components/RcIcon/RcIcon.vue +9 -6
- package/rancher-components/RcIcon/types.ts +13 -9
- package/rancher-components/utils/status.test.ts +10 -15
- package/rancher-components/utils/status.ts +5 -6
- package/store/aws.js +18 -12
- package/store/index.js +4 -8
- package/store/type-map.utils.ts +1 -1
- package/types/kube/kube-api.ts +29 -3
- package/types/rancher/steve.api.ts +40 -0
- package/types/shell/index.d.ts +99 -0
- package/types/store/dashboard-store.types.ts +29 -7
- package/types/store/pagination.types.ts +1 -0
- package/types/store/subscribe-events.types.ts +1 -0
- package/utils/__tests__/azure.test.ts +56 -0
- package/utils/__tests__/back-off.test.ts +364 -245
- package/utils/__tests__/error.test.ts +44 -0
- package/utils/__tests__/fleet.test.ts +8 -1
- package/utils/__tests__/pagination-wrapper.test.ts +167 -0
- package/utils/__tests__/version.test.ts +55 -1
- package/utils/azure.js +12 -0
- package/utils/back-off.ts +302 -69
- package/utils/cspAdaptor.ts +32 -14
- package/utils/dynamic-content/__tests__/index.test.ts +1 -1
- package/utils/dynamic-content/__tests__/new-release.test.ts +48 -7
- package/utils/dynamic-content/__tests__/support-notice.test.ts +1 -4
- package/utils/dynamic-content/index.ts +1 -6
- package/utils/dynamic-content/new-release.ts +5 -3
- package/utils/dynamic-content/types.d.ts +0 -1
- package/utils/error.js +9 -0
- package/utils/fleet.ts +2 -2
- package/utils/inactivity.ts +2 -3
- package/utils/pagination-wrapper.ts +101 -17
- package/utils/validators/formRules/index.ts +3 -0
- package/utils/version.js +38 -0
- package/components/auth/AzureWarning.vue +0 -77
- /package/components/Resource/Detail/{Card/PodsCard/Bubble.vue → Bubble.vue} +0 -0
- /package/components/Resource/Detail/Card/{PodsCard → StatusCard}/composable.ts +0 -0
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import FleetClusters from '@shell/components/fleet/FleetClusters.vue';
|
|
3
|
+
import ResourceTable from '@shell/components/ResourceTable.vue';
|
|
4
|
+
|
|
5
|
+
describe('component: FleetClusters', () => {
|
|
6
|
+
const mockStore = {
|
|
7
|
+
getters: {
|
|
8
|
+
'i18n/t': (key: string) => key,
|
|
9
|
+
'management/schemaFor': () => ({ id: 'fleet.cattle.io.cluster' }),
|
|
10
|
+
'type-map/labelFor': (schema: any, count?: number) => count === 99 ? 'Clusters' : 'Cluster',
|
|
11
|
+
'prefs/get': () => false,
|
|
12
|
+
currentProduct: () => ({ inStore: 'cluster' }),
|
|
13
|
+
'type-map/optionsFor': () => ({}),
|
|
14
|
+
'type-map/headersFor': () => [],
|
|
15
|
+
defaultClusterId: () => 'local',
|
|
16
|
+
},
|
|
17
|
+
dispatch: jest.fn(),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const mockRow = {
|
|
21
|
+
id: 'test-cluster',
|
|
22
|
+
customLabels: [],
|
|
23
|
+
displayCustomLabels: false,
|
|
24
|
+
stateDescription: 'Active',
|
|
25
|
+
nameDisplay: 'test-cluster',
|
|
26
|
+
reposReady: '1/1',
|
|
27
|
+
bundleInfo: {
|
|
28
|
+
ready: 1,
|
|
29
|
+
total: 1
|
|
30
|
+
},
|
|
31
|
+
helmOpsReady: '0/0',
|
|
32
|
+
lastSeen: new Date().toISOString(),
|
|
33
|
+
stateObj: {
|
|
34
|
+
name: 'active',
|
|
35
|
+
transitioning: false,
|
|
36
|
+
error: false
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const createWrapper = (props: any = {}, slots = {}) => {
|
|
41
|
+
const defaultRows = props.rows || [mockRow];
|
|
42
|
+
|
|
43
|
+
return mount(FleetClusters, {
|
|
44
|
+
props: {
|
|
45
|
+
rows: defaultRows,
|
|
46
|
+
schema: { id: 'fleet.cattle.io.cluster' },
|
|
47
|
+
...props,
|
|
48
|
+
},
|
|
49
|
+
slots,
|
|
50
|
+
global: {
|
|
51
|
+
mocks: { $store: mockStore },
|
|
52
|
+
components: { ResourceTable },
|
|
53
|
+
stubs: {
|
|
54
|
+
ResourceTable: {
|
|
55
|
+
template: `
|
|
56
|
+
<div class="resource-table">
|
|
57
|
+
<slot
|
|
58
|
+
name="additional-sub-row"
|
|
59
|
+
:fullColspan="10"
|
|
60
|
+
:row="rows[0]"
|
|
61
|
+
:onRowMouseEnter="() => {}"
|
|
62
|
+
:onRowMouseLeave="() => {}"
|
|
63
|
+
:showSubRow="rows[0].stateDescription"
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
`,
|
|
67
|
+
props: ['rows', 'schema', 'headers', 'subRows', 'loading', 'useQueryParamsForSimpleFiltering', 'keyField']
|
|
68
|
+
},
|
|
69
|
+
Tag: { template: '<span class="tag"><slot /></span>' }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
describe('headers configuration', () => {
|
|
76
|
+
it('should include all required column headers', () => {
|
|
77
|
+
const wrapper = createWrapper();
|
|
78
|
+
const headers = wrapper.vm.headers;
|
|
79
|
+
|
|
80
|
+
expect(headers).toHaveLength(8);
|
|
81
|
+
expect(headers.map((h: any) => h.name || h)).toContain('state');
|
|
82
|
+
expect(headers.map((h: any) => h.name || h)).toContain('name');
|
|
83
|
+
expect(headers.some((h: any) => h.name === 'reposReady')).toBe(true);
|
|
84
|
+
expect(headers.some((h: any) => h.name === 'helmOpsReady')).toBe(true);
|
|
85
|
+
expect(headers.some((h: any) => h.name === 'bundlesReady')).toBe(true);
|
|
86
|
+
expect(headers.some((h: any) => h.name === 'lastSeen')).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should configure reposReady column correctly', () => {
|
|
90
|
+
const wrapper = createWrapper();
|
|
91
|
+
const reposReady = wrapper.vm.headers.find((h: any) => h.name === 'reposReady');
|
|
92
|
+
|
|
93
|
+
expect(reposReady.labelKey).toBe('tableHeaders.reposReady');
|
|
94
|
+
expect(reposReady.value).toBe('status.readyGitRepos');
|
|
95
|
+
expect(reposReady.search).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should configure helmOpsReady column correctly', () => {
|
|
99
|
+
const wrapper = createWrapper();
|
|
100
|
+
const helmOpsReady = wrapper.vm.headers.find((h: any) => h.name === 'helmOpsReady');
|
|
101
|
+
|
|
102
|
+
expect(helmOpsReady.labelKey).toBe('tableHeaders.helmOpsReady');
|
|
103
|
+
expect(helmOpsReady.value).toBe('status.readyHelmOps');
|
|
104
|
+
expect(helmOpsReady.search).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should configure bundlesReady column correctly', () => {
|
|
108
|
+
const wrapper = createWrapper();
|
|
109
|
+
const bundlesReady = wrapper.vm.headers.find((h: any) => h.name === 'bundlesReady');
|
|
110
|
+
|
|
111
|
+
expect(bundlesReady.labelKey).toBe('tableHeaders.bundlesReady');
|
|
112
|
+
expect(bundlesReady.value).toBe('status.display.readyBundles');
|
|
113
|
+
expect(bundlesReady.search).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should configure lastSeen column with LiveDate formatter', () => {
|
|
117
|
+
const wrapper = createWrapper();
|
|
118
|
+
const lastSeen = wrapper.vm.headers.find((h: any) => h.name === 'lastSeen');
|
|
119
|
+
|
|
120
|
+
expect(lastSeen.formatter).toBe('LiveDate');
|
|
121
|
+
expect(lastSeen.formatterOpts).toStrictEqual({ addSuffix: true });
|
|
122
|
+
expect(lastSeen.width).toBe(120);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('additional-sub-row slot', () => {
|
|
127
|
+
it('should render labels row when customLabels exist', () => {
|
|
128
|
+
const rows = [{
|
|
129
|
+
customLabels: ['label1', 'label2', 'label3'],
|
|
130
|
+
displayCustomLabels: false
|
|
131
|
+
}];
|
|
132
|
+
|
|
133
|
+
const wrapper = createWrapper({ rows });
|
|
134
|
+
|
|
135
|
+
expect(wrapper.find('.labels-row').exists()).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should display up to 7 labels by default', () => {
|
|
139
|
+
const rows = [{
|
|
140
|
+
customLabels: ['label1', 'label2', 'label3', 'label4', 'label5', 'label6', 'label7', 'label8'],
|
|
141
|
+
displayCustomLabels: false
|
|
142
|
+
}];
|
|
143
|
+
|
|
144
|
+
const wrapper = createWrapper({ rows });
|
|
145
|
+
const tags = wrapper.findAll('.tag');
|
|
146
|
+
|
|
147
|
+
expect(tags).toHaveLength(7);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should display all labels when displayCustomLabels is true', () => {
|
|
151
|
+
const rows = [{
|
|
152
|
+
customLabels: ['label1', 'label2', 'label3', 'label4', 'label5', 'label6', 'label7', 'label8'],
|
|
153
|
+
displayCustomLabels: true
|
|
154
|
+
}];
|
|
155
|
+
|
|
156
|
+
const wrapper = createWrapper({ rows });
|
|
157
|
+
const tags = wrapper.findAll('.tag');
|
|
158
|
+
|
|
159
|
+
expect(tags).toHaveLength(8);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should show toggle link when more than 7 labels', () => {
|
|
163
|
+
const rows = [{
|
|
164
|
+
customLabels: ['label1', 'label2', 'label3', 'label4', 'label5', 'label6', 'label7', 'label8'],
|
|
165
|
+
displayCustomLabels: false
|
|
166
|
+
}];
|
|
167
|
+
|
|
168
|
+
const wrapper = createWrapper({ rows });
|
|
169
|
+
const toggleLink = wrapper.find('a[href="#"]');
|
|
170
|
+
|
|
171
|
+
expect(toggleLink.exists()).toBe(true);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should not show toggle link when 7 or fewer labels', () => {
|
|
175
|
+
const rows = [{
|
|
176
|
+
customLabels: ['label1', 'label2', 'label3'],
|
|
177
|
+
displayCustomLabels: false
|
|
178
|
+
}];
|
|
179
|
+
|
|
180
|
+
const wrapper = createWrapper({ rows });
|
|
181
|
+
const toggleLink = wrapper.find('a[href="#"]');
|
|
182
|
+
|
|
183
|
+
expect(toggleLink.exists()).toBe(false);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should toggle displayCustomLabels when clicking show/hide link', async() => {
|
|
187
|
+
const rows = [{
|
|
188
|
+
customLabels: ['label1', 'label2', 'label3', 'label4', 'label5', 'label6', 'label7', 'label8'],
|
|
189
|
+
displayCustomLabels: false
|
|
190
|
+
}];
|
|
191
|
+
|
|
192
|
+
const wrapper = createWrapper({ rows });
|
|
193
|
+
const toggleLink = wrapper.find('a[href="#"]');
|
|
194
|
+
|
|
195
|
+
await toggleLink.trigger('click');
|
|
196
|
+
|
|
197
|
+
expect(rows[0].displayCustomLabels).toBe(true);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should render empty cell when no custom labels', () => {
|
|
201
|
+
const rows = [{
|
|
202
|
+
customLabels: [],
|
|
203
|
+
displayCustomLabels: false
|
|
204
|
+
}];
|
|
205
|
+
|
|
206
|
+
const wrapper = createWrapper({ rows });
|
|
207
|
+
const labelsRow = wrapper.find('.labels-row');
|
|
208
|
+
|
|
209
|
+
expect(labelsRow.exists()).toBe(true);
|
|
210
|
+
expect(labelsRow.findAll('.tag')).toHaveLength(0);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should apply has-sub-row class when showSubRow is true', () => {
|
|
214
|
+
const rows = [{
|
|
215
|
+
customLabels: ['label1'],
|
|
216
|
+
displayCustomLabels: false,
|
|
217
|
+
stateDescription: 'Active',
|
|
218
|
+
}];
|
|
219
|
+
|
|
220
|
+
const wrapper = createWrapper({ rows });
|
|
221
|
+
const labelsRow = wrapper.find('.labels-row');
|
|
222
|
+
|
|
223
|
+
expect(labelsRow.classes()).toContain('has-sub-row');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('toggleCustomLabels method', () => {
|
|
228
|
+
it('should toggle displayCustomLabels property', () => {
|
|
229
|
+
const wrapper = createWrapper();
|
|
230
|
+
const row = { displayCustomLabels: false };
|
|
231
|
+
|
|
232
|
+
wrapper.vm.toggleCustomLabels(row);
|
|
233
|
+
expect(row.displayCustomLabels).toBe(true);
|
|
234
|
+
|
|
235
|
+
wrapper.vm.toggleCustomLabels(row);
|
|
236
|
+
expect(row.displayCustomLabels).toBe(false);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('props validation', () => {
|
|
241
|
+
it('should accept required rows prop', () => {
|
|
242
|
+
const rows = [{ id: '1', name: 'cluster1' }];
|
|
243
|
+
const wrapper = createWrapper({ rows });
|
|
244
|
+
|
|
245
|
+
expect(wrapper.props('rows')).toStrictEqual(rows);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('should accept schema prop', () => {
|
|
249
|
+
const schema = { id: 'fleet.cattle.io.cluster' };
|
|
250
|
+
const wrapper = createWrapper({ schema });
|
|
251
|
+
|
|
252
|
+
expect(wrapper.props('schema')).toStrictEqual(schema);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should accept loading prop', () => {
|
|
256
|
+
const wrapper = createWrapper({ loading: true });
|
|
257
|
+
|
|
258
|
+
expect(wrapper.props('loading')).toBe(true);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('should accept useQueryParamsForSimpleFiltering prop', () => {
|
|
262
|
+
const wrapper = createWrapper({ useQueryParamsForSimpleFiltering: true });
|
|
263
|
+
|
|
264
|
+
expect(wrapper.props('useQueryParamsForSimpleFiltering')).toBe(true);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
describe('computed MANAGEMENT_CLUSTER property', () => {
|
|
269
|
+
it('should return MANAGEMENT.CLUSTER', () => {
|
|
270
|
+
const wrapper = createWrapper();
|
|
271
|
+
|
|
272
|
+
expect(wrapper.vm.MANAGEMENT_CLUSTER).toBe('management.cattle.io.cluster');
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe('pagingParams computed property', () => {
|
|
277
|
+
it('should return singular and plural labels', () => {
|
|
278
|
+
const wrapper = createWrapper();
|
|
279
|
+
const params = wrapper.vm.pagingParams;
|
|
280
|
+
|
|
281
|
+
expect(params.singularLabel).toBe('Cluster');
|
|
282
|
+
expect(params.pluralLabel).toBe('Clusters');
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('resourceTable integration', () => {
|
|
287
|
+
it('should pass correct props to ResourceTable', () => {
|
|
288
|
+
const rows = [{ id: '1', customLabels: [] }];
|
|
289
|
+
const schema = { id: 'fleet.cattle.io.cluster' };
|
|
290
|
+
const wrapper = createWrapper({
|
|
291
|
+
rows,
|
|
292
|
+
schema,
|
|
293
|
+
loading: true
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const resourceTable = wrapper.find('.resource-table');
|
|
297
|
+
|
|
298
|
+
expect(resourceTable.exists()).toBe(true);
|
|
299
|
+
expect(wrapper.vm.headers).toBeDefined();
|
|
300
|
+
expect(wrapper.vm.headers).toHaveLength(8);
|
|
301
|
+
expect(wrapper.vm.MANAGEMENT_CLUSTER).toBe('management.cattle.io.cluster');
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
describe('label display behavior', () => {
|
|
306
|
+
it('should show "fleet.cluster.labels" text when labels exist', () => {
|
|
307
|
+
const rows = [{
|
|
308
|
+
customLabels: ['label1'],
|
|
309
|
+
displayCustomLabels: false
|
|
310
|
+
}];
|
|
311
|
+
|
|
312
|
+
const wrapper = createWrapper({ rows });
|
|
313
|
+
|
|
314
|
+
expect(wrapper.text()).toContain('fleet.cluster.labels');
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should render each label in a Tag component', () => {
|
|
318
|
+
const rows = [{
|
|
319
|
+
customLabels: ['env:prod', 'team:backend', 'region:us-west'],
|
|
320
|
+
displayCustomLabels: false
|
|
321
|
+
}];
|
|
322
|
+
|
|
323
|
+
const wrapper = createWrapper({ rows });
|
|
324
|
+
const tags = wrapper.findAll('.tag');
|
|
325
|
+
|
|
326
|
+
expect(tags[0].text()).toBe('env:prod');
|
|
327
|
+
expect(tags[1].text()).toBe('team:backend');
|
|
328
|
+
expect(tags[2].text()).toBe('region:us-west');
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should handle labels with special characters', () => {
|
|
332
|
+
const rows = [{
|
|
333
|
+
customLabels: ['app.kubernetes.io/name=nginx', 'version=1.0.0-beta'],
|
|
334
|
+
displayCustomLabels: false
|
|
335
|
+
}];
|
|
336
|
+
|
|
337
|
+
const wrapper = createWrapper({ rows });
|
|
338
|
+
const tags = wrapper.findAll('.tag');
|
|
339
|
+
|
|
340
|
+
expect(tags[0].text()).toBe('app.kubernetes.io/name=nginx');
|
|
341
|
+
expect(tags[1].text()).toBe('version=1.0.0-beta');
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
describe('mouse events', () => {
|
|
346
|
+
it('should call onRowMouseEnter when mouse enters labels row', async() => {
|
|
347
|
+
const rows = [{
|
|
348
|
+
customLabels: ['label1'],
|
|
349
|
+
displayCustomLabels: false
|
|
350
|
+
}];
|
|
351
|
+
|
|
352
|
+
const wrapper = createWrapper({ rows });
|
|
353
|
+
const labelsRow = wrapper.find('.labels-row');
|
|
354
|
+
|
|
355
|
+
await labelsRow.trigger('mouseenter');
|
|
356
|
+
|
|
357
|
+
// Event binding is tested through the template
|
|
358
|
+
expect(labelsRow.exists()).toBe(true);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('should call onRowMouseLeave when mouse leaves labels row', async() => {
|
|
362
|
+
const rows = [{
|
|
363
|
+
customLabels: ['label1'],
|
|
364
|
+
displayCustomLabels: false
|
|
365
|
+
}];
|
|
366
|
+
|
|
367
|
+
const wrapper = createWrapper({ rows });
|
|
368
|
+
const labelsRow = wrapper.find('.labels-row');
|
|
369
|
+
|
|
370
|
+
await labelsRow.trigger('mouseleave');
|
|
371
|
+
|
|
372
|
+
// Event binding is tested through the template
|
|
373
|
+
expect(labelsRow.exists()).toBe(true);
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
describe('edge cases', () => {
|
|
378
|
+
it('should handle undefined customLabels gracefully', () => {
|
|
379
|
+
const rows = [{
|
|
380
|
+
customLabels: undefined,
|
|
381
|
+
displayCustomLabels: false
|
|
382
|
+
}];
|
|
383
|
+
|
|
384
|
+
// Component should handle this without crashing
|
|
385
|
+
expect(() => createWrapper({ rows })).not.toThrow();
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('should handle exactly 7 labels without toggle link', () => {
|
|
389
|
+
const rows = [{
|
|
390
|
+
customLabels: ['l1', 'l2', 'l3', 'l4', 'l5', 'l6', 'l7'],
|
|
391
|
+
displayCustomLabels: false
|
|
392
|
+
}];
|
|
393
|
+
|
|
394
|
+
const wrapper = createWrapper({ rows });
|
|
395
|
+
const tags = wrapper.findAll('.tag');
|
|
396
|
+
const toggleLink = wrapper.find('a[href="#"]');
|
|
397
|
+
|
|
398
|
+
expect(tags).toHaveLength(7);
|
|
399
|
+
expect(toggleLink.exists()).toBe(false);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('should handle exactly 8 labels with toggle link', () => {
|
|
403
|
+
const rows = [{
|
|
404
|
+
customLabels: ['l1', 'l2', 'l3', 'l4', 'l5', 'l6', 'l7', 'l8'],
|
|
405
|
+
displayCustomLabels: false
|
|
406
|
+
}];
|
|
407
|
+
|
|
408
|
+
const wrapper = createWrapper({ rows });
|
|
409
|
+
const tags = wrapper.findAll('.tag');
|
|
410
|
+
const toggleLink = wrapper.find('a[href="#"]');
|
|
411
|
+
|
|
412
|
+
expect(tags).toHaveLength(7); // Only first 7 shown initially
|
|
413
|
+
expect(toggleLink.exists()).toBe(true);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it('should handle large number of labels', () => {
|
|
417
|
+
const rows = [{
|
|
418
|
+
customLabels: Array.from({ length: 50 }, (_, i) => `label${ i + 1 }`),
|
|
419
|
+
displayCustomLabels: false
|
|
420
|
+
}];
|
|
421
|
+
|
|
422
|
+
const wrapper = createWrapper({ rows });
|
|
423
|
+
const tags = wrapper.findAll('.tag');
|
|
424
|
+
|
|
425
|
+
expect(tags).toHaveLength(7);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('should show all labels when displayCustomLabels is true for large set', () => {
|
|
429
|
+
const rows = [{
|
|
430
|
+
customLabels: Array.from({ length: 50 }, (_, i) => `label${ i + 1 }`),
|
|
431
|
+
displayCustomLabels: true
|
|
432
|
+
}];
|
|
433
|
+
|
|
434
|
+
const wrapper = createWrapper({ rows });
|
|
435
|
+
const tags = wrapper.findAll('.tag');
|
|
436
|
+
|
|
437
|
+
expect(tags).toHaveLength(50);
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
describe('styling classes', () => {
|
|
442
|
+
it('should apply labels-row class', () => {
|
|
443
|
+
const rows = [{
|
|
444
|
+
customLabels: ['label1'],
|
|
445
|
+
displayCustomLabels: false
|
|
446
|
+
}];
|
|
447
|
+
|
|
448
|
+
const wrapper = createWrapper({ rows });
|
|
449
|
+
|
|
450
|
+
expect(wrapper.find('.labels-row').exists()).toBe(true);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('should apply additional-sub-row class', () => {
|
|
454
|
+
const rows = [{
|
|
455
|
+
customLabels: ['label1'],
|
|
456
|
+
displayCustomLabels: false
|
|
457
|
+
}];
|
|
458
|
+
|
|
459
|
+
const wrapper = createWrapper({ rows });
|
|
460
|
+
|
|
461
|
+
expect(wrapper.find('.additional-sub-row').exists()).toBe(true);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it('should apply mt-5 class to labels container', () => {
|
|
465
|
+
const rows = [{
|
|
466
|
+
customLabels: ['label1'],
|
|
467
|
+
displayCustomLabels: false
|
|
468
|
+
}];
|
|
469
|
+
|
|
470
|
+
const wrapper = createWrapper({ rows });
|
|
471
|
+
|
|
472
|
+
expect(wrapper.find('.mt-5').exists()).toBe(true);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('should apply mr-5 class to label tags', () => {
|
|
476
|
+
const rows = [{
|
|
477
|
+
customLabels: ['label1'],
|
|
478
|
+
displayCustomLabels: false
|
|
479
|
+
}];
|
|
480
|
+
|
|
481
|
+
const wrapper = createWrapper({ rows });
|
|
482
|
+
|
|
483
|
+
expect(wrapper.find('.mr-5').exists()).toBe(true);
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// Tests for gap removal fix - Issue #16502
|
|
488
|
+
describe('additional-sub-row properties passed properly', () => {
|
|
489
|
+
it('should render additional-sub-row when customLabels is empty', () => {
|
|
490
|
+
const rows = [{
|
|
491
|
+
customLabels: [],
|
|
492
|
+
displayCustomLabels: false
|
|
493
|
+
}];
|
|
494
|
+
|
|
495
|
+
const wrapper = createWrapper({ rows });
|
|
496
|
+
const additionalSubRow = wrapper.find('.labels-row.additional-sub-row');
|
|
497
|
+
|
|
498
|
+
// Should exist when no custom labels, it should be there to have the border
|
|
499
|
+
expect(additionalSubRow.exists()).toBe(true);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('should render additional-sub-row when customLabels is undefined', () => {
|
|
503
|
+
const rows = [{
|
|
504
|
+
customLabels: undefined,
|
|
505
|
+
displayCustomLabels: false
|
|
506
|
+
}];
|
|
507
|
+
|
|
508
|
+
const wrapper = createWrapper({ rows });
|
|
509
|
+
const additionalSubRow = wrapper.find('.labels-row.additional-sub-row');
|
|
510
|
+
|
|
511
|
+
// Should exist when no custom labels, it should be there to have the border
|
|
512
|
+
expect(additionalSubRow.exists()).toBe(true);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
it('should render additional-sub-row when customLabels is null', () => {
|
|
516
|
+
const rows = [{
|
|
517
|
+
customLabels: null,
|
|
518
|
+
displayCustomLabels: false
|
|
519
|
+
}];
|
|
520
|
+
|
|
521
|
+
const wrapper = createWrapper({ rows });
|
|
522
|
+
const additionalSubRow = wrapper.find('.labels-row.additional-sub-row');
|
|
523
|
+
|
|
524
|
+
// Should not exist when customLabels is null
|
|
525
|
+
expect(additionalSubRow.exists()).toBe(true);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it('should handle mixed scenarios - some clusters with labels, some without', () => {
|
|
529
|
+
const mixedRows = [
|
|
530
|
+
{
|
|
531
|
+
id: 'cluster-1',
|
|
532
|
+
customLabels: ['env:prod'],
|
|
533
|
+
displayCustomLabels: false
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
id: 'cluster-2',
|
|
537
|
+
customLabels: [],
|
|
538
|
+
displayCustomLabels: false
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
id: 'cluster-3',
|
|
542
|
+
customLabels: ['team:backend', 'region:us-west'],
|
|
543
|
+
displayCustomLabels: false
|
|
544
|
+
}
|
|
545
|
+
];
|
|
546
|
+
|
|
547
|
+
// We need to create separate wrappers since our stub only shows one row
|
|
548
|
+
const wrapper1 = createWrapper({ rows: [mixedRows[0]] });
|
|
549
|
+
const wrapper2 = createWrapper({ rows: [mixedRows[1]] });
|
|
550
|
+
const wrapper3 = createWrapper({ rows: [mixedRows[2]] });
|
|
551
|
+
|
|
552
|
+
// Cluster 1: has labels -> should render additional-sub-row
|
|
553
|
+
expect(wrapper1.find('tr.additional-sub-row').exists()).toBe(true);
|
|
554
|
+
|
|
555
|
+
// Cluster 2: no labels -> should render additional-sub-row
|
|
556
|
+
expect(wrapper2.find('tr.additional-sub-row').exists()).toBe(true);
|
|
557
|
+
|
|
558
|
+
// Cluster 3: has labels -> should render additional-sub-row
|
|
559
|
+
expect(wrapper3.find('tr.additional-sub-row').exists()).toBe(true);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it('should render additional-sub-row without the has-sub-row when there is no stateDescription', () => {
|
|
563
|
+
const rows = [{
|
|
564
|
+
customLabels: null,
|
|
565
|
+
displayCustomLabels: false,
|
|
566
|
+
stateDescription: null,
|
|
567
|
+
}];
|
|
568
|
+
|
|
569
|
+
const wrapper = createWrapper({ rows });
|
|
570
|
+
const additionalSubRow = wrapper.find('.labels-row.additional-sub-row.has-sub-row');
|
|
571
|
+
|
|
572
|
+
// Should not exist when customLabels is null
|
|
573
|
+
expect(additionalSubRow.exists()).toBe(false);
|
|
574
|
+
});
|
|
575
|
+
});
|
|
576
|
+
});
|