Skip to content

Commit

Permalink
Merge branch 'capricorn86:master' into jest-environment_support_full_…
Browse files Browse the repository at this point in the history
…config
  • Loading branch information
Codex- authored Mar 11, 2024
2 parents f0ddc55 + 1bd9020 commit dc53c5e
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 88 deletions.
11 changes: 1 addition & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ on:
pull_request:
types: [closed]

permissions:
contents: write
pull-requests: read

jobs:
check-next-version:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -66,6 +62,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GIT_REPOSITORY_ACCESS_TOKEN }}
ref: master

- name: Use Node.js ${{ matrix.node-version }}
Expand Down Expand Up @@ -105,12 +102,6 @@ jobs:
- name: Run tests
run: npm run test

- name: Configures Git
run: |
git config user.name "${{ secrets.GIT_REPOSITORY_USERNAME }}"
git config user.email "${{ secrets.GIT_REPOSITORY_EMAIL }}"
git remote set-url origin https://${{ secrets.GIT_REPOSITORY_USERNAME }}:${{ secrets.GIT_REPOSITORY_ACCESS_TOKEN }}@github.com/capricorn86/happy-dom.git
- name: Pushes Git tag
run: |
git tag --force v${{ needs.check-next-version.outputs.next_version }} ${GITHUB_SHA}
Expand Down
1 change: 1 addition & 0 deletions packages/happy-dom/src/PropertySymbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,4 @@ export const host = Symbol('host');
export const setURL = Symbol('setURL');
export const localName = Symbol('localName');
export const registedClass = Symbol('registedClass');
export const nodeStream = Symbol('nodeStream');
5 changes: 4 additions & 1 deletion packages/happy-dom/src/config/IHTMLElementTagNameMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ import IHTMLVideoElement from '../nodes/html-video-element/IHTMLVideoElement.js'

// Makes it work with custom elements when they declare their own interface.
declare global {
/* eslint-disable-next-line @typescript-eslint/naming-convention */
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable @typescript-eslint/no-empty-interface */
interface HTMLElementTagNameMap {}
/* eslint-enable @typescript-eslint/naming-convention */
/* eslint-enable @typescript-eslint/no-empty-interface */
}

export default interface IHTMLElementTagNameMap extends HTMLElementTagNameMap {
Expand Down
59 changes: 25 additions & 34 deletions packages/happy-dom/src/fetch/Fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { Socket } from 'net';
import Stream from 'stream';
import DataURIParser from './data-uri/DataURIParser.js';
import FetchCORSUtility from './utilities/FetchCORSUtility.js';
import { ReadableStream } from 'stream/web';
import Request from './Request.js';
import Response from './Response.js';
import Event from '../event/Event.js';
Expand All @@ -28,6 +27,7 @@ import FetchResponseRedirectUtility from './utilities/FetchResponseRedirectUtili
import FetchResponseHeaderUtility from './utilities/FetchResponseHeaderUtility.js';
import FetchHTTPSCertificate from './certificate/FetchHTTPSCertificate.js';
import { Buffer } from 'buffer';
import FetchBodyUtility from './utilities/FetchBodyUtility.js';

const LAST_CHUNK = Buffer.from('0\r\n\r\n');

Expand Down Expand Up @@ -545,7 +545,10 @@ export default class Fetch {
nodeResponse.statusCode === 204 ||
nodeResponse.statusCode === 304
) {
this.response = new this.#window.Response(this.nodeToWebStream(body), responseOptions);
this.response = new this.#window.Response(
FetchBodyUtility.nodeToWebStream(body),
responseOptions
);
(<boolean>this.response.redirected) = this.redirectCount > 0;
(<string>this.response.url) = this.request.url;
this.resolve(this.response);
Expand All @@ -567,7 +570,10 @@ export default class Fetch {
// Ignore error as it is forwarded to the response body.
}
});
this.response = new this.#window.Response(this.nodeToWebStream(body), responseOptions);
this.response = new this.#window.Response(
FetchBodyUtility.nodeToWebStream(body),
responseOptions
);
(<boolean>this.response.redirected) = this.redirectCount > 0;
(<string>this.response.url) = this.request.url;
this.resolve(this.response);
Expand Down Expand Up @@ -599,15 +605,21 @@ export default class Fetch {
});
}

this.response = new this.#window.Response(this.nodeToWebStream(body), responseOptions);
this.response = new this.#window.Response(
FetchBodyUtility.nodeToWebStream(body),
responseOptions
);
(<boolean>this.response.redirected) = this.redirectCount > 0;
(<string>this.response.url) = this.request.url;
this.resolve(this.response);
});
raw.on('end', () => {
// Some old IIS servers return zero-length OK deflate responses, so 'data' is never emitted.
if (!this.response) {
this.response = new this.#window.Response(this.nodeToWebStream(body), responseOptions);
this.response = new this.#window.Response(
FetchBodyUtility.nodeToWebStream(body),
responseOptions
);
(<boolean>this.response.redirected) = this.redirectCount > 0;
(<string>this.response.url) = this.request.url;
this.resolve(this.response);
Expand All @@ -623,15 +635,21 @@ export default class Fetch {
// Ignore error as it is forwarded to the response body.
}
});
this.response = new this.#window.Response(this.nodeToWebStream(body), responseOptions);
this.response = new this.#window.Response(
FetchBodyUtility.nodeToWebStream(body),
responseOptions
);
(<boolean>this.response.redirected) = this.redirectCount > 0;
(<string>this.response.url) = this.request.url;
this.resolve(this.response);
return;
}

// Otherwise, use response as is
this.response = new this.#window.Response(this.nodeToWebStream(body), responseOptions);
this.response = new this.#window.Response(
FetchBodyUtility.nodeToWebStream(body),
responseOptions
);
(<boolean>this.response.redirected) = this.redirectCount > 0;
(<string>this.response.url) = this.request.url;
this.resolve(this.response);
Expand Down Expand Up @@ -806,31 +824,4 @@ export default class Fetch {
this.reject(error);
}
}

/**
* Wraps a Node.js stream into a browser-compatible ReadableStream.
*
* Enables the use of Node.js streams where browser ReadableStreams are required.
* Handles 'data', 'end', and 'error' events from the Node.js stream.
*
* @param nodeStream The Node.js stream to be converted.
* @returns ReadableStream
*/
private nodeToWebStream(nodeStream: Stream): ReadableStream {
return new ReadableStream({
start(controller) {
nodeStream.on('data', (chunk) => {
controller.enqueue(chunk);
});

nodeStream.on('end', () => {
controller.close();
});

nodeStream.on('error', (err) => {
controller.error(err);
});
}
});
}
}
9 changes: 3 additions & 6 deletions packages/happy-dom/src/fetch/Response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,18 +267,15 @@ export default class Response implements IResponse {
* @returns Clone.
*/
public clone(): Response {
const response = new this.#window.Response(this.body, {
const body = FetchBodyUtility.cloneBodyStream(this);

const response = new this.#window.Response(body, {
status: this.status,
statusText: this.statusText,
headers: this.headers
});

(<number>response.status) = this.status;
(<string>response.statusText) = this.statusText;
(<boolean>response.ok) = this.ok;
(<Headers>response.headers) = new Headers(this.headers);
(<ReadableStream>response.body) = this.body;
(<boolean>response.bodyUsed) = this.bodyUsed;
(<boolean>response.redirected) = this.redirected;
(<string>response.type) = this.type;
(<string>response.url) = this.url;
Expand Down
87 changes: 67 additions & 20 deletions packages/happy-dom/src/fetch/utilities/FetchBodyUtility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,12 @@ import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum.js';
import IRequestBody from '../types/IRequestBody.js';
import IResponseBody from '../types/IResponseBody.js';
import { Buffer } from 'buffer';
import Stream from 'stream';

/**
* Fetch body utility.
*/
export default class FetchBodyUtility {
/**
* Wraps a given value in a browser ReadableStream.
*
* This method creates a ReadableStream and immediately enqueues and closes it
* with the provided value, useful for stream API compatibility.
*
* @param value The value to be wrapped in a ReadableStream.
* @returns ReadableStream
*/
public static toReadableStream(value): ReadableStream {
return new ReadableStream({
start(controller) {
controller.enqueue(value);
controller.close();
}
});
}

/**
* Parses body and returns stream and type.
*
Expand Down Expand Up @@ -115,8 +98,8 @@ export default class FetchBodyUtility {
* It creates a pass through stream and pipes the original stream to it.
*
* @param requestOrResponse Request or Response.
* @param requestOrResponse.body
* @param requestOrResponse.bodyUsed
* @param requestOrResponse.body Body.
* @param requestOrResponse.bodyUsed Body used.
* @returns New stream.
*/
public static cloneBodyStream(requestOrResponse: {
Expand All @@ -130,7 +113,25 @@ export default class FetchBodyUtility {
);
}

// If a buffer is set, use it to create a new stream.
if (requestOrResponse[PropertySymbol.buffer]) {
return this.toReadableStream(requestOrResponse[PropertySymbol.buffer]);
}

// Pipe underlying node stream if it exists.
if (requestOrResponse.body[PropertySymbol.nodeStream]) {
const stream1 = new Stream.PassThrough();
const stream2 = new Stream.PassThrough();
requestOrResponse.body[PropertySymbol.nodeStream].pipe(stream1);
requestOrResponse.body[PropertySymbol.nodeStream].pipe(stream2);
// Sets the body of the cloned request/response to the first pass through stream.
requestOrResponse.body = this.nodeToWebStream(stream1);
// Returns the clone.
return this.nodeToWebStream(stream2);
}

// Uses the tee() method to clone the ReadableStream
// This requires the stream to be consumed in parallel which is not the case for the fetch API
const [stream1, stream2] = requestOrResponse.body.tee();

// Sets the body of the cloned request to the first pass through stream.
Expand Down Expand Up @@ -198,4 +199,50 @@ export default class FetchBodyUtility {
);
}
}
/**
* Wraps a given value in a browser ReadableStream.
*
* This method creates a ReadableStream and immediately enqueues and closes it
* with the provided value, useful for stream API compatibility.
*
* @param value The value to be wrapped in a ReadableStream.
* @returns ReadableStream
*/
public static toReadableStream(value): ReadableStream {
return new ReadableStream({
start(controller) {
controller.enqueue(value);
controller.close();
}
});
}

/**
* Wraps a Node.js stream into a browser-compatible ReadableStream.
*
* Enables the use of Node.js streams where browser ReadableStreams are required.
* Handles 'data', 'end', and 'error' events from the Node.js stream.
*
* @param nodeStream The Node.js stream to be converted.
* @returns ReadableStream
*/
public static nodeToWebStream(nodeStream: Stream): ReadableStream {
const readableStream = new ReadableStream({
start(controller) {
nodeStream.on('data', (chunk) => {
controller.enqueue(chunk);
});

nodeStream.on('end', () => {
controller.close();
});

nodeStream.on('error', (err) => {
controller.error(err);
});
}
});
readableStream[PropertySymbol.nodeStream] = nodeStream;
return readableStream;
}
}
2 changes: 1 addition & 1 deletion packages/happy-dom/src/location/Location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { URL } from 'url';
*/
export default class Location {
// Public properties
public [Symbol.toStringTag]: string = 'Location';
public [Symbol.toStringTag] = 'Location';

// Private properties
#browserFrame: IBrowserFrame;
Expand Down
8 changes: 4 additions & 4 deletions packages/happy-dom/src/nodes/element/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,10 @@ export default class Element extends Node implements IElement {
public cloneNode(deep = false): IElement {
const clone = <Element>super.cloneNode(deep);

clone[PropertySymbol.tagName] = this[PropertySymbol.tagName];
clone[PropertySymbol.localName] = this[PropertySymbol.localName];
clone[PropertySymbol.namespaceURI] = this[PropertySymbol.namespaceURI];

for (let i = 0, max = this[PropertySymbol.attributes].length; i < max; i++) {
const attribute = this[PropertySymbol.attributes][i];
clone[PropertySymbol.attributes].setNamedItem(
Expand All @@ -488,10 +492,6 @@ export default class Element extends Node implements IElement {
}
}

clone[PropertySymbol.tagName] = this[PropertySymbol.tagName];
clone[PropertySymbol.localName] = this[PropertySymbol.localName];
clone[PropertySymbol.namespaceURI] = this[PropertySymbol.namespaceURI];

return <IElement>clone;
}

Expand Down
5 changes: 4 additions & 1 deletion packages/happy-dom/src/nodes/node/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,10 @@ export default class Node extends EventTarget implements INode {
* @param otherNode Node to test with.
* @returns "true" if this node contains the other node.
*/
public contains(otherNode: INode): boolean {
public contains(otherNode: INode | undefined): boolean {
if (otherNode === undefined) {
return false;
}
return NodeUtility.isInclusiveAncestor(this, otherNode);
}

Expand Down
Loading

0 comments on commit dc53c5e

Please sign in to comment.