Skip to content

Commit

Permalink
feat: add preact example, remove optimizer experimental status, enabl…
Browse files Browse the repository at this point in the history
…e by default (#3854)

Co-authored-by: eryue0220 <eryue0220@gmail.com>
  • Loading branch information
sheremet-va and eryue0220 committed Aug 1, 2023
1 parent b092985 commit 4b946ca
Show file tree
Hide file tree
Showing 21 changed files with 717 additions and 133 deletions.
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

0 comments on commit 4b946ca

Please sign in to comment.