Repository: testing-library/svelte-testing-library (js)

Issue:
Feature proposal: Vite plugin
## Overview

This library requires three bits of setup to work correctly with Vitest

1. Browser bundle resolution
2. Testing library cleanup
3. `jest-dom` matchers (optional, but pretty much everyone wants it)

Setting these up is a bit fiddly and results in inbound issues in this repository

## Proposal

The most effective method at resolving (1) appears to be setting up a simple Vite plugin. A Vite plugin would also be capable of configuring (2) and maybe even (3). I think we should add a Vite plugin as an export to this library.

Usage (e.g. with SvelteKit)

```ts
// vite.config.ts
import { defineConfig } from 'vite'
import { svelteTesting } from '@testing-library/svelte/vite-plugin'
import { svelteKit } from '@sveltejs/kit/vite'

export default defineConfig({
  plugins: [svelteKit(), svelteTesting()],
})
```

```ts
// @testing-library/svelte/vite-plugin
export const svelteTesting = ({
  resolveBrowser = true,
  autoCleanup = true,
  extendExpect = true,
}) => ...
```

## Related issues

- #222 
- #358 

Pull request description:
feat(vitest): add Vite plugin for Svelte browser import and autocleanup
This PR adds a Vite plugin export to more reliably configure STL with Vitest:

- Ensure browser Svelte imported instead of node via `resolve.conditions`
- Add auto cleanup without global "afterEach" to `test.setupFiles` automatically

Closes #359 

Patch:
diff --git a/README.md b/README.md
index 0a39429..51ca77a 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@
 <p>Simple and complete Svelte testing utilities that encourage good testing practices.</p>
 
 [**Read The Docs**](https://testing-library.com/docs/svelte-testing-library/intro) |
-[Edit the docs](https://github.com/alexkrolick/testing-library-docs)
+[Edit the docs](https://github.com/testing-library/testing-library-docs)
 
 <!-- prettier-ignore-start -->
 [![Build Status][build-badge]][build]
@@ -80,19 +80,42 @@ This library has `peerDependencies` listings for `svelte >= 3`.
 You may also be interested in installing `@testing-library/jest-dom` so you can use
 [the custom jest matchers](https://github.com/testing-library/jest-dom).
 
+## Setup
+
+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.
+
+```diff
+  // vite.config.js
+  import { svelte } from '@sveltejs/vite-plugin-svelte'
++ import { svelteTesting } from '@testing-library/svelte/vite'
+
+  export default defineConfig({
+    plugins: [
+      svelte(),
++     svelteTesting(),
+    ]
+  });
+```
+
+See the [setup docs][] for more detailed setup instructions, including for other test runners like Jest.
+
+[vitest]: https://vitest.dev/
+[setup docs]: https://testing-library.com/docs/svelte-testing-library/setup
+
 ### Svelte 5 support
 
 If you are riding the bleeding edge of Svelte 5, you'll need to either
-import from `@testing-library/svelte/svelte5` instead of `@testing-library/svelte`, or have your `vite.config.js` contains the following alias:
+import from `@testing-library/svelte/svelte5` instead of `@testing-library/svelte`, or add an alias to your `vite.config.js`:
 
-```
-export default defineConfig(({ }) => ({
+```js
+export default defineConfig({
+  plugins: [svelte(), svelteTesting()],
   test: {
     alias: {
-      '@testing-library/svelte': '@testing-library/svelte/svelte5'
-    }
+      '@testing-library/svelte': '@testing-library/svelte/svelte5',
+    },
   },
-}))
+})
 ```
 
 ## Docs
diff --git a/package.json b/package.json
index 1948cfc..307e6ba 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,10 @@
     },
     "./vitest": {
       "default": "./src/vitest.js"
+    },
+    "./vite": {
+      "types": "./types/vite.d.ts",
+      "default": "./src/vite.js"
     }
   },
   "type": "module",
@@ -43,8 +47,10 @@
     "e2e"
   ],
   "files": [
-    "src/",
-    "types/index.d.ts"
+    "src",
+    "types",
+    "!*.test-d.ts",
+    "!__tests__"
   ],
   "scripts": {
     "toc": "doctoc README.md",
@@ -68,7 +74,17 @@
     "contributors:generate": "all-contributors generate"
   },
   "peerDependencies": {
-    "svelte": "^3 || ^4 || ^5"
+    "svelte": "^3 || ^4 || ^5",
+    "vite": "*",
+    "vitest": "*"
+  },
+  "peerDependenciesMeta": {
+    "vite": {
+      "optional": true
+    },
+    "vitest": {
+      "optional": true
+    }
   },
   "dependencies": {
     "@testing-library/dom": "^9.3.1"
diff --git a/src/vite.js b/src/vite.js
new file mode 100644
index 0000000..0062b89
--- /dev/null
+++ b/src/vite.js
@@ -0,0 +1,75 @@
+import { dirname, join } from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+/**
+ * Vite plugin to configure @testing-library/svelte.
+ *
+ * Ensures Svelte is imported correctly in tests
+ * and that the DOM is cleaned up after each test.
+ *
+ * @param {{resolveBrowser?: boolean, autoCleanup?: boolean}} options
+ * @returns {import('vite').Plugin}
+ */
+export const svelteTesting = ({
+  resolveBrowser = true,
+  autoCleanup = true,
+} = {}) => ({
+  name: 'vite-plugin-svelte-testing-library',
+  config: (config) => {
+    if (!process.env.VITEST) {
+      return
+    }
+
+    if (resolveBrowser) {
+      addBrowserCondition(config)
+    }
+
+    if (autoCleanup) {
+      addAutoCleanup(config)
+    }
+  },
+})
+
+/**
+ * Add `browser` to `resolve.conditions` before `node`.
+ *
+ * This ensures that Svelte's browser code is used in tests,
+ * rather than its SSR code.
+ *
+ * @param {import('vitest/config').UserConfig} config
+ */
+const addBrowserCondition = (config) => {
+  const resolve = config.resolve ?? {}
+  const conditions = resolve.conditions ?? []
+  const nodeConditionIndex = conditions.indexOf('node')
+  const browserConditionIndex = conditions.indexOf('browser')
+
+  if (
+    nodeConditionIndex >= 0 &&
+    (nodeConditionIndex < browserConditionIndex || browserConditionIndex < 0)
+  ) {
+    conditions.splice(nodeConditionIndex, 0, 'browser')
+  }
+
+  resolve.conditions = conditions
+  config.resolve = resolve
+}
+
+/**
+ * Add auto-cleanup file to Vitest's setup files.
+ *
+ * @param {import('vitest/config').UserConfig} config
+ */
+const addAutoCleanup = (config) => {
+  const test = config.test ?? {}
+  let setupFiles = test.setupFiles ?? []
+
+  if (typeof setupFiles === 'string') {
+    setupFiles = [setupFiles]
+  }
+
+  setupFiles.push(join(dirname(fileURLToPath(import.meta.url)), './vitest.js'))
+
+  test.setupFiles = setupFiles
+  config.test = test
+}
diff --git a/types/vite.d.ts b/types/vite.d.ts
new file mode 100644
index 0000000..470e487
--- /dev/null
+++ b/types/vite.d.ts
@@ -0,0 +1,12 @@
+import type { Plugin } from 'vite'
+
+/**
+ * Vite plugin to configure @testing-library/svelte.
+ *
+ * Ensures Svelte is imported correctly in tests
+ * and that the DOM is cleaned up after each test.
+ */
+export function svelteTesting(options?: {
+  resolveBrowser?: boolean
+  autoCleanup?: boolean
+}): Plugin
diff --git a/vite.config.js b/vite.config.js
index d9085ef..293d426 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -4,6 +4,8 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'
 import { VERSION as SVELTE_VERSION } from 'svelte/compiler'
 import { defineConfig } from 'vite'
 
+import { svelteTesting } from './src/vite.js'
+
 const IS_SVELTE_5 = SVELTE_VERSION >= '5'
 
 const alias = [
@@ -17,15 +19,8 @@ const alias = [
 ]
 
 // https://vitejs.dev/config/
-export default defineConfig(({ mode }) => ({
-  plugins: [svelte({ hot: false })],
-  resolve: {
-    // Ensure `browser` exports are used in tests
-    // Vitest prefers modules' `node` export by default
-    // Svelte's `node` export is its SSR bundle, which does not have onMount
-    // https://github.com/testing-library/svelte-testing-library/issues/222#issuecomment-1909993331
-    conditions: mode === 'test' ? ['browser'] : [],
-  },
+export default defineConfig({
+  plugins: [svelte({ hot: false }), svelteTesting()],
   test: {
     alias,
     environment: 'jsdom',
@@ -37,4 +32,4 @@ export default defineConfig(({ mode }) => ({
       include: ['src'],
     },
   },
-}))
+})


Test patch:
diff --git a/src/__tests__/_vitest-setup.js b/src/__tests__/_vitest-setup.js
index 13ed819..a9d0dd3 100644
--- a/src/__tests__/_vitest-setup.js
+++ b/src/__tests__/_vitest-setup.js
@@ -1,2 +1,1 @@
 import '@testing-library/jest-dom/vitest'
-import '../vitest'