From c517e8df116043ad0d101ca1316ec667f6b54285 Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Thu, 26 Sep 2024 13:26:41 +0200 Subject: [PATCH 1/8] test: unit tests for LayoutContainer in app store --- .../web-app-app-store/src/LayoutContainer.vue | 2 +- .../tests/unit/LayoutContainer.spec.ts | 52 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 packages/web-app-app-store/tests/unit/LayoutContainer.spec.ts diff --git a/packages/web-app-app-store/src/LayoutContainer.vue b/packages/web-app-app-store/src/LayoutContainer.vue index 87497b61bed..20a2750d219 100644 --- a/packages/web-app-app-store/src/LayoutContainer.vue +++ b/packages/web-app-app-store/src/LayoutContainer.vue @@ -2,7 +2,7 @@
diff --git a/packages/web-app-app-store/tests/unit/LayoutContainer.spec.ts b/packages/web-app-app-store/tests/unit/LayoutContainer.spec.ts new file mode 100644 index 00000000000..bb3af8e1b22 --- /dev/null +++ b/packages/web-app-app-store/tests/unit/LayoutContainer.spec.ts @@ -0,0 +1,52 @@ +import LayoutContainer from '../../src/LayoutContainer.vue' +import { defaultComponentMocks, defaultPlugins, mount, nextTicks } from 'web-test-helpers' +import { useAppsStore } from '../../src/piniaStores' + +const selectors = { + loadingSpinner: '#app-loading-spinner', + routerView: '[data-testid="app-store-router-view"]' +} + +describe('LayoutContainer', () => { + it('shows a loading spinner while apps are loading from repositories', () => { + const { wrapper } = getWrapper({ keepLoadingApps: true }) + expect(wrapper.find(selectors.loadingSpinner).exists()).toBeTruthy() + expect(wrapper.find(selectors.routerView).exists()).toBeFalsy() + }) + it('renders the router view when loading apps is done', async () => { + const { wrapper } = getWrapper({}) + await nextTicks(2) + expect(wrapper.find(selectors.loadingSpinner).exists()).toBeFalsy() + expect(wrapper.find(selectors.routerView).exists()).toBeTruthy() + }) +}) + +function getWrapper({ keepLoadingApps }: { keepLoadingApps?: boolean }) { + const plugins = defaultPlugins({}) + + const { loadApps } = useAppsStore() + vi.mocked(loadApps).mockReturnValue( + new Promise((res) => { + if (!keepLoadingApps) { + return res() + } + return setTimeout(() => res(), 500) + }) + ) + + const mocks = { + ...defaultComponentMocks(), + loadApps + } + + return { + mocks, + wrapper: mount(LayoutContainer, { + global: { + mocks, + provide: mocks, + plugins + } + }) + } +} From 95846fa987d10b4859dfa133afeb85f25edcb3a6 Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Fri, 4 Oct 2024 14:43:17 +0200 Subject: [PATCH 2/8] test: unit tests for AppResources in app store --- .../src/components/AppResources.vue | 41 ++++++++++-- packages/web-app-app-store/src/types.ts | 1 + .../unit/components/AppResources.spec.ts | 63 +++++++++++++++++++ 3 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 packages/web-app-app-store/tests/unit/components/AppResources.spec.ts diff --git a/packages/web-app-app-store/src/components/AppResources.vue b/packages/web-app-app-store/src/components/AppResources.vue index 0638f243873..59b3950e0f3 100644 --- a/packages/web-app-app-store/src/components/AppResources.vue +++ b/packages/web-app-app-store/src/components/AppResources.vue @@ -1,16 +1,28 @@ diff --git a/packages/web-app-app-store/src/types.ts b/packages/web-app-app-store/src/types.ts index 6cfc6d6454c..e515cbaedd5 100644 --- a/packages/web-app-app-store/src/types.ts +++ b/packages/web-app-app-store/src/types.ts @@ -41,6 +41,7 @@ export const AppResourceSchema = z.object({ label: z.string(), icon: z.string().optional() }) +export type AppResource = z.infer export const RawAppSchema = z.object({ id: z.string(), diff --git a/packages/web-app-app-store/tests/unit/components/AppResources.spec.ts b/packages/web-app-app-store/tests/unit/components/AppResources.spec.ts new file mode 100644 index 00000000000..635b5519dc6 --- /dev/null +++ b/packages/web-app-app-store/tests/unit/components/AppResources.spec.ts @@ -0,0 +1,63 @@ +import { mount } from 'web-test-helpers' +import AppResources from '../../../src/components/AppResources.vue' +import { App, AppResource } from '../../../src/types' +import { mock } from 'vitest-mock-extended' + +const resource1: AppResource = { + url: 'https://trololo.whatever', + icon: 'github', + label: 'GitHub' +} +const resource2: AppResource = { + url: 'https://wololo', + label: 'Wololo' +} +const resource3: AppResource = { + url: 'https://some.url', + icon: 'file' +} +const resource4: AppResource = { + label: 'Wololo' +} +const resources = [resource1, resource2, resource3, resource4] + +const selectors = { + link: '[data-testid="resource-link"]', + icon: '[data-testid="resource-icon"]', + label: '[data-testid="resource-label"]' +} + +describe('AppResources.vue', () => { + it('renders only resources with a valid URL and a label', () => { + const { wrapper } = getWrapper() + expect(wrapper.findAll(selectors.link).length).toBe(2) + }) + it('renders a link per resource including an icon if present', () => { + const { wrapper } = getWrapper() + + const link1 = wrapper.findAll(selectors.link).at(0) + expect(link1.exists()).toBeTruthy() + expect(link1.attributes().href).toBe(resource1.url) + expect(link1.find(selectors.label).text()).toBe(resource1.label) + expect(link1.find(selectors.icon).exists()).toBeTruthy() + if (link1.find(selectors.icon).exists()) { + expect(link1.find(selectors.icon).attributes().name).toBe(resource1.icon) + } + + const link2 = wrapper.findAll(selectors.link).at(1) + expect(link2.exists()).toBeTruthy() + expect(link2.attributes().href).toBe(resource2.url) + expect(link2.find(selectors.label).text()).toBe(resource2.label) + expect(link2.find(selectors.icon).exists()).toBeFalsy() + }) +}) + +const getWrapper = () => { + const app = { ...mock({}), resources } + + return { + wrapper: mount(AppResources, { + props: { app } + }) + } +} From 21ac91069b1cce62d447fa399ceaffef6a30eeac Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Mon, 7 Oct 2024 14:08:29 +0200 Subject: [PATCH 3/8] test: unit tests for AppAuthors in app store --- .../src/components/AppAuthors.vue | 31 ++++++++-- packages/web-app-app-store/src/types.ts | 1 + .../tests/unit/components/AppAuthors.spec.ts | 58 +++++++++++++++++++ 3 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 packages/web-app-app-store/tests/unit/components/AppAuthors.spec.ts diff --git a/packages/web-app-app-store/src/components/AppAuthors.vue b/packages/web-app-app-store/src/components/AppAuthors.vue index f927d6cc390..e8b78f96178 100644 --- a/packages/web-app-app-store/src/components/AppAuthors.vue +++ b/packages/web-app-app-store/src/components/AppAuthors.vue @@ -1,15 +1,18 @@ diff --git a/packages/web-app-app-store/src/types.ts b/packages/web-app-app-store/src/types.ts index e515cbaedd5..1c66986f2d3 100644 --- a/packages/web-app-app-store/src/types.ts +++ b/packages/web-app-app-store/src/types.ts @@ -29,6 +29,7 @@ export const AppAuthorSchema = z.object({ email: z.string().optional(), url: z.string().optional() }) +export type AppAuthor = z.infer export const AppImageSchema = z.object({ url: z.string(), diff --git a/packages/web-app-app-store/tests/unit/components/AppAuthors.spec.ts b/packages/web-app-app-store/tests/unit/components/AppAuthors.spec.ts new file mode 100644 index 00000000000..41384dbef0c --- /dev/null +++ b/packages/web-app-app-store/tests/unit/components/AppAuthors.spec.ts @@ -0,0 +1,58 @@ +import AppAuthors from '../../../src/components/AppAuthors.vue' +import { mock } from 'vitest-mock-extended' +import { App, AppAuthor } from '../../../src/types' +import { mount } from 'web-test-helpers' + +const author1: AppAuthor = { + name: 'John Doe', + url: 'https://johndoe.com' +} +const author2: AppAuthor = { + name: 'Jane Doe' +} +const author3: AppAuthor = { + name: 'Wololo Priest', + url: 'wololo' +} +const author4: AppAuthor = { + url: 'trololo' +} +const authors = [author1, author2, author3, author4] + +const selectors = { + item: '.app-author-item', + link: '[data-testid="author-link"]', + label: '[data-testid="author-label"]' +} + +describe('AppAuthors.vue', () => { + it('renders only authors with name and valid or empty url', () => { + const { wrapper } = getWrapper() + expect(wrapper.findAll(selectors.item).length).toBe(2) + }) + it('renders authors as link when they have a url and name', () => { + const { wrapper } = getWrapper() + const author = wrapper.findAll(selectors.item).at(0) + expect(author.exists()).toBeTruthy() + const link = author.find(selectors.link) + expect(link.exists()).toBeTruthy() + expect(link.attributes().href).toBe(author1.url) + expect(link.text()).toBe(author1.name) + }) + it('renders authors as span when they only have a name', () => { + const { wrapper } = getWrapper() + const author = wrapper.findAll(selectors.item).at(1) + expect(author.find(selectors.link).exists()).toBeFalsy() + expect(author.find(selectors.label).text()).toBe(author2.name) + }) +}) + +const getWrapper = () => { + const app = { ...mock({}), authors } + + return { + wrapper: mount(AppAuthors, { + props: { app } + }) + } +} From 86703c8ceabe9d849c153e0157424f6129ca26b4 Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Mon, 7 Oct 2024 14:27:05 +0200 Subject: [PATCH 4/8] test: unit tests for AppTags in app store --- .../src/components/AppTags.vue | 1 + .../tests/unit/components/AppTags.spec.ts | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 packages/web-app-app-store/tests/unit/components/AppTags.spec.ts diff --git a/packages/web-app-app-store/src/components/AppTags.vue b/packages/web-app-app-store/src/components/AppTags.vue index db53a38adae..d3a37b76a32 100644 --- a/packages/web-app-app-store/src/components/AppTags.vue +++ b/packages/web-app-app-store/src/components/AppTags.vue @@ -3,6 +3,7 @@ { + it('renders one button per tag', () => { + const { wrapper } = getWrapper() + expect(wrapper.findAll(selectors.button)).toHaveLength(tags.length) + }) + it('shows the tag text as button text', () => { + const { wrapper } = getWrapper() + const buttons = wrapper.findAll(selectors.button) + for (let i = 0; i < buttons.length; i++) { + expect(buttons[i].text()).toBe(tags[i]) + } + }) + it('emits click event on tag click', () => { + const { wrapper } = getWrapper() + wrapper.find(selectors.button).trigger('click') + expect(wrapper.emitted('click')).toBeTruthy() + }) + it('applies mark-element css class to tag text for highlighting', () => { + const { wrapper } = getWrapper() + wrapper.findAll(selectors.button).forEach((button) => { + expect(button.find(selectors.markElement).exists()).toBeTruthy() + }) + }) +}) + +const getWrapper = () => { + const app = { ...mock({}), tags } + + return { + wrapper: mount(AppTags, { + props: { app } + }) + } +} From a1b252589a6d3affbc408a9ee8ea86a6edb8c13a Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Tue, 8 Oct 2024 06:19:31 +0200 Subject: [PATCH 5/8] test: unit tests for AppVersions in app store --- .../src/components/AppVersions.vue | 27 +++-- .../tests/unit/components/AppVersions.spec.ts | 104 ++++++++++++++++++ 2 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 packages/web-app-app-store/tests/unit/components/AppVersions.spec.ts diff --git a/packages/web-app-app-store/src/components/AppVersions.vue b/packages/web-app-app-store/src/components/AppVersions.vue index 8807d070a34..70148c89373 100644 --- a/packages/web-app-app-store/src/components/AppVersions.vue +++ b/packages/web-app-app-store/src/components/AppVersions.vue @@ -17,6 +17,7 @@ import { computed, defineComponent, PropType } from 'vue' import { App } from '../types' import { useGettext } from 'vue3-gettext' import AppActions from './AppActions.vue' +import { isEmpty } from 'lodash-es' export default defineComponent({ name: 'AppVersions', @@ -32,13 +33,25 @@ export default defineComponent({ const { $gettext } = useGettext() const data = computed(() => { - return props.app.versions.map((version) => { - return { - ...version, - minOCIS: version.minOCIS ? `v${version.minOCIS}` : '-', - id: version.version - } - }) + return (props.app.versions || []) + .filter((version) => { + if (isEmpty(version.version) || isEmpty(version.url)) { + return false + } + try { + new URL(version.url) + } catch { + return false + } + return true + }) + .map((version) => { + return { + ...version, + minOCIS: version.minOCIS ? `v${version.minOCIS}` : '-', + id: version.version + } + }) }) const fields = computed(() => { return [ diff --git a/packages/web-app-app-store/tests/unit/components/AppVersions.spec.ts b/packages/web-app-app-store/tests/unit/components/AppVersions.spec.ts new file mode 100644 index 00000000000..e05110abd95 --- /dev/null +++ b/packages/web-app-app-store/tests/unit/components/AppVersions.spec.ts @@ -0,0 +1,104 @@ +import { App, AppVersion } from '../../../src/types' +import AppVersions from '../../../src/components/AppVersions.vue' +import { defaultPlugins, mount } from 'web-test-helpers' +import { mock } from 'vitest-mock-extended' + +const version1: AppVersion = { + url: 'https://wololo.com/download-1.0.0.zip', + version: '1.0.0' +} +const version2: AppVersion = { + url: 'https://wololo.com/download-1.1.0.zip', + version: '1.1.0', + minOCIS: '6.5.0' +} +const version3: AppVersion = { + url: 'https://wololo.com/download-1.1.1.zip', + version: '1.1.1', + minOCIS: '6.5.0' +} +const version4: AppVersion = { + url: 'wololo', + version: '1.2.0' +} +const version5: AppVersion = { + url: 'https://wololo.com/download-1.3.0.zip', + minOCIS: '6.5.0' +} +const validVersions = [version1, version2, version3] +const versions = [...validVersions, version4, version5] +const mostRecentVersion = version2 + +const selectors = { + versionRow: 'tbody tr', + tableCellVersion: '.oc-table-data-cell-version', + tableCellMinOCIS: '.oc-table-data-cell-minOCIS', + tableCellActions: '.oc-table-data-cell-actions', + downloadButton: '[data-testid="action-handler"]' +} + +describe('AppVersions.vue', () => { + it('renders only versions which have at least a version and a valid URL', () => { + const { wrapper } = getWrapper() + const rows = wrapper.findAll(selectors.versionRow) + expect(rows).toHaveLength(validVersions.length) + rows.forEach((row, index) => { + const versionCell = row.find(selectors.tableCellVersion) + expect(versionCell.exists()).toBeTruthy() + expect(versionCell.text().startsWith(`v${versions[index].version}`)).toBeTruthy() + }) + }) + it('adds a "most recent" tag only to the latest version', () => { + const { wrapper } = getWrapper() + const rows = wrapper.findAll(selectors.versionRow) + rows.forEach((row, index) => { + const versionCell = row.find(selectors.tableCellVersion) + expect(versionCell.exists()).toBeTruthy() + if (versions[index].version === mostRecentVersion.version) { + expect(versionCell.text().startsWith(`v${versions[index].version}`)).toBeTruthy() + expect(versionCell.text().endsWith('most recent')).toBeTruthy() + } else { + expect(versionCell.text()).toBe(`v${versions[index].version}`) + } + }) + }) + it('renders the minimum required ocis version if present or "-" if not', () => { + const { wrapper } = getWrapper() + const rows = wrapper.findAll(selectors.versionRow) + rows.forEach((row, index) => { + const minOCISCell = row.find(selectors.tableCellMinOCIS) + expect(minOCISCell.exists()).toBeTruthy() + if (versions[index].minOCIS) { + expect(minOCISCell.text()).toBe(`v${versions[index].minOCIS}`) + } else { + expect(minOCISCell.text()).toBe('-') + } + }) + }) + it('renders a download button', async () => { + const { wrapper } = getWrapper() + const rows = wrapper.findAll(selectors.versionRow) + for (let i = 0; i < rows.length; i++) { + const actionsCell = rows[i].find(selectors.tableCellActions) + expect(actionsCell.exists()).toBeTruthy() + const downloadButton = actionsCell.find(selectors.downloadButton) + expect(downloadButton.exists()).toBeTruthy() + expect(downloadButton.text()).toBe('Download') + await downloadButton.trigger('click') + expect(wrapper.emitted('click')).toBeTruthy() + expect(window.location.href).toBe(versions[i].url) + } + }) +}) + +const getWrapper = () => { + const app: App = { ...mock({}), versions, mostRecentVersion } + return { + wrapper: mount(AppVersions, { + props: { app }, + global: { + plugins: [...defaultPlugins()] + } + }) + } +} From 53a063d3883d31b5f37d062245116ed44c4ecd73 Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Tue, 8 Oct 2024 11:30:49 +0200 Subject: [PATCH 6/8] test: unit tests for AppActions in app store --- .../tests/unit/components/AppActions.spec.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 packages/web-app-app-store/tests/unit/components/AppActions.spec.ts diff --git a/packages/web-app-app-store/tests/unit/components/AppActions.spec.ts b/packages/web-app-app-store/tests/unit/components/AppActions.spec.ts new file mode 100644 index 00000000000..59c9cf665bf --- /dev/null +++ b/packages/web-app-app-store/tests/unit/components/AppActions.spec.ts @@ -0,0 +1,54 @@ +import { defaultPlugins, mount } from 'web-test-helpers' +import AppActions from '../../../src/components/AppActions.vue' +import { App, AppVersion } from '../../../src/types' +import { mock } from 'vitest-mock-extended' + +const version1: AppVersion = { + version: '1.0.0', + url: 'https://example.com/app-1.0.0.zip' +} +const version2: AppVersion = { + version: '1.1.0', + url: 'https://example.com/app-1.1.0.zip' +} +const versions = [version1, version2] +const mostRecentVersion = version2 + +const selectors = { + downloadButton: 'button' +} + +describe('AppActions', () => { + it('renders a "Download" button', () => { + const { wrapper } = getWrapper({}) + expect(wrapper.find(selectors.downloadButton).text()).toBe('Download') + }) + describe('calling the "download" handler', () => { + it('uses the most recent version when none is specified', async () => { + const { wrapper } = getWrapper({}) + await wrapper.find(selectors.downloadButton).trigger('click') + expect(window.location.href).toBe(mostRecentVersion.url) + }) + it('uses the version provided via props', async () => { + const { wrapper } = getWrapper({ version: version1 }) + await wrapper.find(selectors.downloadButton).trigger('click') + expect(window.location.href).toBe(version1.url) + }) + }) +}) + +const getWrapper = ({ version }: { version?: AppVersion }) => { + const app = { ...mock({}), versions, mostRecentVersion } + + return { + wrapper: mount(AppActions, { + props: { + app, + version + }, + global: { + plugins: [...defaultPlugins()] + } + }) + } +} From d43dca07346a1994be689986b49a0af500910f4f Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Tue, 8 Oct 2024 14:10:28 +0200 Subject: [PATCH 7/8] test: unit tests for AppImageGallery in app store --- .../src/components/AppImageGallery.vue | 17 +- packages/web-app-app-store/src/types.ts | 1 + .../unit/components/AppImageGallery.spec.ts | 164 ++++++++++++++++++ 3 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 packages/web-app-app-store/tests/unit/components/AppImageGallery.spec.ts diff --git a/packages/web-app-app-store/src/components/AppImageGallery.vue b/packages/web-app-app-store/src/components/AppImageGallery.vue index b027dde8665..cea696fcbfa 100644 --- a/packages/web-app-app-store/src/components/AppImageGallery.vue +++ b/packages/web-app-app-store/src/components/AppImageGallery.vue @@ -11,12 +11,19 @@
  • - +
  • - +
  • diff --git a/packages/web-app-app-store/src/types.ts b/packages/web-app-app-store/src/types.ts index 1c66986f2d3..29e4ae43d4c 100644 --- a/packages/web-app-app-store/src/types.ts +++ b/packages/web-app-app-store/src/types.ts @@ -23,6 +23,7 @@ export const AppBadgeSchema = z.object({ label: z.string(), color: z.enum(BADGE_COLORS).optional().default('primary') }) +export type AppBadge = z.infer export const AppAuthorSchema = z.object({ name: z.string(), diff --git a/packages/web-app-app-store/tests/unit/components/AppImageGallery.spec.ts b/packages/web-app-app-store/tests/unit/components/AppImageGallery.spec.ts new file mode 100644 index 00000000000..d0d860b25a9 --- /dev/null +++ b/packages/web-app-app-store/tests/unit/components/AppImageGallery.spec.ts @@ -0,0 +1,164 @@ +import { defaultPlugins, mount } from 'web-test-helpers' +import { mock } from 'vitest-mock-extended' +import AppImageGallery from '../../../src/components/AppImageGallery.vue' +import { App, AppBadge, AppImage, BADGE_COLORS } from '../../../src/types' + +const coverImageWithUrl: AppImage = { + url: 'https://example.com/cover.jpg', + caption: 'Cover image' +} +const coverImageWithoutUrl: AppImage = { + caption: 'Trololo' +} +const screenshot1: AppImage = { + url: 'https://example.com/screenshot1.jpg', + caption: 'Screenshot 1' +} +const screenshot2: AppImage = { + url: 'https://example.com/screenshot2.jpg', + caption: 'Screenshot 2' +} +const screenshot3: AppImage = { + url: 'https://example.com/screenshot3.jpg', + caption: 'Screenshot 3' +} +const screenshots = [screenshot1, screenshot2, screenshot3] + +const selectors = { + badge: '.app-image-ribbon', + image: '.app-image img', + imageFallback: '.app-image .fallback-icon', + pagination: '.app-image-navigation', + paginationPrev: '[data-testid="prev-image"]', + paginationNext: '[data-testid="next-image"]', + paginationSet: '[data-testid="set-image"]' +} + +describe('AppImageGallery.vue', () => { + describe('badges', () => { + it('renders no ribbon if the app has no badge', () => { + const { wrapper } = getWrapper({}) + expect(wrapper.find(selectors.badge).exists()).toBeFalsy() + }) + it('renders a ribbon if the app has a badge', () => { + const badge = { label: 'New', color: BADGE_COLORS[1] } + const { wrapper } = getWrapper({ badge }) + expect(wrapper.find(selectors.badge).exists()).toBeTruthy() + expect(wrapper.find(selectors.badge).text()).toBe(badge.label) + expect( + wrapper.find(selectors.badge).element.className.includes(`app-image-ribbon-${badge.color}`) + ).toBeTruthy() + }) + }) + describe('current image', () => { + it('renders oc-img if the image has a url', () => { + const { wrapper } = getWrapper({ coverImage: coverImageWithUrl }) + expect(wrapper.find(selectors.image).exists()).toBeTruthy() + expect(wrapper.find(selectors.image).attributes().src).toBe(coverImageWithUrl.url) + expect(wrapper.find(selectors.imageFallback).exists()).toBeFalsy() + }) + it('renders oc-icon if the image has no url', () => { + const { wrapper } = getWrapper({ coverImage: coverImageWithoutUrl }) + expect(wrapper.find(selectors.image).exists()).toBeFalsy() + expect(wrapper.find(selectors.imageFallback).exists()).toBeTruthy() + }) + it('renders oc-icon if the app has no coverImage field', () => { + const { wrapper } = getWrapper({ coverImage: null }) + expect(wrapper.find(selectors.image).exists()).toBeFalsy() + expect(wrapper.find(selectors.imageFallback).exists()).toBeTruthy() + }) + }) + describe('navigation', () => { + it('has no navigation if there is only a single image', () => { + const { wrapper } = getWrapper({ showPagination: true, coverImage: coverImageWithUrl }) + expect(wrapper.find(selectors.pagination).exists()).toBeFalsy() + }) + it('has no navigation if it is disabled via prop', () => { + const { wrapper } = getWrapper({ + showPagination: false, + coverImage: coverImageWithUrl, + screenshots + }) + expect(wrapper.find(selectors.pagination).exists()).toBeFalsy() + }) + describe('is visible', () => { + it('has a pagination container', () => { + const { wrapper } = getWrapper({ + showPagination: true, + coverImage: coverImageWithUrl, + screenshots + }) + expect(wrapper.find(selectors.pagination).exists()).toBeTruthy() + }) + it('has a prev button which cycles through images backwards', async () => { + const { wrapper } = getWrapper({ + showPagination: true, + coverImage: coverImageWithUrl, + screenshots + }) + const button = wrapper.find(selectors.paginationPrev) + expect(button.exists()).toBeTruthy() + const images = [coverImageWithUrl, ...screenshots] + for (let i = 1; i <= images.length; i++) { + await button.trigger('click') + expect(wrapper.find(selectors.image).attributes().src).toBe(images[images.length - i].url) + } + }) + it('has a next button which cycles through images forward', async () => { + const { wrapper } = getWrapper({ + showPagination: true, + coverImage: coverImageWithUrl, + screenshots + }) + const button = wrapper.find(selectors.paginationNext) + const images = [coverImageWithUrl, ...screenshots] + expect(button.exists()).toBeTruthy() + for (let i = 1; i <= images.length; i++) { + await button.trigger('click') + expect(wrapper.find(selectors.image).attributes().src).toBe(images[i % images.length].url) + } + }) + it('has a set-button per image which changes the current image', async () => { + const { wrapper } = getWrapper({ + showPagination: true, + coverImage: coverImageWithUrl, + screenshots + }) + const buttons = wrapper.findAll(selectors.paginationSet) + const images = [coverImageWithUrl, ...screenshots] + expect(buttons.length).toBe(images.length) + const indices = [2, 0, 1, 3, 3, 3, 0] + for (let i = 0; i < indices.length; i++) { + await buttons[indices[i]].trigger('click') + expect(wrapper.find(selectors.image).attributes().src).toBe(images[indices[i]].url) + } + }) + }) + }) +}) + +const getWrapper = ({ + showPagination, + badge, + coverImage, + screenshots = [] +}: { + showPagination?: boolean + badge?: AppBadge + coverImage?: AppImage + screenshots?: AppImage[] +}) => { + const app = { ...mock({}), badge, coverImage, screenshots } + + return { + wrapper: mount(AppImageGallery, { + props: { + app, + showPagination + }, + global: { + plugins: defaultPlugins() + } + }) + } +} From 0fdcd37a42d595703a8158ce32287eb30b3b58b8 Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Fri, 11 Oct 2024 10:42:24 +0200 Subject: [PATCH 8/8] chore: fix web-test-helper import --- .../web-app-app-store/tests/unit/LayoutContainer.spec.ts | 7 ++++++- .../tests/unit/components/AppActions.spec.ts | 2 +- .../tests/unit/components/AppAuthors.spec.ts | 2 +- .../tests/unit/components/AppImageGallery.spec.ts | 2 +- .../tests/unit/components/AppResources.spec.ts | 2 +- .../tests/unit/components/AppTags.spec.ts | 2 +- .../tests/unit/components/AppVersions.spec.ts | 2 +- 7 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/web-app-app-store/tests/unit/LayoutContainer.spec.ts b/packages/web-app-app-store/tests/unit/LayoutContainer.spec.ts index bb3af8e1b22..c694a88d69d 100644 --- a/packages/web-app-app-store/tests/unit/LayoutContainer.spec.ts +++ b/packages/web-app-app-store/tests/unit/LayoutContainer.spec.ts @@ -1,5 +1,10 @@ import LayoutContainer from '../../src/LayoutContainer.vue' -import { defaultComponentMocks, defaultPlugins, mount, nextTicks } from 'web-test-helpers' +import { + defaultComponentMocks, + defaultPlugins, + mount, + nextTicks +} from '@ownclouders/web-test-helpers' import { useAppsStore } from '../../src/piniaStores' const selectors = { diff --git a/packages/web-app-app-store/tests/unit/components/AppActions.spec.ts b/packages/web-app-app-store/tests/unit/components/AppActions.spec.ts index 59c9cf665bf..595eee41de2 100644 --- a/packages/web-app-app-store/tests/unit/components/AppActions.spec.ts +++ b/packages/web-app-app-store/tests/unit/components/AppActions.spec.ts @@ -1,4 +1,4 @@ -import { defaultPlugins, mount } from 'web-test-helpers' +import { defaultPlugins, mount } from '@ownclouders/web-test-helpers' import AppActions from '../../../src/components/AppActions.vue' import { App, AppVersion } from '../../../src/types' import { mock } from 'vitest-mock-extended' diff --git a/packages/web-app-app-store/tests/unit/components/AppAuthors.spec.ts b/packages/web-app-app-store/tests/unit/components/AppAuthors.spec.ts index 41384dbef0c..126fd7820ea 100644 --- a/packages/web-app-app-store/tests/unit/components/AppAuthors.spec.ts +++ b/packages/web-app-app-store/tests/unit/components/AppAuthors.spec.ts @@ -1,7 +1,7 @@ import AppAuthors from '../../../src/components/AppAuthors.vue' import { mock } from 'vitest-mock-extended' import { App, AppAuthor } from '../../../src/types' -import { mount } from 'web-test-helpers' +import { mount } from '@ownclouders/web-test-helpers' const author1: AppAuthor = { name: 'John Doe', diff --git a/packages/web-app-app-store/tests/unit/components/AppImageGallery.spec.ts b/packages/web-app-app-store/tests/unit/components/AppImageGallery.spec.ts index d0d860b25a9..956db5bdc2f 100644 --- a/packages/web-app-app-store/tests/unit/components/AppImageGallery.spec.ts +++ b/packages/web-app-app-store/tests/unit/components/AppImageGallery.spec.ts @@ -1,4 +1,4 @@ -import { defaultPlugins, mount } from 'web-test-helpers' +import { defaultPlugins, mount } from '@ownclouders/web-test-helpers' import { mock } from 'vitest-mock-extended' import AppImageGallery from '../../../src/components/AppImageGallery.vue' import { App, AppBadge, AppImage, BADGE_COLORS } from '../../../src/types' diff --git a/packages/web-app-app-store/tests/unit/components/AppResources.spec.ts b/packages/web-app-app-store/tests/unit/components/AppResources.spec.ts index 635b5519dc6..98e6eb72f7f 100644 --- a/packages/web-app-app-store/tests/unit/components/AppResources.spec.ts +++ b/packages/web-app-app-store/tests/unit/components/AppResources.spec.ts @@ -1,4 +1,4 @@ -import { mount } from 'web-test-helpers' +import { mount } from '@ownclouders/web-test-helpers' import AppResources from '../../../src/components/AppResources.vue' import { App, AppResource } from '../../../src/types' import { mock } from 'vitest-mock-extended' diff --git a/packages/web-app-app-store/tests/unit/components/AppTags.spec.ts b/packages/web-app-app-store/tests/unit/components/AppTags.spec.ts index 91c44e534cf..78f4cab5083 100644 --- a/packages/web-app-app-store/tests/unit/components/AppTags.spec.ts +++ b/packages/web-app-app-store/tests/unit/components/AppTags.spec.ts @@ -1,5 +1,5 @@ import AppTags from '../../../src/components/AppTags.vue' -import { mount } from 'web-test-helpers' +import { mount } from '@ownclouders/web-test-helpers' import { mock } from 'vitest-mock-extended' import { App } from '../../../src/types' diff --git a/packages/web-app-app-store/tests/unit/components/AppVersions.spec.ts b/packages/web-app-app-store/tests/unit/components/AppVersions.spec.ts index e05110abd95..60d3079f447 100644 --- a/packages/web-app-app-store/tests/unit/components/AppVersions.spec.ts +++ b/packages/web-app-app-store/tests/unit/components/AppVersions.spec.ts @@ -1,6 +1,6 @@ import { App, AppVersion } from '../../../src/types' import AppVersions from '../../../src/components/AppVersions.vue' -import { defaultPlugins, mount } from 'web-test-helpers' +import { defaultPlugins, mount } from '@ownclouders/web-test-helpers' import { mock } from 'vitest-mock-extended' const version1: AppVersion = {