Skip to content

Commit

Permalink
Merge branch 'master' into htmltimeelement
Browse files Browse the repository at this point in the history
  • Loading branch information
capricorn86 authored May 14, 2024
2 parents 3bd1db4 + 909069b commit a7aa4d7
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 25 deletions.
10 changes: 8 additions & 2 deletions packages/happy-dom/src/mutation-observer/MutationListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default class MutationListener {
#window: BrowserWindow;
#observer: MutationObserver;
#callback: (record: MutationRecord[], observer: MutationObserver) => void;
#records: MutationRecord[] = [];
#records: MutationRecord[] | null = [];
#immediate: NodeJS.Immediate | null = null;

/**
Expand Down Expand Up @@ -46,13 +46,19 @@ export default class MutationListener {
* @param record Record.
*/
public report(record: MutationRecord): void {
if (!this.#records) {
return;
}

this.#records.push(record);

if (this.#immediate) {
this.#window.cancelAnimationFrame(this.#immediate);
}

this.#immediate = this.#window.requestAnimationFrame(() => {
const records = this.#records;
if (records.length > 0) {
if (records?.length > 0) {
this.#records = [];
this.#callback(records, this.#observer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import NamedNodeMap from '../../named-node-map/NamedNodeMap.js';
import HTMLAnchorElementNamedNodeMap from './HTMLAnchorElementNamedNodeMap.js';
import Event from '../../event/Event.js';
import EventPhaseEnum from '../../event/EventPhaseEnum.js';
import PointerEvent from '../../event/events/PointerEvent.js';
import MouseEvent from '../../event/events/MouseEvent.js';

/**
* HTML Anchor Element.
Expand Down Expand Up @@ -499,7 +499,7 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLHyper

if (
event.type === 'click' &&
event instanceof PointerEvent &&
event instanceof MouseEvent &&
(event.eventPhase === EventPhaseEnum.atTarget ||
event.eventPhase === EventPhaseEnum.bubbling) &&
!event.defaultPrevented
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import HTMLLabelElement from '../html-label-element/HTMLLabelElement.js';
import Node from '../node/Node.js';
import NodeList from '../node/NodeList.js';
import HTMLButtonElementNamedNodeMap from './HTMLButtonElementNamedNodeMap.js';
import PointerEvent from '../../event/events/PointerEvent.js';
import { URL } from 'url';
import MouseEvent from '../../event/events/MouseEvent.js';

const BUTTON_TYPES = ['submit', 'reset', 'button', 'menu'];

Expand Down Expand Up @@ -295,7 +295,7 @@ export default class HTMLButtonElement extends HTMLElement {
public override dispatchEvent(event: Event): boolean {
if (
event.type === 'click' &&
event instanceof PointerEvent &&
event instanceof MouseEvent &&
event.eventPhase === EventPhaseEnum.none &&
this.disabled
) {
Expand All @@ -306,7 +306,7 @@ export default class HTMLButtonElement extends HTMLElement {

if (
event.type === 'click' &&
event instanceof PointerEvent &&
event instanceof MouseEvent &&
(event.eventPhase === EventPhaseEnum.atTarget ||
event.eventPhase === EventPhaseEnum.bubbling) &&
this[PropertySymbol.isConnected]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import HTMLInputElementDateUtility from './HTMLInputElementDateUtility.js';
import HTMLLabelElementUtility from '../html-label-element/HTMLLabelElementUtility.js';
import NamedNodeMap from '../../named-node-map/NamedNodeMap.js';
import HTMLInputElementNamedNodeMap from './HTMLInputElementNamedNodeMap.js';
import PointerEvent from '../../event/events/PointerEvent.js';
import { URL } from 'url';
import MouseEvent from '../../event/events/MouseEvent.js';

/**
* HTML Input Element.
Expand Down Expand Up @@ -1305,7 +1305,7 @@ export default class HTMLInputElement extends HTMLElement {
// Do nothing if the input element is disabled and the event is a click event.
if (
event.type === 'click' &&
event instanceof PointerEvent &&
event instanceof MouseEvent &&
event.eventPhase === EventPhaseEnum.none &&
this.disabled
) {
Expand All @@ -1320,7 +1320,7 @@ export default class HTMLInputElement extends HTMLElement {
(event.eventPhase === EventPhaseEnum.atTarget ||
event.eventPhase === EventPhaseEnum.bubbling) &&
event.type === 'click' &&
event instanceof PointerEvent
event instanceof MouseEvent
) {
const inputType = this.type;
if (inputType === 'checkbox' || inputType === 'radio') {
Expand All @@ -1336,7 +1336,7 @@ export default class HTMLInputElement extends HTMLElement {
(event.eventPhase === EventPhaseEnum.atTarget ||
event.eventPhase === EventPhaseEnum.bubbling) &&
event.type === 'click' &&
event instanceof PointerEvent &&
event instanceof MouseEvent &&
this[PropertySymbol.isConnected]
) {
const inputType = this.type;
Expand Down Expand Up @@ -1364,7 +1364,7 @@ export default class HTMLInputElement extends HTMLElement {
(event.eventPhase === EventPhaseEnum.atTarget ||
event.eventPhase === EventPhaseEnum.bubbling) &&
event.type === 'click' &&
event instanceof PointerEvent &&
event instanceof MouseEvent &&
previousCheckedValue !== null
) {
const inputType = this.type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as PropertySymbol from '../../PropertySymbol.js';
import HTMLFormElement from '../html-form-element/HTMLFormElement.js';
import Event from '../../event/Event.js';
import EventPhaseEnum from '../../event/EventPhaseEnum.js';
import PointerEvent from '../../event/events/PointerEvent.js';
import MouseEvent from '../../event/events/MouseEvent.js';

/**
* HTML Label Element.
Expand Down Expand Up @@ -77,12 +77,12 @@ export default class HTMLLabelElement extends HTMLElement {

if (
event.type === 'click' &&
event instanceof PointerEvent &&
event instanceof MouseEvent &&
(event.eventPhase === EventPhaseEnum.atTarget || event.eventPhase === EventPhaseEnum.bubbling)
) {
const control = this.control;
if (control && event.target !== control) {
control.dispatchEvent(new PointerEvent('click', { bubbles: true, cancelable: true }));
control.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
}
}

Expand Down
35 changes: 31 additions & 4 deletions packages/happy-dom/test/mutation-observer/MutationObserver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,13 +307,40 @@ describe('MutationObserver', () => {
const observer = new MutationObserver((mutationRecords) => {
records = mutationRecords;
});
observer.observe(div, { attributes: true });
const span = document.createElement('span');
const text = document.createTextNode('old');

window.close();
span.appendChild(text);
div.appendChild(span);

div.setAttribute('attr', 'value');
document.body.appendChild(div);

await new Promise((resolve) => setTimeout(resolve, 1));
observer.observe(div, {
attributes: true,
childList: true,
subtree: true,
characterData: true,
attributeOldValue: true,
characterDataOldValue: true
});

text.textContent = 'new1';
div.setAttribute('attr', 'value1');

await Promise.all([
window.happyDOM.close(),
(async () => {
text.textContent = 'new2';
div.setAttribute('attr', 'value2');
})(),
new Promise((resolve) => setTimeout(resolve, 10))
]);

text.textContent = 'new3';
div.removeChild(span);
div.setAttribute('attr', 'value3');

await new Promise((resolve) => setTimeout(resolve, 10));

expect(records).toEqual([]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Request from '../../../src/fetch/Request.js';
import Response from '../../../src/fetch/Response.js';
import Fetch from '../../../src/fetch/Fetch.js';
import Browser from '../../../src/browser/Browser.js';
import MouseEvent from '../../../src/event/events/MouseEvent.js';

describe('HTMLAnchorElement', () => {
let window: Window;
Expand Down Expand Up @@ -347,7 +348,7 @@ describe('HTMLAnchorElement', () => {
const element = <HTMLAnchorElement>window.document.createElement('a');
element.href = 'https://www.example.com';
window.document.body.appendChild(element);
element.dispatchEvent(new PointerEvent('click'));
element.dispatchEvent(new MouseEvent('click'));

const newWindow = page.mainFrame.window;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import HTMLElement from '../../../src/nodes/html-element/HTMLElement.js';
import HTMLFormElement from '../../../src/nodes/html-form-element/HTMLFormElement.js';
import ValidityState from '../../../src/validity-state/ValidityState.js';
import { beforeEach, afterEach, describe, it, expect } from 'vitest';
import MouseEvent from '../../../src/event/events/MouseEvent.js';
import PointerEvent from '../../../src/event/events/PointerEvent.js';

describe('HTMLButtonElement', () => {
let window: Window;
Expand Down Expand Up @@ -352,7 +354,7 @@ describe('HTMLButtonElement', () => {
submitter = (<SubmitEvent>event).submitter;
});

button.click();
button.dispatchEvent(new MouseEvent('click'));

expect(submitTriggeredCount).toBe(1);
expect(submitter).toBe(button);
Expand All @@ -376,7 +378,7 @@ describe('HTMLButtonElement', () => {
submitter = (<SubmitEvent>event).submitter;
});

button.click();
button.dispatchEvent(new PointerEvent('click'));

expect(submitTriggeredCount).toBe(1);
expect(submitter).toBe(button);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import DOMExceptionNameEnum from '../../../src/exception/DOMExceptionNameEnum.js
import SubmitEvent from '../../../src/event/events/SubmitEvent.js';
import { beforeEach, describe, it, expect } from 'vitest';
import PointerEvent from '../../../src/event/events/PointerEvent.js';
import MouseEvent from '../../../src/event/events/MouseEvent.js';

describe('HTMLInputElement', () => {
let window: Window;
Expand Down Expand Up @@ -1150,22 +1151,22 @@ describe('HTMLInputElement', () => {
element.addEventListener('change', () => (isChangeTriggered = true));

// "input" and "change" events should only be triggered if connected to DOM
element.dispatchEvent(new PointerEvent('click'));
element.dispatchEvent(new MouseEvent('click'));

expect(isInputTriggered).toBe(false);
expect(isChangeTriggered).toBe(false);
expect(element.checked).toBe(true);

document.body.appendChild(element);

element.dispatchEvent(new PointerEvent('click'));
element.dispatchEvent(new MouseEvent('click'));

// "input" and "change" events should now have been triggered as it is connected to DOM
expect(isInputTriggered).toBe(true);
expect(isChangeTriggered).toBe(true);
expect(element.checked).toBe(false);

element.dispatchEvent(new PointerEvent('click'));
element.dispatchEvent(new MouseEvent('click'));

expect(element.checked).toBe(true);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import HTMLLabelElement from '../../../src/nodes/html-label-element/HTMLLabelEle
import HTMLInputElement from '../../../src/nodes/html-input-element/HTMLInputElement.js';
import PointerEvent from '../../../src/event/events/PointerEvent.js';
import { beforeEach, describe, it, expect } from 'vitest';
import MouseEvent from '../../../src/event/events/MouseEvent.js';

describe('HTMLLabelElement', () => {
let window: Window;
Expand Down Expand Up @@ -103,5 +104,29 @@ describe('HTMLLabelElement', () => {
expect(labelClickCount).toBe(2);
expect(inputClickCount).toBe(1);
});

it('Supports MouseEvent.', () => {
const input = <HTMLInputElement>document.createElement('input');
const span = document.createElement('span');

input.type = 'checkbox';

span.appendChild(input);
element.appendChild(span);

let labelClickCount = 0;
let inputClickCount = 0;

element.addEventListener('click', () => labelClickCount++);
input.addEventListener('click', () => inputClickCount++);

expect(input.checked).toBe(false);

element.dispatchEvent(new MouseEvent('click'));

expect(input.checked).toBe(true);
expect(labelClickCount).toBe(2);
expect(inputClickCount).toBe(1);
});
});
});

0 comments on commit a7aa4d7

Please sign in to comment.