diff --git a/packages/vitest/src/integrations/env/happy-dom.ts b/packages/vitest/src/integrations/env/happy-dom.ts index 3df3b2bd3b5b..fcab5fff7585 100644 --- a/packages/vitest/src/integrations/env/happy-dom.ts +++ b/packages/vitest/src/integrations/env/happy-dom.ts @@ -5,9 +5,12 @@ import { populateGlobal } from './utils' export default ({ name: 'happy-dom', transformMode: 'web', - async setupVM() { + async setupVM({ happyDOM = {} }) { const { Window } = await importModule('happy-dom') as typeof import('happy-dom') - const win = new Window() as any + const win = new Window({ + ...happyDOM, + url: happyDOM.url || 'http://localhost:3000', + }) as any // TODO: browser doesn't expose Buffer, but a lot of dependencies use it win.Buffer = Buffer @@ -26,11 +29,14 @@ export default ({ }, } }, - async setup(global) { + async setup(global, { happyDOM = {} }) { // happy-dom v3 introduced a breaking change to Window, but // provides GlobalWindow as a way to use previous behaviour const { Window, GlobalWindow } = await importModule('happy-dom') as typeof import('happy-dom') - const win = new (GlobalWindow || Window)() + const win = new (GlobalWindow || Window)({ + ...happyDOM, + url: happyDOM.url || 'http://localhost:3000', + }) const { keys, originals } = populateGlobal(global, win, { bindFunctions: true }) diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index b82071f65249..9ae80f19843e 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -8,6 +8,7 @@ import type { TestSequencerConstructor } from '../node/sequencers/types' import type { ChaiConfig } from '../integrations/chai/config' import type { CoverageOptions, ResolvedCoverageOptions } from './coverage' import type { JSDOMOptions } from './jsdom-options' +import type { HappyDOMOptions } from './happy-dom-options' import type { Reporter } from './reporter' import type { SnapshotStateOptions } from './snapshot' import type { Arrayable } from './general' @@ -24,13 +25,14 @@ export type CSSModuleScopeStrategy = 'stable' | 'scoped' | 'non-scoped' export type ApiConfig = Pick -export { JSDOMOptions } +export type { JSDOMOptions, HappyDOMOptions } export interface EnvironmentOptions { /** * jsdom options. */ jsdom?: JSDOMOptions + happyDOM?: HappyDOMOptions [x: string]: unknown } diff --git a/packages/vitest/src/types/happy-dom-options.ts b/packages/vitest/src/types/happy-dom-options.ts new file mode 100644 index 000000000000..6c57cb9dae56 --- /dev/null +++ b/packages/vitest/src/types/happy-dom-options.ts @@ -0,0 +1,20 @@ +/** + * Happy DOM options. + */ +export interface HappyDOMOptions { + width?: number + height?: number + url?: string + settings?: { + disableJavaScriptEvaluation?: boolean + disableJavaScriptFileLoading?: boolean + disableCSSFileLoading?: boolean + disableIframePageLoading?: boolean + disableComputedStyleRendering?: boolean + enableFileSystemHttpRequests?: boolean + device?: { + prefersColorScheme?: string + mediaType?: string + } + } +} diff --git a/packages/vitest/src/utils/test-helpers.ts b/packages/vitest/src/utils/test-helpers.ts index 1f079cc0e3a9..6a682e3a6f32 100644 --- a/packages/vitest/src/utils/test-helpers.ts +++ b/packages/vitest/src/utils/test-helpers.ts @@ -46,13 +46,14 @@ export async function groupFilesByEnv(files: (readonly [WorkspaceProject, string const transformMode = getTransformMode(project.config.testTransformMode, file) const envOptions = JSON.parse(code.match(/@(?:vitest|jest)-environment-options\s+?(.+)/)?.[1] || 'null') + const envKey = env === 'happy-dom' ? 'happyDOM' : env return { file, project, environment: { name: env as VitestEnvironment, transformMode, - options: envOptions ? { [env]: envOptions } as EnvironmentOptions : null, + options: envOptions ? { [envKey]: envOptions } as EnvironmentOptions : null, }, } })) diff --git a/test/core/test/happy-dom-custom.test.ts b/test/core/test/happy-dom-custom.test.ts new file mode 100644 index 000000000000..1bdb2be8f164 --- /dev/null +++ b/test/core/test/happy-dom-custom.test.ts @@ -0,0 +1,22 @@ +/** + * @vitest-environment happy-dom + * @vitest-environment-options { "url": "http://my-website:5435", "settings": { "disableCSSFileLoading": true } } + */ + +/* eslint-disable vars-on-top */ + +import { expect, it } from 'vitest' + +declare global { + // eslint-disable-next-line no-var + var happyDOM: any +} + +it('custom URL is changed to my-website:5435', () => { + expect(location.href).toBe('http://my-website:5435/') +}) + +it('accepts custom environment options', () => { + // default is false + expect(window.happyDOM.settings.disableCSSFileLoading).toBe(true) +}) diff --git a/test/core/test/happy-dom.test.ts b/test/core/test/happy-dom.test.ts index d234ec8ae016..f346db0eec91 100644 --- a/test/core/test/happy-dom.test.ts +++ b/test/core/test/happy-dom.test.ts @@ -11,8 +11,18 @@ import { expect, it, vi } from 'vitest' declare global { // eslint-disable-next-line no-var var __property_dom: unknown + // eslint-disable-next-line no-var + var happyDOM: any } +it('defaults URL to localhost:3000', () => { + expect(location.href).toBe('http://localhost:3000/') +}) + +it('disableCSSFileLoading is false by default because we didn\'t change options', () => { + expect(window.happyDOM.settings.disableCSSFileLoading).toBe(false) +}) + it('defined on self/window are defined on global', () => { expect(self).toBeDefined() expect(window).toBeDefined()