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> +`;