Skip to content
This repository has been archived by the owner on Dec 19, 2023. It is now read-only.

Commit

Permalink
feat!: option to replace happy-dom with jsdom (#121)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: you now need to explicitly add `happy-dom` or `jsdom` as a dev dependency based on which `domEnvironment` you want to be in. (`happy-dom` is set by default.)
  • Loading branch information
enkot committed Jul 5, 2023
1 parent c1bdb1f commit f14361f
Show file tree
Hide file tree
Showing 10 changed files with 359 additions and 103 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
1. First install `nuxt-vitest`:

```bash
pnpm add -D nuxt-vitest vitest
pnpm add -D nuxt-vitest vitest happy-dom

# or
yarn add --dev nuxt-vitest vitest
npm i -D nuxt-vitest vitest
yarn add --dev nuxt-vitest vitest happy-dom
npm i -D nuxt-vitest vitest happy-dom
```

2. Add `nuxt-vitest` to your `nuxt.config.js`:
Expand Down
8 changes: 8 additions & 0 deletions packages/nuxt-vitest/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ declare module 'vitest' {
* @default {nuxt-test}
*/
rootId?: string
/**
* The name of the DOM environment to use.
*
* It also needs to be installed as a dev dependency in your project.
*
* @default {happy-dom}
*/
domEnvironment?: 'happy-dom' | 'jsdom'
}
}
}
14 changes: 13 additions & 1 deletion packages/vitest-environment-nuxt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,29 @@
"defu": "^6.1.2",
"estree-walker": "^3.0.3",
"h3": "^1.6.5",
"happy-dom": "^9.10.9",
"magic-string": "^0.30.0",
"ofetch": "^1.0.0",
"unenv": "^1.0.2"
},
"devDependencies": {
"@types/jsdom": "^21.1.1",
"happy-dom": "^9.10.9",
"jsdom": "^22.0.0",
"vue": "3.3.4"
},
"peerDependencies": {
"happy-dom": "^9.10.9",
"jsdom": "^22.0.0",
"vitest": "^0.24.5 || ^0.26.0 || ^0.27.0 || ^0.28.0 || ^0.29.0 || ^0.30.0",
"vue": "^3.2.45",
"vue-router": "^4.0.0"
},
"peerDependenciesMeta": {
"happy-dom": {
"optional": true
},
"jsdom": {
"optional": true
}
}
}
14 changes: 14 additions & 0 deletions packages/vitest-environment-nuxt/src/env/happy-dom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { importModule } from 'local-pkg'
import { EnvironmentNuxt } from '../types'

export default <EnvironmentNuxt> async function (_, { happyDom = {} }) {
const { Window, GlobalWindow } = await importModule('happy-dom') as typeof import('happy-dom')
const window = new (GlobalWindow || Window)(happyDom) as any

return {
window,
teardown() {
window.happyDOM.cancelAsync()
}
}
}
44 changes: 44 additions & 0 deletions packages/vitest-environment-nuxt/src/env/jsdom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { importModule } from 'local-pkg'
import { EnvironmentNuxt } from '../types'

export default <EnvironmentNuxt> async function (global, { jsdom = {} }) {
const {
CookieJar,
JSDOM,
ResourceLoader,
VirtualConsole,
} = await importModule('jsdom') as typeof import('jsdom')
const {
html = '<!DOCTYPE html>',
userAgent,
url = 'http://localhost:3000',
contentType = 'text/html',
pretendToBeVisual = true,
includeNodeLocations = false,
runScripts = 'dangerously',
resources,
console = false,
cookieJar = false,
...restOptions
} = jsdom as any
const window = new JSDOM(
html,
{
pretendToBeVisual,
resources: resources ?? (userAgent ? new ResourceLoader({ userAgent }) : undefined),
runScripts,
url,
virtualConsole: console && global.console ? new VirtualConsole().sendTo(global.console) : undefined,
cookieJar: cookieJar ? new CookieJar() : undefined,
includeNodeLocations,
contentType,
userAgent,
...restOptions,
},
).window as any

return {
window,
teardown() {}
}
}
40 changes: 21 additions & 19 deletions packages/vitest-environment-nuxt/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
import type { Environment } from 'vitest'
import { Window, GlobalWindow } from 'happy-dom'
import { createFetch } from 'ofetch'
import { joinURL } from 'ufo'
import { createApp, toNodeListener } from 'h3'
import type { App } from 'h3'
import { populateGlobal } from 'vitest/environments'
import {
createCall,
createFetch as createLocalFetch,
} from 'unenv/runtime/fetch/index'
import type { NuxtBuiltinEnvironment } from './types'
import happyDom from './env/happy-dom'
import jsdom from './env/jsdom'

export default <Environment>{
name: 'nuxt',
async setup(_, environmentOptions) {
const startingURL = environmentOptions.url || joinURL('http://localhost:3000', environmentOptions?.nuxtRuntimeConfig.app?.baseURL || '/')

const win: NuxtWindow = new (GlobalWindow || Window)({ url: startingURL }) as any
async setup(global, environmentOptions) {
const url = joinURL('http://localhost:3000', environmentOptions?.nuxtRuntimeConfig.app?.baseURL || '/')
const { window: win, teardown } = await {
'happy-dom': happyDom,
jsdom
}[environmentOptions.nuxt.domEnvironment as NuxtBuiltinEnvironment || 'happy-dom'](global, {
...environmentOptions,
happyDom: {
url,
...environmentOptions?.happyDom,
},
jsdom: {
url,
...environmentOptions?.jsdom,
}
})

win.__NUXT__ = {
serverRendered: false,
Expand Down Expand Up @@ -56,7 +69,7 @@ export default <Environment>{
return localFetch(init, options)
}

win.$fetch = createFetch({ fetch: win.fetch, Headers: win.Headers as any })
win.$fetch = createFetch({ fetch: win.fetch, Headers: win.Headers })

win.__registry = registry
win.__app = h3App
Expand All @@ -71,21 +84,10 @@ export default <Environment>{
return {
// called after all tests with this env have been run
teardown() {
win.happyDOM.cancelAsync()
// @ts-expect-error
teardown()
keys.forEach(key => delete global[key])
// @ts-expect-error
originals.forEach((v, k) => (global[k] = v))
},
}
},
}

interface NuxtWindow extends Window {
__app: App
__registry: Set<string>
__NUXT__: any
$fetch: any
fetch: any
IntersectionObserver: any
}
16 changes: 16 additions & 0 deletions packages/vitest-environment-nuxt/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { App } from "h3"

export type NuxtBuiltinEnvironment = 'happy-dom' | 'jsdom'
export interface NuxtWindow extends Window {
__app: App
__registry: Set<string>
__NUXT__: any
$fetch: any
fetch: any
IntersectionObserver: any
Headers: any
}
export type EnvironmentNuxt = (global: any, options: Record<string, any>) => Promise<{
window: NuxtWindow,
teardown(): void
}>
6 changes: 5 additions & 1 deletion playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
"test:dev": "NUXT_VITEST_DEV_TEST=true nuxt dev",
"test:unit": "vitest",
"test:types": "nuxi prepare && vue-tsc --noEmit",
"test": "pnpm test:unit --run && pnpm test:dev && pnpm test:types"
"test:jsdom": "VITEST_DOM_ENV=jsdom pnpm test:unit --run",
"test:happy-dom": "VITEST_DOM_ENV=happy-dom pnpm test:unit --run",
"test": "pnpm test:happy-dom && pnpm test:jsdom && pnpm test:dev && pnpm test:types"
},
"devDependencies": {
"@nuxt/devtools": "0.6.6",
"nuxt": "3.6.1",
"nuxt-vitest": "0.8.7",
"happy-dom": "^9.10.9",
"jsdom": "^22.0.0",
"typescript": "5.1.6",
"vitest": "0.30.1",
"vitest-environment-nuxt": "0.8.7",
Expand Down
1 change: 1 addition & 0 deletions playground/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default defineVitestConfig({
environmentOptions: {
nuxt: {
rootDir: fileURLToPath(new URL('./', import.meta.url)),
domEnvironment: process.env.VITEST_DOM_ENV as 'happy-dom' | 'jsdom'
},
},
},
Expand Down
Loading

0 comments on commit f14361f

Please sign in to comment.