Skip to content

Commit c0d8448

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 #2902
1 parent 817dcfd commit c0d8448

File tree

14 files changed

+184
-222
lines changed

14 files changed

+184
-222
lines changed

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {FakeViewportRuler} from '../core/overlay/position/fake-viewport-ruler';
1414
import {MdAutocomplete} from './autocomplete';
1515
import {MdInputContainer} from '../input/input-container';
1616
import {Observable} from 'rxjs/Observable';
17+
import {dispatchFakeEvent} from '../core/testing/dispatch-events';
18+
1719
import 'rxjs/add/operator/map';
1820

1921
describe('MdAutocomplete', () => {
@@ -63,7 +65,7 @@ describe('MdAutocomplete', () => {
6365
expect(fixture.componentInstance.trigger.panelOpen)
6466
.toBe(false, `Expected panel state to start out closed.`);
6567

66-
dispatchEvent('focus', input);
68+
dispatchFakeEvent(input, 'focus');
6769
fixture.detectChanges();
6870

6971
expect(fixture.componentInstance.trigger.panelOpen)
@@ -90,11 +92,11 @@ describe('MdAutocomplete', () => {
9092
});
9193

9294
it('should close the panel when blurred', async(() => {
93-
dispatchEvent('focus', input);
95+
dispatchFakeEvent(input, 'focus');
9496
fixture.detectChanges();
9597

9698
fixture.whenStable().then(() => {
97-
dispatchEvent('blur', input);
99+
dispatchFakeEvent(input, 'blur');
98100
fixture.detectChanges();
99101

100102
expect(fixture.componentInstance.trigger.panelOpen)
@@ -105,7 +107,7 @@ describe('MdAutocomplete', () => {
105107
}));
106108

107109
it('should close the panel when an option is clicked', async(() => {
108-
dispatchEvent('focus', input);
110+
dispatchFakeEvent(input, 'focus');
109111
fixture.detectChanges();
110112

111113
fixture.whenStable().then(() => {
@@ -121,7 +123,7 @@ describe('MdAutocomplete', () => {
121123
}));
122124

123125
it('should close the panel when a newly created option is clicked', async(() => {
124-
dispatchEvent('focus', input);
126+
dispatchFakeEvent(input, 'focus');
125127
fixture.detectChanges();
126128

127129
fixture.whenStable().then(() => {
@@ -166,7 +168,7 @@ describe('MdAutocomplete', () => {
166168
});
167169

168170
it('should hide the panel when the options list is empty', async(() => {
169-
dispatchEvent('focus', input);
171+
dispatchFakeEvent(input, 'focus');
170172

171173
fixture.whenStable().then(() => {
172174
fixture.detectChanges();
@@ -214,7 +216,7 @@ describe('MdAutocomplete', () => {
214216
.toBe(false, `Expected panel state to start out closed.`);
215217

216218
input.value = 'Alabama';
217-
dispatchEvent('input', input);
219+
dispatchFakeEvent(input, 'input');
218220
fixture.detectChanges();
219221

220222
expect(fixture.componentInstance.trigger.panelOpen)
@@ -467,7 +469,7 @@ describe('MdAutocomplete', () => {
467469
expect(fixture.componentInstance.stateCtrl.touched)
468470
.toBe(false, `Expected control to start out untouched.`);
469471

470-
dispatchEvent('blur', input);
472+
dispatchFakeEvent(input, 'blur');
471473
fixture.detectChanges();
472474

473475
expect(fixture.componentInstance.stateCtrl.touched)
@@ -487,8 +489,8 @@ describe('MdAutocomplete', () => {
487489
fixture.detectChanges();
488490

489491
input = fixture.debugElement.query(By.css('input')).nativeElement;
490-
DOWN_ARROW_EVENT = new FakeKeyboardEvent(DOWN_ARROW) as KeyboardEvent;
491-
ENTER_EVENT = new FakeKeyboardEvent(ENTER) as KeyboardEvent;
492+
DOWN_ARROW_EVENT = new MockKeyboardEvent(DOWN_ARROW) as KeyboardEvent;
493+
ENTER_EVENT = new MockKeyboardEvent(ENTER) as KeyboardEvent;
492494

493495
fixture.componentInstance.trigger.openPanel();
494496
fixture.detectChanges();
@@ -549,7 +551,7 @@ describe('MdAutocomplete', () => {
549551
const optionEls =
550552
overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
551553

552-
const UP_ARROW_EVENT = new FakeKeyboardEvent(UP_ARROW) as KeyboardEvent;
554+
const UP_ARROW_EVENT = new MockKeyboardEvent(UP_ARROW) as KeyboardEvent;
553555
fixture.componentInstance.trigger._handleKeydown(UP_ARROW_EVENT);
554556

555557
fixture.whenStable().then(() => {
@@ -615,7 +617,7 @@ describe('MdAutocomplete', () => {
615617
typeInElement('New', input);
616618
fixture.detectChanges();
617619

618-
const SPACE_EVENT = new FakeKeyboardEvent(SPACE) as KeyboardEvent;
620+
const SPACE_EVENT = new MockKeyboardEvent(SPACE) as KeyboardEvent;
619621
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
620622
fixture.componentInstance.trigger._handleKeydown(SPACE_EVENT);
621623
fixture.detectChanges();
@@ -724,7 +726,7 @@ describe('MdAutocomplete', () => {
724726
expect(input.hasAttribute('aria-activedescendant'))
725727
.toBe(false, 'Expected aria-activedescendant to be absent if no active item.');
726728

727-
const DOWN_ARROW_EVENT = new FakeKeyboardEvent(DOWN_ARROW) as KeyboardEvent;
729+
const DOWN_ARROW_EVENT = new MockKeyboardEvent(DOWN_ARROW) as KeyboardEvent;
728730
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
729731
fixture.detectChanges();
730732

@@ -866,7 +868,7 @@ describe('MdAutocomplete', () => {
866868
fixture.detectChanges();
867869

868870
const input = fixture.debugElement.query(By.css('input')).nativeElement;
869-
dispatchEvent('focus', input);
871+
dispatchFakeEvent(input, 'focus');
870872
fixture.detectChanges();
871873

872874
expect(fixture.componentInstance.trigger.panelOpen)
@@ -995,21 +997,6 @@ class AutocompleteWithoutForms {
995997

996998
}
997999

998-
/**
999-
* TODO: Move this to core testing utility until Angular has event faking
1000-
* support.
1001-
*
1002-
* Dispatches an event from an element.
1003-
* @param eventName Name of the event
1004-
* @param element The element from which the event will be dispatched.
1005-
*/
1006-
function dispatchEvent(eventName: string, element: HTMLElement): void {
1007-
let event = document.createEvent('Event');
1008-
event.initEvent(eventName, true, true);
1009-
element.dispatchEvent(event);
1010-
}
1011-
1012-
10131000
/**
10141001
* Focuses an input, sets its value and dispatches
10151002
* the `input` event, simulating the user typing.
@@ -1019,11 +1006,11 @@ function dispatchEvent(eventName: string, element: HTMLElement): void {
10191006
function typeInElement(value: string, element: HTMLInputElement) {
10201007
element.focus();
10211008
element.value = value;
1022-
dispatchEvent('input', element);
1009+
dispatchFakeEvent(element, 'input');
10231010
}
10241011

10251012
/** This is a mock keyboard event to test keyboard events in the autocomplete. */
1026-
class FakeKeyboardEvent {
1013+
class MockKeyboardEvent {
10271014
constructor(public keyCode: number) {}
10281015
preventDefault() {}
10291016
}

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)