@happy-dom/jest-environment 9.20.3 → 10.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc.cjs ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('happy-dom/.eslintrc.cjs');
@@ -0,0 +1 @@
1
+ module.exports = require('../happy-dom/.prettierrc.cjs');
package/README.md CHANGED
@@ -38,6 +38,10 @@ And much more..
38
38
 
39
39
  - [Vue](https://vuejs.org/)
40
40
 
41
+ ### Module Systems
42
+
43
+ - [CommonJS](https://nodejs.org/api/modules.html#modules-commonjs-modules) (ESM is not supported due to limitations with Jest)
44
+
41
45
  # Installation
42
46
 
43
47
  ```bash
@@ -91,43 +95,9 @@ Jest uses `node` as test environment by default. In order to tell Jest to use a
91
95
 
92
96
  3. Save the file.
93
97
 
94
- # Additional Features
95
-
96
- Happy DOM exposes two functions that may be useful when testing asynchrounous code.
97
-
98
- **whenAsyncComplete()**
99
-
100
- Returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that is resolved when all async tasks has been completed.
101
-
102
- ```javascript
103
- describe('scrollToTop()', () => {
104
- it('scrolls to top using the built in browser animation', async () => {
105
- element.scrollToTop();
106
-
107
- // Waits for asynchronous tasks like setTimeout(), requestAnimationFrame() etc. to complete
108
- await happyDOM.whenAsyncComplete();
98
+ # Documentation
109
99
 
110
- expect(document.documentElement.scrollTop).toBe(0);
111
- });
112
- });
113
- ```
114
-
115
- **cancelAsync()**
116
-
117
- This method will cancel all running async tasks.
118
-
119
- ```javascript
120
- describe('runAnimation()', () => {
121
- it('runs animation', () => {
122
- element.runAnimation();
123
-
124
- // Cancels all asynchronous tasks like setTimeout(), requestAnimationFrame() etc.
125
- happyDOM.cancelAsync();
126
-
127
- expect(element.animationCompleted).toBe(true);
128
- });
129
- });
130
- ```
100
+ Read more about how Happy DOM works in our [documentation](https://github.com/capricorn86/happy-dom/wiki).
131
101
 
132
102
  # Performance
133
103
 
@@ -143,7 +113,7 @@ describe('runAnimation()', () => {
143
113
  | querySelectorAll('[class~="name"]') | 5.5 ms | 2.9 ms |
144
114
  | querySelectorAll(':nth-child(2n+1)') | 10.4 ms | 3.8 ms |
145
115
 
146
- [See how the test was done here](https://github.com/capricorn86/happy-dom-performance-test)
116
+ See how the test was done [here](https://github.com/capricorn86/happy-dom-performance-test)
147
117
 
148
118
  # Sponsors
149
119
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happy-dom/jest-environment",
3
- "version": "9.20.3",
3
+ "version": "10.0.1",
4
4
  "license": "MIT",
5
5
  "homepage": "https://github.com/capricorn86/happy-dom/tree/master/packages/jest-environment",
6
6
  "repository": "https://github.com/capricorn86/happy-dom",
@@ -54,7 +54,7 @@
54
54
  "@jest/types": "^29.4.0",
55
55
  "jest-mock": "^29.4.0",
56
56
  "jest-util": "^29.4.0",
57
- "happy-dom": "^9.20.3"
57
+ "happy-dom": "^10.0.1"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@typescript-eslint/eslint-plugin": "^5.16.0",
@@ -64,19 +64,20 @@
64
64
  "@testing-library/react": "^12.1.2",
65
65
  "@testing-library/user-event": "^14.4.3",
66
66
  "@types/react-dom": "^17.0.2",
67
+ "@types/jest": "29.5.2",
67
68
  "eslint": "^8.11.0",
68
69
  "eslint-config-prettier": "^8.5.0",
69
70
  "eslint-plugin-prettier": "^4.0.0",
70
71
  "eslint-plugin-filenames": "^1.3.2",
71
- "eslint-plugin-import": "^2.25.4",
72
+ "eslint-plugin-import": "^2.27.5",
72
73
  "eslint-plugin-jest": "^26.1.2",
73
74
  "eslint-plugin-jsdoc": "^38.0.6",
74
75
  "eslint-plugin-json": "^3.1.0",
75
76
  "eslint-plugin-turbo": "^0.0.7",
76
77
  "prettier": "^2.6.0",
77
- "typescript": "^4.6.2",
78
+ "typescript": "^5.0.4",
78
79
  "jest": "^29.4.0",
79
- "ts-jest": "^29.0.5",
80
+ "ts-jest": "^29.1.1",
80
81
  "react": "^17.0.2",
81
82
  "react-dom": "^17.0.2",
82
83
  "vue": "^3.2.31",
@@ -89,6 +90,7 @@
89
90
  "@angular/platform-browser": "^10.0.7",
90
91
  "@angular/compiler": "^10.0.7",
91
92
  "@angular/common": "^10.0.7",
93
+ "rxjs": "^6.5.3",
92
94
  "jquery": "^3.6.0",
93
95
  "zone.js": "^0.10.3",
94
96
  "glob": "^7.2.0",
package/tsconfig.json CHANGED
@@ -21,20 +21,16 @@
21
21
  "baseUrl": ".",
22
22
  "composite": true,
23
23
  "incremental": true,
24
- "jsx": "react",
25
24
  "types": [
26
25
  "node"
27
26
  ],
28
27
  "lib": [
29
- "es2015",
30
- "es2016",
31
- "es2017",
28
+ "es2020",
32
29
  "dom"
33
30
  ]
34
31
  },
35
32
  "include": [
36
33
  "@types/node",
37
- "src",
38
- "src/**/*.json"
34
+ "src"
39
35
  ]
40
36
  }
package/.eslintrc.js DELETED
@@ -1 +0,0 @@
1
- module.exports = require('happy-dom/.eslintrc');
package/.prettierrc.js DELETED
@@ -1 +0,0 @@
1
- module.exports = require('../happy-dom/.prettierrc.js');
@@ -1,23 +0,0 @@
1
- import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
2
- import { enableProdMode } from '@angular/core';
3
- import 'zone.js';
4
- import AngularModule from './AngularModule';
5
-
6
- describe('React', () => {
7
- let appElement: Element;
8
-
9
- beforeEach(() => {
10
- appElement = document.createElement('app');
11
- document.body.appendChild(appElement);
12
- });
13
-
14
- afterEach(() => {
15
- document.body.removeChild(appElement);
16
- });
17
-
18
- it('Tests integration with Angular.', async () => {
19
- enableProdMode();
20
- await platformBrowserDynamic().bootstrapModule(AngularModule);
21
- expect(appElement.innerHTML).toBe('<div>Test</div>');
22
- });
23
- });
@@ -1,9 +0,0 @@
1
- import { Component } from '@angular/core';
2
- /**
3
- *
4
- */
5
- @Component({
6
- selector: 'app',
7
- template: ` <div>Test</div> `
8
- })
9
- export default class AngularComponent {}
@@ -1,13 +0,0 @@
1
- import { NgModule } from '@angular/core';
2
- import { BrowserModule } from '@angular/platform-browser';
3
- import AngularComponent from './AngularComponent';
4
-
5
- /**
6
- *
7
- */
8
- @NgModule({
9
- imports: [BrowserModule],
10
- declarations: [AngularComponent],
11
- bootstrap: [AngularComponent]
12
- })
13
- export default class AngularModule {}
@@ -1,140 +0,0 @@
1
- import Express from 'express';
2
-
3
- describe('JavaScript', () => {
4
- it('Functions should have the constructor global.Function', () => {
5
- expect((() => {}).constructor).toBe(Function);
6
- });
7
-
8
- it('Object should have the constructor global.Object', () => {
9
- expect({}.constructor).toBe(Object);
10
- });
11
-
12
- it('Can perform a real fetch()', async () => {
13
- const express = Express();
14
-
15
- express.get('/get/json', (_req, res) => {
16
- res.set('Content-Type', 'application/json');
17
- res.send('{ "key1": "value1" }');
18
- });
19
-
20
- const server = express.listen(3000);
21
-
22
- const response = await fetch('http://localhost:3000/get/json');
23
-
24
- server.close();
25
-
26
- expect(response.headers.get('content-type')).toBe('application/json; charset=utf-8');
27
- expect(response.ok).toBe(true);
28
- expect(response.status).toBe(200);
29
- expect(response.statusText).toBe('OK');
30
- expect(response.url).toBe('http://localhost:3000/get/json');
31
- expect(response.redirected).toBe(false);
32
-
33
- const json = await response.json();
34
-
35
- expect(json.key1).toBe('value1');
36
- });
37
-
38
- it('Can perform a real FormData post request using fetch()', async () => {
39
- const express = Express();
40
-
41
- express.post('/post/formdata', (req, res) => {
42
- let body = '';
43
- res.set('Content-Type', 'text/html');
44
- req.on('data', function (chunk) {
45
- body += chunk.toString();
46
- });
47
- req.on('end', function () {
48
- res.send(`header:\n${req.headers['content-type']}\n\nbody:\n${body}`);
49
- });
50
- });
51
-
52
- const server = express.listen(3000);
53
-
54
- const requestFormData = new FormData();
55
-
56
- requestFormData.append('key1', 'value1');
57
- requestFormData.append('key2', 'value2');
58
-
59
- const response = await fetch('http://localhost:3000/post/formdata', {
60
- method: 'POST',
61
- body: requestFormData
62
- });
63
-
64
- expect(response.headers.get('content-type')).toBe('text/html; charset=utf-8');
65
- expect(response.ok).toBe(true);
66
- expect(response.status).toBe(200);
67
- expect(response.statusText).toBe('OK');
68
- expect(response.url).toBe('http://localhost:3000/post/formdata');
69
- expect(response.redirected).toBe(false);
70
-
71
- const text = await response.text();
72
-
73
- server.close();
74
-
75
- expect(
76
- text.replace(
77
- /\-\-\-\-HappyDOMFormDataBoundary0\.[a-zA-Z0-9]+/gm,
78
- '----HappyDOMFormDataBoundary0.noRandom'
79
- )
80
- ).toBe(
81
- 'header:\nmultipart/form-data; boundary=----HappyDOMFormDataBoundary0.noRandom\n\nbody:\n------HappyDOMFormDataBoundary0.noRandom\r\nContent-Disposition: form-data; name="key1"\r\n\r\nvalue1\r\n------HappyDOMFormDataBoundary0.noRandom\r\nContent-Disposition: form-data; name="key2"\r\n\r\nvalue2\r\n'
82
- );
83
- });
84
-
85
- it('Can perform a real asynchronous XMLHttpRequest request', (done) => {
86
- const express = Express();
87
-
88
- express.get('/get/json', (_req, res) => {
89
- res.set('Content-Type', 'application/json');
90
- res.send('{ "key1": "value1" }');
91
- });
92
-
93
- const server = express.listen(3000);
94
-
95
- const request = new XMLHttpRequest();
96
-
97
- request.open('GET', 'http://localhost:3000/get/json', true);
98
-
99
- request.addEventListener('load', () => {
100
- expect(request.getResponseHeader('content-type')).toBe('application/json; charset=utf-8');
101
- expect(request.responseText).toBe('{ "key1": "value1" }');
102
- expect(request.status).toBe(200);
103
- expect(request.statusText).toBe('OK');
104
- expect(request.responseURL).toBe('http://localhost:3000/get/json');
105
-
106
- server.close();
107
-
108
- done();
109
- });
110
-
111
- request.send();
112
- });
113
-
114
- it('Can perform a real synchronous XMLHttpRequest request to Github.com', () => {
115
- const request = new XMLHttpRequest();
116
-
117
- request.open(
118
- 'GET',
119
- 'https://raw.githubusercontent.com/capricorn86/happy-dom/master/.gitignore',
120
- false
121
- );
122
-
123
- request.send();
124
-
125
- expect(request.getResponseHeader('content-type')).toBe('text/plain; charset=utf-8');
126
- expect(request.responseText.includes('node_modules')).toBe(true);
127
- expect(request.status).toBe(200);
128
- expect(request.statusText).toBe('OK');
129
- expect(request.responseURL).toBe(
130
- 'https://raw.githubusercontent.com/capricorn86/happy-dom/master/.gitignore'
131
- );
132
- });
133
-
134
- it('Binds global methods to the Window context', () => {
135
- const eventListener = (): void => {};
136
- addEventListener('click', eventListener);
137
- removeEventListener('click', eventListener);
138
- clearTimeout(setTimeout(eventListener));
139
- });
140
- });
@@ -1,28 +0,0 @@
1
- import JQuery from 'jquery';
2
-
3
- describe('JQuery', () => {
4
- beforeEach(() => {
5
- document.body.innerHTML = `
6
- <div class="class1 class2" id="id">
7
- <!-- Comment 1 !-->
8
- <b>Bold</b>
9
- <!-- Comment 2 !-->
10
- <span>Span</span>
11
- </div>
12
- <article class="class1 class2" id="id">
13
- <!-- Comment 1 !-->
14
- <b>Bold</b>
15
- <!-- Comment 2 !-->
16
- </article>
17
- `;
18
- });
19
-
20
- afterEach(() => {
21
- document.body.innerHTML = '';
22
- });
23
-
24
- it('Tests integration.', () => {
25
- JQuery('span').addClass('test-span');
26
- expect(document.body.children[0].children[1].getAttribute('class')).toBe('test-span');
27
- });
28
- });
@@ -1,40 +0,0 @@
1
- import LitElementComponent from './LitElementComponent';
2
- import './LitElementComponent';
3
-
4
- const PROP1 = 'PROP1';
5
-
6
- describe('LitElementComponent', () => {
7
- beforeEach(() => {
8
- document.body.innerHTML = `<lit-element-component prop1="${PROP1}"></lit-element-component>`;
9
- });
10
-
11
- afterEach(() => {
12
- document.body.innerHTML = '';
13
- });
14
-
15
- it('Tests integration.', () => {
16
- const litElement = <LitElementComponent>document.body.querySelector('lit-element-component');
17
- const shadowRoot = litElement.shadowRoot;
18
-
19
- expect(document.body.innerHTML).toBe(
20
- `<lit-element-component prop1="${PROP1}"></lit-element-component>`
21
- );
22
-
23
- expect(shadowRoot.querySelector('span').innerText).toBe(PROP1);
24
- expect(
25
- shadowRoot.innerHTML
26
- .replace(/[\s]/gm, '')
27
- .replace(/<!--\?lit\$[0-9]+\$-->/gm, '<!--?lit$123456$-->')
28
- ).toBe(
29
- `
30
- <!---->Some text
31
- <span><!--?lit$123456$-->${PROP1}</span>!
32
- <style>
33
- span {
34
- color: green;
35
- }
36
- </style>
37
- `.replace(/[\s]/gm, '')
38
- );
39
- });
40
- });
@@ -1,24 +0,0 @@
1
- import { LitElement, TemplateResult, html, css, CSSResult } from '../../lib/node_modules/lit';
2
- import { customElement, property } from '../../lib/node_modules/lit/decorators';
3
-
4
- /**
5
- *
6
- */
7
- @customElement('lit-element-component')
8
- export default class LitElementComponent extends LitElement {
9
- public static styles: CSSResult = css`
10
- span {
11
- color: green;
12
- }
13
- `;
14
-
15
- @property({ type: String })
16
- public prop1: string = null;
17
-
18
- /**
19
- * Renders the component.
20
- */
21
- public render(): TemplateResult {
22
- return html` Some text <span>${this.prop1}</span>! `;
23
- }
24
- }
@@ -1,46 +0,0 @@
1
- import React from 'react';
2
- import ReactDOM from 'react-dom';
3
- import * as ReactTestingLibrary from '@testing-library/react';
4
- import ReactTestingLibraryUserEvent from '@testing-library/user-event';
5
- import { ReactDivComponent, ReactSelectComponent, ReactInputComponent } from './ReactComponents';
6
-
7
- /* eslint-disable @typescript-eslint/consistent-type-assertions */
8
-
9
- const TESTING_LIBRARY_USER = ReactTestingLibraryUserEvent.setup();
10
-
11
- describe('React', () => {
12
- let appElement: Element;
13
-
14
- beforeEach(() => {
15
- appElement = document.createElement('app');
16
- document.body.appendChild(appElement);
17
- });
18
-
19
- afterEach(() => {
20
- document.body.removeChild(appElement);
21
- });
22
-
23
- it('Tests integration.', () => {
24
- ReactDOM.render(<ReactDivComponent />, appElement);
25
- expect(appElement.innerHTML).toBe('<div>Test</div>');
26
- });
27
-
28
- it('Can unmount a component.', () => {
29
- ReactDOM.render(<ReactDivComponent />, appElement);
30
- ReactDOM.unmountComponentAtNode(appElement);
31
- expect(appElement.innerHTML).toBe('');
32
- });
33
-
34
- it('Handles adding and removing event listeners.', () => {
35
- ReactDOM.render(<ReactSelectComponent onChange={() => {}} />, appElement);
36
- });
37
-
38
- it('Testing library handles input', async () => {
39
- const { getByPlaceholderText } = ReactTestingLibrary.render(<ReactInputComponent />);
40
- const input: HTMLInputElement = getByPlaceholderText('input field') as HTMLInputElement;
41
-
42
- await TESTING_LIBRARY_USER.type(input, 'hello');
43
-
44
- expect(input.value).toBe('hello');
45
- });
46
- });
@@ -1,75 +0,0 @@
1
- import React from 'react';
2
-
3
- /* eslint-disable @typescript-eslint/consistent-type-assertions */
4
-
5
- /**
6
- *
7
- */
8
- export class ReactDivComponent extends React.Component<{}, {}> {
9
- public $props = {};
10
-
11
- /**
12
- * @override
13
- */
14
- public render(): React.ReactElement {
15
- return <div>Test</div>;
16
- }
17
- }
18
-
19
- const OPTIONS = [
20
- { value: 't1', label: 'test 1' },
21
- { value: 't2', label: 'test 2' }
22
- ];
23
-
24
- /**
25
- *
26
- */
27
- export class ReactSelectComponent extends React.Component<
28
- {
29
- value?: string;
30
- onChange?: (value: string) => void;
31
- },
32
- {}
33
- > {
34
- public $props = {
35
- value: OPTIONS[0].value,
36
- onChange: null
37
- };
38
-
39
- /**
40
- * @override
41
- */
42
- public render(): React.ReactElement {
43
- return (
44
- <select
45
- value={this.props.value}
46
- onChange={
47
- this.props.onChange
48
- ? (event: React.ChangeEvent) =>
49
- this.props.onChange((event.target as HTMLSelectElement).value)
50
- : undefined
51
- }
52
- >
53
- {OPTIONS.map((option) => (
54
- <option key={option.value} value={option.value}>
55
- {option.label}
56
- </option>
57
- ))}
58
- </select>
59
- );
60
- }
61
- }
62
-
63
- /**
64
- *
65
- */
66
- export class ReactInputComponent extends React.Component<{}, {}> {
67
- public $props = {};
68
-
69
- /**
70
- * @override
71
- */
72
- public render(): React.ReactElement {
73
- return <input placeholder="input field" />;
74
- }
75
- }
@@ -1,59 +0,0 @@
1
- import { render, screen } from '@testing-library/react';
2
- import userEvent from '@testing-library/user-event';
3
- import React from 'react';
4
-
5
- describe('TestingLibrary', () => {
6
- it('Triggers change event in input when typing.', async () => {
7
- const user = userEvent.setup();
8
- const onChange = jest.fn();
9
-
10
- render(<input onChange={(event) => onChange(event.target.value)} />);
11
-
12
- await user.type(screen.getByRole('textbox'), 'hello');
13
- expect(onChange).toHaveBeenCalledWith('hello');
14
- });
15
-
16
- it('Triggers click and submit event once.', async () => {
17
- const user = userEvent.setup();
18
- const handleSubmit = jest.fn((ev) => {
19
- ev.preventDefault();
20
- });
21
- const clickHandler = jest.fn();
22
-
23
- render(
24
- <form onSubmit={handleSubmit}>
25
- <button type="submit" onClick={clickHandler}>
26
- submit
27
- </button>
28
- </form>
29
- );
30
-
31
- await user.click(screen.getByRole('button'));
32
- expect(handleSubmit).toHaveBeenCalledTimes(1);
33
- expect(clickHandler).toHaveBeenCalledTimes(1);
34
- });
35
-
36
- it('Triggers change event once.', async () => {
37
- const user = userEvent.setup();
38
- const changeHandler = jest.fn();
39
-
40
- render(<input type="checkbox" onChange={changeHandler} />);
41
-
42
- await user.click(screen.getByRole('checkbox'));
43
-
44
- expect(changeHandler).toHaveBeenCalledTimes(1);
45
- });
46
-
47
- it('Finds elements using "screen.getByText()".', async () => {
48
- const user = userEvent.setup();
49
- const clickHandler = jest.fn();
50
-
51
- render(<input type="submit" value="Submit Button" onClick={clickHandler} />);
52
-
53
- const element = screen.getByText('Submit Button');
54
-
55
- await user.click(element);
56
-
57
- expect(clickHandler).toHaveBeenCalledTimes(1);
58
- });
59
- });
@@ -1,41 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "outDir": "../tmp",
4
- "rootDir": ".",
5
- "tsBuildInfoFile": "../tmp/.tsbuildinfo-test",
6
- "target": "es2020",
7
- "declaration": true,
8
- "module": "CommonJS",
9
- "moduleResolution": "node",
10
- "esModuleInterop": true,
11
- "experimentalDecorators": true,
12
- "allowSyntheticDefaultImports": true,
13
- "resolveJsonModule": true,
14
- "noUnusedLocals": true,
15
- "noUnusedParameters": true,
16
- "removeComments": false,
17
- "preserveConstEnums": true,
18
- "sourceMap": true,
19
- "skipLibCheck": true,
20
- "baseUrl": ".",
21
- "composite": true,
22
- "incremental": true,
23
- "jsx": "react",
24
- "types": [
25
- "jest"
26
- ],
27
- "lib": [
28
- "es2015",
29
- "es2016",
30
- "es2017",
31
- "dom"
32
- ]
33
- },
34
- "include": [
35
- "@types/node",
36
- "@types/jest",
37
- "@types/react",
38
- "@types/react-dom",
39
- "."
40
- ]
41
- }
@@ -1,28 +0,0 @@
1
- import { createApp } from 'vue';
2
-
3
- describe('Vue', () => {
4
- let appElement: Element;
5
-
6
- beforeEach(() => {
7
- appElement = document.createElement('div');
8
- appElement.id = 'app';
9
- appElement.innerHTML = '{{ message }}';
10
- document.body.appendChild(appElement);
11
- });
12
-
13
- afterEach(() => {
14
- document.body.innerHTML = '';
15
- });
16
-
17
- it('Tests integration.', () => {
18
- const app = createApp({
19
- data() {
20
- return {
21
- message: 'Test'
22
- };
23
- }
24
- });
25
- app.mount('#app');
26
- expect(document.body.innerHTML).toBe('<div id="app" data-v-app="">Test</div>');
27
- });
28
- });