Skip to content

Commit ecdab95

Browse files
committed
feat: add testing utilities for components
* Moves existing testing helpers to a testing folder in the core package. * The testing utilities currently consist of functions to create dom events and shorthands to dispatch them. * Also fixes that **releases** include components `spec.d.ts` files. The `core/testing` utilities are not includes in releases for now. It could be possible to expose them as part of the Component Toolkit. Closes angular#2902
1 parent c203589 commit ecdab95

File tree

14 files changed

+181
-220
lines changed

14 files changed

+181
-220
lines changed

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
1313
import {FakeViewportRuler} from '../core/overlay/position/fake-viewport-ruler';
1414
import {MdAutocomplete} from './autocomplete';
1515
import {MdInputContainer} from '../input/input-container';
16+
import {dispatchFakeEvent} from '../core/testing/dispatch-events';
1617

1718
describe('MdAutocomplete', () => {
1819
let overlayContainerElement: HTMLElement;
@@ -61,7 +62,7 @@ describe('MdAutocomplete', () => {
6162
expect(fixture.componentInstance.trigger.panelOpen)
6263
.toBe(false, `Expected panel state to start out closed.`);
6364

64-
dispatchEvent('focus', input);
65+
dispatchFakeEvent(input, 'focus');
6566
fixture.detectChanges();
6667

6768
expect(fixture.componentInstance.trigger.panelOpen)
@@ -88,11 +89,11 @@ describe('MdAutocomplete', () => {
8889
});
8990

9091
it('should close the panel when blurred', async(() => {
91-
dispatchEvent('focus', input);
92+
dispatchFakeEvent(input, 'focus');
9293
fixture.detectChanges();
9394

9495
fixture.whenStable().then(() => {
95-
dispatchEvent('blur', input);
96+
dispatchFakeEvent(input, 'blur');
9697
fixture.detectChanges();
9798

9899
expect(fixture.componentInstance.trigger.panelOpen)
@@ -103,7 +104,7 @@ describe('MdAutocomplete', () => {
103104
}));
104105

105106
it('should close the panel when an option is clicked', async(() => {
106-
dispatchEvent('focus', input);
107+
dispatchFakeEvent(input, 'focus');
107108
fixture.detectChanges();
108109

109110
fixture.whenStable().then(() => {
@@ -119,7 +120,7 @@ describe('MdAutocomplete', () => {
119120
}));
120121

121122
it('should close the panel when a newly created option is clicked', async(() => {
122-
dispatchEvent('focus', input);
123+
dispatchFakeEvent(input, 'focus');
123124
fixture.detectChanges();
124125

125126
fixture.whenStable().then(() => {
@@ -164,7 +165,7 @@ describe('MdAutocomplete', () => {
164165
});
165166

166167
it('should hide the panel when the options list is empty', async(() => {
167-
dispatchEvent('focus', input);
168+
dispatchFakeEvent(input, 'focus');
168169

169170
fixture.whenStable().then(() => {
170171
fixture.detectChanges();
@@ -423,7 +424,7 @@ describe('MdAutocomplete', () => {
423424
expect(fixture.componentInstance.stateCtrl.touched)
424425
.toBe(false, `Expected control to start out untouched.`);
425426

426-
dispatchEvent('blur', input);
427+
dispatchFakeEvent(input, 'blur');
427428
fixture.detectChanges();
428429

429430
expect(fixture.componentInstance.stateCtrl.touched)
@@ -443,8 +444,8 @@ describe('MdAutocomplete', () => {
443444
fixture.detectChanges();
444445

445446
input = fixture.debugElement.query(By.css('input')).nativeElement;
446-
DOWN_ARROW_EVENT = new FakeKeyboardEvent(DOWN_ARROW) as KeyboardEvent;
447-
ENTER_EVENT = new FakeKeyboardEvent(ENTER) as KeyboardEvent;
447+
DOWN_ARROW_EVENT = new MockKeyboardEvent(DOWN_ARROW) as KeyboardEvent;
448+
ENTER_EVENT = new MockKeyboardEvent(ENTER) as KeyboardEvent;
448449

449450
fixture.componentInstance.trigger.openPanel();
450451
fixture.detectChanges();
@@ -505,7 +506,7 @@ describe('MdAutocomplete', () => {
505506
const optionEls =
506507
overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
507508

508-
const UP_ARROW_EVENT = new FakeKeyboardEvent(UP_ARROW) as KeyboardEvent;
509+
const UP_ARROW_EVENT = new MockKeyboardEvent(UP_ARROW) as KeyboardEvent;
509510
fixture.componentInstance.trigger._handleKeydown(UP_ARROW_EVENT);
510511

511512
fixture.whenStable().then(() => {
@@ -571,7 +572,7 @@ describe('MdAutocomplete', () => {
571572
typeInElement('New', input);
572573
fixture.detectChanges();
573574

574-
const SPACE_EVENT = new FakeKeyboardEvent(SPACE) as KeyboardEvent;
575+
const SPACE_EVENT = new MockKeyboardEvent(SPACE) as KeyboardEvent;
575576
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
576577
fixture.componentInstance.trigger._handleKeydown(SPACE_EVENT);
577578
fixture.detectChanges();
@@ -680,7 +681,7 @@ describe('MdAutocomplete', () => {
680681
expect(input.hasAttribute('aria-activedescendant'))
681682
.toBe(false, 'Expected aria-activedescendant to be absent if no active item.');
682683

683-
const DOWN_ARROW_EVENT = new FakeKeyboardEvent(DOWN_ARROW) as KeyboardEvent;
684+
const DOWN_ARROW_EVENT = new MockKeyboardEvent(DOWN_ARROW) as KeyboardEvent;
684685
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
685686
fixture.detectChanges();
686687

@@ -905,21 +906,6 @@ class AutocompleteWithoutForms {
905906

906907
}
907908

908-
/**
909-
* TODO: Move this to core testing utility until Angular has event faking
910-
* support.
911-
*
912-
* Dispatches an event from an element.
913-
* @param eventName Name of the event
914-
* @param element The element from which the event will be dispatched.
915-
*/
916-
function dispatchEvent(eventName: string, element: HTMLElement): void {
917-
let event = document.createEvent('Event');
918-
event.initEvent(eventName, true, true);
919-
element.dispatchEvent(event);
920-
}
921-
922-
923909
/**
924910
* Focuses an input, sets its value and dispatches
925911
* the `input` event, simulating the user typing.
@@ -929,11 +915,11 @@ function dispatchEvent(eventName: string, element: HTMLElement): void {
929915
function typeInElement(value: string, element: HTMLInputElement) {
930916
element.focus();
931917
element.value = value;
932-
dispatchEvent('input', element);
918+
dispatchFakeEvent(element, 'input');
933919
}
934920

935921
/** This is a mock keyboard event to test keyboard events in the autocomplete. */
936-
class FakeKeyboardEvent {
922+
class MockKeyboardEvent {
937923
constructor(public keyCode: number) {}
938924
preventDefault() {}
939925
}

src/lib/core/overlay/scroll/scroll-dispatcher.spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {NgModule, Component, ViewChild, ElementRef} from '@angular/core';
33
import {ScrollDispatcher} from './scroll-dispatcher';
44
import {OverlayModule} from '../overlay-directives';
55
import {Scrollable} from './scrollable';
6+
import {dispatchFakeEvent} from '../../testing/dispatch-events';
67

78
describe('Scroll Dispatcher', () => {
89

@@ -53,9 +54,7 @@ describe('Scroll Dispatcher', () => {
5354
// Emit a scroll event from the scrolling element in our component.
5455
// This event should be picked up by the scrollable directive and notify.
5556
// The notification should be picked up by the service.
56-
const scrollEvent = document.createEvent('UIEvents');
57-
scrollEvent.initUIEvent('scroll', true, true, window, 0);
58-
fixture.componentInstance.scrollingElement.nativeElement.dispatchEvent(scrollEvent);
57+
dispatchFakeEvent(fixture.componentInstance.scrollingElement.nativeElement, 'scroll');
5958

6059
// The scrollable directive should have notified the service immediately.
6160
expect(hasDirectiveScrollNotified).toBe(true);

src/lib/core/ripple/ripple.spec.ts

Lines changed: 25 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Component, ViewChild} from '@angular/core';
33
import {MdRipple, MdRippleModule} from './ripple';
44
import {ViewportRuler} from '../overlay/position/viewport-ruler';
55
import {RIPPLE_FADE_OUT_DURATION, RIPPLE_FADE_IN_DURATION} from './ripple-renderer';
6+
import {dispatchMouseEvent} from '../testing/dispatch-events';
67

78

89
/** Creates a DOM mouse event. */
@@ -65,15 +66,6 @@ describe('MdRipple', () => {
6566
document.body.style.margin = originalBodyMargin;
6667
});
6768

68-
function dispatchMouseEvent(type: string, offsetX = 0, offsetY = 0) {
69-
let mouseEvent = createMouseEvent(type, {
70-
clientX: rippleTarget.clientLeft + offsetX,
71-
clientY: rippleTarget.clientTop + offsetY
72-
});
73-
74-
rippleTarget.dispatchEvent(mouseEvent);
75-
}
76-
7769
describe('basic ripple', () => {
7870
let rippleDirective: MdRipple;
7971

@@ -89,20 +81,20 @@ describe('MdRipple', () => {
8981
});
9082

9183
it('creates ripple on mousedown', () => {
92-
dispatchMouseEvent('mousedown');
93-
dispatchMouseEvent('mouseup');
84+
dispatchMouseEvent(rippleTarget, 'mousedown');
85+
dispatchMouseEvent(rippleTarget, 'mouseup');
9486

9587
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
9688

97-
dispatchMouseEvent('mousedown');
98-
dispatchMouseEvent('mouseup');
89+
dispatchMouseEvent(rippleTarget, 'mousedown');
90+
dispatchMouseEvent(rippleTarget, 'mouseup');
9991

10092
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(2);
10193
});
10294

10395
it('removes ripple after timeout', fakeAsync(() => {
104-
dispatchMouseEvent('mousedown');
105-
dispatchMouseEvent('mouseup');
96+
dispatchMouseEvent(rippleTarget, 'mousedown');
97+
dispatchMouseEvent(rippleTarget, 'mouseup');
10698

10799
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
108100

@@ -140,8 +132,8 @@ describe('MdRipple', () => {
140132
let elementRect = rippleTarget.getBoundingClientRect();
141133

142134
// Dispatch a ripple at the following relative coordinates (X: 50| Y: 75)
143-
dispatchMouseEvent('mousedown', 50, 75);
144-
dispatchMouseEvent('mouseup');
135+
dispatchMouseEvent(rippleTarget, 'mousedown', 50, 75);
136+
dispatchMouseEvent(rippleTarget, 'mouseup');
145137

146138
// Calculate distance from the click to farthest edge of the ripple target.
147139
let maxDistanceX = TARGET_WIDTH - 50;
@@ -174,8 +166,8 @@ describe('MdRipple', () => {
174166
fixture.componentInstance.isDestroyed = true;
175167
fixture.detectChanges();
176168

177-
dispatchMouseEvent('mousedown');
178-
dispatchMouseEvent('mouseup');
169+
dispatchMouseEvent(rippleTarget, 'mousedown');
170+
dispatchMouseEvent(rippleTarget, 'mouseup');
179171

180172
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
181173
});
@@ -245,15 +237,10 @@ describe('MdRipple', () => {
245237
rippleTarget.style.top = `${elementTop}px`;
246238

247239
// Simulate a keyboard-triggered click by setting event coordinates to 0.
248-
let clickEvent = createMouseEvent('mousedown', {
249-
clientX: left + elementLeft - pageScrollLeft,
250-
clientY: top + elementTop - pageScrollTop,
251-
screenX: left + elementLeft,
252-
screenY: top + elementTop
253-
});
254-
255-
rippleTarget.dispatchEvent(clickEvent);
256-
dispatchMouseEvent('mouseup');
240+
dispatchMouseEvent(rippleTarget, 'mousedown',
241+
left + elementLeft - pageScrollLeft,
242+
top + elementTop - pageScrollTop
243+
);
257244

258245
let expectedRadius = Math.sqrt(250 * 250 + 125 * 125);
259246
let expectedLeft = left - expectedRadius;
@@ -298,8 +285,8 @@ describe('MdRipple', () => {
298285
controller.color = backgroundColor;
299286
fixture.detectChanges();
300287

301-
dispatchMouseEvent('mousedown');
302-
dispatchMouseEvent('mouseup');
288+
dispatchMouseEvent(rippleTarget, 'mousedown');
289+
dispatchMouseEvent(rippleTarget, 'mouseup');
303290

304291
let ripple = rippleTarget.querySelector('.mat-ripple-element');
305292
expect(window.getComputedStyle(ripple).backgroundColor).toBe(backgroundColor);
@@ -309,16 +296,16 @@ describe('MdRipple', () => {
309296
controller.disabled = true;
310297
fixture.detectChanges();
311298

312-
dispatchMouseEvent('mousedown');
313-
dispatchMouseEvent('mouseup');
299+
dispatchMouseEvent(rippleTarget, 'mousedown');
300+
dispatchMouseEvent(rippleTarget, 'mouseup');
314301

315302
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
316303

317304
controller.disabled = false;
318305
fixture.detectChanges();
319306

320-
dispatchMouseEvent('mousedown');
321-
dispatchMouseEvent('mouseup');
307+
dispatchMouseEvent(rippleTarget, 'mousedown');
308+
dispatchMouseEvent(rippleTarget, 'mouseup');
322309

323310
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
324311
});
@@ -352,8 +339,8 @@ describe('MdRipple', () => {
352339
let elementRect = rippleTarget.getBoundingClientRect();
353340

354341
// Click the ripple element 50 px to the right and 75px down from its upper left.
355-
dispatchMouseEvent('mousedown', 50, 75);
356-
dispatchMouseEvent('mouseup');
342+
dispatchMouseEvent(rippleTarget, 'mousedown', 50, 75);
343+
dispatchMouseEvent(rippleTarget, 'mouseup');
357344

358345
// Because the centered input is true, the center of the ripple should be the midpoint of the
359346
// bounding rect. The ripple should expand to cover the rect corners, which are 150px
@@ -379,8 +366,8 @@ describe('MdRipple', () => {
379366
let elementRect = rippleTarget.getBoundingClientRect();
380367

381368
// Click the ripple element 50 px to the right and 75px down from its upper left.
382-
dispatchMouseEvent('mousedown', 50, 75);
383-
dispatchMouseEvent('mouseup');
369+
dispatchMouseEvent(rippleTarget, 'mousedown', 50, 75);
370+
dispatchMouseEvent(rippleTarget, 'mouseup');
384371

385372
let expectedLeft = elementRect.left + 50 - customRadius;
386373
let expectedTop = elementRect.top + 75 - customRadius;

0 commit comments

Comments
 (0)