diff --git a/packages/happy-dom/src/named-node-map/INamedNodeMap.ts b/packages/happy-dom/src/named-node-map/INamedNodeMap.ts new file mode 100644 index 000000000..a0a904fc8 --- /dev/null +++ b/packages/happy-dom/src/named-node-map/INamedNodeMap.ts @@ -0,0 +1,70 @@ +import IAttr from '../nodes/attr/IAttr'; + +/** + * NamedNodeMap. + * + * Reference: + * https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap. + */ +export default interface INamedNodeMap extends Iterable { + [index: number]: IAttr; + [Symbol.toStringTag]: string; + readonly length: number; + + /** + * Returns attribute by index. + * + * @param index Index. + */ + item: (index: number) => IAttr; + + /** + * Returns attribute by name. + * + * @param qualifiedName Name. + * @returns Attribute. + */ + getNamedItem: (qualifiedName: string) => IAttr; + + /** + * Returns attribute by name and namespace. + * + * @param namespace Namespace. + * @param localName Local name of the attribute. + * @returns Attribute. + */ + getNamedItemNS: (namespace: string, localName: string) => IAttr; + + /** + * Adds a new attribute node. + * + * @param attr Attribute. + * @returns Replaced attribute. + */ + setNamedItem: (attr: IAttr) => IAttr; + + /** + * Adds a new namespaced attribute node. + * + * @param attr Attribute. + * @returns Replaced attribute. + */ + setNamedItemNS: (attr: IAttr) => IAttr; + + /** + * Removes an attribute. + * + * @param qualifiedName Name of the attribute. + * @returns Removed attribute. + */ + removeNamedItem: (qualifiedName: string) => IAttr; + + /** + * Removes a namespaced attribute. + * + * @param namespace Namespace. + * @param localName Local name of the attribute. + * @returns Removed attribute. + */ + removeNamedItemNS: (namespace: string, localName: string) => IAttr; +} diff --git a/packages/happy-dom/src/named-node-map/NamedNodeMap.ts b/packages/happy-dom/src/named-node-map/NamedNodeMap.ts new file mode 100644 index 000000000..19b18968a --- /dev/null +++ b/packages/happy-dom/src/named-node-map/NamedNodeMap.ts @@ -0,0 +1,145 @@ +import type Element from '../nodes/element/Element'; +import IAttr from '../nodes/attr/IAttr'; +import INamedNodeMap from './INamedNodeMap'; + +/** + * NamedNodeMap. + * + * Reference: + * https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap. + */ +export default class NamedNodeMap implements INamedNodeMap { + [index: number]: IAttr; + + /** + * Reference to the element. + */ + #ownerElement: Element; + + /** + * Constructor. + * + * @param element Associated element. + */ + constructor(element: Element) { + this.#ownerElement = element; + } + + /** + * Returns string. + * + * @returns string. + */ + public get [Symbol.toStringTag](): string { + return this.constructor.name; + } + + /** + * Length. + * + * @returns Length. + */ + public get length(): number { + return Object.keys(this.#ownerElement._attributes).length; + } + + /** + * Returns attribute by index. + * + * @param index Index. + */ + public item(index: number): IAttr | null { + if (index < 0) { + return null; + } + const attr = Object.values(this.#ownerElement._attributes)[index]; + return attr ? attr : null; + } + + /** + * Returns attribute by name. + * + * @param qualifiedName Name. + * @returns Attribute. + */ + public getNamedItem(qualifiedName: string): IAttr | null { + return this.#ownerElement.getAttributeNode(qualifiedName); + } + + /** + * Returns attribute by name and namespace. + * + * @param namespace Namespace. + * @param localName Local name of the attribute. + * @returns Attribute. + */ + public getNamedItemNS(namespace: string, localName: string): IAttr | null { + return this.#ownerElement.getAttributeNodeNS(namespace, localName); + } + + /** + * Adds a new attribute node. + * + * @param attr Attribute. + * @returns Replaced attribute. + */ + public setNamedItem(attr: IAttr): IAttr { + return this.#ownerElement.setAttributeNode(attr); + } + + /** + * Adds a new namespaced attribute node. + * + * @param attr Attribute. + * @returns Replaced attribute. + */ + public setNamedItemNS(attr: IAttr): IAttr { + return this.#ownerElement.setAttributeNodeNS(attr); + } + + /** + * Removes an attribute. + * + * @param qualifiedName Name of the attribute. + * @returns Removed attribute. + */ + public removeNamedItem(qualifiedName: string): IAttr | null { + const attr = this.getNamedItem(qualifiedName); + + if (attr) { + this.#ownerElement.removeAttributeNode(attr); + } + return attr; + } + + /** + * Removes a namespaced attribute. + * + * @param namespace Namespace. + * @param localName Local name of the attribute. + * @returns Removed attribute. + */ + public removeNamedItemNS(namespace: string, localName: string): IAttr | null { + const attr = this.getNamedItemNS(namespace, localName); + + if (attr) { + this.#ownerElement.removeAttributeNode(attr); + } + return attr; + } + + /** + * Iterator. + * + * @returns Iterator. + */ + public [Symbol.iterator](): Iterator { + let index = -1; + return { + next: () => { + index++; + return { value: this.item(index), done: index >= this.length }; + } + }; + } +} diff --git a/packages/happy-dom/src/nodes/element/Element.ts b/packages/happy-dom/src/nodes/element/Element.ts index e41f4c9f6..0e90932d0 100644 --- a/packages/happy-dom/src/nodes/element/Element.ts +++ b/packages/happy-dom/src/nodes/element/Element.ts @@ -1,6 +1,7 @@ import Node from '../node/Node'; import ShadowRoot from '../shadow-root/ShadowRoot'; import Attr from '../attr/Attr'; +import NamedNodeMap from '../../named-node-map/NamedNodeMap'; import DOMRect from './DOMRect'; import DOMTokenList from '../../dom-token-list/DOMTokenList'; import IDOMTokenList from '../../dom-token-list/IDOMTokenList'; @@ -26,6 +27,8 @@ import IText from '../text/IText'; import IDOMRectList from './IDOMRectList'; import DOMRectListFactory from './DOMRectListFactory'; import IAttr from '../attr/IAttr'; +import INamedNodeMap from '../../named-node-map/INamedNodeMap'; + import Event from '../../event/Event'; /** @@ -249,11 +252,8 @@ export default class Element extends Node implements IElement { * * @returns Attributes. */ - public get attributes(): { [k: string | number]: IAttr } & { length: number } { - const attributes = Object.values(this._attributes); - return Object.assign({}, this._attributes, attributes, { - length: attributes.length - }); + public get attributes(): INamedNodeMap { + return Object.assign(new NamedNodeMap(this), Object.values(this._attributes), this._attributes); } /** diff --git a/packages/happy-dom/src/nodes/element/IElement.ts b/packages/happy-dom/src/nodes/element/IElement.ts index 20c6c7656..7649cd0bf 100644 --- a/packages/happy-dom/src/nodes/element/IElement.ts +++ b/packages/happy-dom/src/nodes/element/IElement.ts @@ -1,5 +1,6 @@ import IShadowRoot from '../shadow-root/IShadowRoot'; import IAttr from '../attr/IAttr'; +import INamedNodeMap from '../../named-node-map/INamedNodeMap'; import DOMRect from './DOMRect'; import IDOMTokenList from '../../dom-token-list/IDOMTokenList'; import INode from './../node/INode'; @@ -29,7 +30,7 @@ export default interface IElement extends IChildNode, INonDocumentTypeChildNode, slot: string; readonly nodeName: string; readonly localName: string; - readonly attributes: { [k: string | number]: IAttr } & { length: number }; + readonly attributes: INamedNodeMap; // Events oncancel: (event: Event) => void | null; diff --git a/packages/happy-dom/src/nodes/node/NodeUtility.ts b/packages/happy-dom/src/nodes/node/NodeUtility.ts index 12e91da93..0cafdcf0a 100644 --- a/packages/happy-dom/src/nodes/node/NodeUtility.ts +++ b/packages/happy-dom/src/nodes/node/NodeUtility.ts @@ -148,8 +148,8 @@ export default class NodeUtility { * @param elementB */ public static attributeListsEqual(elementA: IElement, elementB: IElement): boolean { - const listA = Object.values(elementA.attributes); - const listB = Object.values(elementB.attributes); + const listA = >Object.values(elementA['_attributes']); + const listB = >Object.values(elementB['_attributes']); const lengthA = listA.length; const lengthB = listB.length; diff --git a/packages/happy-dom/src/window/IWindow.ts b/packages/happy-dom/src/window/IWindow.ts index 3d9a61eb9..94dee772f 100644 --- a/packages/happy-dom/src/window/IWindow.ts +++ b/packages/happy-dom/src/window/IWindow.ts @@ -95,6 +95,7 @@ import MediaQueryList from '../match-media/MediaQueryList'; import DOMRect from '../nodes/element/DOMRect'; import Window from './Window'; import Attr from '../nodes/attr/Attr'; +import NamedNodeMap from '../named-node-map/NamedNodeMap'; import { URLSearchParams } from 'url'; import { Performance } from 'perf_hooks'; import IElement from '../nodes/element/IElement'; @@ -134,6 +135,7 @@ export default interface IWindow extends IEventTarget, NodeJS.Global { readonly HTMLBaseElement: typeof HTMLBaseElement; readonly HTMLDialogElement: typeof HTMLDialogElement; readonly Attr: typeof Attr; + readonly NamedNodeMap: typeof NamedNodeMap; readonly SVGSVGElement: typeof SVGSVGElement; readonly SVGElement: typeof SVGElement; readonly Image: typeof Image; diff --git a/packages/happy-dom/src/window/Window.ts b/packages/happy-dom/src/window/Window.ts index 5120800a6..ac23a14f1 100644 --- a/packages/happy-dom/src/window/Window.ts +++ b/packages/happy-dom/src/window/Window.ts @@ -108,6 +108,7 @@ import { Buffer } from 'buffer'; import Base64 from '../base64/Base64'; import IDocument from '../nodes/document/IDocument'; import Attr from '../nodes/attr/Attr'; +import NamedNodeMap from '../named-node-map/NamedNodeMap'; import IElement from '../nodes/element/IElement'; import ProcessingInstruction from '../nodes/processing-instruction/ProcessingInstruction'; @@ -167,6 +168,7 @@ export default class Window extends EventTarget implements IWindow { public readonly HTMLBaseElement = HTMLBaseElement; public readonly HTMLDialogElement = HTMLDialogElement; public readonly Attr = Attr; + public readonly NamedNodeMap = NamedNodeMap; public readonly SVGSVGElement = SVGSVGElement; public readonly SVGElement = SVGElement; public readonly Text = Text; diff --git a/packages/happy-dom/test/named-node-map/NamedNodeMap.test.ts b/packages/happy-dom/test/named-node-map/NamedNodeMap.test.ts new file mode 100644 index 000000000..fefa0a226 --- /dev/null +++ b/packages/happy-dom/test/named-node-map/NamedNodeMap.test.ts @@ -0,0 +1,154 @@ +import IWindow from '../../src/window/IWindow'; +import Window from '../../src/window/Window'; +import IDocument from '../../src/nodes/document/IDocument'; +import IElement from '../../src/nodes/element/IElement'; +import INamedNodeMap from 'src/named-node-map/INamedNodeMap'; + +describe('NamedNodeMap', () => { + let window: IWindow; + let document: IDocument; + let element: IElement; + let attributes: INamedNodeMap; + + beforeEach(() => { + window = new Window(); + document = window.document; + element = document.createElement('div'); + attributes = element.attributes; + }); + + describe('get toString()', () => { + it('Returns a stirng.', () => { + expect(attributes.toString()).toBe('[object NamedNodeMap]'); + }); + }); + + describe('get toString()', () => { + it('Returns a stirng.', () => { + expect(attributes.toString()).toBe('[object NamedNodeMap]'); + }); + }); + + describe('Symbol.iterator()', () => { + it('Handles being an iterator.', () => { + element.setAttribute('key1', 'value1'); + element.setAttribute('key2', 'value2'); + + const attributeList = []; + + for (const attribute of attributes) { + attributeList.push(attribute); + } + + expect(attributes.length).toBe(2); + expect(attributeList[0].name).toBe('key1'); + expect(attributeList[0].value).toBe('value1'); + expect(attributeList[1].name).toBe('key2'); + expect(attributeList[1].value).toBe('value2'); + + element.setAttribute('key3', 'value3'); + + expect(attributes.length).toBe(3); + }); + }); + + describe('item()', () => { + it('Returns an attribute by index.', () => { + element.setAttribute('key1', 'value1'); + element.setAttribute('key2', 'value2'); + + expect(attributes.item(0).name).toBe('key1'); + expect(attributes.item(0).value).toBe('value1'); + expect(attributes.item(1).name).toBe('key2'); + expect(attributes.item(1).value).toBe('value2'); + }); + }); + + describe('getNamedItem()', () => { + it('Returns an attribute by name.', () => { + element.setAttribute('key1', 'value1'); + element.setAttribute('key2', 'value2'); + + expect(attributes.getNamedItem('key1').name).toBe('key1'); + expect(attributes.getNamedItem('key1').value).toBe('value1'); + expect(attributes.getNamedItem('key2').name).toBe('key2'); + expect(attributes.getNamedItem('key2').value).toBe('value2'); + }); + }); + + describe('getNamedItemNS()', () => { + it('Returns an attribute by name.', () => { + element.setAttributeNS('namespace', 'key1', 'value1'); + element.setAttributeNS('namespace', 'key2', 'value2'); + + expect(attributes.getNamedItemNS('namespace', 'key1').name).toBe('key1'); + expect(attributes.getNamedItemNS('namespace', 'key1').value).toBe('value1'); + expect(attributes.getNamedItemNS('namespace', 'key2').name).toBe('key2'); + expect(attributes.getNamedItemNS('namespace', 'key2').value).toBe('value2'); + }); + }); + + describe('setNamedItem()', () => { + it('Adds an attribute when not existing.', () => { + element.setAttribute('key', 'value'); + const attr = attributes.removeNamedItem('key'); + + expect(attributes.getNamedItem('key')).toBe(null); + + attributes.setNamedItem(attr); + + expect(attributes.getNamedItem('key')).toBe(attr); + }); + + it('Replaces an attribute when existing.', () => { + element.setAttribute('key', 'value1'); + const attr = document.createAttributeNS('namespace', 'key'); + attr.value = 'value2'; + + const replaced = attributes.setNamedItem(attr); + + expect(replaced.name).toBe('key'); + expect(replaced.value).toBe('value1'); + expect(attributes.getNamedItem('key')).toBe(attr); + expect(element.getAttribute('key')).toBe('value2'); + }); + }); + + describe('setNamedItemNS()', () => { + it('Adds an namespaced attribute when not existing.', () => { + element.setAttributeNS('namespace', 'key', 'value'); + const attr = attributes.removeNamedItemNS('namespace', 'key'); + + attributes.setNamedItemNS(attr); + + expect(attributes.getNamedItem('key')).toBe(attr); + expect(element.getAttributeNS('namespace', 'key')).toBe('value'); + }); + + it('Replaces an attribute when existing.', () => { + element.setAttributeNS('namespace', 'key', 'value1'); + const attr = document.createAttributeNS('namespace', 'key'); + attr.value = 'value2'; + + const replaced = attributes.setNamedItemNS(attr); + + expect(replaced.name).toBe('key'); + expect(replaced.value).toBe('value1'); + + expect(attributes.getNamedItemNS('namespace', 'key')).toBe(attr); + expect(element.getAttributeNS('namespace', 'key')).toBe('value2'); + }); + }); + + describe('removeNamedItem()', () => { + it('Removes an attribute from the list.', () => { + element.setAttribute('key', 'value'); + const removed = attributes.removeNamedItem('key'); + + expect(removed.name).toBe('key'); + expect(removed.value).toBe('value'); + + expect(element.getAttribute('key')).toBe(null); + }); + }); +}); diff --git a/packages/happy-dom/test/nodes/element/Element.test.ts b/packages/happy-dom/test/nodes/element/Element.test.ts index f35e87e1d..cdb0ca826 100644 --- a/packages/happy-dom/test/nodes/element/Element.test.ts +++ b/packages/happy-dom/test/nodes/element/Element.test.ts @@ -267,26 +267,26 @@ describe('Element', () => { expect(element.attributes[2].ownerElement === element).toBe(true); expect(element.attributes[2].ownerDocument === document).toBe(true); - expect(element.attributes.key1.name).toBe('key1'); - expect(element.attributes.key1.value).toBe('value1'); - expect(element.attributes.key1.namespaceURI).toBe(null); - expect(element.attributes.key1.specified).toBe(true); - expect(element.attributes.key1.ownerElement === element).toBe(true); - expect(element.attributes.key1.ownerDocument === document).toBe(true); - - expect(element.attributes.key2.name).toBe('key2'); - expect(element.attributes.key2.value).toBe('value2'); - expect(element.attributes.key2.namespaceURI).toBe(null); - expect(element.attributes.key2.specified).toBe(true); - expect(element.attributes.key2.ownerElement === element).toBe(true); - expect(element.attributes.key2.ownerDocument === document).toBe(true); - - expect(element.attributes.key3.name).toBe('key3'); - expect(element.attributes.key3.value).toBe('value3'); - expect(element.attributes.key3.namespaceURI).toBe(null); - expect(element.attributes.key3.specified).toBe(true); - expect(element.attributes.key3.ownerElement === element).toBe(true); - expect(element.attributes.key3.ownerDocument === document).toBe(true); + expect(element.attributes['key1'].name).toBe('key1'); + expect(element.attributes['key1'].value).toBe('value1'); + expect(element.attributes['key1'].namespaceURI).toBe(null); + expect(element.attributes['key1'].specified).toBe(true); + expect(element.attributes['key1'].ownerElement === element).toBe(true); + expect(element.attributes['key1'].ownerDocument === document).toBe(true); + + expect(element.attributes['key2'].name).toBe('key2'); + expect(element.attributes['key2'].value).toBe('value2'); + expect(element.attributes['key2'].namespaceURI).toBe(null); + expect(element.attributes['key2'].specified).toBe(true); + expect(element.attributes['key2'].ownerElement === element).toBe(true); + expect(element.attributes['key2'].ownerDocument === document).toBe(true); + + expect(element.attributes['key3'].name).toBe('key3'); + expect(element.attributes['key3'].value).toBe('value3'); + expect(element.attributes['key3'].namespaceURI).toBe(null); + expect(element.attributes['key3'].specified).toBe(true); + expect(element.attributes['key3'].ownerElement === element).toBe(true); + expect(element.attributes['key3'].ownerDocument === document).toBe(true); }); }); @@ -1120,19 +1120,19 @@ describe('Element', () => { expect(element.attributes[1].ownerElement === element).toBe(true); expect(element.attributes[1].ownerDocument === document).toBe(true); - expect(element.attributes.key1.name).toBe('key1'); - expect(element.attributes.key1.value).toBe('value1'); - expect(element.attributes.key1.namespaceURI).toBe(null); - expect(element.attributes.key1.specified).toBe(true); - expect(element.attributes.key1.ownerElement === element).toBe(true); - expect(element.attributes.key1.ownerDocument === document).toBe(true); + expect(element.attributes['key1'].name).toBe('key1'); + expect(element.attributes['key1'].value).toBe('value1'); + expect(element.attributes['key1'].namespaceURI).toBe(null); + expect(element.attributes['key1'].specified).toBe(true); + expect(element.attributes['key1'].ownerElement === element).toBe(true); + expect(element.attributes['key1'].ownerDocument === document).toBe(true); - expect(element.attributes.key2.name).toBe('key2'); - expect(element.attributes.key2.value).toBe(''); - expect(element.attributes.key2.namespaceURI).toBe(null); - expect(element.attributes.key2.specified).toBe(true); - expect(element.attributes.key2.ownerElement === element).toBe(true); - expect(element.attributes.key2.ownerDocument === document).toBe(true); + expect(element.attributes['key2'].name).toBe('key2'); + expect(element.attributes['key2'].value).toBe(''); + expect(element.attributes['key2'].namespaceURI).toBe(null); + expect(element.attributes['key2'].specified).toBe(true); + expect(element.attributes['key2'].ownerElement === element).toBe(true); + expect(element.attributes['key2'].ownerDocument === document).toBe(true); }); }); @@ -1212,9 +1212,7 @@ describe('Element', () => { it('Removes an attribute.', () => { element.setAttribute('key1', 'value1'); element.removeAttribute('key1'); - expect(element.attributes).toEqual({ - length: 0 - }); + expect(element.attributes.length).toBe(0); }); }); @@ -1222,9 +1220,7 @@ describe('Element', () => { it('Removes a namespace attribute.', () => { element.setAttributeNS(NAMESPACE_URI, 'global:local', 'value'); element.removeAttributeNS(NAMESPACE_URI, 'local'); - expect(element.attributes).toEqual({ - length: 0 - }); + expect(element.attributes.length).toBe(0); }); }); @@ -1363,19 +1359,19 @@ describe('Element', () => { expect((element.attributes[1]).ownerElement).toBe(element); expect((element.attributes[1]).ownerDocument).toBe(document); - expect((element.attributes.key1).name).toBe('key1'); - expect((element.attributes.key1).namespaceURI).toBe(NamespaceURI.svg); - expect((element.attributes.key1).value).toBe('value1'); - expect((element.attributes.key1).specified).toBe(true); - expect((element.attributes.key1).ownerElement).toBe(element); - expect((element.attributes.key1).ownerDocument).toBe(document); - - expect((element.attributes.key2).name).toBe('key2'); - expect((element.attributes.key2).namespaceURI).toBe(null); - expect((element.attributes.key2).value).toBe('value2'); - expect((element.attributes.key2).specified).toBe(true); - expect((element.attributes.key2).ownerElement).toBe(element); - expect((element.attributes.key2).ownerDocument).toBe(document); + expect((element.attributes['key1']).name).toBe('key1'); + expect((element.attributes['key1']).namespaceURI).toBe(NamespaceURI.svg); + expect((element.attributes['key1']).value).toBe('value1'); + expect((element.attributes['key1']).specified).toBe(true); + expect((element.attributes['key1']).ownerElement).toBe(element); + expect((element.attributes['key1']).ownerDocument).toBe(document); + + expect((element.attributes['key2']).name).toBe('key2'); + expect((element.attributes['key2']).namespaceURI).toBe(null); + expect((element.attributes['key2']).value).toBe('value2'); + expect((element.attributes['key2']).specified).toBe(true); + expect((element.attributes['key2']).ownerElement).toBe(element); + expect((element.attributes['key2']).ownerDocument).toBe(document); }); it('Sets an Attr node on an element.', () => { @@ -1405,19 +1401,19 @@ describe('Element', () => { expect((svg.attributes[1]).ownerElement).toBe(svg); expect((svg.attributes[1]).ownerDocument).toBe(document); - expect((svg.attributes.KEY1).name).toBe('KEY1'); - expect((svg.attributes.KEY1).namespaceURI).toBe(NamespaceURI.svg); - expect((svg.attributes.KEY1).value).toBe('value1'); - expect((svg.attributes.KEY1).specified).toBe(true); - expect((svg.attributes.KEY1).ownerElement).toBe(svg); - expect((svg.attributes.KEY1).ownerDocument).toBe(document); - - expect((svg.attributes.key2).name).toBe('key2'); - expect((svg.attributes.key2).namespaceURI).toBe(null); - expect((svg.attributes.key2).value).toBe('value2'); - expect((svg.attributes.key2).specified).toBe(true); - expect((svg.attributes.key2).ownerElement).toBe(svg); - expect((svg.attributes.key2).ownerDocument).toBe(document); + expect((svg.attributes['KEY1']).name).toBe('KEY1'); + expect((svg.attributes['KEY1']).namespaceURI).toBe(NamespaceURI.svg); + expect((svg.attributes['KEY1']).value).toBe('value1'); + expect((svg.attributes['KEY1']).specified).toBe(true); + expect((svg.attributes['KEY1']).ownerElement).toBe(svg); + expect((svg.attributes['KEY1']).ownerDocument).toBe(document); + + expect((svg.attributes['key2']).name).toBe('key2'); + expect((svg.attributes['key2']).namespaceURI).toBe(null); + expect((svg.attributes['key2']).value).toBe('value2'); + expect((svg.attributes['key2']).specified).toBe(true); + expect((svg.attributes['key2']).ownerElement).toBe(svg); + expect((svg.attributes['key2']).ownerDocument).toBe(document); }); }); } @@ -1492,7 +1488,7 @@ describe('Element', () => { element.setAttributeNode(attribute); element[method](attribute); - expect(element.attributes).toEqual({ length: 0 }); + expect(element.attributes.length).toBe(0); }); }); } diff --git a/packages/happy-dom/test/xml-parser/XMLParser.test.ts b/packages/happy-dom/test/xml-parser/XMLParser.test.ts index 17d213807..e1448b650 100644 --- a/packages/happy-dom/test/xml-parser/XMLParser.test.ts +++ b/packages/happy-dom/test/xml-parser/XMLParser.test.ts @@ -80,25 +80,25 @@ describe('XMLParser', () => { true ); - expect((root.childNodes[0]).attributes.class.name).toBe('class'); - expect((root.childNodes[0]).attributes.class.value).toBe('class1 class2'); - expect((root.childNodes[0]).attributes.class.namespaceURI).toBe(null); - expect((root.childNodes[0]).attributes.class.specified).toBe(true); + expect((root.childNodes[0]).attributes['class'].name).toBe('class'); + expect((root.childNodes[0]).attributes['class'].value).toBe('class1 class2'); + expect((root.childNodes[0]).attributes['class'].namespaceURI).toBe(null); + expect((root.childNodes[0]).attributes['class'].specified).toBe(true); expect( - (root.childNodes[0]).attributes.class.ownerElement === root.childNodes[0] + (root.childNodes[0]).attributes['class'].ownerElement === root.childNodes[0] + ).toBe(true); + expect( + (root.childNodes[0]).attributes['class'].ownerDocument === document ).toBe(true); - expect((root.childNodes[0]).attributes.class.ownerDocument === document).toBe( - true - ); - expect((root.childNodes[0]).attributes.id.name).toBe('id'); - expect((root.childNodes[0]).attributes.id.value).toBe('id'); - expect((root.childNodes[0]).attributes.id.namespaceURI).toBe(null); - expect((root.childNodes[0]).attributes.id.specified).toBe(true); + expect((root.childNodes[0]).attributes['id'].name).toBe('id'); + expect((root.childNodes[0]).attributes['id'].value).toBe('id'); + expect((root.childNodes[0]).attributes['id'].namespaceURI).toBe(null); + expect((root.childNodes[0]).attributes['id'].specified).toBe(true); expect( - (root.childNodes[0]).attributes.id.ownerElement === root.childNodes[0] + (root.childNodes[0]).attributes['id'].ownerElement === root.childNodes[0] ).toBe(true); - expect((root.childNodes[0]).attributes.id.ownerDocument === document).toBe( + expect((root.childNodes[0]).attributes['id'].ownerDocument === document).toBe( true ); @@ -295,33 +295,33 @@ describe('XMLParser', () => { expect(svg.attributes[3].ownerElement === svg).toBe(true); expect(svg.attributes[3].ownerDocument === document).toBe(true); - expect(svg.attributes.viewBox.name).toBe('viewBox'); - expect(svg.attributes.viewBox.value).toBe('0 0 300 100'); - expect(svg.attributes.viewBox.namespaceURI).toBe(null); - expect(svg.attributes.viewBox.specified).toBe(true); - expect(svg.attributes.viewBox.ownerElement === svg).toBe(true); - expect(svg.attributes.viewBox.ownerDocument === document).toBe(true); - - expect(svg.attributes.stroke.name).toBe('stroke'); - expect(svg.attributes.stroke.value).toBe('red'); - expect(svg.attributes.stroke.namespaceURI).toBe(null); - expect(svg.attributes.stroke.specified).toBe(true); - expect(svg.attributes.stroke.ownerElement === svg).toBe(true); - expect(svg.attributes.stroke.ownerDocument === document).toBe(true); - - expect(svg.attributes.fill.name).toBe('fill'); - expect(svg.attributes.fill.value).toBe('grey'); - expect(svg.attributes.fill.namespaceURI).toBe(null); - expect(svg.attributes.fill.specified).toBe(true); - expect(svg.attributes.fill.ownerElement === svg).toBe(true); - expect(svg.attributes.fill.ownerDocument === document).toBe(true); - - expect(svg.attributes.xmlns.name).toBe('xmlns'); - expect(svg.attributes.xmlns.value).toBe(NamespaceURI.html); - expect(svg.attributes.xmlns.namespaceURI).toBe(NamespaceURI.html); - expect(svg.attributes.xmlns.specified).toBe(true); - expect(svg.attributes.xmlns.ownerElement === svg).toBe(true); - expect(svg.attributes.xmlns.ownerDocument === document).toBe(true); + expect(svg.attributes['viewBox'].name).toBe('viewBox'); + expect(svg.attributes['viewBox'].value).toBe('0 0 300 100'); + expect(svg.attributes['viewBox'].namespaceURI).toBe(null); + expect(svg.attributes['viewBox'].specified).toBe(true); + expect(svg.attributes['viewBox'].ownerElement === svg).toBe(true); + expect(svg.attributes['viewBox'].ownerDocument === document).toBe(true); + + expect(svg.attributes['stroke'].name).toBe('stroke'); + expect(svg.attributes['stroke'].value).toBe('red'); + expect(svg.attributes['stroke'].namespaceURI).toBe(null); + expect(svg.attributes['stroke'].specified).toBe(true); + expect(svg.attributes['stroke'].ownerElement === svg).toBe(true); + expect(svg.attributes['stroke'].ownerDocument === document).toBe(true); + + expect(svg.attributes['fill'].name).toBe('fill'); + expect(svg.attributes['fill'].value).toBe('grey'); + expect(svg.attributes['fill'].namespaceURI).toBe(null); + expect(svg.attributes['fill'].specified).toBe(true); + expect(svg.attributes['fill'].ownerElement === svg).toBe(true); + expect(svg.attributes['fill'].ownerDocument === document).toBe(true); + + expect(svg.attributes['xmlns'].name).toBe('xmlns'); + expect(svg.attributes['xmlns'].value).toBe(NamespaceURI.html); + expect(svg.attributes['xmlns'].namespaceURI).toBe(NamespaceURI.html); + expect(svg.attributes['xmlns'].specified).toBe(true); + expect(svg.attributes['xmlns'].ownerElement === svg).toBe(true); + expect(svg.attributes['xmlns'].ownerDocument === document).toBe(true); expect(new XMLSerializer().serializeToString(root).replace(/[\s]/gm, '')).toBe( `