Merge pull request #47466 from nextcloud/chore/migrate-vitest

test: Migrate from Jest to vitest
pull/47465/head
Ferdinand Thiessen 2024-08-26 20:30:26 +07:00 committed by GitHub
commit 4fcf4dba02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 10014 additions and 12922 deletions

@ -1,12 +0,0 @@
/**
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import '@testing-library/jest-dom'
// Mock `window.location` with Jest spies and extend expect
import 'jest-location-mock'
// Mock `window.fetch` with Jest
import 'jest-fetch-mock'

@ -2,7 +2,12 @@
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { beforeEach } from 'vitest'
window.OC = { ...window.OC }
window.OCA = { ...window.OCA }
window.OCP = { ...window.OCP }
beforeEach(() => {
window.location = new URL('http://nextcloud.local')
})

@ -0,0 +1,5 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: CC0-1.0
*/
import '@testing-library/jest-dom/vitest'

@ -1,6 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"verbatimModuleSyntax": false
}
}

@ -2,9 +2,10 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './inlineUnreadCommentsAction'
import { expect } from '@jest/globals'
import { File, Permission, View, FileAction } from '@nextcloud/files'
import { describe, expect, test, vi } from 'vitest'
import { action } from './inlineUnreadCommentsAction'
import logger from '../logger'
const view = {
@ -29,7 +30,7 @@ describe('Inline unread comments action display name tests', () => {
expect(action.id).toBe('comments-unread')
expect(action.displayName([file], view)).toBe('')
expect(action.title!([file], view)).toBe('1 new comment')
expect(action.iconSvgInline([], view)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([], view)).toMatch(/<svg.+<\/svg>/)
expect(action.enabled!([file], view)).toBe(true)
expect(action.inline!(file, view)).toBe(true)
expect(action.default).toBeUndefined()
@ -115,8 +116,8 @@ describe('Inline unread comments action enabled tests', () => {
describe('Inline unread comments action execute tests', () => {
test('Action opens sidebar', async () => {
const openMock = jest.fn()
const setActiveTabMock = jest.fn()
const openMock = vi.fn()
const setActiveTabMock = vi.fn()
window.OCA = {
Files: {
Sidebar: {
@ -145,8 +146,8 @@ describe('Inline unread comments action execute tests', () => {
})
test('Action handles sidebar open failure', async () => {
const openMock = jest.fn(() => { throw new Error('Mock error') })
const setActiveTabMock = jest.fn()
const openMock = vi.fn(() => { throw new Error('Mock error') })
const setActiveTabMock = vi.fn()
window.OCA = {
Files: {
Sidebar: {
@ -155,7 +156,7 @@ describe('Inline unread comments action execute tests', () => {
},
},
}
jest.spyOn(logger, 'error').mockImplementation(() => jest.fn())
vi.spyOn(logger, 'error').mockImplementation(() => vi.fn())
const file = new File({
id: 1,

@ -3,40 +3,33 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { render } from '@testing-library/vue'
import { beforeEach, describe, expect, test, vi } from 'vitest'
import CalDavSettings from './CalDavSettings.vue'
// eslint-disable-next-line no-unused-vars
import { generateUrl } from '@nextcloud/router'
jest.mock('@nextcloud/axios')
jest.mock('@nextcloud/router', () => {
vi.mock('@nextcloud/axios')
vi.mock('@nextcloud/router', () => {
return {
generateUrl(url) {
return url
},
}
})
jest.mock('@nextcloud/initial-state', () => {
vi.mock('@nextcloud/initial-state', () => {
return {
loadState: jest.fn(() => 'https://docs.nextcloud.com/server/23/go.php?to=user-sync-calendars'),
loadState: vi.fn(() => 'https://docs.nextcloud.com/server/23/go.php?to=user-sync-calendars'),
}
})
describe('CalDavSettings', () => {
const originalOC = global.OC
const originalOCP = global.OCP
beforeEach(() => {
global.OC = { requestToken: 'secret' }
global.OCP = {
window.OC = { requestToken: 'secret' }
window.OCP = {
AppConfig: {
setValue: jest.fn(),
setValue: vi.fn(),
},
}
})
afterAll(() => {
global.OC = originalOC
global.OCP = originalOCP
})
test('interactions', async () => {
const TLUtils = render(
@ -53,7 +46,7 @@ describe('CalDavSettings', () => {
},
},
Vue => {
Vue.prototype.$t = jest.fn((app, text) => text)
Vue.prototype.$t = vi.fn((app, text) => text)
},
)
expect(TLUtils.container).toMatchSnapshot()

@ -1,4 +1,437 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`CalDavSettings > interactions 1`] = `
<div>
<div
class="settings-section settings-section--limit-width"
data-v-0974f50a=""
data-v-6b8d4c30=""
>
<h2
class="settings-section__name"
data-v-0974f50a=""
>
Calendar server
<a
aria-label="External documentation for Calendar server"
class="settings-section__info"
data-v-0974f50a=""
href="https://docs.nextcloud.com/server/23/go.php?to=user-sync-calendars"
rel="noreferrer nofollow"
target="_blank"
title="External documentation for Calendar server"
>
<span
aria-hidden="true"
class="material-design-icon help-circle-icon"
data-v-0974f50a=""
role="img"
>
<svg
class="material-design-icon__svg"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
>
<path
d="M15.07,11.25L14.17,12.17C13.45,12.89 13,13.5 13,15H11V14.5C11,13.39 11.45,12.39 12.17,11.67L13.41,10.41C13.78,10.05 14,9.55 14,9C14,7.89 13.1,7 12,7A2,2 0 0,0 10,9H8A4,4 0 0,1 12,5A4,4 0 0,1 16,9C16,9.88 15.64,10.67 15.07,11.25M13,19H11V17H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z"
>
<!---->
</path>
</svg>
</span>
</a>
</h2>
<!---->
<p
class="settings-hint"
data-v-0974f50a=""
data-v-6b8d4c30=""
>
Also install the
<a
href="../apps/office/calendar"
target="_blank"
>
Calendar app
</a>
, or
<a
href="https://docs.nextcloud.com/server/23/go.php?to=user-sync-calendars"
rel="noreferrer noopener"
target="_blank"
>
connect your desktop & mobile for syncing ↗
</a>
.
</p>
<p
data-v-0974f50a=""
data-v-6b8d4c30=""
>
<span
class="checkbox-radio-switch checkbox-radio-switch-switch checkbox-radio-switch--checked"
data-v-0974f50a=""
data-v-6b8d4c30=""
data-v-feaabebe=""
style="--icon-size: 36px; --icon-height: 16px;"
>
<input
aria-labelledby="caldavSendInvitations-label"
class="checkbox-radio-switch__input"
data-v-feaabebe=""
id="caldavSendInvitations"
type="checkbox"
value=""
/>
<span
class="checkbox-content checkbox-radio-switch__content checkbox-content-switch checkbox-content--has-text"
data-v-e75842d8=""
data-v-feaabebe=""
id="caldavSendInvitations-label"
>
<span
aria-hidden="true"
class="checkbox-content__icon checkbox-content__icon--checked checkbox-radio-switch__icon"
data-v-e75842d8=""
inert="inert"
>
<span
aria-hidden="true"
class="material-design-icon toggle-switch-icon"
data-v-e75842d8=""
role="img"
>
<svg
class="material-design-icon__svg"
fill="currentColor"
height="36"
viewBox="0 0 24 24"
width="36"
>
<path
d="M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z"
>
<!---->
</path>
</svg>
</span>
</span>
<span
class="checkbox-content__text checkbox-radio-switch__text"
data-v-e75842d8=""
>
Send invitations to attendees
</span>
</span>
</span>
<em
data-v-0974f50a=""
data-v-6b8d4c30=""
>
Please make sure to properly set up
<a
href="../admin#mail_general_settings"
>
the email server
</a>
.
</em>
</p>
<p
data-v-0974f50a=""
data-v-6b8d4c30=""
>
<span
class="checkbox-radio-switch checkbox checkbox-radio-switch-switch checkbox-radio-switch--checked"
data-v-0974f50a=""
data-v-6b8d4c30=""
data-v-feaabebe=""
style="--icon-size: 36px; --icon-height: 16px;"
>
<input
aria-labelledby="caldavGenerateBirthdayCalendar-label"
class="checkbox-radio-switch__input"
data-v-feaabebe=""
id="caldavGenerateBirthdayCalendar"
type="checkbox"
value=""
/>
<span
class="checkbox-content checkbox-radio-switch__content checkbox-content-switch checkbox-content--has-text"
data-v-e75842d8=""
data-v-feaabebe=""
id="caldavGenerateBirthdayCalendar-label"
>
<span
aria-hidden="true"
class="checkbox-content__icon checkbox-content__icon--checked checkbox-radio-switch__icon"
data-v-e75842d8=""
inert="inert"
>
<span
aria-hidden="true"
class="material-design-icon toggle-switch-icon"
data-v-e75842d8=""
role="img"
>
<svg
class="material-design-icon__svg"
fill="currentColor"
height="36"
viewBox="0 0 24 24"
width="36"
>
<path
d="M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z"
>
<!---->
</path>
</svg>
</span>
</span>
<span
class="checkbox-content__text checkbox-radio-switch__text"
data-v-e75842d8=""
>
Automatically generate a birthday calendar
</span>
</span>
</span>
<em
data-v-0974f50a=""
data-v-6b8d4c30=""
>
Birthday calendars will be generated by a background job.
</em>
<br
data-v-0974f50a=""
data-v-6b8d4c30=""
/>
<em
data-v-0974f50a=""
data-v-6b8d4c30=""
>
Hence they will not be available immediately after enabling but will show up after some time.
</em>
</p>
<p
data-v-0974f50a=""
data-v-6b8d4c30=""
>
<span
class="checkbox-radio-switch checkbox-radio-switch-switch checkbox-radio-switch--checked"
data-v-0974f50a=""
data-v-6b8d4c30=""
data-v-feaabebe=""
style="--icon-size: 36px; --icon-height: 16px;"
>
<input
aria-labelledby="caldavSendEventReminders-label"
class="checkbox-radio-switch__input"
data-v-feaabebe=""
id="caldavSendEventReminders"
type="checkbox"
value=""
/>
<span
class="checkbox-content checkbox-radio-switch__content checkbox-content-switch checkbox-content--has-text"
data-v-e75842d8=""
data-v-feaabebe=""
id="caldavSendEventReminders-label"
>
<span
aria-hidden="true"
class="checkbox-content__icon checkbox-content__icon--checked checkbox-radio-switch__icon"
data-v-e75842d8=""
inert="inert"
>
<span
aria-hidden="true"
class="material-design-icon toggle-switch-icon"
data-v-e75842d8=""
role="img"
>
<svg
class="material-design-icon__svg"
fill="currentColor"
height="36"
viewBox="0 0 24 24"
width="36"
>
<path
d="M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z"
>
<!---->
</path>
</svg>
</span>
</span>
<span
class="checkbox-content__text checkbox-radio-switch__text"
data-v-e75842d8=""
>
Send notifications for events
</span>
</span>
</span>
<em
data-v-0974f50a=""
data-v-6b8d4c30=""
>
Please make sure to properly set up
<a
href="../admin#mail_general_settings"
>
the email server
</a>
.
</em>
<br
data-v-0974f50a=""
data-v-6b8d4c30=""
/>
<em
data-v-0974f50a=""
data-v-6b8d4c30=""
>
Notifications are sent via background jobs, so these must occur often enough.
</em>
</p>
<p
class="indented"
data-v-0974f50a=""
data-v-6b8d4c30=""
>
<span
class="checkbox-radio-switch checkbox-radio-switch-switch checkbox-radio-switch--checked"
data-v-0974f50a=""
data-v-6b8d4c30=""
data-v-feaabebe=""
style="--icon-size: 36px; --icon-height: 16px;"
>
<input
aria-labelledby="caldavSendEventRemindersToSharedGroupMembers-label"
class="checkbox-radio-switch__input"
data-v-feaabebe=""
id="caldavSendEventRemindersToSharedGroupMembers"
type="checkbox"
value=""
/>
<span
class="checkbox-content checkbox-radio-switch__content checkbox-content-switch checkbox-content--has-text"
data-v-e75842d8=""
data-v-feaabebe=""
id="caldavSendEventRemindersToSharedGroupMembers-label"
>
<span
aria-hidden="true"
class="checkbox-content__icon checkbox-content__icon--checked checkbox-radio-switch__icon"
data-v-e75842d8=""
inert="inert"
>
<span
aria-hidden="true"
class="material-design-icon toggle-switch-icon"
data-v-e75842d8=""
role="img"
>
<svg
class="material-design-icon__svg"
fill="currentColor"
height="36"
viewBox="0 0 24 24"
width="36"
>
<path
d="M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z"
>
<!---->
</path>
</svg>
</span>
</span>
<span
class="checkbox-content__text checkbox-radio-switch__text"
data-v-e75842d8=""
>
Send reminder notifications to calendar sharees as well
</span>
</span>
</span>
<em
data-v-0974f50a=""
data-v-6b8d4c30=""
>
Reminders are always sent to organizers and attendees.
</em>
</p>
<p
class="indented"
data-v-0974f50a=""
data-v-6b8d4c30=""
>
<span
class="checkbox-radio-switch checkbox-radio-switch-switch checkbox-radio-switch--checked"
data-v-0974f50a=""
data-v-6b8d4c30=""
data-v-feaabebe=""
style="--icon-size: 36px; --icon-height: 16px;"
>
<input
aria-labelledby="caldavSendEventRemindersPush-label"
class="checkbox-radio-switch__input"
data-v-feaabebe=""
id="caldavSendEventRemindersPush"
type="checkbox"
value=""
/>
<span
class="checkbox-content checkbox-radio-switch__content checkbox-content-switch checkbox-content--has-text"
data-v-e75842d8=""
data-v-feaabebe=""
id="caldavSendEventRemindersPush-label"
>
<span
aria-hidden="true"
class="checkbox-content__icon checkbox-content__icon--checked checkbox-radio-switch__icon"
data-v-e75842d8=""
inert="inert"
>
<span
aria-hidden="true"
class="material-design-icon toggle-switch-icon"
data-v-e75842d8=""
role="img"
>
<svg
class="material-design-icon__svg"
fill="currentColor"
height="36"
viewBox="0 0 24 24"
width="36"
>
<path
d="M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z"
>
<!---->
</path>
</svg>
</span>
</span>
<span
class="checkbox-content__text checkbox-radio-switch__text"
data-v-e75842d8=""
>
Enable notifications for events via push
</span>
</span>
</span>
</p>
</div>
</div>
`;
exports[`CalDavSettings interactions 1`] = `
<div>

@ -2,15 +2,20 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './deleteAction'
import { expect } from '@jest/globals'
import { File, Folder, Permission, View, FileAction } from '@nextcloud/files'
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
import axios from '@nextcloud/axios'
import * as capabilities from '@nextcloud/capabilities'
import eventBus from '@nextcloud/event-bus'
import * as eventBus from '@nextcloud/event-bus'
import { action } from './deleteAction'
import logger from '../logger'
vi.mock('@nextcloud/auth')
vi.mock('@nextcloud/axios')
vi.mock('@nextcloud/capabilities')
const view = {
id: 'files',
name: 'Files',
@ -22,8 +27,8 @@ const trashbinView = {
} as View
describe('Delete action conditions tests', () => {
afterEach(() => {
jest.restoreAllMocks()
beforeEach(() => {
vi.restoreAllMocks()
})
const file = new File({
@ -82,7 +87,7 @@ describe('Delete action conditions tests', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('delete')
expect(action.displayName([file], view)).toBe('Delete file')
expect(action.iconSvgInline([], view)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([], view)).toMatch(/<svg.+<\/svg>/)
expect(action.default).toBeUndefined()
expect(action.order).toBe(100)
})
@ -96,7 +101,7 @@ describe('Delete action conditions tests', () => {
})
test('Trashbin disabled displayName', () => {
jest.spyOn(capabilities, 'getCapabilities').mockImplementation(() => {
vi.spyOn(capabilities, 'getCapabilities').mockImplementation(() => {
return {
files: {},
}
@ -176,11 +181,11 @@ describe('Delete action enabled tests', () => {
describe('Delete action execute tests', () => {
afterEach(() => {
jest.restoreAllMocks()
vi.restoreAllMocks()
})
test('Delete action', async () => {
jest.spyOn(axios, 'delete')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'delete')
vi.spyOn(eventBus, 'emit')
const file = new File({
id: 1,
@ -201,10 +206,10 @@ describe('Delete action execute tests', () => {
})
test('Delete action batch', async () => {
jest.spyOn(axios, 'delete')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'delete')
vi.spyOn(eventBus, 'emit')
const confirmMock = jest.fn()
const confirmMock = vi.fn()
// @ts-expect-error We only mock what needed
window.OC = { dialogs: { confirmDestructive: confirmMock } }
@ -240,11 +245,11 @@ describe('Delete action execute tests', () => {
})
test('Delete action batch large set', async () => {
jest.spyOn(axios, 'delete')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'delete')
vi.spyOn(eventBus, 'emit')
// Emulate the confirmation dialog to always confirm
const confirmMock = jest.fn().mockImplementation((a, b, c, resolve) => resolve(true))
const confirmMock = vi.fn().mockImplementation((a, b, c, resolve) => resolve(true))
// @ts-expect-error We only mock what needed
window.OC = { dialogs: { confirmDestructive: confirmMock } }
@ -310,16 +315,16 @@ describe('Delete action execute tests', () => {
})
test('Delete action batch trashbin disabled', async () => {
jest.spyOn(axios, 'delete')
jest.spyOn(eventBus, 'emit')
jest.spyOn(capabilities, 'getCapabilities').mockImplementation(() => {
vi.spyOn(axios, 'delete')
vi.spyOn(eventBus, 'emit')
vi.spyOn(capabilities, 'getCapabilities').mockImplementation(() => {
return {
files: {},
}
})
// Emulate the confirmation dialog to always confirm
const confirmMock = jest.fn().mockImplementation((a, b, c, resolve) => resolve(true))
const confirmMock = vi.fn().mockImplementation((a, b, c, resolve) => resolve(true))
// @ts-expect-error We only mock what needed
window.OC = { dialogs: { confirmDestructive: confirmMock } }
@ -355,9 +360,9 @@ describe('Delete action execute tests', () => {
})
test('Delete fails', async () => {
jest.spyOn(axios, 'delete').mockImplementation(() => { throw new Error('Mock error') })
jest.spyOn(logger, 'error').mockImplementation(() => jest.fn())
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'delete').mockImplementation(() => { throw new Error('Mock error') })
vi.spyOn(logger, 'error').mockImplementation(() => vi.fn())
vi.spyOn(eventBus, 'emit')
const file = new File({
id: 1,
@ -378,16 +383,16 @@ describe('Delete action execute tests', () => {
})
test('Delete is cancelled', async () => {
jest.spyOn(axios, 'delete')
jest.spyOn(eventBus, 'emit')
jest.spyOn(capabilities, 'getCapabilities').mockImplementation(() => {
vi.spyOn(axios, 'delete')
vi.spyOn(eventBus, 'emit')
vi.spyOn(capabilities, 'getCapabilities').mockImplementation(() => {
return {
files: {},
}
})
// Emulate the confirmation dialog to always confirm
const confirmMock = jest.fn().mockImplementation((a, b, c, resolve) => resolve(false))
const confirmMock = vi.fn().mockImplementation((a, b, c, resolve) => resolve(false))
// @ts-expect-error We only mock what needed
window.OC = { dialogs: { confirmDestructive: confirmMock } }

@ -2,9 +2,10 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './downloadAction'
import { expect } from '@jest/globals'
import { File, Folder, Permission, View, FileAction, DefaultType } from '@nextcloud/files'
import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'
import { action } from './downloadAction'
const view = {
id: 'files',
@ -22,7 +23,7 @@ describe('Download action conditions tests', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('download')
expect(action.displayName([], view)).toBe('Download')
expect(action.iconSvgInline([], view)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([], view)).toMatch(/<svg.+<\/svg>/)
expect(action.default).toBe(DefaultType.DEFAULT)
expect(action.order).toBe(30)
})
@ -83,11 +84,12 @@ describe('Download action enabled tests', () => {
describe('Download action execute tests', () => {
const link = {
click: jest.fn(),
click: vi.fn(),
} as unknown as HTMLAnchorElement
beforeEach(() => {
jest.spyOn(document, 'createElement').mockImplementation(() => link)
vi.resetAllMocks()
vi.spyOn(document, 'createElement').mockImplementation(() => link)
})
test('Download single file', async () => {

@ -2,25 +2,15 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './editLocallyAction'
import { expect } from '@jest/globals'
import { File, Permission, View, FileAction } from '@nextcloud/files'
import { DialogBuilder, showError } from '@nextcloud/dialogs'
import axios from '@nextcloud/axios'
import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'
const dialogBuilder = {
setName: jest.fn().mockReturnThis(),
setText: jest.fn().mockReturnThis(),
setButtons: jest.fn().mockReturnThis(),
build: jest.fn().mockReturnValue({
show: jest.fn().mockResolvedValue(true),
}),
} as unknown as DialogBuilder
import axios from '@nextcloud/axios'
import * as nextcloudDialogs from '@nextcloud/dialogs'
import { action } from './editLocallyAction'
jest.mock('@nextcloud/dialogs', () => ({
DialogBuilder: jest.fn(() => dialogBuilder),
showError: jest.fn(),
}))
vi.mock('@nextcloud/auth')
vi.mock('@nextcloud/axios')
const view = {
id: 'files',
@ -31,7 +21,7 @@ const view = {
beforeAll(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any)._oc_webroot = '';
(window as any).OCA = { Viewer: { open: jest.fn() } }
(window as any).OCA = { Viewer: { open: vi.fn() } }
})
describe('Edit locally action conditions tests', () => {
@ -39,7 +29,7 @@ describe('Edit locally action conditions tests', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('edit-locally')
expect(action.displayName([], view)).toBe('Edit locally')
expect(action.iconSvgInline([], view)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([], view)).toMatch(/<svg.+<\/svg>/)
expect(action.default).toBeUndefined()
expect(action.order).toBe(25)
})
@ -118,16 +108,22 @@ describe('Edit locally action enabled tests', () => {
})
describe('Edit locally action execute tests', () => {
let spyShowDialog
beforeEach(() => {
vi.resetAllMocks()
spyShowDialog = vi.spyOn(nextcloudDialogs.Dialog.prototype, 'show')
.mockImplementation(() => Promise.resolve())
})
test('Edit locally opens proper URL', async () => {
jest.spyOn(axios, 'post').mockImplementation(async () => ({
data: { ocs: { data: { token: 'foobar' } } }
vi.spyOn(axios, 'post').mockImplementation(async () => ({
data: { ocs: { data: { token: 'foobar' } } },
}))
const mockedShowError = jest.mocked(showError)
const spyDialogBuilder = jest.spyOn(dialogBuilder, 'build')
const showError = vi.spyOn(nextcloudDialogs, 'showError')
const file = new File({
id: 1,
source: 'http://localhost/remote.php/dav/files/admin/foobar.txt',
source: 'http://nextcloud.local/remote.php/dav/files/admin/foobar.txt',
owner: 'admin',
mime: 'text/plain',
permissions: Permission.UPDATE,
@ -135,24 +131,23 @@ describe('Edit locally action execute tests', () => {
const exec = await action.exec(file, view, '/')
expect(spyDialogBuilder).toBeCalled()
expect(spyShowDialog).toBeCalled()
// Silent action
expect(exec).toBe(null)
expect(axios.post).toBeCalledTimes(1)
expect(axios.post).toBeCalledWith('http://localhost/ocs/v2.php/apps/files/api/v1/openlocaleditor?format=json', { path: '/foobar.txt' })
expect(mockedShowError).toBeCalledTimes(0)
expect(window.location.href).toBe('nc://open/test@localhost/foobar.txt?token=foobar')
expect(axios.post).toBeCalledWith('http://nextcloud.local/ocs/v2.php/apps/files/api/v1/openlocaleditor?format=json', { path: '/foobar.txt' })
expect(showError).toBeCalledTimes(0)
expect(window.location.href).toBe('nc://open/test@nextcloud.local/foobar.txt?token=foobar')
})
test('Edit locally fails and shows error', async () => {
jest.spyOn(axios, 'post').mockImplementation(async () => ({}))
const mockedShowError = jest.mocked(showError)
const spyDialogBuilder = jest.spyOn(dialogBuilder, 'build')
vi.spyOn(axios, 'post').mockImplementation(async () => ({}))
const showError = vi.spyOn(nextcloudDialogs, 'showError')
const file = new File({
id: 1,
source: 'http://localhost/remote.php/dav/files/admin/foobar.txt',
source: 'http://nextcloud.local/remote.php/dav/files/admin/foobar.txt',
owner: 'admin',
mime: 'text/plain',
permissions: Permission.UPDATE,
@ -160,14 +155,14 @@ describe('Edit locally action execute tests', () => {
const exec = await action.exec(file, view, '/')
expect(spyDialogBuilder).toBeCalled()
expect(spyShowDialog).toBeCalled()
// Silent action
expect(exec).toBe(null)
expect(axios.post).toBeCalledTimes(1)
expect(axios.post).toBeCalledWith('http://localhost/ocs/v2.php/apps/files/api/v1/openlocaleditor?format=json', { path: '/foobar.txt' })
expect(mockedShowError).toBeCalledTimes(1)
expect(mockedShowError).toBeCalledWith('Failed to redirect to client')
expect(window.location.href).toBe('http://localhost/')
expect(axios.post).toBeCalledWith('http://nextcloud.local/ocs/v2.php/apps/files/api/v1/openlocaleditor?format=json', { path: '/foobar.txt' })
expect(showError).toBeCalledTimes(1)
expect(showError).toBeCalledWith('Failed to redirect to client')
expect(window.location.href).toBe('http://nextcloud.local/')
})
})

@ -2,14 +2,18 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './favoriteAction'
import { expect } from '@jest/globals'
import { File, Permission, View, FileAction } from '@nextcloud/files'
import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'
import { action } from './favoriteAction'
import axios from '@nextcloud/axios'
import eventBus from '@nextcloud/event-bus'
import * as eventBus from '@nextcloud/event-bus'
import * as favoriteAction from './favoriteAction'
import logger from '../logger'
vi.mock('@nextcloud/auth')
vi.mock('@nextcloud/axios')
const view = {
id: 'files',
name: 'Files',
@ -20,13 +24,12 @@ const favoriteView = {
name: 'Favorites',
} as View
window.OC = {
...window.OC,
TAG_FAVORITE: '_$!<Favorite>!$_',
}
// Mock webroot variable
beforeAll(() => {
window.OC = {
...window.OC,
TAG_FAVORITE: '_$!<Favorite>!$_',
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any)._oc_webroot = ''
})
@ -43,7 +46,7 @@ describe('Favorite action conditions tests', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('favorite')
expect(action.displayName([file], view)).toBe('Add to favorites')
expect(action.iconSvgInline([], view)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([], view)).toMatch(/<svg.+<\/svg>/)
expect(action.default).toBeUndefined()
expect(action.order).toBe(-50)
})
@ -129,13 +132,11 @@ describe('Favorite action enabled tests', () => {
})
describe('Favorite action execute tests', () => {
afterEach(() => {
jest.spyOn(axios, 'post').mockRestore()
})
beforeEach(() => { vi.resetAllMocks() })
test('Favorite triggers tag addition', async () => {
jest.spyOn(axios, 'post')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'post')
vi.spyOn(eventBus, 'emit')
const file = new File({
id: 1,
@ -159,8 +160,8 @@ describe('Favorite action execute tests', () => {
})
test('Favorite triggers tag removal', async () => {
jest.spyOn(axios, 'post')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'post')
vi.spyOn(eventBus, 'emit')
const file = new File({
id: 1,
@ -187,8 +188,8 @@ describe('Favorite action execute tests', () => {
})
test('Favorite triggers node removal if favorite view and root dir', async () => {
jest.spyOn(axios, 'post')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'post')
vi.spyOn(eventBus, 'emit')
const file = new File({
id: 1,
@ -216,8 +217,8 @@ describe('Favorite action execute tests', () => {
})
test('Favorite does NOT triggers node removal if favorite view but NOT root dir', async () => {
jest.spyOn(axios, 'post')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'post')
vi.spyOn(eventBus, 'emit')
const file = new File({
id: 1,
@ -246,8 +247,8 @@ describe('Favorite action execute tests', () => {
test('Favorite fails and show error', async () => {
const error = new Error('Mock error')
jest.spyOn(axios, 'post').mockImplementation(() => { throw new Error('Mock error') })
jest.spyOn(logger, 'error').mockImplementation(() => jest.fn())
vi.spyOn(axios, 'post').mockImplementation(() => { throw new Error('Mock error') })
vi.spyOn(logger, 'error').mockImplementation(() => vi.fn())
const file = new File({
id: 1,
@ -276,8 +277,8 @@ describe('Favorite action execute tests', () => {
test('Removing from favorites fails and show error', async () => {
const error = new Error('Mock error')
jest.spyOn(axios, 'post').mockImplementation(() => { throw error })
jest.spyOn(logger, 'error').mockImplementation(() => jest.fn())
vi.spyOn(axios, 'post').mockImplementation(() => { throw error })
vi.spyOn(logger, 'error').mockImplementation(() => vi.fn())
const file = new File({
id: 1,
@ -306,9 +307,11 @@ describe('Favorite action execute tests', () => {
})
describe('Favorite action batch execute tests', () => {
beforeEach(() => { vi.restoreAllMocks() })
test('Favorite action batch execute with mixed files', async () => {
jest.spyOn(favoriteAction, 'favoriteNode')
jest.spyOn(axios, 'post')
vi.spyOn(favoriteAction, 'favoriteNode')
vi.spyOn(axios, 'post')
const file1 = new File({
id: 1,
@ -336,15 +339,14 @@ describe('Favorite action batch execute tests', () => {
expect(exec).toStrictEqual([true, true])
expect([file1, file2].every(file => file.attributes.favorite === 1)).toBe(true)
expect(favoriteAction.favoriteNode).toBeCalledTimes(2)
expect(axios.post).toBeCalledTimes(2)
expect(axios.post).toHaveBeenNthCalledWith(1, '/index.php/apps/files/api/v1/files/foo.txt', { tags: ['_$!<Favorite>!$_'] })
expect(axios.post).toHaveBeenNthCalledWith(2, '/index.php/apps/files/api/v1/files/bar.txt', { tags: ['_$!<Favorite>!$_'] })
})
test('Remove from favorite action batch execute with favorites only files', async () => {
jest.spyOn(favoriteAction, 'favoriteNode')
jest.spyOn(axios, 'post')
vi.spyOn(favoriteAction, 'favoriteNode')
vi.spyOn(axios, 'post')
const file1 = new File({
id: 1,
@ -372,7 +374,6 @@ describe('Favorite action batch execute tests', () => {
expect(exec).toStrictEqual([true, true])
expect([file1, file2].every(file => file.attributes.favorite === 0)).toBe(true)
expect(favoriteAction.favoriteNode).toBeCalledTimes(2)
expect(axios.post).toBeCalledTimes(2)
expect(axios.post).toHaveBeenNthCalledWith(1, '/index.php/apps/files/api/v1/files/foo.txt', { tags: [] })
expect(axios.post).toHaveBeenNthCalledWith(2, '/index.php/apps/files/api/v1/files/bar.txt', { tags: [] })

@ -2,8 +2,8 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { expect } from '@jest/globals'
import { File, Folder, Node, Permission, View, DefaultType, FileAction } from '@nextcloud/files'
import { describe, expect, test, vi } from 'vitest'
import { action } from './openFolderAction'
@ -24,7 +24,7 @@ describe('Open folder action conditions tests', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('open-folder')
expect(action.displayName([folder], view)).toBe('Open folder FooBar')
expect(action.iconSvgInline([], view)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([], view)).toMatch(/<svg.+<\/svg>/)
expect(action.default).toBe(DefaultType.HIDDEN)
expect(action.order).toBe(-100)
})
@ -100,7 +100,7 @@ describe('Open folder action enabled tests', () => {
describe('Open folder action execute tests', () => {
test('Open folder', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }
@ -119,7 +119,7 @@ describe('Open folder action execute tests', () => {
})
test('Open folder fails without node', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }
@ -129,7 +129,7 @@ describe('Open folder action execute tests', () => {
})
test('Open folder fails without Folder', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }

@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './openInFilesAction'
import { expect } from '@jest/globals'
import { describe, expect, test, vi } from 'vitest'
import { File, Folder, Permission, View, DefaultType, FileAction } from '@nextcloud/files'
const view = {
@ -42,7 +42,7 @@ describe('Open in files action enabled tests', () => {
describe('Open in files action execute tests', () => {
test('Open in files', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }
@ -64,7 +64,7 @@ describe('Open in files action execute tests', () => {
})
test('Open in files with folder', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }

@ -3,9 +3,9 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './renameAction'
import { expect } from '@jest/globals'
import { File, Permission, View, FileAction } from '@nextcloud/files'
import eventBus from '@nextcloud/event-bus'
import * as eventBus from '@nextcloud/event-bus'
import { describe, expect, test, vi } from 'vitest'
const view = {
id: 'files',
@ -17,7 +17,7 @@ describe('Rename action conditions tests', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('rename')
expect(action.displayName([], view)).toBe('Rename')
expect(action.iconSvgInline([], view)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([], view)).toMatch(/<svg.+<\/svg>/)
expect(action.default).toBeUndefined()
expect(action.order).toBe(10)
})
@ -73,7 +73,7 @@ describe('Rename action enabled tests', () => {
describe('Rename action exec tests', () => {
test('Rename', async () => {
jest.spyOn(eventBus, 'emit')
vi.spyOn(eventBus, 'emit')
const file = new File({
id: 1,

@ -2,8 +2,8 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { expect } from '@jest/globals'
import { File, Permission, View, FileAction } from '@nextcloud/files'
import { describe, expect, test, vi } from 'vitest'
import { action } from './sidebarAction'
import logger from '../logger'
@ -18,7 +18,7 @@ describe('Open sidebar action conditions tests', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('details')
expect(action.displayName([], view)).toBe('Open details')
expect(action.iconSvgInline([], view)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([], view)).toMatch(/<svg.+<\/svg>/)
expect(action.default).toBeUndefined()
expect(action.order).toBe(-50)
})
@ -107,9 +107,9 @@ describe('Open sidebar action enabled tests', () => {
describe('Open sidebar action exec tests', () => {
test('Open sidebar', async () => {
const openMock = jest.fn()
const openMock = vi.fn()
window.OCA = { Files: { Sidebar: { open: openMock } } }
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }
@ -133,9 +133,9 @@ describe('Open sidebar action exec tests', () => {
})
test('Open sidebar fails', async () => {
const openMock = jest.fn(() => { throw new Error('Mock error') })
const openMock = vi.fn(() => { throw new Error('Mock error') })
window.OCA = { Files: { Sidebar: { open: openMock } } }
jest.spyOn(logger, 'error').mockImplementation(() => jest.fn())
vi.spyOn(logger, 'error').mockImplementation(() => vi.fn())
const file = new File({
id: 1,

@ -2,9 +2,9 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './viewInFolderAction'
import { expect } from '@jest/globals'
import { File, Folder, Node, Permission, View, FileAction } from '@nextcloud/files'
import { describe, expect, test, vi } from 'vitest'
import { action } from './viewInFolderAction'
const view = {
id: 'trashbin',
@ -21,7 +21,7 @@ describe('View in folder action conditions tests', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('view-in-folder')
expect(action.displayName([], view)).toBe('View in folder')
expect(action.iconSvgInline([], view)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([], view)).toMatch(/<svg.+<\/svg>/)
expect(action.default).toBeUndefined()
expect(action.order).toBe(80)
expect(action.enabled).toBeDefined()
@ -113,7 +113,7 @@ describe('View in folder action enabled tests', () => {
describe('View in folder action execute tests', () => {
test('View in folder', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }
@ -133,7 +133,7 @@ describe('View in folder action execute tests', () => {
})
test('View in (sub) folder', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }
@ -154,7 +154,7 @@ describe('View in folder action execute tests', () => {
})
test('View in folder fails without node', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }
@ -164,7 +164,7 @@ describe('View in folder action execute tests', () => {
})
test('View in folder fails without File', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }

@ -2,11 +2,14 @@
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { beforeEach, describe, expect, it, jest } from '@jest/globals'
import nextcloudFiles, { Navigation, View } from '@nextcloud/files'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { defineComponent } from 'vue'
import { useNavigation } from './useNavigation'
import * as nextcloudFiles from '@nextcloud/files'
const { Navigation, View } = nextcloudFiles
// Just a wrapper so we can test the composable
const TestComponent = defineComponent({
@ -21,7 +24,7 @@ const TestComponent = defineComponent({
})
describe('Composables: useNavigation', () => {
const spy = jest.spyOn(nextcloudFiles, 'getNavigation')
const spy = vi.spyOn(nextcloudFiles, 'getNavigation')
let navigation: Navigation
describe('currentView', () => {

@ -2,7 +2,7 @@
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { describe, it, expect } from '@jest/globals'
import { beforeAll, describe, expect, it, vi } from 'vitest'
import { FileSystemDirectoryEntry, FileSystemFileEntry, fileSystemEntryToDataTransferItem, DataTransferItem as DataTransferItemMock } from '../../../../__tests__/FileSystemAPIUtils'
import { join } from 'node:path'
@ -88,20 +88,17 @@ describe('Filesystem API traverseTree', () => {
describe('DropService dataTransferToFileTree', () => {
beforeAll(() => {
// @ts-expect-error jsdom doesn't have DataTransferItem
delete window.DataTransferItem
// DataTransferItem doesn't exists in jsdom, let's mock
// a dumb one so we can check the instanceof
// @ts-expect-error jsdom doesn't have DataTransferItem
window.DataTransferItem = DataTransferItemMock
})
afterAll(() => {
// @ts-expect-error jsdom doesn't have DataTransferItem
delete window.DataTransferItem
})
it('Should return a RootDirectory with Filesystem API', async () => {
jest.spyOn(logger, 'error').mockImplementation(() => jest.fn())
jest.spyOn(logger, 'warn').mockImplementation(() => jest.fn())
vi.spyOn(logger, 'error').mockImplementation(() => vi.fn())
vi.spyOn(logger, 'warn').mockImplementation(() => vi.fn())
const dataTransferItems = buildDataTransferItemArray('root', dataTree)
const fileTree = await dataTransferToFileTree(dataTransferItems as unknown as DataTransferItem[])
@ -121,8 +118,8 @@ describe('DropService dataTransferToFileTree', () => {
})
it('Should return a RootDirectory with legacy File API ignoring recursive directories', async () => {
jest.spyOn(logger, 'error').mockImplementation(() => jest.fn())
jest.spyOn(logger, 'warn').mockImplementation(() => jest.fn())
vi.spyOn(logger, 'error').mockImplementation(() => vi.fn())
vi.spyOn(logger, 'warn').mockImplementation(() => vi.fn())
const dataTransferItems = buildDataTransferItemArray('root', dataTree, false)

@ -3,20 +3,18 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { basename } from 'path'
import { expect } from '@jest/globals'
import { Folder, Navigation, getNavigation } from '@nextcloud/files'
import { CancelablePromise } from 'cancelable-promise'
import eventBus, { emit } from '@nextcloud/event-bus'
import { basename } from 'path'
import { beforeEach, describe, expect, test, vi } from 'vitest'
import * as eventBus from '@nextcloud/event-bus'
import * as initialState from '@nextcloud/initial-state'
import { action } from '../actions/favoriteAction'
import * as favoritesService from '../services/Favorites'
import registerFavoritesView from './favorites'
jest.mock('webdav/dist/node/request.js', () => ({
request: jest.fn(),
}))
vi.mock('@nextcloud/axios')
window.OC = {
...window.OC,
@ -32,17 +30,16 @@ declare global {
describe('Favorites view definition', () => {
let Navigation
beforeEach(() => {
Navigation = getNavigation()
expect(window._nc_navigation).toBeDefined()
})
vi.resetAllMocks()
afterEach(() => {
delete window._nc_navigation
Navigation = getNavigation()
expect(window._nc_navigation).toBeDefined()
})
test('Default empty favorite view', () => {
jest.spyOn(eventBus, 'subscribe')
jest.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] }))
vi.spyOn(eventBus, 'subscribe')
vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] }))
registerFavoritesView()
const favoritesView = Navigation.views.find(view => view.id === 'favorites')
@ -61,7 +58,7 @@ describe('Favorites view definition', () => {
expect(favoritesView?.id).toBe('favorites')
expect(favoritesView?.name).toBe('Favorites')
expect(favoritesView?.caption).toBeDefined()
expect(favoritesView?.icon).toBe('<svg>SvgMock</svg>')
expect(favoritesView?.icon).toMatch(/<svg.+<\/svg>/)
expect(favoritesView?.order).toBe(15)
expect(favoritesView?.columns).toStrictEqual([])
expect(favoritesView?.getContents).toBeDefined()
@ -73,8 +70,8 @@ describe('Favorites view definition', () => {
{ fileid: 2, path: '/bar' },
{ fileid: 3, path: '/foo/bar' },
]
jest.spyOn(initialState, 'loadState').mockReturnValue(favoriteFolders)
jest.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] }))
vi.spyOn(initialState, 'loadState').mockReturnValue(favoriteFolders)
vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] }))
registerFavoritesView()
const favoritesView = Navigation.views.find(view => view.id === 'favorites')
@ -90,7 +87,7 @@ describe('Favorites view definition', () => {
expect(favoriteView).toBeDefined()
expect(favoriteView?.id).toBeDefined()
expect(favoriteView?.name).toBe(basename(folder.path))
expect(favoriteView?.icon).toBe('<svg>SvgMock</svg>')
expect(favoriteView?.icon).toMatch(/<svg.+<\/svg>/)
expect(favoriteView?.order).toBe(index)
expect(favoriteView?.params).toStrictEqual({
dir: folder.path,
@ -104,20 +101,19 @@ describe('Favorites view definition', () => {
})
})
describe('Dynamic update of favourite folders', () => {
describe('Dynamic update of favorite folders', () => {
let Navigation
beforeEach(() => {
Navigation = getNavigation()
})
vi.restoreAllMocks()
afterEach(() => {
delete window._nc_navigation
Navigation = getNavigation()
})
test('Add a favorite folder creates a new entry in the navigation', async () => {
jest.spyOn(eventBus, 'emit')
jest.spyOn(initialState, 'loadState').mockReturnValue([])
jest.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] }))
vi.spyOn(eventBus, 'emit')
vi.spyOn(initialState, 'loadState').mockReturnValue([])
vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] }))
registerFavoritesView()
const favoritesView = Navigation.views.find(view => view.id === 'favorites')
@ -131,7 +127,7 @@ describe('Dynamic update of favourite folders', () => {
// Create new folder to favorite
const folder = new Folder({
id: 1,
source: 'http://localhost/remote.php/dav/files/admin/Foo/Bar',
source: 'http://nextcloud.local/remote.php/dav/files/admin/Foo/Bar',
owner: 'admin',
})
@ -143,10 +139,9 @@ describe('Dynamic update of favourite folders', () => {
})
test('Remove a favorite folder remove the entry from the navigation column', async () => {
jest.spyOn(eventBus, 'emit')
jest.spyOn(eventBus, 'subscribe')
jest.spyOn(initialState, 'loadState').mockReturnValue([{ fileid: 42, path: '/Foo/Bar' }])
jest.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] }))
vi.spyOn(eventBus, 'emit')
vi.spyOn(initialState, 'loadState').mockReturnValue([{ fileid: 42, path: '/Foo/Bar' }])
vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] }))
registerFavoritesView()
let favoritesView = Navigation.views.find(view => view.id === 'favorites')
@ -160,7 +155,7 @@ describe('Dynamic update of favourite folders', () => {
// Create new folder to favorite
const folder = new Folder({
id: 1,
source: 'http://localhost/remote.php/dav/files/admin/Foo/Bar',
source: 'http://nextcloud.local/remote.php/dav/files/admin/Foo/Bar',
owner: 'admin',
root: '/files/admin',
attributes: {
@ -168,11 +163,15 @@ describe('Dynamic update of favourite folders', () => {
},
})
const fo = vi.fn()
eventBus.subscribe('files:favorites:removed', fo)
// Exec the action
await action.exec(folder, favoritesView, '/')
expect(eventBus.emit).toHaveBeenCalledTimes(1)
expect(eventBus.emit).toHaveBeenCalledWith('files:favorites:removed', folder)
expect(fo).toHaveBeenCalled()
favoritesView = Navigation.views.find(view => view.id === 'favorites')
favoriteFoldersViews = Navigation.views.filter(view => view.parent === 'favorites')
@ -184,9 +183,9 @@ describe('Dynamic update of favourite folders', () => {
})
test('Renaming a favorite folder updates the navigation', async () => {
jest.spyOn(eventBus, 'emit')
jest.spyOn(initialState, 'loadState').mockReturnValue([])
jest.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] }))
vi.spyOn(eventBus, 'emit')
vi.spyOn(initialState, 'loadState').mockReturnValue([])
vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] }))
registerFavoritesView()
const favoritesView = Navigation.views.find(view => view.id === 'favorites')
@ -202,7 +201,7 @@ describe('Dynamic update of favourite folders', () => {
// Create new folder to favorite
const folder = new Folder({
id: 1,
source: 'http://localhost/remote.php/dav/files/admin/Foo/Bar',
source: 'http://nextcloud.local/remote.php/dav/files/admin/Foo/Bar',
owner: 'admin',
})
@ -213,12 +212,12 @@ describe('Dynamic update of favourite folders', () => {
// Create a folder with the same id but renamed
const renamedFolder = new Folder({
id: 1,
source: 'http://localhost/remote.php/dav/files/admin/Foo/Bar.renamed',
source: 'http://nextcloud.local/remote.php/dav/files/admin/Foo/Bar.renamed',
owner: 'admin',
})
// Exec the rename action
emit('files:node:renamed', renamedFolder)
eventBus.emit('files:node:renamed', renamedFolder)
expect(eventBus.emit).toHaveBeenNthCalledWith(2, 'files:node:renamed', renamedFolder)
})
})

@ -90,7 +90,7 @@ export default () => {
})
/**
* Remove favourites navigation when a folder is removed
* Remove favorites navigation when a folder is removed
*/
subscribe('files:favorites:removed', (node: Node) => {
if (node.type !== FileType.Folder) {

@ -2,10 +2,11 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './enterCredentialsAction'
import { expect } from '@jest/globals'
import { File, Folder, Permission, View, DefaultType, FileAction } from '@nextcloud/files'
import type { StorageConfig } from '../services/externalStorage'
import { File, Folder, Permission, View, DefaultType, FileAction } from '@nextcloud/files'
import { describe, expect, test } from 'vitest'
import { action } from './enterCredentialsAction'
import { STORAGE_STATUS } from '../utils/credentialsUtils'
const view = {
@ -36,7 +37,7 @@ describe('Enter credentials action conditions tests', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('credentials-external-storage')
expect(action.displayName([storage], externalStorageView)).toBe('Enter missing credentials')
expect(action.iconSvgInline([storage], externalStorageView)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([storage], externalStorageView)).toMatch(/<svg.+<\/svg>/)
expect(action.default).toBe(DefaultType.DEFAULT)
expect(action.order).toBe(-1000)
expect(action.inline!(storage, externalStorageView)).toBe(true)

@ -2,10 +2,11 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './openInFilesAction'
import { expect } from '@jest/globals'
import { Folder, Permission, View, DefaultType, FileAction } from '@nextcloud/files'
import { describe, expect, test, vi } from 'vitest'
import type { StorageConfig } from '../services/externalStorage'
import { action } from './openInFilesAction'
import { STORAGE_STATUS } from '../utils/credentialsUtils'
const view = {
@ -73,7 +74,7 @@ describe('Open in files action enabled tests', () => {
describe('Open in files action execute tests', () => {
test('Open in files', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }
@ -98,7 +99,7 @@ describe('Open in files action execute tests', () => {
})
test('Open in files broken storage', async () => {
const confirmMock = jest.fn()
const confirmMock = vi.fn()
// @ts-expect-error We only mock what is needed
window.OC = { dialogs: { confirm: confirmMock } }

@ -2,8 +2,8 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { expect } from '@jest/globals'
import { File, Folder, Permission } from '@nextcloud/files'
import { describe, expect, test } from 'vitest'
import { isNodeExternalStorage } from './externalStorageUtils'
describe('Is node an external storage', () => {

@ -2,14 +2,17 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'
import { action } from './acceptShareAction'
import { expect } from '@jest/globals'
import { File, Permission, View, FileAction } from '@nextcloud/files'
import { ShareType } from '@nextcloud/sharing'
import eventBus from '@nextcloud/event-bus'
import * as eventBus from '@nextcloud/event-bus'
import axios from '@nextcloud/axios'
import '../main'
vi.mock('@nextcloud/axios')
const view = {
id: 'files',
name: 'Files',
@ -39,7 +42,7 @@ describe('Accept share action conditions tests', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('accept-share')
expect(action.displayName([file], pendingShareView)).toBe('Accept share')
expect(action.iconSvgInline([file], pendingShareView)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([file], pendingShareView)).toMatch(/<svg.+<\/svg>/)
expect(action.default).toBeUndefined()
expect(action.order).toBe(1)
expect(action.inline).toBeDefined()
@ -92,9 +95,11 @@ describe('Accept share action enabled tests', () => {
})
describe('Accept share action execute tests', () => {
beforeEach(() => { vi.resetAllMocks() })
test('Accept share action', async () => {
jest.spyOn(axios, 'post')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'post')
vi.spyOn(eventBus, 'emit')
const file = new File({
id: 1,
@ -112,15 +117,15 @@ describe('Accept share action execute tests', () => {
expect(exec).toBe(true)
expect(axios.post).toBeCalledTimes(1)
expect(axios.post).toBeCalledWith('http://localhost/ocs/v2.php/apps/files_sharing/api/v1/shares/pending/123')
expect(axios.post).toBeCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares/pending/123')
expect(eventBus.emit).toBeCalledTimes(1)
expect(eventBus.emit).toBeCalledWith('files:node:deleted', file)
})
test('Accept remote share action', async () => {
jest.spyOn(axios, 'post')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'post')
vi.spyOn(eventBus, 'emit')
const file = new File({
id: 1,
@ -139,15 +144,15 @@ describe('Accept share action execute tests', () => {
expect(exec).toBe(true)
expect(axios.post).toBeCalledTimes(1)
expect(axios.post).toBeCalledWith('http://localhost/ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/123')
expect(axios.post).toBeCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/123')
expect(eventBus.emit).toBeCalledTimes(1)
expect(eventBus.emit).toBeCalledWith('files:node:deleted', file)
})
test('Accept share action batch', async () => {
jest.spyOn(axios, 'post')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'post')
vi.spyOn(eventBus, 'emit')
const file1 = new File({
id: 1,
@ -177,8 +182,8 @@ describe('Accept share action execute tests', () => {
expect(exec).toStrictEqual([true, true])
expect(axios.post).toBeCalledTimes(2)
expect(axios.post).toHaveBeenNthCalledWith(1, 'http://localhost/ocs/v2.php/apps/files_sharing/api/v1/shares/pending/123')
expect(axios.post).toHaveBeenNthCalledWith(2, 'http://localhost/ocs/v2.php/apps/files_sharing/api/v1/shares/pending/456')
expect(axios.post).toHaveBeenNthCalledWith(1, 'http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares/pending/123')
expect(axios.post).toHaveBeenNthCalledWith(2, 'http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares/pending/456')
expect(eventBus.emit).toBeCalledTimes(2)
expect(eventBus.emit).toHaveBeenNthCalledWith(1, 'files:node:deleted', file1)
@ -186,7 +191,7 @@ describe('Accept share action execute tests', () => {
})
test('Accept fails', async () => {
jest.spyOn(axios, 'post').mockImplementation(() => { throw new Error('Mock error') })
vi.spyOn(axios, 'post').mockImplementation(() => { throw new Error('Mock error') })
const file = new File({
id: 1,
@ -204,7 +209,7 @@ describe('Accept share action execute tests', () => {
expect(exec).toBe(false)
expect(axios.post).toBeCalledTimes(1)
expect(axios.post).toBeCalledWith('http://localhost/ocs/v2.php/apps/files_sharing/api/v1/shares/pending/123')
expect(axios.post).toBeCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares/pending/123')
expect(eventBus.emit).toBeCalledTimes(0)
})

@ -2,8 +2,8 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { expect } from '@jest/globals'
import { File, Permission, View, DefaultType, FileAction } from '@nextcloud/files'
import { describe, expect, test, vi } from 'vitest'
import '../main'
import { action } from './openInFilesAction'
@ -56,7 +56,7 @@ describe('Open in files action enabled tests', () => {
describe('Open in files action execute tests', () => {
test('Open in files', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }

@ -2,14 +2,17 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './rejectShareAction'
import { expect } from '@jest/globals'
import { File, Folder, Permission, View, FileAction } from '@nextcloud/files'
import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'
import { ShareType } from '@nextcloud/sharing'
import eventBus from '@nextcloud/event-bus'
import * as eventBus from '@nextcloud/event-bus'
import axios from '@nextcloud/axios'
import { action } from './rejectShareAction'
import '../main'
vi.mock('@nextcloud/axios')
const view = {
id: 'files',
name: 'Files',
@ -39,7 +42,7 @@ describe('Reject share action conditions tests', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('reject-share')
expect(action.displayName([file], pendingShareView)).toBe('Reject share')
expect(action.iconSvgInline([file], pendingShareView)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([file], pendingShareView)).toMatch(/<svg.+<\/svg>/)
expect(action.default).toBeUndefined()
expect(action.order).toBe(2)
expect(action.inline).toBeDefined()
@ -119,9 +122,11 @@ describe('Reject share action enabled tests', () => {
})
describe('Reject share action execute tests', () => {
beforeEach(() => { vi.resetAllMocks() })
test('Reject share action', async () => {
jest.spyOn(axios, 'delete')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'delete')
vi.spyOn(eventBus, 'emit')
const file = new File({
id: 1,
@ -139,15 +144,15 @@ describe('Reject share action execute tests', () => {
expect(exec).toBe(true)
expect(axios.delete).toBeCalledTimes(1)
expect(axios.delete).toBeCalledWith('http://localhost/ocs/v2.php/apps/files_sharing/api/v1/shares/123')
expect(axios.delete).toBeCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares/123')
expect(eventBus.emit).toBeCalledTimes(1)
expect(eventBus.emit).toBeCalledWith('files:node:deleted', file)
})
test('Reject remote share action', async () => {
jest.spyOn(axios, 'delete')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'delete')
vi.spyOn(eventBus, 'emit')
const file = new File({
id: 1,
@ -166,15 +171,15 @@ describe('Reject share action execute tests', () => {
expect(exec).toBe(true)
expect(axios.delete).toBeCalledTimes(1)
expect(axios.delete).toBeCalledWith('http://localhost/ocs/v2.php/apps/files_sharing/api/v1/remote_shares/123')
expect(axios.delete).toBeCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/remote_shares/123')
expect(eventBus.emit).toBeCalledTimes(1)
expect(eventBus.emit).toBeCalledWith('files:node:deleted', file)
})
test('Reject share action batch', async () => {
jest.spyOn(axios, 'delete')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'delete')
vi.spyOn(eventBus, 'emit')
const file1 = new File({
id: 1,
@ -204,8 +209,8 @@ describe('Reject share action execute tests', () => {
expect(exec).toStrictEqual([true, true])
expect(axios.delete).toBeCalledTimes(2)
expect(axios.delete).toHaveBeenNthCalledWith(1, 'http://localhost/ocs/v2.php/apps/files_sharing/api/v1/shares/123')
expect(axios.delete).toHaveBeenNthCalledWith(2, 'http://localhost/ocs/v2.php/apps/files_sharing/api/v1/shares/456')
expect(axios.delete).toHaveBeenNthCalledWith(1, 'http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares/123')
expect(axios.delete).toHaveBeenNthCalledWith(2, 'http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares/456')
expect(eventBus.emit).toBeCalledTimes(2)
expect(eventBus.emit).toHaveBeenNthCalledWith(1, 'files:node:deleted', file1)
@ -213,7 +218,7 @@ describe('Reject share action execute tests', () => {
})
test('Reject fails', async () => {
jest.spyOn(axios, 'delete').mockImplementation(() => { throw new Error('Mock error') })
vi.spyOn(axios, 'delete').mockImplementation(() => { throw new Error('Mock error') })
const file = new File({
id: 1,
@ -231,7 +236,7 @@ describe('Reject share action execute tests', () => {
expect(exec).toBe(false)
expect(axios.delete).toBeCalledTimes(1)
expect(axios.delete).toBeCalledWith('http://localhost/ocs/v2.php/apps/files_sharing/api/v1/shares/123')
expect(axios.delete).toBeCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares/123')
expect(eventBus.emit).toBeCalledTimes(0)
})

@ -2,14 +2,18 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './restoreShareAction'
import { expect } from '@jest/globals'
import { File, Permission, View, FileAction } from '@nextcloud/files'
import { ShareType } from '@nextcloud/sharing'
import eventBus from '@nextcloud/event-bus'
import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'
import axios from '@nextcloud/axios'
import * as eventBus from '@nextcloud/event-bus'
import { action } from './restoreShareAction'
import '../main'
vi.mock('@nextcloud/auth')
vi.mock('@nextcloud/axios')
const view = {
id: 'files',
name: 'Files',
@ -39,7 +43,7 @@ describe('Restore share action conditions tests', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('restore-share')
expect(action.displayName([file], deletedShareView)).toBe('Restore share')
expect(action.iconSvgInline([file], deletedShareView)).toBe('<svg>SvgMock</svg>')
expect(action.iconSvgInline([file], deletedShareView)).toMatch(/<svg.+<\/svg>/)
expect(action.default).toBeUndefined()
expect(action.order).toBe(1)
expect(action.inline).toBeDefined()
@ -92,9 +96,11 @@ describe('Restore share action enabled tests', () => {
})
describe('Restore share action execute tests', () => {
beforeEach(() => { vi.resetAllMocks() })
test('Restore share action', async () => {
jest.spyOn(axios, 'post')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'post')
vi.spyOn(eventBus, 'emit')
const file = new File({
id: 1,
@ -112,15 +118,15 @@ describe('Restore share action execute tests', () => {
expect(exec).toBe(true)
expect(axios.post).toBeCalledTimes(1)
expect(axios.post).toBeCalledWith('http://localhost/ocs/v2.php/apps/files_sharing/api/v1/deletedshares/123')
expect(axios.post).toBeCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/deletedshares/123')
expect(eventBus.emit).toBeCalledTimes(1)
expect(eventBus.emit).toBeCalledWith('files:node:deleted', file)
})
test('Restore share action batch', async () => {
jest.spyOn(axios, 'post')
jest.spyOn(eventBus, 'emit')
vi.spyOn(axios, 'post')
vi.spyOn(eventBus, 'emit')
const file1 = new File({
id: 1,
@ -150,8 +156,8 @@ describe('Restore share action execute tests', () => {
expect(exec).toStrictEqual([true, true])
expect(axios.post).toBeCalledTimes(2)
expect(axios.post).toHaveBeenNthCalledWith(1, 'http://localhost/ocs/v2.php/apps/files_sharing/api/v1/deletedshares/123')
expect(axios.post).toHaveBeenNthCalledWith(2, 'http://localhost/ocs/v2.php/apps/files_sharing/api/v1/deletedshares/456')
expect(axios.post).toHaveBeenNthCalledWith(1, 'http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/deletedshares/123')
expect(axios.post).toHaveBeenNthCalledWith(2, 'http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/deletedshares/456')
expect(eventBus.emit).toBeCalledTimes(2)
expect(eventBus.emit).toHaveBeenNthCalledWith(1, 'files:node:deleted', file1)
@ -159,7 +165,8 @@ describe('Restore share action execute tests', () => {
})
test('Restore fails', async () => {
jest.spyOn(axios, 'post').mockImplementation(() => { throw new Error('Mock error') })
vi.spyOn(axios, 'post')
.mockImplementation(() => { throw new Error('Mock error') })
const file = new File({
id: 1,
@ -177,7 +184,7 @@ describe('Restore share action execute tests', () => {
expect(exec).toBe(false)
expect(axios.post).toBeCalledTimes(1)
expect(axios.post).toBeCalledWith('http://localhost/ocs/v2.php/apps/files_sharing/api/v1/deletedshares/123')
expect(axios.post).toBeCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/deletedshares/123')
expect(eventBus.emit).toBeCalledTimes(0)
})

@ -2,6 +2,7 @@
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { describe, expect, test } from 'vitest'
import {
ATOMIC_PERMISSIONS,

@ -3,29 +3,35 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { OCSResponse } from '@nextcloud/typings/ocs'
import { expect } from '@jest/globals'
import { Type } from '@nextcloud/sharing'
import * as auth from '@nextcloud/auth'
import axios from '@nextcloud/axios'
import { getContents } from './SharingService'
import { File, Folder } from '@nextcloud/files'
import { ShareType } from '@nextcloud/sharing'
import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'
import { getContents } from './SharingService'
import * as auth from '@nextcloud/auth'
import logger from './logger'
window.OC = {
...window.OC,
TAG_FAVORITE: '_$!<Favorite>!$_',
}
const TAG_FAVORITE = '_$!<Favorite>!$_'
const axios = vi.hoisted(() => ({ get: vi.fn() }))
vi.mock('@nextcloud/auth')
vi.mock('@nextcloud/axios', () => ({ default: axios }))
// Mock webroot variable
// Mock web root variable
beforeAll(() => {
window.OC = {
...window.OC,
TAG_FAVORITE,
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any)._oc_webroot = ''
;(window as any)._oc_webroot = ''
})
describe('SharingService methods definitions', () => {
beforeAll(() => {
jest.spyOn(axios, 'get').mockImplementation(async (): Promise<any> => {
beforeEach(() => {
vi.resetAllMocks()
axios.get.mockImplementation(async (): Promise<unknown> => {
return {
data: {
ocs: {
@ -36,20 +42,16 @@ describe('SharingService methods definitions', () => {
},
data: [],
},
} as OCSResponse<any>,
} as OCSResponse,
}
})
})
afterAll(() => {
jest.restoreAllMocks()
})
test('Shared with you', async () => {
await getContents(true, false, false, false, [])
expect(axios.get).toHaveBeenCalledTimes(2)
expect(axios.get).toHaveBeenNthCalledWith(1, 'http://localhost/ocs/v2.php/apps/files_sharing/api/v1/shares', {
expect(axios.get).toHaveBeenNthCalledWith(1, 'http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares', {
headers: {
'Content-Type': 'application/json',
},
@ -58,7 +60,7 @@ describe('SharingService methods definitions', () => {
include_tags: true,
},
})
expect(axios.get).toHaveBeenNthCalledWith(2, 'http://localhost/ocs/v2.php/apps/files_sharing/api/v1/remote_shares', {
expect(axios.get).toHaveBeenNthCalledWith(2, 'http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/remote_shares', {
headers: {
'Content-Type': 'application/json',
},
@ -72,7 +74,7 @@ describe('SharingService methods definitions', () => {
await getContents(false, true, false, false, [])
expect(axios.get).toHaveBeenCalledTimes(1)
expect(axios.get).toHaveBeenCalledWith('http://localhost/ocs/v2.php/apps/files_sharing/api/v1/shares', {
expect(axios.get).toHaveBeenCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares', {
headers: {
'Content-Type': 'application/json',
},
@ -87,7 +89,7 @@ describe('SharingService methods definitions', () => {
await getContents(false, false, true, false, [])
expect(axios.get).toHaveBeenCalledTimes(2)
expect(axios.get).toHaveBeenNthCalledWith(1, 'http://localhost/ocs/v2.php/apps/files_sharing/api/v1/shares/pending', {
expect(axios.get).toHaveBeenNthCalledWith(1, 'http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares/pending', {
headers: {
'Content-Type': 'application/json',
},
@ -95,7 +97,7 @@ describe('SharingService methods definitions', () => {
include_tags: true,
},
})
expect(axios.get).toHaveBeenNthCalledWith(2, 'http://localhost/ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending', {
expect(axios.get).toHaveBeenNthCalledWith(2, 'http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending', {
headers: {
'Content-Type': 'application/json',
},
@ -109,7 +111,7 @@ describe('SharingService methods definitions', () => {
await getContents(false, true, false, false, [])
expect(axios.get).toHaveBeenCalledTimes(1)
expect(axios.get).toHaveBeenCalledWith('http://localhost/ocs/v2.php/apps/files_sharing/api/v1/shares', {
expect(axios.get).toHaveBeenCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares', {
headers: {
'Content-Type': 'application/json',
},
@ -121,7 +123,7 @@ describe('SharingService methods definitions', () => {
})
test('Unknown owner', async () => {
jest.spyOn(auth, 'getCurrentUser').mockReturnValue(null)
vi.spyOn(auth, 'getCurrentUser').mockReturnValue(null)
const results = await getContents(false, true, false, false, [])
expect(results.folder.owner).toEqual(null)
@ -129,8 +131,9 @@ describe('SharingService methods definitions', () => {
})
describe('SharingService filtering', () => {
beforeAll(() => {
jest.spyOn(axios, 'get').mockImplementation(async (): Promise<any> => {
beforeEach(() => {
vi.resetAllMocks()
axios.get.mockImplementation(async (): Promise<unknown> => {
return {
data: {
ocs: {
@ -142,7 +145,7 @@ describe('SharingService filtering', () => {
data: [
{
id: '62',
share_type: Type.SHARE_TYPE_USER,
share_type: ShareType.User,
uid_owner: 'test',
displayname_owner: 'test',
permissions: 31,
@ -168,12 +171,8 @@ describe('SharingService filtering', () => {
})
})
afterAll(() => {
jest.restoreAllMocks()
})
test('Shared with others filtering', async () => {
const shares = await getContents(false, true, false, false, [Type.SHARE_TYPE_USER])
const shares = await getContents(false, true, false, false, [ShareType.User])
expect(axios.get).toHaveBeenCalledTimes(1)
expect(shares.contents).toHaveLength(1)
@ -182,7 +181,7 @@ describe('SharingService filtering', () => {
})
test('Shared with others filtering empty', async () => {
const shares = await getContents(false, true, false, false, [Type.SHARE_TYPE_LINK])
const shares = await getContents(false, true, false, false, [ShareType.Link])
expect(axios.get).toHaveBeenCalledTimes(1)
expect(shares.contents).toHaveLength(0)
@ -275,11 +274,13 @@ describe('SharingService share to Node mapping', () => {
mail_send: 0,
hide_download: 0,
attributes: null,
tags: [window.OC.TAG_FAVORITE],
tags: [TAG_FAVORITE],
}
beforeEach(() => { vi.resetAllMocks() })
test('File', async () => {
jest.spyOn(axios, 'get').mockReturnValueOnce(Promise.resolve({
axios.get.mockReturnValueOnce(Promise.resolve({
data: {
ocs: {
data: [shareFile],
@ -295,7 +296,7 @@ describe('SharingService share to Node mapping', () => {
const file = shares.contents[0] as File
expect(file).toBeInstanceOf(File)
expect(file.fileid).toBe(530936)
expect(file.source).toBe('http://localhost/remote.php/dav/files/test/document.md')
expect(file.source).toBe('http://nextcloud.local/remote.php/dav/files/test/document.md')
expect(file.owner).toBe('test')
expect(file.mime).toBe('text/markdown')
expect(file.mtime).toBeInstanceOf(Date)
@ -308,7 +309,7 @@ describe('SharingService share to Node mapping', () => {
})
test('Folder', async () => {
jest.spyOn(axios, 'get').mockReturnValueOnce(Promise.resolve({
axios.get.mockReturnValueOnce(Promise.resolve({
data: {
ocs: {
data: [shareFolder],
@ -324,7 +325,7 @@ describe('SharingService share to Node mapping', () => {
const folder = shares.contents[0] as Folder
expect(folder).toBeInstanceOf(Folder)
expect(folder.fileid).toBe(531080)
expect(folder.source).toBe('http://localhost/remote.php/dav/files/test/Folder')
expect(folder.source).toBe('http://nextcloud.local/remote.php/dav/files/test/Folder')
expect(folder.owner).toBe('test')
expect(folder.mime).toBe('httpd/unix-directory')
expect(folder.mtime).toBeInstanceOf(Date)
@ -338,8 +339,8 @@ describe('SharingService share to Node mapping', () => {
})
test('Empty', async () => {
jest.spyOn(logger, 'error').mockImplementationOnce(() => {})
jest.spyOn(axios, 'get').mockReturnValueOnce(Promise.resolve({
vi.spyOn(logger, 'error').mockImplementationOnce(() => {})
axios.get.mockReturnValueOnce(Promise.resolve({
data: {
ocs: {
data: [],
@ -353,8 +354,8 @@ describe('SharingService share to Node mapping', () => {
})
test('Error', async () => {
jest.spyOn(logger, 'error').mockImplementationOnce(() => {})
jest.spyOn(axios, 'get').mockReturnValueOnce(Promise.resolve({
vi.spyOn(logger, 'error').mockImplementationOnce(() => {})
axios.get.mockReturnValueOnce(Promise.resolve({
data: {
ocs: {
data: [null],

@ -4,8 +4,8 @@
*/
/* eslint-disable n/no-extraneous-import */
import type { OCSResponse } from '@nextcloud/typings/ocs'
import { expect } from '@jest/globals'
import { Folder, Navigation, View, getNavigation } from '@nextcloud/files'
import { beforeEach, describe, expect, test, vi } from 'vitest'
import axios from '@nextcloud/axios'
import '../main'
@ -20,16 +20,13 @@ declare global {
describe('Sharing views definition', () => {
let Navigation
beforeEach(() => {
delete window._nc_navigation
Navigation = getNavigation()
expect(window._nc_navigation).toBeDefined()
})
afterAll(() => {
delete window._nc_navigation
})
test('Default values', () => {
jest.spyOn(Navigation, 'register')
vi.spyOn(Navigation, 'register')
expect(Navigation.views.length).toBe(0)
@ -47,7 +44,7 @@ describe('Sharing views definition', () => {
expect(shareOverviewView?.id).toBe('shareoverview')
expect(shareOverviewView?.name).toBe('Shares')
expect(shareOverviewView?.caption).toBe('Overview of shared files.')
expect(shareOverviewView?.icon).toBe('<svg>SvgMock</svg>')
expect(shareOverviewView?.icon).toMatch(/<svg.+<\/svg>/i)
expect(shareOverviewView?.order).toBe(20)
expect(shareOverviewView?.columns).toStrictEqual([])
expect(shareOverviewView?.getContents).toBeDefined()
@ -68,7 +65,7 @@ describe('Sharing views definition', () => {
expect(view?.caption).toBeDefined()
expect(view?.emptyTitle).toBeDefined()
expect(view?.emptyCaption).toBeDefined()
expect(view?.icon).toBe('<svg>SvgMock</svg>')
expect(view?.icon).match(/<svg.+<\/svg>/)
expect(view?.order).toBe(index + 1)
expect(view?.columns).toStrictEqual([])
expect(view?.getContents).toBeDefined()
@ -79,16 +76,13 @@ describe('Sharing views definition', () => {
describe('Sharing views contents', () => {
let Navigation
beforeEach(() => {
delete window._nc_navigation
Navigation = getNavigation()
expect(window._nc_navigation).toBeDefined()
})
afterAll(() => {
delete window._nc_navigation
})
test('Sharing overview get contents', async () => {
jest.spyOn(axios, 'get').mockImplementation(async (): Promise<any> => {
vi.spyOn(axios, 'get').mockImplementation(async (): Promise<any> => {
return {
data: {
ocs: {

@ -5,7 +5,7 @@
import type { IAppDiscoverElement } from '../constants/AppDiscoverTypes'
import { describe, expect, it } from '@jest/globals'
import { describe, expect, it } from 'vitest'
import { filterElements, parseApiResponse } from './appDiscoverParser'
describe('App Discover API parser', () => {

@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './inlineSystemTagsAction'
import { expect } from '@jest/globals'
import { describe, expect, test } from 'vitest'
import { File, Permission, View, FileAction } from '@nextcloud/files'
const view = {

@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { action } from './openInFilesAction'
import { expect } from '@jest/globals'
import { describe, expect, test, vi } from 'vitest'
import { File, Folder, Permission, View, DefaultType, FileAction } from '@nextcloud/files'
const view = {
@ -79,7 +79,7 @@ describe('Open in files action enabled tests', () => {
describe('Open in files action execute tests', () => {
test('Open in files', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }
@ -101,7 +101,7 @@ describe('Open in files action execute tests', () => {
})
test('Open in files with folder', async () => {
const goToRouteMock = jest.fn()
const goToRouteMock = vi.fn()
// @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }

@ -5,8 +5,8 @@
import type { DAVResultResponseProps } from 'webdav'
import type { ServerTag, Tag } from './types.js'
import { describe, expect, it } from 'vitest'
import { describe, it, expect } from '@jest/globals'
import { formatTag, parseIdFromLocation, parseTags } from './utils'
describe('systemtags - utils', () => {

@ -62,7 +62,6 @@ $expectedFiles = [
'dist',
'index.html',
'index.php',
'jest.config.ts',
'lib',
'LICENSES',
'occ',
@ -82,6 +81,7 @@ $expectedFiles = [
'tsconfig.json',
'vendor-bin',
'version.php',
'vitest.config.ts',
'webpack.common.js',
'webpack.config.js',
'webpack.modules.js',

@ -3,10 +3,12 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import { beforeEach, describe, expect, test, vi } from 'vitest'
import { manageToken, setToken } from '../../OC/requesttoken.js'
const eventbus = vi.hoisted(() => ({ emit: vi.fn() }))
vi.mock('@nextcloud/event-bus', () => eventbus)
describe('request token', () => {
let emit
@ -14,7 +16,7 @@ describe('request token', () => {
const token = 'abc123'
beforeEach(() => {
emit = jest.fn()
emit = vi.fn()
const head = window.document.getElementsByTagName('head')[0]
head.setAttribute('data-requesttoken', token)
@ -32,22 +34,10 @@ describe('request token', () => {
})
describe('@nextcloud/auth integration', () => {
let listener
beforeEach(() => {
listener = jest.fn()
subscribe('csrf-token-update', listener)
})
afterEach(() => {
unsubscribe('csrf-token-update', listener)
})
test('fires off an event for @nextcloud/auth', () => {
setToken('123')
expect(listener).toHaveBeenCalledWith({ token: '123' })
expect(eventbus.emit).toHaveBeenCalledWith('csrf-token-update', { token: '123' })
})
})

@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { describe, expect, it } from 'vitest'
import { shallowMount } from '@vue/test-utils'
import Contact from '../../../components/ContactsMenu/Contact.vue'

@ -3,13 +3,18 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import axios from '@nextcloud/axios'
import { mount, shallowMount } from '@vue/test-utils'
import { describe, expect, it, vi } from 'vitest'
import ContactsMenu from '../../views/ContactsMenu.vue'
jest.mock('@nextcloud/axios', () => ({
post: jest.fn(),
const axios = vi.hoisted(() => ({
post: vi.fn(),
}))
vi.mock('@nextcloud/axios', () => ({ default: axios }))
vi.mock('@nextcloud/auth', () => ({
getCurrentUser: () => ({ uid: 'user', isAdmin: false, displayName: 'User' }),
}))
describe('ContactsMenu', function() {
@ -39,7 +44,7 @@ describe('ContactsMenu', function() {
it('shows error view when contacts can not be loaded', async () => {
const view = mount(ContactsMenu)
axios.post.mockResolvedValue({})
jest.spyOn(console, 'error').mockImplementation(() => {})
vi.spyOn(console, 'error').mockImplementation(() => {})
try {
await view.vm.handleOpen()
@ -56,7 +61,7 @@ describe('ContactsMenu', function() {
it('shows text when there are no contacts', async () => {
const view = mount(ContactsMenu)
axios.post.mockResolvedValue({
axios.post.mockResolvedValueOnce({
data: {
contacts: [],
contactsAppEnabled: false,

File diff suppressed because one or more lines are too long

@ -1,81 +0,0 @@
/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { Config } from 'jest'
// TODO: find a way to consolidate this in one place, with webpack.common.js
const ignorePatterns = [
'@buttercup/fetch',
'@juliushaertl',
'@mdi/svg',
'@nextcloud/files',
'@nextcloud/upload',
'@nextcloud/vue',
'ansi-regex',
'camelcase',
'char-regex',
'hot-patcher',
'is-svg',
'layerr',
'mime',
'p-cancelable',
'p-limit',
'p-queue',
'p-timeout',
'splitpanes',
'string-length',
'strip-ansi',
'tributejs',
'unist-.+',
'url-join',
'vue-material-design-icons',
'webdav',
'yocto-queue',
]
const config: Config = {
testMatch: ['<rootDir>/**/*.(spec|test).(ts|js)'],
clearMocks: true,
setupFilesAfterEnv: [
'<rootDir>/__tests__/jest-setup.ts',
'<rootDir>/__tests__/mock-window.js',
],
testEnvironment: 'jest-environment-jsdom',
preset: 'ts-jest/presets/js-with-ts',
roots: [
'<rootDir>/__mocks__',
'<rootDir>/__tests__',
'<rootDir>/apps',
'<rootDir>/core',
],
transform: {
// process `*.js` files with `babel-jest`
'^.+\\.c?js$': 'babel-jest',
'^.+\\.vue$': '@vue/vue2-jest',
'^.+\\.ts$': ['ts-jest', {
// @see https://github.com/kulshekhar/ts-jest/issues/4081
tsconfig: './__tests__/tsconfig.json',
}],
},
transformIgnorePatterns: [
'node_modules/(?!(' + ignorePatterns.join('|') + ')/)',
],
// Allow mocking svg files
moduleNameMapper: {
'^.+\\.svg(\\?raw)?$': '<rootDir>/__mocks__/svg.js',
'\\.s?css$': '<rootDir>/__mocks__/css.js',
},
modulePathIgnorePatterns: [
'<rootDir>/apps2/',
'<rootDir>/apps-extra/',
],
}
export default config

21661
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -15,9 +15,9 @@
"watch": "webpack --node-env development --progress --watch",
"lint": "eslint $(for appdir in $(ls apps); do if ! $(git check-ignore -q $appdir); then printf \"apps/$appdir \"; fi; done) core --no-error-on-unmatched-pattern",
"lint:fix": "eslint $(for appdir in $(ls apps); do if ! $(git check-ignore -q $appdir); then printf \"apps/$appdir \"; fi; done) core --no-error-on-unmatched-pattern --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test": "vitest run",
"test:watch": "vitest watch",
"test:coverage": "vitest run --coverage",
"test:jsunit": "karma start tests/karma.config.js --single-run",
"sass": "sass --style compressed --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)",
"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)",
@ -121,7 +121,6 @@
"@babel/preset-typescript": "^7.24.7",
"@cypress/vue2": "^2.1.1",
"@cypress/webpack-preprocessor": "^6.0.2",
"@jest/globals": "^29.7.0",
"@nextcloud/babel-config": "^1.2.0",
"@nextcloud/cypress": "^1.0.0-beta.8",
"@nextcloud/eslint-config": "^8.4.1",
@ -131,16 +130,15 @@
"@pinia/testing": "^0.1.5",
"@simplewebauthn/types": "^10.0.0",
"@testing-library/cypress": "^10.0.2",
"@testing-library/jest-dom": "^6.4.8",
"@testing-library/jest-dom": "^6.5.0",
"@testing-library/user-event": "^14.4.3",
"@testing-library/vue": "^5.8.3",
"@types/dockerode": "^3.3.29",
"@types/jest": "^29.5.2",
"@types/wait-on": "^5.3.4",
"@vitejs/plugin-vue2": "^2.3.1",
"@vitest/coverage-v8": "^2.0.5",
"@vue/test-utils": "^1.3.5",
"@vue/tsconfig": "^0.5.1",
"@vue/vue2-jest": "^29.2.6",
"babel-jest": "^29.6.4",
"babel-loader": "^9.1.0",
"babel-loader-exclude-node-modules-except": "^1.2.1",
"babel-plugin-module-resolver": "^5.0.2",
@ -160,10 +158,6 @@
"handlebars-loader": "^1.7.3",
"jasmine-core": "~2.5.2",
"jasmine-sinon": "^0.4.0",
"jest": "^29.0.3",
"jest-environment-jsdom": "^29.7.0",
"jest-fetch-mock": "^3.0.3",
"jest-location-mock": "^1.0.9",
"jsdoc": "^4.0.2",
"karma": "^6.4.3",
"karma-chrome-launcher": "^3.1.1",
@ -181,11 +175,11 @@
"sinon": "<= 5.0.7",
"style-loader": "^3.3.1",
"tar": "^7.4.3",
"ts-jest": "^29.2.3",
"ts-loader": "^9.5.0",
"ts-node": "^10.9.1",
"tslib": "^2.6.2",
"typescript": "^5.5.4",
"vitest": "^2.0.5",
"vue-loader": "^15.9.8",
"vue-template-compiler": "^2.7.16",
"wait-on": "^7.2.0",

@ -2,10 +2,10 @@
"extends": "@vue/tsconfig/tsconfig.json",
"include": ["./apps/**/*.ts", "./apps/**/*.vue", "./core/**/*.ts", "./core/**/*.vue", "./*.d.ts"],
"compilerOptions": {
"types": ["jest", "node", "vue", "vue-router"],
"types": ["node", "vue", "vue-router"],
"outDir": "./dist/",
"target": "ESNext",
"module": "esnext",
"module": "ESNext",
// Set module resolution to bundler and `noEmit` to be able to set `allowImportingTsExtensions`, so we can import Typescript with .ts extension
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,

@ -0,0 +1,26 @@
/**
* SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: CC0-1.0
*/
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue2'
export default defineConfig({
plugins: [vue()],
test: {
include: ['{apps,core}/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
environment: 'jsdom',
coverage: {
include: ['apps/*/src/**', 'core/src/**'],
exclude: ['**.spec.*', '**.test.*', '**.cy.*', 'core/src/tests/**'],
provider: 'v8',
reporter: ['lcov', 'text'],
},
setupFiles: ['__tests__/mock-window.js', '__tests__/setup-testing-library.js'],
server: {
deps: {
inline: [/@nextcloud\//],
},
},
},
})