Skip to content

Commit

Permalink
capricorn86#308@trivial: Refactor and add tests on NamedNodeMap.
Browse files Browse the repository at this point in the history
  • Loading branch information
jledentu committed Oct 18, 2022
1 parent 167cabf commit fbb7e74
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 18 deletions.
@@ -1,4 +1,4 @@
import IAttr from './IAttr';
import IAttr from '../nodes/attr/IAttr';

export type INamedNodeMapProps = {
readonly length: number;
Expand Down
@@ -1,5 +1,5 @@
import type Element from '../element/Element';
import IAttr from './IAttr';
import type Element from '../nodes/element/Element';
import IAttr from '../nodes/attr/IAttr';
import type { INamedNodeMapProps } from './INamedNodeMap';

/**
Expand All @@ -8,15 +8,21 @@ import type { INamedNodeMapProps } from './INamedNodeMap';
* Reference: https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap.
*/
export default class NamedNodeMap implements INamedNodeMapProps {
private _element: Element;
/**
* Reference to the element.
*/
private _ownerElement: Element;

/**
* Constructor.
*
* @param element Associated element.
*/
constructor(element: Element) {
Object.defineProperty(this, '_element', { enumerable: false, writable: true, value: element });
Object.defineProperty(this, '_ownerElement', {
enumerable: false,
value: element
});
}

/**
Expand All @@ -32,7 +38,7 @@ export default class NamedNodeMap implements INamedNodeMapProps {
* Length.
*/
public get length(): number {
return Object.keys(this._element._attributes).length;
return Object.keys(this._ownerElement._attributes).length;
}

/**
Expand All @@ -44,7 +50,7 @@ export default class NamedNodeMap implements INamedNodeMapProps {
if (index < 0) {
return null;
}
const attr = Object.values(this._element._attributes)[index];
const attr = Object.values(this._ownerElement._attributes)[index];
return attr ? attr : null;
}

Expand All @@ -54,7 +60,7 @@ export default class NamedNodeMap implements INamedNodeMapProps {
* @param qualifiedName Name.
*/
public getNamedItem(qualifiedName: string): IAttr | null {
return this._element.getAttributeNode(qualifiedName);
return this._ownerElement.getAttributeNode(qualifiedName);
}

/**
Expand All @@ -64,7 +70,7 @@ export default class NamedNodeMap implements INamedNodeMapProps {
* @param localName Local name of the attribute.
*/
public getNamedItemNS(namespace: string, localName: string): IAttr | null {
return this._element.getAttributeNodeNS(namespace, localName);
return this._ownerElement.getAttributeNodeNS(namespace, localName);
}

/**
Expand All @@ -74,7 +80,7 @@ export default class NamedNodeMap implements INamedNodeMapProps {
* @returns Replaced attribute.
*/
public setNamedItem(attr: IAttr): IAttr {
return this._element.setAttributeNode(attr);
return this._ownerElement.setAttributeNode(attr);
}

/**
Expand All @@ -84,7 +90,7 @@ export default class NamedNodeMap implements INamedNodeMapProps {
* @returns Replaced attribute.
*/
public setNamedItemNS(attr: IAttr): IAttr {
return this._element.setAttributeNodeNS(attr);
return this._ownerElement.setAttributeNodeNS(attr);
}

/**
Expand All @@ -97,7 +103,7 @@ export default class NamedNodeMap implements INamedNodeMapProps {
const attr = this.getNamedItem(qualifiedName);

if (attr) {
this._element.removeAttributeNode(attr);
this._ownerElement.removeAttributeNode(attr);
}
return attr;
}
Expand All @@ -113,7 +119,7 @@ export default class NamedNodeMap implements INamedNodeMapProps {
const attr = this.getNamedItemNS(namespace, localName);

if (attr) {
this._element.removeAttributeNode(attr);
this._ownerElement.removeAttributeNode(attr);
}
return attr;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/happy-dom/src/nodes/element/Element.ts
@@ -1,7 +1,7 @@
import Node from '../node/Node';
import ShadowRoot from '../shadow-root/ShadowRoot';
import Attr from '../attr/Attr';
import NamedNodeMap from '../attr/NamedNodeMap';
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';
Expand All @@ -27,7 +27,7 @@ import IText from '../text/IText';
import IDOMRectList from './IDOMRectList';
import DOMRectListFactory from './DOMRectListFactory';
import IAttr from '../attr/IAttr';
import INamedNodeMap from '../attr/INamedNodeMap';
import INamedNodeMap from '../../named-node-map/INamedNodeMap';

import Event from '../../event/Event';

Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/nodes/element/IElement.ts
@@ -1,6 +1,6 @@
import IShadowRoot from '../shadow-root/IShadowRoot';
import IAttr from '../attr/IAttr';
import INamedNodeMap from '../attr/INamedNodeMap';
import INamedNodeMap from '../../named-node-map/INamedNodeMap';
import DOMRect from './DOMRect';
import IDOMTokenList from '../../dom-token-list/IDOMTokenList';
import INode from './../node/INode';
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/window/IWindow.ts
Expand Up @@ -95,7 +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 '../nodes/attr/NamedNodeMap';
import NamedNodeMap from '../named-node-map/NamedNodeMap';
import { URLSearchParams } from 'url';
import { Performance } from 'perf_hooks';
import IElement from '../nodes/element/IElement';
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/window/Window.ts
Expand Up @@ -108,7 +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 '../nodes/attr/NamedNodeMap';
import NamedNodeMap from '../named-node-map/NamedNodeMap';
import IElement from '../nodes/element/IElement';
import ProcessingInstruction from '../nodes/processing-instruction/ProcessingInstruction';

Expand Down
124 changes: 124 additions & 0 deletions packages/happy-dom/test/named-node-map/NamedNodeMap.test.ts
@@ -0,0 +1,124 @@
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 length()', () => {
it('Is an integer representing the number of objects stored in the object.', () => {
element.setAttribute('key1', 'value1');
element.setAttribute('key2', 'value2');

expect(attributes.length).toBe(2);

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 = attributes.getNamedItem('key');
attr.value = 'value2';

attributes.setNamedItem(attr);

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 = attributes.getNamedItemNS('namespace', 'key');
attr.value = 'value2';

attributes.setNamedItemNS(attr);

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');
attributes.removeNamedItem('key');

expect(element.getAttribute('key')).toBe(null);
});
});
});

0 comments on commit fbb7e74

Please sign in to comment.