Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions examples/app-vitest-browser/app.vue

This file was deleted.

5 changes: 5 additions & 0 deletions examples/app-vitest-browser/app/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
14 changes: 14 additions & 0 deletions examples/app-vitest-browser/app/components/Header.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts">
const { public: { site } } = useRuntimeConfig()
</script>

<template>
<header>
<h1>{{ site.title }}</h1>
<nav>
<ul>
<NuxtLink to="/">Top</NuxtLink>
</ul>
</nav>
</header>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,6 @@
</template>

<script setup lang="ts">
const count = ref(0)
const config = useRuntimeConfig()
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
const { count, increment, decrement } = useCounter()
</script>
21 changes: 21 additions & 0 deletions examples/app-vitest-browser/app/components/MyHello.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
const name = defineModel<string>({ default: () => '' })
const { data } = useHelloApi(name)
</script>

<template>
<div>
<label for="name">Name</label>
<input
id="name"
v-model="name"
>
<br>
<label for="message">Message</label>
<textarea
id="message"
:value="data.message"
readonly
/>
</div>
</template>
13 changes: 13 additions & 0 deletions examples/app-vitest-browser/app/composables/useCounter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export function useCounter() {
const count = ref(0)

const increment = () => {
count.value++
}

const decrement = () => {
count.value--
}

return { count, increment, decrement }
}
14 changes: 14 additions & 0 deletions examples/app-vitest-browser/app/composables/useHelloApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export function useHelloApi(name: Ref<string>) {
const {
data,
pending,
} = useFetch('/api/hello', {
query: reactive({ name }),
default: () => ({ message: '' }),
})

return {
data,
pending,
}
}
13 changes: 13 additions & 0 deletions examples/app-vitest-browser/app/layouts/default.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup lang="ts">
const route = useRoute()
</script>

<template>
<div>
<Header />
<main>
<h2>{{ route.meta.title }}</h2>
<slot />
</main>
</div>
</template>
21 changes: 21 additions & 0 deletions examples/app-vitest-browser/app/pages/components.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
definePageMeta({
title: 'Components',
})
</script>

<template>
<section>
<ul>
<li
v-for="name of ['counter', 'hello']"
:key="name"
>
<NuxtLink :to="`/components/${name}`">
{{ `${name[0]?.toUpperCase()}${name.substring(1)}` }}
</NuxtLink>
</li>
</ul>
<NuxtPage />
</section>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script setup lang="ts">
definePageMeta({
title: 'Counter',
})
</script>

<template>
<MyCounter />
</template>
12 changes: 12 additions & 0 deletions examples/app-vitest-browser/app/pages/components/hello.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script setup lang="ts">
const route = useRoute()
const name = ref(route.query.name?.toString() || '')
definePageMeta({
title: 'Hello',
})
</script>

<template>
<MyHello v-model="name" />
</template>
15 changes: 15 additions & 0 deletions examples/app-vitest-browser/app/pages/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">
definePageMeta({
title: 'Index',
})
</script>

<template>
<ul>
<li>
<NuxtLink to="/components">
Components
</NuxtLink>
</li>
</ul>
</template>
7 changes: 7 additions & 0 deletions examples/app-vitest-browser/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,12 @@ export default defineNuxtConfig({
app: {
rootId: 'nuxt-test',
},
runtimeConfig: {
public: {
site: {
title: 'Vitest Browser Mode',
},
},
},
compatibilityDate: '2024-04-03',
})
7 changes: 7 additions & 0 deletions examples/app-vitest-browser/server/api/hello.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineEventHandler, getQuery } from 'h3'

export default defineEventHandler((event) => {
const query = getQuery<{ name?: string | string[] }>(event)
const name = Array.isArray(query.name) ? query.name[0] : query.name
return { message: `Hello ${name || 'Unknown'}!` }
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { page } from 'vitest/browser'
import { mockNuxtImport } from '@nuxt/test-utils/runtime'
import { MyCounter, MyHello } from '#components'

mockNuxtImport(useCounter, vi.fn)
mockNuxtImport(useHelloApi, vi.fn)

describe('Mock Nuxt Import', () => {
beforeEach(() => {
vi.resetAllMocks()
vi.restoreAllMocks()
})

it('useCounter', async () => {
const decrement = vi.fn()

vi.mocked(useCounter).mockImplementation(() => ({
count: ref(100),
increment: vi.fn(),
decrement,
}))

const screen = await page.render(MyCounter)
await expect.element(screen.getByText('Count: 100')).toBeInTheDocument()

const button = screen.getByRole('button', { name: 'Decrement' })
await expect.element(button).toBeInTheDocument()
await button.click()

expect(decrement).toHaveBeenCalledOnce()
})

it('useHelloApi', async () => {
vi.mocked(useHelloApi).mockImplementation(() => ({
data: ref({ message: '(Mocked)' }),
pending: ref(false),
}))

const screen = await page.render(MyHello)
const message = screen.getByRole('textbox', { name: 'Message' })
await expect.element(message).toBeInTheDocument()
await expect.element(message).toHaveValue('(Mocked)')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@ describe('Component (MyCounter)', () => {

it('can be interacted with (increment)', async () => {
const component = await mountSuspended(MyCounter)
const incrementButton = component.findAll('button').filter(btn => btn.text().includes('Increment'))[0]
const incrementButton = component.findAll('button').filter(btn => btn.text().includes('Increment'))[0]!
incrementButton.element.click()
await nextTick()
expect(component.text()).toContain('Count: 1')
})

it('can be interacted with (decrement)', async () => {
const component = await mountSuspended(MyCounter)
const decrementButton = component.findAll('button').filter(btn => btn.text().includes('Decrement'))[0]
const decrementButton = component.findAll('button').filter(btn => btn.text().includes('Decrement'))[0]!
decrementButton.element.click()
await nextTick()
expect(component.text()).toContain('Count: -1')
})

it('can use Nuxt-specific composables', async () => {
const component = await mountSuspended(MyCounter)
expect(component.text()).toContain('"buildAssetsDir": "/_nuxt/"')
expect(component.text()).toMatch(/"buildAssetsDir"\s*:\s*"\/_nuxt\/"/)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { describe, it, expect, afterAll } from 'vitest'
import { registerEndpoint } from '@nuxt/test-utils/runtime'
import { render } from '@nuxt/test-utils/vitest-browser-nuxt'

import { MyHello } from '#components'

const cleanups = [
registerEndpoint(
'/api/hello',
await import('#server/api/hello.get').then(r => r.default),
),
]

describe('Render Component (MyHello)', () => {
afterAll(() => cleanups.forEach(fn => fn()))

it('renders', async () => {
const screen = await render(MyHello)
const message = screen.getByLabelText('Message')
await expect.element(message).toHaveValue('Hello Unknown!')
})

it('can be interacted with input', async () => {
const screen = await render(MyHello)

const name = screen.getByLabelText('Name')
await name.fill('Nuxt')

const message = screen.getByLabelText('Message')
await expect.element(message).toHaveValue('Hello Nuxt!')
})

it('can be rerender', async () => {
const screen = await render(MyHello)

await screen.rerender({ modelValue: 'Nuxt App' })

const message = screen.getByLabelText('Message')
await expect.element(message).toHaveValue('Hello Nuxt App!')
})
})
73 changes: 73 additions & 0 deletions examples/app-vitest-browser/test/nuxt/components/render.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { describe, it, expect, vi } from 'vitest'
import { render } from '@nuxt/test-utils/vitest-browser-nuxt'
import { MyCounter } from '#components'

describe('Render Component', () => {
it('renders', async () => {
const { getByText } = await render(MyCounter)
expect(getByText('Count: 0')).toBeInTheDocument()
})

it('can be interacted with (increment)', async () => {
const { getByText } = await render(MyCounter)
const incrementButton = getByText('Increment')
await incrementButton.click()
expect(getByText('Count: 1')).toBeInTheDocument()
})

it('can be interacted with (decrement)', async () => {
const { getByText } = await render(MyCounter)
const decrementButton = getByText('Decrement')
await decrementButton.click()
expect(getByText('Count: -1')).toBeInTheDocument()
})

it('can use Nuxt-specific composables', async () => {
const { getByText } = await render(MyCounter)
const config = getByText('Runtime Config:')
expect(config).toBeInTheDocument()
expect(config).toHaveTextContent(/"buildAssetsDir"\s*:\s*"\/_nuxt\/"/)
})

it('locator', async () => {
const screen = await render(defineComponent({
render: () => h('h1', {}, 'Hello Nuxt!'),
}))

expect(screen.locator.getByRole('heading')).toHaveTextContent('Hello Nuxt!')
})

it('baseElement', async () => {
const screen = await render(defineComponent({
render: () => h('h1', {}, 'Hello Nuxt!'),
}))

await expect.element(screen.baseElement).toHaveAttribute('id', 'nuxt-test')
expect(screen.baseElement.children[0]?.outerHTML).toBe('<h1>Hello Nuxt!</h1>')
})

it('container', async () => {
const screen = await render(defineComponent({
render: () => h('h1', {}, 'Hello Nuxt!'),
}))

await expect.element(screen.container).toHaveAttribute('id', 'nuxt-test')
expect(screen.container.children[0]?.outerHTML).toBe('<h1>Hello Nuxt!</h1>')
})

it('umount', async () => {
const onBeforeUnmountFn = vi.fn()
const screen = await render(defineComponent({
setup() {
onBeforeUnmount(onBeforeUnmountFn)
},
render: () => h('h1', {}, 'Hello Nuxt!'),
}))

await screen.unmount()

await expect.element(screen.container).toHaveAttribute('id', 'nuxt-test')
expect(screen.container.children.length).toBe(0)
expect(onBeforeUnmountFn).toHaveBeenCalledOnce()
})
})
Loading
Loading