refactor(test): migrate cypress component tests to vitest
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>pull/55747/head
parent
5aa1f5bb84
commit
81cfb9580a
@ -1,123 +0,0 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import RemoteShareDialog from './RemoteShareDialog.vue'
|
||||
|
||||
describe('RemoteShareDialog', () => {
|
||||
it('can be mounted', () => {
|
||||
cy.mount(RemoteShareDialog, {
|
||||
propsData: {
|
||||
owner: 'user123',
|
||||
name: 'my-photos',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: false,
|
||||
},
|
||||
})
|
||||
|
||||
cy.findByRole('dialog')
|
||||
.should('be.visible')
|
||||
.and('contain.text', 'user123@nextcloud.local')
|
||||
.and('contain.text', 'my-photos')
|
||||
cy.findByRole('button', { name: 'Cancel' })
|
||||
.should('be.visible')
|
||||
cy.findByRole('button', { name: /add remote share/i })
|
||||
.should('be.visible')
|
||||
})
|
||||
|
||||
it('does not show password input if not enabled', () => {
|
||||
cy.mount(RemoteShareDialog, {
|
||||
propsData: {
|
||||
owner: 'user123',
|
||||
name: 'my-photos',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: false,
|
||||
},
|
||||
})
|
||||
|
||||
cy.findByRole('dialog')
|
||||
.should('be.visible')
|
||||
.find('input[type="password"]')
|
||||
.should('not.exist')
|
||||
})
|
||||
|
||||
it('emits true when accepted', () => {
|
||||
const onClose = cy.spy().as('onClose')
|
||||
|
||||
cy.mount(RemoteShareDialog, {
|
||||
listeners: {
|
||||
close: onClose,
|
||||
},
|
||||
propsData: {
|
||||
owner: 'user123',
|
||||
name: 'my-photos',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: false,
|
||||
},
|
||||
})
|
||||
|
||||
cy.findByRole('button', { name: 'Cancel' }).click()
|
||||
cy.get('@onClose')
|
||||
.should('have.been.calledWith', false)
|
||||
})
|
||||
|
||||
it('show password input if needed', () => {
|
||||
cy.mount(RemoteShareDialog, {
|
||||
propsData: {
|
||||
owner: 'admin',
|
||||
name: 'secret-data',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: true,
|
||||
},
|
||||
})
|
||||
|
||||
cy.findByRole('dialog')
|
||||
.should('be.visible')
|
||||
.find('input[type="password"]')
|
||||
.should('be.visible')
|
||||
})
|
||||
|
||||
it('emits the submitted password', () => {
|
||||
const onClose = cy.spy().as('onClose')
|
||||
|
||||
cy.mount(RemoteShareDialog, {
|
||||
listeners: {
|
||||
close: onClose,
|
||||
},
|
||||
propsData: {
|
||||
owner: 'admin',
|
||||
name: 'secret-data',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: true,
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('input[type="password"]')
|
||||
.type('my password{enter}')
|
||||
cy.get('@onClose')
|
||||
.should('have.been.calledWith', true, 'my password')
|
||||
})
|
||||
|
||||
it('emits no password if cancelled', () => {
|
||||
const onClose = cy.spy().as('onClose')
|
||||
|
||||
cy.mount(RemoteShareDialog, {
|
||||
listeners: {
|
||||
close: onClose,
|
||||
},
|
||||
propsData: {
|
||||
owner: 'admin',
|
||||
name: 'secret-data',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: true,
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('input[type="password"]')
|
||||
.type('my password')
|
||||
cy.findByRole('button', { name: 'Cancel' }).click()
|
||||
cy.get('@onClose')
|
||||
.should('have.been.calledWith', false)
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { cleanup, fireEvent, render } from '@testing-library/vue'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import RemoteShareDialog from './RemoteShareDialog.vue'
|
||||
|
||||
describe('RemoteShareDialog', () => {
|
||||
beforeEach(cleanup)
|
||||
|
||||
it('can be mounted', async () => {
|
||||
const component = render(RemoteShareDialog, {
|
||||
props: {
|
||||
owner: 'user123',
|
||||
name: 'my-photos',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: false,
|
||||
},
|
||||
})
|
||||
|
||||
await expect(component.findByRole('dialog', { name: 'Remote share' })).resolves.not.toThrow()
|
||||
expect(component.getByRole('dialog').innerText).toContain(/my-photos from user123@nextcloud.local/)
|
||||
await expect(component.findByRole('button', { name: 'Cancel' })).resolves.not.toThrow()
|
||||
await expect(component.findByRole('button', { name: /Add remote share/ })).resolves.not.toThrow()
|
||||
})
|
||||
|
||||
it('does not show password input if not enabled', async () => {
|
||||
const component = render(RemoteShareDialog, {
|
||||
props: {
|
||||
owner: 'user123',
|
||||
name: 'my-photos',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: false,
|
||||
},
|
||||
})
|
||||
|
||||
await expect(component.findByLabelText('Remote share password')).rejects.toThrow()
|
||||
})
|
||||
|
||||
it('emits true when accepted', () => {
|
||||
const onClose = vi.fn()
|
||||
|
||||
const component = render(RemoteShareDialog, {
|
||||
listeners: {
|
||||
close: onClose,
|
||||
},
|
||||
props: {
|
||||
owner: 'user123',
|
||||
name: 'my-photos',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: false,
|
||||
},
|
||||
})
|
||||
|
||||
component.getByRole('button', { name: 'Cancel' }).click()
|
||||
expect(onClose).toHaveBeenCalledWith(false)
|
||||
})
|
||||
|
||||
it('show password input if needed', async () => {
|
||||
const component = render(RemoteShareDialog, {
|
||||
props: {
|
||||
owner: 'admin',
|
||||
name: 'secret-data',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: true,
|
||||
},
|
||||
})
|
||||
|
||||
await expect(component.findByLabelText('Remote share password')).resolves.not.toThrow()
|
||||
})
|
||||
|
||||
it('emits the submitted password', async () => {
|
||||
const onClose = vi.fn()
|
||||
|
||||
const component = render(RemoteShareDialog, {
|
||||
listeners: {
|
||||
close: onClose,
|
||||
},
|
||||
props: {
|
||||
owner: 'admin',
|
||||
name: 'secret-data',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: true,
|
||||
},
|
||||
})
|
||||
|
||||
const input = component.getByLabelText('Remote share password')
|
||||
await fireEvent.update(input, 'my password')
|
||||
component.getByRole('button', { name: 'Add remote share' }).click()
|
||||
expect(onClose).toHaveBeenCalledWith(true, 'my password')
|
||||
})
|
||||
|
||||
it('emits no password if cancelled', async () => {
|
||||
const onClose = vi.fn()
|
||||
|
||||
const component = render(RemoteShareDialog, {
|
||||
listeners: {
|
||||
close: onClose,
|
||||
},
|
||||
props: {
|
||||
owner: 'admin',
|
||||
name: 'secret-data',
|
||||
remote: 'nextcloud.local',
|
||||
passwordRequired: true,
|
||||
},
|
||||
})
|
||||
|
||||
const input = component.getByLabelText('Remote share password')
|
||||
await fireEvent.update(input, 'my password')
|
||||
component.getByRole('button', { name: 'Cancel' }).click()
|
||||
expect(onClose).toHaveBeenCalledWith(false)
|
||||
})
|
||||
})
|
||||
@ -1,55 +0,0 @@
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { defineComponent } from 'vue'
|
||||
import { useFileListWidth } from './useFileListWidth.ts'
|
||||
|
||||
const ComponentMock = defineComponent({
|
||||
template: '<div id="test-component" style="width: 100%;background: white;">{{ fileListWidth }}</div>',
|
||||
setup() {
|
||||
return {
|
||||
fileListWidth: useFileListWidth(),
|
||||
}
|
||||
},
|
||||
})
|
||||
const FileListMock = defineComponent({
|
||||
template: '<main id="app-content-vue" style="width: 100%;"><component-mock /></main>',
|
||||
components: {
|
||||
ComponentMock,
|
||||
},
|
||||
})
|
||||
|
||||
describe('composable: fileListWidth', () => {
|
||||
it('Has initial value', () => {
|
||||
cy.viewport(600, 400)
|
||||
|
||||
cy.mount(FileListMock, {})
|
||||
cy.get('#app-content-vue')
|
||||
.should('be.visible')
|
||||
.and('contain.text', '600')
|
||||
})
|
||||
|
||||
it('Is reactive to size change', () => {
|
||||
cy.viewport(600, 400)
|
||||
cy.mount(FileListMock)
|
||||
cy.get('#app-content-vue').should('contain.text', '600')
|
||||
|
||||
cy.viewport(800, 400)
|
||||
cy.screenshot()
|
||||
cy.get('#app-content-vue').should('contain.text', '800')
|
||||
})
|
||||
|
||||
it('Is reactive to style changes', () => {
|
||||
cy.viewport(600, 400)
|
||||
cy.mount(FileListMock)
|
||||
cy.get('#app-content-vue')
|
||||
.should('be.visible')
|
||||
.and('contain.text', '600')
|
||||
.invoke('attr', 'style', 'width: 100px')
|
||||
|
||||
cy.get('#app-content-vue')
|
||||
.should('contain.text', '100')
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,80 @@
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { cleanup, render } from '@testing-library/vue'
|
||||
import { configMocks, mockResizeObserver } from 'jsdom-testing-mocks'
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest'
|
||||
import { defineComponent } from 'vue'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
let resizeObserver: ReturnType<typeof mockResizeObserver>
|
||||
|
||||
describe('composable: fileListWidth', () => {
|
||||
configMocks({ beforeAll, afterAll, beforeEach, afterEach })
|
||||
|
||||
beforeAll(() => {
|
||||
resizeObserver = mockResizeObserver()
|
||||
})
|
||||
|
||||
beforeEach(cleanup)
|
||||
|
||||
it('Has initial value', async () => {
|
||||
const { component } = await getFileList()
|
||||
expect(component.textContent).toBe('600')
|
||||
})
|
||||
|
||||
it('observes the file list element', async () => {
|
||||
const { fileList } = await getFileList()
|
||||
expect(resizeObserver.getObservedElements()).toContain(fileList)
|
||||
})
|
||||
|
||||
it('Is reactive to size change', async () => {
|
||||
const { component, fileList } = await getFileList()
|
||||
expect(component.textContent).toBe('600')
|
||||
expect(resizeObserver.getObservedElements()).toHaveLength(1)
|
||||
|
||||
resizeObserver.mockElementSize(fileList, { contentBoxSize: { inlineSize: 800, blockSize: 300 } })
|
||||
resizeObserver.resize(fileList)
|
||||
|
||||
// await rending
|
||||
await nextTick()
|
||||
expect(component.textContent).toBe('800')
|
||||
})
|
||||
})
|
||||
|
||||
async function getFileList() {
|
||||
const { useFileListWidth } = await import('./useFileListWidth.ts')
|
||||
|
||||
const ComponentMock = defineComponent({
|
||||
template: '<div data-testid="component" style="width: 100%;background: white;">{{ fileListWidth }}</div>',
|
||||
setup() {
|
||||
return {
|
||||
fileListWidth: useFileListWidth(),
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const FileListMock = defineComponent({
|
||||
template: '<main id="app-content-vue" style="width: 100%;"><component-mock /></main>',
|
||||
components: {
|
||||
ComponentMock,
|
||||
},
|
||||
})
|
||||
|
||||
const root = render(FileListMock)
|
||||
const fileList = root.baseElement.querySelector('#app-content-vue') as HTMLElement
|
||||
|
||||
// mock initial size
|
||||
resizeObserver.mockElementSize(fileList, { contentBoxSize: { inlineSize: 600, blockSize: 200 } })
|
||||
resizeObserver.resize()
|
||||
// await rending
|
||||
await nextTick()
|
||||
|
||||
return {
|
||||
root,
|
||||
component: root.getByTestId('component'),
|
||||
fileList,
|
||||
}
|
||||
}
|
||||
@ -1,162 +0,0 @@
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import DialogConfirmFileExtension from './DialogConfirmFileExtension.vue'
|
||||
import { useUserConfigStore } from '../store/userconfig.ts'
|
||||
|
||||
describe('DialogConfirmFileExtension', () => {
|
||||
it('renders with both extensions', () => {
|
||||
cy.mount(DialogConfirmFileExtension, {
|
||||
propsData: {
|
||||
oldExtension: '.old',
|
||||
newExtension: '.new',
|
||||
},
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
cy.findByRole('dialog')
|
||||
.as('dialog')
|
||||
.should('be.visible')
|
||||
cy.get('@dialog')
|
||||
.findByRole('heading')
|
||||
.should('contain.text', 'Change file extension')
|
||||
cy.get('@dialog')
|
||||
.findByRole('checkbox', { name: /Do not show this dialog again/i })
|
||||
.should('exist')
|
||||
.and('not.be.checked')
|
||||
cy.get('@dialog')
|
||||
.findByRole('button', { name: 'Keep .old' })
|
||||
.should('be.visible')
|
||||
cy.get('@dialog')
|
||||
.findByRole('button', { name: 'Use .new' })
|
||||
.should('be.visible')
|
||||
})
|
||||
|
||||
it('renders without old extension', () => {
|
||||
cy.mount(DialogConfirmFileExtension, {
|
||||
propsData: {
|
||||
newExtension: '.new',
|
||||
},
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
cy.findByRole('dialog')
|
||||
.as('dialog')
|
||||
.should('be.visible')
|
||||
cy.get('@dialog')
|
||||
.findByRole('button', { name: 'Keep without extension' })
|
||||
.should('be.visible')
|
||||
cy.get('@dialog')
|
||||
.findByRole('button', { name: 'Use .new' })
|
||||
.should('be.visible')
|
||||
})
|
||||
|
||||
it('renders without new extension', () => {
|
||||
cy.mount(DialogConfirmFileExtension, {
|
||||
propsData: {
|
||||
oldExtension: '.old',
|
||||
},
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
cy.findByRole('dialog')
|
||||
.as('dialog')
|
||||
.should('be.visible')
|
||||
cy.get('@dialog')
|
||||
.findByRole('button', { name: 'Keep .old' })
|
||||
.should('be.visible')
|
||||
cy.get('@dialog')
|
||||
.findByRole('button', { name: 'Remove extension' })
|
||||
.should('be.visible')
|
||||
})
|
||||
|
||||
it('emits correct value on keep old', () => {
|
||||
cy.mount(DialogConfirmFileExtension, {
|
||||
propsData: {
|
||||
oldExtension: '.old',
|
||||
newExtension: '.new',
|
||||
},
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})],
|
||||
},
|
||||
}).as('component')
|
||||
|
||||
cy.findByRole('dialog')
|
||||
.as('dialog')
|
||||
.should('be.visible')
|
||||
cy.get('@dialog')
|
||||
.findByRole('button', { name: 'Keep .old' })
|
||||
.click()
|
||||
cy.get('@component')
|
||||
.its('wrapper')
|
||||
.should((wrapper) => expect(wrapper.emitted('close')).to.eql([[false]]))
|
||||
})
|
||||
|
||||
it('emits correct value on use new', () => {
|
||||
cy.mount(DialogConfirmFileExtension, {
|
||||
propsData: {
|
||||
oldExtension: '.old',
|
||||
newExtension: '.new',
|
||||
},
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})],
|
||||
},
|
||||
}).as('component')
|
||||
|
||||
cy.findByRole('dialog')
|
||||
.as('dialog')
|
||||
.should('be.visible')
|
||||
cy.get('@dialog')
|
||||
.findByRole('button', { name: 'Use .new' })
|
||||
.click()
|
||||
cy.get('@component')
|
||||
.its('wrapper')
|
||||
.should((wrapper) => expect(wrapper.emitted('close')).to.eql([[true]]))
|
||||
})
|
||||
|
||||
it('updates user config when checking the checkbox', () => {
|
||||
const pinia = createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})
|
||||
|
||||
cy.mount(DialogConfirmFileExtension, {
|
||||
propsData: {
|
||||
oldExtension: '.old',
|
||||
newExtension: '.new',
|
||||
},
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
},
|
||||
}).as('component')
|
||||
|
||||
cy.findByRole('dialog')
|
||||
.as('dialog')
|
||||
.should('be.visible')
|
||||
cy.get('@dialog')
|
||||
.findByRole('checkbox', { name: /Do not show this dialog again/i })
|
||||
.check({ force: true })
|
||||
|
||||
cy.wrap(useUserConfigStore())
|
||||
.its('update')
|
||||
.should('have.been.calledWith', 'show_dialog_file_extension', false)
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,132 @@
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { cleanup, fireEvent, render } from '@testing-library/vue'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import DialogConfirmFileExtension from './DialogConfirmFileExtension.vue'
|
||||
import { useUserConfigStore } from '../store/userconfig.ts'
|
||||
|
||||
describe('DialogConfirmFileExtension', () => {
|
||||
beforeEach(cleanup)
|
||||
|
||||
it('renders with both extensions', async () => {
|
||||
const component = render(DialogConfirmFileExtension, {
|
||||
props: {
|
||||
oldExtension: '.old',
|
||||
newExtension: '.new',
|
||||
},
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
await expect(component.findByRole('dialog', { name: 'Change file extension' })).resolves.not.toThrow()
|
||||
expect((component.getByRole('checkbox', { name: /Do not show this dialog again/i }) as HTMLInputElement).checked).toBe(false)
|
||||
await expect(component.findByRole('button', { name: 'Keep .old' })).resolves.not.toThrow()
|
||||
await expect(component.findByRole('button', { name: 'Use .new' })).resolves.not.toThrow()
|
||||
})
|
||||
|
||||
it('renders without old extension', async () => {
|
||||
const component = render(DialogConfirmFileExtension, {
|
||||
props: {
|
||||
newExtension: '.new',
|
||||
},
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
await expect(component.findByRole('dialog', { name: 'Change file extension' })).resolves.not.toThrow()
|
||||
await expect(component.findByRole('button', { name: 'Keep without extension' })).resolves.not.toThrow()
|
||||
await expect(component.findByRole('button', { name: 'Use .new' })).resolves.not.toThrow()
|
||||
})
|
||||
|
||||
it('renders without new extension', async () => {
|
||||
const component = render(DialogConfirmFileExtension, {
|
||||
props: {
|
||||
oldExtension: '.old',
|
||||
},
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
await expect(component.findByRole('dialog', { name: 'Change file extension' })).resolves.not.toThrow()
|
||||
await expect(component.findByRole('button', { name: 'Keep .old' })).resolves.not.toThrow()
|
||||
await expect(component.findByRole('button', { name: 'Remove extension' })).resolves.not.toThrow()
|
||||
})
|
||||
|
||||
it('emits correct value on keep old', async () => {
|
||||
const onclose = vi.fn()
|
||||
const component = render(DialogConfirmFileExtension, {
|
||||
props: {
|
||||
oldExtension: '.old',
|
||||
newExtension: '.new',
|
||||
},
|
||||
listeners: {
|
||||
close: onclose,
|
||||
},
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
await fireEvent.click(component.getByRole('button', { name: 'Keep .old' }))
|
||||
expect(onclose).toHaveBeenCalledOnce()
|
||||
expect(onclose).toHaveBeenCalledWith(false)
|
||||
})
|
||||
|
||||
it('emits correct value on use new', async () => {
|
||||
const onclose = vi.fn()
|
||||
const component = render(DialogConfirmFileExtension, {
|
||||
props: {
|
||||
oldExtension: '.old',
|
||||
newExtension: '.new',
|
||||
},
|
||||
listeners: {
|
||||
close: onclose,
|
||||
},
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
await fireEvent.click(component.getByRole('button', { name: 'Use .new' }))
|
||||
expect(onclose).toHaveBeenCalledOnce()
|
||||
expect(onclose).toHaveBeenCalledWith(true)
|
||||
})
|
||||
|
||||
it('updates user config when checking the checkbox', async () => {
|
||||
const pinia = createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
})
|
||||
|
||||
const component = render(DialogConfirmFileExtension, {
|
||||
props: {
|
||||
oldExtension: '.old',
|
||||
newExtension: '.new',
|
||||
},
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
},
|
||||
})
|
||||
|
||||
await fireEvent.click(component.getByRole('checkbox', { name: /Do not show this dialog again/i }))
|
||||
const store = useUserConfigStore()
|
||||
expect(store.update).toHaveBeenCalledOnce()
|
||||
expect(store.update).toHaveBeenCalledWith('show_dialog_file_extension', false)
|
||||
})
|
||||
})
|
||||
@ -1,262 +0,0 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { Folder, Navigation } from '@nextcloud/files'
|
||||
|
||||
import FolderSvg from '@mdi/svg/svg/folder.svg?raw'
|
||||
import { getNavigation, View } from '@nextcloud/files'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import NavigationView from './FilesNavigation.vue'
|
||||
import router from '../router/router.ts'
|
||||
import RouterService from '../services/RouterService.ts'
|
||||
import { useViewConfigStore } from '../store/viewConfig.ts'
|
||||
|
||||
function resetNavigation() {
|
||||
const nav = getNavigation()
|
||||
;[...nav.views].forEach(({ id }) => nav.remove(id))
|
||||
nav.setActive(null)
|
||||
}
|
||||
|
||||
function createView(id: string, name: string, parent?: string) {
|
||||
return new View({
|
||||
id,
|
||||
name,
|
||||
getContents: async () => ({ folder: {} as Folder, contents: [] }),
|
||||
icon: FolderSvg,
|
||||
order: 1,
|
||||
parent,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function mockWindow() {
|
||||
window.OCP ??= {}
|
||||
window.OCP.Files ??= {}
|
||||
window.OCP.Files.Router = new RouterService(router)
|
||||
}
|
||||
|
||||
describe('Navigation renders', () => {
|
||||
before(async () => {
|
||||
delete window._nc_navigation
|
||||
mockWindow()
|
||||
getNavigation().register(createView('files', 'Files'))
|
||||
await router.replace({ name: 'filelist', params: { view: 'files' } })
|
||||
|
||||
cy.mockInitialState('files', 'storageStats', {
|
||||
used: 1000 * 1000 * 1000,
|
||||
quota: -1,
|
||||
})
|
||||
})
|
||||
|
||||
after(() => cy.unmockInitialState())
|
||||
|
||||
it('renders', () => {
|
||||
cy.mount(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('[data-cy-files-navigation]').should('be.visible')
|
||||
cy.get('[data-cy-files-navigation-settings-quota]').should('be.visible')
|
||||
cy.get('[data-cy-files-navigation-settings-button]').should('be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Navigation API', () => {
|
||||
let Navigation: Navigation
|
||||
|
||||
before(async () => {
|
||||
delete window._nc_navigation
|
||||
Navigation = getNavigation()
|
||||
mockWindow()
|
||||
|
||||
await router.replace({ name: 'filelist', params: { view: 'files' } })
|
||||
})
|
||||
|
||||
beforeEach(() => resetNavigation())
|
||||
|
||||
it('Check API entries rendering', () => {
|
||||
Navigation.register(createView('files', 'Files'))
|
||||
console.warn(Navigation.views)
|
||||
|
||||
cy.mount(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [
|
||||
createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('[data-cy-files-navigation]').should('be.visible')
|
||||
cy.get('[data-cy-files-navigation-item]').should('have.length', 1)
|
||||
cy.get('[data-cy-files-navigation-item="files"]').should('be.visible')
|
||||
cy.get('[data-cy-files-navigation-item="files"]').should('contain.text', 'Files')
|
||||
})
|
||||
|
||||
it('Adds a new entry and render', () => {
|
||||
Navigation.register(createView('files', 'Files'))
|
||||
Navigation.register(createView('sharing', 'Sharing'))
|
||||
|
||||
cy.mount(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('[data-cy-files-navigation]').should('be.visible')
|
||||
cy.get('[data-cy-files-navigation-item]').should('have.length', 2)
|
||||
cy.get('[data-cy-files-navigation-item="sharing"]').should('be.visible')
|
||||
cy.get('[data-cy-files-navigation-item="sharing"]').should('contain.text', 'Sharing')
|
||||
})
|
||||
|
||||
it('Adds a new children, render and open menu', () => {
|
||||
Navigation.register(createView('files', 'Files'))
|
||||
Navigation.register(createView('sharing', 'Sharing'))
|
||||
Navigation.register(createView('sharingin', 'Shared with me', 'sharing'))
|
||||
|
||||
cy.mount(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
cy.wrap(useViewConfigStore()).as('viewConfigStore')
|
||||
|
||||
cy.get('[data-cy-files-navigation]').should('be.visible')
|
||||
cy.get('[data-cy-files-navigation-item]').should('have.length', 3)
|
||||
|
||||
// Toggle the sharing entry children
|
||||
cy.get('[data-cy-files-navigation-item="sharing"] button.icon-collapse').should('exist')
|
||||
cy.get('[data-cy-files-navigation-item="sharing"] button.icon-collapse').click({ force: true })
|
||||
|
||||
// Expect store update to be called
|
||||
cy.get('@viewConfigStore').its('update').should('have.been.calledWith', 'sharing', 'expanded', true)
|
||||
|
||||
// Validate children
|
||||
cy.get('[data-cy-files-navigation-item="sharingin"]').should('be.visible')
|
||||
cy.get('[data-cy-files-navigation-item="sharingin"]').should('contain.text', 'Shared with me')
|
||||
|
||||
// Toggle the sharing entry children 🇦again
|
||||
cy.get('[data-cy-files-navigation-item="sharing"] button.icon-collapse').click({ force: true })
|
||||
cy.get('[data-cy-files-navigation-item="sharingin"]').should('not.be.visible')
|
||||
|
||||
// Expect store update to be called
|
||||
cy.get('@viewConfigStore').its('update').should('have.been.calledWith', 'sharing', 'expanded', false)
|
||||
})
|
||||
|
||||
it('Throws when adding a duplicate entry', () => {
|
||||
Navigation.register(createView('files', 'Files'))
|
||||
expect(() => Navigation.register(createView('files', 'Files')))
|
||||
.to.throw('View id files is already registered')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Quota rendering', () => {
|
||||
before(async () => {
|
||||
delete window._nc_navigation
|
||||
mockWindow()
|
||||
getNavigation().register(createView('files', 'Files'))
|
||||
await router.replace({ name: 'filelist', params: { view: 'files' } })
|
||||
})
|
||||
|
||||
afterEach(() => cy.unmockInitialState())
|
||||
|
||||
it('Unknown quota', () => {
|
||||
cy.mount(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('[data-cy-files-navigation-settings-quota]').should('not.exist')
|
||||
})
|
||||
|
||||
it('Unlimited quota', () => {
|
||||
cy.mockInitialState('files', 'storageStats', {
|
||||
used: 1024 * 1024 * 1024,
|
||||
quota: -1,
|
||||
total: 50 * 1024 * 1024 * 1024,
|
||||
})
|
||||
|
||||
cy.mount(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('[data-cy-files-navigation-settings-quota]').should('be.visible')
|
||||
cy.get('[data-cy-files-navigation-settings-quota]').should('contain.text', '1 GB used')
|
||||
cy.get('[data-cy-files-navigation-settings-quota] progress').should('not.exist')
|
||||
})
|
||||
|
||||
it('Non-reached quota', () => {
|
||||
cy.mockInitialState('files', 'storageStats', {
|
||||
used: 1024 * 1024 * 1024,
|
||||
quota: 5 * 1024 * 1024 * 1024,
|
||||
total: 5 * 1024 * 1024 * 1024,
|
||||
relative: 20, // percent
|
||||
})
|
||||
|
||||
cy.mount(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('[data-cy-files-navigation-settings-quota]').should('be.visible')
|
||||
cy.get('[data-cy-files-navigation-settings-quota]').should('contain.text', '1 GB of 5 GB used')
|
||||
cy.get('[data-cy-files-navigation-settings-quota] progress')
|
||||
.should('exist')
|
||||
.and('have.attr', 'value', '20')
|
||||
})
|
||||
|
||||
it('Reached quota', () => {
|
||||
cy.mockInitialState('files', 'storageStats', {
|
||||
used: 5 * 1024 * 1024 * 1024,
|
||||
quota: 1024 * 1024 * 1024,
|
||||
total: 1024 * 1024 * 1024,
|
||||
relative: 500, // percent
|
||||
})
|
||||
|
||||
cy.mount(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: cy.spy,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('[data-cy-files-navigation-settings-quota]').should('be.visible')
|
||||
cy.get('[data-cy-files-navigation-settings-quota]').should('contain.text', '5 GB of 1 GB used')
|
||||
cy.get('[data-cy-files-navigation-settings-quota] progress')
|
||||
.should('exist')
|
||||
.and('have.attr', 'value', '100') // progress max is 100
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,286 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { Folder, Navigation } from '@nextcloud/files'
|
||||
|
||||
import FolderSvg from '@mdi/svg/svg/folder.svg?raw'
|
||||
import { getNavigation, View } from '@nextcloud/files'
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { cleanup, fireEvent, getAllByRole, render } from '@testing-library/vue'
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import NavigationView from './FilesNavigation.vue'
|
||||
import router from '../router/router.ts'
|
||||
import RouterService from '../services/RouterService.ts'
|
||||
import { useViewConfigStore } from '../store/viewConfig.ts'
|
||||
|
||||
afterEach(() => removeInitialState())
|
||||
beforeAll(async () => {
|
||||
Object.defineProperty(document.documentElement, 'clientWidth', { value: 1920 })
|
||||
await fireEvent.resize(window)
|
||||
})
|
||||
|
||||
describe('Navigation', () => {
|
||||
beforeEach(cleanup)
|
||||
|
||||
beforeEach(async () => {
|
||||
delete window._nc_navigation
|
||||
mockWindow()
|
||||
getNavigation().register(createView('files', 'Files'))
|
||||
await router.replace({ name: 'filelist', params: { view: 'files' } })
|
||||
})
|
||||
|
||||
it('renders navigation with settings button and search', async () => {
|
||||
const component = render(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
// see the navigation
|
||||
await expect(component.findByRole('navigation', { name: 'Files' })).resolves.not.toThrow()
|
||||
// see the search box
|
||||
await expect(component.findByRole('searchbox', { name: /Search here/ })).resolves.not.toThrow()
|
||||
// see the settings entry
|
||||
await expect(component.findByRole('link', { name: /Files settings/ })).resolves.not.toThrow()
|
||||
})
|
||||
|
||||
it('renders no quota without storage stats', () => {
|
||||
const component = render(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
expect(component.baseElement.querySelector('[data-cy-files-navigation-settings-quota]')).toBeNull()
|
||||
})
|
||||
|
||||
it('Unlimited quota shows used storage but no progressbar', async () => {
|
||||
mockInitialState('files', 'storageStats', {
|
||||
used: 1024 * 1024 * 1024,
|
||||
quota: -1,
|
||||
total: 50 * 1024 * 1024 * 1024,
|
||||
})
|
||||
|
||||
const component = render(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
expect(component.baseElement.querySelector('[data-cy-files-navigation-settings-quota]')).not.toBeNull()
|
||||
|
||||
await expect(component.findByText('1 GB used')).resolves.not.toThrow()
|
||||
await expect(component.findByRole('progressbar')).rejects.toThrow()
|
||||
})
|
||||
|
||||
it('Non-reached quota shows stats and progress', async () => {
|
||||
mockInitialState('files', 'storageStats', {
|
||||
used: 1024 * 1024 * 1024,
|
||||
quota: 5 * 1024 * 1024 * 1024,
|
||||
total: 5 * 1024 * 1024 * 1024,
|
||||
relative: 20, // percent
|
||||
})
|
||||
|
||||
const component = render(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
await expect(component.findByText('1 GB of 5 GB used')).resolves.not.toThrow()
|
||||
await expect(component.findByRole('progressbar')).resolves.not.toThrow()
|
||||
expect((component.getByRole('progressbar') as HTMLProgressElement).value).toBe(20)
|
||||
})
|
||||
|
||||
it('Reached quota', async () => {
|
||||
mockInitialState('files', 'storageStats', {
|
||||
used: 5 * 1024 * 1024 * 1024,
|
||||
quota: 1024 * 1024 * 1024,
|
||||
total: 1024 * 1024 * 1024,
|
||||
relative: 500, // percent
|
||||
})
|
||||
|
||||
const component = render(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
})],
|
||||
},
|
||||
})
|
||||
|
||||
await expect(component.findByText('5 GB of 1 GB used')).resolves.not.toThrow()
|
||||
await expect(component.findByRole('progressbar')).resolves.not.toThrow()
|
||||
expect((component.getByRole('progressbar') as HTMLProgressElement).value).toBe(100)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Navigation API', () => {
|
||||
let Navigation: Navigation
|
||||
|
||||
beforeEach(async () => {
|
||||
delete window._nc_navigation
|
||||
Navigation = getNavigation()
|
||||
mockWindow()
|
||||
|
||||
await router.replace({ name: 'filelist', params: { view: 'files' } })
|
||||
})
|
||||
|
||||
beforeEach(resetNavigation)
|
||||
beforeEach(cleanup)
|
||||
|
||||
it('Check API entries rendering', async () => {
|
||||
Navigation.register(createView('files', 'Files'))
|
||||
|
||||
const component = render(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
// see the navigation
|
||||
await expect(component.findByRole('navigation', { name: 'Files' })).resolves.not.toThrow()
|
||||
// see the views
|
||||
await expect(component.findByRole('list', { name: 'Views' })).resolves.not.toThrow()
|
||||
// see the entry
|
||||
await expect(component.findByRole('link', { name: 'Files' })).resolves.not.toThrow()
|
||||
// see that the entry has all props
|
||||
const entry = component.getByRole('link', { name: 'Files' })
|
||||
expect(entry.getAttribute('href')).toMatch(/\/apps\/files\/files$/)
|
||||
expect(entry.getAttribute('aria-current')).toBe('page')
|
||||
expect(entry.getAttribute('title')).toBe('Files')
|
||||
})
|
||||
|
||||
it('Adds a new entry and render', async () => {
|
||||
Navigation.register(createView('files', 'Files'))
|
||||
Navigation.register(createView('sharing', 'Sharing'))
|
||||
|
||||
const component = render(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const list = component.getByRole('list', { name: 'Views' })
|
||||
expect(getAllByRole(list, 'listitem')).toHaveLength(2)
|
||||
|
||||
await expect(component.findByRole('link', { name: 'Files' })).resolves.not.toThrow()
|
||||
await expect(component.findByRole('link', { name: 'Sharing' })).resolves.not.toThrow()
|
||||
// see that the entry has all props
|
||||
const entry = component.getByRole('link', { name: 'Sharing' })
|
||||
expect(entry.getAttribute('href')).toMatch(/\/apps\/files\/sharing$/)
|
||||
expect(entry.getAttribute('aria-current')).toBeNull()
|
||||
expect(entry.getAttribute('title')).toBe('Sharing')
|
||||
})
|
||||
|
||||
it('Adds a new children, render and open menu', async () => {
|
||||
Navigation.register(createView('files', 'Files'))
|
||||
Navigation.register(createView('sharing', 'Sharing'))
|
||||
Navigation.register(createView('sharingin', 'Shared with me', 'sharing'))
|
||||
|
||||
const component = render(NavigationView, {
|
||||
router,
|
||||
global: {
|
||||
plugins: [
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
const viewConfigStore = useViewConfigStore()
|
||||
|
||||
const list = component.getByRole('list', { name: 'Views' })
|
||||
expect(getAllByRole(list, 'listitem')).toHaveLength(3)
|
||||
|
||||
// Toggle the sharing entry children
|
||||
const entry = component.getByRole('link', { name: 'Sharing' })
|
||||
expect(entry.getAttribute('aria-expanded')).toBe('false')
|
||||
await fireEvent.click(component.getByRole('button', { name: 'Open menu' }))
|
||||
expect(entry.getAttribute('aria-expanded')).toBe('true')
|
||||
|
||||
// Expect store update to be called
|
||||
expect(viewConfigStore.update).toHaveBeenCalled()
|
||||
expect(viewConfigStore.update).toHaveBeenCalledWith('sharing', 'expanded', true)
|
||||
|
||||
// Validate children
|
||||
await expect(component.findByRole('link', { name: 'Shared with me' })).resolves.not.toThrow()
|
||||
|
||||
await fireEvent.click(component.getByRole('button', { name: 'Collapse menu' }))
|
||||
// Expect store update to be called
|
||||
expect(viewConfigStore.update).toHaveBeenCalledWith('sharing', 'expanded', false)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Remove the mocked initial state
|
||||
*/
|
||||
function removeInitialState(): void {
|
||||
document.querySelectorAll('input[type="hidden"]').forEach((el) => {
|
||||
el.remove()
|
||||
})
|
||||
// clear the cache
|
||||
delete globalThis._nc_initial_state
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to mock an initial state value
|
||||
* @param app - The app
|
||||
* @param key - The key
|
||||
* @param value - The value
|
||||
*/
|
||||
function mockInitialState(app: string, key: string, value: unknown): void {
|
||||
const el = document.createElement('input')
|
||||
el.value = btoa(JSON.stringify(value))
|
||||
el.id = `initial-state-${app}-${key}`
|
||||
el.type = 'hidden'
|
||||
|
||||
document.head.appendChild(el)
|
||||
}
|
||||
|
||||
function resetNavigation() {
|
||||
const nav = getNavigation()
|
||||
;[...nav.views].forEach(({ id }) => nav.remove(id))
|
||||
nav.setActive(null)
|
||||
}
|
||||
|
||||
function createView(id: string, name: string, parent?: string) {
|
||||
return new View({
|
||||
id,
|
||||
name,
|
||||
getContents: async () => ({ folder: {} as Folder, contents: [] }),
|
||||
icon: FolderSvg,
|
||||
order: 1,
|
||||
parent,
|
||||
})
|
||||
}
|
||||
|
||||
function mockWindow() {
|
||||
window.OCP ??= {}
|
||||
window.OCP.Files ??= {}
|
||||
window.OCP.Files.Router = new RouterService(router)
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import Markdown from './Markdown.vue'
|
||||
|
||||
describe('Markdown component', () => {
|
||||
it('renders links', () => {
|
||||
cy.mount(Markdown, {
|
||||
propsData: {
|
||||
text: 'This is [a link](http://example.com)!',
|
||||
},
|
||||
})
|
||||
|
||||
cy.contains('This is')
|
||||
.find('a')
|
||||
.should('exist')
|
||||
.and('have.attr', 'href', 'http://example.com')
|
||||
.and('contain.text', 'a link')
|
||||
})
|
||||
|
||||
it('renders headings', () => {
|
||||
cy.mount(Markdown, {
|
||||
propsData: {
|
||||
text: '# level 1\nText\n## level 2\nText\n### level 3\nText\n#### level 4\nText\n##### level 5\nText\n###### level 6\nText\n',
|
||||
},
|
||||
})
|
||||
|
||||
for (let level = 1; level <= 6; level++) {
|
||||
cy.contains(`h${level}`, `level ${level}`)
|
||||
.should('be.visible')
|
||||
}
|
||||
})
|
||||
|
||||
it('can limit headings', () => {
|
||||
cy.mount(Markdown, {
|
||||
propsData: {
|
||||
text: '# level 1\nText\n## level 2\nText\n### level 3\nText\n#### level 4\nText\n##### level 5\nText\n###### level 6\nText\n',
|
||||
minHeading: 4,
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('h1').should('not.exist')
|
||||
cy.get('h2').should('not.exist')
|
||||
cy.get('h3').should('not.exist')
|
||||
cy.get('h4')
|
||||
.should('exist')
|
||||
.and('contain.text', 'level 1')
|
||||
cy.get('h5')
|
||||
.should('exist')
|
||||
.and('contain.text', 'level 2')
|
||||
cy.contains('h6', 'level 3').should('exist')
|
||||
cy.contains('h6', 'level 4').should('exist')
|
||||
cy.contains('h6', 'level 5').should('exist')
|
||||
cy.contains('h6', 'level 6').should('exist')
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,58 @@
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { cleanup, render } from '@testing-library/vue'
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
import Markdown from './Markdown.vue'
|
||||
|
||||
describe('Markdown component', () => {
|
||||
beforeEach(cleanup)
|
||||
|
||||
it('renders links', () => {
|
||||
const component = render(Markdown, {
|
||||
props: {
|
||||
text: 'This is [a link](http://example.com)!',
|
||||
},
|
||||
})
|
||||
|
||||
const link = component.getByRole('link')
|
||||
expect(link).toBeInstanceOf(HTMLAnchorElement)
|
||||
expect(link.getAttribute('href')).toBe('http://example.com')
|
||||
expect(link.textContent).toBe('a link')
|
||||
})
|
||||
|
||||
it('renders headings', () => {
|
||||
const component = render(Markdown, {
|
||||
props: {
|
||||
text: '# level 1\nText\n## level 2\nText\n### level 3\nText\n#### level 4\nText\n##### level 5\nText\n###### level 6\nText\n',
|
||||
},
|
||||
})
|
||||
|
||||
for (let level = 1; level <= 6; level++) {
|
||||
const heading = component.getByRole('heading', { level })
|
||||
expect(heading.textContent).toBe(`level ${level}`)
|
||||
}
|
||||
})
|
||||
|
||||
it('can limit headings', async () => {
|
||||
const component = render(Markdown, {
|
||||
props: {
|
||||
text: '# level 1\nText\n## level 2\nText\n### level 3\nText\n#### level 4\nText\n##### level 5\nText\n###### level 6\nText\n',
|
||||
minHeading: 4,
|
||||
},
|
||||
})
|
||||
|
||||
await expect(component.findByRole('heading', { level: 1 })).rejects.toThrow()
|
||||
await expect(component.findByRole('heading', { level: 2 })).rejects.toThrow()
|
||||
await expect(component.findByRole('heading', { level: 3 })).rejects.toThrow()
|
||||
|
||||
expect(component.getByRole('heading', { level: 4 }).textContent).toBe('level 1')
|
||||
expect(component.getByRole('heading', { level: 5 }).textContent).toBe('level 2')
|
||||
await expect(component.findByRole('heading', { level: 6, name: 'level 3' })).resolves.not.toThrow()
|
||||
await expect(component.findByRole('heading', { level: 6, name: 'level 4' })).resolves.not.toThrow()
|
||||
await expect(component.findByRole('heading', { level: 6, name: 'level 5' })).resolves.not.toThrow()
|
||||
await expect(component.findByRole('heading', { level: 6, name: 'level 6' })).resolves.not.toThrow()
|
||||
})
|
||||
})
|
||||
Loading…
Reference in New Issue