@scality/data-browser-library 1.0.4 → 1.0.6
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.js +18 -8
- package/dist/components/__tests__/BucketCreate.test.js +60 -20
- package/dist/components/__tests__/BucketList.test.js +91 -6
- package/dist/components/__tests__/BucketNotificationFormPage.test.js +54 -19
- package/dist/components/__tests__/BucketReplicationFormPage.test.js +183 -61
- package/dist/components/__tests__/MetadataSearch.test.js +18 -12
- package/dist/components/__tests__/ObjectList.test.js +94 -2
- package/dist/components/buckets/BucketCreate.d.ts +1 -0
- package/dist/components/buckets/BucketCreate.js +57 -7
- package/dist/components/buckets/BucketDetails.js +0 -1
- package/dist/components/buckets/BucketLifecycleFormPage.js +209 -213
- package/dist/components/buckets/BucketList.js +25 -4
- package/dist/components/buckets/BucketReplicationFormPage.js +9 -3
- package/dist/components/buckets/DeleteBucketConfigRuleButton.js +1 -1
- package/dist/components/buckets/notifications/BucketNotificationList.js +1 -1
- package/dist/components/objects/DeleteObjectButton.d.ts +1 -0
- package/dist/components/objects/DeleteObjectButton.js +11 -5
- package/dist/components/objects/ObjectDetails/FormComponents.d.ts +9 -0
- package/dist/components/objects/ObjectDetails/FormComponents.js +37 -0
- package/dist/components/objects/ObjectDetails/ObjectMetadata.js +182 -204
- package/dist/components/objects/ObjectDetails/ObjectSummary.js +22 -5
- package/dist/components/objects/ObjectDetails/ObjectTags.js +109 -154
- package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.js +3 -3
- package/dist/components/objects/ObjectDetails/__tests__/ObjectMetadata.test.d.ts +1 -0
- package/dist/components/objects/ObjectDetails/__tests__/ObjectMetadata.test.js +230 -0
- package/dist/components/objects/ObjectDetails/__tests__/ObjectTags.test.d.ts +1 -0
- package/dist/components/objects/ObjectDetails/__tests__/ObjectTags.test.js +342 -0
- package/dist/components/objects/ObjectDetails/__tests__/formUtils.test.d.ts +1 -0
- package/dist/components/objects/ObjectDetails/__tests__/formUtils.test.js +202 -0
- package/dist/components/objects/ObjectDetails/index.d.ts +2 -1
- package/dist/components/objects/ObjectDetails/index.js +12 -16
- package/dist/components/objects/ObjectList.d.ts +3 -2
- package/dist/components/objects/ObjectList.js +204 -104
- package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.js +78 -26
- package/dist/components/objects/ObjectPage.js +22 -5
- package/dist/components/ui/ArrayFieldActions.js +0 -2
- package/dist/components/ui/FilterFormSection.js +17 -36
- package/dist/components/ui/FormGrid.d.ts +7 -0
- package/dist/components/ui/FormGrid.js +37 -0
- package/dist/components/ui/Table.elements.js +1 -0
- package/dist/config/__tests__/factory.test.js +29 -1
- package/dist/config/factory.d.ts +2 -0
- package/dist/config/factory.js +3 -1
- package/dist/config/types.d.ts +45 -2
- package/dist/hooks/__tests__/usePresigningS3Client.test.d.ts +1 -0
- package/dist/hooks/__tests__/usePresigningS3Client.test.js +104 -0
- package/dist/hooks/factories/index.d.ts +1 -1
- package/dist/hooks/factories/index.js +2 -2
- package/dist/hooks/factories/useCreateS3MutationHook.d.ts +2 -1
- package/dist/hooks/factories/useCreateS3MutationHook.js +10 -3
- package/dist/hooks/factories/useCreateS3QueryHook.d.ts +1 -0
- package/dist/hooks/factories/useCreateS3QueryHook.js +9 -6
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +2 -1
- package/dist/hooks/presignedOperations.js +4 -4
- package/dist/hooks/useBucketLocations.d.ts +6 -0
- package/dist/hooks/useBucketLocations.js +45 -0
- package/dist/hooks/usePresigningS3Client.d.ts +13 -0
- package/dist/hooks/usePresigningS3Client.js +21 -0
- package/package.json +4 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { AppContainer } from "@scality/core-ui";
|
|
2
|
+
import { AppContainer, ScrollbarWrapper } from "@scality/core-ui";
|
|
3
3
|
import { Route, Routes } from "react-router";
|
|
4
4
|
import styled_components from "styled-components";
|
|
5
5
|
import { DataBrowserUICustomizationProvider } from "../contexts/DataBrowserUICustomizationContext.js";
|
|
@@ -12,14 +12,22 @@ const RoutesContainer = styled_components.div`
|
|
|
12
12
|
width: 100%;
|
|
13
13
|
overflow: hidden;
|
|
14
14
|
`;
|
|
15
|
+
const HeaderSlot = styled_components.div`
|
|
16
|
+
&:has([data-header-content]:empty) {
|
|
17
|
+
display: none;
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
15
20
|
const DataBrowserUIContent = ({ header })=>{
|
|
16
21
|
useS3ConfigSwitch();
|
|
17
22
|
return /*#__PURE__*/ jsx(AppContainer, {
|
|
18
23
|
children: /*#__PURE__*/ jsxs(Fragment, {
|
|
19
24
|
children: [
|
|
20
|
-
header && /*#__PURE__*/ jsx(
|
|
21
|
-
children: /*#__PURE__*/ jsx(
|
|
22
|
-
children:
|
|
25
|
+
header && /*#__PURE__*/ jsx(HeaderSlot, {
|
|
26
|
+
children: /*#__PURE__*/ jsx(AppContainer.ContextContainer, {
|
|
27
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
28
|
+
"data-header-content": true,
|
|
29
|
+
children: header
|
|
30
|
+
})
|
|
23
31
|
})
|
|
24
32
|
}),
|
|
25
33
|
/*#__PURE__*/ jsx(AppContainer.MainContent, {
|
|
@@ -90,10 +98,12 @@ const DataBrowserUIContent = ({ header })=>{
|
|
|
90
98
|
})
|
|
91
99
|
});
|
|
92
100
|
};
|
|
93
|
-
const DataBrowserUI = (props)=>/*#__PURE__*/ jsx(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
const DataBrowserUI = (props)=>/*#__PURE__*/ jsx(ScrollbarWrapper, {
|
|
102
|
+
children: /*#__PURE__*/ jsx(DataBrowserUICustomizationProvider, {
|
|
103
|
+
config: props,
|
|
104
|
+
children: /*#__PURE__*/ jsx(DataBrowserUIContent, {
|
|
105
|
+
header: props.header
|
|
106
|
+
})
|
|
97
107
|
})
|
|
98
108
|
});
|
|
99
109
|
export { DataBrowserUI };
|
|
@@ -76,7 +76,9 @@ const setupMocks = (overrides = {})=>{
|
|
|
76
76
|
mockUseSetBucketEncryption.mockReturnValue(createMockMutationResult(mockMutations.setBucketEncryption));
|
|
77
77
|
};
|
|
78
78
|
const fillBucketName = async (name)=>{
|
|
79
|
-
const nameInput = screen.
|
|
79
|
+
const nameInput = screen.getByRole('textbox', {
|
|
80
|
+
name: /bucket name/i
|
|
81
|
+
});
|
|
80
82
|
await user_event.type(nameInput, name);
|
|
81
83
|
};
|
|
82
84
|
const submitForm = async ()=>{
|
|
@@ -101,13 +103,21 @@ describe('BucketCreate', ()=>{
|
|
|
101
103
|
it('renders form with required fields', ()=>{
|
|
102
104
|
renderBucketCreate();
|
|
103
105
|
expect(screen.getByText('Create a New Bucket')).toBeInTheDocument();
|
|
104
|
-
expect(screen.
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
expect(screen.getByRole('textbox', {
|
|
107
|
+
name: /bucket name/i
|
|
108
|
+
})).toBeInTheDocument();
|
|
109
|
+
expect(screen.getByRole('checkbox', {
|
|
110
|
+
name: /versioning/i
|
|
111
|
+
})).toBeInTheDocument();
|
|
112
|
+
expect(screen.getByRole('checkbox', {
|
|
113
|
+
name: /object-lock/i
|
|
114
|
+
})).toBeInTheDocument();
|
|
107
115
|
});
|
|
108
116
|
it('shows validation error for invalid bucket name with uppercase', async ()=>{
|
|
109
117
|
renderBucketCreate();
|
|
110
|
-
await user_event.type(screen.
|
|
118
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
119
|
+
name: /bucket name/i
|
|
120
|
+
}), 'INVALID');
|
|
111
121
|
await user_event.tab();
|
|
112
122
|
await waitFor(()=>{
|
|
113
123
|
expect(screen.getByText(/bucket names can include only lowercase/i)).toBeInTheDocument();
|
|
@@ -115,7 +125,9 @@ describe('BucketCreate', ()=>{
|
|
|
115
125
|
});
|
|
116
126
|
it('shows validation error for bucket name starting with hyphen', async ()=>{
|
|
117
127
|
renderBucketCreate();
|
|
118
|
-
await user_event.type(screen.
|
|
128
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
129
|
+
name: /bucket name/i
|
|
130
|
+
}), '-invalid');
|
|
119
131
|
await user_event.tab();
|
|
120
132
|
await waitFor(()=>{
|
|
121
133
|
expect(screen.getByText(/bucket names can include only lowercase/i)).toBeInTheDocument();
|
|
@@ -123,7 +135,9 @@ describe('BucketCreate', ()=>{
|
|
|
123
135
|
});
|
|
124
136
|
it('shows validation error for bucket name ending with hyphen', async ()=>{
|
|
125
137
|
renderBucketCreate();
|
|
126
|
-
await user_event.type(screen.
|
|
138
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
139
|
+
name: /bucket name/i
|
|
140
|
+
}), 'invalid-');
|
|
127
141
|
await user_event.tab();
|
|
128
142
|
await waitFor(()=>{
|
|
129
143
|
expect(screen.getByText(/bucket names can include only lowercase/i)).toBeInTheDocument();
|
|
@@ -131,7 +145,9 @@ describe('BucketCreate', ()=>{
|
|
|
131
145
|
});
|
|
132
146
|
it('shows validation error for bucket name with adjacent periods', async ()=>{
|
|
133
147
|
renderBucketCreate();
|
|
134
|
-
await user_event.type(screen.
|
|
148
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
149
|
+
name: /bucket name/i
|
|
150
|
+
}), 'test..bucket');
|
|
135
151
|
await user_event.tab();
|
|
136
152
|
await waitFor(()=>{
|
|
137
153
|
expect(screen.getByText(/bucket names cannot contain two adjacent periods/i)).toBeInTheDocument();
|
|
@@ -139,7 +155,9 @@ describe('BucketCreate', ()=>{
|
|
|
139
155
|
});
|
|
140
156
|
it('shows validation error for bucket name formatted as IP address', async ()=>{
|
|
141
157
|
renderBucketCreate();
|
|
142
|
-
await user_event.type(screen.
|
|
158
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
159
|
+
name: /bucket name/i
|
|
160
|
+
}), '192.168.1.1');
|
|
143
161
|
await user_event.tab();
|
|
144
162
|
await waitFor(()=>{
|
|
145
163
|
expect(screen.getByText(/bucket names must not be formatted as an ip address/i)).toBeInTheDocument();
|
|
@@ -147,7 +165,9 @@ describe('BucketCreate', ()=>{
|
|
|
147
165
|
});
|
|
148
166
|
it('shows validation error for bucket name with forbidden prefix', async ()=>{
|
|
149
167
|
renderBucketCreate();
|
|
150
|
-
await user_event.type(screen.
|
|
168
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
169
|
+
name: /bucket name/i
|
|
170
|
+
}), 'xn--test');
|
|
151
171
|
await user_event.tab();
|
|
152
172
|
await waitFor(()=>{
|
|
153
173
|
expect(screen.getByText(/bucket names must not start with/i)).toBeInTheDocument();
|
|
@@ -155,7 +175,9 @@ describe('BucketCreate', ()=>{
|
|
|
155
175
|
});
|
|
156
176
|
it('shows validation error for bucket name with forbidden suffix', async ()=>{
|
|
157
177
|
renderBucketCreate();
|
|
158
|
-
await user_event.type(screen.
|
|
178
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
179
|
+
name: /bucket name/i
|
|
180
|
+
}), 'test-s3alias');
|
|
159
181
|
await user_event.tab();
|
|
160
182
|
await waitFor(()=>{
|
|
161
183
|
expect(screen.getByText(/bucket names must not end with/i)).toBeInTheDocument();
|
|
@@ -170,7 +192,9 @@ describe('BucketCreate', ()=>{
|
|
|
170
192
|
]
|
|
171
193
|
}
|
|
172
194
|
});
|
|
173
|
-
await user_event.type(screen.
|
|
195
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
196
|
+
name: /bucket name/i
|
|
197
|
+
}), 'existing-bucket');
|
|
174
198
|
await user_event.tab();
|
|
175
199
|
await waitFor(()=>{
|
|
176
200
|
expect(screen.getByText(/a bucket with this name already exists/i)).toBeInTheDocument();
|
|
@@ -185,7 +209,9 @@ describe('BucketCreate', ()=>{
|
|
|
185
209
|
'123-bucket-456'
|
|
186
210
|
];
|
|
187
211
|
for (const name of validNames){
|
|
188
|
-
const input = screen.
|
|
212
|
+
const input = screen.getByRole('textbox', {
|
|
213
|
+
name: /bucket name/i
|
|
214
|
+
});
|
|
189
215
|
await user_event.clear(input);
|
|
190
216
|
await user_event.type(input, name);
|
|
191
217
|
await user_event.tab();
|
|
@@ -469,7 +495,9 @@ describe('BucketCreate', ()=>{
|
|
|
469
495
|
});
|
|
470
496
|
renderBucketCreate();
|
|
471
497
|
await fillBucketName('test-bucket');
|
|
472
|
-
const encryptionCheckbox = screen.
|
|
498
|
+
const encryptionCheckbox = screen.getByRole('checkbox', {
|
|
499
|
+
name: /encryption/i
|
|
500
|
+
});
|
|
473
501
|
await user_event.click(encryptionCheckbox);
|
|
474
502
|
await submitForm();
|
|
475
503
|
const createSuccessCallback = mockMutations.createBucket.mock.calls[0][1].onSuccess;
|
|
@@ -516,8 +544,12 @@ describe('BucketCreate', ()=>{
|
|
|
516
544
|
});
|
|
517
545
|
renderBucketCreate();
|
|
518
546
|
await fillBucketName('test-bucket');
|
|
519
|
-
await user_event.click(screen.
|
|
520
|
-
|
|
547
|
+
await user_event.click(screen.getByRole('checkbox', {
|
|
548
|
+
name: /versioning/i
|
|
549
|
+
}));
|
|
550
|
+
await user_event.click(screen.getByRole('checkbox', {
|
|
551
|
+
name: /encryption/i
|
|
552
|
+
}));
|
|
521
553
|
await submitForm();
|
|
522
554
|
const createSuccessCallback = mockMutations.createBucket.mock.calls[0][1].onSuccess;
|
|
523
555
|
createSuccessCallback();
|
|
@@ -536,9 +568,15 @@ describe('BucketCreate', ()=>{
|
|
|
536
568
|
});
|
|
537
569
|
renderBucketCreate();
|
|
538
570
|
await fillBucketName('test-bucket');
|
|
539
|
-
await user_event.click(screen.
|
|
540
|
-
|
|
541
|
-
|
|
571
|
+
await user_event.click(screen.getByRole('checkbox', {
|
|
572
|
+
name: /object-lock/i
|
|
573
|
+
}));
|
|
574
|
+
await user_event.click(screen.getByRole('checkbox', {
|
|
575
|
+
name: /default retention/i
|
|
576
|
+
}));
|
|
577
|
+
await user_event.click(screen.getByRole('checkbox', {
|
|
578
|
+
name: /encryption/i
|
|
579
|
+
}));
|
|
542
580
|
await submitForm();
|
|
543
581
|
const createSuccessCallback = mockMutations.createBucket.mock.calls[0][1].onSuccess;
|
|
544
582
|
createSuccessCallback();
|
|
@@ -557,7 +595,9 @@ describe('BucketCreate', ()=>{
|
|
|
557
595
|
});
|
|
558
596
|
renderBucketCreate();
|
|
559
597
|
await fillBucketName('test-bucket');
|
|
560
|
-
await user_event.click(screen.
|
|
598
|
+
await user_event.click(screen.getByRole('checkbox', {
|
|
599
|
+
name: /encryption/i
|
|
600
|
+
}));
|
|
561
601
|
await submitForm();
|
|
562
602
|
const createSuccessCallback = mockMutations.createBucket.mock.calls[0][1].onSuccess;
|
|
563
603
|
createSuccessCallback();
|
|
@@ -3,6 +3,7 @@ import { fireEvent, render, screen } from "@testing-library/react";
|
|
|
3
3
|
import { MemoryRouter } from "react-router";
|
|
4
4
|
import { createTestWrapper, mockOffsetSize } from "../../test/testUtils.js";
|
|
5
5
|
import { BucketList } from "../buckets/BucketList.js";
|
|
6
|
+
import * as __rspack_external__hooks_index_js_a0d26731 from "../../hooks/index.js";
|
|
6
7
|
import * as __rspack_external__contexts_DataBrowserUICustomizationContext_js_f267b01c from "../../contexts/DataBrowserUICustomizationContext.js";
|
|
7
8
|
const mockUseDataBrowserUICustomization = (config = {})=>{
|
|
8
9
|
jest.spyOn(__rspack_external__contexts_DataBrowserUICustomizationContext_js_f267b01c, 'useDataBrowserUICustomization').mockReturnValue(config);
|
|
@@ -37,15 +38,22 @@ describe('BucketList', ()=>{
|
|
|
37
38
|
jest.clearAllMocks();
|
|
38
39
|
mockOffsetSize(800, 600);
|
|
39
40
|
mockUseDataBrowserUICustomization({});
|
|
41
|
+
jest.spyOn(__rspack_external__hooks_index_js_a0d26731, 'useBucketLocations').mockReturnValue(new Map());
|
|
40
42
|
});
|
|
41
43
|
it('shows a table with proper headers', ()=>{
|
|
42
44
|
renderBucketList({
|
|
43
45
|
buckets: mockBuckets
|
|
44
46
|
});
|
|
45
47
|
expect(screen.getByRole('grid')).toBeInTheDocument();
|
|
46
|
-
expect(screen.
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
expect(screen.getByRole('columnheader', {
|
|
49
|
+
name: /bucket name/i
|
|
50
|
+
})).toBeInTheDocument();
|
|
51
|
+
expect(screen.getByRole('columnheader', {
|
|
52
|
+
name: /storage location/i
|
|
53
|
+
})).toBeInTheDocument();
|
|
54
|
+
expect(screen.getByRole('columnheader', {
|
|
55
|
+
name: /created on/i
|
|
56
|
+
})).toBeInTheDocument();
|
|
49
57
|
});
|
|
50
58
|
it('displays buckets with their names as clickable links', ()=>{
|
|
51
59
|
const onNavigateToBucket = jest.fn();
|
|
@@ -76,7 +84,7 @@ describe('BucketList', ()=>{
|
|
|
76
84
|
});
|
|
77
85
|
const rows = screen.getAllByRole('row');
|
|
78
86
|
fireEvent.click(rows[1]);
|
|
79
|
-
expect(onBucketSelect).toHaveBeenCalledWith('test-bucket-
|
|
87
|
+
expect(onBucketSelect).toHaveBeenCalledWith('test-bucket-1');
|
|
80
88
|
});
|
|
81
89
|
it('shows selected bucket when selectedBucketName is provided', ()=>{
|
|
82
90
|
renderBucketList({
|
|
@@ -107,14 +115,16 @@ describe('BucketList', ()=>{
|
|
|
107
115
|
});
|
|
108
116
|
const searchInput = screen.getByRole('searchbox');
|
|
109
117
|
expect(searchInput).toBeInTheDocument();
|
|
110
|
-
expect(searchInput).toHaveAttribute('placeholder', '
|
|
118
|
+
expect(searchInput).toHaveAttribute('placeholder', 'Search');
|
|
111
119
|
});
|
|
112
120
|
it('handles empty buckets list', ()=>{
|
|
113
121
|
renderBucketList({
|
|
114
122
|
buckets: []
|
|
115
123
|
});
|
|
116
124
|
expect(screen.getByRole('grid')).toBeInTheDocument();
|
|
117
|
-
expect(screen.
|
|
125
|
+
expect(screen.getByRole('columnheader', {
|
|
126
|
+
name: /bucket name/i
|
|
127
|
+
})).toBeInTheDocument();
|
|
118
128
|
});
|
|
119
129
|
it('handles buckets without names gracefully', ()=>{
|
|
120
130
|
const bucketsWithMissingNames = [
|
|
@@ -205,6 +215,81 @@ describe('BucketList', ()=>{
|
|
|
205
215
|
fireEvent.click(bucketName.closest('[role="row"]') || bucketName);
|
|
206
216
|
expect(onBucketSelect).not.toHaveBeenCalled();
|
|
207
217
|
});
|
|
218
|
+
it('sorts by creation date by default, not by bucket name', ()=>{
|
|
219
|
+
const bucketsWithReversedDateOrder = [
|
|
220
|
+
{
|
|
221
|
+
Name: 'alpha-bucket',
|
|
222
|
+
CreationDate: new Date('2024-03-01T00:00:00Z')
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
Name: 'beta-bucket',
|
|
226
|
+
CreationDate: new Date('2024-01-01T00:00:00Z')
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
Name: 'gamma-bucket',
|
|
230
|
+
CreationDate: new Date('2024-02-01T00:00:00Z')
|
|
231
|
+
}
|
|
232
|
+
];
|
|
233
|
+
renderBucketList({
|
|
234
|
+
buckets: bucketsWithReversedDateOrder
|
|
235
|
+
});
|
|
236
|
+
const rows = screen.getAllByRole('row');
|
|
237
|
+
expect(rows[1]).toHaveTextContent('beta-bucket');
|
|
238
|
+
expect(rows[2]).toHaveTextContent('gamma-bucket');
|
|
239
|
+
expect(rows[3]).toHaveTextContent('alpha-bucket');
|
|
240
|
+
});
|
|
241
|
+
it('toggles to date descending when Created on header is clicked', ()=>{
|
|
242
|
+
const bucketsWithReversedDateOrder = [
|
|
243
|
+
{
|
|
244
|
+
Name: 'alpha-bucket',
|
|
245
|
+
CreationDate: new Date('2024-03-01T00:00:00Z')
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
Name: 'beta-bucket',
|
|
249
|
+
CreationDate: new Date('2024-01-01T00:00:00Z')
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
Name: 'gamma-bucket',
|
|
253
|
+
CreationDate: new Date('2024-02-01T00:00:00Z')
|
|
254
|
+
}
|
|
255
|
+
];
|
|
256
|
+
renderBucketList({
|
|
257
|
+
buckets: bucketsWithReversedDateOrder
|
|
258
|
+
});
|
|
259
|
+
fireEvent.click(screen.getByRole('columnheader', {
|
|
260
|
+
name: /created on/i
|
|
261
|
+
}));
|
|
262
|
+
const rows = screen.getAllByRole('row');
|
|
263
|
+
expect(rows[1]).toHaveTextContent('alpha-bucket');
|
|
264
|
+
expect(rows[2]).toHaveTextContent('gamma-bucket');
|
|
265
|
+
expect(rows[3]).toHaveTextContent('beta-bucket');
|
|
266
|
+
});
|
|
267
|
+
it('sorts by storage location when location column header is clicked', ()=>{
|
|
268
|
+
jest.spyOn(__rspack_external__hooks_index_js_a0d26731, 'useBucketLocations').mockReturnValue(new Map([
|
|
269
|
+
[
|
|
270
|
+
'test-bucket-1',
|
|
271
|
+
'us-west-2'
|
|
272
|
+
],
|
|
273
|
+
[
|
|
274
|
+
'test-bucket-2',
|
|
275
|
+
'eu-west-1'
|
|
276
|
+
],
|
|
277
|
+
[
|
|
278
|
+
'test-bucket-3',
|
|
279
|
+
'ap-southeast-1'
|
|
280
|
+
]
|
|
281
|
+
]));
|
|
282
|
+
renderBucketList({
|
|
283
|
+
buckets: mockBuckets
|
|
284
|
+
});
|
|
285
|
+
fireEvent.click(screen.getByRole('columnheader', {
|
|
286
|
+
name: /storage location/i
|
|
287
|
+
}));
|
|
288
|
+
const rows = screen.getAllByRole('row');
|
|
289
|
+
expect(rows[1]).toHaveTextContent('test-bucket-3');
|
|
290
|
+
expect(rows[2]).toHaveTextContent('test-bucket-2');
|
|
291
|
+
expect(rows[3]).toHaveTextContent('test-bucket-1');
|
|
292
|
+
});
|
|
208
293
|
describe('extraBucketListColumns support', ()=>{
|
|
209
294
|
it('renders extra columns when extraBucketListColumns is configured', ()=>{
|
|
210
295
|
const CustomColumn = ({ data })=>/*#__PURE__*/ jsxs("span", {
|
|
@@ -66,11 +66,18 @@ describe('BucketNotificationFormPage', ()=>{
|
|
|
66
66
|
it('renders create notification form with all fields', ()=>{
|
|
67
67
|
renderCreatePage();
|
|
68
68
|
expect(screen.getByText('Create Bucket Notification')).toBeInTheDocument();
|
|
69
|
-
expect(screen.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
expect(screen.
|
|
73
|
-
|
|
69
|
+
expect(screen.getByRole('textbox', {
|
|
70
|
+
name: /rule name/i
|
|
71
|
+
})).toBeInTheDocument();
|
|
72
|
+
expect(screen.getByRole('textbox', {
|
|
73
|
+
name: /destination queue/i
|
|
74
|
+
})).toBeInTheDocument();
|
|
75
|
+
expect(screen.getByRole('textbox', {
|
|
76
|
+
name: /^prefix$/i
|
|
77
|
+
})).toBeInTheDocument();
|
|
78
|
+
expect(screen.getByRole('textbox', {
|
|
79
|
+
name: /^suffix$/i
|
|
80
|
+
})).toBeInTheDocument();
|
|
74
81
|
});
|
|
75
82
|
it('disables create button when form is pristine', ()=>{
|
|
76
83
|
renderCreatePage();
|
|
@@ -80,7 +87,9 @@ describe('BucketNotificationFormPage', ()=>{
|
|
|
80
87
|
});
|
|
81
88
|
it('shows validation error when rule name is empty', async ()=>{
|
|
82
89
|
renderCreatePage();
|
|
83
|
-
const ruleNameInput = screen.
|
|
90
|
+
const ruleNameInput = screen.getByRole('textbox', {
|
|
91
|
+
name: /rule name/i
|
|
92
|
+
});
|
|
84
93
|
await user_event.type(ruleNameInput, 'a');
|
|
85
94
|
await user_event.clear(ruleNameInput);
|
|
86
95
|
await waitFor(()=>{
|
|
@@ -104,7 +113,9 @@ describe('BucketNotificationFormPage', ()=>{
|
|
|
104
113
|
status: 'success'
|
|
105
114
|
});
|
|
106
115
|
renderCreatePage();
|
|
107
|
-
const ruleNameInput = screen.
|
|
116
|
+
const ruleNameInput = screen.getByRole('textbox', {
|
|
117
|
+
name: /rule name/i
|
|
118
|
+
});
|
|
108
119
|
await user_event.type(ruleNameInput, 'existing-rule');
|
|
109
120
|
await waitFor(()=>{
|
|
110
121
|
expect(screen.getByText(/a rule with this name already exists/i)).toBeInTheDocument();
|
|
@@ -112,7 +123,9 @@ describe('BucketNotificationFormPage', ()=>{
|
|
|
112
123
|
});
|
|
113
124
|
it('shows validation error when queue ARN is invalid', async ()=>{
|
|
114
125
|
renderCreatePage();
|
|
115
|
-
const queueArnInput = screen.
|
|
126
|
+
const queueArnInput = screen.getByRole('textbox', {
|
|
127
|
+
name: /destination queue/i
|
|
128
|
+
});
|
|
116
129
|
await user_event.type(queueArnInput, 'invalid-arn');
|
|
117
130
|
await waitFor(()=>{
|
|
118
131
|
expect(screen.getByText(/must be a valid arn/i)).toBeInTheDocument();
|
|
@@ -120,8 +133,12 @@ describe('BucketNotificationFormPage', ()=>{
|
|
|
120
133
|
});
|
|
121
134
|
it('enables create button when form is valid', async ()=>{
|
|
122
135
|
renderCreatePage();
|
|
123
|
-
await user_event.type(screen.
|
|
124
|
-
|
|
136
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
137
|
+
name: /rule name/i
|
|
138
|
+
}), 'test-rule');
|
|
139
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
140
|
+
name: /destination queue/i
|
|
141
|
+
}), 'arn:aws:sqs:us-east-1:123:queue');
|
|
125
142
|
const checkbox = screen.getByRole('checkbox', {
|
|
126
143
|
name: /s3:ObjectCreated:\*/i
|
|
127
144
|
});
|
|
@@ -134,13 +151,21 @@ describe('BucketNotificationFormPage', ()=>{
|
|
|
134
151
|
});
|
|
135
152
|
it('creates notification with all fields and navigates on success', async ()=>{
|
|
136
153
|
renderCreatePage();
|
|
137
|
-
await user_event.type(screen.
|
|
138
|
-
|
|
154
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
155
|
+
name: /rule name/i
|
|
156
|
+
}), 'test-rule');
|
|
157
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
158
|
+
name: /destination queue/i
|
|
159
|
+
}), 'arn:aws:sqs:us-east-1:123:my-queue');
|
|
139
160
|
fireEvent.click(screen.getByRole('checkbox', {
|
|
140
161
|
name: /s3:ObjectCreated:Put/i
|
|
141
162
|
}));
|
|
142
|
-
await user_event.type(screen.
|
|
143
|
-
|
|
163
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
164
|
+
name: /^prefix$/i
|
|
165
|
+
}), 'uploads/');
|
|
166
|
+
await user_event.type(screen.getByRole('textbox', {
|
|
167
|
+
name: /^suffix$/i
|
|
168
|
+
}), '.jpg');
|
|
144
169
|
mockMutate.mockImplementation((_, options)=>{
|
|
145
170
|
options?.onSuccess?.();
|
|
146
171
|
});
|
|
@@ -263,9 +288,15 @@ describe('BucketNotificationFormPage', ()=>{
|
|
|
263
288
|
it('pre-fills form with existing rule data', async ()=>{
|
|
264
289
|
renderEditPage();
|
|
265
290
|
await waitFor(()=>{
|
|
266
|
-
expect(screen.
|
|
267
|
-
|
|
268
|
-
|
|
291
|
+
expect(screen.getByRole('textbox', {
|
|
292
|
+
name: /destination queue/i
|
|
293
|
+
})).toHaveValue('arn:aws:sqs:us-east-1:123:existing-queue');
|
|
294
|
+
expect(screen.getByRole('textbox', {
|
|
295
|
+
name: /^prefix$/i
|
|
296
|
+
})).toHaveValue('data/');
|
|
297
|
+
expect(screen.getByRole('textbox', {
|
|
298
|
+
name: /^suffix$/i
|
|
299
|
+
})).toHaveValue('.json');
|
|
269
300
|
});
|
|
270
301
|
});
|
|
271
302
|
it('shows Save button instead of Create in edit mode', ()=>{
|
|
@@ -280,9 +311,13 @@ describe('BucketNotificationFormPage', ()=>{
|
|
|
280
311
|
it('updates notification and navigates on success', async ()=>{
|
|
281
312
|
renderEditPage();
|
|
282
313
|
await waitFor(()=>{
|
|
283
|
-
expect(screen.
|
|
314
|
+
expect(screen.getByRole('textbox', {
|
|
315
|
+
name: /destination queue/i
|
|
316
|
+
})).toHaveValue('arn:aws:sqs:us-east-1:123:existing-queue');
|
|
317
|
+
});
|
|
318
|
+
const queueInput = screen.getByRole('textbox', {
|
|
319
|
+
name: /destination queue/i
|
|
284
320
|
});
|
|
285
|
-
const queueInput = screen.getByLabelText(/destination queue/i);
|
|
286
321
|
await user_event.clear(queueInput);
|
|
287
322
|
await user_event.type(queueInput, 'arn:aws:sqs:us-east-1:123:updated-queue');
|
|
288
323
|
mockMutate.mockImplementation((_, options)=>{
|