storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/browser/app/js/objects/__tests__/actions.test.js (about) 1 /* 2 * MinIO Cloud Storage (C) 2018 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import configureStore from "redux-mock-store" 18 import thunk from "redux-thunk" 19 import * as actionsObjects from "../actions" 20 import * as alertActions from "../../alert/actions" 21 import { 22 minioBrowserPrefix, 23 SORT_BY_NAME, 24 SORT_ORDER_ASC, 25 SORT_BY_LAST_MODIFIED, 26 SORT_ORDER_DESC 27 } from "../../constants" 28 import history from "../../history" 29 30 jest.mock("../../web", () => ({ 31 LoggedIn: jest 32 .fn(() => true) 33 .mockReturnValueOnce(true) 34 .mockReturnValueOnce(false) 35 .mockReturnValueOnce(true) 36 .mockReturnValueOnce(true) 37 .mockReturnValueOnce(true) 38 .mockReturnValueOnce(false), 39 ListObjects: jest.fn(({ bucketName }) => { 40 if (bucketName === "test-deny") { 41 return Promise.reject({ 42 message: "listobjects is denied" 43 }) 44 } else { 45 return Promise.resolve({ 46 objects: [{ name: "test1" }, { name: "test2" }], 47 writable: false 48 }) 49 } 50 }), 51 RemoveObject: jest.fn(({ bucketName, objects }) => { 52 if (!bucketName) { 53 return Promise.reject({ message: "Invalid bucket" }) 54 } 55 return Promise.resolve({}) 56 }), 57 PresignedGet: jest.fn(({ bucket, object }) => { 58 if (!bucket) { 59 return Promise.reject({ message: "Invalid bucket" }) 60 } 61 return Promise.resolve({ url: "https://test.com/bk1/pre1/b.txt" }) 62 }), 63 CreateURLToken: jest 64 .fn() 65 .mockImplementationOnce(() => { 66 return Promise.resolve({ token: "test" }) 67 }) 68 .mockImplementationOnce(() => { 69 return Promise.reject({ message: "Error in creating token" }) 70 }) 71 .mockImplementationOnce(() => { 72 return Promise.resolve({ token: "test" }) 73 }) 74 .mockImplementationOnce(() => { 75 return Promise.resolve({ token: "test" }) 76 }), 77 GetBucketPolicy: jest.fn(({ bucketName, prefix }) => { 78 if (!bucketName) { 79 return Promise.reject({ message: "Invalid bucket" }) 80 } 81 if (bucketName === 'test-public') return Promise.resolve({ policy: 'readonly' }) 82 return Promise.resolve({}) 83 }) 84 })) 85 86 const middlewares = [thunk] 87 const mockStore = configureStore(middlewares) 88 89 describe("Objects actions", () => { 90 it("creates objects/SET_LIST action", () => { 91 const store = mockStore() 92 const expectedActions = [ 93 { 94 type: "objects/SET_LIST", 95 objects: [{ name: "test1" }, { name: "test2" }] 96 } 97 ] 98 store.dispatch( 99 actionsObjects.setList([{ name: "test1" }, { name: "test2" }]) 100 ) 101 const actions = store.getActions() 102 expect(actions).toEqual(expectedActions) 103 }) 104 105 it("creates objects/SET_SORT_BY action", () => { 106 const store = mockStore() 107 const expectedActions = [ 108 { 109 type: "objects/SET_SORT_BY", 110 sortBy: SORT_BY_NAME 111 } 112 ] 113 store.dispatch(actionsObjects.setSortBy(SORT_BY_NAME)) 114 const actions = store.getActions() 115 expect(actions).toEqual(expectedActions) 116 }) 117 118 it("creates objects/SET_SORT_ORDER action", () => { 119 const store = mockStore() 120 const expectedActions = [ 121 { 122 type: "objects/SET_SORT_ORDER", 123 sortOrder: SORT_ORDER_ASC 124 } 125 ] 126 store.dispatch(actionsObjects.setSortOrder(SORT_ORDER_ASC)) 127 const actions = store.getActions() 128 expect(actions).toEqual(expectedActions) 129 }) 130 131 it("creates objects/SET_LIST after fetching the objects", () => { 132 const store = mockStore({ 133 buckets: { currentBucket: "bk1" }, 134 objects: { currentPrefix: "" } 135 }) 136 const expectedActions = [ 137 { 138 type: "objects/RESET_LIST" 139 }, 140 { listLoading: true, type: "objects/SET_LIST_LOADING" }, 141 { 142 type: "objects/SET_SORT_BY", 143 sortBy: SORT_BY_LAST_MODIFIED 144 }, 145 { 146 type: "objects/SET_SORT_ORDER", 147 sortOrder: SORT_ORDER_DESC 148 }, 149 { 150 type: "objects/SET_LIST", 151 objects: [{ name: "test2" }, { name: "test1" }] 152 }, 153 { 154 type: "objects/SET_PREFIX_WRITABLE", 155 prefixWritable: false 156 }, 157 { listLoading: false, type: "objects/SET_LIST_LOADING" } 158 ] 159 return store.dispatch(actionsObjects.fetchObjects()).then(() => { 160 const actions = store.getActions() 161 expect(actions).toEqual(expectedActions) 162 }) 163 }) 164 165 it("creates objects/RESET_LIST after failing to fetch the objects from bucket with ListObjects denied for LoggedIn users", () => { 166 const store = mockStore({ 167 buckets: { currentBucket: "test-deny" }, 168 objects: { currentPrefix: "" } 169 }) 170 const expectedActions = [ 171 { 172 type: "objects/RESET_LIST" 173 }, 174 { listLoading: true, type: "objects/SET_LIST_LOADING" }, 175 { 176 type: "alert/SET", 177 alert: { 178 type: "danger", 179 message: "listobjects is denied", 180 id: alertActions.alertId, 181 autoClear: true 182 } 183 }, 184 { 185 type: "objects/RESET_LIST" 186 }, 187 { listLoading: false, type: "objects/SET_LIST_LOADING" } 188 ] 189 return store.dispatch(actionsObjects.fetchObjects()).then(() => { 190 const actions = store.getActions() 191 expect(actions).toEqual(expectedActions) 192 }) 193 }) 194 195 it("redirect to login after failing to fetch the objects from bucket for non-LoggedIn users", () => { 196 const store = mockStore({ 197 buckets: { currentBucket: "test-deny" }, 198 objects: { currentPrefix: "" } 199 }) 200 return store.dispatch(actionsObjects.fetchObjects()).then(() => { 201 expect(history.location.pathname.endsWith("/login")).toBeTruthy() 202 }) 203 }) 204 205 it("creates objects/SET_SORT_BY and objects/SET_SORT_ORDER when sortObjects is called", () => { 206 const store = mockStore({ 207 objects: { 208 list: [], 209 sortBy: "", 210 sortOrder: SORT_ORDER_ASC 211 } 212 }) 213 const expectedActions = [ 214 { 215 type: "objects/SET_SORT_BY", 216 sortBy: SORT_BY_NAME 217 }, 218 { 219 type: "objects/SET_SORT_ORDER", 220 sortOrder: SORT_ORDER_ASC 221 }, 222 { 223 type: "objects/SET_LIST", 224 objects: [] 225 } 226 ] 227 store.dispatch(actionsObjects.sortObjects(SORT_BY_NAME)) 228 const actions = store.getActions() 229 expect(actions).toEqual(expectedActions) 230 }) 231 232 it("should update browser url and creates objects/SET_CURRENT_PREFIX and objects/CHECKED_LIST_RESET actions when selectPrefix is called", () => { 233 const store = mockStore({ 234 buckets: { currentBucket: "test" }, 235 objects: { currentPrefix: "" } 236 }) 237 const expectedActions = [ 238 { type: "objects/SET_CURRENT_PREFIX", prefix: "abc/" }, 239 { 240 type: "objects/RESET_LIST" 241 }, 242 { listLoading: true, type: "objects/SET_LIST_LOADING" }, 243 { type: "objects/CHECKED_LIST_RESET" } 244 ] 245 store.dispatch(actionsObjects.selectPrefix("abc/")) 246 const actions = store.getActions() 247 expect(actions).toEqual(expectedActions) 248 expect(window.location.pathname.endsWith("/test/abc/")).toBeTruthy() 249 }) 250 251 it("create objects/SET_PREFIX_WRITABLE action", () => { 252 const store = mockStore() 253 const expectedActions = [ 254 { type: "objects/SET_PREFIX_WRITABLE", prefixWritable: true } 255 ] 256 store.dispatch(actionsObjects.setPrefixWritable(true)) 257 const actions = store.getActions() 258 expect(actions).toEqual(expectedActions) 259 }) 260 261 it("creates objects/REMOVE action", () => { 262 const store = mockStore() 263 const expectedActions = [{ type: "objects/REMOVE", object: "obj1" }] 264 store.dispatch(actionsObjects.removeObject("obj1")) 265 const actions = store.getActions() 266 expect(actions).toEqual(expectedActions) 267 }) 268 269 it("creates objects/REMOVE action when object is deleted", () => { 270 const store = mockStore({ 271 buckets: { currentBucket: "test" }, 272 objects: { currentPrefix: "pre1/" } 273 }) 274 const expectedActions = [{ type: "objects/REMOVE", object: "obj1" }] 275 store.dispatch(actionsObjects.deleteObject("obj1")).then(() => { 276 const actions = store.getActions() 277 expect(actions).toEqual(expectedActions) 278 }) 279 }) 280 281 it("creates alert/SET action when invalid bucket is provided", () => { 282 const store = mockStore({ 283 buckets: { currentBucket: "" }, 284 objects: { currentPrefix: "pre1/" } 285 }) 286 const expectedActions = [ 287 { 288 type: "alert/SET", 289 alert: { 290 type: "danger", 291 message: "Invalid bucket", 292 id: alertActions.alertId 293 } 294 } 295 ] 296 return store.dispatch(actionsObjects.deleteObject("obj1")).then(() => { 297 const actions = store.getActions() 298 expect(actions).toEqual(expectedActions) 299 }) 300 }) 301 302 it("creates objects/SET_SHARE_OBJECT action for showShareObject", () => { 303 const store = mockStore() 304 const expectedActions = [ 305 { 306 type: "objects/SET_SHARE_OBJECT", 307 show: true, 308 object: "b.txt", 309 url: "test", 310 showExpiryDate: true 311 } 312 ] 313 store.dispatch(actionsObjects.showShareObject("b.txt", "test")) 314 const actions = store.getActions() 315 expect(actions).toEqual(expectedActions) 316 }) 317 318 it("creates objects/SET_SHARE_OBJECT action for hideShareObject", () => { 319 const store = mockStore() 320 const expectedActions = [ 321 { 322 type: "objects/SET_SHARE_OBJECT", 323 show: false, 324 object: "", 325 url: "" 326 } 327 ] 328 store.dispatch(actionsObjects.hideShareObject()) 329 const actions = store.getActions() 330 expect(actions).toEqual(expectedActions) 331 }) 332 333 it("creates objects/SET_SHARE_OBJECT when object is shared", () => { 334 const store = mockStore({ 335 buckets: { currentBucket: "bk1" }, 336 objects: { currentPrefix: "pre1/" }, 337 browser: { serverInfo: {} }, 338 }) 339 const expectedActions = [ 340 { 341 type: "objects/SET_SHARE_OBJECT", 342 show: true, 343 object: "a.txt", 344 url: "https://test.com/bk1/pre1/b.txt", 345 showExpiryDate: true 346 }, 347 { 348 type: "alert/SET", 349 alert: { 350 type: "success", 351 message: "Object shared. Expires in 1 days 0 hours 0 minutes", 352 id: alertActions.alertId 353 } 354 } 355 ] 356 return store 357 .dispatch(actionsObjects.shareObject("a.txt", 1, 0, 0)) 358 .then(() => { 359 const actions = store.getActions() 360 expect(actions).toEqual(expectedActions) 361 }) 362 }) 363 364 it("creates objects/SET_SHARE_OBJECT when object is shared with public link", () => { 365 const store = mockStore({ 366 buckets: { currentBucket: "test-public" }, 367 objects: { currentPrefix: "pre1/" }, 368 browser: { serverInfo: { info: { domains: ['public.com'] }} }, 369 }) 370 const expectedActions = [ 371 { 372 type: "objects/SET_SHARE_OBJECT", 373 show: true, 374 object: "a.txt", 375 url: "public.com/test-public/pre1/a.txt", 376 showExpiryDate: false 377 }, 378 { 379 type: "alert/SET", 380 alert: { 381 type: "success", 382 message: "Object shared.", 383 id: alertActions.alertId 384 } 385 } 386 ] 387 return store 388 .dispatch(actionsObjects.shareObject("a.txt", 1, 0, 0)) 389 .then(() => { 390 const actions = store.getActions() 391 expect(actions).toEqual(expectedActions) 392 }) 393 }) 394 395 it("creates alert/SET when shareObject is failed", () => { 396 const store = mockStore({ 397 buckets: { currentBucket: "" }, 398 objects: { currentPrefix: "pre1/" }, 399 browser: { serverInfo: {} }, 400 }) 401 const expectedActions = [ 402 { 403 type: "alert/SET", 404 alert: { 405 type: "danger", 406 message: "Invalid bucket", 407 id: alertActions.alertId 408 } 409 } 410 ] 411 return store 412 .dispatch(actionsObjects.shareObject("a.txt", 1, 0, 0)) 413 .then(() => { 414 const actions = store.getActions() 415 expect(actions).toEqual(expectedActions) 416 }) 417 }) 418 419 describe("Download object", () => { 420 it("should download the object non-LoggedIn users", () => { 421 const setLocation = jest.fn() 422 Object.defineProperty(window, "location", { 423 set(url) { 424 setLocation(url) 425 }, 426 get() { 427 return { 428 origin: "http://localhost:8080" 429 } 430 } 431 }) 432 const store = mockStore({ 433 buckets: { currentBucket: "bk1" }, 434 objects: { currentPrefix: "pre1/" } 435 }) 436 store.dispatch(actionsObjects.downloadObject("obj1")) 437 const url = `${ 438 window.location.origin 439 }${minioBrowserPrefix}/download/bk1/${encodeURI("pre1/obj1")}?token=` 440 expect(setLocation).toHaveBeenCalledWith(url) 441 }) 442 443 it("should download the object for LoggedIn users", () => { 444 const setLocation = jest.fn() 445 Object.defineProperty(window, "location", { 446 set(url) { 447 setLocation(url) 448 }, 449 get() { 450 return { 451 origin: "http://localhost:8080" 452 } 453 } 454 }) 455 const store = mockStore({ 456 buckets: { currentBucket: "bk1" }, 457 objects: { currentPrefix: "pre1/" } 458 }) 459 return store.dispatch(actionsObjects.downloadObject("obj1")).then(() => { 460 const url = `${ 461 window.location.origin 462 }${minioBrowserPrefix}/download/bk1/${encodeURI( 463 "pre1/obj1" 464 )}?token=test` 465 expect(setLocation).toHaveBeenCalledWith(url) 466 }) 467 }) 468 469 it("create alert/SET action when CreateUrlToken fails", () => { 470 const store = mockStore({ 471 buckets: { currentBucket: "bk1" }, 472 objects: { currentPrefix: "pre1/" } 473 }) 474 const expectedActions = [ 475 { 476 type: "alert/SET", 477 alert: { 478 type: "danger", 479 message: "Error in creating token", 480 id: alertActions.alertId 481 } 482 } 483 ] 484 return store.dispatch(actionsObjects.downloadObject("obj1")).then(() => { 485 const actions = store.getActions() 486 expect(actions).toEqual(expectedActions) 487 }) 488 }) 489 }) 490 491 it("should download prefix", () => { 492 const open = jest.fn() 493 const send = jest.fn() 494 const xhrMockClass = () => ({ 495 open: open, 496 send: send 497 }) 498 window.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass) 499 500 const store = mockStore({ 501 buckets: { currentBucket: "bk1" }, 502 objects: { currentPrefix: "pre1/" } 503 }) 504 return store.dispatch(actionsObjects.downloadPrefix("pre2/")).then(() => { 505 const requestUrl = `${ 506 location.origin 507 }${minioBrowserPrefix}/zip?token=test` 508 expect(open).toHaveBeenCalledWith("POST", requestUrl, true) 509 expect(send).toHaveBeenCalledWith( 510 JSON.stringify({ 511 bucketName: "bk1", 512 prefix: "pre1/", 513 objects: ["pre2/"] 514 }) 515 ) 516 }) 517 }) 518 519 it("creates objects/CHECKED_LIST_ADD action", () => { 520 const store = mockStore() 521 const expectedActions = [ 522 { 523 type: "objects/CHECKED_LIST_ADD", 524 object: "obj1" 525 } 526 ] 527 store.dispatch(actionsObjects.checkObject("obj1")) 528 const actions = store.getActions() 529 expect(actions).toEqual(expectedActions) 530 }) 531 532 it("creates objects/CHECKED_LIST_REMOVE action", () => { 533 const store = mockStore() 534 const expectedActions = [ 535 { 536 type: "objects/CHECKED_LIST_REMOVE", 537 object: "obj1" 538 } 539 ] 540 store.dispatch(actionsObjects.uncheckObject("obj1")) 541 const actions = store.getActions() 542 expect(actions).toEqual(expectedActions) 543 }) 544 545 it("creates objects/CHECKED_LIST_RESET action", () => { 546 const store = mockStore() 547 const expectedActions = [ 548 { 549 type: "objects/CHECKED_LIST_RESET" 550 } 551 ] 552 store.dispatch(actionsObjects.resetCheckedList()) 553 const actions = store.getActions() 554 expect(actions).toEqual(expectedActions) 555 }) 556 557 it("should download checked objects", () => { 558 const open = jest.fn() 559 const send = jest.fn() 560 const xhrMockClass = () => ({ 561 open: open, 562 send: send 563 }) 564 window.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass) 565 566 const store = mockStore({ 567 buckets: { currentBucket: "bk1" }, 568 objects: { currentPrefix: "pre1/", checkedList: ["obj1"] } 569 }) 570 return store.dispatch(actionsObjects.downloadCheckedObjects()).then(() => { 571 const requestUrl = `${ 572 location.origin 573 }${minioBrowserPrefix}/zip?token=test` 574 expect(open).toHaveBeenCalledWith("POST", requestUrl, true) 575 expect(send).toHaveBeenCalledWith( 576 JSON.stringify({ 577 bucketName: "bk1", 578 prefix: "pre1/", 579 objects: ["obj1"] 580 }) 581 ) 582 }) 583 }) 584 })