@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.
- package/dist/components/DataBrowserUI.d.ts +20 -0
- package/dist/components/DataBrowserUI.js +64 -0
- package/dist/components/__tests__/BucketDetails.test.d.ts +1 -0
- package/dist/components/__tests__/BucketDetails.test.js +421 -0
- package/dist/components/__tests__/BucketList.test.js +389 -164
- package/dist/components/__tests__/BucketOverview.test.js +19 -63
- package/dist/components/__tests__/ObjectList.test.js +719 -219
- package/dist/components/buckets/BucketDetails.d.ts +40 -0
- package/dist/components/buckets/BucketDetails.js +194 -86
- package/dist/components/buckets/BucketList.d.ts +5 -6
- package/dist/components/buckets/BucketList.js +152 -97
- package/dist/components/buckets/BucketOverview.d.ts +6 -0
- package/dist/components/buckets/BucketOverview.js +363 -179
- package/dist/components/buckets/BucketPage.js +1 -5
- package/dist/components/buckets/BucketVersioning.js +3 -0
- package/dist/components/buckets/EmptyBucketButton.js +1 -1
- package/dist/components/index.d.ts +2 -1
- package/dist/components/index.js +2 -1
- package/dist/components/layouts/ArrowNavigation.js +20 -8
- package/dist/components/objects/CreateFolderButton.js +1 -1
- package/dist/components/objects/ObjectDetails/ObjectSummary.js +287 -157
- package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.d.ts +1 -0
- package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.js +516 -0
- package/dist/components/objects/ObjectDetails/__tests__/ObjectSummary.test.d.ts +1 -0
- package/dist/components/objects/ObjectDetails/__tests__/ObjectSummary.test.js +813 -0
- package/dist/components/objects/ObjectDetails/index.d.ts +16 -0
- package/dist/components/objects/ObjectDetails/index.js +132 -46
- package/dist/components/objects/ObjectList.d.ts +7 -5
- package/dist/components/objects/ObjectList.js +566 -286
- package/dist/components/objects/UploadButton.js +1 -1
- package/dist/config/types.d.ts +117 -0
- package/dist/contexts/DataBrowserUICustomizationContext.d.ts +27 -0
- package/dist/contexts/DataBrowserUICustomizationContext.js +13 -0
- package/dist/test/testUtils.d.ts +64 -0
- package/dist/test/testUtils.js +100 -1
- package/dist/types/index.d.ts +5 -3
- package/dist/utils/constants.d.ts +7 -0
- package/dist/utils/constants.js +8 -1
- package/dist/utils/useFeatures.js +1 -1
- 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
|
+
});
|