@scality/data-browser-library 1.0.0-preview.11 → 1.0.0-preview.13

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.
Files changed (40) hide show
  1. package/dist/components/DataBrowserUI.d.ts +20 -0
  2. package/dist/components/DataBrowserUI.js +64 -0
  3. package/dist/components/__tests__/BucketDetails.test.d.ts +1 -0
  4. package/dist/components/__tests__/BucketDetails.test.js +421 -0
  5. package/dist/components/__tests__/BucketList.test.js +389 -164
  6. package/dist/components/__tests__/BucketOverview.test.js +19 -63
  7. package/dist/components/__tests__/ObjectList.test.js +719 -219
  8. package/dist/components/buckets/BucketDetails.d.ts +40 -0
  9. package/dist/components/buckets/BucketDetails.js +194 -86
  10. package/dist/components/buckets/BucketList.d.ts +5 -6
  11. package/dist/components/buckets/BucketList.js +152 -97
  12. package/dist/components/buckets/BucketOverview.d.ts +6 -0
  13. package/dist/components/buckets/BucketOverview.js +363 -179
  14. package/dist/components/buckets/BucketPage.js +1 -5
  15. package/dist/components/buckets/BucketVersioning.js +3 -0
  16. package/dist/components/buckets/EmptyBucketButton.js +1 -1
  17. package/dist/components/index.d.ts +2 -1
  18. package/dist/components/index.js +2 -1
  19. package/dist/components/layouts/ArrowNavigation.js +20 -8
  20. package/dist/components/objects/CreateFolderButton.js +1 -1
  21. package/dist/components/objects/ObjectDetails/ObjectSummary.js +287 -157
  22. package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.d.ts +1 -0
  23. package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.js +516 -0
  24. package/dist/components/objects/ObjectDetails/__tests__/ObjectSummary.test.d.ts +1 -0
  25. package/dist/components/objects/ObjectDetails/__tests__/ObjectSummary.test.js +813 -0
  26. package/dist/components/objects/ObjectDetails/index.d.ts +16 -0
  27. package/dist/components/objects/ObjectDetails/index.js +132 -46
  28. package/dist/components/objects/ObjectList.d.ts +7 -5
  29. package/dist/components/objects/ObjectList.js +566 -286
  30. package/dist/components/objects/UploadButton.js +1 -1
  31. package/dist/config/types.d.ts +117 -0
  32. package/dist/contexts/DataBrowserUICustomizationContext.d.ts +27 -0
  33. package/dist/contexts/DataBrowserUICustomizationContext.js +13 -0
  34. package/dist/test/testUtils.d.ts +64 -0
  35. package/dist/test/testUtils.js +100 -1
  36. package/dist/types/index.d.ts +5 -3
  37. package/dist/utils/constants.d.ts +7 -0
  38. package/dist/utils/constants.js +8 -1
  39. package/dist/utils/useFeatures.js +1 -1
  40. package/package.json +2 -2
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ import type { DataBrowserUIProps } from "../config/types";
3
+ /**
4
+ * DataBrowserUI component - Main UI component for data browser.
5
+ * Must be wrapped with DataBrowserProvider for S3 client and theme context,
6
+ * and a BrowserRouter for routing.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * <BrowserRouter basename="/data-browser">
11
+ * <DataBrowserProvider getS3Config={getS3Config} theme={theme}>
12
+ * <DataBrowserUI
13
+ * extraBucketListColumns={[...]}
14
+ * extraObjectListActions={[...]}
15
+ * />
16
+ * </DataBrowserProvider>
17
+ * </BrowserRouter>
18
+ * ```
19
+ */
20
+ export declare const DataBrowserUI: React.FC<DataBrowserUIProps>;
@@ -0,0 +1,64 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import "react";
3
+ import { Navigate, Route, Routes } from "react-router-dom";
4
+ import { DataBrowserUICustomizationProvider } from "../contexts/DataBrowserUICustomizationContext.js";
5
+ import { BucketCreate, BucketLifecycleFormPage, BucketNotificationCreatePage, BucketPage, BucketPolicyPage, BucketReplicationFormPage, ObjectLockSettings, ObjectPage } from "./index.js";
6
+ const DataBrowserUI = (props)=>/*#__PURE__*/ jsx(DataBrowserUICustomizationProvider, {
7
+ config: props,
8
+ children: /*#__PURE__*/ jsxs(Routes, {
9
+ children: [
10
+ /*#__PURE__*/ jsx(Route, {
11
+ path: "/",
12
+ element: /*#__PURE__*/ jsx(Navigate, {
13
+ to: "/buckets",
14
+ replace: true
15
+ })
16
+ }),
17
+ /*#__PURE__*/ jsx(Route, {
18
+ path: "/buckets",
19
+ element: /*#__PURE__*/ jsx(BucketPage, {})
20
+ }),
21
+ /*#__PURE__*/ jsx(Route, {
22
+ path: "/buckets/-/create",
23
+ element: /*#__PURE__*/ jsx(BucketCreate, {})
24
+ }),
25
+ /*#__PURE__*/ jsx(Route, {
26
+ path: "/buckets/:bucketName/*",
27
+ element: /*#__PURE__*/ jsx(BucketPage, {})
28
+ }),
29
+ /*#__PURE__*/ jsx(Route, {
30
+ path: "/buckets/:bucketName/policy",
31
+ element: /*#__PURE__*/ jsx(BucketPolicyPage, {})
32
+ }),
33
+ /*#__PURE__*/ jsx(Route, {
34
+ path: "/buckets/:bucketName/objects",
35
+ element: /*#__PURE__*/ jsx(ObjectPage, {})
36
+ }),
37
+ /*#__PURE__*/ jsx(Route, {
38
+ path: "/buckets/:bucketName/notifications/create",
39
+ element: /*#__PURE__*/ jsx(BucketNotificationCreatePage, {})
40
+ }),
41
+ /*#__PURE__*/ jsx(Route, {
42
+ path: "/buckets/:bucketName/lifecycle/create",
43
+ element: /*#__PURE__*/ jsx(BucketLifecycleFormPage, {})
44
+ }),
45
+ /*#__PURE__*/ jsx(Route, {
46
+ path: "/buckets/:bucketName/lifecycle/edit/:ruleId",
47
+ element: /*#__PURE__*/ jsx(BucketLifecycleFormPage, {})
48
+ }),
49
+ /*#__PURE__*/ jsx(Route, {
50
+ path: "/buckets/:bucketName/replication/create",
51
+ element: /*#__PURE__*/ jsx(BucketReplicationFormPage, {})
52
+ }),
53
+ /*#__PURE__*/ jsx(Route, {
54
+ path: "/buckets/:bucketName/replication/edit/:ruleId",
55
+ element: /*#__PURE__*/ jsx(BucketReplicationFormPage, {})
56
+ }),
57
+ /*#__PURE__*/ jsx(Route, {
58
+ path: "/buckets/:bucketName/objects/object-lock-settings",
59
+ element: /*#__PURE__*/ jsx(ObjectLockSettings, {})
60
+ })
61
+ ]
62
+ })
63
+ });
64
+ export { DataBrowserUI };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,421 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { render, screen, waitFor } from "@testing-library/react";
3
+ import { MemoryRouter } from "react-router-dom";
4
+ import { BucketDetails } from "../buckets/BucketDetails.js";
5
+ import { applyBucketMocks, createTestWrapper } from "../../test/testUtils.js";
6
+ import { useGetBucketAcl, useGetBucketCors, useGetBucketLifecycle, useGetBucketLocation, useGetBucketObjectLockConfiguration, useGetBucketPolicy, useGetBucketReplication, useGetBucketTagging, useGetBucketVersioning } from "../../hooks/index.js";
7
+ import { useISVBucketStatus } from "../../hooks/useISVBucketDetection.js";
8
+ import { useFeatures } from "../../utils/useFeatures.js";
9
+ import * as __WEBPACK_EXTERNAL_MODULE__contexts_DataBrowserUICustomizationContext_js_f267b01c__ from "../../contexts/DataBrowserUICustomizationContext.js";
10
+ jest.mock("../../hooks");
11
+ jest.mock("../../hooks/useISVBucketDetection");
12
+ jest.mock("../../utils/useFeatures");
13
+ jest.mock("../../contexts/DataBrowserUICustomizationContext");
14
+ const mockUseParams = jest.fn();
15
+ const mockUseNavigate = jest.fn();
16
+ jest.mock("react-router-dom", ()=>({
17
+ ...jest.requireActual("react-router-dom"),
18
+ useParams: ()=>mockUseParams(),
19
+ useNavigate: ()=>mockUseNavigate()
20
+ }));
21
+ const mockUseGetBucketLifecycle = jest.mocked(useGetBucketLifecycle);
22
+ const mockUseGetBucketReplication = jest.mocked(useGetBucketReplication);
23
+ const mockUseGetBucketVersioning = jest.mocked(useGetBucketVersioning);
24
+ const mockUseGetBucketAcl = jest.mocked(useGetBucketAcl);
25
+ const mockUseGetBucketCors = jest.mocked(useGetBucketCors);
26
+ const mockUseGetBucketObjectLockConfiguration = jest.mocked(useGetBucketObjectLockConfiguration);
27
+ const mockUseGetBucketPolicy = jest.mocked(useGetBucketPolicy);
28
+ const mockUseGetBucketLocation = jest.mocked(useGetBucketLocation);
29
+ const mockUseGetBucketTagging = jest.mocked(useGetBucketTagging);
30
+ const mockUseISVBucketStatus = jest.mocked(useISVBucketStatus);
31
+ const mockUseFeatures = jest.mocked(useFeatures);
32
+ const mockUseDataBrowserUICustomization = (config = {})=>{
33
+ jest.spyOn(__WEBPACK_EXTERNAL_MODULE__contexts_DataBrowserUICustomizationContext_js_f267b01c__, "useDataBrowserUICustomization").mockReturnValue(config);
34
+ };
35
+ const renderBucketDetails = ()=>{
36
+ const Wrapper = createTestWrapper();
37
+ return render(/*#__PURE__*/ jsx(MemoryRouter, {
38
+ initialEntries: [
39
+ "/buckets/test-bucket"
40
+ ],
41
+ children: /*#__PURE__*/ jsx(Wrapper, {
42
+ children: /*#__PURE__*/ jsx(BucketDetails, {})
43
+ })
44
+ }));
45
+ };
46
+ const mockHookDefaults = (overrides = {})=>{
47
+ applyBucketMocks({
48
+ useGetBucketVersioning: mockUseGetBucketVersioning,
49
+ useGetBucketAcl: mockUseGetBucketAcl,
50
+ useGetBucketLocation: mockUseGetBucketLocation,
51
+ useGetBucketCors: mockUseGetBucketCors,
52
+ useGetBucketObjectLockConfiguration: mockUseGetBucketObjectLockConfiguration,
53
+ useGetBucketPolicy: mockUseGetBucketPolicy,
54
+ useGetBucketTagging: mockUseGetBucketTagging,
55
+ useGetBucketLifecycle: mockUseGetBucketLifecycle,
56
+ useGetBucketReplication: mockUseGetBucketReplication,
57
+ useISVBucketStatus: mockUseISVBucketStatus,
58
+ useFeatures: mockUseFeatures
59
+ }, overrides);
60
+ };
61
+ describe("BucketDetails", ()=>{
62
+ beforeEach(()=>{
63
+ jest.clearAllMocks();
64
+ mockHookDefaults();
65
+ mockUseDataBrowserUICustomization({});
66
+ mockUseParams.mockReturnValue({
67
+ bucketName: "test-bucket"
68
+ });
69
+ mockUseNavigate.mockReturnValue(jest.fn());
70
+ });
71
+ it("renders all default tabs", async ()=>{
72
+ renderBucketDetails();
73
+ await waitFor(()=>{
74
+ expect(screen.getByText("Overview")).toBeInTheDocument();
75
+ expect(screen.getByText("Lifecycle")).toBeInTheDocument();
76
+ expect(screen.getByText("Replication")).toBeInTheDocument();
77
+ });
78
+ });
79
+ it("shows overview tab content by default", ()=>{
80
+ renderBucketDetails();
81
+ const overviewTab = screen.getByText("Overview").closest('[role="tab"]');
82
+ expect(overviewTab).toHaveAttribute("class", expect.stringContaining("selected"));
83
+ });
84
+ it("handles lifecycle data loading", ()=>{
85
+ mockUseGetBucketLifecycle.mockReturnValue({
86
+ data: void 0,
87
+ status: "pending",
88
+ error: null
89
+ });
90
+ renderBucketDetails();
91
+ expect(screen.getByText("Lifecycle")).toBeInTheDocument();
92
+ });
93
+ it("handles lifecycle error gracefully", ()=>{
94
+ mockUseGetBucketLifecycle.mockReturnValue({
95
+ data: void 0,
96
+ status: "error",
97
+ error: new Error("Failed to fetch")
98
+ });
99
+ renderBucketDetails();
100
+ expect(screen.getByText("Lifecycle")).toBeInTheDocument();
101
+ });
102
+ it("handles lifecycle 404 as success", ()=>{
103
+ const notFoundError = new Error("Not found");
104
+ notFoundError.name = "NoSuchLifecycleConfiguration";
105
+ notFoundError.$metadata = {
106
+ httpStatusCode: 404
107
+ };
108
+ mockUseGetBucketLifecycle.mockReturnValue({
109
+ data: void 0,
110
+ status: "error",
111
+ error: notFoundError
112
+ });
113
+ renderBucketDetails();
114
+ expect(screen.getByText("Lifecycle")).toBeInTheDocument();
115
+ });
116
+ it("displays lifecycle rules when available", ()=>{
117
+ mockUseGetBucketLifecycle.mockReturnValue({
118
+ data: {
119
+ Rules: [
120
+ {
121
+ ID: "rule-1",
122
+ Status: "Enabled",
123
+ Filter: {}
124
+ },
125
+ {
126
+ ID: "rule-2",
127
+ Status: "Disabled",
128
+ Filter: {}
129
+ }
130
+ ]
131
+ },
132
+ status: "success",
133
+ error: null
134
+ });
135
+ renderBucketDetails();
136
+ expect(screen.getByText("Lifecycle")).toBeInTheDocument();
137
+ });
138
+ it("handles replication data loading", ()=>{
139
+ mockUseGetBucketReplication.mockReturnValue({
140
+ data: void 0,
141
+ status: "pending",
142
+ error: null
143
+ });
144
+ renderBucketDetails();
145
+ expect(screen.getByText("Replication")).toBeInTheDocument();
146
+ });
147
+ it("handles replication error gracefully", ()=>{
148
+ mockUseGetBucketReplication.mockReturnValue({
149
+ data: void 0,
150
+ status: "error",
151
+ error: new Error("Failed to fetch")
152
+ });
153
+ renderBucketDetails();
154
+ expect(screen.getByText("Replication")).toBeInTheDocument();
155
+ });
156
+ it("handles replication 404 as success", ()=>{
157
+ const notFoundError = new Error("Not found");
158
+ notFoundError.name = "ReplicationConfigurationNotFoundError";
159
+ notFoundError.$metadata = {
160
+ httpStatusCode: 404
161
+ };
162
+ mockUseGetBucketReplication.mockReturnValue({
163
+ data: void 0,
164
+ status: "error",
165
+ error: notFoundError
166
+ });
167
+ renderBucketDetails();
168
+ expect(screen.getByText("Replication")).toBeInTheDocument();
169
+ });
170
+ it("displays replication rules when available", ()=>{
171
+ mockUseGetBucketReplication.mockReturnValue({
172
+ data: {
173
+ ReplicationConfiguration: {
174
+ Role: "arn:aws:iam::123456789012:role/replication-role",
175
+ Rules: [
176
+ {
177
+ ID: "repl-rule-1",
178
+ Status: "Enabled",
179
+ Priority: 1,
180
+ Destination: {
181
+ Bucket: "arn:aws:s3:::destination-bucket"
182
+ }
183
+ }
184
+ ]
185
+ }
186
+ },
187
+ status: "success",
188
+ error: null
189
+ });
190
+ renderBucketDetails();
191
+ expect(screen.getByText("Replication")).toBeInTheDocument();
192
+ });
193
+ it("shows message when bucket name is missing", ()=>{
194
+ mockUseParams.mockReturnValue({
195
+ bucketName: void 0
196
+ });
197
+ renderBucketDetails();
198
+ expect(screen.getByText("No bucket selected")).toBeInTheDocument();
199
+ });
200
+ describe("extraBucketTabs support", ()=>{
201
+ it("renders extra tabs when configured", async ()=>{
202
+ const CustomTab = ()=>/*#__PURE__*/ jsx("div", {
203
+ children: "Custom Tab Content"
204
+ });
205
+ mockUseDataBrowserUICustomization({
206
+ extraBucketTabs: [
207
+ {
208
+ id: "customTab",
209
+ title: "Custom Tab",
210
+ render: CustomTab
211
+ }
212
+ ]
213
+ });
214
+ renderBucketDetails();
215
+ await waitFor(()=>{
216
+ expect(screen.getByText("Custom Tab")).toBeInTheDocument();
217
+ });
218
+ });
219
+ it("renders multiple extra tabs", async ()=>{
220
+ const Tab1 = ()=>/*#__PURE__*/ jsx("div", {
221
+ children: "Tab 1 Content"
222
+ });
223
+ const Tab2 = ()=>/*#__PURE__*/ jsx("div", {
224
+ children: "Tab 2 Content"
225
+ });
226
+ mockUseDataBrowserUICustomization({
227
+ extraBucketTabs: [
228
+ {
229
+ id: "tab1",
230
+ title: "Tab 1",
231
+ render: Tab1
232
+ },
233
+ {
234
+ id: "tab2",
235
+ title: "Tab 2",
236
+ render: Tab2
237
+ }
238
+ ]
239
+ });
240
+ renderBucketDetails();
241
+ await waitFor(()=>{
242
+ expect(screen.getByText("Tab 1")).toBeInTheDocument();
243
+ expect(screen.getByText("Tab 2")).toBeInTheDocument();
244
+ });
245
+ });
246
+ it("can replace default tabs using matching IDs", async ()=>{
247
+ const CustomOverview = ()=>/*#__PURE__*/ jsx("div", {
248
+ children: "Custom Overview Content"
249
+ });
250
+ mockUseDataBrowserUICustomization({
251
+ extraBucketTabs: [
252
+ {
253
+ id: "overview",
254
+ title: "Custom Overview",
255
+ render: CustomOverview
256
+ }
257
+ ]
258
+ });
259
+ renderBucketDetails();
260
+ await waitFor(()=>{
261
+ expect(screen.getByText("Custom Overview")).toBeInTheDocument();
262
+ expect(screen.queryByText("Overview")).not.toBeInTheDocument();
263
+ });
264
+ });
265
+ it("supports withoutPadding option for extra tabs", async ()=>{
266
+ const CustomTab = ()=>/*#__PURE__*/ jsx("div", {
267
+ children: "No Padding Content"
268
+ });
269
+ mockUseDataBrowserUICustomization({
270
+ extraBucketTabs: [
271
+ {
272
+ id: "noPaddingTab",
273
+ title: "No Padding",
274
+ withoutPadding: true,
275
+ render: CustomTab
276
+ }
277
+ ]
278
+ });
279
+ renderBucketDetails();
280
+ await waitFor(()=>{
281
+ expect(screen.getByText("No Padding")).toBeInTheDocument();
282
+ });
283
+ });
284
+ it("supports custom path for extra tabs", async ()=>{
285
+ const CustomTab = ()=>/*#__PURE__*/ jsx("div", {
286
+ children: "Custom Path Content"
287
+ });
288
+ mockUseDataBrowserUICustomization({
289
+ extraBucketTabs: [
290
+ {
291
+ id: "customPath",
292
+ title: "Custom Path Tab",
293
+ path: "/custom-path",
294
+ render: CustomTab
295
+ }
296
+ ]
297
+ });
298
+ renderBucketDetails();
299
+ await waitFor(()=>{
300
+ expect(screen.getByText("Custom Path Tab")).toBeInTheDocument();
301
+ });
302
+ });
303
+ it("maintains correct tab order with extra tabs", async ()=>{
304
+ const ExtraTab = ()=>/*#__PURE__*/ jsx("div", {
305
+ children: "Extra Tab Content"
306
+ });
307
+ mockUseDataBrowserUICustomization({
308
+ extraBucketTabs: [
309
+ {
310
+ id: "extraTab",
311
+ title: "Extra Tab",
312
+ render: ExtraTab
313
+ }
314
+ ]
315
+ });
316
+ renderBucketDetails();
317
+ await waitFor(()=>{
318
+ const tabs = screen.getAllByRole("tab");
319
+ const tabTexts = tabs.map((tab)=>tab.textContent);
320
+ expect(tabTexts).toEqual([
321
+ "Overview",
322
+ "Lifecycle",
323
+ "Replication",
324
+ "Extra Tab"
325
+ ]);
326
+ });
327
+ });
328
+ it("can replace lifecycle tab", async ()=>{
329
+ const CustomLifecycle = ()=>/*#__PURE__*/ jsx("div", {
330
+ children: "Custom Lifecycle Content"
331
+ });
332
+ mockUseDataBrowserUICustomization({
333
+ extraBucketTabs: [
334
+ {
335
+ id: "lifecycle",
336
+ title: "Custom Lifecycle",
337
+ render: CustomLifecycle
338
+ }
339
+ ]
340
+ });
341
+ renderBucketDetails();
342
+ await waitFor(()=>{
343
+ expect(screen.getByText("Custom Lifecycle")).toBeInTheDocument();
344
+ expect(screen.queryByText("Lifecycle")).not.toBeInTheDocument();
345
+ });
346
+ });
347
+ it("can replace replication tab", async ()=>{
348
+ const CustomReplication = ()=>/*#__PURE__*/ jsx("div", {
349
+ children: "Custom Replication Content"
350
+ });
351
+ mockUseDataBrowserUICustomization({
352
+ extraBucketTabs: [
353
+ {
354
+ id: "replication",
355
+ title: "Custom Replication",
356
+ render: CustomReplication
357
+ }
358
+ ]
359
+ });
360
+ renderBucketDetails();
361
+ await waitFor(()=>{
362
+ expect(screen.getByText("Custom Replication")).toBeInTheDocument();
363
+ expect(screen.queryByText("Replication")).not.toBeInTheDocument();
364
+ });
365
+ });
366
+ });
367
+ describe("Tab query parameters", ()=>{
368
+ it("creates correct query object for overview tab", ()=>{
369
+ renderBucketDetails();
370
+ const overviewTab = screen.getByText("Overview");
371
+ expect(overviewTab).toBeInTheDocument();
372
+ expect(overviewTab.closest('[role="tab"]')).toBeInTheDocument();
373
+ });
374
+ it("creates correct query object for lifecycle tab", ()=>{
375
+ renderBucketDetails();
376
+ const lifecycleTab = screen.getByText("Lifecycle");
377
+ expect(lifecycleTab).toBeInTheDocument();
378
+ expect(lifecycleTab.closest('[role="tab"]')).toBeInTheDocument();
379
+ });
380
+ it("creates correct query object for replication tab", ()=>{
381
+ renderBucketDetails();
382
+ const replicationTab = screen.getByText("Replication");
383
+ expect(replicationTab).toBeInTheDocument();
384
+ expect(replicationTab.closest('[role="tab"]')).toBeInTheDocument();
385
+ });
386
+ });
387
+ describe("Status transformation", ()=>{
388
+ it("transforms pending status to loading", ()=>{
389
+ mockUseGetBucketLifecycle.mockReturnValue({
390
+ data: void 0,
391
+ status: "pending",
392
+ error: null
393
+ });
394
+ renderBucketDetails();
395
+ expect(screen.getByText("Lifecycle")).toBeInTheDocument();
396
+ });
397
+ it("transforms error status correctly", ()=>{
398
+ mockUseGetBucketLifecycle.mockReturnValue({
399
+ data: void 0,
400
+ status: "error",
401
+ error: new Error("Generic error")
402
+ });
403
+ renderBucketDetails();
404
+ expect(screen.getByText("Lifecycle")).toBeInTheDocument();
405
+ });
406
+ it("transforms 404 error to success", ()=>{
407
+ const notFoundError = new Error("Not found");
408
+ notFoundError.name = "NoSuchLifecycleConfiguration";
409
+ notFoundError.$metadata = {
410
+ httpStatusCode: 404
411
+ };
412
+ mockUseGetBucketLifecycle.mockReturnValue({
413
+ data: void 0,
414
+ status: "error",
415
+ error: notFoundError
416
+ });
417
+ renderBucketDetails();
418
+ expect(screen.getByText("Lifecycle")).toBeInTheDocument();
419
+ });
420
+ });
421
+ });