From bf84b1132f077d6a03c6cdc539979d277bf16df8 Mon Sep 17 00:00:00 2001 From: David Ortner Date: Sat, 13 Jan 2024 15:48:10 +0100 Subject: [PATCH 1/2] #1207@patch: Fixes problem related to exception thrown when creating a Node in the constructor of a custom element. --- .../dom-implementation/DOMImplementation.ts | 9 ++--- packages/happy-dom/src/nodes/NodeFactory.ts | 34 +++++++++++++++++++ .../happy-dom/src/nodes/document/Document.ts | 34 +++++++------------ .../happy-dom/src/nodes/document/IDocument.ts | 4 +-- .../document/NodeCreationOwnerDocument.ts | 14 -------- .../happy-dom/src/nodes/element/Element.ts | 26 +++++++------- packages/happy-dom/src/nodes/node/Node.ts | 25 +++++++------- .../happy-dom/src/window/BrowserWindow.ts | 6 ++++ packages/happy-dom/test/CustomElement.ts | 3 ++ .../test/event/events/SubmitEvent.test.ts | 7 ++-- packages/happy-dom/test/fetch/Fetch.test.ts | 30 ++++++++-------- .../happy-dom/test/fetch/SyncFetch.test.ts | 34 +++++++++---------- .../test/nodes/document/Document.test.ts | 10 ++---- .../happy-dom/test/nodes/node/Node.test.ts | 5 +-- 14 files changed, 133 insertions(+), 108 deletions(-) create mode 100644 packages/happy-dom/src/nodes/NodeFactory.ts delete mode 100644 packages/happy-dom/src/nodes/document/NodeCreationOwnerDocument.ts diff --git a/packages/happy-dom/src/dom-implementation/DOMImplementation.ts b/packages/happy-dom/src/dom-implementation/DOMImplementation.ts index 18b8282d2..5a5b75224 100644 --- a/packages/happy-dom/src/dom-implementation/DOMImplementation.ts +++ b/packages/happy-dom/src/dom-implementation/DOMImplementation.ts @@ -1,7 +1,7 @@ import DocumentType from '../nodes/document-type/DocumentType.js'; import * as PropertySymbol from '../PropertySymbol.js'; import IDocument from '../nodes/document/IDocument.js'; -import NodeCreationOwnerDocument from '../nodes/document/NodeCreationOwnerDocument.js'; +import NodeFactory from '../nodes/NodeFactory.js'; /** * The DOMImplementation interface represents an object providing methods which are not dependent on any particular document. Such an object is returned by the. @@ -46,9 +46,10 @@ export default class DOMImplementation { publicId: string, systemId: string ): DocumentType { - NodeCreationOwnerDocument.ownerDocument = this.#document; - const documentType = new this.#document[PropertySymbol.defaultView].DocumentType(); - NodeCreationOwnerDocument.ownerDocument = null; + const documentType = NodeFactory.createNode( + this.#document, + this.#document[PropertySymbol.defaultView].DocumentType + ); documentType.name = qualifiedName; documentType.publicId = publicId; documentType.systemId = systemId; diff --git a/packages/happy-dom/src/nodes/NodeFactory.ts b/packages/happy-dom/src/nodes/NodeFactory.ts new file mode 100644 index 000000000..80a8dde76 --- /dev/null +++ b/packages/happy-dom/src/nodes/NodeFactory.ts @@ -0,0 +1,34 @@ +import IDocument from '../nodes/document/IDocument.js'; + +/** + * Node factory used for setting the owner document to nodes. + */ +export default class NodeFactory { + public static ownerDocuments: IDocument[] = []; + + /** + * Creates a node instance with the given owner document. + * + * @param ownerDocument Owner document. + * @param nodeClass Node class. + * @param [args] Node arguments. + * @returns Node instance. + */ + public static createNode( + ownerDocument: IDocument, + nodeClass: new (...args) => T, + ...args: any[] + ): T { + this.ownerDocuments.push(ownerDocument); + return new nodeClass(...args); + } + + /** + * Pulls an owner document from the queue. + * + * @returns Document. + */ + public static pullOwnerDocument(): IDocument { + return this.ownerDocuments.pop(); + } +} diff --git a/packages/happy-dom/src/nodes/document/Document.ts b/packages/happy-dom/src/nodes/document/Document.ts index 448148598..5486f36f3 100644 --- a/packages/happy-dom/src/nodes/document/Document.ts +++ b/packages/happy-dom/src/nodes/document/Document.ts @@ -45,7 +45,7 @@ import VisibilityStateEnum from './VisibilityStateEnum.js'; import NodeTypeEnum from '../node/NodeTypeEnum.js'; import CookieStringUtility from '../../cookie/urilities/CookieStringUtility.js'; import IBrowserFrame from '../../browser/types/IBrowserFrame.js'; -import NodeCreationOwnerDocument from './NodeCreationOwnerDocument.js'; +import NodeFactory from '../NodeFactory.js'; const PROCESSING_INSTRUCTION_TARGET_REGEXP = /^[a-z][a-z0-9-]+$/; @@ -835,10 +835,8 @@ export default class Document extends Node implements IDocument { this[PropertySymbol.defaultView][ElementTag[tagName]] || HTMLUnknownElement; - NodeCreationOwnerDocument.ownerDocument = this; - const element = new elementClass(); - NodeCreationOwnerDocument.ownerDocument = null; - element.tagName = tagName; + const element = NodeFactory.createNode(this, elementClass); + (element.tagName) = tagName; (element.namespaceURI) = namespaceURI; if (element instanceof Element && options && options.is) { @@ -857,10 +855,7 @@ export default class Document extends Node implements IDocument { * @returns Text node. */ public createTextNode(data?: string): IText { - NodeCreationOwnerDocument.ownerDocument = this; - const node = new this[PropertySymbol.defaultView].Text(data); - NodeCreationOwnerDocument.ownerDocument = null; - return node; + return NodeFactory.createNode(this, this[PropertySymbol.defaultView].Text, data); } /** @@ -870,10 +865,7 @@ export default class Document extends Node implements IDocument { * @returns Text node. */ public createComment(data?: string): IComment { - NodeCreationOwnerDocument.ownerDocument = this; - const node = new this[PropertySymbol.defaultView].Comment(data); - NodeCreationOwnerDocument.ownerDocument = null; - return node; + return NodeFactory.createNode(this, this[PropertySymbol.defaultView].Comment, data); } /** @@ -943,9 +935,7 @@ export default class Document extends Node implements IDocument { * @returns Element. */ public createAttributeNS(namespaceURI: string, qualifiedName: string): IAttr { - NodeCreationOwnerDocument.ownerDocument = this; - const attribute = new this[PropertySymbol.defaultView].Attr(); - NodeCreationOwnerDocument.ownerDocument = null; + const attribute = NodeFactory.createNode(this, this[PropertySymbol.defaultView].Attr); attribute.namespaceURI = namespaceURI; attribute.name = qualifiedName; return attribute; @@ -1016,9 +1006,9 @@ export default class Document extends Node implements IDocument { /** * Creates a Processing Instruction node. * + * @param target Target. + * @param data Data. * @returns IProcessingInstruction. - * @param target - * @param data */ public createProcessingInstruction(target: string, data: string): IProcessingInstruction { if (!target || !PROCESSING_INSTRUCTION_TARGET_REGEXP.test(target)) { @@ -1031,9 +1021,11 @@ export default class Document extends Node implements IDocument { `Failed to execute 'createProcessingInstruction' on 'Document': The data provided ('?>') contains '?>'` ); } - NodeCreationOwnerDocument.ownerDocument = this; - const processingInstruction = new this[PropertySymbol.defaultView].ProcessingInstruction(data); - NodeCreationOwnerDocument.ownerDocument = null; + const processingInstruction = NodeFactory.createNode( + this, + this[PropertySymbol.defaultView].ProcessingInstruction, + data + ); processingInstruction.target = target; return processingInstruction; } diff --git a/packages/happy-dom/src/nodes/document/IDocument.ts b/packages/happy-dom/src/nodes/document/IDocument.ts index 5b417247f..dcc4d5b10 100644 --- a/packages/happy-dom/src/nodes/document/IDocument.ts +++ b/packages/happy-dom/src/nodes/document/IDocument.ts @@ -333,9 +333,9 @@ export default interface IDocument extends IParentNode { /** * Creates a Processing Instruction node. * + * @param target Target. + * @param data Data. * @returns IProcessingInstruction. - * @param target - * @param data */ createProcessingInstruction(target: string, data: string): IProcessingInstruction; } diff --git a/packages/happy-dom/src/nodes/document/NodeCreationOwnerDocument.ts b/packages/happy-dom/src/nodes/document/NodeCreationOwnerDocument.ts deleted file mode 100644 index 3524d8fe1..000000000 --- a/packages/happy-dom/src/nodes/document/NodeCreationOwnerDocument.ts +++ /dev/null @@ -1,14 +0,0 @@ -import IDocument from './IDocument.js'; - -/** - * When creating a new node, the ownerDocument property is set to the document object associated with the node's. - * The ownerDocument property has to be available in the constructor of the node. - * - * This is used for setting current ownerDocument state when creating a new node. - * - * Another method for achieving this wich is also supported in Node, is to set a static property on the node class. - * This may be necessary for sub-classes wich are bound to a document, but can cause problems in some cases when Node.js sets this.constructor to Reflect.comnstruct(), which is not the original class. - */ -export default <{ ownerDocument: IDocument | null }>{ - ownerDocument: null -}; diff --git a/packages/happy-dom/src/nodes/element/Element.ts b/packages/happy-dom/src/nodes/element/Element.ts index 9e0a8f73b..0b101d821 100644 --- a/packages/happy-dom/src/nodes/element/Element.ts +++ b/packages/happy-dom/src/nodes/element/Element.ts @@ -32,7 +32,7 @@ import ElementNamedNodeMap from './ElementNamedNodeMap.js'; import WindowErrorUtility from '../../window/WindowErrorUtility.js'; import WindowBrowserSettingsReader from '../../window/WindowBrowserSettingsReader.js'; import BrowserErrorCaptureEnum from '../../browser/enums/BrowserErrorCaptureEnum.js'; -import NodeCreationOwnerDocument from '../document/NodeCreationOwnerDocument.js'; +import NodeFactory from '../NodeFactory.js'; /** * Element. @@ -688,17 +688,19 @@ export default class Element extends Node implements IElement { throw new DOMException('Shadow root has already been attached.'); } - NodeCreationOwnerDocument.ownerDocument = this.ownerDocument; - (this[PropertySymbol.shadowRoot]) = new this.ownerDocument[ - PropertySymbol.defaultView - ].ShadowRoot(); - NodeCreationOwnerDocument.ownerDocument = null; - (this[PropertySymbol.shadowRoot].host) = this; - (this[PropertySymbol.shadowRoot].mode) = init.mode; - (this[PropertySymbol.shadowRoot])[PropertySymbol.connectToNode](this); - - if (this[PropertySymbol.shadowRoot].mode === 'open') { - (this.shadowRoot) = this[PropertySymbol.shadowRoot]; + const shadowRoot = NodeFactory.createNode( + this.ownerDocument, + this.ownerDocument[PropertySymbol.defaultView].ShadowRoot + ); + + (this[PropertySymbol.shadowRoot]) = shadowRoot; + + (shadowRoot.host) = this; + (shadowRoot.mode) = init.mode; + (shadowRoot)[PropertySymbol.connectToNode](this); + + if (shadowRoot.mode === 'open') { + (this.shadowRoot) = shadowRoot; } return this[PropertySymbol.shadowRoot]; diff --git a/packages/happy-dom/src/nodes/node/Node.ts b/packages/happy-dom/src/nodes/node/Node.ts index 978968b41..277829fcc 100644 --- a/packages/happy-dom/src/nodes/node/Node.ts +++ b/packages/happy-dom/src/nodes/node/Node.ts @@ -11,14 +11,14 @@ import NodeUtility from './NodeUtility.js'; import IAttr from '../attr/IAttr.js'; import NodeList from './NodeList.js'; import INodeList from './INodeList.js'; -import NodeCreationOwnerDocument from '../document/NodeCreationOwnerDocument.js'; +import NodeFactory from '../NodeFactory.js'; /** * Node. */ export default class Node extends EventTarget implements INode { // Can be set before the Node is created. - public static [PropertySymbol.ownerDocument]: IDocument | null = null; + public static [PropertySymbol.ownerDocument]: IDocument | null; // Public properties public static readonly ELEMENT_NODE = NodeTypeEnum.elementNode; @@ -71,13 +71,16 @@ export default class Node extends EventTarget implements INode { */ constructor() { super(); - if ( - NodeCreationOwnerDocument.ownerDocument || - (this.constructor)[PropertySymbol.ownerDocument] - ) { - this.ownerDocument = - NodeCreationOwnerDocument.ownerDocument || - (this.constructor)[PropertySymbol.ownerDocument]; + if ((this.constructor)[PropertySymbol.ownerDocument] !== undefined) { + this.ownerDocument = (this.constructor)[PropertySymbol.ownerDocument]; + } else { + const ownerDocument = NodeFactory.pullOwnerDocument(); + if (!ownerDocument) { + throw new Error( + 'Failed to construct "Node": No owner document in queue. Please use "NodeFactory" to create instances of a Node.' + ); + } + this.ownerDocument = ownerDocument; } } @@ -279,9 +282,7 @@ export default class Node extends EventTarget implements INode { * @returns Cloned node. */ public cloneNode(deep = false): INode { - NodeCreationOwnerDocument.ownerDocument = this.ownerDocument; - const clone = new (this.constructor)(); - NodeCreationOwnerDocument.ownerDocument = null; + const clone = NodeFactory.createNode(this.ownerDocument, this.constructor); // Document has childNodes directly when it is created if (clone[PropertySymbol.childNodes].length) { diff --git a/packages/happy-dom/src/window/BrowserWindow.ts b/packages/happy-dom/src/window/BrowserWindow.ts index bd5fde4dc..36d7f66be 100644 --- a/packages/happy-dom/src/window/BrowserWindow.ts +++ b/packages/happy-dom/src/window/BrowserWindow.ts @@ -648,6 +648,12 @@ export default class BrowserWindow extends EventTarget implements IBrowserWindow this.XMLDocument = XMLDocument; this.SVGDocument = SVGDocument; + // Override owner document + this.Document[PropertySymbol.ownerDocument] = null; + this.HTMLDocument[PropertySymbol.ownerDocument] = null; + this.XMLDocument[PropertySymbol.ownerDocument] = null; + this.SVGDocument[PropertySymbol.ownerDocument] = null; + // Document this.document = new HTMLDocument(); (this.document.defaultView) = this; diff --git a/packages/happy-dom/test/CustomElement.ts b/packages/happy-dom/test/CustomElement.ts index 0ce4f18b5..a337a1e9d 100644 --- a/packages/happy-dom/test/CustomElement.ts +++ b/packages/happy-dom/test/CustomElement.ts @@ -20,6 +20,9 @@ export default class CustomElement extends HTMLElement { constructor() { super(); this.internalShadowRoot = this.attachShadow({ mode: CustomElement.shadowRootMode }); + + // Test to create a node while constructing this node. + this.ownerDocument.createElement('div'); } /** diff --git a/packages/happy-dom/test/event/events/SubmitEvent.test.ts b/packages/happy-dom/test/event/events/SubmitEvent.test.ts index 9e2ecea2d..7fc9f32fc 100644 --- a/packages/happy-dom/test/event/events/SubmitEvent.test.ts +++ b/packages/happy-dom/test/event/events/SubmitEvent.test.ts @@ -1,12 +1,15 @@ import Event from '../../../src/event/Event.js'; import SubmitEvent from '../../../src/event/events/SubmitEvent.js'; -import HTMLButtonElement from '../../../src/nodes/html-button-element/HTMLButtonElement.js'; +import IHTMLButtonElement from '../../../src/nodes/html-button-element/IHTMLButtonElement.js'; +import Window from '../../../src/window/Window.js'; import { describe, it, expect } from 'vitest'; describe('SubmitEvent', () => { describe('constructor', () => { it('Creates a submit event.', () => { - const submitter = new HTMLButtonElement(); + const window = new Window(); + const document = window.document; + const submitter = document.createElement('button'); const event = new SubmitEvent('submit', { bubbles: true, submitter }); expect(event).toBeInstanceOf(Event); expect(event.bubbles).toBe(true); diff --git a/packages/happy-dom/test/fetch/Fetch.test.ts b/packages/happy-dom/test/fetch/Fetch.test.ts index 877c90b0f..446e2aff9 100644 --- a/packages/happy-dom/test/fetch/Fetch.test.ts +++ b/packages/happy-dom/test/fetch/Fetch.test.ts @@ -3585,7 +3585,7 @@ describe('Fetch', () => { expect(requestCount).toBe(1); }); - it('Revalidates cache with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.003".', async () => { + it('Revalidates cache with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.010".', async () => { const window = new Window({ url: 'https://localhost:8080/' }); const url = 'https://localhost:8080/some/path'; const responseText = 'some text'; @@ -3612,7 +3612,7 @@ describe('Fetch', () => { 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT', 'cache-control', - 'max-age=0.003' + 'max-age=0.010' ]; callback(response); @@ -3632,7 +3632,7 @@ describe('Fetch', () => { 'content-length', String(responseText.length), 'cache-control', - 'max-age=0.003', + 'max-age=0.010', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT' ]; @@ -3677,7 +3677,7 @@ describe('Fetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'cache-control': `max-age=0.003`, + 'cache-control': `max-age=0.010`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT' }); @@ -3690,7 +3690,7 @@ describe('Fetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'Cache-Control': 'max-age=0.003', + 'Cache-Control': 'max-age=0.010', 'Last-Modified': 'Mon, 11 Dec 2023 02:00:00 GMT' }); @@ -3737,7 +3737,7 @@ describe('Fetch', () => { ]); }); - it('Updates cache after a failed revalidation with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.003".', async () => { + it('Updates cache after a failed revalidation with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.010".', async () => { const window = new Window({ url: 'https://localhost:8080/' }); const url = '/some/path'; const responseText1 = 'some text'; @@ -3771,7 +3771,7 @@ describe('Fetch', () => { 'content-length', String(responseText2.length), 'cache-control', - 'max-age=0.003', + 'max-age=0.010', 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT' ]; @@ -3859,7 +3859,7 @@ describe('Fetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText2.length), - 'cache-control': 'max-age=0.003', + 'cache-control': 'max-age=0.010', 'last-modified': 'Mon, 11 Dec 2023 02:00:00 GMT' }); @@ -3963,7 +3963,7 @@ describe('Fetch', () => { 'content-length', String(responseText.length), 'cache-control', - 'max-age=0.003', + 'max-age=0.010', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT', 'etag', @@ -4013,7 +4013,7 @@ describe('Fetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'cache-control': `max-age=0.003`, + 'cache-control': `max-age=0.010`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT', etag: etag1 }); @@ -4027,7 +4027,7 @@ describe('Fetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'cache-control': `max-age=0.003`, + 'cache-control': `max-age=0.010`, 'Last-Modified': 'Mon, 11 Dec 2023 02:00:00 GMT', ETag: etag2 }); @@ -4111,7 +4111,7 @@ describe('Fetch', () => { 'content-length', String(responseText2.length), 'cache-control', - 'max-age=0.003', + 'max-age=0.010', 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT', 'etag', @@ -4135,7 +4135,7 @@ describe('Fetch', () => { 'content-length', String(responseText1.length), 'cache-control', - 'max-age=0.003', + 'max-age=0.010', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT', 'etag', @@ -4182,7 +4182,7 @@ describe('Fetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText1.length), - 'cache-control': `max-age=0.003`, + 'cache-control': `max-age=0.010`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT', etag: etag1 }); @@ -4196,7 +4196,7 @@ describe('Fetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText2.length), - 'cache-control': `max-age=0.003`, + 'cache-control': `max-age=0.010`, 'last-modified': 'Mon, 11 Dec 2023 02:00:00 GMT', etag: etag2 }); diff --git a/packages/happy-dom/test/fetch/SyncFetch.test.ts b/packages/happy-dom/test/fetch/SyncFetch.test.ts index c9938447c..999bb4eee 100644 --- a/packages/happy-dom/test/fetch/SyncFetch.test.ts +++ b/packages/happy-dom/test/fetch/SyncFetch.test.ts @@ -2195,7 +2195,7 @@ describe('SyncFetch', () => { expect(requestCount).toBe(1); }); - it('Revalidates cache with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.005".', async () => { + it('Revalidates cache with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.010".', async () => { browserFrame.url = 'https://localhost:8080/'; const url = 'https://localhost:8080/some/path'; @@ -2216,7 +2216,7 @@ describe('SyncFetch', () => { 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT', 'cache-control', - 'max-age=0.005' + 'max-age=0.010' ], data: '' } @@ -2233,7 +2233,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText.length), 'cache-control', - 'max-age=0.005', + 'max-age=0.010', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT' ], @@ -2279,7 +2279,7 @@ describe('SyncFetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'cache-control': `max-age=0.005`, + 'cache-control': `max-age=0.010`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT' }); @@ -2292,7 +2292,7 @@ describe('SyncFetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'Cache-Control': 'max-age=0.005', + 'Cache-Control': 'max-age=0.010', 'Last-Modified': 'Mon, 11 Dec 2023 02:00:00 GMT' }); @@ -2329,7 +2329,7 @@ describe('SyncFetch', () => { ]); }); - it('Updates cache after a failed revalidation with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.005".', async () => { + it('Updates cache after a failed revalidation with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.010".', async () => { browserFrame.url = 'https://localhost:8080/'; const url = 'https://localhost:8080/some/path'; @@ -2353,7 +2353,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText2.length), 'cache-control', - 'max-age=0.005', + 'max-age=0.010', 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT' ], @@ -2372,7 +2372,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText1.length), 'cache-control', - 'max-age=0.005', + 'max-age=0.010', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT' ], @@ -2426,7 +2426,7 @@ describe('SyncFetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText1.length), - 'cache-control': `max-age=0.005`, + 'cache-control': `max-age=0.010`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT' }); @@ -2439,7 +2439,7 @@ describe('SyncFetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText2.length), - 'cache-control': 'max-age=0.005', + 'cache-control': 'max-age=0.010', 'last-modified': 'Mon, 11 Dec 2023 02:00:00 GMT' }); @@ -2519,7 +2519,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText.length), 'cache-control', - 'max-age=0.005', + 'max-age=0.010', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT', 'etag', @@ -2575,7 +2575,7 @@ describe('SyncFetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'cache-control': `max-age=0.005`, + 'cache-control': `max-age=0.010`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT', etag: etag1 }); @@ -2589,7 +2589,7 @@ describe('SyncFetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'cache-control': `max-age=0.005`, + 'cache-control': `max-age=0.010`, 'Last-Modified': 'Mon, 11 Dec 2023 02:00:00 GMT', ETag: etag2 }); @@ -2653,7 +2653,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText2.length), 'cache-control', - 'max-age=0.005', + 'max-age=0.010', 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT', 'etag', @@ -2674,7 +2674,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText1.length), 'cache-control', - 'max-age=0.005', + 'max-age=0.010', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT', 'etag', @@ -2722,7 +2722,7 @@ describe('SyncFetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText1.length), - 'cache-control': `max-age=0.005`, + 'cache-control': `max-age=0.010`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT', etag: etag1 }); @@ -2736,7 +2736,7 @@ describe('SyncFetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText2.length), - 'cache-control': `max-age=0.005`, + 'cache-control': `max-age=0.010`, 'last-modified': 'Mon, 11 Dec 2023 02:00:00 GMT', etag: etag2 }); diff --git a/packages/happy-dom/test/nodes/document/Document.test.ts b/packages/happy-dom/test/nodes/document/Document.test.ts index fb44b19f3..12d3c4ee6 100644 --- a/packages/happy-dom/test/nodes/document/Document.test.ts +++ b/packages/happy-dom/test/nodes/document/Document.test.ts @@ -1285,13 +1285,9 @@ describe('Document', () => { it('Creates a Processing Instruction node with target & data.', () => { const instruction = document.createProcessingInstruction('foo', 'bar'); expect(instruction instanceof ProcessingInstruction).toBe(true); - expect(instruction).toEqual( - expect.objectContaining({ - target: 'foo', - data: 'bar', - ownerDocument: document - }) - ); + expect(instruction.target).toBe('foo'); + expect(instruction.data).toBe('bar'); + expect(instruction.ownerDocument).toBe(document); }); it('Throws an exception if target is invalid".', () => { diff --git a/packages/happy-dom/test/nodes/node/Node.test.ts b/packages/happy-dom/test/nodes/node/Node.test.ts index 2340b0add..39d1eeb3a 100644 --- a/packages/happy-dom/test/nodes/node/Node.test.ts +++ b/packages/happy-dom/test/nodes/node/Node.test.ts @@ -12,6 +12,7 @@ import EventPhaseEnum from '../../../src/event/EventPhaseEnum.js'; import ErrorEvent from '../../../src/event/events/ErrorEvent.js'; import { beforeEach, describe, it, expect } from 'vitest'; import IShadowRoot from '../../../src/nodes/shadow-root/IShadowRoot.js'; +import NodeFactory from '../../../src/nodes/NodeFactory.js'; describe('Node', () => { let window: IWindow; @@ -126,13 +127,13 @@ describe('Node', () => { describe('get nodeValue()', () => { it('Returns null.', () => { - expect(new Node().nodeValue).toBe(null); + expect(NodeFactory.createNode(document, Node).nodeValue).toBe(null); }); }); describe('get nodeName()', () => { it('Returns emptry string.', () => { - expect(new Node().nodeName).toBe(''); + expect(NodeFactory.createNode(document, Node).nodeName).toBe(''); }); }); From 689f93584588854bb039449f8a7ef40da572835c Mon Sep 17 00:00:00 2001 From: David Ortner Date: Sat, 13 Jan 2024 16:10:32 +0100 Subject: [PATCH 2/2] #1207@trivial: Fixes unit tests. --- packages/happy-dom/test/fetch/Fetch.test.ts | 30 ++++++++-------- .../happy-dom/test/fetch/SyncFetch.test.ts | 34 +++++++++---------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/happy-dom/test/fetch/Fetch.test.ts b/packages/happy-dom/test/fetch/Fetch.test.ts index 446e2aff9..4c94ccab7 100644 --- a/packages/happy-dom/test/fetch/Fetch.test.ts +++ b/packages/happy-dom/test/fetch/Fetch.test.ts @@ -3585,7 +3585,7 @@ describe('Fetch', () => { expect(requestCount).toBe(1); }); - it('Revalidates cache with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.010".', async () => { + it('Revalidates cache with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.020".', async () => { const window = new Window({ url: 'https://localhost:8080/' }); const url = 'https://localhost:8080/some/path'; const responseText = 'some text'; @@ -3612,7 +3612,7 @@ describe('Fetch', () => { 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT', 'cache-control', - 'max-age=0.010' + 'max-age=0.020' ]; callback(response); @@ -3632,7 +3632,7 @@ describe('Fetch', () => { 'content-length', String(responseText.length), 'cache-control', - 'max-age=0.010', + 'max-age=0.020', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT' ]; @@ -3677,7 +3677,7 @@ describe('Fetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'cache-control': `max-age=0.010`, + 'cache-control': `max-age=0.020`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT' }); @@ -3690,7 +3690,7 @@ describe('Fetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'Cache-Control': 'max-age=0.010', + 'Cache-Control': 'max-age=0.020', 'Last-Modified': 'Mon, 11 Dec 2023 02:00:00 GMT' }); @@ -3737,7 +3737,7 @@ describe('Fetch', () => { ]); }); - it('Updates cache after a failed revalidation with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.010".', async () => { + it('Updates cache after a failed revalidation with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.020".', async () => { const window = new Window({ url: 'https://localhost:8080/' }); const url = '/some/path'; const responseText1 = 'some text'; @@ -3771,7 +3771,7 @@ describe('Fetch', () => { 'content-length', String(responseText2.length), 'cache-control', - 'max-age=0.010', + 'max-age=0.020', 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT' ]; @@ -3859,7 +3859,7 @@ describe('Fetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText2.length), - 'cache-control': 'max-age=0.010', + 'cache-control': 'max-age=0.020', 'last-modified': 'Mon, 11 Dec 2023 02:00:00 GMT' }); @@ -3963,7 +3963,7 @@ describe('Fetch', () => { 'content-length', String(responseText.length), 'cache-control', - 'max-age=0.010', + 'max-age=0.020', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT', 'etag', @@ -4013,7 +4013,7 @@ describe('Fetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'cache-control': `max-age=0.010`, + 'cache-control': `max-age=0.020`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT', etag: etag1 }); @@ -4027,7 +4027,7 @@ describe('Fetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'cache-control': `max-age=0.010`, + 'cache-control': `max-age=0.020`, 'Last-Modified': 'Mon, 11 Dec 2023 02:00:00 GMT', ETag: etag2 }); @@ -4111,7 +4111,7 @@ describe('Fetch', () => { 'content-length', String(responseText2.length), 'cache-control', - 'max-age=0.010', + 'max-age=0.020', 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT', 'etag', @@ -4135,7 +4135,7 @@ describe('Fetch', () => { 'content-length', String(responseText1.length), 'cache-control', - 'max-age=0.010', + 'max-age=0.020', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT', 'etag', @@ -4182,7 +4182,7 @@ describe('Fetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText1.length), - 'cache-control': `max-age=0.010`, + 'cache-control': `max-age=0.020`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT', etag: etag1 }); @@ -4196,7 +4196,7 @@ describe('Fetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText2.length), - 'cache-control': `max-age=0.010`, + 'cache-control': `max-age=0.020`, 'last-modified': 'Mon, 11 Dec 2023 02:00:00 GMT', etag: etag2 }); diff --git a/packages/happy-dom/test/fetch/SyncFetch.test.ts b/packages/happy-dom/test/fetch/SyncFetch.test.ts index 999bb4eee..5b7346c2b 100644 --- a/packages/happy-dom/test/fetch/SyncFetch.test.ts +++ b/packages/happy-dom/test/fetch/SyncFetch.test.ts @@ -2195,7 +2195,7 @@ describe('SyncFetch', () => { expect(requestCount).toBe(1); }); - it('Revalidates cache with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.010".', async () => { + it('Revalidates cache with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.020".', async () => { browserFrame.url = 'https://localhost:8080/'; const url = 'https://localhost:8080/some/path'; @@ -2216,7 +2216,7 @@ describe('SyncFetch', () => { 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT', 'cache-control', - 'max-age=0.010' + 'max-age=0.020' ], data: '' } @@ -2233,7 +2233,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText.length), 'cache-control', - 'max-age=0.010', + 'max-age=0.020', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT' ], @@ -2279,7 +2279,7 @@ describe('SyncFetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'cache-control': `max-age=0.010`, + 'cache-control': `max-age=0.020`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT' }); @@ -2292,7 +2292,7 @@ describe('SyncFetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'Cache-Control': 'max-age=0.010', + 'Cache-Control': 'max-age=0.020', 'Last-Modified': 'Mon, 11 Dec 2023 02:00:00 GMT' }); @@ -2329,7 +2329,7 @@ describe('SyncFetch', () => { ]); }); - it('Updates cache after a failed revalidation with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.010".', async () => { + it('Updates cache after a failed revalidation with a "If-Modified-Since" request for a GET response with "Cache-Control" set to "max-age=0.020".', async () => { browserFrame.url = 'https://localhost:8080/'; const url = 'https://localhost:8080/some/path'; @@ -2353,7 +2353,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText2.length), 'cache-control', - 'max-age=0.010', + 'max-age=0.020', 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT' ], @@ -2372,7 +2372,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText1.length), 'cache-control', - 'max-age=0.010', + 'max-age=0.020', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT' ], @@ -2426,7 +2426,7 @@ describe('SyncFetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText1.length), - 'cache-control': `max-age=0.010`, + 'cache-control': `max-age=0.020`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT' }); @@ -2439,7 +2439,7 @@ describe('SyncFetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText2.length), - 'cache-control': 'max-age=0.010', + 'cache-control': 'max-age=0.020', 'last-modified': 'Mon, 11 Dec 2023 02:00:00 GMT' }); @@ -2519,7 +2519,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText.length), 'cache-control', - 'max-age=0.010', + 'max-age=0.020', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT', 'etag', @@ -2575,7 +2575,7 @@ describe('SyncFetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'cache-control': `max-age=0.010`, + 'cache-control': `max-age=0.020`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT', etag: etag1 }); @@ -2589,7 +2589,7 @@ describe('SyncFetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText.length), - 'cache-control': `max-age=0.010`, + 'cache-control': `max-age=0.020`, 'Last-Modified': 'Mon, 11 Dec 2023 02:00:00 GMT', ETag: etag2 }); @@ -2653,7 +2653,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText2.length), 'cache-control', - 'max-age=0.010', + 'max-age=0.020', 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT', 'etag', @@ -2674,7 +2674,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText1.length), 'cache-control', - 'max-age=0.010', + 'max-age=0.020', 'last-modified', 'Mon, 11 Dec 2023 01:00:00 GMT', 'etag', @@ -2722,7 +2722,7 @@ describe('SyncFetch', () => { expect(headers1).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText1.length), - 'cache-control': `max-age=0.010`, + 'cache-control': `max-age=0.020`, 'last-modified': 'Mon, 11 Dec 2023 01:00:00 GMT', etag: etag1 }); @@ -2736,7 +2736,7 @@ describe('SyncFetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText2.length), - 'cache-control': `max-age=0.010`, + 'cache-control': `max-age=0.020`, 'last-modified': 'Mon, 11 Dec 2023 02:00:00 GMT', etag: etag2 });