Skip to content

Commit ed541de

Browse files
authored
feat(vitest): add Vite plugin for Svelte browser import and autocleanup (#362)
Closes #359
1 parent cb9fc3a commit ed541de

File tree

6 files changed

+141
-21
lines changed

6 files changed

+141
-21
lines changed

README.md

+30-7
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<p>Simple and complete Svelte testing utilities that encourage good testing practices.</p>
1414

1515
[**Read The Docs**](https://testing-library.com/docs/svelte-testing-library/intro) |
16-
[Edit the docs](https://github.com/alexkrolick/testing-library-docs)
16+
[Edit the docs](https://github.com/testing-library/testing-library-docs)
1717

1818
<!-- prettier-ignore-start -->
1919
[![Build Status][build-badge]][build]
@@ -80,19 +80,42 @@ This library has `peerDependencies` listings for `svelte >= 3`.
8080
You may also be interested in installing `@testing-library/jest-dom` so you can use
8181
[the custom jest matchers](https://github.com/testing-library/jest-dom).
8282

83+
## Setup
84+
85+
We recommend using `@testing-library/svelte` with [Vitest][] as your test runner. To get started, add the `svelteTesting` plugin to your Vite or Vitest config.
86+
87+
```diff
88+
// vite.config.js
89+
import { svelte } from '@sveltejs/vite-plugin-svelte'
90+
+ import { svelteTesting } from '@testing-library/svelte/vite'
91+
92+
export default defineConfig({
93+
plugins: [
94+
svelte(),
95+
+ svelteTesting(),
96+
]
97+
});
98+
```
99+
100+
See the [setup docs][] for more detailed setup instructions, including for other test runners like Jest.
101+
102+
[vitest]: https://vitest.dev/
103+
[setup docs]: https://testing-library.com/docs/svelte-testing-library/setup
104+
83105
### Svelte 5 support
84106

85107
If you are riding the bleeding edge of Svelte 5, you'll need to either
86-
import from `@testing-library/svelte/svelte5` instead of `@testing-library/svelte`, or have your `vite.config.js` contains the following alias:
108+
import from `@testing-library/svelte/svelte5` instead of `@testing-library/svelte`, or add an alias to your `vite.config.js`:
87109

88-
```
89-
export default defineConfig(({ }) => ({
110+
```js
111+
export default defineConfig({
112+
plugins: [svelte(), svelteTesting()],
90113
test: {
91114
alias: {
92-
'@testing-library/svelte': '@testing-library/svelte/svelte5'
93-
}
115+
'@testing-library/svelte': '@testing-library/svelte/svelte5',
116+
},
94117
},
95-
}))
118+
})
96119
```
97120

98121
## Docs

package.json

+19-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
},
1515
"./vitest": {
1616
"default": "./src/vitest.js"
17+
},
18+
"./vite": {
19+
"types": "./types/vite.d.ts",
20+
"default": "./src/vite.js"
1721
}
1822
},
1923
"type": "module",
@@ -43,8 +47,10 @@
4347
"e2e"
4448
],
4549
"files": [
46-
"src/",
47-
"types/index.d.ts"
50+
"src",
51+
"types",
52+
"!*.test-d.ts",
53+
"!__tests__"
4854
],
4955
"scripts": {
5056
"toc": "doctoc README.md",
@@ -68,7 +74,17 @@
6874
"contributors:generate": "all-contributors generate"
6975
},
7076
"peerDependencies": {
71-
"svelte": "^3 || ^4 || ^5"
77+
"svelte": "^3 || ^4 || ^5",
78+
"vite": "*",
79+
"vitest": "*"
80+
},
81+
"peerDependenciesMeta": {
82+
"vite": {
83+
"optional": true
84+
},
85+
"vitest": {
86+
"optional": true
87+
}
7288
},
7389
"dependencies": {
7490
"@testing-library/dom": "^9.3.1"

src/__tests__/_vitest-setup.js

-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
import '@testing-library/jest-dom/vitest'
2-
import '../vitest'

src/vite.js

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { dirname, join } from 'node:path'
2+
import { fileURLToPath } from 'node:url'
3+
4+
/**
5+
* Vite plugin to configure @testing-library/svelte.
6+
*
7+
* Ensures Svelte is imported correctly in tests
8+
* and that the DOM is cleaned up after each test.
9+
*
10+
* @param {{resolveBrowser?: boolean, autoCleanup?: boolean}} options
11+
* @returns {import('vite').Plugin}
12+
*/
13+
export const svelteTesting = ({
14+
resolveBrowser = true,
15+
autoCleanup = true,
16+
} = {}) => ({
17+
name: 'vite-plugin-svelte-testing-library',
18+
config: (config) => {
19+
if (!process.env.VITEST) {
20+
return
21+
}
22+
23+
if (resolveBrowser) {
24+
addBrowserCondition(config)
25+
}
26+
27+
if (autoCleanup) {
28+
addAutoCleanup(config)
29+
}
30+
},
31+
})
32+
33+
/**
34+
* Add `browser` to `resolve.conditions` before `node`.
35+
*
36+
* This ensures that Svelte's browser code is used in tests,
37+
* rather than its SSR code.
38+
*
39+
* @param {import('vitest/config').UserConfig} config
40+
*/
41+
const addBrowserCondition = (config) => {
42+
const resolve = config.resolve ?? {}
43+
const conditions = resolve.conditions ?? []
44+
const nodeConditionIndex = conditions.indexOf('node')
45+
const browserConditionIndex = conditions.indexOf('browser')
46+
47+
if (
48+
nodeConditionIndex >= 0 &&
49+
(nodeConditionIndex < browserConditionIndex || browserConditionIndex < 0)
50+
) {
51+
conditions.splice(nodeConditionIndex, 0, 'browser')
52+
}
53+
54+
resolve.conditions = conditions
55+
config.resolve = resolve
56+
}
57+
58+
/**
59+
* Add auto-cleanup file to Vitest's setup files.
60+
*
61+
* @param {import('vitest/config').UserConfig} config
62+
*/
63+
const addAutoCleanup = (config) => {
64+
const test = config.test ?? {}
65+
let setupFiles = test.setupFiles ?? []
66+
67+
if (typeof setupFiles === 'string') {
68+
setupFiles = [setupFiles]
69+
}
70+
71+
setupFiles.push(join(dirname(fileURLToPath(import.meta.url)), './vitest.js'))
72+
73+
test.setupFiles = setupFiles
74+
config.test = test
75+
}

types/vite.d.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { Plugin } from 'vite'
2+
3+
/**
4+
* Vite plugin to configure @testing-library/svelte.
5+
*
6+
* Ensures Svelte is imported correctly in tests
7+
* and that the DOM is cleaned up after each test.
8+
*/
9+
export function svelteTesting(options?: {
10+
resolveBrowser?: boolean
11+
autoCleanup?: boolean
12+
}): Plugin

vite.config.js

+5-10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'
44
import { VERSION as SVELTE_VERSION } from 'svelte/compiler'
55
import { defineConfig } from 'vite'
66

7+
import { svelteTesting } from './src/vite.js'
8+
79
const IS_SVELTE_5 = SVELTE_VERSION >= '5'
810

911
const alias = [
@@ -17,15 +19,8 @@ const alias = [
1719
]
1820

1921
// https://vitejs.dev/config/
20-
export default defineConfig(({ mode }) => ({
21-
plugins: [svelte({ hot: false })],
22-
resolve: {
23-
// Ensure `browser` exports are used in tests
24-
// Vitest prefers modules' `node` export by default
25-
// Svelte's `node` export is its SSR bundle, which does not have onMount
26-
// https://github.com/testing-library/svelte-testing-library/issues/222#issuecomment-1909993331
27-
conditions: mode === 'test' ? ['browser'] : [],
28-
},
22+
export default defineConfig({
23+
plugins: [svelte({ hot: false }), svelteTesting()],
2924
test: {
3025
alias,
3126
environment: 'jsdom',
@@ -37,4 +32,4 @@ export default defineConfig(({ mode }) => ({
3732
include: ['src'],
3833
},
3934
},
40-
}))
35+
})

0 commit comments

Comments
 (0)