@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.
Files changed (176) hide show
  1. package/dist/components/Editor.d.ts +12 -0
  2. package/dist/components/Editor.js +28 -0
  3. package/dist/components/__tests__/BucketList.test.d.ts +1 -0
  4. package/dist/components/__tests__/BucketList.test.js +225 -0
  5. package/dist/components/__tests__/BucketOverview.test.d.ts +1 -0
  6. package/dist/components/__tests__/BucketOverview.test.js +479 -0
  7. package/dist/components/__tests__/BucketPolicyPage.test.d.ts +1 -0
  8. package/dist/components/__tests__/BucketPolicyPage.test.js +213 -0
  9. package/dist/components/__tests__/CreateFolderButton.test.d.ts +1 -0
  10. package/dist/components/__tests__/CreateFolderButton.test.js +147 -0
  11. package/dist/components/__tests__/DeleteBucketButton.test.d.ts +1 -0
  12. package/dist/components/__tests__/DeleteBucketButton.test.js +272 -0
  13. package/dist/components/__tests__/DeleteObjectButton.test.d.ts +1 -0
  14. package/dist/components/__tests__/DeleteObjectButton.test.js +302 -0
  15. package/dist/components/__tests__/MetadataSearch.test.d.ts +1 -0
  16. package/dist/components/__tests__/MetadataSearch.test.js +201 -0
  17. package/dist/components/__tests__/ObjectList.test.d.ts +1 -0
  18. package/dist/components/__tests__/ObjectList.test.js +283 -0
  19. package/dist/components/__tests__/UploadButton.test.d.ts +1 -0
  20. package/dist/components/__tests__/UploadButton.test.js +144 -0
  21. package/dist/components/buckets/BucketDetails.d.ts +1 -0
  22. package/dist/components/buckets/BucketDetails.js +51 -0
  23. package/dist/components/buckets/BucketList.d.ts +12 -0
  24. package/dist/components/buckets/BucketList.js +136 -0
  25. package/dist/components/buckets/BucketLocation.d.ts +3 -0
  26. package/dist/components/buckets/BucketLocation.js +16 -0
  27. package/dist/components/buckets/BucketOverview.d.ts +14 -0
  28. package/dist/components/buckets/BucketOverview.js +209 -0
  29. package/dist/components/buckets/BucketPage.d.ts +2 -0
  30. package/dist/components/buckets/BucketPage.js +47 -0
  31. package/dist/components/buckets/BucketPolicyButton.d.ts +7 -0
  32. package/dist/components/buckets/BucketPolicyButton.js +18 -0
  33. package/dist/components/buckets/BucketPolicyPage.d.ts +1 -0
  34. package/dist/components/buckets/BucketPolicyPage.js +205 -0
  35. package/dist/components/buckets/DeleteBucketButton.d.ts +8 -0
  36. package/dist/components/buckets/DeleteBucketButton.js +78 -0
  37. package/dist/components/index.d.ts +12 -0
  38. package/dist/components/index.js +13 -0
  39. package/dist/components/layouts/BrowserPageLayout.d.ts +9 -0
  40. package/dist/components/layouts/BrowserPageLayout.js +46 -0
  41. package/dist/components/objects/CreateFolderButton.d.ts +29 -0
  42. package/dist/components/objects/CreateFolderButton.js +118 -0
  43. package/dist/components/objects/DeleteObjectButton.d.ts +8 -0
  44. package/dist/components/objects/DeleteObjectButton.js +191 -0
  45. package/dist/components/objects/ObjectDetails/ObjectMetadata.d.ts +2 -0
  46. package/dist/components/objects/ObjectDetails/ObjectMetadata.js +323 -0
  47. package/dist/components/objects/ObjectDetails/ObjectSummary.d.ts +3 -0
  48. package/dist/components/objects/ObjectDetails/ObjectSummary.js +193 -0
  49. package/dist/components/objects/ObjectDetails/ObjectTags.d.ts +3 -0
  50. package/dist/components/objects/ObjectDetails/ObjectTags.js +300 -0
  51. package/dist/components/objects/ObjectDetails/index.d.ts +9 -0
  52. package/dist/components/objects/ObjectDetails/index.js +49 -0
  53. package/dist/components/objects/ObjectList.d.ts +40 -0
  54. package/dist/components/objects/ObjectList.js +407 -0
  55. package/dist/components/objects/ObjectPage.d.ts +1 -0
  56. package/dist/components/objects/ObjectPage.js +43 -0
  57. package/dist/components/objects/UploadButton.d.ts +34 -0
  58. package/dist/components/objects/UploadButton.js +229 -0
  59. package/dist/components/providers/DataBrowserProvider.d.ts +20 -0
  60. package/dist/components/providers/DataBrowserProvider.js +42 -0
  61. package/dist/components/search/MetadataSearch.d.ts +5 -0
  62. package/dist/components/search/MetadataSearch.js +162 -0
  63. package/dist/components/search/SearchHints.d.ts +8 -0
  64. package/dist/components/search/SearchHints.js +21 -0
  65. package/dist/components/ui/DeleteObjectModalContent.d.ts +5 -0
  66. package/dist/components/ui/DeleteObjectModalContent.js +71 -0
  67. package/dist/components/ui/Search.elements.d.ts +17 -0
  68. package/dist/components/ui/Search.elements.js +59 -0
  69. package/dist/components/ui/Table.elements.d.ts +36 -0
  70. package/dist/components/ui/Table.elements.js +87 -0
  71. package/dist/config/factory.d.ts +52 -0
  72. package/dist/config/factory.js +70 -0
  73. package/dist/config/types.d.ts +46 -0
  74. package/dist/config/types.js +0 -0
  75. package/dist/hooks/__tests__/useIsBucketEmpty.test.d.ts +1 -0
  76. package/dist/hooks/__tests__/useIsBucketEmpty.test.js +122 -0
  77. package/dist/hooks/bucketConfiguration.d.ts +147 -0
  78. package/dist/hooks/bucketConfiguration.js +59 -0
  79. package/dist/hooks/bucketOperations.d.ts +36 -0
  80. package/dist/hooks/bucketOperations.js +12 -0
  81. package/dist/hooks/factories/__tests__/useCreateS3FunctionMutationHook.test.d.ts +1 -0
  82. package/dist/hooks/factories/__tests__/useCreateS3FunctionMutationHook.test.js +276 -0
  83. package/dist/hooks/factories/__tests__/useCreateS3InfiniteQueryHook.test.d.ts +1 -0
  84. package/dist/hooks/factories/__tests__/useCreateS3InfiniteQueryHook.test.js +259 -0
  85. package/dist/hooks/factories/__tests__/useCreateS3LoginHook.test.d.ts +1 -0
  86. package/dist/hooks/factories/__tests__/useCreateS3LoginHook.test.js +166 -0
  87. package/dist/hooks/factories/__tests__/useCreateS3MutationHook.test.d.ts +1 -0
  88. package/dist/hooks/factories/__tests__/useCreateS3MutationHook.test.js +200 -0
  89. package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.d.ts +1 -0
  90. package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.js +136 -0
  91. package/dist/hooks/factories/index.d.ts +18 -0
  92. package/dist/hooks/factories/index.js +5 -0
  93. package/dist/hooks/factories/useCreateS3InfiniteQueryHook.d.ts +13 -0
  94. package/dist/hooks/factories/useCreateS3InfiniteQueryHook.js +76 -0
  95. package/dist/hooks/factories/useCreateS3LoginHook.d.ts +8 -0
  96. package/dist/hooks/factories/useCreateS3LoginHook.js +22 -0
  97. package/dist/hooks/factories/useCreateS3MutationHook.d.ts +5 -0
  98. package/dist/hooks/factories/useCreateS3MutationHook.js +50 -0
  99. package/dist/hooks/factories/useCreateS3QueryHook.d.ts +3 -0
  100. package/dist/hooks/factories/useCreateS3QueryHook.js +30 -0
  101. package/dist/hooks/index.d.ts +8 -0
  102. package/dist/hooks/index.js +8 -0
  103. package/dist/hooks/loginOperations.d.ts +21 -0
  104. package/dist/hooks/loginOperations.js +9 -0
  105. package/dist/hooks/objectOperations.d.ts +190 -0
  106. package/dist/hooks/objectOperations.js +66 -0
  107. package/dist/hooks/presignedOperations.d.ts +73 -0
  108. package/dist/hooks/presignedOperations.js +72 -0
  109. package/dist/hooks/useIsBucketEmpty.d.ts +7 -0
  110. package/dist/hooks/useIsBucketEmpty.js +36 -0
  111. package/dist/hooks/useLoginMutation.d.ts +21 -0
  112. package/dist/hooks/useLoginMutation.js +9 -0
  113. package/dist/hooks/useS3Client.d.ts +1 -0
  114. package/dist/hooks/useS3Client.js +13 -0
  115. package/dist/index.d.ts +6 -0
  116. package/dist/index.js +6 -0
  117. package/dist/schemas/bucketPolicySchema.json +321 -0
  118. package/dist/test/msw/handlers/deleteBucket.d.ts +1 -0
  119. package/dist/test/msw/handlers/deleteBucket.js +14 -0
  120. package/dist/test/msw/handlers/getBucketAcl.d.ts +1 -0
  121. package/dist/test/msw/handlers/getBucketAcl.js +96 -0
  122. package/dist/test/msw/handlers/getBucketLocation.d.ts +1 -0
  123. package/dist/test/msw/handlers/getBucketLocation.js +23 -0
  124. package/dist/test/msw/handlers/getBucketPolicy.d.ts +11 -0
  125. package/dist/test/msw/handlers/getBucketPolicy.js +72 -0
  126. package/dist/test/msw/handlers/headObject.d.ts +1 -0
  127. package/dist/test/msw/handlers/headObject.js +17 -0
  128. package/dist/test/msw/handlers/listBuckets.d.ts +1 -0
  129. package/dist/test/msw/handlers/listBuckets.js +24 -0
  130. package/dist/test/msw/handlers/listObjectVersions.d.ts +1 -0
  131. package/dist/test/msw/handlers/listObjectVersions.js +83 -0
  132. package/dist/test/msw/handlers/listObjects.d.ts +1 -0
  133. package/dist/test/msw/handlers/listObjects.js +66 -0
  134. package/dist/test/msw/handlers/objectLegalHold.d.ts +1 -0
  135. package/dist/test/msw/handlers/objectLegalHold.js +24 -0
  136. package/dist/test/msw/handlers/objectRetention.d.ts +1 -0
  137. package/dist/test/msw/handlers/objectRetention.js +27 -0
  138. package/dist/test/msw/handlers/putBucketAcl.d.ts +1 -0
  139. package/dist/test/msw/handlers/putBucketAcl.js +18 -0
  140. package/dist/test/msw/handlers/putObject.d.ts +1 -0
  141. package/dist/test/msw/handlers/putObject.js +16 -0
  142. package/dist/test/msw/handlers.d.ts +4 -0
  143. package/dist/test/msw/handlers.js +109 -0
  144. package/dist/test/msw/index.d.ts +2 -0
  145. package/dist/test/msw/index.js +3 -0
  146. package/dist/test/msw/server.d.ts +4 -0
  147. package/dist/test/msw/server.js +20 -0
  148. package/dist/test/msw/utils.d.ts +2 -0
  149. package/dist/test/msw/utils.js +13 -0
  150. package/dist/test/setup.d.ts +1 -0
  151. package/dist/test/setup.js +82 -0
  152. package/dist/test/testUtils.d.ts +82 -0
  153. package/dist/test/testUtils.js +236 -0
  154. package/dist/test/utils/errorHandling.test.d.ts +1 -0
  155. package/dist/test/utils/errorHandling.test.js +385 -0
  156. package/dist/types/index.d.ts +48 -0
  157. package/dist/types/index.js +0 -0
  158. package/dist/utils/deletion/index.d.ts +2 -0
  159. package/dist/utils/deletion/index.js +2 -0
  160. package/dist/utils/deletion/messages.d.ts +5 -0
  161. package/dist/utils/deletion/messages.js +29 -0
  162. package/dist/utils/deletion/types.d.ts +11 -0
  163. package/dist/utils/deletion/types.js +0 -0
  164. package/dist/utils/errorHandling.d.ts +54 -0
  165. package/dist/utils/errorHandling.js +79 -0
  166. package/dist/utils/hooks.d.ts +2 -0
  167. package/dist/utils/hooks.js +26 -0
  168. package/dist/utils/index.d.ts +2 -0
  169. package/dist/utils/index.js +2 -0
  170. package/dist/utils/proxyMiddleware.d.ts +18 -0
  171. package/dist/utils/proxyMiddleware.js +56 -0
  172. package/dist/utils/s3Client.d.ts +5 -0
  173. package/dist/utils/s3Client.js +35 -0
  174. package/dist/utils/useFeatures.d.ts +1 -0
  175. package/dist/utils/useFeatures.js +7 -0
  176. package/package.json +79 -0
@@ -0,0 +1,147 @@
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 { createTestWrapper } from "../../test/testUtils.js";
5
+ import { CreateFolderButton } from "../objects/CreateFolderButton.js";
6
+ describe("CreateFolderButton", ()=>{
7
+ const defaultProps = {
8
+ bucket: "test-bucket",
9
+ prefix: "test-prefix"
10
+ };
11
+ const renderCreateFolderButton = (props = {})=>{
12
+ const Wrapper = createTestWrapper();
13
+ return render(/*#__PURE__*/ jsx(Wrapper, {
14
+ children: /*#__PURE__*/ jsx(CreateFolderButton, {
15
+ ...defaultProps,
16
+ ...props
17
+ })
18
+ }));
19
+ };
20
+ it("renders button with default label", ()=>{
21
+ renderCreateFolderButton();
22
+ expect(screen.getByRole("button", {
23
+ name: /folder/i
24
+ })).toBeInTheDocument();
25
+ });
26
+ it("renders button with custom label", ()=>{
27
+ renderCreateFolderButton({
28
+ label: "Add New Folder"
29
+ });
30
+ expect(screen.getByRole("button", {
31
+ name: /add new folder/i
32
+ })).toBeInTheDocument();
33
+ });
34
+ it("opens modal when button is clicked", async ()=>{
35
+ renderCreateFolderButton();
36
+ fireEvent.click(screen.getByRole("button", {
37
+ name: /folder/i
38
+ }));
39
+ await waitFor(()=>{
40
+ expect(screen.getByText("Create a folder")).toBeInTheDocument();
41
+ });
42
+ });
43
+ it("closes modal when cancel button is clicked", async ()=>{
44
+ renderCreateFolderButton();
45
+ fireEvent.click(screen.getByRole("button", {
46
+ name: /folder/i
47
+ }));
48
+ await waitFor(()=>screen.getByText("Create a folder"));
49
+ fireEvent.click(screen.getByRole("button", {
50
+ name: /cancel/i
51
+ }));
52
+ await waitFor(()=>{
53
+ expect(screen.queryByText("Create a folder")).not.toBeInTheDocument();
54
+ });
55
+ });
56
+ it("disables save button when folder name is empty", async ()=>{
57
+ renderCreateFolderButton();
58
+ fireEvent.click(screen.getByRole("button", {
59
+ name: /folder/i
60
+ }));
61
+ await waitFor(()=>screen.getByText("Create a folder"));
62
+ expect(screen.getByRole("button", {
63
+ name: /save/i
64
+ })).toBeDisabled();
65
+ });
66
+ it("enables save button when valid folder name is entered", async ()=>{
67
+ renderCreateFolderButton();
68
+ fireEvent.click(screen.getByRole("button", {
69
+ name: /folder/i
70
+ }));
71
+ await waitFor(()=>screen.getByText("Create a folder"));
72
+ await user_event.type(screen.getByRole("textbox"), "valid-folder");
73
+ expect(screen.getByRole("button", {
74
+ name: /save/i
75
+ })).toBeEnabled();
76
+ });
77
+ it("disables save button and shows error when folder name starts with slash", async ()=>{
78
+ renderCreateFolderButton();
79
+ fireEvent.click(screen.getByRole("button", {
80
+ name: /folder/i
81
+ }));
82
+ await waitFor(()=>screen.getByText("Create a folder"));
83
+ await user_event.type(screen.getByRole("textbox"), "/invalid-folder");
84
+ expect(screen.getByRole("button", {
85
+ name: /save/i
86
+ })).toBeDisabled();
87
+ });
88
+ it("triggers mutation when save is clicked with valid folder name", async ()=>{
89
+ renderCreateFolderButton();
90
+ fireEvent.click(screen.getByRole("button", {
91
+ name: /folder/i
92
+ }));
93
+ await waitFor(()=>screen.getByText("Create a folder"));
94
+ await user_event.type(screen.getByRole("textbox"), "test-folder");
95
+ const saveButton = screen.getByRole("button", {
96
+ name: /save/i
97
+ });
98
+ expect(saveButton).toBeEnabled();
99
+ fireEvent.click(saveButton);
100
+ expect(screen.getByText("Create a folder")).toBeInTheDocument();
101
+ });
102
+ it("clears folder name when modal is closed", async ()=>{
103
+ renderCreateFolderButton();
104
+ fireEvent.click(screen.getByRole("button", {
105
+ name: /folder/i
106
+ }));
107
+ await waitFor(()=>screen.getByText("Create a folder"));
108
+ await user_event.type(screen.getByRole("textbox"), "test-folder");
109
+ fireEvent.click(screen.getByRole("button", {
110
+ name: /cancel/i
111
+ }));
112
+ await waitFor(()=>{
113
+ expect(screen.queryByText("Create a folder")).not.toBeInTheDocument();
114
+ });
115
+ fireEvent.click(screen.getByRole("button", {
116
+ name: /folder/i
117
+ }));
118
+ await waitFor(()=>screen.getByText("Create a folder"));
119
+ expect(screen.getByRole("textbox")).toHaveValue("");
120
+ });
121
+ it("shows info message about folder creation", async ()=>{
122
+ renderCreateFolderButton();
123
+ fireEvent.click(screen.getByRole("button", {
124
+ name: /folder/i
125
+ }));
126
+ await waitFor(()=>screen.getByText("Create a folder"));
127
+ expect(screen.getByText("Creating a folder")).toBeInTheDocument();
128
+ expect(screen.getByText(/ensure the folder name does not begin with a slash/i)).toBeInTheDocument();
129
+ });
130
+ it("handles folder name validation correctly", async ()=>{
131
+ renderCreateFolderButton();
132
+ fireEvent.click(screen.getByRole("button", {
133
+ name: /folder/i
134
+ }));
135
+ await waitFor(()=>screen.getByText("Create a folder"));
136
+ const input = screen.getByRole("textbox");
137
+ const saveButton = screen.getByRole("button", {
138
+ name: /save/i
139
+ });
140
+ expect(saveButton).toBeDisabled();
141
+ await user_event.type(input, "valid-folder");
142
+ expect(saveButton).toBeEnabled();
143
+ await user_event.clear(input);
144
+ await user_event.type(input, "/invalid");
145
+ expect(saveButton).toBeDisabled();
146
+ });
147
+ });
@@ -0,0 +1,272 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
3
+ import { createTestWrapper } from "../../test/testUtils.js";
4
+ import { DeleteBucketButton } from "../buckets/DeleteBucketButton.js";
5
+ import { useIsBucketEmpty } from "../../hooks/useIsBucketEmpty.js";
6
+ import { useDeleteBucket } from "../../hooks/bucketOperations.js";
7
+ jest.mock("../../hooks/useIsBucketEmpty");
8
+ jest.mock("../../hooks/bucketOperations");
9
+ const mockUseIsBucketEmpty = jest.mocked(useIsBucketEmpty);
10
+ const mockUseDeleteBucket = jest.mocked(useDeleteBucket);
11
+ const renderDeleteBucketButton = (props = {})=>{
12
+ const Wrapper = createTestWrapper();
13
+ return render(/*#__PURE__*/ jsx(Wrapper, {
14
+ children: /*#__PURE__*/ jsx(DeleteBucketButton, {
15
+ bucketName: "test-bucket",
16
+ ...props
17
+ })
18
+ }));
19
+ };
20
+ const mockDeleteBucket = jest.fn();
21
+ const mockHookDefaults = ()=>{
22
+ mockUseIsBucketEmpty.mockReturnValue({
23
+ isEmpty: true,
24
+ isLoading: false,
25
+ error: null
26
+ });
27
+ mockUseDeleteBucket.mockReturnValue({
28
+ mutate: mockDeleteBucket,
29
+ status: "idle"
30
+ });
31
+ };
32
+ describe("DeleteBucketButton", ()=>{
33
+ beforeEach(()=>{
34
+ jest.clearAllMocks();
35
+ mockHookDefaults();
36
+ });
37
+ it("renders delete button with correct label", ()=>{
38
+ renderDeleteBucketButton();
39
+ expect(screen.getByRole("button", {
40
+ name: /delete bucket/i
41
+ })).toBeInTheDocument();
42
+ });
43
+ it("enables button when bucket is empty", ()=>{
44
+ mockUseIsBucketEmpty.mockReturnValue({
45
+ isEmpty: true,
46
+ isLoading: false,
47
+ error: null
48
+ });
49
+ renderDeleteBucketButton();
50
+ const button = screen.getByRole("button", {
51
+ name: /delete bucket/i
52
+ });
53
+ expect(button).not.toBeDisabled();
54
+ });
55
+ it("disables button when bucket is not empty", ()=>{
56
+ mockUseIsBucketEmpty.mockReturnValue({
57
+ isEmpty: false,
58
+ isLoading: false,
59
+ error: null
60
+ });
61
+ renderDeleteBucketButton();
62
+ const button = screen.getByRole("button", {
63
+ name: /delete bucket/i
64
+ });
65
+ expect(button).toBeDisabled();
66
+ });
67
+ it("disables button when checking if bucket is empty", ()=>{
68
+ mockUseIsBucketEmpty.mockReturnValue({
69
+ isEmpty: null,
70
+ isLoading: true,
71
+ error: null
72
+ });
73
+ renderDeleteBucketButton();
74
+ const button = screen.getByRole("button", {
75
+ name: /delete bucket/i
76
+ });
77
+ expect(button).toBeDisabled();
78
+ });
79
+ it("disables button when disabled prop is true", ()=>{
80
+ renderDeleteBucketButton({
81
+ disabled: true
82
+ });
83
+ const button = screen.getByRole("button", {
84
+ name: /delete bucket/i
85
+ });
86
+ expect(button).toBeDisabled();
87
+ });
88
+ it("shows tooltip when bucket is not empty", ()=>{
89
+ mockUseIsBucketEmpty.mockReturnValue({
90
+ isEmpty: false,
91
+ isLoading: false,
92
+ error: null
93
+ });
94
+ renderDeleteBucketButton();
95
+ expect(screen.getByRole("button", {
96
+ name: /delete bucket/i
97
+ })).toBeInTheDocument();
98
+ });
99
+ it("shows checking tooltip when loading", ()=>{
100
+ mockUseIsBucketEmpty.mockReturnValue({
101
+ isEmpty: null,
102
+ isLoading: true,
103
+ error: null
104
+ });
105
+ renderDeleteBucketButton();
106
+ expect(screen.getByRole("button", {
107
+ name: /delete bucket/i
108
+ })).toBeInTheDocument();
109
+ });
110
+ it("opens confirmation modal when button is clicked", ()=>{
111
+ renderDeleteBucketButton();
112
+ const button = screen.getByRole("button", {
113
+ name: /delete bucket/i
114
+ });
115
+ fireEvent.click(button);
116
+ expect(screen.getByText("Confirmation")).toBeInTheDocument();
117
+ expect(screen.getByText(/are you sure you want to delete bucket: test-bucket/i)).toBeInTheDocument();
118
+ });
119
+ it("shows cancel and delete buttons in modal", ()=>{
120
+ renderDeleteBucketButton();
121
+ const button = screen.getByRole("button", {
122
+ name: /delete bucket/i
123
+ });
124
+ fireEvent.click(button);
125
+ expect(screen.getByRole("button", {
126
+ name: /cancel/i
127
+ })).toBeInTheDocument();
128
+ expect(screen.getByRole("button", {
129
+ name: /^delete$/i
130
+ })).toBeInTheDocument();
131
+ });
132
+ it("closes modal when cancel is clicked", ()=>{
133
+ renderDeleteBucketButton();
134
+ const deleteButton = screen.getByRole("button", {
135
+ name: /delete bucket/i
136
+ });
137
+ fireEvent.click(deleteButton);
138
+ const cancelButton = screen.getByRole("button", {
139
+ name: /cancel/i
140
+ });
141
+ fireEvent.click(cancelButton);
142
+ expect(screen.queryByText("Confirmation")).not.toBeInTheDocument();
143
+ });
144
+ it("calls delete mutation when delete is confirmed", ()=>{
145
+ renderDeleteBucketButton();
146
+ const deleteButton = screen.getByRole("button", {
147
+ name: /delete bucket/i
148
+ });
149
+ fireEvent.click(deleteButton);
150
+ const confirmButton = screen.getByRole("button", {
151
+ name: /^delete$/i
152
+ });
153
+ fireEvent.click(confirmButton);
154
+ expect(mockDeleteBucket).toHaveBeenCalledWith({
155
+ Bucket: "test-bucket"
156
+ }, expect.objectContaining({
157
+ onSuccess: expect.any(Function),
158
+ onError: expect.any(Function)
159
+ }));
160
+ });
161
+ it("calls onDeleteSuccess callback when delete succeeds", ()=>{
162
+ const onDeleteSuccess = jest.fn();
163
+ renderDeleteBucketButton({
164
+ onDeleteSuccess
165
+ });
166
+ const deleteButton = screen.getByRole("button", {
167
+ name: /delete bucket/i
168
+ });
169
+ fireEvent.click(deleteButton);
170
+ const confirmButton = screen.getByRole("button", {
171
+ name: /^delete$/i
172
+ });
173
+ fireEvent.click(confirmButton);
174
+ const successCallback = mockDeleteBucket.mock.calls[0][1].onSuccess;
175
+ successCallback();
176
+ expect(onDeleteSuccess).toHaveBeenCalledWith("test-bucket");
177
+ });
178
+ it("calls onDeleteError callback when delete fails", ()=>{
179
+ const onDeleteError = jest.fn();
180
+ const testError = new Error("Delete failed");
181
+ renderDeleteBucketButton({
182
+ onDeleteError
183
+ });
184
+ const deleteButton = screen.getByRole("button", {
185
+ name: /delete bucket/i
186
+ });
187
+ fireEvent.click(deleteButton);
188
+ const confirmButton = screen.getByRole("button", {
189
+ name: /^delete$/i
190
+ });
191
+ fireEvent.click(confirmButton);
192
+ const errorCallback = mockDeleteBucket.mock.calls[0][1].onError;
193
+ errorCallback(testError);
194
+ expect(onDeleteError).toHaveBeenCalledWith(testError);
195
+ });
196
+ it("handles non-Error objects in onError callback", ()=>{
197
+ const onDeleteError = jest.fn();
198
+ renderDeleteBucketButton({
199
+ onDeleteError
200
+ });
201
+ const deleteButton = screen.getByRole("button", {
202
+ name: /delete bucket/i
203
+ });
204
+ fireEvent.click(deleteButton);
205
+ const confirmButton = screen.getByRole("button", {
206
+ name: /^delete$/i
207
+ });
208
+ fireEvent.click(confirmButton);
209
+ const errorCallback = mockDeleteBucket.mock.calls[0][1].onError;
210
+ errorCallback("string error");
211
+ expect(onDeleteError).toHaveBeenCalledWith(new Error("Delete failed"));
212
+ });
213
+ it("closes modal when delete succeeds", async ()=>{
214
+ renderDeleteBucketButton();
215
+ const deleteButton = screen.getByRole("button", {
216
+ name: /delete bucket/i
217
+ });
218
+ fireEvent.click(deleteButton);
219
+ expect(screen.getByText("Confirmation")).toBeInTheDocument();
220
+ const confirmButton = screen.getByRole("button", {
221
+ name: /^delete$/i
222
+ });
223
+ fireEvent.click(confirmButton);
224
+ const successCallback = mockDeleteBucket.mock.calls[0][1].onSuccess;
225
+ successCallback();
226
+ await waitFor(()=>{
227
+ expect(screen.queryByText("Confirmation")).not.toBeInTheDocument();
228
+ });
229
+ });
230
+ it("shows loading state in delete button when deletion is pending", ()=>{
231
+ mockUseDeleteBucket.mockReturnValue({
232
+ mutate: mockDeleteBucket,
233
+ status: "pending"
234
+ });
235
+ renderDeleteBucketButton();
236
+ const deleteButton = screen.getByRole("button", {
237
+ name: /delete bucket/i
238
+ });
239
+ fireEvent.click(deleteButton);
240
+ const confirmButton = screen.getByRole("button", {
241
+ name: /^delete$/i
242
+ });
243
+ expect(confirmButton).toBeDisabled();
244
+ });
245
+ it("works without optional callbacks", ()=>{
246
+ expect(()=>{
247
+ renderDeleteBucketButton();
248
+ const deleteButton = screen.getByRole("button", {
249
+ name: /delete bucket/i
250
+ });
251
+ fireEvent.click(deleteButton);
252
+ const confirmButton = screen.getByRole("button", {
253
+ name: /^delete$/i
254
+ });
255
+ fireEvent.click(confirmButton);
256
+ const successCallback = mockDeleteBucket.mock.calls[0][1].onSuccess;
257
+ const errorCallback = mockDeleteBucket.mock.calls[0][1].onError;
258
+ successCallback();
259
+ errorCallback(new Error("test"));
260
+ }).not.toThrow();
261
+ });
262
+ it("displays correct bucket name in confirmation message", ()=>{
263
+ renderDeleteBucketButton({
264
+ bucketName: "my-special-bucket"
265
+ });
266
+ const deleteButton = screen.getByRole("button", {
267
+ name: /delete bucket/i
268
+ });
269
+ fireEvent.click(deleteButton);
270
+ expect(screen.getByText(/are you sure you want to delete bucket: my-special-bucket/i)).toBeInTheDocument();
271
+ });
272
+ });