chore: adjust vitest infrastructure to properly run both suits

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
pull/56588/head
Ferdinand Thiessen 2025-11-24 16:23:02 +07:00
parent 1b0dd02337
commit 2a8c20b946
21 changed files with 111 additions and 74 deletions

@ -0,0 +1 @@
build/frontend/__mocks__

@ -0,0 +1 @@
build/frontend/__tests__

@ -22,7 +22,9 @@ vi.mock('@nextcloud/axios', async (origial) => ({ ...(await origial()), default:
vi.mock('@nextcloud/auth')
const errorSpy = vi.spyOn(window.console, 'error').mockImplementation(() => {})
beforeEach(() => errorSpy.mockClear())
beforeEach(() => {
vi.resetAllMocks()
})
describe('files_trashbin: file actions - restore action', () => {
it('has id set', () => {

@ -6,12 +6,24 @@
import * as ncDialogs from '@nextcloud/dialogs'
import * as ncEventBus from '@nextcloud/event-bus'
import { Folder } from '@nextcloud/files'
import * as ncInitialState from '@nextcloud/initial-state'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { trashbinView } from '../files_views/trashbinView.ts'
import * as api from '../services/api.ts'
import { emptyTrashAction } from './emptyTrashAction.ts'
const loadState = vi.hoisted(() => vi.fn((app, key, fallback) => {
if (fallback) {
return fallback
}
throw new Error()
}))
vi.mock('@nextcloud/initial-state', () => ({
loadState,
}))
beforeEach(() => vi.resetAllMocks())
describe('files_trashbin: file list actions - empty trashbin', () => {
it('has id set', () => {
expect(emptyTrashAction.id).toBe('empty-trash')
@ -27,7 +39,7 @@ describe('files_trashbin: file list actions - empty trashbin', () => {
})
it('is enabled on trashbin view', () => {
const spy = vi.spyOn(ncInitialState, 'loadState').mockImplementationOnce(() => ({ allow_delete: true }))
loadState.mockImplementation(() => ({ allow_delete: true }))
const root = new Folder({ owner: 'test', source: 'https://example.com/remote.php/dav/trashbin/test/', root: '/trashbin/test/' })
const nodes = [
@ -36,12 +48,12 @@ describe('files_trashbin: file list actions - empty trashbin', () => {
expect(emptyTrashAction.enabled).toBeTypeOf('function')
expect(emptyTrashAction.enabled!(trashbinView, nodes, root)).toBe(true)
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenCalledWith('files_trashbin', 'config')
expect(loadState).toHaveBeenCalled()
expect(loadState).toHaveBeenCalledWith('files_trashbin', 'config')
})
it('is not enabled on another view enabled', () => {
vi.spyOn(ncInitialState, 'loadState').mockImplementationOnce(() => ({ allow_delete: true }))
loadState.mockImplementation(() => ({ allow_delete: true }))
const root = new Folder({ owner: 'test', source: 'https://example.com/remote.php/dav/trashbin/test/', root: '/trashbin/test/' })
const nodes = [
@ -62,7 +74,7 @@ describe('files_trashbin: file list actions - empty trashbin', () => {
})
it('is not enabled when deletion is forbidden', () => {
const spy = vi.spyOn(ncInitialState, 'loadState').mockImplementationOnce(() => ({ allow_delete: false }))
loadState.mockImplementation(() => ({ allow_delete: false }))
const root = new Folder({ owner: 'test', source: 'https://example.com/remote.php/dav/trashbin/test/', root: '/trashbin/test/' })
const nodes = [
@ -71,13 +83,12 @@ describe('files_trashbin: file list actions - empty trashbin', () => {
expect(emptyTrashAction.enabled).toBeTypeOf('function')
expect(emptyTrashAction.enabled!(trashbinView, nodes, root)).toBe(false)
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenCalledWith('files_trashbin', 'config')
expect(loadState).toHaveBeenCalled()
expect(loadState).toHaveBeenCalledWith('files_trashbin', 'config')
})
it('is not enabled when not in trashbin root', () => {
vi.spyOn(ncInitialState, 'loadState').mockImplementationOnce(() => ({ allow_delete: true }))
loadState.mockImplementation(() => ({ allow_delete: true }))
const root = new Folder({ owner: 'test', source: 'https://example.com/remote.php/dav/trashbin/test/other-folder', root: '/trashbin/test/' })
const nodes = [
new Folder({ owner: 'test', source: 'https://example.com/remote.php/dav/trashbin/test/folder', root: '/trashbin/test/' }),
@ -101,6 +112,7 @@ describe('files_trashbin: file list actions - empty trashbin', () => {
}
beforeEach(() => {
vi.resetAllMocks()
dialogBuilder = {
setSeverity: vi.fn(() => dialogBuilder),
setText: vi.fn(() => dialogBuilder),

@ -7,6 +7,7 @@ import vue from '@vitejs/plugin-vue2'
import { exec } from 'node:child_process'
import { resolve } from 'node:path'
import { promisify } from 'node:util'
import { nodePolyfills } from 'vite-plugin-node-polyfills'
import { defaultExclude, defineConfig } from 'vitest/config'
const gitIgnore: string[] = []
@ -26,7 +27,13 @@ try {
}
export default defineConfig({
plugins: [vue()],
plugins: [
nodePolyfills({
include: ['fs', 'path'],
protocolImports: false,
}),
vue(),
],
root: import.meta.dirname,
resolve: {
preserveSymlinks: true,

@ -11,10 +11,10 @@
"dev": "NODE_ENV=development vite build --mode development",
"lint": "eslint --suppressions-location ../eslint-baseline.json --no-error-on-unmatched-pattern ./apps/*/ ./core/",
"lint:fix": "eslint --suppressions-location ../eslint-baseline.json --fix --no-error-on-unmatched-pattern ./apps/*/ ./core/",
"test": "vitest -c ../../vitest.config.ts run",
"test:coverage": "vitest -c ../../vitest.config.ts run --coverage --reporter=default",
"test:update-snapshots": "vitest -c ../../vitest.config.ts run --update",
"test:watch": "vitest -c ../../vitest.config.ts watch",
"test": "vitest run",
"test:coverage": "vitest run --coverage --reporter=default",
"test:update-snapshots": "vitest run --update",
"test:watch": "vitest watch",
"watch": "NODE_ENV=development vite build --mode development --watch"
},
"browserslist": [

@ -0,0 +1,66 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: CC0-1.0
*/
import vue from '@vitejs/plugin-vue'
import { exec } from 'node:child_process'
import { resolve } from 'node:path'
import { promisify } from 'node:util'
import { defaultExclude, defineConfig } from 'vitest/config'
const gitIgnore: string[] = []
// get all files ignored in the apps directory (e.g. if putting `view` app there).
try {
const execAsync = promisify(exec)
const { stdout } = await execAsync('git check-ignore apps/*', { cwd: __dirname })
gitIgnore.push(...stdout.split('\n').filter(Boolean))
// eslint-disable-next-line no-console
console.log('Git ignored files excluded from tests: ', gitIgnore)
} catch (error) {
// we can ignore error code 1 as this just means there are no ignored files
if (!error || typeof error !== 'object' || !('code' in error) || error.code !== 1) {
// but otherwise something bad is happening and we should re-throw
throw error
}
}
export default defineConfig({
plugins: [vue()],
root: resolve(import.meta.dirname),
resolve: {
preserveSymlinks: true,
},
test: {
include: ['apps/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
environment: 'jsdom',
environmentOptions: {
jsdom: {
url: 'http://nextcloud.local',
},
},
coverage: {
include: [
'apps/*/src/**',
/* 'core/src/**', */
],
exclude: ['**.spec.*', '**.test.*', '**.cy.*', 'core/src/tests/**'],
provider: 'v8',
reporter: ['lcov', 'text'],
},
setupFiles: [
resolve(import.meta.dirname, '__tests__/mock-window.js'),
resolve(import.meta.dirname, '__tests__/setup-testing-library.js'),
],
exclude: [
...defaultExclude,
...gitIgnore,
],
globalSetup: resolve(import.meta.dirname, '__tests__/setup-global.js'),
server: {
deps: {
inline: [/@nextcloud\//],
},
},
},
})

@ -29,10 +29,10 @@
"sass:watch": "sass --watch --load-path core/css core/css/ $(for cssdir in $(find apps -mindepth 2 -maxdepth 2 -name \"css\"); do if ! $(git check-ignore -q $cssdir); then printf \"$cssdir \"; fi; done)",
"stylelint": "stylelint $(for appdir in $(ls apps); do if ! $(git check-ignore -q \"apps/$appdir\"); then printf \"'apps/$appdir/**/*.{scss,vue}' \"; fi; done) 'core/**/*.{scss,vue}'",
"stylelint:fix": "npm run stylelint -- --fix",
"test": "vitest run; cd build/frontend-legacy && npm run test",
"test:coverage": "vitest run --coverage --reporter=default",
"test:update-snapshots": "vitest run --update; cd build/frontend-legacy && npm run test:update-snapshots",
"test:watch": "concurrently 'vitest watch' 'cd build/frontend-legacy && npm run test:watch'",
"test": "build/demi.sh test",
"test:coverage": "build/demi.sh test:coverage",
"test:update-snapshots": "build/demi.sh test:update-snapshots",
"test:watch": "build/demi.sh --parallel test:watch",
"watch": "build/demi.sh --parallel watch"
},
"browserslist": [

@ -3,57 +3,5 @@
* SPDX-License-Identifier: CC0-1.0
*/
import vue from '@vitejs/plugin-vue'
import { exec } from 'node:child_process'
import { resolve } from 'node:path'
import { promisify } from 'node:util'
import { defaultExclude, defineConfig } from 'vitest/config'
const gitIgnore: string[] = []
// get all files ignored in the apps directory (e.g. if putting `view` app there).
try {
const execAsync = promisify(exec)
const { stdout } = await execAsync('git check-ignore apps/*', { cwd: __dirname })
gitIgnore.push(...stdout.split('\n').filter(Boolean))
// eslint-disable-next-line no-console
console.log('Git ignored files excluded from tests: ', gitIgnore)
} catch (error) {
// we can ignore error code 1 as this just means there are no ignored files
if (error.code !== 1) {
// but otherwise something bad is happening and we should re-throw
throw error
}
}
export default defineConfig({
plugins: [vue()],
test: {
include: ['build/frontend/apps/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
environment: 'jsdom',
environmentOptions: {
jsdom: {
url: 'http://nextcloud.local',
},
},
coverage: {
include: ['build/frontend/apps/*/src/**', 'build/frontend/core/src/**'],
exclude: ['**.spec.*', '**.test.*', '**.cy.*', 'core/src/tests/**'],
provider: 'v8',
reporter: ['lcov', 'text'],
},
setupFiles: [
resolve(import.meta.dirname, '__tests__/mock-window.js'),
resolve(import.meta.dirname, '__tests__/setup-testing-library.js'),
],
exclude: [
...defaultExclude,
...gitIgnore,
],
globalSetup: resolve(import.meta.dirname, '__tests__/setup-global.js'),
server: {
deps: {
inline: [/@nextcloud\//],
},
},
},
})
// stub - for the moment see build/frontend/vitest.config.ts
export default {}