import { after, afterEach, before, describe, it } from '@ephox/bedrock-client'; import { Fun } from '@ephox/katamari'; import { assert } from 'chai'; import EventUtils, { EventUtilsEvent } from 'tinymce/core/api/dom/EventUtils'; describe('browser.tinymce.core.dom.EventUtilsTest', () => { const eventUtils = EventUtils.Event; before(() => { const testDiv = document.createElement('div'); testDiv.id = 'testDiv'; testDiv.innerHTML = ( '
' + '
' + '
' ); document.body.appendChild(testDiv); }); after(() => { const testDiv = document.querySelector('#testDiv'); if (testDiv) { testDiv.parentNode?.removeChild(testDiv); } }); afterEach(() => { eventUtils.clean(window); }); it('unbind all', () => { let result: Record; eventUtils.bind(window, 'click', () => { result.click = true; }); eventUtils.bind(window, 'keydown', () => { result.keydown1 = true; }); eventUtils.bind(window, 'keydown', () => { result.keydown2 = true; }); result = {}; eventUtils.dispatch(window, 'click'); eventUtils.dispatch(window, 'keydown'); assert.deepEqual(result, { click: true, keydown1: true, keydown2: true }); eventUtils.unbind(window); result = {}; eventUtils.dispatch(window, 'click'); eventUtils.dispatch(window, 'keydown'); assert.deepEqual(result, {}); }); it('unbind event', () => { let result: Record; eventUtils.bind(window, 'click', () => { result.click = true; }); eventUtils.bind(window, 'keydown', () => { result.keydown1 = true; }); eventUtils.bind(window, 'keydown', () => { result.keydown2 = true; }); result = {}; eventUtils.dispatch(window, 'click'); eventUtils.dispatch(window, 'keydown'); assert.deepEqual(result, { click: true, keydown1: true, keydown2: true }); eventUtils.unbind(window, 'click'); result = {}; eventUtils.dispatch(window, 'click'); eventUtils.dispatch(window, 'keydown'); assert.deepEqual(result, { keydown1: true, keydown2: true }); }); it('unbind event non existing', () => { eventUtils.unbind(window, 'noevent'); assert.ok(true, 'No exception'); }); it('unbind callback', () => { let result: Record; eventUtils.bind(window, 'click', () => { result.click = true; }); eventUtils.bind(window, 'keydown', () => { result.keydown1 = true; }); const callback2 = () => { result.keydown2 = true; }; eventUtils.bind(window, 'keydown', callback2); result = {}; eventUtils.dispatch(window, 'click'); eventUtils.dispatch(window, 'keydown'); assert.deepEqual(result, { click: true, keydown1: true, keydown2: true }); eventUtils.unbind(window, 'keydown', callback2); result = {}; eventUtils.dispatch(window, 'click'); eventUtils.dispatch(window, 'keydown'); assert.deepEqual(result, { click: true, keydown1: true }); }); it('unbind multiple', () => { const result: Record = {}; eventUtils.bind(window, 'mouseup mousedown click', (e) => { result[e.type] = true; }); eventUtils.unbind(window, 'mouseup mousedown'); eventUtils.dispatch(window, 'mouseup'); eventUtils.dispatch(window, 'mousedown'); eventUtils.dispatch(window, 'click'); assert.deepEqual(result, { click: true }); }); it('bind multiple', () => { const result: Record = {}; eventUtils.bind(window, 'mouseup mousedown', (e) => { result[e.type] = true; }); eventUtils.dispatch(window, 'mouseup'); eventUtils.dispatch(window, 'mousedown'); eventUtils.dispatch(window, 'click'); assert.deepEqual(result, { mouseup: true, mousedown: true }); }); it('bind/fire bubbling', () => { let result: Record; eventUtils.bind(window, 'click', () => { result.window = true; }); eventUtils.bind(document, 'click', () => { result.document = true; }); eventUtils.bind(document.body, 'click', () => { result.body = true; }); eventUtils.bind(document.getElementById('content'), 'click', () => { result.content = true; }); eventUtils.bind(document.getElementById('inner'), 'click', () => { result.inner = true; }); result = {}; eventUtils.dispatch(window, 'click'); assert.deepEqual(result, { window: true }); result = {}; eventUtils.dispatch(document, 'click'); assert.deepEqual(result, { document: true, window: true }); result = {}; eventUtils.dispatch(document.body, 'click'); assert.deepEqual(result, { body: true, document: true, window: true }); result = {}; eventUtils.dispatch(document.getElementById('content'), 'click'); assert.deepEqual(result, { content: true, body: true, document: true, window: true }); result = {}; eventUtils.dispatch(document.getElementById('inner'), 'click'); assert.deepEqual(result, { inner: true, content: true, body: true, document: true, window: true }); }); it('bubbling with prevented default', () => { let result: Record; eventUtils.bind(window, 'click', (e) => { result.window = true; result.windowPrevented = e.defaultPrevented; result.windowIsPrevented = e.isDefaultPrevented(); }); eventUtils.bind(document.getElementById('inner'), 'click', (e) => { result.inner = true; e.preventDefault(); }); result = {}; eventUtils.dispatch(window, 'click', { defaultPrevented: false, cancelBubble: false }); assert.deepEqual(result, { window: true, windowPrevented: false, windowIsPrevented: false }); result = {}; eventUtils.dispatch(document.getElementById('inner'), 'click', { defaultPrevented: false, cancelBubble: false }); assert.deepEqual(result, { inner: true, window: true, windowPrevented: true, windowIsPrevented: true }); }); it('bind/fire stopImmediatePropagation', () => { eventUtils.bind(window, 'click', () => { result.click1 = true; }); eventUtils.bind(window, 'click', (e) => { result.click2 = true; e.stopImmediatePropagation(); }); eventUtils.bind(window, 'click', () => { result.click3 = true; }); const result: Record = {}; eventUtils.dispatch(window, 'click'); assert.deepEqual(result, { click1: true, click2: true }); }); it('bind/fire stopPropagation', () => { eventUtils.bind(window, 'click', () => { result.click1 = true; }); eventUtils.bind(document.body, 'click', () => { result.click2 = true; }); eventUtils.bind(document.getElementById('inner'), 'click', (e) => { result.click3 = true; e.stopPropagation(); }); const result: Record = {}; eventUtils.dispatch(document.getElementById('inner'), 'click'); assert.deepEqual(result, { click3: true }); }); it('clean window', () => { let result: Record; eventUtils.bind(window, 'click', () => { result.click1 = true; }); eventUtils.bind(document.body, 'click', () => { result.click2 = true; }); eventUtils.bind(document.getElementById('content'), 'click', () => { result.click3 = true; }); eventUtils.bind(document.getElementById('inner'), 'click', () => { result.click4 = true; }); result = {}; eventUtils.dispatch(document.getElementById('inner'), 'click'); assert.deepEqual(result, { click1: true, click2: true, click3: true, click4: true }); eventUtils.clean(window); result = {}; eventUtils.dispatch(document.getElementById('inner'), 'click'); assert.deepEqual(result, {}); }); it('clean document', () => { let result: Record; eventUtils.bind(window, 'click', () => { result.click1 = true; }); eventUtils.bind(document, 'click', () => { result.click2 = true; }); eventUtils.bind(document.body, 'click', () => { result.click3 = true; }); eventUtils.bind(document.getElementById('content'), 'click', () => { result.click4 = true; }); eventUtils.bind(document.getElementById('inner'), 'click', () => { result.click5 = true; }); result = {}; eventUtils.dispatch(document.getElementById('inner'), 'click'); assert.deepEqual(result, { click1: true, click2: true, click3: true, click4: true, click5: true }); eventUtils.clean(document); result = {}; eventUtils.dispatch(document.getElementById('inner'), 'click'); assert.deepEqual(result, { click1: true }); }); it('clean element', () => { let result: Record; eventUtils.bind(window, 'click', () => { result.click1 = true; }); eventUtils.bind(document.body, 'click', () => { result.click2 = true; }); eventUtils.bind(document.getElementById('content'), 'click', () => { result.click3 = true; }); eventUtils.bind(document.getElementById('inner'), 'click', () => { result.click4 = true; }); result = {}; eventUtils.dispatch(document.getElementById('inner'), 'click'); assert.deepEqual(result, { click1: true, click2: true, click3: true, click4: true }); eventUtils.clean(document.getElementById('content')); result = {}; eventUtils.dispatch(document.getElementById('inner'), 'click'); assert.deepEqual(result, { click1: true, click2: true }); }); it('mouseenter/mouseleave bind/unbind', () => { let result: Record = {}; eventUtils.bind(document.body, 'mouseenter mouseleave', (e) => { result[e.type] = true; }); eventUtils.dispatch(document.body, 'mouseenter'); eventUtils.dispatch(document.body, 'mouseleave'); assert.deepEqual(result, { mouseenter: true, mouseleave: true }); result = {}; eventUtils.clean(document.body); eventUtils.dispatch(document.body, 'mouseenter'); eventUtils.dispatch(document.body, 'mouseleave'); assert.deepEqual(result, {}); }); /* asyncTest("focusin/focusout bind/unbind", function() { var result = {}; window.setTimeout(function() { eventUtils.bind(document.body, 'focusin focusout', function(e) { // IE will fire a focusout on the parent element if you focus an element within not a big deal so lets detect it in the test if (e.type == "focusout" && e.target.contains(document.activeElement)) { return; } result[e.type] = result[e.type] ? ++result[e.type] : 1; }); start(); document.getElementById('content').focus(); document.getElementById('inner').focus(); assert.deepEqual(result, {focusin: 2, focusout: 1}); }, 0); }); */ it('bind unbind fire clean on null', () => { eventUtils.bind(null, 'click', Fun.noop); eventUtils.unbind(null, 'click', Fun.noop); eventUtils.dispatch(null, 'click', {}); eventUtils.clean(null); assert.ok(true, 'No exception'); }); it('bind ready when page is loaded', () => { let ready; eventUtils.bind(window, 'ready', () => { ready = true; }); assert.equal(true, eventUtils.domLoaded, 'DomLoaded state true'); assert.equal(true, ready, 'Window is ready.'); }); it('event states when event object is fired twice', () => { const result: Record = {}; eventUtils.bind(window, 'keydown', (e) => { result[e.type] = true; e.preventDefault(); e.stopPropagation(); }); eventUtils.bind(window, 'keyup', (e) => { result[e.type] = true; e.stopImmediatePropagation(); }); const event: any = {}; eventUtils.dispatch(window, 'keydown', event); eventUtils.dispatch(window, 'keyup', event); assert.equal(true, event.isDefaultPrevented(), 'Default is prevented.'); assert.equal(true, event.isPropagationStopped(), 'Propagation is stopped.'); assert.equal(true, event.isImmediatePropagationStopped(), 'Immediate propagation is stopped.'); assert.deepEqual(result, { keydown: true, keyup: true }); }); it('unbind inside callback', () => { let data: string; const append = (value: string) => { return () => { data += value; }; }; const callback = () => { eventUtils.unbind(window, 'click', callback); data += 'b'; }; data = ''; eventUtils.bind(window, 'click', append('a')); eventUtils.bind(window, 'click', callback); eventUtils.bind(window, 'click', append('c')); eventUtils.dispatch(window, 'click', {}); assert.equal(data, 'abc'); data = ''; eventUtils.dispatch(window, 'click', {}); assert.equal(data, 'ac'); }); it('ready/DOMContentLoaded (domLoaded = true)', () => { let evt: EventUtilsEvent | undefined; eventUtils.bind(window, 'ready', (e) => { evt = e; }); assert.equal(evt?.type, 'ready'); }); it('ready/DOMContentLoaded (document.readyState check)', () => { const doc = document as any; let evt: EventUtilsEvent | undefined; try { doc.readyState = 'loading'; } catch (e) { assert.equal(true, true, `IE doesn't allow us to set document.readyState`); return; } eventUtils.domLoaded = false; doc.readyState = 'loading'; eventUtils.bind(window, 'ready', (e) => { evt = e; }); assert.equal(true, typeof evt !== 'undefined'); eventUtils.domLoaded = false; doc.readyState = 'complete'; eventUtils.bind(window, 'ready', (e) => { evt = e; }); assert.equal(evt.type, 'ready'); }); it('isDefaultPrevented', () => { const testObj: any = {}; const testCallback = Fun.constant('hello'); testObj.isDefaultPrevented = testCallback; eventUtils.dispatch(window, 'testEvent', testObj); assert.notEqual(testObj.isDefaultPrevented, testCallback, 'Is overwritten by our isDefaultPrevented'); assert.equal(typeof testObj.isPropagationStopped, 'function', 'Has our isPropagationStopped'); assert.equal(typeof testObj.isImmediatePropagationStopped, 'function', 'Has our isImmediatePropagationStopped'); }); });