Skip to content

Commit

Permalink
fix: [#1578] Fixes bug where child nodes of HTMLSelectElement and HTM…
Browse files Browse the repository at this point in the history
…LFormElement had the wrong reference to the parent (#1579)
  • Loading branch information
capricorn86 authored Nov 4, 2024
1 parent 8f74989 commit 38ab960
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default class HTMLSelectElement extends HTMLElement {
public [PropertySymbol.options]: HTMLOptionsCollection | null = null;
public [PropertySymbol.selectedOptions]: HTMLCollection<HTMLOptionElement> | null = null;
public [PropertySymbol.selectedIndex]: number = -1;
public [PropertySymbol.proxy]: HTMLSelectElement;

// Events
public onchange: (event: Event) => void | null = null;
Expand Down Expand Up @@ -195,6 +196,7 @@ export default class HTMLSelectElement extends HTMLElement {
}
});

this[PropertySymbol.proxy] = proxy;
this[PropertySymbol.selectNode] = proxy;

return proxy;
Expand Down
6 changes: 3 additions & 3 deletions packages/happy-dom/src/nodes/node/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ export default class Node extends EventTarget {
node[PropertySymbol.parentNode][PropertySymbol.removeChild](node);
}

node[PropertySymbol.parentNode] = this;
node[PropertySymbol.parentNode] = this[PropertySymbol.proxy] || this;

node[PropertySymbol.clearCache]();

Expand Down Expand Up @@ -647,7 +647,7 @@ export default class Node extends EventTarget {
newNode[PropertySymbol.parentNode][PropertySymbol.removeChild](newNode);
}

newNode[PropertySymbol.parentNode] = this;
newNode[PropertySymbol.parentNode] = this[PropertySymbol.proxy] || this;

newNode[PropertySymbol.clearCache]();

Expand Down Expand Up @@ -1061,7 +1061,7 @@ export default class Node extends EventTarget {
* 2. Let node1 be other and node2 be this.
*/
let node1: Node = otherNode;
let node2: Node = this;
let node2: Node = this[PropertySymbol.proxy] || this;

/**
* 3. Let attr1 and attr2 be null.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,46 @@ describe('HTMLFormElement', () => {
});
});

describe('appendChild()', () => {
it('Sets "parentNode" of child elements to the proxy and not the original element.', () => {
const child = document.createElement('input');
const child2 = document.createElement('textarea');
const child3 = document.createElement('select');

element.appendChild(child);
element.appendChild(child2);
element.appendChild(child3);

expect(child.parentNode).toBe(element);
expect(child2.parentNode).toBe(element);
expect(child3.parentNode).toBe(element);

expect(child.parentElement).toBe(element);
expect(child2.parentElement).toBe(element);
expect(child3.parentElement).toBe(element);
});
});

describe('insertBefore()', () => {
it('Sets "parentNode" of child elements to the proxy and not the original element.', () => {
const child = document.createElement('input');
const child2 = document.createElement('textarea');
const child3 = document.createElement('select');

element.appendChild(child);
element.appendChild(child2);
element.insertBefore(child3, child2);

expect(child.parentNode).toBe(element);
expect(child2.parentNode).toBe(element);
expect(child3.parentNode).toBe(element);

expect(child.parentElement).toBe(element);
expect(child2.parentElement).toBe(element);
expect(child3.parentElement).toBe(element);
});
});

for (const method of ['checkValidity', 'reportValidity']) {
describe(`${method}()`, () => {
it('Validates the form.', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,24 @@ describe('HTMLSelectElement', () => {
expect((<Event>(<unknown>dispatchedEvent)).type).toBe('change');
expect(element.selectedIndex).toBe(0);
});

it('Sets "parentNode" of child elements to the proxy and not the original element.', () => {
const option1 = <HTMLOptionElement>document.createElement('option');
const option2 = <HTMLOptionElement>document.createElement('option');
const option3 = <HTMLOptionElement>document.createElement('option');

element.appendChild(option1);
element.appendChild(option2);
element.appendChild(option3);

expect(option1.parentNode).toBe(element);
expect(option2.parentNode).toBe(element);
expect(option3.parentNode).toBe(element);

expect(option1.parentElement).toBe(element);
expect(option2.parentElement).toBe(element);
expect(option3.parentElement).toBe(element);
});
});

describe(`insertBefore()`, () => {
Expand Down Expand Up @@ -569,6 +587,24 @@ describe('HTMLSelectElement', () => {
expect(element.item(1) === option2).toBe(true);
expect(element.item(2) === option3).toBe(true);
});

it('Sets "parentNode" of child elements to the proxy and not the original element.', () => {
const option1 = <HTMLOptionElement>document.createElement('option');
const option2 = <HTMLOptionElement>document.createElement('option');
const option3 = <HTMLOptionElement>document.createElement('option');

element.appendChild(option1);
element.appendChild(option2);
element.insertBefore(option3, option2);

expect(option1.parentNode).toBe(element);
expect(option2.parentNode).toBe(element);
expect(option3.parentNode).toBe(element);

expect(option1.parentElement).toBe(element);
expect(option2.parentElement).toBe(element);
expect(option3.parentElement).toBe(element);
});
});

describe(`removeChild()`, () => {
Expand Down

0 comments on commit 38ab960

Please sign in to comment.