Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(mock-doc): dispatch blur and focus events #3449

Merged
26 changes: 26 additions & 0 deletions src/mock-doc/event.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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: string, focusEventInitDic?: FocusEventInit) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably not crucial, but could we type type here as "blur" | "focus" ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch thank you!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just waiting on this change, and then it looks good!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So sorry I missed that one! Just committed

super(type);

if (focusEventInitDic != null) {
Object.assign(this, focusEventInitDic);
}
}
}

export class MockEventListener {
type: string;
handler: (ev?: any) => void;
Expand Down
4 changes: 3 additions & 1 deletion src/mock-doc/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -148,6 +148,7 @@ const WINDOW_PROPS = [
'HTMLElement',
'Node',
'NodeList',
'FocusEvent',
'KeyboardEvent',
'MouseEvent',
];
Expand All @@ -156,6 +157,7 @@ const GLOBAL_CONSTRUCTORS: [string, any][] = [
['CustomEvent', MockCustomEvent],
['Event', MockEvent],
['Headers', MockHeaders],
['FocusEvent', MockFocusEvent],
['KeyboardEvent', MockKeyboardEvent],
['MouseEvent', MockMouseEvent],
['Request', MockRequest],
Expand Down
21 changes: 18 additions & 3 deletions src/mock-doc/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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') {
Expand Down
43 changes: 43 additions & 0 deletions src/mock-doc/test/event.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be good to have a test where we pass a legit value through for this field, what do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean by legit value?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry should have been more explicit! just something that matches the EventTarget interface — not a super essential test though!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked into this and it doesn't look like any of the Mock elements (MockInputElement, etc) inherit from EventTarget currently.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok no worries, let's leave it as-is then

};
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
Expand Down
2 changes: 2 additions & 0 deletions src/mock-doc/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
MockMouseEvent,
MockCustomEvent,
MockKeyboardEvent,
MockFocusEvent,
} from './event';
import { MockDocument, resetDocument } from './document';
import { MockDocumentFragment } from './document-fragment';
Expand Down Expand Up @@ -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;

Expand Down