@@ -93,17 +193,19 @@ function createEventContractMultiContainer(
function createEventContract({
eventContractContainerManager,
+ exportAddA11yClickSupport = false,
eventTypes,
dispatcher,
}: {
eventContractContainerManager: EventContractContainerManager;
+ exportAddA11yClickSupport?: boolean;
eventTypes: Array;
dispatcher?: jasmine.Spy;
}): EventContract {
- const eventContract = new EventContract(
- eventContractContainerManager,
- /* useActionResolver= */ false,
- );
+ const eventContract = new EventContract(eventContractContainerManager);
+ if (exportAddA11yClickSupport) {
+ eventContract.exportAddA11yClickSupport();
+ }
for (const eventType of eventTypes) {
if (typeof eventType === 'string') {
eventContract.addEvent(eventType);
@@ -118,10 +220,6 @@ function createEventContract({
return eventContract;
}
-function createDispatcherSpy() {
- return jasmine.createSpy('dispatcher');
-}
-
function getLastDispatchedEventInfoWrapper(dispatcher: jasmine.Spy): EventInfoWrapper {
return new EventInfoWrapper(dispatcher.calls.mostRecent().args[0]);
}
@@ -170,6 +268,50 @@ function dispatchMouseEvent(
return event;
}
+function dispatchKeyboardEvent(
+ target: Element,
+ {
+ type = 'keydown',
+ key = '',
+ location = 0,
+ ctrlKey = false,
+ altKey = false,
+ shiftKey = false,
+ metaKey = false,
+ }: {
+ type?: string;
+ key?: string;
+ location?: number;
+ ctrlKey?: boolean;
+ altKey?: boolean;
+ shiftKey?: boolean;
+ metaKey?: boolean;
+ } = {},
+) {
+ // createEvent/initKeyboardEvent is used to support IE11
+ // tslint:disable:deprecation
+ const event = document.createEvent('KeyboardEvent');
+ event.initKeyboardEvent(
+ type,
+ true,
+ true,
+ window,
+ key,
+ location,
+ ctrlKey,
+ altKey,
+ shiftKey,
+ metaKey,
+ );
+ // tslint:enable:deprecation
+ // This is necessary as Chrome does not respect the key parameter in
+ // `initKeyboardEvent`.
+ Object.defineProperty(event, 'key', {value: key});
+ spyOn(event, 'preventDefault').and.callThrough();
+ target.dispatchEvent(event);
+ return event;
+}
+
describe('EventContract', () => {
beforeEach(() => {
safeElement.setInnerHtml(document.body, testonlyHtml(domContent));
@@ -187,7 +329,7 @@ describe('EventContract', () => {
const addEventListenerSpy2 = spyOn(container2, 'addEventListener');
const eventContractContainerManager = new EventContractMultiContainer();
- const eventContract = createEventContract({eventContractContainerManager, eventTypes: []});
+ const eventContract = new EventContract(eventContractContainerManager);
eventContract.addEvent('click');
expect(addEventListenerSpy).not.toHaveBeenCalled();
@@ -213,7 +355,7 @@ describe('EventContract', () => {
const addEventListenerSpy2 = spyOn(container2, 'addEventListener');
const eventContractContainerManager = new EventContractMultiContainer();
- const eventContract = createEventContract({eventContractContainerManager, eventTypes: []});
+ const eventContract = new EventContract(eventContractContainerManager);
eventContractContainerManager.addContainer(container);
eventContractContainerManager.addContainer(container2);
@@ -238,7 +380,7 @@ describe('EventContract', () => {
const addEventListenerSpy = spyOn(container, 'addEventListener');
const eventContractContainerManager = new EventContractMultiContainer();
- const eventContract = createEventContract({eventContractContainerManager, eventTypes: []});
+ const eventContract = new EventContract(eventContractContainerManager);
eventContract.addEvent('animationend', 'webkitanimationend');
eventContractContainerManager.addContainer(container);
@@ -250,6 +392,7 @@ describe('EventContract', () => {
it('queues events until dispatcher is registered', () => {
const container = getRequiredElementById('click-container');
+ const actionElement = getRequiredElementById('click-action-element');
const targetElement = getRequiredElementById('click-target-element');
const eventContract = createEventContract({
@@ -259,7 +402,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement);
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
eventContract.registerDispatcher(dispatcher, Restriction.I_AM_THE_JSACTION_FRAMEWORK);
expect(dispatcher).toHaveBeenCalledTimes(1);
@@ -269,15 +412,17 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe(EventType.CLICK);
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
expect(eventInfoWrapper.getIsReplay()).toBe(true);
});
it('dispatches event', () => {
const container = getRequiredElementById('click-container');
+ const actionElement = getRequiredElementById('click-action-element');
const targetElement = getRequiredElementById('click-target-element');
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
createEventContract({
eventContractContainerManager: new EventContractContainer(container),
eventTypes: ['click'],
@@ -293,7 +438,80 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe(EventType.CLICK);
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ expect(eventInfoWrapper.getResolved()).toBe(true);
+ });
+
+ it('dispatches event when targetElement is actionElement', () => {
+ const container = getRequiredElementById('self-click-container');
+ const targetElement = getRequiredElementById('self-click-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click'],
+ dispatcher,
+ });
+
+ const clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe(EventType.CLICK);
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(targetElement);
+ });
+
+ it('dispatch event to child and ignore parent', () => {
+ const container = getRequiredElementById('parent-and-child-container');
+ const actionElement = getRequiredElementById('parent-and-child-action-element');
+ const targetElement = getRequiredElementById('parent-and-child-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click'],
+ dispatcher,
+ });
+
+ const clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('childHandleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('dispatch event through owner', () => {
+ const container = getRequiredElementById('owner-click-container');
+ const actionElement = getRequiredElementById('owner-click-action-element');
+ const targetElement = getRequiredElementById('owner-click-target-element');
+ targetElement[OWNER] = actionElement;
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click'],
+ dispatcher,
+ });
+
+ const clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('ownerHandleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
});
it('dispatches event for `webkitanimationend` alias event type', () => {
@@ -302,9 +520,10 @@ describe('EventContract', () => {
return;
}
const container = getRequiredElementById('animationend-container');
+ const actionElement = getRequiredElementById('animationend-action-element');
const targetElement = getRequiredElementById('animationend-target-element');
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
createEventContract({
eventContractContainerManager: new EventContractContainer(container),
eventTypes: [['animationend', 'webkitanimationend']],
@@ -323,15 +542,234 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe('animationend');
expect(eventInfoWrapper.getEvent()).toBe(animationEndEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleAnimationEnd');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('dispatches modified click event', () => {
+ const container = getRequiredElementById('clickmod-container');
+ const actionElement = getRequiredElementById('clickmod-action-element');
+ const targetElement = getRequiredElementById('clickmod-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click'],
+ dispatcher,
+ });
+
+ const clickEvent = dispatchMouseEvent(targetElement, {shiftKey: true});
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('clickmod');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClickMod');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('caches jsaction attribute', () => {
+ const container = getRequiredElementById('click-container');
+ const actionElement = getRequiredElementById('click-action-element');
+ const targetElement = getRequiredElementById('click-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click'],
+ dispatcher,
+ });
+
+ let clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ let eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+
+ actionElement.setAttribute('jsaction', 'renamedHandleClick');
+ dispatcher.calls.reset();
+
+ clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('re-parses jsaction attribute if the action cache is cleared', () => {
+ const container = getRequiredElementById('click-container');
+ const actionElement = getRequiredElementById('click-action-element');
+ const targetElement = getRequiredElementById('click-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click'],
+ dispatcher,
+ });
+
+ let clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ let eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+
+ actionElement.setAttribute('jsaction', 'renamedHandleClick');
+ // Clear attribute cache.
+ cache.clear(actionElement);
+ dispatcher.calls.reset();
+
+ clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('renamedHandleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('handles trailing semicolon in jsaction attribute', () => {
+ const container = getRequiredElementById('trailing-semicolon-container');
+ const actionElement = getRequiredElementById('trailing-semicolon-action-element');
+ const targetElement = getRequiredElementById('trailing-semicolon-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click'],
+ dispatcher,
+ });
+
+ const clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('handles jsaction attributes without action names, first action', () => {
+ const container = getRequiredElementById('no-action-name-container');
+ const actionElement = getRequiredElementById('no-action-name-action-element');
+ const targetElement = getRequiredElementById('no-action-name-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click', 'keydown', 'keyup'],
+ dispatcher,
+ });
+
+ const keydownEvent = dispatchKeyboardEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('keydown');
+ expect(eventInfoWrapper.getEvent()).toBe(keydownEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('handles jsaction attributes without action names, last action', () => {
+ const container = getRequiredElementById('no-action-name-container');
+ const actionElement = getRequiredElementById('no-action-name-action-element');
+ const targetElement = getRequiredElementById('no-action-name-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click', 'keydown', 'keyup'],
+ dispatcher,
+ });
+
+ const keyupEvent = dispatchKeyboardEvent(targetElement, {type: 'keyup'});
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('keyup');
+ expect(eventInfoWrapper.getEvent()).toBe(keyupEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('does not handle jsaction attributes without event type or action name', () => {
+ const container = getRequiredElementById('no-action-name-container');
+ const targetElement = getRequiredElementById('no-action-name-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click', 'keydown', 'keyup'],
+ dispatcher,
+ });
+
+ const clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
expect(eventInfoWrapper.getAction()).toBeUndefined();
});
+ it('dispatches event from shadow dom', () => {
+ const container = getRequiredElementById('shadow-dom-container');
+ const actionElement = getRequiredElementById('shadow-dom-action-element');
+
+ // Not supported in ie11.
+ if (!actionElement.attachShadow) {
+ return;
+ }
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click'],
+ dispatcher,
+ });
+
+ const shadow = actionElement.attachShadow({mode: 'open'});
+ const shadowChild = document.createElement('div');
+ shadow.appendChild(shadowChild);
+
+ shadowChild.click();
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ // Target element is set to the host from the event.
+ expect(eventInfoWrapper.getTargetElement()).toBe(actionElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
it('cleanUp removes all event listeners and containers', () => {
const container = getRequiredElementById('click-container');
const removeEventListenerSpy = spyOn(container, 'removeEventListener').and.callThrough();
const actionElement = getRequiredElementById('click-action-element');
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
const eventContractContainerManager = new EventContractContainer(container);
const cleanUpSpy = spyOn(eventContractContainerManager, 'cleanUp').and.callThrough();
const eventContract = createEventContract({
@@ -354,9 +792,10 @@ describe('EventContract', () => {
it('exposes event handlers with `handler()`', () => {
const container = getRequiredElementById('click-container');
+ const actionElement = getRequiredElementById('click-action-element');
const targetElement = getRequiredElementById('click-target-element');
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
const eventContract = createEventContract({
eventContractContainerManager: new EventContractContainer(container),
eventTypes: ['click'],
@@ -377,7 +816,8 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe(EventType.CLICK);
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
});
it('has no event handlers with `handler()` for unregistered event type', () => {
@@ -405,23 +845,239 @@ describe('EventContract', () => {
expect(clickEvent.preventDefault).not.toHaveBeenCalled();
});
+ describe('a11y click', () => {
+ beforeEach(() => {
+ EventContract.A11Y_CLICK_SUPPORT = true;
+ });
+
+ it('dispatches keydown as click event', () => {
+ const container = getRequiredElementById('a11y-click-container');
+ const actionElement = getRequiredElementById('a11y-click-action-element');
+ const targetElement = getRequiredElementById('a11y-click-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click'],
+ dispatcher,
+ });
+
+ const keydownEvent = dispatchKeyboardEvent(targetElement, {key: 'Enter'});
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ expect(eventInfoWrapper.getEvent()).toBe(keydownEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('dispatches keydown event', () => {
+ const container = getRequiredElementById('keydown-container');
+ const actionElement = getRequiredElementById('keydown-action-element');
+ const targetElement = getRequiredElementById('keydown-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['keydown'],
+ dispatcher,
+ });
+
+ const keydownEvent = dispatchKeyboardEvent(targetElement, {key: 'a'});
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('keydown');
+ expect(eventInfoWrapper.getEvent()).toBe(keydownEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleKeydown');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('dispatches clickonly event', () => {
+ const container = getRequiredElementById('a11y-clickonly-container');
+ const actionElement = getRequiredElementById('a11y-clickonly-action-element');
+ const targetElement = getRequiredElementById('a11y-clickonly-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click'],
+ dispatcher,
+ });
+
+ const clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('clickonly');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClickOnly');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('dispatches click event to click handler rather than clickonly', () => {
+ const container = getRequiredElementById('a11y-click-clickonly-container');
+ const actionElement = getRequiredElementById('a11y-click-clickonly-action-element');
+ const targetElement = getRequiredElementById('a11y-click-clickonly-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['click'],
+ dispatcher,
+ });
+
+ const clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+ });
+
+ describe('a11y click support deferred', () => {
+ it('dispatches keydown as click event', () => {
+ const container = getRequiredElementById('a11y-click-container');
+ const actionElement = getRequiredElementById('a11y-click-action-element');
+ const targetElement = getRequiredElementById('a11y-click-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ const eventContract = createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ exportAddA11yClickSupport: true,
+ eventTypes: ['click'],
+ dispatcher,
+ });
+ addDeferredA11yClickSupport(eventContract);
+
+ const keydownEvent = dispatchKeyboardEvent(targetElement, {key: 'Enter'});
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ expect(eventInfoWrapper.getEvent()).toBe(keydownEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('dispatches keydown event', () => {
+ const container = getRequiredElementById('keydown-container');
+ const actionElement = getRequiredElementById('keydown-action-element');
+ const targetElement = getRequiredElementById('keydown-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ const eventContract = createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ exportAddA11yClickSupport: true,
+ eventTypes: ['keydown'],
+ dispatcher,
+ });
+ addDeferredA11yClickSupport(eventContract);
+
+ const keydownEvent = dispatchKeyboardEvent(targetElement, {key: 'a'});
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('keydown');
+ expect(eventInfoWrapper.getEvent()).toBe(keydownEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleKeydown');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('dispatches clickonly event', () => {
+ const container = getRequiredElementById('a11y-clickonly-container');
+ const actionElement = getRequiredElementById('a11y-clickonly-action-element');
+ const targetElement = getRequiredElementById('a11y-clickonly-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ const eventContract = createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ exportAddA11yClickSupport: true,
+ eventTypes: ['click'],
+ dispatcher,
+ });
+ addDeferredA11yClickSupport(eventContract);
+
+ const clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('clickonly');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClickOnly');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('dispatches click event to click handler rather than clickonly', () => {
+ const container = getRequiredElementById('a11y-click-clickonly-container');
+ const actionElement = getRequiredElementById('a11y-click-clickonly-action-element');
+ const targetElement = getRequiredElementById('a11y-click-clickonly-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ const eventContract = createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ exportAddA11yClickSupport: true,
+ eventTypes: ['click'],
+ dispatcher,
+ });
+ addDeferredA11yClickSupport(eventContract);
+
+ const clickEvent = dispatchMouseEvent(targetElement);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('click');
+ expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
+ expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+ });
+
describe('nested containers', () => {
let outerContainer: Element;
+ let outerActionElement: Element;
let outerTargetElement: Element;
let innerContainer: Element;
+ let innerActionElement: Element;
let innerTargetElement: Element;
beforeEach(() => {
outerContainer = getRequiredElementById('nested-outer-container');
+ outerActionElement = getRequiredElementById('nested-outer-action-element');
outerTargetElement = getRequiredElementById('nested-outer-target-element');
innerContainer = getRequiredElementById('nested-inner-container');
+ innerActionElement = getRequiredElementById('nested-inner-action-element');
innerTargetElement = getRequiredElementById('nested-inner-target-element');
});
it('dispatches events in outer container', () => {
const documentListener = jasmine.createSpy('documentListener');
window.document.documentElement.addEventListener('click', documentListener);
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
const eventContractContainerManager = createEventContractMultiContainer(outerContainer);
createEventContract({
eventContractContainerManager,
@@ -437,14 +1093,16 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(outerTargetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(eventInfoWrapper.getAction()?.name).toBe('outerHandleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(outerActionElement);
+
expect(documentListener).toHaveBeenCalledTimes(1);
});
it('dispatches events in inner container', () => {
const documentListener = jasmine.createSpy('documentListener');
window.document.documentElement.addEventListener('click', documentListener);
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
const eventContractContainerManager = createEventContractMultiContainer(outerContainer);
createEventContract({
eventContractContainerManager,
@@ -460,14 +1118,16 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(innerTargetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(eventInfoWrapper.getAction()?.name).toBe('innerHandleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(innerActionElement);
+
expect(documentListener).toHaveBeenCalledTimes(1);
});
it('dispatches events in outer container, inner registered first', () => {
const documentListener = jasmine.createSpy('documentListener');
window.document.documentElement.addEventListener('click', documentListener);
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
const eventContractContainerManager = createEventContractMultiContainer(innerContainer);
createEventContract({
eventContractContainerManager,
@@ -483,7 +1143,8 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(outerTargetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(eventInfoWrapper.getAction()?.name).toBe('outerHandleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(outerActionElement);
expect(documentListener).toHaveBeenCalledTimes(1);
});
@@ -491,7 +1152,7 @@ describe('EventContract', () => {
it('dispatches events in inner container, inner container registered first', () => {
const documentListener = jasmine.createSpy('documentListener');
window.document.documentElement.addEventListener('click', documentListener);
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
const eventContractContainerManager = createEventContractMultiContainer(innerContainer);
createEventContract({
eventContractContainerManager,
@@ -507,7 +1168,8 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(innerTargetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(eventInfoWrapper.getAction()?.name).toBe('innerHandleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(innerActionElement);
expect(documentListener).toHaveBeenCalledTimes(1);
});
@@ -515,7 +1177,7 @@ describe('EventContract', () => {
it('dispatches events in inner container, inner container removed', () => {
const documentListener = jasmine.createSpy('documentListener');
window.document.documentElement.addEventListener('click', documentListener);
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
const eventContractContainerManager = createEventContractMultiContainer(outerContainer);
createEventContract({
eventContractContainerManager,
@@ -532,7 +1194,8 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(innerTargetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(eventInfoWrapper.getAction()?.name).toBe('innerHandleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(innerActionElement);
expect(documentListener).toHaveBeenCalledTimes(1);
@@ -548,21 +1211,246 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(innerTargetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(eventInfoWrapper.getAction()?.name).toBe('innerHandleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(innerActionElement);
expect(documentListener).toHaveBeenCalledTimes(1);
});
});
- describe('early events', () => {
- it('early events are dispatched', () => {
- const container = getRequiredElementById('click-container');
- const targetElement = getRequiredElementById('click-target-element');
+ describe('non-bubbling mouse events', () => {
+ beforeEach(() => {
+ EventContract.MOUSE_SPECIAL_SUPPORT = true;
+ });
- const removeEventListenerSpy = spyOn(
+ it('dispatches matching mouseover as mouseenter event', () => {
+ const container = getRequiredElementById('mouseenter-container');
+ const actionElement = getRequiredElementById('mouseenter-action-element');
+ const targetElement = getRequiredElementById('mouseenter-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['mouseenter'],
+ dispatcher,
+ });
+
+ dispatchMouseEvent(targetElement, {
+ type: 'mouseover',
+ // Indicates that the mouse exited the container and entered the
+ // target element.
+ relatedTarget: container,
+ });
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('mouseenter');
+ const syntheticMouseEvent = eventInfoWrapper.getEvent();
+ expect(syntheticMouseEvent.type).toBe('mouseenter');
+ expect(syntheticMouseEvent.target).toBe(actionElement);
+ expect(eventInfoWrapper.getTargetElement()).toBe(actionElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleMouseEnter');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('does not dispatch non-matching mouseover event as mouseenter', () => {
+ const container = getRequiredElementById('mouseenter-container');
+ const actionElement = getRequiredElementById('mouseenter-action-element');
+ const targetElement = getRequiredElementById('mouseenter-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['mouseenter'],
+ dispatcher,
+ });
+
+ dispatchMouseEvent(targetElement, {
+ type: 'mouseover',
+ // Indicates that the mouse exited the action element and entered the
+ // target element.
+ relatedTarget: actionElement,
+ });
+
+ // Global dispatch for the mouseover event still happens.
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ });
+
+ it('dispatches matching mouseout as mouseleave event', () => {
+ const container = getRequiredElementById('mouseleave-container');
+ const actionElement = getRequiredElementById('mouseleave-action-element');
+ const targetElement = getRequiredElementById('mouseleave-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['mouseleave'],
+ dispatcher,
+ });
+
+ dispatchMouseEvent(targetElement, {
+ type: 'mouseout',
+ // Indicates that the mouse entered the container and exited the
+ // target element.
+ relatedTarget: container,
+ });
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('mouseleave');
+ const syntheticMouseEvent = eventInfoWrapper.getEvent();
+ expect(syntheticMouseEvent.type).toBe('mouseleave');
+ expect(syntheticMouseEvent.target).toBe(actionElement);
+ expect(eventInfoWrapper.getTargetElement()).toBe(actionElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleMouseLeave');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('does not dispatch non-matching mouseout event as mouseleave', () => {
+ const container = getRequiredElementById('mouseleave-container');
+ const actionElement = getRequiredElementById('mouseleave-action-element');
+ const targetElement = getRequiredElementById('mouseleave-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['mouseleave'],
+ dispatcher,
+ });
+
+ dispatchMouseEvent(targetElement, {
+ type: 'mouseout',
+ // Indicates that the mouse entered the action element and exited the
+ // target element.
+ relatedTarget: actionElement,
+ });
+
+ // Global dispatch for the mouseout event still happens.
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ });
+
+ it('dispatches matching pointerover as pointerenter event', () => {
+ const container = getRequiredElementById('pointerenter-container');
+ const actionElement = getRequiredElementById('pointerenter-action-element');
+ const targetElement = getRequiredElementById('pointerenter-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['pointerenter'],
+ dispatcher,
+ });
+
+ dispatchMouseEvent(targetElement, {
+ type: 'pointerover',
+ // Indicates that the pointer exited the container and entered the
+ // target element.
+ relatedTarget: container,
+ });
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('pointerenter');
+ const syntheticMouseEvent = eventInfoWrapper.getEvent();
+ expect(syntheticMouseEvent.type).toBe('pointerenter');
+ expect(syntheticMouseEvent.target).toBe(actionElement);
+ expect(eventInfoWrapper.getTargetElement()).toBe(actionElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handlePointerEnter');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('does not dispatch non-matching pointerover event as pointerenter', () => {
+ const container = getRequiredElementById('pointerenter-container');
+ const actionElement = getRequiredElementById('pointerenter-action-element');
+ const targetElement = getRequiredElementById('pointerenter-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['pointerenter'],
+ dispatcher,
+ });
+
+ dispatchMouseEvent(targetElement, {
+ type: 'pointerover',
+ // Indicates that the pointer exited the action element and entered the
+ // target element.
+ relatedTarget: actionElement,
+ });
+
+ // Global dispatch for the pointerover event still happens.
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ });
+
+ it('dispatches matching pointerout as pointerleave event', () => {
+ const container = getRequiredElementById('pointerleave-container');
+ const actionElement = getRequiredElementById('pointerleave-action-element');
+ const targetElement = getRequiredElementById('pointerleave-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['pointerleave'],
+ dispatcher,
+ });
+
+ dispatchMouseEvent(targetElement, {
+ type: 'pointerout',
+ // Indicates that the pointer entered the container and exited the
+ // target element.
+ relatedTarget: container,
+ });
+
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
+ expect(eventInfoWrapper.getEventType()).toBe('pointerleave');
+ const syntheticMouseEvent = eventInfoWrapper.getEvent();
+ expect(syntheticMouseEvent.type).toBe('pointerleave');
+ expect(syntheticMouseEvent.target).toBe(actionElement);
+ expect(eventInfoWrapper.getTargetElement()).toBe(actionElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handlePointerLeave');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
+ });
+
+ it('does not dispatch non-matching pointerout event as pointerleave', () => {
+ const container = getRequiredElementById('pointerleave-container');
+ const actionElement = getRequiredElementById('pointerleave-action-element');
+ const targetElement = getRequiredElementById('pointerleave-target-element');
+
+ const dispatcher = jasmine.createSpy('dispatcher');
+ createEventContract({
+ eventContractContainerManager: new EventContractContainer(container),
+ eventTypes: ['pointerleave'],
+ dispatcher,
+ });
+
+ dispatchMouseEvent(targetElement, {
+ type: 'pointerout',
+ // Indicates that the pointer entered the action element and exited the
+ // target element.
+ relatedTarget: actionElement,
+ });
+
+ // Global dispatch for the pointerout event still happens.
+ expect(dispatcher).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('early events', () => {
+ let removeEventListenerSpy: jasmine.Spy;
+
+ beforeEach(() => {
+ removeEventListenerSpy = spyOn(
window.document.documentElement,
'removeEventListener',
).and.callThrough();
+ });
+
+ it('early events are dispatched', () => {
+ const container = getRequiredElementById('click-container');
+ const actionElement = getRequiredElementById('click-action-element');
+ const targetElement = getRequiredElementById('click-target-element');
+
const earlyEventContract = new EarlyEventContract();
earlyEventContract.addEvents(['click']);
@@ -573,7 +1461,7 @@ describe('EventContract', () => {
expect(earlyJsactionData!.q.length).toBe(1);
expect(earlyJsactionData!.q[0].event).toBe(clickEvent);
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
const eventContract = createEventContract({
eventContractContainerManager: new EventContractContainer(container),
eventTypes: ['click'],
@@ -589,11 +1477,13 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
});
it('early capture events are dispatched', () => {
const container = getRequiredElementById('focus-container');
+ const actionElement = getRequiredElementById('focus-action-element');
const targetElement = getRequiredElementById('focus-target-element');
const replaySink = {_ejsa: undefined};
const removeEventListenerSpy = spyOn(container, 'removeEventListener').and.callThrough();
@@ -608,7 +1498,7 @@ describe('EventContract', () => {
expect(earlyJsactionData!.q.length).toBe(1);
expect(earlyJsactionData!.q[0].event.type).toBe('focus');
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
const eventContract = createEventContract({
eventContractContainerManager: new EventContractContainer(container),
eventTypes: ['focus'],
@@ -624,17 +1514,15 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe('focus');
expect(eventInfoWrapper.getEvent().type).toBe('focus');
expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleFocus');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
});
it('early events are dispatched when target is cleared', () => {
const container = getRequiredElementById('click-container');
+ const actionElement = getRequiredElementById('click-action-element');
const targetElement = getRequiredElementById('click-target-element');
- const removeEventListenerSpy = spyOn(
- window.document.documentElement,
- 'removeEventListener',
- ).and.callThrough();
const earlyEventContract = new EarlyEventContract();
earlyEventContract.addEvents(['click']);
@@ -648,7 +1536,7 @@ describe('EventContract', () => {
// Emulating browser behavior of clearing target after dispatch.
Object.defineProperty(clickEvent, 'target', {value: null});
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
const eventContract = createEventContract({
eventContractContainerManager: new EventContractContainer(container),
eventTypes: ['click'],
@@ -664,25 +1552,20 @@ describe('EventContract', () => {
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleClick');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
});
describe('non-bubbling mouse events', () => {
beforeEach(() => {
EventContract.MOUSE_SPECIAL_SUPPORT = true;
});
- afterEach(() => {
- EventContract.MOUSE_SPECIAL_SUPPORT = false;
- });
it('early mouseout dispatched as mouseleave and mouseout', () => {
const container = getRequiredElementById('mouseleave-container');
+ const actionElement = getRequiredElementById('mouseleave-action-element');
const targetElement = getRequiredElementById('mouseleave-target-element');
- const removeEventListenerSpy = spyOn(
- window.document.documentElement,
- 'removeEventListener',
- ).and.callThrough();
const early = new EarlyEventContract();
early.addEvents(['mouseout']);
@@ -700,7 +1583,7 @@ describe('EventContract', () => {
expect(earlyJsactionData!.q.length).toBe(1);
expect(earlyJsactionData!.q[0].event).toBe(mouseOutEvent);
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
const eventContract = createEventContract({
eventContractContainerManager: new EventContractContainer(container),
eventTypes: ['mouseout', 'mouseleave'],
@@ -715,20 +1598,18 @@ describe('EventContract', () => {
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('mouseleave');
const syntheticMouseEvent = eventInfoWrapper.getEvent();
- expect(syntheticMouseEvent.type).toBe('mouseout');
- expect(syntheticMouseEvent.target).toBe(targetElement);
- expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(syntheticMouseEvent.type).toBe('mouseleave');
+ expect(syntheticMouseEvent.target).toBe(actionElement);
+ expect(eventInfoWrapper.getTargetElement()).toBe(actionElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleMouseLeave');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
});
it('early mouseout dispatched as only mouseleave', () => {
const container = getRequiredElementById('mouseleave-container');
+ const actionElement = getRequiredElementById('mouseleave-action-element');
const targetElement = getRequiredElementById('mouseleave-target-element');
- const removeEventListenerSpy = spyOn(
- window.document.documentElement,
- 'removeEventListener',
- ).and.callThrough();
const early = new EarlyEventContract();
early.addEvents(['mouseout']);
@@ -744,7 +1625,7 @@ describe('EventContract', () => {
expect(earlyJsactionData!.q.length).toBe(1);
expect(earlyJsactionData!.q[0].event).toBe(mouseOutEvent);
- const dispatcher = createDispatcherSpy();
+ const dispatcher = jasmine.createSpy('dispatcher');
const eventContract = createEventContract({
eventContractContainerManager: new EventContractContainer(container),
eventTypes: ['mouseleave'],
@@ -759,10 +1640,11 @@ describe('EventContract', () => {
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('mouseleave');
const syntheticMouseEvent = eventInfoWrapper.getEvent();
- expect(syntheticMouseEvent.type).toBe('mouseout');
- expect(syntheticMouseEvent.target).toBe(targetElement);
- expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
- expect(eventInfoWrapper.getAction()).toBeUndefined();
+ expect(syntheticMouseEvent.type).toBe('mouseleave');
+ expect(syntheticMouseEvent.target).toBe(actionElement);
+ expect(eventInfoWrapper.getTargetElement()).toBe(actionElement);
+ expect(eventInfoWrapper.getAction()?.name).toBe('handleMouseLeave');
+ expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
});
});
});
diff --git a/packages/core/src/event_delegation_utils.ts b/packages/core/src/event_delegation_utils.ts
index 35a773f8440..142f8d9f1f9 100644
--- a/packages/core/src/event_delegation_utils.ts
+++ b/packages/core/src/event_delegation_utils.ts
@@ -78,10 +78,7 @@ export const initGlobalEventDelegation = (
if (injector.get(IS_EVENT_REPLAY_ENABLED, EVENT_REPLAY_ENABLED_DEFAULT)) {
return;
}
- eventDelegation.eventContract = new EventContract(
- new EventContractContainer(document.body),
- /* useActionResolver= */ false,
- );
+ eventDelegation.eventContract = new EventContract(new EventContractContainer(document.body));
const dispatcher = new EventDispatcher(invokeRegisteredListeners);
registerDispatcher(eventDelegation.eventContract, dispatcher);
};
diff --git a/packages/core/src/hydration/event_replay.ts b/packages/core/src/hydration/event_replay.ts
index c3d36d11ebe..e611742c783 100644
--- a/packages/core/src/hydration/event_replay.ts
+++ b/packages/core/src/hydration/event_replay.ts
@@ -118,7 +118,6 @@ const initEventReplay = (eventDelegation: GlobalEventDelegation, injector: Injec
const earlyJsactionData = getJsactionData(container)!;
const eventContract = (eventDelegation.eventContract = new EventContract(
new EventContractContainer(earlyJsactionData.c),
- /* useActionResolver= */ false,
));
for (const et of earlyJsactionData.et) {
eventContract.addEvent(et);