diff --git a/core/src/OC/mimeType.js b/core/src/OC/mimeType.js index 9e51da631c7..1b92477dc92 100644 --- a/core/src/OC/mimeType.js +++ b/core/src/OC/mimeType.js @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { generateUrl } from '@nextcloud/router' +import { generateUrl, getRootUrl } from '@nextcloud/router' const iconCache = new Map() @@ -23,36 +23,24 @@ export function getIconUrl(mimeType) { } if (!iconCache.has(mimeType)) { - // First try to get the correct icon from the current theme - let gotIcon = null + let gotIcon = false let path = '' + // First try to get the correct icon from the current legacy-theme if (OC.theme.folder !== '' && Array.isArray(OC.MimeTypeList.themes[OC.theme.folder])) { - path = generateUrl('/themes/' + window.OC.theme.folder + '/core/img/filetypes/') + path = getRootUrl() + '/themes/' + window.OC.theme.folder + '/core/img/filetypes/' const icon = getMimeTypeIcon(mimeType, window.OC.MimeTypeList.themes[OC.theme.folder]) - - if (icon !== null) { + if (icon) { gotIcon = true - path += icon + path += icon + '.svg' } } - if (window.OCA.Theming && gotIcon === null) { - path = generateUrl('/apps/theming/img/core/filetypes/') - path += getMimeTypeIcon(mimeType, window.OC.MimeTypeList.files) - gotIcon = true - } - - // If we do not yet have an icon fall back to the default - if (gotIcon === null) { - path = generateUrl('/core/img/filetypes/') - path += getMimeTypeIcon(mimeType, window.OC.MimeTypeList.files) - } - - path += '.svg' - if (window.OCA.Theming) { - path += '?v=' + window.OCA.Theming.cacheBuster + // theming is always enabled since Nextcloud 20 so we get it from that + if (!gotIcon) { + path = generateUrl('/apps/theming/img/core/filetypes/' + getMimeTypeIcon(mimeType, window.OC.MimeTypeList.files) + '.svg') } + path += '?v=' + window.OCA.Theming.cacheBuster // Cache the result iconCache.set(mimeType, path) } @@ -92,3 +80,10 @@ function getMimeTypeIcon(mimeType, files) { return null } + +/** + * Clear the icon cache + */ +export function clearIconCache() { + iconCache.clear() +} diff --git a/core/src/tests/OC/mimeType.spec.ts b/core/src/tests/OC/mimeType.spec.ts index 1c10e40aec7..873660abc23 100644 --- a/core/src/tests/OC/mimeType.spec.ts +++ b/core/src/tests/OC/mimeType.spec.ts @@ -3,22 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { join } from 'node:path' -import { beforeEach, describe, expect, it, vi } from 'vitest' +import { beforeEach, describe, expect, it, test } from 'vitest' +import { clearIconCache, getIconUrl } from '../../OC/mimeType.js' -const generateUrl = vi.hoisted(() => vi.fn((url) => join('/ROOT', url))) - -vi.mock('@nextcloud/router', () => ({ - generateUrl, -})) - -beforeEach(() => { - vi.resetModules() - vi.resetAllMocks() -}) - -describe('OC.MimeType tests', async () => { - beforeEach(async () => { +describe('OC.MimeType tests', () => { + beforeEach(() => { window.OC.MimeTypeList = { aliases: { 'app/foobar': 'foo/bar' }, files: ['folder', 'folder-shared', 'folder-external', 'foo-bar', 'foo', 'file'], @@ -26,11 +15,42 @@ describe('OC.MimeType tests', async () => { abc: ['folder'], }, } + // @ts-expect-error - mocking global variable + window._oc_webroot = '/ROOT' + // setup for legacy theme + window.OC.theme ??= {} + window.OC.theme.folder = '' + // the theming app is always enabled since Nextcloud 20 + window.OCA.Theming ??= {} + window.OCA.Theming.cacheBuster = '1cacheBuster2' + clearIconCache() + }) + + test('uses icon cache if availble', async () => { + window.OC.theme.folder = 'abc' + expect(getIconUrl('dir')).toEqual('/ROOT/themes/abc/core/img/filetypes/folder.svg?v=1cacheBuster2') + window.OC.theme.folder = '' + expect(getIconUrl('dir')).toEqual('/ROOT/themes/abc/core/img/filetypes/folder.svg?v=1cacheBuster2') + clearIconCache() + expect(getIconUrl('dir')).toEqual('/ROOT/index.php/apps/theming/img/core/filetypes/folder.svg?v=1cacheBuster2') }) - describe('no theme', async () => { - beforeEach(async () => { - window.OC.theme ??= {} + describe('with legacy themes', async () => { + beforeEach(() => { + window.OC.theme.folder = 'abc' + }) + + it('uses theme path if a theme icon is availble', async () => { + expect(getIconUrl('dir')).toEqual('/ROOT/themes/abc/core/img/filetypes/folder.svg?v=1cacheBuster2') + }) + + it('fallbacks to the default theme if no icon is available in the theme', async () => { + expect(getIconUrl('dir-shared')).toEqual('/ROOT/index.php/apps/theming/img/core/filetypes/folder-shared.svg?v=1cacheBuster2') + }) + }) + + describe('no legacy theme', async () => { + beforeEach(() => { window.OC.theme.folder = '' }) @@ -47,72 +67,24 @@ describe('OC.MimeType tests', async () => { // return the file mimetype if we have no matching icon but do have a file icon { mimeType: 'foobar', icon: 'file' }, ])('returns correct icon', async ({ icon, mimeType }) => { - const { getIconUrl } = await getMethod() - expect(getIconUrl(mimeType)).toEqual(`/ROOT/core/img/filetypes/${icon}.svg`) + expect(getIconUrl(mimeType)).toEqual(`/ROOT/index.php/apps/theming/img/core/filetypes/${icon}.svg?v=1cacheBuster2`) }) it('returns undefined if the an icon for undefined is requested', async () => { - const { getIconUrl } = await getMethod() + // @ts-expect-error - testing invalid input expect(getIconUrl(undefined)).toEqual(undefined) }) - it('uses the cache if available', async () => { - const { getIconUrl } = await getMethod() - expect(generateUrl).not.toHaveBeenCalled() - - expect(getIconUrl('dir')).toEqual('/ROOT/core/img/filetypes/folder.svg') - expect(generateUrl).toHaveBeenCalledTimes(1) - - expect(getIconUrl('dir')).toEqual('/ROOT/core/img/filetypes/folder.svg') - expect(generateUrl).toHaveBeenCalledTimes(1) - - expect(getIconUrl('dir-shared')).toEqual('/ROOT/core/img/filetypes/folder-shared.svg') - expect(generateUrl).toHaveBeenCalledTimes(2) - }) - it('converts aliases correctly', async () => { - const { getIconUrl } = await getMethod() - expect(getIconUrl('app/foobar')).toEqual('/ROOT/core/img/filetypes/foo-bar.svg') - }) - }) - - describe('with legacy themes', async () => { - beforeEach(async () => { - window.OC.theme ??= {} - window.OC.theme.folder = 'abc' - }) - - it('uses theme path if a theme icon is availble', async () => { - const { getIconUrl } = await getMethod() - expect(getIconUrl('dir')).toEqual('/ROOT/themes/abc/core/img/filetypes/folder.svg') - }) - - it('fallbacks to the default theme if no icon is available in the theme', async () => { - const { getIconUrl } = await getMethod() - expect(getIconUrl('dir-shared')).toEqual('/ROOT/core/img/filetypes/folder-shared.svg') - }) - }) - - describe('with theming app', async () => { - beforeEach(async () => { - window.OC.theme ??= {} - window.OC.theme.folder = '' - window.OCA.Theming ??= {} - window.OCA.Theming.cacheBuster = '1cacheBuster2' + expect(getIconUrl('app/foobar')).toEqual('/ROOT/index.php/apps/theming/img/core/filetypes/foo-bar.svg?v=1cacheBuster2') }) it('uses the correct theming URL', async () => { - const { getIconUrl } = await getMethod() - expect(getIconUrl('dir')).toMatch('/apps/theming/img/core/filetypes/folder.svg') + expect(getIconUrl('dir')).toMatch('/ROOT/index.php/apps/theming/img/core/filetypes/folder.svg?v=1cacheBuster2') }) it('uses the cache buster', async () => { - const { getIconUrl } = await getMethod() expect(getIconUrl('file')).toMatch(/\?v=1cacheBuster2$/) }) }) }) - -async function getMethod() { - return await import('../../OC/mimeType.js') -}