From 05294d4f1ea57d533ab1f3147382b50d3246f1c9 Mon Sep 17 00:00:00 2001
From: Jannik Stehle <jannik.stehle@gmail.com>
Date: Wed, 4 Jan 2023 13:16:09 +0100
Subject: [PATCH] Add unit tests

---
 .../src/components/Spaces/SpacesList.vue      |  20 +--
 .../unit/components/Spaces/SpacesList.spec.ts |  90 +++++++++++++
 .../__snapshots__/SpacesList.spec.ts.snap     | 126 ++++++++++++++++++
 .../tests/unit/views/Spaces.spec.ts           | 115 ++++++++++++++++
 .../views/__snapshots__/Spaces.spec.ts.snap   |  56 ++++++++
 5 files changed, 397 insertions(+), 10 deletions(-)
 create mode 100644 packages/web-app-admin-settings/tests/unit/components/Spaces/SpacesList.spec.ts
 create mode 100644 packages/web-app-admin-settings/tests/unit/components/Spaces/__snapshots__/SpacesList.spec.ts.snap
 create mode 100644 packages/web-app-admin-settings/tests/unit/views/Spaces.spec.ts
 create mode 100644 packages/web-app-admin-settings/tests/unit/views/__snapshots__/Spaces.spec.ts.snap

diff --git a/packages/web-app-admin-settings/src/components/Spaces/SpacesList.vue b/packages/web-app-admin-settings/src/components/Spaces/SpacesList.vue
index 9c65f4e70c3..46a751e840e 100644
--- a/packages/web-app-admin-settings/src/components/Spaces/SpacesList.vue
+++ b/packages/web-app-admin-settings/src/components/Spaces/SpacesList.vue
@@ -118,24 +118,24 @@ export default defineComponent({
 
         switch (prop) {
           case 'members':
-            a = getMemberCount(s1).toString() || ''
-            b = getMemberCount(s2).toString() || ''
+            a = getMemberCount(s1).toString()
+            b = getMemberCount(s2).toString()
             break
           case 'availableQuota':
-            a = getAvailableQuota(s1).toString() || ''
-            b = getAvailableQuota(s2).toString() || ''
+            a = getAvailableQuota(s1).toString()
+            b = getAvailableQuota(s2).toString()
             break
           case 'usedQuota':
-            a = getUsedQuota(s1).toString() || ''
-            b = getUsedQuota(s2).toString() || ''
+            a = getUsedQuota(s1).toString()
+            b = getUsedQuota(s2).toString()
             break
           case 'remainingQuota':
-            a = getRemainingQuota(s1).toString() || ''
-            b = getRemainingQuota(s2).toString() || ''
+            a = getRemainingQuota(s1).toString()
+            b = getRemainingQuota(s2).toString()
             break
           case 'status':
-            a = s1.disabled.toString() || ''
-            b = s2.disabled.toString() || ''
+            a = s1.disabled.toString()
+            b = s2.disabled.toString()
             break
           default:
             a = s1[prop] || ''
diff --git a/packages/web-app-admin-settings/tests/unit/components/Spaces/SpacesList.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Spaces/SpacesList.spec.ts
new file mode 100644
index 00000000000..7b6f5300dc6
--- /dev/null
+++ b/packages/web-app-admin-settings/tests/unit/components/Spaces/SpacesList.spec.ts
@@ -0,0 +1,90 @@
+import SpacesList from '../../../../src/components/Spaces/SpacesList.vue'
+import { defaultPlugins, mount, shallowMount } from 'web-test-helpers'
+
+const spaceMocks = [
+  {
+    id: '1',
+    name: '1 Some space',
+    disabled: false,
+    spaceRoles: {
+      manager: ['user1'],
+      editor: [],
+      viewer: []
+    },
+    spaceQuota: {
+      total: 1000000000,
+      used: 0,
+      remaining: 1000000000
+    }
+  },
+  {
+    id: '2',
+    name: '2 Another space',
+    disabled: true,
+    spaceRoles: {
+      manager: ['user1'],
+      editor: ['user2'],
+      viewer: ['user3']
+    },
+    spaceQuota: {
+      total: 2000000000,
+      used: 500000000,
+      remaining: 1500000000
+    }
+  }
+]
+
+const selectors = {
+  ocTableStub: 'oc-table-stub'
+}
+
+describe('SpacesList', () => {
+  it('should render all spaces in a table', () => {
+    const { wrapper } = getWrapper({ spaces: [spaceMocks[0]] })
+    expect(wrapper.html()).toMatchSnapshot()
+  })
+  it.each(['name', 'members', 'availableQuota', 'usedQuota', 'remainingQuota', 'status'])(
+    'sorts by property "%s"',
+    async (prop) => {
+      const { wrapper } = getWrapper({ mountType: shallowMount, spaces: spaceMocks })
+      wrapper.vm.sortBy = prop
+      await wrapper.vm.$nextTick()
+      expect(wrapper.find(selectors.ocTableStub).props().data[0].id).toBe(spaceMocks[0].id)
+      wrapper.vm.sortDir = 'desc'
+      await wrapper.vm.$nextTick()
+      expect(wrapper.find(selectors.ocTableStub).props().data[0].id).toBe(spaceMocks[1].id)
+    }
+  )
+  it('should set the sort parameters accordingly when calling "handleSort"', () => {
+    const { wrapper } = getWrapper({ spaces: [spaceMocks[0]] })
+    const sortBy = 'members'
+    const sortDir = 'desc'
+    wrapper.vm.handleSort({ sortBy, sortDir })
+    expect(wrapper.vm.sortBy).toEqual(sortBy)
+    expect(wrapper.vm.sortDir).toEqual(sortDir)
+  })
+  it('emits events on file click', () => {
+    const { wrapper } = getWrapper({ spaces: [spaceMocks[0]] })
+    wrapper.vm.fileClicked(spaceMocks[0])
+    expect(wrapper.emitted().toggleUnSelectAllSpaces.length).toBeTruthy()
+    expect(wrapper.emitted().toggleSelectSpace).toBeTruthy()
+  })
+})
+
+function getWrapper({ mountType = mount, spaces = [], selectedSpaces = [] } = {}) {
+  return {
+    wrapper: mountType(SpacesList, {
+      props: {
+        spaces,
+        selectedSpaces,
+        headerPosition: 0
+      },
+      global: {
+        plugins: [...defaultPlugins()],
+        stubs: {
+          OcCheckbox: true
+        }
+      }
+    })
+  }
+}
diff --git a/packages/web-app-admin-settings/tests/unit/components/Spaces/__snapshots__/SpacesList.spec.ts.snap b/packages/web-app-admin-settings/tests/unit/components/Spaces/__snapshots__/SpacesList.spec.ts.snap
new file mode 100644
index 00000000000..327ff29f1b0
--- /dev/null
+++ b/packages/web-app-admin-settings/tests/unit/components/Spaces/__snapshots__/SpacesList.spec.ts.snap
@@ -0,0 +1,126 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SpacesList should render all spaces in a table 1`] = `
+<div>
+  <table class="spaces-table oc-table oc-table-hover oc-table-sticky">
+    <thead class="oc-thead">
+      <tr class="oc-table-header-row" tabindex="-1">
+        <th class="oc-th oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-shrink oc-text-nowrap oc-table-header-cell oc-table-header-cell-select oc-pl-s " style="top: 0px;">
+          <span class="oc-table-thead-content">
+            <occheckbox-stub class="oc-ml-s" hidelabel="true" id="oc-checkbox-1" label="Select all spaces" size="large"></occheckbox-stub>
+          </span>
+          <!---->
+        </th>
+        <th class="oc-th oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-shrink oc-text-nowrap oc-table-header-cell oc-table-header-cell-icon" style="top: 0px;">
+          <span class="oc-table-thead-content header-text"></span>
+          <!---->
+        </th>
+        <th aria-sort="ascending" class="oc-th oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-text-nowrap oc-table-header-cell oc-table-header-cell-name" style="top: 0px;">
+          <span class="oc-table-thead-content header-text">Name</span>
+          <button aria-label="Sort by name" class="oc-button-sort oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-passive oc-button-passive-raw" type="button">
+            <span class="oc-icon oc-icon-s oc-icon-passive">
+              <!---->
+            </span>
+          </button>
+        </th>
+        <th class="oc-th oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-text-nowrap oc-table-header-cell oc-table-header-cell-manager" style="top: 0px;">
+          <span class="oc-table-thead-content header-text">Manager</span>
+          <!---->
+        </th>
+        <th aria-sort="none" class="oc-th oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-text-nowrap oc-table-header-cell oc-table-header-cell-members" style="top: 0px;">
+          <span class="oc-table-thead-content header-text">Members</span>
+          <button aria-label="Sort by members" class="oc-button-sort oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-passive oc-button-passive-raw oc-invisible" type="button">
+            <span class="oc-icon oc-icon-s oc-icon-passive">
+              <!---->
+            </span>
+          </button>
+        </th>
+        <th aria-sort="none" class="oc-th oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-text-nowrap oc-table-header-cell oc-table-header-cell-availableQuota" style="top: 0px;">
+          <span class="oc-table-thead-content header-text">Available Quota</span>
+          <button aria-label="Sort by availableQuota" class="oc-button-sort oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-passive oc-button-passive-raw oc-invisible" type="button">
+            <span class="oc-icon oc-icon-s oc-icon-passive">
+              <!---->
+            </span>
+          </button>
+        </th>
+        <th aria-sort="none" class="oc-th oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-text-nowrap oc-table-header-cell oc-table-header-cell-usedQuota" style="top: 0px;">
+          <span class="oc-table-thead-content header-text">Used Quota</span>
+          <button aria-label="Sort by usedQuota" class="oc-button-sort oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-passive oc-button-passive-raw oc-invisible" type="button">
+            <span class="oc-icon oc-icon-s oc-icon-passive">
+              <!---->
+            </span>
+          </button>
+        </th>
+        <th aria-sort="none" class="oc-th oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-text-nowrap oc-table-header-cell oc-table-header-cell-remainingQuota" style="top: 0px;">
+          <span class="oc-table-thead-content header-text">Remaining Quota</span>
+          <button aria-label="Sort by remainingQuota" class="oc-button-sort oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-passive oc-button-passive-raw oc-invisible" type="button">
+            <span class="oc-icon oc-icon-s oc-icon-passive">
+              <!---->
+            </span>
+          </button>
+        </th>
+        <th aria-sort="none" class="oc-th oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-text-nowrap oc-table-header-cell oc-table-header-cell-mdate" style="top: 0px;">
+          <span class="oc-table-thead-content header-text">Modified</span>
+          <button aria-label="Sort by mdate" class="oc-button-sort oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-passive oc-button-passive-raw oc-invisible" type="button">
+            <span class="oc-icon oc-icon-s oc-icon-passive">
+              <!---->
+            </span>
+          </button>
+        </th>
+        <th aria-sort="none" class="oc-th oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-text-nowrap oc-table-header-cell oc-table-header-cell-status oc-pr-s" style="top: 0px;">
+          <span class="oc-table-thead-content header-text">Status</span>
+          <button aria-label="Sort by status" class="oc-button-sort oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-passive oc-button-passive-raw oc-invisible" type="button">
+            <span class="oc-icon oc-icon-s oc-icon-passive">
+              <!---->
+            </span>
+          </button>
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr class="oc-tbody-tr oc-tbody-tr-1" data-item-id="1" draggable="false" tabindex="-1">
+        <td class="oc-td oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-shrink oc-table-data-cell oc-table-data-cell-select oc-pl-s ">
+          <occheckbox-stub class="oc-ml-s" hidelabel="true" id="oc-checkbox-2" label="Select 1 Some space" option="[object Object]" size="large"></occheckbox-stub>
+        </td>
+        <td class="oc-td oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-shrink oc-table-data-cell oc-table-data-cell-icon">
+          <span class="oc-icon oc-icon-m oc-icon-passive">
+            <!---->
+          </span>
+        </td>
+        <td class="oc-td oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-table-data-cell oc-table-data-cell-name">
+          1 Some space
+        </td>
+        <td class="oc-td oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-table-data-cell oc-table-data-cell-manager">
+          managers
+        </td>
+        <td class="oc-td oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-table-data-cell oc-table-data-cell-members">
+          1
+        </td>
+        <td class="oc-td oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-table-data-cell oc-table-data-cell-availableQuota"> 1 GB </td>
+        <td class="oc-td oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-table-data-cell oc-table-data-cell-usedQuota"> 0.00 GB </td>
+        <td class="oc-td oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-table-data-cell oc-table-data-cell-remainingQuota"> 1 GB </td>
+        <td class="oc-td oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-table-data-cell oc-table-data-cell-mdate">
+          <span tabindex="0"></span>
+        </td>
+        <td class="oc-td oc-table-cell oc-table-cell-align-left oc-table-cell-align-middle oc-table-cell-width-auto oc-table-data-cell oc-table-data-cell-status oc-pr-s">
+          <span class="oc-flex oc-flex-middle">
+            <span class="oc-mr-s oc-icon oc-icon-m oc-icon-success">
+              <!---->
+            </span>
+            <span>Enabled</span>
+          </span>
+        </td>
+      </tr>
+    </tbody>
+    <tfoot class="oc-table-footer">
+      <tr class="oc-table-footer-row">
+        <td class="oc-table-footer-cell" colspan="10">
+          <div class="oc-text-nowrap oc-text-center oc-width-1-1 oc-my-s">
+            <p class="oc-text-muted">1 spaces in total</p>
+          </div>
+        </td>
+      </tr>
+    </tfoot>
+  </table>
+</div>
+`;
diff --git a/packages/web-app-admin-settings/tests/unit/views/Spaces.spec.ts b/packages/web-app-admin-settings/tests/unit/views/Spaces.spec.ts
new file mode 100644
index 00000000000..8bf0a758b3d
--- /dev/null
+++ b/packages/web-app-admin-settings/tests/unit/views/Spaces.spec.ts
@@ -0,0 +1,115 @@
+import { mockAxiosResolve } from 'web-test-helpers/src/mocks'
+import { Graph } from 'web-client'
+import { mockDeep } from 'jest-mock-extended'
+import { ClientService } from 'web-pkg/src'
+import {
+  createStore,
+  defaultComponentMocks,
+  defaultPlugins,
+  defaultStoreMockOptions,
+  mount
+} from 'web-test-helpers'
+import Spaces from '../../../src/views/Spaces.vue'
+
+const selectors = {
+  loadingSpinnerStub: 'apploadingspinner-stub',
+  spacesListStub: 'spaceslist-stub',
+  noContentMessageStub: 'nocontentmessage-stub'
+}
+
+describe('Spaces view', () => {
+  describe('loading states', () => {
+    it('should show loading spinner if loading', () => {
+      const { wrapper } = getWrapper()
+      expect(wrapper.find(selectors.loadingSpinnerStub).exists()).toBeTruthy()
+    })
+    it('should render spaces list after loading has been finished', async () => {
+      const { wrapper } = getWrapper()
+      await wrapper.vm.loadResourcesTask.last
+      expect(wrapper.html()).toMatchSnapshot()
+      expect(wrapper.find(selectors.spacesListStub).exists()).toBeTruthy()
+    })
+  })
+  it('should render no content message if no spaces found', async () => {
+    const graph = mockDeep<Graph>()
+    graph.drives.listAllDrives.mockImplementation(() => mockAxiosResolve({ value: [] }))
+    const { wrapper } = getWrapper({ spaces: [] })
+    await wrapper.vm.loadResourcesTask.last
+    expect(wrapper.find(selectors.noContentMessageStub).exists()).toBeTruthy()
+  })
+  describe('toggle selection', () => {
+    describe('toggleSelectAllSpaces method', () => {
+      it('selects all spaces', async () => {
+        const spaces = [{ name: 'Some Space' }]
+        const { wrapper } = getWrapper({ spaces })
+        await wrapper.vm.loadResourcesTask.last
+        wrapper.vm.toggleSelectAllSpaces()
+        expect(wrapper.vm.selectedSpaces).toEqual(
+          expect.arrayContaining([expect.objectContaining({ name: spaces[0].name })])
+        )
+      })
+      it('de-selects all selected spaces', async () => {
+        const spaces = [{ name: 'Some Space' }]
+        const { wrapper } = getWrapper({ spaces })
+        await wrapper.vm.loadResourcesTask.last
+        wrapper.vm.selectedSpaces = spaces
+        wrapper.vm.toggleSelectAllSpaces()
+        expect(wrapper.vm.selectedSpaces.length).toBe(0)
+      })
+    })
+    describe('toggleSelectSpace method', () => {
+      it('selects a space', async () => {
+        const spaces = [{ name: 'Some Space' }]
+        const { wrapper } = getWrapper()
+        await wrapper.vm.loadResourcesTask.last
+        wrapper.vm.toggleSelectSpace(spaces[0])
+        expect(wrapper.vm.selectedSpaces).toEqual(
+          expect.arrayContaining([expect.objectContaining({ name: spaces[0].name })])
+        )
+      })
+      it('de-selects a selected space', async () => {
+        const spaces = [{ name: 'Some Space' }]
+        const { wrapper } = getWrapper()
+        await wrapper.vm.loadResourcesTask.last
+        wrapper.vm.selectedSpaces = spaces
+        wrapper.vm.toggleSelectSpace(spaces[0])
+        expect(wrapper.vm.selectedSpaces.length).toBe(0)
+      })
+    })
+    describe('unselectAllSpaces method', () => {
+      it('de-selects all selected spaces', async () => {
+        const spaces = [{ name: 'Some Space' }]
+        const { wrapper } = getWrapper({ spaces })
+        await wrapper.vm.loadResourcesTask.last
+        wrapper.vm.selectedSpaces = spaces
+        wrapper.vm.unselectAllSpaces()
+        expect(wrapper.vm.selectedSpaces.length).toBe(0)
+      })
+    })
+  })
+})
+
+function getWrapper({ spaces = [{ name: 'Some Space' }] } = {}) {
+  const graph = mockDeep<Graph>()
+  graph.drives.listAllDrives.mockImplementation(() => mockAxiosResolve({ value: spaces }))
+  const $clientService = mockDeep<ClientService>()
+  $clientService.graphAuthenticated.mockImplementation(() => graph)
+  const mocks = { ...defaultComponentMocks(), $clientService }
+
+  const storeOptions = { ...defaultStoreMockOptions }
+  const store = createStore(storeOptions)
+
+  return {
+    wrapper: mount(Spaces, {
+      global: {
+        plugins: [...defaultPlugins(), store],
+        mocks,
+        stubs: {
+          AppLoadingSpinner: true,
+          NoContentMessage: true,
+          SpacesList: true
+        }
+      }
+    })
+  }
+}
diff --git a/packages/web-app-admin-settings/tests/unit/views/__snapshots__/Spaces.spec.ts.snap b/packages/web-app-admin-settings/tests/unit/views/__snapshots__/Spaces.spec.ts.snap
new file mode 100644
index 00000000000..82c2b543fcb
--- /dev/null
+++ b/packages/web-app-admin-settings/tests/unit/views/__snapshots__/Spaces.spec.ts.snap
@@ -0,0 +1,56 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Spaces view loading states should render spaces list after loading has been finished 1`] = `
+<div>
+  <main class="oc-flex oc-height-1-1 app-content oc-width-1-1">
+    <div class="oc-width-expand" id="admin-settings-wrapper">
+      <div class="oc-app-bar oc-py-s" id="admin-settings-app-bar">
+        <div class="oc-flex oc-flex-between">
+          <nav class="oc-flex oc-flex-middle oc-breadcrumb oc-breadcrumb-default">
+            <ol class="oc-breadcrumb-list oc-flex oc-m-rm oc-p-rm">
+              <li class="oc-breadcrumb-list-item oc-flex oc-flex-middle">
+                <a href="/admin-settings">
+                  <span>Administration Settings</span>
+                </a>
+                <span class="oc-mx-xs oc-icon oc-icon-m oc-icon-passive">
+                  <!---->
+                </span>
+                <!---->
+              </li>
+              <li class="oc-breadcrumb-list-item oc-flex oc-flex-middle">
+                <!---->
+                <button aria-current="page" class="oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-passive oc-button-passive-raw" type="button">
+                  <span>Spaces</span>
+                </button>
+                <!---->
+              </li>
+            </ol>
+            <div class="oc-breadcrumb-drop">
+              <label aria-expanded="false" class="oc-breadcrumb-drop-label oc-flex oc-flex-middle oc-flex-between" tabindex="0">
+                <span aria-current="page" class="oc-breadcrumb-drop-label-text oc-text-truncate">Spaces</span>
+                <span class="oc-breadcrumb-drop-label-icon oc-icon oc-icon-m oc-icon-passive">
+                  <!---->
+                </span>
+              </label>
+            </div>
+          </nav>
+          <div>
+            <button aria-label="Open sidebar to view details" class="oc-my-s oc-p-xs oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-passive oc-button-passive-raw" id="files-toggle-sidebar" type="button">
+              <span class="oc-icon oc-icon-m oc-icon-passive">
+                <!---->
+              </span>
+            </button>
+          </div>
+        </div>
+        <div class="admin-settings-app-bar-actions oc-mt-xs">
+          <!---->
+        </div>
+      </div>
+      <div>
+        <spaceslist-stub headerposition="0" selectedspaces="" spaces="[object Object]"></spaceslist-stub>
+      </div>
+    </div>
+    <!---->
+  </main>
+</div>
+`;