Skip to content

Commit

Permalink
test: re-organise test suite
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe committed Dec 6, 2023
1 parent fe420b9 commit 9c87782
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 254 deletions.
22 changes: 18 additions & 4 deletions examples/app-vitest-full/tests/nuxt/auto-import.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import { expect, it } from 'vitest'
import { describe, expect, it } from 'vitest'

it('should not mock', () => {
expect(useAutoImportedTarget()).toMatchInlineSnapshot('"the original"')
expect(useAutoImportedNonTarget()).toMatchInlineSnapshot('"the original"')
describe('auto-imports', () => {
it('can use core nuxt composables within test file', () => {
expect(useAppConfig().hey).toMatchInlineSnapshot('false')
})

it('can access auto-imported composables from within project', () => {
const state = useSingleState()
expect(state.value).toMatchInlineSnapshot('{}')
state.value.field = 'new value'
expect(state.value.field).toMatchInlineSnapshot('"new value"')
expect(useSingleState().value.field).toMatchInlineSnapshot('"new value"')
})

it('should not mock imports that are mocked in another test file', () => {
expect(useAutoImportedTarget()).toMatchInlineSnapshot('"the original"')
expect(useAutoImportedNonTarget()).toMatchInlineSnapshot('"the original"')
})
})

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,53 +1,26 @@
import { describe, expect, it } from 'vitest'

import { mountSuspended, registerEndpoint } from '@nuxt/test-utils/runtime-utils'

import { listen } from 'listhen'
import { createApp, eventHandler, toNodeListener } from 'h3'
import { mountSuspended } from '@nuxt/test-utils/runtime-utils'

import App from '~/app.vue'
import FetchComponent from '~/components/FetchComponent.vue'
import OptionsComponent from '~/components/OptionsComponent.vue'
import WrapperTests from '~/components/WrapperTests.vue'

import { mount } from '@vue/test-utils'

describe('client-side nuxt features', () => {
it('can use core nuxt composables within test file', () => {
expect(useAppConfig().hey).toMatchInlineSnapshot('false')
})
import ExportDefaultComponent from '~/components/ExportDefaultComponent.vue'
import ExportDefineComponent from '~/components/ExportDefineComponent.vue'
import ExportDefaultWithRenderComponent from '~/components/ExportDefaultWithRenderComponent.vue'
import ExportDefaultReturnsRenderComponent from '~/components/ExportDefaultReturnsRenderComponent.vue'

it('can access auto-imported composables from within project', () => {
const state = useSingleState()
expect(state.value).toMatchInlineSnapshot('{}')
state.value.field = 'new value'
expect(state.value.field).toMatchInlineSnapshot('"new value"')
expect(useSingleState().value.field).toMatchInlineSnapshot('"new value"')
})

it('can access injections from nuxt plugins', () => {
const app = useNuxtApp()
expect(app.$auth.didInject).toMatchInlineSnapshot('true')
expect(app.$router).toBeDefined()
})
const formats = {
ExportDefaultComponent,
ExportDefineComponent,
ExportDefaultWithRenderComponent,
ExportDefaultReturnsRenderComponent,
}

it('defaults to index page', async () => {
expect(useRoute().matched[0].meta).toMatchInlineSnapshot(`
{
"value": "set in index",
}
`)
})

it('allows pushing to other pages', async () => {
await navigateTo('/something')
expect(useNuxtApp().$router.currentRoute.value.path).toEqual('/something')
await nextTick()
expect(useRoute().path).toEqual('/something')
})
})

describe('test utils', () => {
describe('mountSuspended', () => {
it('can mount components within nuxt suspense', async () => {
const component = await mountSuspended(App)
expect(component.html()).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -114,19 +87,6 @@ describe('test utils', () => {
`)
})

it('can use $fetch', async () => {
const app = createApp().use(
'/todos/1',
eventHandler(() => ({ id: 1 }))
)
const server = await listen(toNodeListener(app))
const [{ url }] = await server.getURLs()
expect(await $fetch<unknown>('/todos/1', { baseURL: url })).toMatchObject({
id: 1,
})
await server.close()
})

// This test works (you can delete it later)
it('can receive emitted events from components using defineModel', () => {
const component = mount(WrapperTests)
Expand All @@ -147,51 +107,19 @@ describe('test utils', () => {
expect(component.vm.testExpose?.()).toBe('thing')
expect(component.vm.someRef).toBe('thing')
})
})

it('can mock fetch requests', async () => {
registerEndpoint('https://jsonplaceholder.typicode.com/todos/1', () => ({
title: 'title from mocked api',
}))
const component = await mountSuspended(FetchComponent)
expect(component.html()).toMatchInlineSnapshot(
'"<div>title from mocked api</div>"'
)
})

it('can mock fetch requests', async () => {
registerEndpoint('/with-query', () => ({
title: 'mocked',
}))
expect(
await $fetch<unknown>('/with-query', { query: { test: true } })
).toMatchObject({
title: 'mocked',
})
})

it('can mock fetch requests with explicit methods', async () => {
registerEndpoint('/method', {
method: 'POST',
handler: () => ({ method: 'POST' }),
})
registerEndpoint('/method', {
method: 'GET',
handler: () => ({ method: 'GET' }),
})
expect(await $fetch<unknown>('/method', { method: 'POST' })).toMatchObject({
method: 'POST',
describe.each(Object.entries(formats))(`%s`, (name, component) => {
it('mounts with props', async () => {
const wrapper = await mountSuspended(component, {
props: {
myProp: 'Hello nuxt-vitest',
},
})
expect(await $fetch<unknown>('/method')).toMatchObject({ method: 'GET' })
})

// TODO: reenable when merging Nuxt 3.7
it.skip('handles nuxt routing', async () => {
const component = await mountSuspended(App, { route: '/test' })
expect(component.html()).toMatchInlineSnapshot(`
"<div>This is an auto-imported component</div>
<div> I am a global component </div>
<div>/test</div>
<a href=\\"/test\\"> Test link </a>"
`)
expect(wrapper.html()).toEqual(`
<div>
<h1>${name}</h1><pre>Hello nuxt-vitest</pre><pre>XHello nuxt-vitest</pre>
</div>
`.trim())
})
})
9 changes: 9 additions & 0 deletions examples/app-vitest-full/tests/nuxt/plugins.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { describe, expect, it } from 'vitest'

describe('plugins', () => {
it('can access injections', () => {
const app = useNuxtApp()
expect(app.$auth.didInject).toMatchInlineSnapshot('true')
expect(app.$router).toBeDefined()
})
})
94 changes: 94 additions & 0 deletions examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { afterEach, describe, expect, it } from 'vitest'
import { renderSuspended } from '@nuxt/test-utils/runtime-utils'
import { cleanup, fireEvent, screen } from '@testing-library/vue'

import App from '~/app.vue'
import OptionsComponent from '~/components/OptionsComponent.vue'
import WrapperTests from '~/components/WrapperTests.vue'

describe('renderSuspended', () => {
afterEach(() => {
// since we're not running with Vitest globals when running the tests
// from inside the test server. This means testing-library cannot
// auto-attach the cleanup go testing globals, and we have to do
// it here manually.
if (process.env.NUXT_VITEST_DEV_TEST) {
cleanup()
}
})

it('can render components within nuxt suspense', async () => {
const { html } = await renderSuspended(App)
expect(html()).toMatchInlineSnapshot(`
"<div id="test-wrapper">
<div>This is an auto-imported component</div>
<div> I am a global component </div>
<div>Index page</div><a href="/test"> Test link </a>
</div>"
`)
})

it('should render default props within nuxt suspense', async () => {
await renderSuspended(OptionsComponent)
expect(screen.getByRole('heading', { level: 2 })).toMatchInlineSnapshot(
`
<h2>
The original
</h2>
`
)
})

it('should render passed props within nuxt suspense', async () => {
await renderSuspended(OptionsComponent, {
props: {
title: 'title from mount suspense props',
},
})
expect(screen.getByRole('heading', { level: 2 })).toMatchInlineSnapshot(
`
<h2>
title from mount suspense props
</h2>
`
)
})

it('can pass slots to rendered components within nuxt suspense', async () => {
const text = 'slot from mount suspense'
await renderSuspended(OptionsComponent, {
slots: {
default: () => text,
},
})
expect(screen.getByText(text)).toBeDefined()
})

it('can receive emitted events from components rendered within nuxt suspense', async () => {
const { emitted } = await renderSuspended(WrapperTests)
const button = screen.getByRole('button', { name: 'Click me!' })
await fireEvent.click(button)

const emittedEvents = emitted()
expect(emittedEvents.click).toMatchObject(
expect.arrayContaining([
expect.arrayContaining([expect.objectContaining({ type: 'click' })]),
])
)

// since this is a native event it doesn't serialize well
delete emittedEvents.click
expect(emittedEvents).toMatchInlineSnapshot(`
{
"customEvent": [
[
"foo",
],
],
"otherEvent": [
[],
],
}
`)
})
})
32 changes: 32 additions & 0 deletions examples/app-vitest-full/tests/nuxt/routing.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expect, it } from 'vitest'

import { mountSuspended } from '@nuxt/test-utils/runtime-utils'

import App from '~/app.vue'

describe('routing', () => {
it('defaults to index page', async () => {
expect(useRoute().matched[0].meta).toMatchInlineSnapshot(`
{
"value": "set in index",
}
`)
})

it('allows pushing to other pages', async () => {
await navigateTo('/something')
expect(useNuxtApp().$router.currentRoute.value.path).toEqual('/something')
await nextTick()
expect(useRoute().path).toEqual('/something')
})

it('handles nuxt routing', async () => {
const component = await mountSuspended(App, { route: '/test' })
expect(component.html()).toMatchInlineSnapshot(`
"<div>This is an auto-imported component</div>
<div> I am a global component </div>
<div>/test</div>
<a href="/test"> Test link </a>"
`)
})
})

0 comments on commit 9c87782

Please sign in to comment.