From 15520b7066b366078f79be95ccc59d33aeff40d9 Mon Sep 17 00:00:00 2001 From: Marlon Harrison <92547717+marlon-ionic@users.noreply.github.com> Date: Mon, 18 Jul 2022 10:51:03 -0500 Subject: [PATCH] feat(mock-doc): dispatch blur and focus events (#3449) for mock-doc, `blur()` and `focus()` methods will now dispatch an event. created `MockFocusEvent` which extends a new `MockUIEvent`, which extends the existing `MockEvent`. --- src/mock-doc/event.ts | 26 ++++++++++++++++++++ src/mock-doc/global.ts | 4 ++- src/mock-doc/node.ts | 21 +++++++++++++--- src/mock-doc/test/event.spec.ts | 43 +++++++++++++++++++++++++++++++++ src/mock-doc/window.ts | 2 ++ 5 files changed, 92 insertions(+), 4 deletions(-) diff --git a/src/mock-doc/event.ts b/src/mock-doc/event.ts index 77ab1516b75..3cfc4412404 100644 --- a/src/mock-doc/event.ts +++ b/src/mock-doc/event.ts @@ -1,6 +1,7 @@ import { MockDocument } from './document'; import { MockElement } from './node'; import { NODE_NAMES } from './constants'; +import { MockWindow } from './window'; export class MockEvent { bubbles = false; @@ -113,6 +114,31 @@ export class MockMouseEvent extends MockEvent { } } +export class MockUIEvent extends MockEvent { + detail: number | null = null; + view: MockWindow | null = null; + + constructor(type: string, uiEventInitDic?: UIEventInit) { + super(type); + + if (uiEventInitDic != null) { + Object.assign(this, uiEventInitDic); + } + } +} + +export class MockFocusEvent extends MockUIEvent { + relatedTarget: EventTarget | null = null; + + constructor(type: 'blur' | 'focus', focusEventInitDic?: FocusEventInit) { + super(type); + + if (focusEventInitDic != null) { + Object.assign(this, focusEventInitDic); + } + } +} + export class MockEventListener { type: string; handler: (ev?: any) => void; diff --git a/src/mock-doc/global.ts b/src/mock-doc/global.ts index 8c9ce9475df..35278f7eb84 100644 --- a/src/mock-doc/global.ts +++ b/src/mock-doc/global.ts @@ -13,7 +13,7 @@ import { MockTemplateElement, MockTitleElement, } from './element'; -import { MockCustomEvent, MockEvent, MockKeyboardEvent, MockMouseEvent } from './event'; +import { MockCustomEvent, MockEvent, MockFocusEvent, MockKeyboardEvent, MockMouseEvent } from './event'; import { MockHeaders } from './headers'; import { MockRequest, MockResponse } from './request-response'; import { MockDOMParser } from './parser'; @@ -148,6 +148,7 @@ const WINDOW_PROPS = [ 'HTMLElement', 'Node', 'NodeList', + 'FocusEvent', 'KeyboardEvent', 'MouseEvent', ]; @@ -156,6 +157,7 @@ const GLOBAL_CONSTRUCTORS: [string, any][] = [ ['CustomEvent', MockCustomEvent], ['Event', MockEvent], ['Headers', MockHeaders], + ['FocusEvent', MockFocusEvent], ['KeyboardEvent', MockKeyboardEvent], ['MouseEvent', MockMouseEvent], ['Request', MockRequest], diff --git a/src/mock-doc/node.ts b/src/mock-doc/node.ts index f49d4b006c6..22e1f5180b8 100644 --- a/src/mock-doc/node.ts +++ b/src/mock-doc/node.ts @@ -4,7 +4,14 @@ import { matches, selectAll, selectOne } from './selector'; import { MockAttr, MockAttributeMap, createAttributeProxy } from './attribute'; import { MockClassList } from './class-list'; import { MockCSSStyleDeclaration, createCSSStyleDeclaration } from './css-style-declaration'; -import { MockEvent, addEventListener, dispatchEvent, removeEventListener, resetEventListeners } from './event'; +import { + MockEvent, + addEventListener, + dispatchEvent, + removeEventListener, + resetEventListeners, + MockFocusEvent, +} from './event'; import { NODE_NAMES, NODE_TYPES } from './constants'; import { NON_ESCAPABLE_CONTENT, SerializeNodeToHtmlOptions, serializeNodeToHtml } from './serialize-node'; import { parseFragmentUtil } from './parse-util'; @@ -236,7 +243,10 @@ export class MockElement extends MockNode { } blur() { - /**/ + dispatchEvent( + this, + new MockFocusEvent('blur', { relatedTarget: null, bubbles: true, cancelable: true, composed: true }) + ); } get shadowRoot() { @@ -320,7 +330,12 @@ export class MockElement extends MockNode { return this.children[0] || null; } - focus(_options?: { preventScroll?: boolean }) {} + focus(_options?: { preventScroll?: boolean }) { + dispatchEvent( + this, + new MockFocusEvent('focus', { relatedTarget: null, bubbles: true, cancelable: true, composed: true }) + ); + } getAttribute(attrName: string) { if (attrName === 'style') { diff --git a/src/mock-doc/test/event.spec.ts b/src/mock-doc/test/event.spec.ts index b352f59e828..60abdd053c6 100644 --- a/src/mock-doc/test/event.spec.ts +++ b/src/mock-doc/test/event.spec.ts @@ -90,6 +90,49 @@ describe('event', () => { expect(ev.detail).toBe(88); }); + it('FocusEvent() requires type', () => { + expect(() => { + // @ts-ignore checking that it throws when not supplied required arguments + new win.FocusEvent(); + }).toThrow(); + }); + + const focusEventTypes: ('blur' | 'focus')[] = ['blur', 'focus']; + it.each(focusEventTypes)('creates a %s-type MockFocusEvent', (focusType) => { + const ev = new win.FocusEvent(focusType); + expect(ev.bubbles).toBe(false); + expect(ev.cancelBubble).toBe(false); + expect(ev.cancelable).toBe(false); + expect(ev.composed).toBe(false); + expect(ev.currentTarget).toBe(null); + expect(ev.defaultPrevented).toBe(false); + expect(ev.srcElement).toBe(null); + expect(ev.target).toBe(null); + expect(typeof ev.timeStamp).toBe('number'); + expect(ev.type).toBe(focusType); + expect(ev.relatedTarget).toBe(null); + }); + + it('FocusEvent(type, focusEventInitDic)', () => { + const focusEventInitDic = { + bubbles: true, + composed: true, + relatedTarget: null as EventTarget | null, + }; + const ev = new win.FocusEvent('blur', focusEventInitDic); + expect(ev.bubbles).toBe(true); + expect(ev.cancelBubble).toBe(false); + expect(ev.cancelable).toBe(false); + expect(ev.composed).toBe(true); + expect(ev.currentTarget).toBe(null); + expect(ev.defaultPrevented).toBe(false); + expect(ev.srcElement).toBe(null); + expect(ev.target).toBe(null); + expect(typeof ev.timeStamp).toBe('number'); + expect(ev.type).toBe('blur'); + expect(ev.relatedTarget).toBe(null); + }); + it('KeyboardEvent() requires type', () => { expect(() => { // @ts-ignore checking that it throws when not supplied required arguments diff --git a/src/mock-doc/window.ts b/src/mock-doc/window.ts index 47b36937b46..16b60f49e61 100644 --- a/src/mock-doc/window.ts +++ b/src/mock-doc/window.ts @@ -10,6 +10,7 @@ import { MockMouseEvent, MockCustomEvent, MockKeyboardEvent, + MockFocusEvent, } from './event'; import { MockDocument, resetDocument } from './document'; import { MockDocumentFragment } from './document-fragment'; @@ -74,6 +75,7 @@ export class MockWindow { CustomEvent: typeof MockCustomEvent; Event: typeof MockEvent; Headers: typeof MockHeaders; + FocusEvent: typeof MockFocusEvent; KeyboardEvent: typeof MockKeyboardEvent; MouseEvent: typeof MockMouseEvent;