From 3518bc0522b6bcc815950dc637f2b3167e8212e4 Mon Sep 17 00:00:00 2001 From: Mas0nShi Date: Tue, 14 Jun 2022 18:40:42 +0800 Subject: [PATCH 1/6] #504@patch: Fixes XmlParser error parse with tags. --- packages/happy-dom/src/xml-parser/XMLParser.ts | 2 +- .../happy-dom/test/xml-parser/XMLParser.test.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/happy-dom/src/xml-parser/XMLParser.ts b/packages/happy-dom/src/xml-parser/XMLParser.ts index a0bdf7142..9b0541f47 100755 --- a/packages/happy-dom/src/xml-parser/XMLParser.ts +++ b/packages/happy-dom/src/xml-parser/XMLParser.ts @@ -11,7 +11,7 @@ import INode from '../nodes/node/INode'; import IElement from '../nodes/element/IElement'; import HTMLLinkElement from '../nodes/html-link-element/HTMLLinkElement'; -const MARKUP_REGEXP = /<(\/?)([a-z][-.0-9_a-z]*)\s*([^>]*?)(\/?)>/gi; +const MARKUP_REGEXP = /<(\/?)([a-z][-.0-9_a-z]*)\s*([^<>]*?)(\/?)>/gi; const COMMENT_REGEXP = /|<([!?])([^>]*)>/gi; const DOCUMENT_TYPE_ATTRIBUTE_REGEXP = /"([^"]+)"/gm; const ATTRIBUTE_REGEXP = /([^\s=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+)))/gms; diff --git a/packages/happy-dom/test/xml-parser/XMLParser.test.ts b/packages/happy-dom/test/xml-parser/XMLParser.test.ts index 85164e4b8..2ae522c00 100644 --- a/packages/happy-dom/test/xml-parser/XMLParser.test.ts +++ b/packages/happy-dom/test/xml-parser/XMLParser.test.ts @@ -185,6 +185,23 @@ describe('XMLParser', () => { `.replace(/[\s]/gm, '') ); + + const root2 = XMLParser.parse( + window.document, + ` + + Title + + + + + ` + ); + expect((root2.children[0].children[1].children[0]).innerText).toBe( + 'var vars = []; for (var i=0;i { From c5052fb726e03094ddb0154643cf6768dc7454b4 Mon Sep 17 00:00:00 2001 From: Mas0nShi Date: Tue, 14 Jun 2022 18:46:05 +0800 Subject: [PATCH 2/6] #504@patch: Continue fixes XmlParser error parse with tags. --- .../happy-dom/test/xml-parser/XMLParser.test.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/happy-dom/test/xml-parser/XMLParser.test.ts b/packages/happy-dom/test/xml-parser/XMLParser.test.ts index 2ae522c00..e55ff23de 100644 --- a/packages/happy-dom/test/xml-parser/XMLParser.test.ts +++ b/packages/happy-dom/test/xml-parser/XMLParser.test.ts @@ -189,19 +189,17 @@ describe('XMLParser', () => { const root2 = XMLParser.parse( window.document, ` - - Title - - - - - ` + + Title + + + + +` ); expect((root2.children[0].children[1].children[0]).innerText).toBe( 'var vars = []; for (var i=0;i { From 63a480093ea8bbac4ea20863781e9beda5b3d2bf Mon Sep 17 00:00:00 2001 From: Mas0nShi Date: Wed, 15 Jun 2022 10:52:58 +0800 Subject: [PATCH 3/6] #451@patch: Fixes missing innerHTML in html-template. --- .../HTMLTemplateElement.ts | 24 ++++++++++++++++++ .../HTMLTemplateElement.test.ts | 25 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 packages/happy-dom/test/nodes/html-template-element/HTMLTemplateElement.test.ts diff --git a/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts b/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts index 075b20743..5eef807d8 100644 --- a/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts +++ b/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts @@ -3,6 +3,7 @@ import HTMLElement from '../html-element/HTMLElement'; import IDocumentFragment from '../document-fragment/IDocumentFragment'; import INode from '../node/INode'; import IHTMLTemplateElement from './IHTMLTemplateElement'; +import XMLSerializer from '../../xml-serializer/XMLSerializer'; /** * HTML Template Element. @@ -13,6 +14,29 @@ import IHTMLTemplateElement from './IHTMLTemplateElement'; export default class HTMLTemplateElement extends HTMLElement implements IHTMLTemplateElement { private _contentElement: IDocumentFragment = null; + /** + * Sets inner HTML. + * + * @param html HTML. + */ + public set innerHTML(html: string) { + super.innerHTML = html; + } + + /** + * Returns inner HTML. + * + * @returns HTML. + */ + public get innerHTML(): string { + const xmlSerializer = new XMLSerializer(); + let xml = ''; + for (const node of this.content.childNodes) { + xml += xmlSerializer.serializeToString(node); + } + return xml; + } + /** * Returns the content. * diff --git a/packages/happy-dom/test/nodes/html-template-element/HTMLTemplateElement.test.ts b/packages/happy-dom/test/nodes/html-template-element/HTMLTemplateElement.test.ts new file mode 100644 index 000000000..0447b9d2d --- /dev/null +++ b/packages/happy-dom/test/nodes/html-template-element/HTMLTemplateElement.test.ts @@ -0,0 +1,25 @@ +import Window from '../../../src/window/Window'; +import Document from '../../../src/nodes/document/Document'; +import HTMLTemplateElement from '../../../src/nodes/html-template-element/HTMLTemplateElement'; + +describe('HTMLTemplateElement', () => { + let window: Window; + let document: Document; + let element: HTMLTemplateElement; + + beforeEach(() => { + window = new Window(); + document = window.document; + element = document.createElement('template'); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('InnerHTML', () => { + const div = '
happy-dom is cool!
'; + element.innerHTML = div; + expect(element.innerHTML).toBe(div); + }); +}); From 157dbcffe56325dc57b8c625ee9eae912ab70d6d Mon Sep 17 00:00:00 2001 From: Mas0nShi Date: Wed, 15 Jun 2022 11:02:07 +0800 Subject: [PATCH 4/6] #451@patch: Continue fixes missing innerHTML in html-template. --- .../nodes/html-template-element/HTMLTemplateElement.ts | 9 ++++++++- .../html-template-element/HTMLTemplateElement.test.ts | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts b/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts index 5eef807d8..f9208b6b1 100644 --- a/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts +++ b/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts @@ -4,6 +4,7 @@ import IDocumentFragment from '../document-fragment/IDocumentFragment'; import INode from '../node/INode'; import IHTMLTemplateElement from './IHTMLTemplateElement'; import XMLSerializer from '../../xml-serializer/XMLSerializer'; +import XMLParser from '../../xml-parser/XMLParser'; /** * HTML Template Element. @@ -20,7 +21,13 @@ export default class HTMLTemplateElement extends HTMLElement implements IHTMLTem * @param html HTML. */ public set innerHTML(html: string) { - super.innerHTML = html; + for (const child of this.content.childNodes.slice()) { + this.content.removeChild(child); + } + + for (const node of XMLParser.parse(this.ownerDocument, html).childNodes.slice()) { + this.content.appendChild(node); + } } /** diff --git a/packages/happy-dom/test/nodes/html-template-element/HTMLTemplateElement.test.ts b/packages/happy-dom/test/nodes/html-template-element/HTMLTemplateElement.test.ts index 0447b9d2d..22d3879c3 100644 --- a/packages/happy-dom/test/nodes/html-template-element/HTMLTemplateElement.test.ts +++ b/packages/happy-dom/test/nodes/html-template-element/HTMLTemplateElement.test.ts @@ -19,7 +19,11 @@ describe('HTMLTemplateElement', () => { it('InnerHTML', () => { const div = '
happy-dom is cool!
'; + expect(element.content.childNodes.length).toBe(0); element.innerHTML = div; expect(element.innerHTML).toBe(div); + expect(element.content.childNodes.length).toBe(1); + element.innerHTML = ''; + expect(element.content.childNodes.length).toBe(0); }); }); From af05c45253c646778f7d9901cf1d248b08cbce11 Mon Sep 17 00:00:00 2001 From: Mas0nShi Date: Thu, 16 Jun 2022 00:13:25 +0800 Subject: [PATCH 5/6] #451@patch: Continue fixes missing innerHTML in html-template. --- .../HTMLTemplateElement.ts | 99 ++++++++----------- 1 file changed, 42 insertions(+), 57 deletions(-) diff --git a/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts b/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts index f9208b6b1..8911a01da 100644 --- a/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts +++ b/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts @@ -3,8 +3,9 @@ import HTMLElement from '../html-element/HTMLElement'; import IDocumentFragment from '../document-fragment/IDocumentFragment'; import INode from '../node/INode'; import IHTMLTemplateElement from './IHTMLTemplateElement'; -import XMLSerializer from '../../xml-serializer/XMLSerializer'; import XMLParser from '../../xml-parser/XMLParser'; +import XMLSerializer from '../../xml-serializer/XMLSerializer'; +import DOMException from '../../exception/DOMException'; /** * HTML Template Element. @@ -13,12 +14,17 @@ import XMLParser from '../../xml-parser/XMLParser'; * https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement. */ export default class HTMLTemplateElement extends HTMLElement implements IHTMLTemplateElement { - private _contentElement: IDocumentFragment = null; + public readonly content: IDocumentFragment = this.ownerDocument.createDocumentFragment(); /** - * Sets inner HTML. - * - * @param html HTML. + * @override + */ + public get innerHTML(): string { + return this.getInnerHTML(); + } + + /** + * @override */ public set innerHTML(html: string) { for (const child of this.content.childNodes.slice()) { @@ -31,116 +37,95 @@ export default class HTMLTemplateElement extends HTMLElement implements IHTMLTem } /** - * Returns inner HTML. - * - * @returns HTML. + * @override */ - public get innerHTML(): string { - const xmlSerializer = new XMLSerializer(); - let xml = ''; - for (const node of this.content.childNodes) { - xml += xmlSerializer.serializeToString(node); - } - return xml; + public get outerHTML(): string { + return new XMLSerializer().serializeToString(this.content); } /** - * Returns the content. - * - * @returns Content. + * @override */ - public get content(): IDocumentFragment { - if (!this._contentElement) { - this._contentElement = this.ownerDocument.createDocumentFragment(); - } - return this._contentElement; + public set outerHTML(_html: string) { + throw new DOMException( + `Failed to set the 'outerHTML' property on 'Element': This element has no parent node.` + ); } /** - * Previous sibling. - * - * @returns Node. + * @override */ public get previousSibling(): INode { return this.content.previousSibling; } /** - * Next sibling. - * - * @returns Node. + * @override */ public get nextSibling(): INode { return this.content.nextSibling; } /** - * First child. - * - * @returns Node. + * @override */ public get firstChild(): INode { return this.content.firstChild; } /** - * Last child. - * - * @returns Node. + * @override */ public get lastChild(): INode { return this.content.lastChild; } /** - * Append a child node to childNodes. - * - * @param node Node to append. - * @returns Appended node. + * @override + */ + public getInnerHTML(options?: { includeShadowRoots?: boolean }): string { + const xmlSerializer = new XMLSerializer(); + let xml = ''; + for (const node of this.content.childNodes) { + xml += xmlSerializer.serializeToString(node, options); + } + return xml; + } + + /** + * @override */ public appendChild(node: INode): INode { return this.content.appendChild(node); } /** - * Remove Child element from childNodes array. - * - * @param node Node to remove. + * @override */ public removeChild(node: Node): INode { return this.content.removeChild(node); } /** - * Inserts a node before another. - * - * @param newNode Node to insert. - * @param referenceNode Node to insert before. - * @returns Inserted node. + * @override */ public insertBefore(newNode: INode, referenceNode: INode): INode { return this.content.insertBefore(newNode, referenceNode); } /** - * Replaces a node with another. - * - * @param newChild New child. - * @param oldChild Old child. - * @returns Replaced node. + * @override */ public replaceChild(newChild: INode, oldChild: INode): INode { return this.content.replaceChild(newChild, oldChild); } /** - * Clones a node. - * * @override - * @param [deep=false] "true" to clone deep. - * @returns Cloned node. */ public cloneNode(deep = false): IHTMLTemplateElement { - return super.cloneNode(deep); + const clone = super.cloneNode(deep); + (clone.content) = this.content.cloneNode(deep); + return clone; } } From f52cb2a0a6753b96f7e188a4a355c7a5ab797fd4 Mon Sep 17 00:00:00 2001 From: Dave Honneffer Date: Sat, 18 Jun 2022 18:35:21 +0200 Subject: [PATCH 6/6] #509@patch: Fix GlobalRegistrator's unregister. --- packages/global-registrator/src/GlobalRegistrator.ts | 6 +++--- packages/global-registrator/test/react/React.test.tsx | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/global-registrator/src/GlobalRegistrator.ts b/packages/global-registrator/src/GlobalRegistrator.ts index 6cc615da5..c0854cf54 100644 --- a/packages/global-registrator/src/GlobalRegistrator.ts +++ b/packages/global-registrator/src/GlobalRegistrator.ts @@ -11,7 +11,7 @@ export default class GlobalRegistrator { */ public static register(): void { if (this.registered.length) { - throw new Error('Failed to registered. Happy DOM has already been globally registered.'); + throw new Error('Failed to register. Happy DOM has already been globally registered.'); } const window = new GlobalWindow(); for (const key of Object.keys(window)) { @@ -26,9 +26,9 @@ export default class GlobalRegistrator { * Registers Happy DOM globally. */ public static unregister(): void { - if (this.registered.length) { + if (!this.registered.length) { throw new Error( - 'Failed to unregistered. Happy DOM has not previously been globally registered.' + 'Failed to unregister. Happy DOM has not previously been globally registered.' ); } for (const key of this.registered) { diff --git a/packages/global-registrator/test/react/React.test.tsx b/packages/global-registrator/test/react/React.test.tsx index 0d371d53c..3130031c0 100644 --- a/packages/global-registrator/test/react/React.test.tsx +++ b/packages/global-registrator/test/react/React.test.tsx @@ -26,3 +26,5 @@ function unmountReactComponent(): void { mountReactComponent(); unmountReactComponent(); + +GlobalRegistrator.unregister();