Skip to content

Commit

Permalink
Merge pull request #520 from Mas0nShi/463-adds-support-for-xml-http-r…
Browse files Browse the repository at this point in the history
…equest

#463@minor: Adds support for XMLHttpRequest.
  • Loading branch information
capricorn86 committed Dec 7, 2022
2 parents b9ecfc7 + 2e45e3e commit 89ca67b
Show file tree
Hide file tree
Showing 37 changed files with 2,906 additions and 309 deletions.
31 changes: 16 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

89 changes: 62 additions & 27 deletions packages/happy-dom/README.md
@@ -1,6 +1,5 @@
![Happy DOM Logo](https://github.com/capricorn86/happy-dom/raw/master/docs/happy-dom-logo.jpg)


# About

[Happy DOM](https://github.com/capricorn86/happy-dom) is a JavaScript implementation of a web browser without its graphical user interface. It includes many web standards from WHATWG [DOM](https://dom.spec.whatwg.org/) and [HTML](https://html.spec.whatwg.org/multipage/).
Expand All @@ -9,7 +8,6 @@ The goal of [Happy DOM](https://github.com/capricorn86/happy-dom) is to emulate

[Happy DOM](https://github.com/capricorn86/happy-dom) focuses heavily on performance and can be used as an alternative to [JSDOM](https://github.com/jsdom/jsdom).


### DOM Features

- Custom Elements (Web Components)
Expand All @@ -26,8 +24,6 @@ The goal of [Happy DOM](https://github.com/capricorn86/happy-dom) is to emulate

And much more..



### Works With

- [Google LitHTML](https://lit-html.polymer-project.org)
Expand All @@ -40,20 +36,14 @@ And much more..

- [Vue](https://vuejs.org/)



# Installation

```bash
npm install happy-dom
```



# Usage



## Basic Usage

A simple example of how you can use Happy DOM.
Expand All @@ -75,8 +65,6 @@ container.appendChild(button);
console.log(document.body.innerHTML);
```



## VM Context

The default Window class is a [VM context](https://nodejs.org/api/vm.html#vm_vm_createcontext_sandbox_options). A [VM context](https://nodejs.org/api/vm.html#vm_vm_createcontext_sandbox_options) will execute JavaScript code scoped within the context where the Window instance will be the global object.
Expand All @@ -85,9 +73,9 @@ The default Window class is a [VM context](https://nodejs.org/api/vm.html#vm_vm_
import { Window } from 'happy-dom';

const window = new Window({
innerWidth: 1024,
innerHeight: 768,
url: 'http://localhost:8080'
innerWidth: 1024,
innerHeight: 768,
url: 'http://localhost:8080'
});
const document = window.document;

Expand Down Expand Up @@ -146,9 +134,9 @@ The example below will show you how to setup a Node [VM context](https://nodejs.
import { Window } from 'happy-dom';

const window = new Window({
innerWidth: 1024,
innerHeight: 768,
url: 'http://localhost:8080'
innerWidth: 1024,
innerHeight: 768,
url: 'http://localhost:8080'
});
const document = window.document;

Expand Down Expand Up @@ -207,19 +195,15 @@ Will output:
console.log(document.body.querySelector('div').getInnerHTML({ includeShadowRoots: true }));
```



## Additional Features

Happy DOM exposes two functions that may be useful when working with asynchrounous code.

**whenAsyncComplete()**

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.

```javascript
window.happyDOM.whenAsyncComplete().then(() => {
// Do something when all async tasks are completed.
// Do something when all async tasks are completed.
});
```

Expand All @@ -229,7 +213,7 @@ This method will cancel all running async tasks.

```javascript
window.setTimeout(() => {
// This timeout will be canceled
// This timeout will be canceled
});
window.happyDOM.cancelAsync();
```
Expand All @@ -250,7 +234,60 @@ Sets the property `window.innerHeight` and dispatches a "resize" event.
window.happyDOM.setInnerHeight(768);
```

**setURL()**

Sets the property `window.location.href`.

```javascript
window.happyDOM.setURL('https://localhost:3000');
```

## Settings

Settings can be sent to the constructor or by setting them on the "window.happyDOM.settings" property.

Set by constructor:

```javascript
const window = new Window({
innerWidth: 1024,
innerHeight: 768,
url: 'https://localhost:8080',
settings: {
disableJavaScriptFileLoading: true,
disableJavaScriptEvaluation: true,
disableCSSFileLoading: true,
enableFileSystemHttpRequests: true
}
});
```

Set by property:

```javascript
const window = new Window();

window.happyDOM.settings.disableJavaScriptFileLoading = true;
window.happyDOM.settings.disableJavaScriptEvaluation = true;
window.happyDOM.settings.disableCSSFileLoading = true;
window.happyDOM.settings.enableFileSystemHttpRequests = true;
```

**disableJavaScriptFileLoading**

Set it to "true" to disable JavaScript file loading. Defaults to "false".

**disableJavaScriptEvaluation**

Set it to "true" to completely disable JavaScript evaluation. Defaults to "false".

**disableCSSFileLoading**

Set it to "true" to disable CSS file loading using the HTMLLinkElement. Defaults to "false".

**enableFileSystemHttpRequests**

Set it to "true" to enable file system HTTP requests using XMLHttpRequest. Defaults to "false".

# Performance

Expand All @@ -268,12 +305,10 @@ window.happyDOM.setInnerHeight(768);

[See how the test was done here](https://github.com/capricorn86/happy-dom-performance-test)



# Jest

Happy DOM provide with a package called [@happy-dom/jest-environment](https://github.com/capricorn86/happy-dom/tree/master/packages/jest-environment) that makes it possible to use Happy DOM with [Jest](https://jestjs.io/).

# Global Registration

Happy DOM provide with a package called [@happy-dom/global-registrator](https://github.com/capricorn86/happy-dom/tree/master/packages/global-registrator) that can register Happy DOM globally. It makes it possible to use Happy DOM for testing in a Node environment.
Happy DOM provide with a package called [@happy-dom/global-registrator](https://github.com/capricorn86/happy-dom/tree/master/packages/global-registrator) that can register Happy DOM globally. It makes it possible to use Happy DOM for testing in a Node environment.
50 changes: 24 additions & 26 deletions packages/happy-dom/src/async-task-manager/AsyncTaskManager.ts
Expand Up @@ -3,7 +3,7 @@
*/
export default class AsyncTaskManager {
private static taskID = 0;
private runningTasks: number[] = [];
private runningTasks: { [k: string]: () => void } = {};
private runningTimers: NodeJS.Timeout[] = [];
private queue: { resolve: () => void; reject: (error: Error) => void }[] = [];

Expand All @@ -24,21 +24,27 @@ export default class AsyncTaskManager {
}

/**
* Cancels all tasks.
* Ends all tasks.
*
* @param [error] Error.
*/
public cancelAll(error?: Error): void {
for (const timerID of this.runningTimers) {
global.clearTimeout(timerID);
}

const runningTimers = this.runningTimers;
const runningTasks = this.runningTasks;
const promises = this.queue;

this.runningTasks = [];
this.runningTasks = {};
this.runningTimers = [];
this.queue = [];

for (const timer of runningTimers) {
global.clearTimeout(timer);
}

for (const key of Object.keys(runningTasks)) {
runningTasks[key]();
}

for (const promise of promises) {
if (error) {
promise.reject(error);
Expand Down Expand Up @@ -67,19 +73,20 @@ export default class AsyncTaskManager {
if (index !== -1) {
this.runningTimers.splice(index, 1);
}
if (!this.runningTasks.length && !this.runningTimers.length) {
if (!Object.keys(this.runningTasks).length && !this.runningTimers.length) {
this.cancelAll();
}
}

/**
* Starts an async task.
*
* @param abortHandler Abort handler.
* @returns Task ID.
*/
public startTask(): number {
public startTask(abortHandler?: () => void): number {
const taskID = this.newTaskID();
this.runningTasks.push(taskID);
this.runningTasks[taskID] = abortHandler ? abortHandler : () => {};
return taskID;
}

Expand All @@ -89,12 +96,12 @@ export default class AsyncTaskManager {
* @param taskID Task ID.
*/
public endTask(taskID: number): void {
const index = this.runningTasks.indexOf(taskID);
if (index !== -1) {
this.runningTasks.splice(index, 1);
}
if (!this.runningTasks.length && !this.runningTimers.length) {
this.cancelAll();
if (this.runningTasks[taskID]) {
delete this.runningTasks[taskID];

if (!Object.keys(this.runningTasks).length && !this.runningTimers.length) {
this.cancelAll();
}
}
}

Expand All @@ -104,16 +111,7 @@ export default class AsyncTaskManager {
* @returns Count.
*/
public getTaskCount(): number {
return this.runningTasks.length;
}

/**
* Returns the amount of running timers.
*
* @returns Count.
*/
public getTimerCount(): number {
return this.runningTimers.length;
return Object.keys(this.runningTasks).length;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/event/IEventListener.ts
Expand Up @@ -7,7 +7,7 @@ export default interface IEventListener {
/**
* Handles event.
*
* @param type Event type.
* @param event Event.
*/
handleEvent(event: Event): void;
}
5 changes: 4 additions & 1 deletion packages/happy-dom/src/exception/DOMExceptionNameEnum.ts
Expand Up @@ -8,6 +8,9 @@ enum DOMExceptionNameEnum {
invalidNodeTypeError = 'InvalidNodeTypeError',
invalidCharacterError = 'InvalidCharacterError',
notFoundError = 'NotFoundError',
domException = 'DOMException'
securityError = 'SecurityError',
networkError = 'NetworkError',
domException = 'DOMException',
invalidAccessError = 'InvalidAccessError'
}
export default DOMExceptionNameEnum;

0 comments on commit 89ca67b

Please sign in to comment.