-
Notifications
You must be signed in to change notification settings - Fork 310
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This environment is a copy of `jest-environment-jsdom` which allows end users to decide which version of `jsdom` they would like to use in their project. See more in documentation site at https://thymikee.github.io/jest-preset-angular/docs/guides/jsdom-version Closes #2883
- Loading branch information
Showing
19 changed files
with
592 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,7 +36,6 @@ commitlint.config.js | |
.prettierrc | ||
|
||
# Internal jest config | ||
jest.config.js | ||
jest*.config.ts | ||
|
||
# Tsconfig | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { TestBed } from '@angular/core/testing'; | ||
|
||
import { FooComponent } from '../foo.component'; | ||
|
||
describe('FooComponent', () => { | ||
it('should trigger change detection without fixture.detectChanges', () => { | ||
TestBed.configureTestingModule({ | ||
imports: [FooComponent], | ||
}); | ||
const fixture = TestBed.createComponent(FooComponent); | ||
|
||
expect(fixture.componentInstance.value1()).toBe('val1'); | ||
|
||
fixture.componentRef.setInput('value1', 'hello'); | ||
|
||
expect(fixture.componentInstance.value1()).toBe('hello'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<!-- SOMETHING --> | ||
<p>Line 1</p> | ||
<div> | ||
<div *ngIf="condition1"> | ||
{{ value1() }} | ||
</div> | ||
<span *ngIf="condition2"> | ||
{{ value2() }} | ||
</span> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
p { | ||
font-size: 1.6rem; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { NgIf } from '@angular/common'; | ||
import { Component, input } from '@angular/core'; | ||
|
||
@Component({ | ||
selector: 'foo', | ||
standalone: true, | ||
templateUrl: './foo.component.html', | ||
styleUrls: ['./foo.component.scss'], | ||
// we have to setup styles this way, since simple styles/styleUrs properties will be removed (jest does not unit test styles) | ||
styles: [ | ||
` | ||
p { | ||
color: red; | ||
} | ||
`, | ||
], | ||
imports: [NgIf], | ||
}) | ||
export class FooComponent { | ||
readonly value1 = input('val1'); | ||
readonly value2 = input('val2'); | ||
|
||
protected readonly condition1 = true; | ||
protected readonly condition2 = false; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import type { JestConfigWithTsJest } from 'ts-jest'; | ||
|
||
const config: JestConfigWithTsJest = { | ||
displayName: 'e2e-custom-jsdom-env', | ||
testEnvironment: '<rootDir>/../../environments/jest-jsdom-env.js', | ||
setupFilesAfterEnv: ['<rootDir>/../setup-test-env.ts'], | ||
transform: { | ||
'^.+\\.(ts|mjs|js|html)$': [ | ||
'<rootDir>/../../build/index.js', | ||
{ | ||
tsconfig: '<rootDir>/tsconfig-cjs.spec.json', | ||
stringifyContentPathRegex: '\\.(html|svg)$', | ||
}, | ||
], | ||
}, | ||
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], | ||
}; | ||
|
||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import type { JestConfigWithTsJest } from 'ts-jest'; | ||
|
||
const config: JestConfigWithTsJest = { | ||
displayName: 'e2e-custom-jsdom-env', | ||
testEnvironment: '<rootDir>/../../environments/jest-jsdom-env.js', | ||
setupFilesAfterEnv: ['<rootDir>/../setup-test-env.mts'], | ||
moduleNameMapper: { | ||
rxjs: '<rootDir>/../../node_modules/rxjs/dist/bundles/rxjs.umd.js', | ||
}, | ||
extensionsToTreatAsEsm: ['.ts', '.mts'], | ||
transform: { | ||
'^.+\\.(ts|mts|mjs|js|html)$': [ | ||
'<rootDir>/../../build/index.js', | ||
{ | ||
useESM: true, | ||
tsconfig: '<rootDir>/tsconfig-esm.spec.json', | ||
stringifyContentPathRegex: '\\.(html|svg)$', | ||
}, | ||
], | ||
}, | ||
}; | ||
|
||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import type { JestConfigWithTsJest } from 'ts-jest'; | ||
|
||
const config: JestConfigWithTsJest = { | ||
displayName: 'e2e-custom-jsdom-env', | ||
testEnvironment: '<rootDir>/../../environments/jest-jsdom-env.js', | ||
setupFilesAfterEnv: ['<rootDir>/../setup-test-env.ts'], | ||
transform: { | ||
'^.+\\.(ts|mjs|js|html)$': [ | ||
'<rootDir>/../../build/index.js', | ||
{ | ||
tsconfig: '<rootDir>/tsconfig-cjs.spec.json', | ||
stringifyContentPathRegex: '\\.(html|svg)$', | ||
isolatedModules: true, | ||
}, | ||
], | ||
}, | ||
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], | ||
}; | ||
|
||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import type { JestConfigWithTsJest } from 'ts-jest'; | ||
|
||
const config: JestConfigWithTsJest = { | ||
displayName: 'e2e-custom-jsdom-env', | ||
testEnvironment: '<rootDir>/../../environments/jest-jsdom-env.js', | ||
setupFilesAfterEnv: ['<rootDir>/../setup-test-env.mts'], | ||
moduleNameMapper: { | ||
rxjs: '<rootDir>/../../node_modules/rxjs/dist/bundles/rxjs.umd.js', | ||
}, | ||
extensionsToTreatAsEsm: ['.ts', '.mts'], | ||
transform: { | ||
'^.+\\.(ts|mts|mjs|js|html)$': [ | ||
'<rootDir>/../../build/index.js', | ||
{ | ||
useESM: true, | ||
tsconfig: '<rootDir>/tsconfig-esm.spec.json', | ||
stringifyContentPathRegex: '\\.(html|svg)$', | ||
isolatedModules: true, | ||
}, | ||
], | ||
}, | ||
}; | ||
|
||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "../../tsconfig-base.spec.json" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"extends": "../../tsconfig-base.spec.json", | ||
"compilerOptions": { | ||
"module": "ES2022", | ||
"esModuleInterop": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import type { EnvironmentContext, JestEnvironmentConfig } from '@jest/environment'; | ||
|
||
import BaseEnv from '../build/environments/jest-env-jsdom-abstract'; | ||
|
||
export default class JestJSDOMEnvironment extends BaseEnv { | ||
constructor(config: JestEnvironmentConfig, context: EnvironmentContext); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
const jestJsdomEnv = require('../build/environments/jest-jsdom-env'); | ||
|
||
module.exports = jestJsdomEnv; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
/* eslint-disable */ | ||
// @ts-nocheck TODO: replace with `@jest/environment-jsdom-abstract` package when Jest 30 is released | ||
|
||
/** | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
import type { Context } from 'node:vm'; | ||
|
||
import type { EnvironmentContext, JestEnvironment, JestEnvironmentConfig } from '@jest/environment'; | ||
import { LegacyFakeTimers, ModernFakeTimers } from '@jest/fake-timers'; | ||
import type { Global } from '@jest/types'; | ||
import { ModuleMocker } from 'jest-mock'; | ||
import { installCommonGlobals } from 'jest-util'; | ||
import type * as jsdom from 'jsdom'; | ||
|
||
// The `Window` interface does not have an `Error.stackTraceLimit` property, but | ||
// `JSDOMEnvironment` assumes it is there. | ||
type Win = Window & | ||
Global.Global & { | ||
Error: { | ||
stackTraceLimit: number; | ||
}; | ||
}; | ||
|
||
function isString(value: unknown): value is string { | ||
return typeof value === 'string'; | ||
} | ||
|
||
export default abstract class BaseJSDOMEnvironment implements JestEnvironment<number> { | ||
dom: jsdom.JSDOM | null; | ||
fakeTimers: LegacyFakeTimers<number> | null; | ||
fakeTimersModern: ModernFakeTimers | null; | ||
global: Win; | ||
private errorEventListener: ((event: Event & { error: Error }) => void) | null; | ||
moduleMocker: ModuleMocker | null; | ||
customExportConditions = ['browser']; | ||
private readonly _configuredExportConditions?: Array<string>; | ||
|
||
protected constructor(config: JestEnvironmentConfig, context: EnvironmentContext, jsdomModule: typeof jsdom) { | ||
const { projectConfig } = config; | ||
|
||
const { JSDOM, ResourceLoader, VirtualConsole } = jsdomModule; | ||
|
||
const virtualConsole = new VirtualConsole(); | ||
virtualConsole.sendTo(context.console, { omitJSDOMErrors: true }); | ||
virtualConsole.on('jsdomError', (error) => { | ||
context.console.error(error); | ||
}); | ||
|
||
this.dom = new JSDOM( | ||
typeof projectConfig.testEnvironmentOptions.html === 'string' | ||
? projectConfig.testEnvironmentOptions.html | ||
: '<!DOCTYPE html>', | ||
{ | ||
pretendToBeVisual: true, | ||
resources: | ||
typeof projectConfig.testEnvironmentOptions.userAgent === 'string' | ||
? new ResourceLoader({ | ||
userAgent: projectConfig.testEnvironmentOptions.userAgent, | ||
}) | ||
: undefined, | ||
runScripts: 'dangerously', | ||
url: 'http://localhost/', | ||
virtualConsole, | ||
...projectConfig.testEnvironmentOptions, | ||
}, | ||
); | ||
const global = (this.global = this.dom.window as unknown as Win); | ||
|
||
if (global == null) { | ||
throw new Error('JSDOM did not return a Window object'); | ||
} | ||
|
||
global.global = global; | ||
|
||
// Node's error-message stack size is limited at 10, but it's pretty useful | ||
// to see more than that when a test fails. | ||
this.global.Error.stackTraceLimit = 100; | ||
installCommonGlobals(global, projectConfig.globals); | ||
|
||
// Report uncaught errors. | ||
this.errorEventListener = (event) => { | ||
if (userErrorListenerCount === 0 && event.error != null) { | ||
process.emit('uncaughtException', event.error); | ||
} | ||
}; | ||
global.addEventListener('error', this.errorEventListener); | ||
|
||
// However, don't report them as uncaught if the user listens to 'error' event. | ||
// In that case, we assume the might have custom error handling logic. | ||
const originalAddListener = global.addEventListener.bind(global); | ||
const originalRemoveListener = global.removeEventListener.bind(global); | ||
let userErrorListenerCount = 0; | ||
global.addEventListener = function (...args: Parameters<typeof originalAddListener>) { | ||
if (args[0] === 'error') { | ||
userErrorListenerCount++; | ||
} | ||
|
||
return originalAddListener.apply(this, args); | ||
}; | ||
global.removeEventListener = function (...args: Parameters<typeof originalRemoveListener>) { | ||
if (args[0] === 'error') { | ||
userErrorListenerCount--; | ||
} | ||
|
||
return originalRemoveListener.apply(this, args); | ||
}; | ||
|
||
if ('customExportConditions' in projectConfig.testEnvironmentOptions) { | ||
const { customExportConditions } = projectConfig.testEnvironmentOptions; | ||
if (Array.isArray(customExportConditions) && customExportConditions.every(isString)) { | ||
this._configuredExportConditions = customExportConditions; | ||
} else { | ||
throw new Error('Custom export conditions specified but they are not an array of strings'); | ||
} | ||
} | ||
|
||
this.moduleMocker = new ModuleMocker(global); | ||
|
||
this.fakeTimers = new LegacyFakeTimers({ | ||
config: projectConfig, | ||
global: global as unknown as typeof globalThis, | ||
moduleMocker: this.moduleMocker, | ||
timerConfig: { | ||
idToRef: (id: number) => id, | ||
refToId: (ref: number) => ref, | ||
}, | ||
}); | ||
|
||
this.fakeTimersModern = new ModernFakeTimers({ | ||
config: projectConfig, | ||
global: global as unknown as typeof globalThis, | ||
}); | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-empty-function | ||
async setup(): Promise<void> {} | ||
|
||
async teardown(): Promise<void> { | ||
if (this.fakeTimers) { | ||
this.fakeTimers.dispose(); | ||
} | ||
if (this.fakeTimersModern) { | ||
this.fakeTimersModern.dispose(); | ||
} | ||
if (this.global != null) { | ||
if (this.errorEventListener) { | ||
this.global.removeEventListener('error', this.errorEventListener); | ||
} | ||
this.global.close(); | ||
} | ||
this.errorEventListener = null; | ||
// @ts-expect-error: this.global not allowed to be `null` | ||
this.global = null; | ||
this.dom = null; | ||
this.fakeTimers = null; | ||
this.fakeTimersModern = null; | ||
} | ||
|
||
exportConditions(): Array<string> { | ||
return this._configuredExportConditions ?? this.customExportConditions; | ||
} | ||
|
||
getVmContext(): Context | null { | ||
if (this.dom) { | ||
return this.dom.getInternalVMContext(); | ||
} | ||
|
||
return null; | ||
} | ||
} |
Oops, something went wrong.