Skip to content
Draft
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ storybook-static
*storybook.log
coverage
test-results/
__screenshots__
6 changes: 3 additions & 3 deletions apps/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
"dev": "vite --host --port 3000",
"build": "pnpm type-check && vite build",
"preview": "vite preview",
"test": "vitest run",
"test:unit": "vitest",
"test:unit-ui": "vitest --ui",
"test": "vitest",
"test:unit": "vitest --project unit",
"test:browser": "vitest --project browser",
"type-check": "vue-tsc --build tsconfig.app.json"
},
"dependencies": {
Expand Down
1 change: 1 addition & 0 deletions apps/playground/src/layout/v-header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

<v-button
class="toggle-theme"
data-testid="toggle-theme"
@click="theme.toggleTheme"
>
<moon-icon
Expand Down
25 changes: 25 additions & 0 deletions apps/playground/src/layout/v-layout.browser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { beforeAll, expect, it } from 'vitest'
import { defineComponent } from 'vue'
import { renderComponent } from '@/test-utils'

const TestTheme = defineComponent({
template: '<h1>Test theme</h1>',
})

beforeAll(() => {
localStorage.removeItem('theme')
})

it('toggle theme', async () => {
const screen = await renderComponent(TestTheme)
const button = screen.getByTestId('toggle-theme')

function getTheme() {
return document.body.classList.contains('dark') ? 'dark' : 'light'
}

expect(getTheme()).toBe('dark')
await expect.element(button).toBeVisible()
await button.click()
expect(getTheme()).toBe('light')
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { expect, it } from 'vitest'
import { renderComponent } from '@/test-utils'
import CounterButton from './counter-button.vue'

it('counter button increments the count', async () => {
const screen = await renderComponent(CounterButton)
const button = screen.getByTestId('counter')

await expect.element(button).toBeVisible()
await button.click()
await expect.element(button).toHaveTextContent('Count 1 * 2 = 2')

await button.tripleClick()
await expect.element(button).toHaveTextContent('Count 4 * 2 = 8')
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<template>
<v-button @click="counter.increment()">
<v-button
data-testid="counter"
@click="counter.increment()"
>
Count {{ counter.count }} * 2 = {{ counter.doubleCount }}
</v-button>
</template>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import { createPinia, setActivePinia } from 'pinia'
import { beforeEach, describe, expect, it } from 'vitest'
import { describe, expect, it } from 'vitest'
import { useCounterStore } from './use-counter'

describe('use-counter', () => {
beforeEach(() => {
// creates a fresh pinia and makes it active
// so it's automatically picked up by any useStore() call
// without having to pass it to it: `useStore(pinia)`
setActivePinia(createPinia())
})

it('increments', () => {
const counter = useCounterStore()
expect(counter.count).toBe(0)
Expand Down
37 changes: 37 additions & 0 deletions apps/playground/src/test-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createPinia } from 'pinia'
import { render as vitestRender } from 'vitest-browser-vue'
import { createMemoryHistory, createRouter } from 'vue-router'
import App from './app.vue'
import type { Component } from 'vue'

export async function renderComponent(
component: Component,
options: any = {},
) {
const router = createRouter({
history: createMemoryHistory(),
routes: [
{
path: '/',
component,
},
],
})

const pinia = createPinia()

const screen = vitestRender(App, {
...options,
global: {
plugins: [
router,
pinia,
],
...options.global,
},
})

await router.isReady()

return screen
}
83 changes: 83 additions & 0 deletions apps/playground/src/test/render-component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { VueQueryPlugin } from '@tanstack/vue-query'
import { createPinia } from 'pinia'
import { render as vitestRender } from 'vitest-browser-vue'
import { createMemoryHistory, createRouter } from 'vue-router'
import App from '@/app.vue'
import type { ComponentMountingOptions } from '@vue/test-utils'
import type {
Locator,
LocatorSelectors,
PrettyDOMOptions,
} from 'vitest/browser'
import type { DefineComponent } from 'vue'
import type { RouteRecordRaw } from 'vue-router'

type ComponentProps<T> = T extends new (...args: any) => {
$props: infer P
// eslint-disable-next-line ts/no-empty-object-type
} ? NonNullable<P> : T extends (props: infer P, ...args: any) => any ? P : {}

interface RenderResult<Props> extends LocatorSelectors {
container: HTMLElement
baseElement: HTMLElement
locator: Locator
debug(el?: HTMLElement | HTMLElement[] | Locator | Locator[], maxLength?: number, options?: PrettyDOMOptions): void
unmount(): void
emitted<T = unknown>(): Record<string, T[]>
emitted<T = unknown[]>(eventName: string): undefined | T[]
rerender(props: Partial<Props>): void
}

interface ComponentRenderOptions<C, P extends ComponentProps<C>> extends ComponentMountingOptions<C, P> {
container?: HTMLElement
baseElement?: HTMLElement
routes?: RouteRecordRaw[]
}

export async function renderComponent<T, C = T extends ((...args: any) => any) | (new (...args: any) => any) ? T : T extends {
props?: infer Props
} ? DefineComponent<Props extends Readonly<(infer PropNames)[]> | (infer PropNames)[] ? {
[key in PropNames extends string ? PropNames : string]?: any;
} : Props> : DefineComponent, P extends ComponentProps<C> = ComponentProps<C>>(
component: T,
options: ComponentRenderOptions<C, P> = {},
): Promise<RenderResult<P>> {
const pinia = createPinia()

const router = createRouter({
history: createMemoryHistory(),
routes: [
{
path: '/',
// @ts-ignore
component,
},
...options.routes ?? [],
],
})

const screen = vitestRender(App, {
...options,
global: {
plugins: [
pinia,
router,
[VueQueryPlugin, {
queryClientConfig: {
defaultOptions: {
queries: {
refetchOnMount: true,
refetchOnWindowFocus: false,
},
},
},
}],
],
...options?.global,
},
})

await router.isReady()

return screen
};
14 changes: 14 additions & 0 deletions apps/playground/src/test/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { enableAutoUnmount } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import { afterEach, beforeEach } from 'vitest'

// import '../main.scss'

beforeEach(() => {
// creates a fresh pinia and makes it active
// so it's automatically picked up by any useStore() call
// without having to pass it to it: `useStore(pinia)`
setActivePinia(createPinia())
})

enableAutoUnmount(afterEach)
46 changes: 37 additions & 9 deletions apps/playground/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,40 @@
import { playwright } from '@vitest/browser-playwright'
import { defineConfig, mergeConfig } from 'vitest/config'
import viteConfig from './vite.config'

export default mergeConfig(
viteConfig,
defineConfig({
test: {
environment: 'happy-dom',
setupFiles: './vitest.setup.ts',
},
}),
)
export default mergeConfig(viteConfig, defineConfig({
test: {
projects: [
{
extends: true,
test: {
name: 'unit',
environment: 'happy-dom',
setupFiles: './src/test/setup.ts',
include: ['./src/**/*.test.ts'],
exclude: ['./src/**/*.browser.test.ts'],
},
},
{
extends: true,
test: {
name: 'browser',
setupFiles: './src/test/setup.ts',
include: ['./src/**/*.browser.test.ts'],
browser: {
enabled: true,
headless: true,
provider: playwright(),
instances: [
{ browser: 'chromium' },
],
viewport: {
width: 1280,
height: 720,
},
},
},
},
],
},
}))
1 change: 0 additions & 1 deletion apps/playground/vitest.setup.ts

This file was deleted.

2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export default antfu({
'no-alert': 'off',
'no-console': 'off',
'no-cond-assign': 'off',
'no-unused-vars': 'off',
'no-template-curly-in-string': 'off',
'ts/no-redeclare': 'off',
'ts/ban-ts-comment': 'off',
Expand All @@ -111,7 +112,6 @@ export default antfu({
'regexp/no-super-linear-backtracking': 'off',

// warns
'no-unused-vars': 'warn',
'unused-imports/no-unused-imports': 'warn',

// errors
Expand Down
13 changes: 6 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
"build:ui": "turbo run build --filter=./packages/ui",
"build:packages": "turbo run build --filter=./packages/*",
"type-check": "turbo run type-check",
"test": "vitest run",
"test:unit": "vitest",
"test:unit-ui": "vitest --ui",
"lint": "eslint . --cache",
"lint:fix": "eslint . --fix --cache",
"generate-api": "pnpm --filter=@vue-workspace/api generate-api",
Expand All @@ -32,15 +29,16 @@
"@types/jscodeshift": "17.3.0",
"@types/node": "24.10.9",
"@vitejs/plugin-vue": "6.0.2",
"@vitest/ui": "4.0.14",
"@vitest/browser-playwright": "4.0.18",
"@vitest/ui": "4.0.18",
"@vue/test-utils": "2.4.6",
"@vue/tsconfig": "0.8.1",
"eslint": "9.39.2",
"eslint-plugin-format": "1.3.1",
"eslint-plugin-pinia": "0.4.2",
"happy-dom": "20.0.11",
"happy-dom": "20.3.9",
"jscodeshift": "17.3.0",
"playwright": "1.57.0",
"playwright": "1.58.0",
"prettier-plugin-css-order": "2.2.0",
"sass-embedded": "1.93.3",
"tsx": "4.20.6",
Expand All @@ -51,7 +49,8 @@
"vite": "7.2.4",
"vite-plugin-static-copy": "3.1.4",
"vite-plugin-vue-devtools": "8.0.5",
"vitest": "4.0.14",
"vitest": "4.0.18",
"vitest-browser-vue": "2.0.2",
"vue-tsc": "3.1.5"
}
}
Loading