diff --git a/apps/files_versions/src/views/FilesVersionsSidebarTab.spec.ts b/apps/files_versions/src/views/FilesVersionsSidebarTab.spec.ts new file mode 100644 index 0000000000000..e3a92b9542f83 --- /dev/null +++ b/apps/files_versions/src/views/FilesVersionsSidebarTab.spec.ts @@ -0,0 +1,80 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { flushPromises, mount } from '@vue/test-utils' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { defineComponent, h } from 'vue' + +const viewer = vi.hoisted(() => ({ + canCompare: vi.fn(() => true), + canView: vi.fn(() => true), + getViewer: vi.fn(), +})) +vi.mock('@nextcloud/viewer', () => viewer) +// On a phone there is no room for two versions side by side +vi.mock('@nextcloud/vue/composables/useIsMobile', async () => { + const { ref } = await import('vue') + return { useIsMobile: () => ref(false) } +}) + +vi.mock('../utils/versions.ts', () => ({ + fetchVersions: vi.fn(async () => [{ mtime: 1, fileVersion: '1', label: '', basename: 'notes.md' }]), + deleteVersion: vi.fn(), + restoreVersion: vi.fn(), + setVersionLabel: vi.fn(), + versionToNode: vi.fn(), +})) + +// The list renders its rows in a slot only once it has measured itself: +// render them all here, the entries are what is tested +vi.mock('../components/VirtualScrolling.vue', () => ({ + default: defineComponent({ + props: ['sections', 'headerHeight'], + setup(props, { slots }) { + return () => h('div', slots.default?.({ visibleSections: props.sections })) + }, + }), +})) + +vi.mock('../components/VersionEntry.vue', () => ({ + default: defineComponent({ + name: 'VersionEntry', + props: ['canView', 'canCompare', 'loadPreview', 'version', 'node', 'isCurrent', 'isFirstVersion'], + setup: () => () => h('li'), + }), +})) + +import FilesVersionsSidebarTab from './FilesVersionsSidebarTab.vue' + +/** + * Show the versions tab of a markdown file. + */ +async function mountTab() { + const node = { id: 1, fileid: 1, mime: 'text/markdown', mtime: new Date(2), basename: 'notes.md' } + const wrapper = mount(FilesVersionsSidebarTab, { props: { active: true, node } as never }) + await flushPromises() + return { wrapper, node } +} + +beforeEach(() => vi.clearAllMocks()) + +describe('comparing two versions', () => { + // It used to be offered for pictures only, until the viewer could say + // which of its handlers compare: Text does, and is not a picture + it('is offered where the viewer says it is worth it', async () => { + viewer.canCompare.mockReturnValue(true) + const { wrapper, node } = await mountTab() + + expect(viewer.canCompare).toHaveBeenCalledWith(node) + expect(wrapper.findComponent({ name: 'VersionEntry' }).props('canCompare')).toBe(true) + }) + + it('is not offered where the viewer says it is not', async () => { + viewer.canCompare.mockReturnValue(false) + const { wrapper } = await mountTab() + + expect(wrapper.findComponent({ name: 'VersionEntry' }).props('canCompare')).toBe(false) + }) +}) diff --git a/apps/files_versions/src/views/FilesVersionsSidebarTab.vue b/apps/files_versions/src/views/FilesVersionsSidebarTab.vue index 839fb82d31936..6b3da37331dcb 100644 --- a/apps/files_versions/src/views/FilesVersionsSidebarTab.vue +++ b/apps/files_versions/src/views/FilesVersionsSidebarTab.vue @@ -47,7 +47,7 @@ import type { Version } from '../utils/versions.ts' import { showError, showSuccess } from '@nextcloud/dialogs' import { emit } from '@nextcloud/event-bus' import { t } from '@nextcloud/l10n' -import { getViewer, canView as viewerCanView } from '@nextcloud/viewer' +import { getViewer, canCompare as viewerCanCompare, canView as viewerCanView } from '@nextcloud/viewer' import { useIsMobile } from '@nextcloud/vue/composables/useIsMobile' import { watchDebounced } from '@vueuse/core' import { computed, ref, watch } from 'vue' @@ -117,14 +117,12 @@ const initialVersionMtime = computed(() => { const canView = computed(() => props.node !== null && viewerCanView(props.node)) -// Comparison puts two files side by side, which needs the width for it and -// only means something for a picture: two clips or two recordings playing -// next to each other say nothing about what changed between them. The old -// viewer asked each handler through a `canCompare` flag, which the package -// does not carry yet; until it does, the mime is what decides. +// Comparison puts two files side by side, which needs the width for it, and +// is only offered where the handler showing the file says it means +// something: two pictures or two documents, not two clips playing at once. const canCompare = computed(() => !isMobile.value - && canView.value - && (props.node?.mime ?? '').startsWith('image/')) + && props.node !== null + && viewerCanCompare(props.node)) // When either the current node to show or its mtime changes we need to refetch the versions // When the id changed we immediately show changes diff --git a/package-lock.json b/package-lock.json index 9dcaea28a251f..4eaeddaf336ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "@nextcloud/paths": "^3.1.0", "@nextcloud/router": "^3.2.0", "@nextcloud/sharing": "^1.0.0-beta.4", - "@nextcloud/viewer": "^2.0.0-beta.14", + "@nextcloud/viewer": "^2.0.0-beta.15", "@nextcloud/vue": "^9.12.0", "@simplewebauthn/browser": "^14.0.0", "@vueuse/core": "^15.0.0", @@ -2202,9 +2202,9 @@ } }, "node_modules/@nextcloud/viewer": { - "version": "2.0.0-beta.14", - "resolved": "https://registry.npmjs.org/@nextcloud/viewer/-/viewer-2.0.0-beta.14.tgz", - "integrity": "sha512-1we9o780BP/HQBZhcz+czM76yq8Df1smWLj5JhKPQXs+jcAuAeaK5u490qwdZKHr4n4Voqsz0H6sdVU+3vG+VQ==", + "version": "2.0.0-beta.15", + "resolved": "https://registry.npmjs.org/@nextcloud/viewer/-/viewer-2.0.0-beta.15.tgz", + "integrity": "sha512-kRLwIWB50B/2IbyGqYlNJIDxpnFt1yyI7zV3Qps+Q2mp9F0I/Nnw/OBkm0NNbDP3PL5o4tcVVq/4Psfuw2izag==", "license": "AGPL-3.0-or-later", "dependencies": { "@mdi/svg": "^7.4.47", @@ -2226,6 +2226,7 @@ "@nextcloud/dialogs": "^7.5.0", "@nextcloud/event-bus": "^3.3.3", "@nextcloud/files": "^4.0.0", + "@nextcloud/initial-state": "^3.0.0", "@nextcloud/l10n": "^3.4.1", "@nextcloud/paths": "^3.1.0", "@nextcloud/router": "^3.1.0", diff --git a/package.json b/package.json index 8dff1d4790f07..bd57e2bd12a36 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@nextcloud/paths": "^3.1.0", "@nextcloud/router": "^3.2.0", "@nextcloud/sharing": "^1.0.0-beta.4", - "@nextcloud/viewer": "^2.0.0-beta.14", + "@nextcloud/viewer": "^2.0.0-beta.15", "@nextcloud/vue": "^9.12.0", "@simplewebauthn/browser": "^14.0.0", "@vueuse/core": "^15.0.0",