@scality/data-browser-library 1.0.0-preview.7 → 1.0.0-preview.9
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/dist/components/__tests__/BucketCreate.test.d.ts +1 -0
- package/dist/components/__tests__/BucketCreate.test.js +408 -0
- package/dist/components/__tests__/BucketLifecycleFormPage.test.d.ts +1 -0
- package/dist/components/__tests__/BucketLifecycleFormPage.test.js +618 -0
- package/dist/components/__tests__/BucketLifecycleList.test.d.ts +1 -0
- package/dist/components/__tests__/BucketLifecycleList.test.js +325 -0
- package/dist/components/__tests__/BucketList.test.js +190 -0
- package/dist/components/__tests__/BucketOverview.test.js +298 -8
- package/dist/components/__tests__/BucketReplicationFormPage.test.d.ts +1 -0
- package/dist/components/__tests__/BucketReplicationFormPage.test.js +1757 -0
- package/dist/components/__tests__/BucketReplicationList.test.d.ts +1 -0
- package/dist/components/__tests__/BucketReplicationList.test.js +344 -0
- package/dist/components/__tests__/DeleteBucketConfigRuleButton.test.d.ts +1 -0
- package/dist/components/__tests__/DeleteBucketConfigRuleButton.test.js +196 -0
- package/dist/components/__tests__/EmptyBucketButton.test.d.ts +1 -0
- package/dist/components/__tests__/EmptyBucketButton.test.js +302 -0
- package/dist/components/buckets/BucketCreate.d.ts +49 -0
- package/dist/components/buckets/BucketCreate.js +237 -0
- package/dist/components/buckets/BucketDetails.js +62 -10
- package/dist/components/buckets/BucketLifecycleFormPage.d.ts +15 -0
- package/dist/components/buckets/BucketLifecycleFormPage.js +1070 -0
- package/dist/components/buckets/BucketLifecycleList.d.ts +10 -0
- package/dist/components/buckets/BucketLifecycleList.js +270 -0
- package/dist/components/buckets/BucketList.d.ts +5 -2
- package/dist/components/buckets/BucketList.js +38 -28
- package/dist/components/buckets/BucketOverview.d.ts +65 -4
- package/dist/components/buckets/BucketOverview.js +261 -179
- package/dist/components/buckets/BucketPage.js +1 -1
- package/dist/components/buckets/BucketReplicationFormPage.d.ts +1 -0
- package/dist/components/buckets/BucketReplicationFormPage.js +834 -0
- package/dist/components/buckets/BucketReplicationList.d.ts +11 -0
- package/dist/components/buckets/BucketReplicationList.js +189 -0
- package/dist/components/buckets/DeleteBucketConfigRuleButton.d.ts +18 -0
- package/dist/components/buckets/DeleteBucketConfigRuleButton.js +53 -0
- package/dist/components/buckets/EmptyBucketButton.d.ts +5 -0
- package/dist/components/buckets/EmptyBucketButton.js +232 -0
- package/dist/components/buckets/EmptyBucketSummary.d.ts +9 -0
- package/dist/components/buckets/EmptyBucketSummary.js +60 -0
- package/dist/components/buckets/EmptyBucketSummaryList.d.ts +13 -0
- package/dist/components/buckets/EmptyBucketSummaryList.js +140 -0
- package/dist/components/buckets/notifications/BucketNotificationCreatePage.js +8 -8
- package/dist/components/index.d.ts +8 -1
- package/dist/components/index.js +9 -2
- package/dist/components/objects/ObjectLock/EditRetentionButton.d.ts +4 -0
- package/dist/components/objects/ObjectLock/EditRetentionButton.js +32 -0
- package/dist/components/objects/ObjectLock/ObjectLockRetentionSettings.d.ts +3 -0
- package/dist/components/objects/ObjectLock/ObjectLockRetentionSettings.js +211 -0
- package/dist/components/objects/ObjectLock/ObjectLockSettings.d.ts +9 -0
- package/dist/components/objects/ObjectLock/ObjectLockSettings.js +158 -0
- package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.d.ts +8 -0
- package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.js +39 -0
- package/dist/components/objects/ObjectLock/__tests__/EditRetentionButton.test.d.ts +1 -0
- package/dist/components/objects/ObjectLock/__tests__/EditRetentionButton.test.js +204 -0
- package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.d.ts +1 -0
- package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.js +374 -0
- package/dist/components/ui/ArrayFieldActions.d.ts +36 -0
- package/dist/components/ui/ArrayFieldActions.js +38 -0
- package/dist/components/ui/ConfirmDeleteRuleModal.d.ts +16 -0
- package/dist/components/ui/ConfirmDeleteRuleModal.js +43 -0
- package/dist/components/ui/FilterFormSection.d.ts +44 -0
- package/dist/components/ui/FilterFormSection.js +159 -0
- package/dist/config/factory.d.ts +13 -2
- package/dist/config/factory.js +9 -6
- package/dist/hooks/__tests__/useISVBucketDetection.test.d.ts +1 -0
- package/dist/hooks/__tests__/useISVBucketDetection.test.js +188 -0
- package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.js +44 -1
- package/dist/hooks/factories/useCreateS3QueryHook.js +22 -1
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.js +5 -1
- package/dist/hooks/useDeleteBucketConfigRule.d.ts +26 -0
- package/dist/hooks/useDeleteBucketConfigRule.js +46 -0
- package/dist/hooks/useEmptyBucket.d.ts +27 -0
- package/dist/hooks/useEmptyBucket.js +116 -0
- package/dist/hooks/useISVBucketDetection.d.ts +15 -0
- package/dist/hooks/useISVBucketDetection.js +27 -0
- package/dist/hooks/useTableRowSelection.d.ts +9 -0
- package/dist/hooks/useTableRowSelection.js +45 -0
- package/dist/test/setup.js +8 -0
- package/dist/test/testUtils.d.ts +99 -17
- package/dist/test/testUtils.js +64 -16
- package/dist/test/utils/errorHandling.test.js +39 -1
- package/dist/utils/constants.d.ts +12 -0
- package/dist/utils/constants.js +9 -0
- package/dist/utils/errorHandling.d.ts +9 -0
- package/dist/utils/errorHandling.js +6 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/s3RuleUtils.d.ts +53 -0
- package/dist/utils/s3RuleUtils.js +101 -0
- package/package.json +1 -1
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { fireEvent, render, screen, within } from "@testing-library/react";
|
|
3
|
+
import { MemoryRouter } from "react-router";
|
|
4
|
+
import { createTestWrapper, mockOffsetSize } from "../../test/testUtils.js";
|
|
5
|
+
import { BucketLifecycleList } from "../buckets/BucketLifecycleList.js";
|
|
6
|
+
const renderBucketLifecycleList = (props = {})=>{
|
|
7
|
+
const Wrapper = createTestWrapper();
|
|
8
|
+
return render(/*#__PURE__*/ jsx(MemoryRouter, {
|
|
9
|
+
children: /*#__PURE__*/ jsx(Wrapper, {
|
|
10
|
+
children: /*#__PURE__*/ jsx(BucketLifecycleList, {
|
|
11
|
+
bucketName: "test-bucket",
|
|
12
|
+
lifecycleRules: [],
|
|
13
|
+
...props
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
}));
|
|
17
|
+
};
|
|
18
|
+
const mockLifecycleRules = [
|
|
19
|
+
{
|
|
20
|
+
ID: "rule-1",
|
|
21
|
+
Status: "Enabled",
|
|
22
|
+
Expiration: {
|
|
23
|
+
Days: 30
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
ID: "rule-2",
|
|
28
|
+
Status: "Disabled",
|
|
29
|
+
NoncurrentVersionExpiration: {
|
|
30
|
+
NoncurrentDays: 90
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
ID: "rule-3",
|
|
35
|
+
Status: "Enabled",
|
|
36
|
+
AbortIncompleteMultipartUpload: {
|
|
37
|
+
DaysAfterInitiation: 7
|
|
38
|
+
},
|
|
39
|
+
Expiration: {
|
|
40
|
+
Days: 365
|
|
41
|
+
},
|
|
42
|
+
NoncurrentVersionExpiration: {
|
|
43
|
+
NoncurrentDays: 30
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
];
|
|
47
|
+
const mockRuleWithDate = {
|
|
48
|
+
ID: "rule-with-date",
|
|
49
|
+
Status: "Enabled",
|
|
50
|
+
Expiration: {
|
|
51
|
+
Date: new Date("2025-12-31T00:00:00Z")
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const mockRuleWithSingleDay = {
|
|
55
|
+
ID: "rule-single-day",
|
|
56
|
+
Status: "Enabled",
|
|
57
|
+
Expiration: {
|
|
58
|
+
Days: 1
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
describe("BucketLifecycleList", ()=>{
|
|
62
|
+
beforeEach(()=>{
|
|
63
|
+
jest.clearAllMocks();
|
|
64
|
+
mockOffsetSize(800, 600);
|
|
65
|
+
});
|
|
66
|
+
it("shows a table with proper headers", ()=>{
|
|
67
|
+
renderBucketLifecycleList({
|
|
68
|
+
lifecycleRules: mockLifecycleRules
|
|
69
|
+
});
|
|
70
|
+
expect(screen.getByRole("grid")).toBeInTheDocument();
|
|
71
|
+
expect(screen.getByText("Rule ID")).toBeInTheDocument();
|
|
72
|
+
expect(screen.getByText("Status")).toBeInTheDocument();
|
|
73
|
+
expect(screen.getByText("Actions")).toBeInTheDocument();
|
|
74
|
+
});
|
|
75
|
+
it("displays lifecycle rules with their IDs", ()=>{
|
|
76
|
+
renderBucketLifecycleList({
|
|
77
|
+
lifecycleRules: mockLifecycleRules
|
|
78
|
+
});
|
|
79
|
+
expect(screen.getByText("rule-1")).toBeInTheDocument();
|
|
80
|
+
expect(screen.getByText("rule-2")).toBeInTheDocument();
|
|
81
|
+
expect(screen.getByText("rule-3")).toBeInTheDocument();
|
|
82
|
+
});
|
|
83
|
+
it("displays status as Active for Enabled rules", ()=>{
|
|
84
|
+
renderBucketLifecycleList({
|
|
85
|
+
lifecycleRules: mockLifecycleRules
|
|
86
|
+
});
|
|
87
|
+
const activeStatuses = screen.getAllByText("Active");
|
|
88
|
+
expect(activeStatuses.length).toBe(2);
|
|
89
|
+
});
|
|
90
|
+
it("displays status as Inactive for Disabled rules", ()=>{
|
|
91
|
+
renderBucketLifecycleList({
|
|
92
|
+
lifecycleRules: mockLifecycleRules
|
|
93
|
+
});
|
|
94
|
+
expect(screen.getByText("Inactive")).toBeInTheDocument();
|
|
95
|
+
});
|
|
96
|
+
it("displays single action correctly", ()=>{
|
|
97
|
+
const singleActionRule = [
|
|
98
|
+
{
|
|
99
|
+
ID: "single-action-rule",
|
|
100
|
+
Status: "Enabled",
|
|
101
|
+
Expiration: {
|
|
102
|
+
Days: 30
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
];
|
|
106
|
+
renderBucketLifecycleList({
|
|
107
|
+
lifecycleRules: singleActionRule
|
|
108
|
+
});
|
|
109
|
+
expect(screen.getByText(/Expire current \(30 days\)/)).toBeInTheDocument();
|
|
110
|
+
});
|
|
111
|
+
it("displays multiple actions with +N more indicator", ()=>{
|
|
112
|
+
renderBucketLifecycleList({
|
|
113
|
+
lifecycleRules: mockLifecycleRules
|
|
114
|
+
});
|
|
115
|
+
expect(screen.getByText(/\+2/)).toBeInTheDocument();
|
|
116
|
+
});
|
|
117
|
+
it("pluralizes days correctly - singular", ()=>{
|
|
118
|
+
renderBucketLifecycleList({
|
|
119
|
+
lifecycleRules: [
|
|
120
|
+
mockRuleWithSingleDay
|
|
121
|
+
]
|
|
122
|
+
});
|
|
123
|
+
expect(screen.getByText(/1 day/)).toBeInTheDocument();
|
|
124
|
+
});
|
|
125
|
+
it("pluralizes days correctly - plural", ()=>{
|
|
126
|
+
renderBucketLifecycleList({
|
|
127
|
+
lifecycleRules: mockLifecycleRules
|
|
128
|
+
});
|
|
129
|
+
expect(screen.getByText(/30 days/)).toBeInTheDocument();
|
|
130
|
+
});
|
|
131
|
+
it("displays Abort Incomplete MPU action", ()=>{
|
|
132
|
+
const mpuRule = [
|
|
133
|
+
{
|
|
134
|
+
ID: "mpu-rule",
|
|
135
|
+
Status: "Enabled",
|
|
136
|
+
AbortIncompleteMultipartUpload: {
|
|
137
|
+
DaysAfterInitiation: 7
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
];
|
|
141
|
+
renderBucketLifecycleList({
|
|
142
|
+
lifecycleRules: mpuRule
|
|
143
|
+
});
|
|
144
|
+
expect(screen.getByText(/Abort Incomplete MPU \(7 days\)/)).toBeInTheDocument();
|
|
145
|
+
});
|
|
146
|
+
it("displays dates in formatted format", ()=>{
|
|
147
|
+
renderBucketLifecycleList({
|
|
148
|
+
lifecycleRules: [
|
|
149
|
+
mockRuleWithDate
|
|
150
|
+
]
|
|
151
|
+
});
|
|
152
|
+
const gridElement = screen.getByRole("grid");
|
|
153
|
+
expect(gridElement).toHaveTextContent("2025");
|
|
154
|
+
});
|
|
155
|
+
it("handles edit button click", ()=>{
|
|
156
|
+
const onEditRule = jest.fn();
|
|
157
|
+
renderBucketLifecycleList({
|
|
158
|
+
lifecycleRules: mockLifecycleRules,
|
|
159
|
+
onEditRule
|
|
160
|
+
});
|
|
161
|
+
const editButtons = screen.getAllByRole("button", {
|
|
162
|
+
name: /edit rule/i
|
|
163
|
+
});
|
|
164
|
+
expect(editButtons.length).toBeGreaterThanOrEqual(3);
|
|
165
|
+
fireEvent.click(editButtons[0]);
|
|
166
|
+
expect(onEditRule).toHaveBeenCalled();
|
|
167
|
+
expect(onEditRule).toHaveBeenCalledWith(expect.any(String));
|
|
168
|
+
});
|
|
169
|
+
it("renders delete buttons for each rule", ()=>{
|
|
170
|
+
renderBucketLifecycleList({
|
|
171
|
+
lifecycleRules: mockLifecycleRules
|
|
172
|
+
});
|
|
173
|
+
const deleteButtons = screen.getAllByRole("button", {
|
|
174
|
+
name: /delete rule/i
|
|
175
|
+
});
|
|
176
|
+
expect(deleteButtons.length).toBeGreaterThanOrEqual(3);
|
|
177
|
+
});
|
|
178
|
+
it("handles empty lifecycle rules list", ()=>{
|
|
179
|
+
renderBucketLifecycleList({
|
|
180
|
+
lifecycleRules: []
|
|
181
|
+
});
|
|
182
|
+
expect(screen.getByRole("grid")).toBeInTheDocument();
|
|
183
|
+
expect(screen.getByText("Rule ID")).toBeInTheDocument();
|
|
184
|
+
});
|
|
185
|
+
it("handles lifecycle rules without IDs", ()=>{
|
|
186
|
+
const rulesWithoutIds = [
|
|
187
|
+
{
|
|
188
|
+
ID: void 0,
|
|
189
|
+
Status: "Enabled"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
Status: "Disabled"
|
|
193
|
+
}
|
|
194
|
+
];
|
|
195
|
+
renderBucketLifecycleList({
|
|
196
|
+
lifecycleRules: rulesWithoutIds
|
|
197
|
+
});
|
|
198
|
+
const dashes = screen.getAllByText("-");
|
|
199
|
+
expect(dashes.length).toBeGreaterThan(0);
|
|
200
|
+
});
|
|
201
|
+
it("shows loading state when lifecycleStatus is loading", ()=>{
|
|
202
|
+
renderBucketLifecycleList({
|
|
203
|
+
lifecycleRules: [],
|
|
204
|
+
lifecycleStatus: "loading"
|
|
205
|
+
});
|
|
206
|
+
expect(screen.getByRole("grid")).toBeInTheDocument();
|
|
207
|
+
expect(screen.queryByText("rule-1")).not.toBeInTheDocument();
|
|
208
|
+
});
|
|
209
|
+
it("shows error state when lifecycleStatus is error", ()=>{
|
|
210
|
+
renderBucketLifecycleList({
|
|
211
|
+
lifecycleRules: [],
|
|
212
|
+
lifecycleStatus: "error"
|
|
213
|
+
});
|
|
214
|
+
expect(screen.getByRole("grid")).toBeInTheDocument();
|
|
215
|
+
expect(screen.queryByText("rule-1")).not.toBeInTheDocument();
|
|
216
|
+
});
|
|
217
|
+
it("shows data when lifecycleStatus is success", ()=>{
|
|
218
|
+
renderBucketLifecycleList({
|
|
219
|
+
lifecycleRules: mockLifecycleRules,
|
|
220
|
+
lifecycleStatus: "success"
|
|
221
|
+
});
|
|
222
|
+
expect(screen.getByRole("grid")).toBeInTheDocument();
|
|
223
|
+
expect(screen.getByText("rule-1")).toBeInTheDocument();
|
|
224
|
+
expect(screen.getByText("rule-2")).toBeInTheDocument();
|
|
225
|
+
});
|
|
226
|
+
it("works when no callbacks are provided", ()=>{
|
|
227
|
+
renderBucketLifecycleList({
|
|
228
|
+
lifecycleRules: mockLifecycleRules
|
|
229
|
+
});
|
|
230
|
+
expect(()=>{
|
|
231
|
+
const createButton = screen.getByRole("button", {
|
|
232
|
+
name: /create rule/i
|
|
233
|
+
});
|
|
234
|
+
fireEvent.click(createButton);
|
|
235
|
+
const editButtons = screen.getAllByLabelText("Edit rule");
|
|
236
|
+
fireEvent.click(editButtons[0]);
|
|
237
|
+
const deleteButtons = screen.getAllByLabelText("Delete rule");
|
|
238
|
+
fireEvent.click(deleteButtons[0]);
|
|
239
|
+
}).not.toThrow();
|
|
240
|
+
});
|
|
241
|
+
it("renders rows that can be selected", ()=>{
|
|
242
|
+
renderBucketLifecycleList({
|
|
243
|
+
lifecycleRules: mockLifecycleRules
|
|
244
|
+
});
|
|
245
|
+
const rows = screen.getAllByRole("row");
|
|
246
|
+
expect(rows.length).toBeGreaterThan(1);
|
|
247
|
+
const firstDataRow = rows[1];
|
|
248
|
+
expect(firstDataRow).toHaveAttribute("aria-selected");
|
|
249
|
+
});
|
|
250
|
+
it("displays noncurrent version expiration with keep versions", ()=>{
|
|
251
|
+
const ruleWithKeep = [
|
|
252
|
+
{
|
|
253
|
+
ID: "rule-with-keep",
|
|
254
|
+
Status: "Enabled",
|
|
255
|
+
NoncurrentVersionExpiration: {
|
|
256
|
+
NoncurrentDays: 90,
|
|
257
|
+
NewerNoncurrentVersions: 3
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
];
|
|
261
|
+
renderBucketLifecycleList({
|
|
262
|
+
lifecycleRules: ruleWithKeep
|
|
263
|
+
});
|
|
264
|
+
expect(screen.getByText(/90 days, keep 3/)).toBeInTheDocument();
|
|
265
|
+
});
|
|
266
|
+
it("displays transition actions", ()=>{
|
|
267
|
+
const ruleWithTransition = [
|
|
268
|
+
{
|
|
269
|
+
ID: "transition-rule",
|
|
270
|
+
Status: "Enabled",
|
|
271
|
+
Transitions: [
|
|
272
|
+
{
|
|
273
|
+
Days: 60,
|
|
274
|
+
StorageClass: "GLACIER"
|
|
275
|
+
}
|
|
276
|
+
]
|
|
277
|
+
}
|
|
278
|
+
];
|
|
279
|
+
renderBucketLifecycleList({
|
|
280
|
+
lifecycleRules: ruleWithTransition
|
|
281
|
+
});
|
|
282
|
+
expect(screen.getByText(/Transition current \(60 days\)/)).toBeInTheDocument();
|
|
283
|
+
});
|
|
284
|
+
it("displays noncurrent version transition actions", ()=>{
|
|
285
|
+
const ruleWithNoncurrentTransition = [
|
|
286
|
+
{
|
|
287
|
+
ID: "noncurrent-transition-rule",
|
|
288
|
+
Status: "Enabled",
|
|
289
|
+
NoncurrentVersionTransitions: [
|
|
290
|
+
{
|
|
291
|
+
NoncurrentDays: 30,
|
|
292
|
+
StorageClass: "GLACIER"
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
];
|
|
297
|
+
renderBucketLifecycleList({
|
|
298
|
+
lifecycleRules: ruleWithNoncurrentTransition
|
|
299
|
+
});
|
|
300
|
+
expect(screen.getByText(/Transition noncurrent \(30 days\)/)).toBeInTheDocument();
|
|
301
|
+
});
|
|
302
|
+
it("displays expired object delete marker action", ()=>{
|
|
303
|
+
const ruleWithDeleteMarker = [
|
|
304
|
+
{
|
|
305
|
+
ID: "delete-marker-rule",
|
|
306
|
+
Status: "Enabled",
|
|
307
|
+
Expiration: {
|
|
308
|
+
ExpiredObjectDeleteMarker: true
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
];
|
|
312
|
+
renderBucketLifecycleList({
|
|
313
|
+
lifecycleRules: ruleWithDeleteMarker
|
|
314
|
+
});
|
|
315
|
+
expect(screen.getByText("Delete expired markers")).toBeInTheDocument();
|
|
316
|
+
});
|
|
317
|
+
it("shows info icon for multiple actions", ()=>{
|
|
318
|
+
renderBucketLifecycleList({
|
|
319
|
+
lifecycleRules: mockLifecycleRules
|
|
320
|
+
});
|
|
321
|
+
const rows = screen.getAllByRole("row");
|
|
322
|
+
const ruleWithMultipleActions = rows.find((row)=>within(row).queryByText(/\+2/));
|
|
323
|
+
expect(ruleWithMultipleActions).toBeInTheDocument();
|
|
324
|
+
});
|
|
325
|
+
});
|
|
@@ -222,4 +222,194 @@ describe("BucketList", ()=>{
|
|
|
222
222
|
fireEvent.click(bucketName.closest('[role="row"]') || bucketName);
|
|
223
223
|
expect(onBucketSelect).not.toHaveBeenCalled();
|
|
224
224
|
});
|
|
225
|
+
describe("additionalColumns support", ()=>{
|
|
226
|
+
it("renders additional columns when additionalColumns prop is provided", ()=>{
|
|
227
|
+
const additionalColumns = [
|
|
228
|
+
{
|
|
229
|
+
Header: "Metrics",
|
|
230
|
+
accessor: "metrics",
|
|
231
|
+
id: "metrics",
|
|
232
|
+
Cell: ()=>/*#__PURE__*/ jsx("span", {
|
|
233
|
+
children: "Metrics Data"
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
];
|
|
237
|
+
renderBucketList({
|
|
238
|
+
buckets: mockBuckets,
|
|
239
|
+
additionalColumns
|
|
240
|
+
});
|
|
241
|
+
expect(screen.getByText("Metrics")).toBeInTheDocument();
|
|
242
|
+
expect(screen.getAllByText("Metrics Data")).toHaveLength(3);
|
|
243
|
+
});
|
|
244
|
+
it("renders multiple additional columns in correct order", ()=>{
|
|
245
|
+
const additionalColumns = [
|
|
246
|
+
{
|
|
247
|
+
Header: "Column A",
|
|
248
|
+
accessor: "columnA",
|
|
249
|
+
id: "columnA",
|
|
250
|
+
Cell: ()=>/*#__PURE__*/ jsx("span", {
|
|
251
|
+
children: "A"
|
|
252
|
+
})
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
Header: "Column B",
|
|
256
|
+
accessor: "columnB",
|
|
257
|
+
id: "columnB",
|
|
258
|
+
Cell: ()=>/*#__PURE__*/ jsx("span", {
|
|
259
|
+
children: "B"
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
];
|
|
263
|
+
renderBucketList({
|
|
264
|
+
buckets: mockBuckets,
|
|
265
|
+
additionalColumns
|
|
266
|
+
});
|
|
267
|
+
const headers = screen.getAllByRole("columnheader");
|
|
268
|
+
const headerTexts = headers.map((h)=>h.textContent);
|
|
269
|
+
expect(headerTexts).toContain("Bucket Name");
|
|
270
|
+
expect(headerTexts).toContain("Storage Location");
|
|
271
|
+
expect(headerTexts).toContain("Column A");
|
|
272
|
+
expect(headerTexts).toContain("Column B");
|
|
273
|
+
expect(headerTexts).toContain("Created on");
|
|
274
|
+
const bucketNameIndex = headerTexts.indexOf("Bucket Name");
|
|
275
|
+
const storageLocationIndex = headerTexts.indexOf("Storage Location");
|
|
276
|
+
const columnAIndex = headerTexts.indexOf("Column A");
|
|
277
|
+
const columnBIndex = headerTexts.indexOf("Column B");
|
|
278
|
+
const createdOnIndex = headerTexts.indexOf("Created on");
|
|
279
|
+
expect(bucketNameIndex).toBeLessThan(storageLocationIndex);
|
|
280
|
+
expect(storageLocationIndex).toBeLessThan(columnAIndex);
|
|
281
|
+
expect(columnAIndex).toBeLessThan(columnBIndex);
|
|
282
|
+
expect(columnBIndex).toBeLessThan(createdOnIndex);
|
|
283
|
+
});
|
|
284
|
+
it("works without additional columns (backward compatibility)", ()=>{
|
|
285
|
+
renderBucketList({
|
|
286
|
+
buckets: mockBuckets
|
|
287
|
+
});
|
|
288
|
+
const headers = screen.getAllByRole("columnheader");
|
|
289
|
+
const headerTexts = headers.map((h)=>h.textContent);
|
|
290
|
+
expect(headerTexts).toContain("Bucket Name");
|
|
291
|
+
expect(headerTexts).toContain("Storage Location");
|
|
292
|
+
expect(headerTexts).toContain("Created on");
|
|
293
|
+
expect(headerTexts).toHaveLength(3);
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
describe("transformBucketData support", ()=>{
|
|
297
|
+
it("transforms bucket data when transformBucketData is provided", ()=>{
|
|
298
|
+
const transformBucketData = jest.fn((bucket)=>({
|
|
299
|
+
...bucket,
|
|
300
|
+
metrics: {
|
|
301
|
+
value: 100
|
|
302
|
+
}
|
|
303
|
+
}));
|
|
304
|
+
const additionalColumns = [
|
|
305
|
+
{
|
|
306
|
+
Header: "Metrics",
|
|
307
|
+
accessor: "metrics",
|
|
308
|
+
id: "metrics",
|
|
309
|
+
Cell: ({ value })=>/*#__PURE__*/ jsxs("span", {
|
|
310
|
+
children: [
|
|
311
|
+
"Value: ",
|
|
312
|
+
value.value
|
|
313
|
+
]
|
|
314
|
+
})
|
|
315
|
+
}
|
|
316
|
+
];
|
|
317
|
+
renderBucketList({
|
|
318
|
+
buckets: mockBuckets,
|
|
319
|
+
additionalColumns,
|
|
320
|
+
transformBucketData
|
|
321
|
+
});
|
|
322
|
+
expect(transformBucketData).toHaveBeenCalledTimes(3);
|
|
323
|
+
expect(transformBucketData).toHaveBeenCalledWith(mockBuckets[0]);
|
|
324
|
+
expect(transformBucketData).toHaveBeenCalledWith(mockBuckets[1]);
|
|
325
|
+
expect(transformBucketData).toHaveBeenCalledWith(mockBuckets[2]);
|
|
326
|
+
});
|
|
327
|
+
it("renders transformed data in additional columns", ()=>{
|
|
328
|
+
const transformBucketData = (bucket)=>({
|
|
329
|
+
...bucket,
|
|
330
|
+
metrics: {
|
|
331
|
+
value: "test-bucket-1" === bucket.Name ? 50 : 100
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
const additionalColumns = [
|
|
335
|
+
{
|
|
336
|
+
Header: "Metrics",
|
|
337
|
+
accessor: "metrics",
|
|
338
|
+
id: "metrics",
|
|
339
|
+
Cell: ({ value })=>/*#__PURE__*/ jsxs("span", {
|
|
340
|
+
children: [
|
|
341
|
+
"Metric: ",
|
|
342
|
+
value.value
|
|
343
|
+
]
|
|
344
|
+
})
|
|
345
|
+
}
|
|
346
|
+
];
|
|
347
|
+
renderBucketList({
|
|
348
|
+
buckets: mockBuckets,
|
|
349
|
+
additionalColumns,
|
|
350
|
+
transformBucketData
|
|
351
|
+
});
|
|
352
|
+
expect(screen.getByText("Metric: 50")).toBeInTheDocument();
|
|
353
|
+
expect(screen.getAllByText("Metric: 100")).toHaveLength(2);
|
|
354
|
+
});
|
|
355
|
+
it("works without transformBucketData (uses default behavior)", ()=>{
|
|
356
|
+
const additionalColumns = [
|
|
357
|
+
{
|
|
358
|
+
Header: "Test Column",
|
|
359
|
+
accessor: "Name",
|
|
360
|
+
id: "testColumn",
|
|
361
|
+
Cell: ({ value })=>/*#__PURE__*/ jsxs("span", {
|
|
362
|
+
children: [
|
|
363
|
+
"Name: ",
|
|
364
|
+
value
|
|
365
|
+
]
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
];
|
|
369
|
+
renderBucketList({
|
|
370
|
+
buckets: mockBuckets,
|
|
371
|
+
additionalColumns
|
|
372
|
+
});
|
|
373
|
+
expect(screen.getByText("Name: test-bucket-1")).toBeInTheDocument();
|
|
374
|
+
expect(screen.getByText("Name: test-bucket-2")).toBeInTheDocument();
|
|
375
|
+
expect(screen.getByText("Name: test-bucket-3")).toBeInTheDocument();
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
describe("generic type support", ()=>{
|
|
379
|
+
it("maintains type safety with extended bucket types", ()=>{
|
|
380
|
+
const transformBucketData = (bucket)=>({
|
|
381
|
+
...bucket,
|
|
382
|
+
customField: `Custom ${bucket.Name}`,
|
|
383
|
+
numericField: 42
|
|
384
|
+
});
|
|
385
|
+
const additionalColumns = [
|
|
386
|
+
{
|
|
387
|
+
Header: "Custom",
|
|
388
|
+
accessor: "customField",
|
|
389
|
+
id: "custom",
|
|
390
|
+
Cell: ({ value })=>/*#__PURE__*/ jsx("span", {
|
|
391
|
+
children: value
|
|
392
|
+
})
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
Header: "Number",
|
|
396
|
+
accessor: "numericField",
|
|
397
|
+
id: "numeric",
|
|
398
|
+
Cell: ({ value })=>/*#__PURE__*/ jsxs("span", {
|
|
399
|
+
children: [
|
|
400
|
+
"Num: ",
|
|
401
|
+
value
|
|
402
|
+
]
|
|
403
|
+
})
|
|
404
|
+
}
|
|
405
|
+
];
|
|
406
|
+
renderBucketList({
|
|
407
|
+
buckets: mockBuckets,
|
|
408
|
+
additionalColumns,
|
|
409
|
+
transformBucketData
|
|
410
|
+
});
|
|
411
|
+
expect(screen.getByText("Custom test-bucket-1")).toBeInTheDocument();
|
|
412
|
+
expect(screen.getAllByText("Num: 42")).toHaveLength(3);
|
|
413
|
+
});
|
|
414
|
+
});
|
|
225
415
|
});
|