Skip to content

Commit

Permalink
fix(user-interaction): EventTarget is undefined in IE (#627)
Browse files Browse the repository at this point in the history
* fix(user-interaction): EventTarget is undefined in IE

* fix: lint

* fix: lint 2

* simulate IE in test

* lint

* Move code around until it's no longer affected by double enable call

* lint

would be nice if local errors were anywhere close to CI-s output but no

Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com>
Co-authored-by: Bartlomiej Obecny <bobecny@gmail.com>
  • Loading branch information
3 people authored Oct 12, 2021
1 parent 25c0e30 commit 5a00bed
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,24 @@ export class UserInteractionInstrumentation extends InstrumentationBase<unknown>
};
}

/**
* Most browser provide event listener api via EventTarget in prototype chain.
* Exception to this is IE 11 which has it on the prototypes closest to EventTarget:
*
* * - has addEventListener in IE
* ** - has addEventListener in all other browsers
* ! - missing in IE
*
* HTMLElement -> Element -> Node * -> EventTarget **! -> Object
* Document -> Node * -> EventTarget **! -> Object
* Window * -> WindowProperties ! -> EventTarget **! -> Object
*/
private _getPatchableEventTargets(): EventTarget[] {
return window.EventTarget
? [EventTarget.prototype]
: [Node.prototype, Window.prototype];
}

/**
* Patches the history api
*/
Expand Down Expand Up @@ -571,26 +589,27 @@ export class UserInteractionInstrumentation extends InstrumentationBase<unknown>
);
} else {
this._zonePatched = false;
if (isWrapped(EventTarget.prototype.addEventListener)) {
this._unwrap(EventTarget.prototype, 'addEventListener');
api.diag.debug('removing previous patch from method addEventListener');
}
if (isWrapped(EventTarget.prototype.removeEventListener)) {
this._unwrap(EventTarget.prototype, 'removeEventListener');
api.diag.debug(
'removing previous patch from method removeEventListener'
const targets = this._getPatchableEventTargets();
targets.forEach(target => {
if (isWrapped(target.addEventListener)) {
this._unwrap(target, 'addEventListener');
api.diag.debug(
'removing previous patch from method addEventListener'
);
}
if (isWrapped(target.removeEventListener)) {
this._unwrap(target, 'removeEventListener');
api.diag.debug(
'removing previous patch from method removeEventListener'
);
}
this._wrap(target, 'addEventListener', this._patchAddEventListener());
this._wrap(
target,
'removeEventListener',
this._patchRemoveEventListener()
);
}
this._wrap(
EventTarget.prototype,
'addEventListener',
this._patchAddEventListener()
);
this._wrap(
EventTarget.prototype,
'removeEventListener',
this._patchRemoveEventListener()
);
});
}

this._patchHistoryApi();
Expand Down Expand Up @@ -619,12 +638,15 @@ export class UserInteractionInstrumentation extends InstrumentationBase<unknown>
this._unwrap(ZoneWithPrototype.prototype, 'cancelTask');
}
} else {
if (isWrapped(HTMLElement.prototype.addEventListener)) {
this._unwrap(HTMLElement.prototype, 'addEventListener');
}
if (isWrapped(HTMLElement.prototype.removeEventListener)) {
this._unwrap(HTMLElement.prototype, 'removeEventListener');
}
const targets = this._getPatchableEventTargets();
targets.forEach(target => {
if (isWrapped(target.addEventListener)) {
this._unwrap(target, 'addEventListener');
}
if (isWrapped(target.removeEventListener)) {
this._unwrap(target, 'removeEventListener');
}
});
}
this._unpatchHistoryApi();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -619,5 +619,42 @@ describe('UserInteractionInstrumentation', () => {
'go should be unwrapped'
);
});

describe('simulate IE', () => {
// Save window.EventTarget reference (including enumerable state)
const EventTargetDesc = Object.getOwnPropertyDescriptor(
window,
'EventTarget'
)!;
before(() => {
// @ts-expect-error window.EventTarget not optional
delete window.EventTarget;
});
after(() => {
Object.defineProperty(window, 'EventTarget', EventTargetDesc);
// Undo unwrap putting originals back on it's targets
// @ts-expect-error event listener API not optional
delete Node.prototype.addEventListener;
// @ts-expect-error copy
delete Node.prototype.removeEventListener;
// @ts-expect-error copy
delete Window.prototype.addEventListener;
// @ts-expect-error copy
delete Window.prototype.removeEventListener;
});

it('works with missing EventTarget', () => {
/*
* Would already error out with:
* "before each" hook for "works with missing EventTarget"
* ReferenceError: EventTarget is not defined
*/

fakeInteraction();
assert.equal(exportSpy.args.length, 1, 'should export one span');
const spanClick = exportSpy.args[0][0][0];
assertClickSpan(spanClick);
});
});
});
});

0 comments on commit 5a00bed

Please sign in to comment.