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  })