Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add preact example, remove optimizer experimental status, enable by default #3854

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 7 additions & 7 deletions docs/config/index.md
Expand Up @@ -177,17 +177,17 @@ Directory to save cache files.

### deps

- **Type:** `{ experimentalOptimizer?, registerNodeLoader?, ... }`
- **Type:** `{ optimizer?, registerNodeLoader?, ... }`

Handling for dependencies resolution.

#### deps.experimentalOptimizer
#### deps.optimizer

- **Type:** `{ ssr?, web? }`
- **Version:** Since Vitest 0.29.0
- **Version:** Since Vitest 0.34.0
- **See also:** [Dep Optimization Options](https://vitejs.dev/config/dep-optimization-options.html)

Enable dependency optimization. If you have a lot of tests, this might improve their performance.
Enable dependency optimization. If you have a lot of tests, this might improve their performance. Before Vitest 0.34.0, it was named as `deps.experimentalOptimizer`.

When Vitest encounters the external library listed in `include`, it will be bundled into a single file using esbuild and imported as a whole module. This is good for several reasons:

Expand All @@ -196,12 +196,12 @@ When Vitest encounters the external library listed in `include`, it will be bund
- Your `alias` configuration is now respected inside bundled packages
- Code in your tests is running closer to how it's running in the browser

Be aware that only packages in `deps.experimentalOptimizer?.[mode].include` option are bundled (some plugins populate this automatically, like Svelte). You can read more about available options in [Vite](https://vitejs.dev/config/dep-optimization-options.html) docs. By default, Vitest uses `experimentalOptimizer.web` for `jsdom` and `happy-dom` environments, and `experimentalOptimizer.ssr` for `node` and `edge` environments, but it is configurable by [`transformMode`](#transformmode).
Be aware that only packages in `deps.optimizer?.[mode].include` option are bundled (some plugins populate this automatically, like Svelte). You can read more about available options in [Vite](https://vitejs.dev/config/dep-optimization-options.html) docs. By default, Vitest uses `optimizer.web` for `jsdom` and `happy-dom` environments, and `optimizer.ssr` for `node` and `edge` environments, but it is configurable by [`transformMode`](#transformmode).

This options also inherits your `optimizeDeps` configuration (for web Vitest will extend `optimizeDeps`, for ssr - `ssr.optimizeDeps`). If you redefine `include`/`exclude` option in `deps.experimentalOptimizer` it will extend your `optimizeDeps` when running tests. Vitest automatically removes the same options from `include`, if they are listed in `exclude`.
This options also inherits your `optimizeDeps` configuration (for web Vitest will extend `optimizeDeps`, for ssr - `ssr.optimizeDeps`). If you redefine `include`/`exclude` option in `deps.optimizer` it will extend your `optimizeDeps` when running tests. Vitest automatically removes the same options from `include`, if they are listed in `exclude`.

::: tip
You will not be able to edit your `node_modules` code for debugging, since the code is actually located in your `cacheDir` or `test.cache.dir` directory. If you want to debug with `console.log` statements, edit it directly or force rebundling with `deps.experimentalOptimizer?.[mode].force` option.
You will not be able to edit your `node_modules` code for debugging, since the code is actually located in your `cacheDir` or `test.cache.dir` directory. If you want to debug with `console.log` statements, edit it directly or force rebundling with `deps.optimizer?.[mode].force` option.
:::

#### deps.registerNodeLoader<NonProjectOption />
Expand Down
12 changes: 12 additions & 0 deletions examples/preact-testing-lib/index.html
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Preact Example</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="src/main.tsx"></script>
</body>
</html>
28 changes: 28 additions & 0 deletions examples/preact-testing-lib/package.json
@@ -0,0 +1,28 @@
{
"name": "@vitest/example-preact-testing-lib",
"private": true,
"scripts": {
"build": "tsc && vite build",
"coverage": "vitest run --coverage",
"dev": "vite",
"preview": "vite preview",
"test": "vitest",
"test:ui": "vitest --ui"
},
"dependencies": {
"preact": "^10.15.1",
"react": "npm:@preact/compat",
"react-dom": "npm:@preact/compat",
"react-router-dom": "^6.3.0"
},
"devDependencies": {
"@preact/preset-vite": "^2.5.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/preact": "^3.2.3",
"@vitest/ui": "latest",
"less": "^4.1.3",
"typescript": "^4.8.4",
"vite": "latest",
"vitest": "latest"
}
}
23 changes: 23 additions & 0 deletions examples/preact-testing-lib/src/App.less
@@ -0,0 +1,23 @@
.app {
text-align: center;

header {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
background-color: #282c34;
font-size: calc(10px + 2vmin);
color: white;

.app-link {
color: white;
text-decoration: none;

&:hover {
color: #747bff;
}
}
}
}
18 changes: 18 additions & 0 deletions examples/preact-testing-lib/src/App.test.tsx
@@ -0,0 +1,18 @@
import { describe, expect, it } from 'vitest'
import { fireEvent, render, screen } from '@testing-library/preact'
import { BrowserRouter } from 'react-router-dom'

import App from './App'

describe('Preact Demo Test Suite', () => {
it('basic', () => {
render(<BrowserRouter><App /></BrowserRouter>)
expect(screen.getByText(/Hello Vite & Preact!/i)).toBeInTheDocument()
})

it('click event', async () => {
render(<BrowserRouter><App /></BrowserRouter>)
fireEvent.click(screen.getByRole('button'))
expect(await screen.findByText(/count is: 1/i)).toBeInTheDocument()
})
})
36 changes: 36 additions & 0 deletions examples/preact-testing-lib/src/App.tsx
@@ -0,0 +1,36 @@
import { useCount } from './hooks/useCount'
import './App.less'

export default function App() {
const { count, inc } = useCount()

return (
<div class="app">
<header>
<h1>Hello Vite & Preact!</h1>
<p>
<button onClick={inc}>Count is: {count}</button>
</p>
<p>
<a
className="app-link"
href="https://preactjs.com/"
target="_blank"
rel="noopener noreferrer"
>
Learn Preact
</a>
{' | '}
<a
className="app-link"
href="https://vitejs.dev/guide/features.html"
target="_blank"
rel="noopener noreferrer"
>
Vite Docs
</a>
</p>
</header>
</div>
)
}
12 changes: 12 additions & 0 deletions examples/preact-testing-lib/src/hooks/useCount.test.ts
@@ -0,0 +1,12 @@
import { act, renderHook } from '@testing-library/preact'
import { useCount } from './useCount'

describe('useCount hook', () => {
it('should increment', () => {
const { result } = renderHook(() => useCount())
act(() => {
result.current.inc()
})
expect(result.current.count).toBe(1)
})
})
10 changes: 10 additions & 0 deletions examples/preact-testing-lib/src/hooks/useCount.ts
@@ -0,0 +1,10 @@
import { useCallback, useState } from 'preact/hooks'

export function useCount() {
const [count, setCount] = useState(0)
const inc = useCallback(() => setCount(x => x + 1), [])
return {
count,
inc,
}
}
13 changes: 13 additions & 0 deletions examples/preact-testing-lib/src/main.less
@@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
5 changes: 5 additions & 0 deletions examples/preact-testing-lib/src/main.tsx
@@ -0,0 +1,5 @@
import { render } from 'preact'
import App from './App'
import './main.less'

render(<App />, document.getElementById('root'))
5 changes: 5 additions & 0 deletions examples/preact-testing-lib/test/setup.ts
@@ -0,0 +1,5 @@
import { afterEach } from 'vitest'
import { cleanup } from '@testing-library/preact'
import '@testing-library/jest-dom'

afterEach(() => cleanup())
24 changes: 24 additions & 0 deletions examples/preact-testing-lib/tsconfig.json
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment",
"types": ["vitest/globals"]
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
9 changes: 9 additions & 0 deletions examples/preact-testing-lib/tsconfig.node.json
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
20 changes: 20 additions & 0 deletions examples/preact-testing-lib/vite.config.ts
@@ -0,0 +1,20 @@
/// <reference types="vitest" />
/// <reference types="vite/client" />

import { defineConfig } from 'vite'
import preact from '@preact/preset-vite'

export default defineConfig({
plugins: [preact()],
resolve: {
// react-router-dom specifies "module" field in package.json for ESM entry
// if it's not mapped, it uses the "main" field which is CommonJS that redirects to CJS preact
mainFields: ['module'],
},
test: {
globals: true,
environment: 'jsdom',
setupFiles: './test/setup.ts',
css: true,
},
})
2 changes: 1 addition & 1 deletion packages/vitest/src/node/create.ts
Expand Up @@ -28,7 +28,7 @@ export async function createVitest(mode: VitestRunMode, options: UserConfig, vit
const server = await createServer(mergeConfig(config, mergeConfig(viteOverrides, { root: options.root })))

// optimizer needs .listen() to be called
if (ctx.config.api?.port || ctx.config.deps?.experimentalOptimizer?.web?.enabled || ctx.config.deps?.experimentalOptimizer?.ssr?.enabled)
if (ctx.config.api?.port || ctx.config.deps?.optimizer?.web?.enabled || ctx.config.deps?.optimizer?.ssr?.enabled)
await server.listen()
else
await server.pluginContainer.buildStart({})
Expand Down
4 changes: 2 additions & 2 deletions packages/vitest/src/node/plugins/index.ts
Expand Up @@ -109,8 +109,8 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t
}
}

const webOptimizer = resolveOptimizerConfig(testConfig.deps?.experimentalOptimizer?.web, viteConfig.optimizeDeps, testConfig)
const ssrOptimizer = resolveOptimizerConfig(testConfig.deps?.experimentalOptimizer?.ssr, viteConfig.ssr?.optimizeDeps, testConfig)
const webOptimizer = resolveOptimizerConfig(testConfig.deps?.optimizer?.web, viteConfig.optimizeDeps, testConfig)
const ssrOptimizer = resolveOptimizerConfig(testConfig.deps?.optimizer?.ssr, viteConfig.ssr?.optimizeDeps, testConfig)

config.cacheDir = webOptimizer.cacheDir || ssrOptimizer.cacheDir || config.cacheDir
config.optimizeDeps = webOptimizer.optimizeDeps
Expand Down
16 changes: 10 additions & 6 deletions packages/vitest/src/node/plugins/utils.ts
Expand Up @@ -3,13 +3,17 @@ import { version as viteVersion } from 'vite'
import type { DepOptimizationOptions, UserConfig as ViteConfig } from 'vite'
import type { DepsOptimizationOptions, InlineConfig } from '../../types'

export function resolveOptimizerConfig(testOptionc: DepsOptimizationOptions | undefined, viteOptions: DepOptimizationOptions | undefined, testConfig: InlineConfig) {
export function resolveOptimizerConfig(_testOptions: DepsOptimizationOptions | undefined, viteOptions: DepOptimizationOptions | undefined, testConfig: InlineConfig) {
const testOptions = _testOptions || {}
const newConfig: { cacheDir?: string; optimizeDeps: DepOptimizationOptions } = {} as any
const [major, minor] = viteVersion.split('.').map(Number)
const allowed = major >= 5 || (major === 4 && minor >= 3)
if (!allowed && testOptionc?.enabled === true)
if (!allowed && testOptions?.enabled === true)
console.warn(`Vitest: "deps.optimizer" is only available in Vite >= 4.3.0, current Vite version: ${viteVersion}`)
if (!allowed || testOptionc?.enabled !== true) {
else
// enable by default
testOptions.enabled ??= true
if (!allowed || testOptions?.enabled !== true) {
newConfig.cacheDir = undefined
newConfig.optimizeDeps = {
// experimental in Vite >2.9.2, entries remains to help with older versions
Expand All @@ -22,12 +26,12 @@ export function resolveOptimizerConfig(testOptionc: DepsOptimizationOptions | un
newConfig.cacheDir = cacheDir ?? 'node_modules/.vitest'
newConfig.optimizeDeps = {
...viteOptions,
...testOptionc,
...testOptions,
noDiscovery: true,
disabled: false,
entries: [],
exclude: ['vitest', ...builtinModules, ...(testOptionc.exclude || viteOptions?.exclude || [])],
include: (testOptionc.include || viteOptions?.include || []).filter((n: string) => n !== 'vitest'),
exclude: ['vitest', ...builtinModules, ...(testOptions.exclude || viteOptions?.exclude || [])],
include: (testOptions.include || viteOptions?.include || []).filter((n: string) => n !== 'vitest'),
}
}
return newConfig
Expand Down
4 changes: 2 additions & 2 deletions packages/vitest/src/node/plugins/workspace.ts
Expand Up @@ -89,8 +89,8 @@ export function WorkspaceVitestPlugin(project: WorkspaceProject, options: Worksp
}
}

const webOptimizer = resolveOptimizerConfig(testConfig.deps?.experimentalOptimizer?.web, viteConfig.optimizeDeps, testConfig)
const ssrOptimizer = resolveOptimizerConfig(testConfig.deps?.experimentalOptimizer?.ssr, viteConfig.ssr?.optimizeDeps, testConfig)
const webOptimizer = resolveOptimizerConfig(testConfig.deps?.optimizer?.web, viteConfig.optimizeDeps, testConfig)
const ssrOptimizer = resolveOptimizerConfig(testConfig.deps?.optimizer?.ssr, viteConfig.ssr?.optimizeDeps, testConfig)

config.cacheDir = webOptimizer.cacheDir || ssrOptimizer.cacheDir || config.cacheDir
config.optimizeDeps = webOptimizer.optimizeDeps
Expand Down
6 changes: 3 additions & 3 deletions packages/vitest/src/node/workspace.ts
Expand Up @@ -52,7 +52,7 @@ export async function initializeProject(workspacePath: string | number, ctx: Vit
const server = await createServer(config)

// optimizer needs .listen() to be called
if (ctx.config.api?.port || project.config.deps?.experimentalOptimizer?.web?.enabled || project.config.deps?.experimentalOptimizer?.ssr?.enabled)
if (ctx.config.api?.port || project.config.deps?.optimizer?.web?.enabled || project.config.deps?.optimizer?.ssr?.enabled)
await server.listen()
else
await server.pluginContainer.buildStart({})
Expand Down Expand Up @@ -293,10 +293,10 @@ export class WorkspaceProject {
...this.config.deps,
optimizer: {
web: {
enabled: this.config.deps?.experimentalOptimizer?.web?.enabled ?? false,
enabled: this.config.deps?.optimizer?.web?.enabled ?? false,
},
ssr: {
enabled: this.config.deps?.experimentalOptimizer?.ssr?.enabled ?? false,
enabled: this.config.deps?.optimizer?.ssr?.enabled ?? false,
},
},
},
Expand Down
4 changes: 2 additions & 2 deletions packages/vitest/src/types/config.ts
Expand Up @@ -77,7 +77,7 @@ interface SequenceOptions {
}

export type DepsOptimizationOptions = Omit<DepOptimizationConfig, 'disabled' | 'noDiscovery'> & {
enabled: boolean
enabled?: boolean
}

export interface TransformModePatterns {
Expand All @@ -102,7 +102,7 @@ interface DepsOptions {
/**
* Enable dependency optimization. This can improve the performance of your tests.
*/
experimentalOptimizer?: {
optimizer?: {
web?: DepsOptimizationOptions
ssr?: DepsOptimizationOptions
}
Expand Down