Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions apps/files_versions/src/views/FilesVersionsSidebarTab.spec.ts
Original file line number Diff line number Diff line change
@@ -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)
})
})
14 changes: 6 additions & 8 deletions apps/files_versions/src/views/FilesVersionsSidebarTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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
Expand Down
9 changes: 5 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading