Skip to content

Commit

Permalink
chore: [#1508] Fixes problem where the release build failed
Browse files Browse the repository at this point in the history
  • Loading branch information
capricorn86 committed Aug 28, 2024
1 parent 897a657 commit 9a64097
Show file tree
Hide file tree
Showing 133 changed files with 3,597 additions and 2,463 deletions.
2 changes: 1 addition & 1 deletion packages/global-registrator/src/GlobalRegistrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export default class GlobalRegistrator {
}

// Set owner window on document to global
global.document[PropertySymbol.ownerWindow] = global;
global.document[PropertySymbol.window] = global;
global.document[PropertySymbol.defaultView] = global;
}

Expand Down
17 changes: 10 additions & 7 deletions packages/happy-dom/src/ClassMethodBinder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ export default class ClassMethodBinder {
* @param [options] Options.
* @param [options.bindSymbols] Bind symbol methods.
* @param [options.forwardToPrototype] Forwards the method calls to the prototype. This makes it possible for test tools to override methods on the prototype (e.g. Object.defineProperty(HTMLCollection.prototype, 'item', {})).
* @param [options.proxy] Bind methods using a proxy.
*/
public static bindMethods(
target: Object,
classes: any[],
options?: { bindSymbols?: boolean; forwardToPrototype?: boolean }
options?: { bindSymbols?: boolean; forwardToPrototype?: boolean; proxy?: any }
): void {
for (const _class of classes) {
const propertyDescriptors = Object.getOwnPropertyDescriptors(_class.prototype);
Expand All @@ -26,6 +27,8 @@ export default class ClassMethodBinder {
}
}

const scope = options?.proxy ? options.proxy : target;

if (options?.forwardToPrototype) {
for (const key of keys) {
const descriptor = propertyDescriptors[<string>key];
Expand All @@ -34,11 +37,11 @@ export default class ClassMethodBinder {
...descriptor,
get:
descriptor.get &&
(() => Object.getOwnPropertyDescriptor(_class.prototype, key).get.call(target)),
(() => Object.getOwnPropertyDescriptor(_class.prototype, key).get.call(scope)),
set:
descriptor.set &&
((newValue) =>
Object.getOwnPropertyDescriptor(_class.prototype, key).set.call(target, newValue))
Object.getOwnPropertyDescriptor(_class.prototype, key).set.call(scope, newValue))
});
} else if (
key !== 'constructor' &&
Expand All @@ -47,7 +50,7 @@ export default class ClassMethodBinder {
) {
Object.defineProperty(target, key, {
...descriptor,
value: (...args) => _class.prototype[key].apply(target, args)
value: (...args) => _class.prototype[key].apply(scope, args)
});
}
}
Expand All @@ -57,8 +60,8 @@ export default class ClassMethodBinder {
if (descriptor.get || descriptor.set) {
Object.defineProperty(target, key, {
...descriptor,
get: descriptor.get?.bind(target),
set: descriptor.set?.bind(target)
get: descriptor.get?.bind(scope),
set: descriptor.set?.bind(scope)
});
} else if (
key !== 'constructor' &&
Expand All @@ -67,7 +70,7 @@ export default class ClassMethodBinder {
) {
Object.defineProperty(target, key, {
...descriptor,
value: descriptor.value.bind(target)
value: descriptor.value.bind(scope)
});
}
}
Expand Down
17 changes: 14 additions & 3 deletions packages/happy-dom/src/PropertySymbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export const bodyBuffer = Symbol('bodyBuffer');
export const buffer = Symbol('buffer');
export const cachedResponse = Symbol('cachedResponse');
export const callbacks = Symbol('callbacks');
export const captureEventListenerCount = Symbol('captureEventListenerCount');
export const checked = Symbol('checked');
export const childNodes = Symbol('childNodes');
export const children = Symbol('children');
Expand All @@ -29,7 +28,7 @@ export const evaluateCSS = Symbol('evaluateCSS');
export const evaluateScript = Symbol('evaluateScript');
export const exceptionObserver = Symbol('exceptionObserver');
export const formNode = Symbol('formNode');
export const happyDOMSettingsID = Symbol('happyDOMSettingsID');
export const internalId = Symbol('internalId');
export const height = Symbol('height');
export const immediatePropagationStopped = Symbol('immediatePropagationStopped');
export const isFirstWrite = Symbol('isFirstWrite');
Expand Down Expand Up @@ -102,7 +101,6 @@ export const specified = Symbol('specified');
export const adoptedStyleSheets = Symbol('adoptedStyleSheets');
export const implementation = Symbol('implementation');
export const readyState = Symbol('readyState');
export const ownerWindow = Symbol('ownerWindow');
export const publicId = Symbol('publicId');
export const systemId = Symbol('systemId');
export const validationMessage = Symbol('validationMessage');
Expand Down Expand Up @@ -220,3 +218,16 @@ export const illegalConstructor = Symbol('illegalConstructor');
export const state = Symbol('state');
export const canvas = Symbol('canvas');
export const popoverTargetElement = Symbol('popoverTargetElement');
export const composed = Symbol('composed');
export const bubbles = Symbol('bubbles');
export const cancelable = Symbol('cancelable');
export const defaultPrevented = Symbol('defaultPrevented');
export const eventPhase = Symbol('eventPhase');
export const timeStamp = Symbol('timeStamp');
export const type = Symbol('type');
export const detail = Symbol('detail');
export const globalObject = Symbol('globalObject');
export const destroyed = Symbol('destroyed');
export const aborted = Symbol('aborted');
export const browserFrames = Symbol('browserFrames');
export const windowInternalId = Symbol('windowInternalId');
110 changes: 71 additions & 39 deletions packages/happy-dom/src/async-task-manager/AsyncTaskManager.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import IBrowserFrame from '../browser/types/IBrowserFrame.js';

// We need to set this as a global constant, so that using fake timers in Jest and Vitest won't override this on the global object.
const TIMER = {
setTimeout: globalThis.setTimeout.bind(globalThis),
Expand All @@ -10,12 +12,24 @@ const TIMER = {
*/
export default class AsyncTaskManager {
private static taskID = 0;
private runningTasks: { [k: string]: (destroy: boolean) => void | Promise<void> } = {};
private runningTasks: { [k: string]: (destroy: boolean) => void } = {};
private runningTaskCount = 0;
private runningTimers: NodeJS.Timeout[] = [];
private runningImmediates: NodeJS.Immediate[] = [];
private waitUntilCompleteTimer: NodeJS.Immediate | null = null;
private waitUntilCompleteResolvers: Array<() => void> = [];
private aborted = false;
private destroyed = false;
#browserFrame: IBrowserFrame;

/**
* Constructor.
*
* @param browserFrame Browser frame.
*/
constructor(browserFrame: IBrowserFrame) {
this.#browserFrame = browserFrame;
}

/**
* Returns a promise that is resolved when async tasks are complete.
Expand All @@ -25,21 +39,33 @@ export default class AsyncTaskManager {
public waitUntilComplete(): Promise<void> {
return new Promise((resolve) => {
this.waitUntilCompleteResolvers.push(resolve);
this.endTask(this.startTask());
this.resolveWhenComplete();
});
}

/**
* Aborts all tasks.
*/
public abort(): Promise<void> {
if (this.aborted) {
return new Promise((resolve) => {
this.waitUntilCompleteResolvers.push(resolve);
this.resolveWhenComplete();
});
}
return this.abortAll(false);
}

/**
* Destroys the manager.
*/
public destroy(): Promise<void> {
if (this.aborted) {
return new Promise((resolve) => {
this.waitUntilCompleteResolvers.push(resolve);
this.resolveWhenComplete();
});
}
return this.abortAll(true);
}

Expand All @@ -49,6 +75,10 @@ export default class AsyncTaskManager {
* @param timerID Timer ID.
*/
public startTimer(timerID: NodeJS.Timeout): void {
if (this.aborted) {
TIMER.clearTimeout(timerID);
return;
}
if (this.waitUntilCompleteTimer) {
TIMER.clearTimeout(this.waitUntilCompleteTimer);
this.waitUntilCompleteTimer = null;
Expand All @@ -62,16 +92,16 @@ export default class AsyncTaskManager {
* @param timerID Timer ID.
*/
public endTimer(timerID: NodeJS.Timeout): void {
if (this.waitUntilCompleteTimer) {
TIMER.clearTimeout(this.waitUntilCompleteTimer);
this.waitUntilCompleteTimer = null;
if (this.aborted) {
TIMER.clearTimeout(timerID);
return;
}
const index = this.runningTimers.indexOf(timerID);
if (index !== -1) {
this.runningTimers.splice(index, 1);
}
if (!this.runningTaskCount && !this.runningTimers.length && !this.runningImmediates.length) {
this.resolveWhenComplete();
if (!this.runningTaskCount && !this.runningTimers.length && !this.runningImmediates.length) {
this.resolveWhenComplete();
}
}
}

Expand All @@ -81,6 +111,10 @@ export default class AsyncTaskManager {
* @param immediateID Immediate ID.
*/
public startImmediate(immediateID: NodeJS.Immediate): void {
if (this.aborted) {
TIMER.clearImmediate(immediateID);
return;
}
if (this.waitUntilCompleteTimer) {
TIMER.clearTimeout(this.waitUntilCompleteTimer);
this.waitUntilCompleteTimer = null;
Expand All @@ -94,16 +128,16 @@ export default class AsyncTaskManager {
* @param immediateID Immediate ID.
*/
public endImmediate(immediateID: NodeJS.Immediate): void {
if (this.waitUntilCompleteTimer) {
TIMER.clearTimeout(this.waitUntilCompleteTimer);
this.waitUntilCompleteTimer = null;
if (this.aborted) {
TIMER.clearImmediate(immediateID);
return;
}
const index = this.runningImmediates.indexOf(immediateID);
if (index !== -1) {
this.runningImmediates.splice(index, 1);
}
if (!this.runningTaskCount && !this.runningTimers.length && !this.runningImmediates.length) {
this.resolveWhenComplete();
if (!this.runningTaskCount && !this.runningTimers.length && !this.runningImmediates.length) {
this.resolveWhenComplete();
}
}
}

Expand All @@ -113,7 +147,15 @@ export default class AsyncTaskManager {
* @param abortHandler Abort handler.
* @returns Task ID.
*/
public startTask(abortHandler?: () => void): number {
public startTask(abortHandler?: (destroy?: boolean) => void): number {
if (this.aborted) {
if (abortHandler) {
abortHandler(this.destroyed);
}
throw new this.#browserFrame.window.Error(
`Failed to execute 'startTask()' on 'AsyncTaskManager': The asynchrounous task manager has been aborted.`
);
}
if (this.waitUntilCompleteTimer) {
TIMER.clearTimeout(this.waitUntilCompleteTimer);
this.waitUntilCompleteTimer = null;
Expand All @@ -130,16 +172,15 @@ export default class AsyncTaskManager {
* @param taskID Task ID.
*/
public endTask(taskID: number): void {
if (this.waitUntilCompleteTimer) {
TIMER.clearTimeout(this.waitUntilCompleteTimer);
this.waitUntilCompleteTimer = null;
if (this.aborted) {
return;
}
if (this.runningTasks[taskID]) {
delete this.runningTasks[taskID];
this.runningTaskCount--;
}
if (!this.runningTaskCount && !this.runningTimers.length && !this.runningImmediates.length) {
this.resolveWhenComplete();
if (!this.runningTaskCount && !this.runningTimers.length && !this.runningImmediates.length) {
this.resolveWhenComplete();
}
}
}

Expand Down Expand Up @@ -186,8 +227,9 @@ export default class AsyncTaskManager {
for (const resolver of resolvers) {
resolver();
}
this.aborted = false;
}
});
}, 1);
}

/**
Expand All @@ -200,6 +242,8 @@ export default class AsyncTaskManager {
const runningImmediates = this.runningImmediates;
const runningTasks = this.runningTasks;

this.aborted = true;
this.destroyed = destroy;
this.runningTasks = {};
this.runningTaskCount = 0;
this.runningImmediates = [];
Expand All @@ -218,26 +262,14 @@ export default class AsyncTaskManager {
TIMER.clearTimeout(timer);
}

const taskPromises = [];

for (const key of Object.keys(runningTasks)) {
const returnValue = runningTasks[key](destroy);
if (returnValue instanceof Promise) {
taskPromises.push(returnValue);
}
}

if (taskPromises.length) {
return Promise.all(taskPromises)
.then(() => this.waitUntilComplete())
.catch((error) => {
/* eslint-disable-next-line no-console */
console.error(error);
throw error;
});
runningTasks[key](destroy);
}

// We need to wait for microtasks to complete before resolving.
return this.waitUntilComplete();
return new Promise((resolve) => {
this.waitUntilCompleteResolvers.push(resolve);
this.resolveWhenComplete();
});
}
}
7 changes: 7 additions & 0 deletions packages/happy-dom/src/browser/Browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import IOptionalBrowserSettings from './types/IOptionalBrowserSettings.js';
import BrowserSettingsFactory from './BrowserSettingsFactory.js';
import BrowserPage from './BrowserPage.js';
import IBrowser from './types/IBrowser.js';
import BrowserExceptionObserver from './utilities/BrowserExceptionObserver.js';
import * as PropertySymbol from '../PropertySymbol.js';
import BrowserErrorCaptureEnum from './enums/BrowserErrorCaptureEnum.js';

/**
* Browser.
Expand All @@ -14,6 +17,7 @@ export default class Browser implements IBrowser {
public readonly contexts: BrowserContext[];
public readonly settings: IBrowserSettings;
public readonly console: Console | null;
public [PropertySymbol.exceptionObserver]: BrowserExceptionObserver | null = null;

/**
* Constructor.
Expand All @@ -25,6 +29,9 @@ export default class Browser implements IBrowser {
constructor(options?: { settings?: IOptionalBrowserSettings; console?: Console }) {
this.console = options?.console || null;
this.settings = BrowserSettingsFactory.createSettings(options?.settings);
if (this.settings.errorCapture === BrowserErrorCaptureEnum.processLevel) {
this[PropertySymbol.exceptionObserver] = new BrowserExceptionObserver();
}
this.contexts = [new BrowserContext(this)];
}

Expand Down
Loading

0 comments on commit 9a64097

Please sign in to comment.