@scality/data-browser-library 1.0.0-preview.2
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/Editor.d.ts +12 -0
- package/dist/components/Editor.js +28 -0
- package/dist/components/__tests__/BucketList.test.d.ts +1 -0
- package/dist/components/__tests__/BucketList.test.js +225 -0
- package/dist/components/__tests__/BucketOverview.test.d.ts +1 -0
- package/dist/components/__tests__/BucketOverview.test.js +479 -0
- package/dist/components/__tests__/BucketPolicyPage.test.d.ts +1 -0
- package/dist/components/__tests__/BucketPolicyPage.test.js +213 -0
- package/dist/components/__tests__/CreateFolderButton.test.d.ts +1 -0
- package/dist/components/__tests__/CreateFolderButton.test.js +147 -0
- package/dist/components/__tests__/DeleteBucketButton.test.d.ts +1 -0
- package/dist/components/__tests__/DeleteBucketButton.test.js +272 -0
- package/dist/components/__tests__/DeleteObjectButton.test.d.ts +1 -0
- package/dist/components/__tests__/DeleteObjectButton.test.js +302 -0
- package/dist/components/__tests__/MetadataSearch.test.d.ts +1 -0
- package/dist/components/__tests__/MetadataSearch.test.js +201 -0
- package/dist/components/__tests__/ObjectList.test.d.ts +1 -0
- package/dist/components/__tests__/ObjectList.test.js +283 -0
- package/dist/components/__tests__/UploadButton.test.d.ts +1 -0
- package/dist/components/__tests__/UploadButton.test.js +144 -0
- package/dist/components/buckets/BucketDetails.d.ts +1 -0
- package/dist/components/buckets/BucketDetails.js +51 -0
- package/dist/components/buckets/BucketList.d.ts +12 -0
- package/dist/components/buckets/BucketList.js +136 -0
- package/dist/components/buckets/BucketLocation.d.ts +3 -0
- package/dist/components/buckets/BucketLocation.js +16 -0
- package/dist/components/buckets/BucketOverview.d.ts +14 -0
- package/dist/components/buckets/BucketOverview.js +209 -0
- package/dist/components/buckets/BucketPage.d.ts +2 -0
- package/dist/components/buckets/BucketPage.js +47 -0
- package/dist/components/buckets/BucketPolicyButton.d.ts +7 -0
- package/dist/components/buckets/BucketPolicyButton.js +18 -0
- package/dist/components/buckets/BucketPolicyPage.d.ts +1 -0
- package/dist/components/buckets/BucketPolicyPage.js +205 -0
- package/dist/components/buckets/DeleteBucketButton.d.ts +8 -0
- package/dist/components/buckets/DeleteBucketButton.js +78 -0
- package/dist/components/index.d.ts +12 -0
- package/dist/components/index.js +13 -0
- package/dist/components/layouts/BrowserPageLayout.d.ts +9 -0
- package/dist/components/layouts/BrowserPageLayout.js +46 -0
- package/dist/components/objects/CreateFolderButton.d.ts +29 -0
- package/dist/components/objects/CreateFolderButton.js +118 -0
- package/dist/components/objects/DeleteObjectButton.d.ts +8 -0
- package/dist/components/objects/DeleteObjectButton.js +191 -0
- package/dist/components/objects/ObjectDetails/ObjectMetadata.d.ts +2 -0
- package/dist/components/objects/ObjectDetails/ObjectMetadata.js +323 -0
- package/dist/components/objects/ObjectDetails/ObjectSummary.d.ts +3 -0
- package/dist/components/objects/ObjectDetails/ObjectSummary.js +193 -0
- package/dist/components/objects/ObjectDetails/ObjectTags.d.ts +3 -0
- package/dist/components/objects/ObjectDetails/ObjectTags.js +300 -0
- package/dist/components/objects/ObjectDetails/index.d.ts +9 -0
- package/dist/components/objects/ObjectDetails/index.js +49 -0
- package/dist/components/objects/ObjectList.d.ts +40 -0
- package/dist/components/objects/ObjectList.js +407 -0
- package/dist/components/objects/ObjectPage.d.ts +1 -0
- package/dist/components/objects/ObjectPage.js +43 -0
- package/dist/components/objects/UploadButton.d.ts +34 -0
- package/dist/components/objects/UploadButton.js +229 -0
- package/dist/components/providers/DataBrowserProvider.d.ts +20 -0
- package/dist/components/providers/DataBrowserProvider.js +42 -0
- package/dist/components/search/MetadataSearch.d.ts +5 -0
- package/dist/components/search/MetadataSearch.js +162 -0
- package/dist/components/search/SearchHints.d.ts +8 -0
- package/dist/components/search/SearchHints.js +21 -0
- package/dist/components/ui/DeleteObjectModalContent.d.ts +5 -0
- package/dist/components/ui/DeleteObjectModalContent.js +71 -0
- package/dist/components/ui/Search.elements.d.ts +17 -0
- package/dist/components/ui/Search.elements.js +59 -0
- package/dist/components/ui/Table.elements.d.ts +36 -0
- package/dist/components/ui/Table.elements.js +87 -0
- package/dist/config/factory.d.ts +52 -0
- package/dist/config/factory.js +70 -0
- package/dist/config/types.d.ts +46 -0
- package/dist/config/types.js +0 -0
- package/dist/hooks/__tests__/useIsBucketEmpty.test.d.ts +1 -0
- package/dist/hooks/__tests__/useIsBucketEmpty.test.js +122 -0
- package/dist/hooks/bucketConfiguration.d.ts +147 -0
- package/dist/hooks/bucketConfiguration.js +59 -0
- package/dist/hooks/bucketOperations.d.ts +36 -0
- package/dist/hooks/bucketOperations.js +12 -0
- package/dist/hooks/factories/__tests__/useCreateS3FunctionMutationHook.test.d.ts +1 -0
- package/dist/hooks/factories/__tests__/useCreateS3FunctionMutationHook.test.js +276 -0
- package/dist/hooks/factories/__tests__/useCreateS3InfiniteQueryHook.test.d.ts +1 -0
- package/dist/hooks/factories/__tests__/useCreateS3InfiniteQueryHook.test.js +259 -0
- package/dist/hooks/factories/__tests__/useCreateS3LoginHook.test.d.ts +1 -0
- package/dist/hooks/factories/__tests__/useCreateS3LoginHook.test.js +166 -0
- package/dist/hooks/factories/__tests__/useCreateS3MutationHook.test.d.ts +1 -0
- package/dist/hooks/factories/__tests__/useCreateS3MutationHook.test.js +200 -0
- package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.d.ts +1 -0
- package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.js +136 -0
- package/dist/hooks/factories/index.d.ts +18 -0
- package/dist/hooks/factories/index.js +5 -0
- package/dist/hooks/factories/useCreateS3InfiniteQueryHook.d.ts +13 -0
- package/dist/hooks/factories/useCreateS3InfiniteQueryHook.js +76 -0
- package/dist/hooks/factories/useCreateS3LoginHook.d.ts +8 -0
- package/dist/hooks/factories/useCreateS3LoginHook.js +22 -0
- package/dist/hooks/factories/useCreateS3MutationHook.d.ts +5 -0
- package/dist/hooks/factories/useCreateS3MutationHook.js +50 -0
- package/dist/hooks/factories/useCreateS3QueryHook.d.ts +3 -0
- package/dist/hooks/factories/useCreateS3QueryHook.js +30 -0
- package/dist/hooks/index.d.ts +8 -0
- package/dist/hooks/index.js +8 -0
- package/dist/hooks/loginOperations.d.ts +21 -0
- package/dist/hooks/loginOperations.js +9 -0
- package/dist/hooks/objectOperations.d.ts +190 -0
- package/dist/hooks/objectOperations.js +66 -0
- package/dist/hooks/presignedOperations.d.ts +73 -0
- package/dist/hooks/presignedOperations.js +72 -0
- package/dist/hooks/useIsBucketEmpty.d.ts +7 -0
- package/dist/hooks/useIsBucketEmpty.js +36 -0
- package/dist/hooks/useLoginMutation.d.ts +21 -0
- package/dist/hooks/useLoginMutation.js +9 -0
- package/dist/hooks/useS3Client.d.ts +1 -0
- package/dist/hooks/useS3Client.js +13 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/schemas/bucketPolicySchema.json +321 -0
- package/dist/test/msw/handlers/deleteBucket.d.ts +1 -0
- package/dist/test/msw/handlers/deleteBucket.js +14 -0
- package/dist/test/msw/handlers/getBucketAcl.d.ts +1 -0
- package/dist/test/msw/handlers/getBucketAcl.js +96 -0
- package/dist/test/msw/handlers/getBucketLocation.d.ts +1 -0
- package/dist/test/msw/handlers/getBucketLocation.js +23 -0
- package/dist/test/msw/handlers/getBucketPolicy.d.ts +11 -0
- package/dist/test/msw/handlers/getBucketPolicy.js +72 -0
- package/dist/test/msw/handlers/headObject.d.ts +1 -0
- package/dist/test/msw/handlers/headObject.js +17 -0
- package/dist/test/msw/handlers/listBuckets.d.ts +1 -0
- package/dist/test/msw/handlers/listBuckets.js +24 -0
- package/dist/test/msw/handlers/listObjectVersions.d.ts +1 -0
- package/dist/test/msw/handlers/listObjectVersions.js +83 -0
- package/dist/test/msw/handlers/listObjects.d.ts +1 -0
- package/dist/test/msw/handlers/listObjects.js +66 -0
- package/dist/test/msw/handlers/objectLegalHold.d.ts +1 -0
- package/dist/test/msw/handlers/objectLegalHold.js +24 -0
- package/dist/test/msw/handlers/objectRetention.d.ts +1 -0
- package/dist/test/msw/handlers/objectRetention.js +27 -0
- package/dist/test/msw/handlers/putBucketAcl.d.ts +1 -0
- package/dist/test/msw/handlers/putBucketAcl.js +18 -0
- package/dist/test/msw/handlers/putObject.d.ts +1 -0
- package/dist/test/msw/handlers/putObject.js +16 -0
- package/dist/test/msw/handlers.d.ts +4 -0
- package/dist/test/msw/handlers.js +109 -0
- package/dist/test/msw/index.d.ts +2 -0
- package/dist/test/msw/index.js +3 -0
- package/dist/test/msw/server.d.ts +4 -0
- package/dist/test/msw/server.js +20 -0
- package/dist/test/msw/utils.d.ts +2 -0
- package/dist/test/msw/utils.js +13 -0
- package/dist/test/setup.d.ts +1 -0
- package/dist/test/setup.js +82 -0
- package/dist/test/testUtils.d.ts +82 -0
- package/dist/test/testUtils.js +236 -0
- package/dist/test/utils/errorHandling.test.d.ts +1 -0
- package/dist/test/utils/errorHandling.test.js +385 -0
- package/dist/types/index.d.ts +48 -0
- package/dist/types/index.js +0 -0
- package/dist/utils/deletion/index.d.ts +2 -0
- package/dist/utils/deletion/index.js +2 -0
- package/dist/utils/deletion/messages.d.ts +5 -0
- package/dist/utils/deletion/messages.js +29 -0
- package/dist/utils/deletion/types.d.ts +11 -0
- package/dist/utils/deletion/types.js +0 -0
- package/dist/utils/errorHandling.d.ts +54 -0
- package/dist/utils/errorHandling.js +79 -0
- package/dist/utils/hooks.d.ts +2 -0
- package/dist/utils/hooks.js +26 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/proxyMiddleware.d.ts +18 -0
- package/dist/utils/proxyMiddleware.js +56 -0
- package/dist/utils/s3Client.d.ts +5 -0
- package/dist/utils/s3Client.js +35 -0
- package/dist/utils/useFeatures.d.ts +1 -0
- package/dist/utils/useFeatures.js +7 -0
- package/package.json +79 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import { createTestWrapper } from "../../test/testUtils.js";
|
|
4
|
+
import { DeleteObjectButton } from "../objects/DeleteObjectButton.js";
|
|
5
|
+
import { useDeleteObjects, useGetBucketVersioning } from "../../hooks/index.js";
|
|
6
|
+
jest.mock("../../hooks");
|
|
7
|
+
const mockUseDeleteObjects = jest.mocked(useDeleteObjects);
|
|
8
|
+
const mockUseGetBucketVersioning = jest.mocked(useGetBucketVersioning);
|
|
9
|
+
const mockDeleteObjects = jest.fn();
|
|
10
|
+
const createMockObjects = (count, withVersions = false)=>Array.from({
|
|
11
|
+
length: count
|
|
12
|
+
}, (_, i)=>({
|
|
13
|
+
Key: `file${i + 1}.txt`,
|
|
14
|
+
displayName: `file${i + 1}.txt`,
|
|
15
|
+
Size: 1024 * (i + 1),
|
|
16
|
+
LastModified: new Date(`2023-01-${i + 1}T10:00:00Z`),
|
|
17
|
+
type: "object",
|
|
18
|
+
...withVersions && {
|
|
19
|
+
VersionId: `version-${i + 1}`
|
|
20
|
+
}
|
|
21
|
+
}));
|
|
22
|
+
const createMockFolders = (count)=>Array.from({
|
|
23
|
+
length: count
|
|
24
|
+
}, (_, i)=>({
|
|
25
|
+
Key: `folder${i + 1}/`,
|
|
26
|
+
displayName: `folder${i + 1}/`,
|
|
27
|
+
type: "folder",
|
|
28
|
+
LastModified: void 0
|
|
29
|
+
}));
|
|
30
|
+
const renderDeleteObjectButton = (props = {})=>{
|
|
31
|
+
const defaultProps = {
|
|
32
|
+
objects: createMockObjects(2),
|
|
33
|
+
bucketName: "test-bucket",
|
|
34
|
+
...props
|
|
35
|
+
};
|
|
36
|
+
const Wrapper = createTestWrapper();
|
|
37
|
+
return render(/*#__PURE__*/ jsx(Wrapper, {
|
|
38
|
+
children: /*#__PURE__*/ jsx(DeleteObjectButton, {
|
|
39
|
+
...defaultProps
|
|
40
|
+
})
|
|
41
|
+
}));
|
|
42
|
+
};
|
|
43
|
+
const mockHookDefaults = (versioningEnabled = false)=>{
|
|
44
|
+
mockUseGetBucketVersioning.mockReturnValue({
|
|
45
|
+
data: {
|
|
46
|
+
Status: versioningEnabled ? "Enabled" : "Suspended"
|
|
47
|
+
},
|
|
48
|
+
isLoading: false,
|
|
49
|
+
error: null
|
|
50
|
+
});
|
|
51
|
+
mockUseDeleteObjects.mockReturnValue({
|
|
52
|
+
mutate: mockDeleteObjects,
|
|
53
|
+
isPending: false
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
describe("DeleteObjectButton", ()=>{
|
|
57
|
+
beforeEach(()=>{
|
|
58
|
+
jest.clearAllMocks();
|
|
59
|
+
mockHookDefaults();
|
|
60
|
+
});
|
|
61
|
+
it("renders delete button with correct label and icon", ()=>{
|
|
62
|
+
renderDeleteObjectButton();
|
|
63
|
+
const button = screen.getByRole("button", {
|
|
64
|
+
name: /delete/i
|
|
65
|
+
});
|
|
66
|
+
expect(button).toBeInTheDocument();
|
|
67
|
+
expect(button).toHaveAttribute("id", "object-list-delete-button");
|
|
68
|
+
});
|
|
69
|
+
it("disables button when no objects are selected", ()=>{
|
|
70
|
+
renderDeleteObjectButton({
|
|
71
|
+
objects: []
|
|
72
|
+
});
|
|
73
|
+
const button = screen.getByRole("button", {
|
|
74
|
+
name: /delete/i
|
|
75
|
+
});
|
|
76
|
+
expect(button).toBeDisabled();
|
|
77
|
+
});
|
|
78
|
+
it("enables button when objects are selected", ()=>{
|
|
79
|
+
renderDeleteObjectButton();
|
|
80
|
+
const button = screen.getByRole("button", {
|
|
81
|
+
name: /delete/i
|
|
82
|
+
});
|
|
83
|
+
expect(button).not.toBeDisabled();
|
|
84
|
+
});
|
|
85
|
+
it("opens confirmation modal when delete button is clicked", ()=>{
|
|
86
|
+
renderDeleteObjectButton();
|
|
87
|
+
const button = screen.getByRole("button", {
|
|
88
|
+
name: /delete/i
|
|
89
|
+
});
|
|
90
|
+
fireEvent.click(button);
|
|
91
|
+
expect(screen.getByText("Confirmation")).toBeInTheDocument();
|
|
92
|
+
expect(screen.getByText(/do you want to.*delete/i)).toBeInTheDocument();
|
|
93
|
+
});
|
|
94
|
+
it("shows correct confirmation message for objects without versioning", ()=>{
|
|
95
|
+
renderDeleteObjectButton({
|
|
96
|
+
objects: createMockObjects(2)
|
|
97
|
+
});
|
|
98
|
+
const button = screen.getByRole("button", {
|
|
99
|
+
name: /delete/i
|
|
100
|
+
});
|
|
101
|
+
fireEvent.click(button);
|
|
102
|
+
expect(screen.getByText((content, _)=>content.includes("2") && content.includes("object"))).toBeInTheDocument();
|
|
103
|
+
});
|
|
104
|
+
it("shows correct confirmation message for mixed objects and folders", ()=>{
|
|
105
|
+
const mixedObjects = [
|
|
106
|
+
...createMockObjects(1),
|
|
107
|
+
...createMockFolders(1)
|
|
108
|
+
];
|
|
109
|
+
renderDeleteObjectButton({
|
|
110
|
+
objects: mixedObjects
|
|
111
|
+
});
|
|
112
|
+
const button = screen.getByRole("button", {
|
|
113
|
+
name: /delete/i
|
|
114
|
+
});
|
|
115
|
+
fireEvent.click(button);
|
|
116
|
+
expect(screen.getByText(/do you want to/i)).toBeInTheDocument();
|
|
117
|
+
expect(screen.getByText(/delete the selected/i)).toBeInTheDocument();
|
|
118
|
+
});
|
|
119
|
+
it("shows permanently delete message for versioned objects with specific versions", ()=>{
|
|
120
|
+
mockHookDefaults(true);
|
|
121
|
+
renderDeleteObjectButton({
|
|
122
|
+
objects: createMockObjects(2, true)
|
|
123
|
+
});
|
|
124
|
+
const button = screen.getByRole("button", {
|
|
125
|
+
name: /delete/i
|
|
126
|
+
});
|
|
127
|
+
fireEvent.click(button);
|
|
128
|
+
expect(screen.getByText("The selected versions will be permanently deleted.")).toBeInTheDocument();
|
|
129
|
+
expect(screen.getByText(/do you want to/i)).toBeInTheDocument();
|
|
130
|
+
expect(screen.getByText(/object versions/i)).toBeInTheDocument();
|
|
131
|
+
});
|
|
132
|
+
it("displays total size of selected objects", ()=>{
|
|
133
|
+
const objects = createMockObjects(2);
|
|
134
|
+
renderDeleteObjectButton({
|
|
135
|
+
objects
|
|
136
|
+
});
|
|
137
|
+
const button = screen.getByRole("button", {
|
|
138
|
+
name: /delete/i
|
|
139
|
+
});
|
|
140
|
+
fireEvent.click(button);
|
|
141
|
+
expect(screen.getByText(/total.*3.*kb/i)).toBeInTheDocument();
|
|
142
|
+
});
|
|
143
|
+
it("shows info banner with deletion message", ()=>{
|
|
144
|
+
renderDeleteObjectButton();
|
|
145
|
+
const button = screen.getByRole("button", {
|
|
146
|
+
name: /delete/i
|
|
147
|
+
});
|
|
148
|
+
fireEvent.click(button);
|
|
149
|
+
expect(screen.getByText(/the selected objects will be permanently deleted/i)).toBeInTheDocument();
|
|
150
|
+
});
|
|
151
|
+
it("shows different info message for versioned buckets without specific versions", ()=>{
|
|
152
|
+
mockHookDefaults(true);
|
|
153
|
+
renderDeleteObjectButton({
|
|
154
|
+
objects: createMockObjects(2, false)
|
|
155
|
+
});
|
|
156
|
+
const button = screen.getByRole("button", {
|
|
157
|
+
name: /delete/i
|
|
158
|
+
});
|
|
159
|
+
fireEvent.click(button);
|
|
160
|
+
expect(screen.getByText(/delete markers will be added to the objects/i)).toBeInTheDocument();
|
|
161
|
+
});
|
|
162
|
+
it("closes modal when cancel button is clicked", ()=>{
|
|
163
|
+
renderDeleteObjectButton();
|
|
164
|
+
const deleteButton = screen.getByRole("button", {
|
|
165
|
+
name: /delete/i
|
|
166
|
+
});
|
|
167
|
+
fireEvent.click(deleteButton);
|
|
168
|
+
const cancelButton = screen.getByRole("button", {
|
|
169
|
+
name: /cancel/i
|
|
170
|
+
});
|
|
171
|
+
fireEvent.click(cancelButton);
|
|
172
|
+
expect(screen.queryByText("Confirmation")).not.toBeInTheDocument();
|
|
173
|
+
});
|
|
174
|
+
it("calls delete mutation with correct parameters when deletion is confirmed", ()=>{
|
|
175
|
+
const objects = createMockObjects(2);
|
|
176
|
+
renderDeleteObjectButton({
|
|
177
|
+
objects,
|
|
178
|
+
bucketName: "my-bucket"
|
|
179
|
+
});
|
|
180
|
+
const deleteButton = screen.getByRole("button", {
|
|
181
|
+
name: /delete/i
|
|
182
|
+
});
|
|
183
|
+
fireEvent.click(deleteButton);
|
|
184
|
+
const confirmButton = document.getElementById("object-delete-delete-button");
|
|
185
|
+
expect(confirmButton).toBeInTheDocument();
|
|
186
|
+
fireEvent.click(confirmButton);
|
|
187
|
+
expect(mockDeleteObjects).toHaveBeenCalledWith({
|
|
188
|
+
Bucket: "my-bucket",
|
|
189
|
+
Delete: {
|
|
190
|
+
Objects: [
|
|
191
|
+
{
|
|
192
|
+
Key: "file1.txt"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
Key: "file2.txt"
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
}, expect.objectContaining({
|
|
200
|
+
onSuccess: expect.any(Function),
|
|
201
|
+
onError: expect.any(Function)
|
|
202
|
+
}));
|
|
203
|
+
});
|
|
204
|
+
it("includes version IDs in delete request when objects have versions", ()=>{
|
|
205
|
+
const objects = createMockObjects(1, true);
|
|
206
|
+
renderDeleteObjectButton({
|
|
207
|
+
objects
|
|
208
|
+
});
|
|
209
|
+
const deleteButton = screen.getByRole("button", {
|
|
210
|
+
name: /delete/i
|
|
211
|
+
});
|
|
212
|
+
fireEvent.click(deleteButton);
|
|
213
|
+
const confirmButton = document.getElementById("object-delete-delete-button");
|
|
214
|
+
fireEvent.click(confirmButton);
|
|
215
|
+
expect(mockDeleteObjects).toHaveBeenCalledWith(expect.objectContaining({
|
|
216
|
+
Delete: {
|
|
217
|
+
Objects: [
|
|
218
|
+
{
|
|
219
|
+
Key: "file1.txt",
|
|
220
|
+
VersionId: "version-1"
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
}), expect.any(Object));
|
|
225
|
+
});
|
|
226
|
+
it("handles mixed objects and folders in delete request", ()=>{
|
|
227
|
+
const mixedObjects = [
|
|
228
|
+
...createMockObjects(1),
|
|
229
|
+
...createMockFolders(1)
|
|
230
|
+
];
|
|
231
|
+
renderDeleteObjectButton({
|
|
232
|
+
objects: mixedObjects
|
|
233
|
+
});
|
|
234
|
+
const deleteButton = screen.getByRole("button", {
|
|
235
|
+
name: /delete/i
|
|
236
|
+
});
|
|
237
|
+
fireEvent.click(deleteButton);
|
|
238
|
+
const confirmButton = document.getElementById("object-delete-delete-button");
|
|
239
|
+
fireEvent.click(confirmButton);
|
|
240
|
+
expect(mockDeleteObjects).toHaveBeenCalledWith(expect.objectContaining({
|
|
241
|
+
Delete: {
|
|
242
|
+
Objects: [
|
|
243
|
+
{
|
|
244
|
+
Key: "file1.txt"
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
Key: "folder1/"
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
}), expect.any(Object));
|
|
252
|
+
});
|
|
253
|
+
it("shows success toast and closes modal when deletion succeeds", async ()=>{
|
|
254
|
+
renderDeleteObjectButton();
|
|
255
|
+
const deleteButton = screen.getByRole("button", {
|
|
256
|
+
name: /delete/i
|
|
257
|
+
});
|
|
258
|
+
fireEvent.click(deleteButton);
|
|
259
|
+
expect(screen.getByText("Confirmation")).toBeInTheDocument();
|
|
260
|
+
const confirmButton = document.getElementById("object-delete-delete-button");
|
|
261
|
+
fireEvent.click(confirmButton);
|
|
262
|
+
await act(async ()=>{
|
|
263
|
+
const successCallback = mockDeleteObjects.mock.calls[0][1].onSuccess;
|
|
264
|
+
successCallback();
|
|
265
|
+
});
|
|
266
|
+
await waitFor(()=>{
|
|
267
|
+
expect(screen.queryByText("Confirmation")).not.toBeInTheDocument();
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
it("handles non-Error objects in error callback", async ()=>{
|
|
271
|
+
renderDeleteObjectButton();
|
|
272
|
+
const deleteButton = screen.getByRole("button", {
|
|
273
|
+
name: /delete/i
|
|
274
|
+
});
|
|
275
|
+
fireEvent.click(deleteButton);
|
|
276
|
+
const confirmButton = document.getElementById("object-delete-delete-button");
|
|
277
|
+
fireEvent.click(confirmButton);
|
|
278
|
+
await act(async ()=>{
|
|
279
|
+
const errorCallback = mockDeleteObjects.mock.calls[0][1].onError;
|
|
280
|
+
errorCallback("string error");
|
|
281
|
+
});
|
|
282
|
+
expect(mockDeleteObjects.mock.calls[0][1].onError).toBeDefined();
|
|
283
|
+
});
|
|
284
|
+
it("updates objects when props change", ()=>{
|
|
285
|
+
const { rerender } = renderDeleteObjectButton({
|
|
286
|
+
objects: createMockObjects(1)
|
|
287
|
+
});
|
|
288
|
+
expect(screen.getByRole("button", {
|
|
289
|
+
name: /delete/i
|
|
290
|
+
})).not.toBeDisabled();
|
|
291
|
+
const Wrapper = createTestWrapper();
|
|
292
|
+
rerender(/*#__PURE__*/ jsx(Wrapper, {
|
|
293
|
+
children: /*#__PURE__*/ jsx(DeleteObjectButton, {
|
|
294
|
+
objects: [],
|
|
295
|
+
bucketName: "test-bucket"
|
|
296
|
+
})
|
|
297
|
+
}));
|
|
298
|
+
expect(screen.getByRole("button", {
|
|
299
|
+
name: /delete/i
|
|
300
|
+
})).toBeDisabled();
|
|
301
|
+
});
|
|
302
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import user_event from "@testing-library/user-event";
|
|
4
|
+
import { MemoryRouter } from "react-router-dom";
|
|
5
|
+
import { createQueryWrapper } from "../../test/testUtils.js";
|
|
6
|
+
import { MetadataSearch } from "../search/MetadataSearch.js";
|
|
7
|
+
const renderMetadataSearch = (props = {}, initialEntries = [
|
|
8
|
+
"/bucket/test-bucket"
|
|
9
|
+
])=>{
|
|
10
|
+
const QueryWrapper = createQueryWrapper();
|
|
11
|
+
return render(/*#__PURE__*/ jsx(QueryWrapper, {
|
|
12
|
+
children: /*#__PURE__*/ jsx(MemoryRouter, {
|
|
13
|
+
initialEntries: initialEntries,
|
|
14
|
+
children: /*#__PURE__*/ jsx(MetadataSearch, {
|
|
15
|
+
isError: false,
|
|
16
|
+
...props
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
}));
|
|
20
|
+
};
|
|
21
|
+
describe("MetadataSearch", ()=>{
|
|
22
|
+
const getInput = ()=>screen.getByPlaceholderText(/Metadata Search/i);
|
|
23
|
+
it("renders search input with placeholder", ()=>{
|
|
24
|
+
renderMetadataSearch();
|
|
25
|
+
expect(getInput()).toBeInTheDocument();
|
|
26
|
+
expect(screen.getByPlaceholderText(/Metadata Search/i)).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
it("renders search button", ()=>{
|
|
29
|
+
renderMetadataSearch();
|
|
30
|
+
expect(screen.getByRole("button", {
|
|
31
|
+
name: /execute metadata search/i
|
|
32
|
+
})).toBeInTheDocument();
|
|
33
|
+
});
|
|
34
|
+
it("search button is disabled when input is empty", ()=>{
|
|
35
|
+
renderMetadataSearch();
|
|
36
|
+
const searchButton = screen.getByRole("button", {
|
|
37
|
+
name: /execute metadata search/i
|
|
38
|
+
});
|
|
39
|
+
expect(searchButton).toBeDisabled();
|
|
40
|
+
});
|
|
41
|
+
it("search button is enabled when input has text", async ()=>{
|
|
42
|
+
renderMetadataSearch();
|
|
43
|
+
const input = getInput();
|
|
44
|
+
const searchButton = screen.getByRole("button", {
|
|
45
|
+
name: /execute metadata search/i
|
|
46
|
+
});
|
|
47
|
+
await user_event.type(input, "test query");
|
|
48
|
+
expect(searchButton).toBeEnabled();
|
|
49
|
+
});
|
|
50
|
+
it("shows search icon by default", ()=>{
|
|
51
|
+
renderMetadataSearch();
|
|
52
|
+
const input = getInput();
|
|
53
|
+
const searchIcon = input.parentElement?.querySelector('[aria-label*="Search"]');
|
|
54
|
+
expect(searchIcon).toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
it("shows check icon when metadata search is active", ()=>{
|
|
57
|
+
renderMetadataSearch({}, [
|
|
58
|
+
"/bucket/test-bucket?metadatasearch=test"
|
|
59
|
+
]);
|
|
60
|
+
const input = getInput();
|
|
61
|
+
const checkIcon = input.parentElement?.querySelector('[aria-label*="Check"]');
|
|
62
|
+
expect(checkIcon).toBeInTheDocument();
|
|
63
|
+
});
|
|
64
|
+
it("shows error icon when error prop is true", ()=>{
|
|
65
|
+
renderMetadataSearch({
|
|
66
|
+
isError: true
|
|
67
|
+
});
|
|
68
|
+
const input = getInput();
|
|
69
|
+
const errorIcon = input.parentElement?.querySelector('[aria-label*="Close"]');
|
|
70
|
+
expect(errorIcon).toBeInTheDocument();
|
|
71
|
+
});
|
|
72
|
+
it("populates input with existing search query from URL", ()=>{
|
|
73
|
+
renderMetadataSearch({}, [
|
|
74
|
+
"/bucket/test-bucket?metadatasearch=existing+query"
|
|
75
|
+
]);
|
|
76
|
+
const input = getInput();
|
|
77
|
+
expect(input).toHaveValue("existing query");
|
|
78
|
+
});
|
|
79
|
+
it("shows reset button when input has text", async ()=>{
|
|
80
|
+
renderMetadataSearch();
|
|
81
|
+
const input = getInput();
|
|
82
|
+
await user_event.type(input, "test query");
|
|
83
|
+
expect(screen.getByRole("button", {
|
|
84
|
+
name: /clear search input/i
|
|
85
|
+
})).toBeInTheDocument();
|
|
86
|
+
});
|
|
87
|
+
it("clears input when reset button is clicked", async ()=>{
|
|
88
|
+
renderMetadataSearch();
|
|
89
|
+
const input = getInput();
|
|
90
|
+
await user_event.type(input, "test query");
|
|
91
|
+
const resetButton = screen.getByRole("button", {
|
|
92
|
+
name: /clear search input/i
|
|
93
|
+
});
|
|
94
|
+
fireEvent.click(resetButton);
|
|
95
|
+
expect(input).toHaveValue("");
|
|
96
|
+
});
|
|
97
|
+
it("shows hints when input is clicked and empty", async ()=>{
|
|
98
|
+
renderMetadataSearch();
|
|
99
|
+
const input = getInput();
|
|
100
|
+
fireEvent.click(input);
|
|
101
|
+
await waitFor(()=>{
|
|
102
|
+
expect(screen.getByText("Suggestions")).toBeInTheDocument();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
it("does not show hints when input has text", async ()=>{
|
|
106
|
+
renderMetadataSearch();
|
|
107
|
+
const input = getInput();
|
|
108
|
+
await user_event.type(input, "test");
|
|
109
|
+
fireEvent.click(input);
|
|
110
|
+
expect(screen.queryByText("Suggestions")).not.toBeInTheDocument();
|
|
111
|
+
});
|
|
112
|
+
it("hides hints when clicking outside", async ()=>{
|
|
113
|
+
renderMetadataSearch();
|
|
114
|
+
const input = getInput();
|
|
115
|
+
fireEvent.click(input);
|
|
116
|
+
await waitFor(()=>{
|
|
117
|
+
expect(screen.getByText("Suggestions")).toBeInTheDocument();
|
|
118
|
+
});
|
|
119
|
+
fireEvent.mouseDown(document.body);
|
|
120
|
+
await waitFor(()=>{
|
|
121
|
+
expect(screen.queryByText("Suggestions")).not.toBeInTheDocument();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
it("displays search hint examples", async ()=>{
|
|
125
|
+
renderMetadataSearch();
|
|
126
|
+
const input = getInput();
|
|
127
|
+
fireEvent.click(input);
|
|
128
|
+
await waitFor(()=>{
|
|
129
|
+
expect(screen.getByText('files with extension ".pdf"')).toBeInTheDocument();
|
|
130
|
+
expect(screen.getByText("files bigger than 1MB")).toBeInTheDocument();
|
|
131
|
+
expect(screen.getByText("files tagged with color blue")).toBeInTheDocument();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
it("applies hint to input when hint is clicked", async ()=>{
|
|
135
|
+
renderMetadataSearch();
|
|
136
|
+
const input = getInput();
|
|
137
|
+
fireEvent.click(input);
|
|
138
|
+
await waitFor(()=>{
|
|
139
|
+
expect(screen.getByText("Suggestions")).toBeInTheDocument();
|
|
140
|
+
});
|
|
141
|
+
const pdfHint = screen.getByText('files with extension ".pdf"');
|
|
142
|
+
fireEvent.click(pdfHint);
|
|
143
|
+
expect(input).toHaveValue("key like /pdf$/");
|
|
144
|
+
expect(screen.queryByText("Suggestions")).not.toBeInTheDocument();
|
|
145
|
+
});
|
|
146
|
+
it("focuses input after hint is selected", async ()=>{
|
|
147
|
+
renderMetadataSearch();
|
|
148
|
+
const input = getInput();
|
|
149
|
+
fireEvent.click(input);
|
|
150
|
+
await waitFor(()=>{
|
|
151
|
+
expect(screen.getByText("Suggestions")).toBeInTheDocument();
|
|
152
|
+
});
|
|
153
|
+
const hint = screen.getByText("files bigger than 1MB");
|
|
154
|
+
fireEvent.click(hint);
|
|
155
|
+
expect(input).toHaveFocus();
|
|
156
|
+
});
|
|
157
|
+
it("submits search when form is submitted", async ()=>{
|
|
158
|
+
renderMetadataSearch();
|
|
159
|
+
const input = getInput();
|
|
160
|
+
const searchButton = screen.getByRole("button", {
|
|
161
|
+
name: /execute metadata search/i
|
|
162
|
+
});
|
|
163
|
+
await user_event.type(input, "content-type=application/pdf");
|
|
164
|
+
fireEvent.click(searchButton);
|
|
165
|
+
expect(input).toHaveValue("content-type=application/pdf");
|
|
166
|
+
});
|
|
167
|
+
it("submits search when Enter key is pressed", async ()=>{
|
|
168
|
+
renderMetadataSearch();
|
|
169
|
+
const input = getInput();
|
|
170
|
+
await user_event.type(input, "key like /test$/");
|
|
171
|
+
fireEvent.submit(input.closest("form"));
|
|
172
|
+
expect(input).toHaveValue("key like /test$/");
|
|
173
|
+
});
|
|
174
|
+
it("is hidden when prefix points to a file", ()=>{
|
|
175
|
+
const { container } = renderMetadataSearch({}, [
|
|
176
|
+
"/bucket/test-bucket?prefix=folder/file.txt"
|
|
177
|
+
]);
|
|
178
|
+
const form = container.querySelector("form");
|
|
179
|
+
expect(form).toHaveStyle({
|
|
180
|
+
visibility: "hidden"
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
it("is hidden when prefix is a folder", ()=>{
|
|
184
|
+
const { container } = renderMetadataSearch({}, [
|
|
185
|
+
"/bucket/test-bucket?prefix=folder/"
|
|
186
|
+
]);
|
|
187
|
+
const form = container.querySelector("form");
|
|
188
|
+
expect(form).toHaveStyle({
|
|
189
|
+
visibility: "hidden"
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
it("prevents search submission when prefix points to a file", async ()=>{
|
|
193
|
+
const { container } = renderMetadataSearch({}, [
|
|
194
|
+
"/bucket/test-bucket?prefix=folder/file.txt"
|
|
195
|
+
]);
|
|
196
|
+
const input = getInput();
|
|
197
|
+
const searchButton = container.querySelector('button[type="submit"]');
|
|
198
|
+
await user_event.type(input, "test query");
|
|
199
|
+
expect(searchButton).toBeDisabled();
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|