@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 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
3
|
+
import { MemoryRouter } from "react-router";
|
|
4
|
+
import { createTestWrapper, mockOffsetSize } from "../../test/testUtils.js";
|
|
5
|
+
import { BucketReplicationList } from "../buckets/BucketReplicationList.js";
|
|
6
|
+
const renderBucketReplicationList = (props = {})=>{
|
|
7
|
+
const Wrapper = createTestWrapper();
|
|
8
|
+
return render(/*#__PURE__*/ jsx(MemoryRouter, {
|
|
9
|
+
children: /*#__PURE__*/ jsx(Wrapper, {
|
|
10
|
+
children: /*#__PURE__*/ jsx(BucketReplicationList, {
|
|
11
|
+
bucketName: "test-bucket",
|
|
12
|
+
replicationRules: [],
|
|
13
|
+
replicationRole: "arn:aws:iam::123456789012:role/replication-role",
|
|
14
|
+
...props
|
|
15
|
+
})
|
|
16
|
+
})
|
|
17
|
+
}));
|
|
18
|
+
};
|
|
19
|
+
const mockReplicationRules = [
|
|
20
|
+
{
|
|
21
|
+
ID: "rule-1",
|
|
22
|
+
Status: "Enabled",
|
|
23
|
+
Priority: 1,
|
|
24
|
+
Destination: {
|
|
25
|
+
Bucket: "arn:aws:s3:::destination-bucket-1",
|
|
26
|
+
StorageClass: "STANDARD"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
ID: "rule-2",
|
|
31
|
+
Status: "Disabled",
|
|
32
|
+
Priority: 2,
|
|
33
|
+
Destination: {
|
|
34
|
+
Bucket: "arn:aws:s3:::123456789012:backup-bucket",
|
|
35
|
+
StorageClass: "GLACIER",
|
|
36
|
+
Account: "123456789012"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
ID: "rule-3",
|
|
41
|
+
Status: "Enabled",
|
|
42
|
+
Priority: 100,
|
|
43
|
+
Destination: {
|
|
44
|
+
Bucket: "arn:aws:s3:::logs-backup",
|
|
45
|
+
StorageClass: "ONEZONE_IA"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
];
|
|
49
|
+
const mockRuleWithoutPriority = {
|
|
50
|
+
ID: "no-priority-rule",
|
|
51
|
+
Status: "Enabled",
|
|
52
|
+
Destination: {
|
|
53
|
+
Bucket: "arn:aws:s3:::simple-backup"
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const mockRuleWithoutStorageClass = {
|
|
57
|
+
ID: "no-storage-class-rule",
|
|
58
|
+
Status: "Enabled",
|
|
59
|
+
Priority: 5,
|
|
60
|
+
Destination: {
|
|
61
|
+
Bucket: "arn:aws:s3:::mirror-bucket"
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
describe("BucketReplicationList", ()=>{
|
|
65
|
+
beforeEach(()=>{
|
|
66
|
+
jest.clearAllMocks();
|
|
67
|
+
mockOffsetSize(800, 600);
|
|
68
|
+
});
|
|
69
|
+
it("shows a table with proper headers", ()=>{
|
|
70
|
+
renderBucketReplicationList({
|
|
71
|
+
replicationRules: mockReplicationRules
|
|
72
|
+
});
|
|
73
|
+
expect(screen.getByRole("grid")).toBeInTheDocument();
|
|
74
|
+
expect(screen.getByText("Rule ID")).toBeInTheDocument();
|
|
75
|
+
expect(screen.getByText("Status")).toBeInTheDocument();
|
|
76
|
+
expect(screen.getByText("Destination")).toBeInTheDocument();
|
|
77
|
+
expect(screen.getByText("Priority")).toBeInTheDocument();
|
|
78
|
+
});
|
|
79
|
+
it("displays replication rules with their IDs", ()=>{
|
|
80
|
+
renderBucketReplicationList({
|
|
81
|
+
replicationRules: mockReplicationRules
|
|
82
|
+
});
|
|
83
|
+
expect(screen.getByText("rule-1")).toBeInTheDocument();
|
|
84
|
+
expect(screen.getByText("rule-2")).toBeInTheDocument();
|
|
85
|
+
expect(screen.getByText("rule-3")).toBeInTheDocument();
|
|
86
|
+
});
|
|
87
|
+
it("displays status as Active for Enabled rules", ()=>{
|
|
88
|
+
renderBucketReplicationList({
|
|
89
|
+
replicationRules: mockReplicationRules
|
|
90
|
+
});
|
|
91
|
+
const activeStatuses = screen.getAllByText("Active");
|
|
92
|
+
expect(activeStatuses.length).toBe(2);
|
|
93
|
+
});
|
|
94
|
+
it("displays status as Inactive for Disabled rules", ()=>{
|
|
95
|
+
renderBucketReplicationList({
|
|
96
|
+
replicationRules: mockReplicationRules
|
|
97
|
+
});
|
|
98
|
+
expect(screen.getByText("Inactive")).toBeInTheDocument();
|
|
99
|
+
});
|
|
100
|
+
it("displays replication action with bucket name", ()=>{
|
|
101
|
+
renderBucketReplicationList({
|
|
102
|
+
replicationRules: mockReplicationRules
|
|
103
|
+
});
|
|
104
|
+
expect(screen.getByText(/Replicate to destination-bucket-1/)).toBeInTheDocument();
|
|
105
|
+
expect(screen.getByText(/Replicate to backup-bucket/)).toBeInTheDocument();
|
|
106
|
+
expect(screen.getByText(/Replicate to logs-backup/)).toBeInTheDocument();
|
|
107
|
+
});
|
|
108
|
+
it("displays storage class in actions column", ()=>{
|
|
109
|
+
renderBucketReplicationList({
|
|
110
|
+
replicationRules: mockReplicationRules
|
|
111
|
+
});
|
|
112
|
+
const actionTexts = screen.getAllByText(/Replicate to/);
|
|
113
|
+
expect(actionTexts.length).toBeGreaterThanOrEqual(3);
|
|
114
|
+
expect(screen.getByText(/STANDARD/, {
|
|
115
|
+
exact: false
|
|
116
|
+
})).toBeInTheDocument();
|
|
117
|
+
expect(screen.getByText(/GLACIER/, {
|
|
118
|
+
exact: false
|
|
119
|
+
})).toBeInTheDocument();
|
|
120
|
+
expect(screen.getByText(/ONEZONE_IA/, {
|
|
121
|
+
exact: false
|
|
122
|
+
})).toBeInTheDocument();
|
|
123
|
+
});
|
|
124
|
+
it("displays shortened account ID for cross-account replication", ()=>{
|
|
125
|
+
renderBucketReplicationList({
|
|
126
|
+
replicationRules: mockReplicationRules
|
|
127
|
+
});
|
|
128
|
+
expect(screen.getByText(/account: 123\.\.\.012/)).toBeInTheDocument();
|
|
129
|
+
});
|
|
130
|
+
it("displays priority values correctly", ()=>{
|
|
131
|
+
renderBucketReplicationList({
|
|
132
|
+
replicationRules: mockReplicationRules
|
|
133
|
+
});
|
|
134
|
+
expect(screen.getByText("1")).toBeInTheDocument();
|
|
135
|
+
expect(screen.getByText("2")).toBeInTheDocument();
|
|
136
|
+
expect(screen.getByText("100")).toBeInTheDocument();
|
|
137
|
+
});
|
|
138
|
+
it("displays 'None' for rules without priority", ()=>{
|
|
139
|
+
renderBucketReplicationList({
|
|
140
|
+
replicationRules: [
|
|
141
|
+
mockRuleWithoutPriority
|
|
142
|
+
]
|
|
143
|
+
});
|
|
144
|
+
expect(screen.getByText("None")).toBeInTheDocument();
|
|
145
|
+
});
|
|
146
|
+
it("displays action without storage class when not provided", ()=>{
|
|
147
|
+
renderBucketReplicationList({
|
|
148
|
+
replicationRules: [
|
|
149
|
+
mockRuleWithoutStorageClass
|
|
150
|
+
]
|
|
151
|
+
});
|
|
152
|
+
const actionText = screen.getByText(/Replicate to mirror-bucket/);
|
|
153
|
+
expect(actionText).toBeInTheDocument();
|
|
154
|
+
expect(actionText.textContent).toBe("Replicate to mirror-bucket");
|
|
155
|
+
});
|
|
156
|
+
it("renders create rule button as enabled", ()=>{
|
|
157
|
+
renderBucketReplicationList({
|
|
158
|
+
replicationRules: mockReplicationRules
|
|
159
|
+
});
|
|
160
|
+
const createButton = screen.getByRole("button", {
|
|
161
|
+
name: /create rule/i
|
|
162
|
+
});
|
|
163
|
+
expect(createButton).toBeInTheDocument();
|
|
164
|
+
expect(createButton).toBeEnabled();
|
|
165
|
+
});
|
|
166
|
+
it("calls create rule callback when button is clicked", ()=>{
|
|
167
|
+
const onCreateRule = jest.fn();
|
|
168
|
+
renderBucketReplicationList({
|
|
169
|
+
replicationRules: mockReplicationRules,
|
|
170
|
+
onCreateRule
|
|
171
|
+
});
|
|
172
|
+
const createButton = screen.getByRole("button", {
|
|
173
|
+
name: /create rule/i
|
|
174
|
+
});
|
|
175
|
+
expect(createButton).toBeEnabled();
|
|
176
|
+
fireEvent.click(createButton);
|
|
177
|
+
expect(onCreateRule).toHaveBeenCalled();
|
|
178
|
+
});
|
|
179
|
+
it("handles edit button click", ()=>{
|
|
180
|
+
const onEditRule = jest.fn();
|
|
181
|
+
renderBucketReplicationList({
|
|
182
|
+
replicationRules: mockReplicationRules,
|
|
183
|
+
onEditRule
|
|
184
|
+
});
|
|
185
|
+
const editButtons = screen.getAllByRole("button", {
|
|
186
|
+
name: /edit rule/i
|
|
187
|
+
});
|
|
188
|
+
expect(editButtons.length).toBeGreaterThanOrEqual(3);
|
|
189
|
+
fireEvent.click(editButtons[0]);
|
|
190
|
+
expect(onEditRule).toHaveBeenCalledWith(expect.any(String));
|
|
191
|
+
});
|
|
192
|
+
it("renders delete buttons for each rule", ()=>{
|
|
193
|
+
renderBucketReplicationList({
|
|
194
|
+
replicationRules: mockReplicationRules
|
|
195
|
+
});
|
|
196
|
+
const deleteButtons = screen.getAllByRole("button", {
|
|
197
|
+
name: /delete rule/i
|
|
198
|
+
});
|
|
199
|
+
expect(deleteButtons.length).toBeGreaterThanOrEqual(3);
|
|
200
|
+
});
|
|
201
|
+
it("handles empty replication rules list", ()=>{
|
|
202
|
+
renderBucketReplicationList({
|
|
203
|
+
replicationRules: []
|
|
204
|
+
});
|
|
205
|
+
expect(screen.getByRole("grid")).toBeInTheDocument();
|
|
206
|
+
expect(screen.getByText(/no replication rules found/i)).toBeInTheDocument();
|
|
207
|
+
});
|
|
208
|
+
it("handles replication rules without IDs", ()=>{
|
|
209
|
+
const ruleWithoutId = {
|
|
210
|
+
Status: "Enabled",
|
|
211
|
+
Priority: 1,
|
|
212
|
+
Destination: {
|
|
213
|
+
Bucket: "arn:aws:s3:::test-bucket"
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
renderBucketReplicationList({
|
|
217
|
+
replicationRules: [
|
|
218
|
+
ruleWithoutId
|
|
219
|
+
]
|
|
220
|
+
});
|
|
221
|
+
expect(screen.getByText("-")).toBeInTheDocument();
|
|
222
|
+
});
|
|
223
|
+
it("shows loading state when replicationStatus is loading", ()=>{
|
|
224
|
+
renderBucketReplicationList({
|
|
225
|
+
replicationRules: [],
|
|
226
|
+
replicationStatus: "loading"
|
|
227
|
+
});
|
|
228
|
+
expect(screen.getByRole("grid")).toBeInTheDocument();
|
|
229
|
+
expect(screen.queryByText("rule-1")).not.toBeInTheDocument();
|
|
230
|
+
});
|
|
231
|
+
it("shows error state when replicationStatus is error", ()=>{
|
|
232
|
+
renderBucketReplicationList({
|
|
233
|
+
replicationRules: [],
|
|
234
|
+
replicationStatus: "error"
|
|
235
|
+
});
|
|
236
|
+
expect(screen.getByRole("grid")).toBeInTheDocument();
|
|
237
|
+
expect(screen.queryByText("rule-1")).not.toBeInTheDocument();
|
|
238
|
+
});
|
|
239
|
+
it("shows data when replicationStatus is success", ()=>{
|
|
240
|
+
renderBucketReplicationList({
|
|
241
|
+
replicationRules: mockReplicationRules,
|
|
242
|
+
replicationStatus: "success"
|
|
243
|
+
});
|
|
244
|
+
expect(screen.getByRole("grid")).toBeInTheDocument();
|
|
245
|
+
expect(screen.getByText("rule-1")).toBeInTheDocument();
|
|
246
|
+
expect(screen.getByText("rule-2")).toBeInTheDocument();
|
|
247
|
+
});
|
|
248
|
+
it("works when no callbacks are provided", ()=>{
|
|
249
|
+
expect(()=>{
|
|
250
|
+
renderBucketReplicationList({
|
|
251
|
+
replicationRules: mockReplicationRules
|
|
252
|
+
});
|
|
253
|
+
}).not.toThrow();
|
|
254
|
+
});
|
|
255
|
+
it("renders rows that can be selected", ()=>{
|
|
256
|
+
renderBucketReplicationList({
|
|
257
|
+
replicationRules: mockReplicationRules
|
|
258
|
+
});
|
|
259
|
+
const rows = screen.getAllByRole("row");
|
|
260
|
+
expect(rows.length).toBeGreaterThan(3);
|
|
261
|
+
const dataRows = rows.slice(1);
|
|
262
|
+
dataRows.forEach((row)=>{
|
|
263
|
+
expect(row).toHaveAttribute("aria-selected");
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
it("displays complex cross-account replication with all details", ()=>{
|
|
267
|
+
const complexRule = {
|
|
268
|
+
ID: "complex-rule",
|
|
269
|
+
Status: "Enabled",
|
|
270
|
+
Priority: 25,
|
|
271
|
+
Destination: {
|
|
272
|
+
Bucket: "arn:aws:s3:::999888777666555:shared-bucket",
|
|
273
|
+
Account: "999888777666555",
|
|
274
|
+
StorageClass: "GLACIER_IR"
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
renderBucketReplicationList({
|
|
278
|
+
replicationRules: [
|
|
279
|
+
complexRule
|
|
280
|
+
]
|
|
281
|
+
});
|
|
282
|
+
expect(screen.getByText(/account: 999\.\.\.555/)).toBeInTheDocument();
|
|
283
|
+
expect(screen.getByText(/GLACIER_IR/)).toBeInTheDocument();
|
|
284
|
+
expect(screen.getByText(/Replicate to shared-bucket \(account: 999\.\.\.555, GLACIER_IR\)/)).toBeInTheDocument();
|
|
285
|
+
});
|
|
286
|
+
it("displays action text without account ID for same-account replication", ()=>{
|
|
287
|
+
renderBucketReplicationList({
|
|
288
|
+
replicationRules: [
|
|
289
|
+
mockReplicationRules[0]
|
|
290
|
+
]
|
|
291
|
+
});
|
|
292
|
+
const actionText = screen.getByText(/Replicate to destination-bucket-1/);
|
|
293
|
+
expect(actionText.textContent).not.toContain("account:");
|
|
294
|
+
});
|
|
295
|
+
it("renders with custom bucket name and replication role", ()=>{
|
|
296
|
+
renderBucketReplicationList({
|
|
297
|
+
replicationRules: mockReplicationRules,
|
|
298
|
+
bucketName: "custom-bucket",
|
|
299
|
+
replicationRole: "arn:aws:iam::999888777666:role/custom-role"
|
|
300
|
+
});
|
|
301
|
+
expect(screen.getByText("rule-1")).toBeInTheDocument();
|
|
302
|
+
expect(screen.getByText("rule-2")).toBeInTheDocument();
|
|
303
|
+
expect(screen.getByText("rule-3")).toBeInTheDocument();
|
|
304
|
+
});
|
|
305
|
+
it("sorts rules by priority from low to high by default", ()=>{
|
|
306
|
+
const unsortedRules = [
|
|
307
|
+
{
|
|
308
|
+
ID: "high-priority-rule",
|
|
309
|
+
Status: "Enabled",
|
|
310
|
+
Priority: 100,
|
|
311
|
+
Destination: {
|
|
312
|
+
Bucket: "arn:aws:s3:::bucket-high"
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
ID: "low-priority-rule",
|
|
317
|
+
Status: "Enabled",
|
|
318
|
+
Priority: 1,
|
|
319
|
+
Destination: {
|
|
320
|
+
Bucket: "arn:aws:s3:::bucket-low"
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
ID: "mid-priority-rule",
|
|
325
|
+
Status: "Enabled",
|
|
326
|
+
Priority: 50,
|
|
327
|
+
Destination: {
|
|
328
|
+
Bucket: "arn:aws:s3:::bucket-mid"
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
];
|
|
332
|
+
renderBucketReplicationList({
|
|
333
|
+
replicationRules: unsortedRules
|
|
334
|
+
});
|
|
335
|
+
const rows = screen.getAllByRole("row");
|
|
336
|
+
const dataRows = rows.slice(1);
|
|
337
|
+
expect(dataRows[0]).toHaveTextContent("low-priority-rule");
|
|
338
|
+
expect(dataRows[0]).toHaveTextContent("1");
|
|
339
|
+
expect(dataRows[1]).toHaveTextContent("mid-priority-rule");
|
|
340
|
+
expect(dataRows[1]).toHaveTextContent("50");
|
|
341
|
+
expect(dataRows[2]).toHaveTextContent("high-priority-rule");
|
|
342
|
+
expect(dataRows[2]).toHaveTextContent("100");
|
|
343
|
+
});
|
|
344
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import { MemoryRouter } from "react-router-dom";
|
|
4
|
+
import { createTestWrapper } from "../../test/testUtils.js";
|
|
5
|
+
import { DeleteBucketConfigRuleButton } from "../buckets/DeleteBucketConfigRuleButton.js";
|
|
6
|
+
const mockLifecycleRules = [
|
|
7
|
+
{
|
|
8
|
+
ID: "rule-1",
|
|
9
|
+
Status: "Enabled",
|
|
10
|
+
Expiration: {
|
|
11
|
+
Days: 30
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
ID: "rule-2",
|
|
16
|
+
Status: "Disabled",
|
|
17
|
+
NoncurrentVersionExpiration: {
|
|
18
|
+
NoncurrentDays: 90
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
];
|
|
22
|
+
describe("DeleteBucketConfigRuleButton", ()=>{
|
|
23
|
+
let mockUpdate;
|
|
24
|
+
let mockDelete;
|
|
25
|
+
const renderButton = (props = {})=>{
|
|
26
|
+
const defaultProps = {
|
|
27
|
+
bucketName: "test-bucket",
|
|
28
|
+
ruleId: "rule-1",
|
|
29
|
+
rules: mockLifecycleRules,
|
|
30
|
+
ruleType: "lifecycle",
|
|
31
|
+
updateMutation: {
|
|
32
|
+
mutate: mockUpdate,
|
|
33
|
+
status: "idle"
|
|
34
|
+
},
|
|
35
|
+
deleteMutation: {
|
|
36
|
+
mutate: mockDelete,
|
|
37
|
+
status: "idle"
|
|
38
|
+
},
|
|
39
|
+
buildUpdateInput: (remainingRules)=>({
|
|
40
|
+
LifecycleConfiguration: {
|
|
41
|
+
Rules: remainingRules
|
|
42
|
+
}
|
|
43
|
+
}),
|
|
44
|
+
invalidationKeys: [
|
|
45
|
+
"GetBucketLifecycle"
|
|
46
|
+
],
|
|
47
|
+
successMessage: "Rule deleted successfully",
|
|
48
|
+
errorMessage: "Failed to delete rule"
|
|
49
|
+
};
|
|
50
|
+
const Wrapper = createTestWrapper();
|
|
51
|
+
return render(/*#__PURE__*/ jsx(MemoryRouter, {
|
|
52
|
+
children: /*#__PURE__*/ jsx(Wrapper, {
|
|
53
|
+
children: /*#__PURE__*/ jsx(DeleteBucketConfigRuleButton, {
|
|
54
|
+
...defaultProps,
|
|
55
|
+
...props
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
}));
|
|
59
|
+
};
|
|
60
|
+
beforeEach(()=>{
|
|
61
|
+
jest.clearAllMocks();
|
|
62
|
+
mockUpdate = jest.fn();
|
|
63
|
+
mockDelete = jest.fn();
|
|
64
|
+
});
|
|
65
|
+
it("renders delete button", ()=>{
|
|
66
|
+
renderButton();
|
|
67
|
+
const deleteButton = screen.getByRole("button", {
|
|
68
|
+
name: /delete rule/i
|
|
69
|
+
});
|
|
70
|
+
expect(deleteButton).toBeInTheDocument();
|
|
71
|
+
});
|
|
72
|
+
it("opens confirmation modal when clicked", ()=>{
|
|
73
|
+
renderButton();
|
|
74
|
+
const deleteButton = screen.getByRole("button", {
|
|
75
|
+
name: /delete rule/i
|
|
76
|
+
});
|
|
77
|
+
fireEvent.click(deleteButton);
|
|
78
|
+
expect(screen.getByText(/are you sure you want to delete the lifecycle rule/i)).toBeInTheDocument();
|
|
79
|
+
expect(screen.getByText(/rule-1/)).toBeInTheDocument();
|
|
80
|
+
});
|
|
81
|
+
it("closes modal when cancel is clicked", ()=>{
|
|
82
|
+
renderButton();
|
|
83
|
+
fireEvent.click(screen.getByRole("button", {
|
|
84
|
+
name: /delete rule/i
|
|
85
|
+
}));
|
|
86
|
+
expect(screen.getByText(/are you sure/i)).toBeInTheDocument();
|
|
87
|
+
fireEvent.click(screen.getByRole("button", {
|
|
88
|
+
name: /cancel/i
|
|
89
|
+
}));
|
|
90
|
+
expect(screen.queryByText(/are you sure/i)).not.toBeInTheDocument();
|
|
91
|
+
});
|
|
92
|
+
it("calls update when deleting a rule with remaining rules", async ()=>{
|
|
93
|
+
renderButton();
|
|
94
|
+
fireEvent.click(screen.getByRole("button", {
|
|
95
|
+
name: /delete rule/i
|
|
96
|
+
}));
|
|
97
|
+
fireEvent.click(screen.getByRole("button", {
|
|
98
|
+
name: /^delete$/i
|
|
99
|
+
}));
|
|
100
|
+
await waitFor(()=>{
|
|
101
|
+
expect(mockUpdate).toHaveBeenCalled();
|
|
102
|
+
const callArgs = mockUpdate.mock.calls[0][0];
|
|
103
|
+
expect(callArgs.Bucket).toBe("test-bucket");
|
|
104
|
+
expect(callArgs.LifecycleConfiguration.Rules).toHaveLength(1);
|
|
105
|
+
expect(callArgs.LifecycleConfiguration.Rules[0].ID).toBe("rule-2");
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
it("calls delete when deleting the last rule", async ()=>{
|
|
109
|
+
renderButton({
|
|
110
|
+
rules: [
|
|
111
|
+
mockLifecycleRules[0]
|
|
112
|
+
]
|
|
113
|
+
});
|
|
114
|
+
fireEvent.click(screen.getByRole("button", {
|
|
115
|
+
name: /delete rule/i
|
|
116
|
+
}));
|
|
117
|
+
fireEvent.click(screen.getByRole("button", {
|
|
118
|
+
name: /^delete$/i
|
|
119
|
+
}));
|
|
120
|
+
await waitFor(()=>{
|
|
121
|
+
expect(mockDelete).toHaveBeenCalledWith({
|
|
122
|
+
Bucket: "test-bucket"
|
|
123
|
+
}, expect.any(Object));
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
it("closes modal after successful deletion", async ()=>{
|
|
127
|
+
mockUpdate.mockImplementation((_, options)=>{
|
|
128
|
+
options.onSuccess();
|
|
129
|
+
});
|
|
130
|
+
renderButton();
|
|
131
|
+
fireEvent.click(screen.getByRole("button", {
|
|
132
|
+
name: /delete rule/i
|
|
133
|
+
}));
|
|
134
|
+
fireEvent.click(screen.getByRole("button", {
|
|
135
|
+
name: /^delete$/i
|
|
136
|
+
}));
|
|
137
|
+
await waitFor(()=>{
|
|
138
|
+
expect(screen.queryByText(/are you sure/i)).not.toBeInTheDocument();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
it("closes modal after failed deletion", async ()=>{
|
|
142
|
+
const consoleError = jest.spyOn(console, "error").mockImplementation(()=>{});
|
|
143
|
+
mockUpdate.mockImplementation((_, options)=>{
|
|
144
|
+
options.onError(new Error("Delete failed"));
|
|
145
|
+
});
|
|
146
|
+
renderButton();
|
|
147
|
+
fireEvent.click(screen.getByRole("button", {
|
|
148
|
+
name: /delete rule/i
|
|
149
|
+
}));
|
|
150
|
+
fireEvent.click(screen.getByRole("button", {
|
|
151
|
+
name: /^delete$/i
|
|
152
|
+
}));
|
|
153
|
+
await waitFor(()=>{
|
|
154
|
+
expect(mockUpdate).toHaveBeenCalled();
|
|
155
|
+
});
|
|
156
|
+
await waitFor(()=>{
|
|
157
|
+
expect(screen.queryByText(/are you sure/i)).not.toBeInTheDocument();
|
|
158
|
+
});
|
|
159
|
+
consoleError.mockRestore();
|
|
160
|
+
});
|
|
161
|
+
it("disables buttons while deletion is in progress", ()=>{
|
|
162
|
+
renderButton({
|
|
163
|
+
updateMutation: {
|
|
164
|
+
mutate: mockUpdate,
|
|
165
|
+
status: "pending"
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
fireEvent.click(screen.getByRole("button", {
|
|
169
|
+
name: /delete rule/i
|
|
170
|
+
}));
|
|
171
|
+
const cancelButton = screen.getByRole("button", {
|
|
172
|
+
name: /cancel/i
|
|
173
|
+
});
|
|
174
|
+
const confirmButton = screen.getByRole("button", {
|
|
175
|
+
name: /^delete$/i
|
|
176
|
+
});
|
|
177
|
+
expect(cancelButton).toBeDisabled();
|
|
178
|
+
expect(confirmButton).toBeDisabled();
|
|
179
|
+
});
|
|
180
|
+
it("stops event propagation when delete button is clicked", ()=>{
|
|
181
|
+
renderButton();
|
|
182
|
+
const deleteButton = screen.getByRole("button", {
|
|
183
|
+
name: /delete rule/i
|
|
184
|
+
});
|
|
185
|
+
const mockStopPropagation = jest.fn();
|
|
186
|
+
const clickEvent = new MouseEvent("click", {
|
|
187
|
+
bubbles: true
|
|
188
|
+
});
|
|
189
|
+
Object.defineProperty(clickEvent, "stopPropagation", {
|
|
190
|
+
value: mockStopPropagation,
|
|
191
|
+
writable: true
|
|
192
|
+
});
|
|
193
|
+
deleteButton.dispatchEvent(clickEvent);
|
|
194
|
+
expect(mockStopPropagation).toHaveBeenCalled();
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|