From c0d844846dc65f5b7fc23ad5545107c22dd13bd6 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Sun, 5 Feb 2017 20:59:25 +0100 Subject: [PATCH 1/2] 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 --- src/lib/autocomplete/autocomplete.spec.ts | 49 ++++++--------- .../overlay/scroll/scroll-dispatcher.spec.ts | 5 +- src/lib/core/ripple/ripple.spec.ts | 63 ++++++++----------- .../core/style/focus-origin-monitor.spec.ts | 54 ++++------------ src/lib/core/testing/dispatch-events.ts | 26 ++++++++ src/lib/core/testing/event-objects.ts | 62 ++++++++++++++++++ src/lib/radio/radio.spec.ts | 26 +++----- src/lib/select/select.spec.ts | 19 +----- src/lib/slide-toggle/slide-toggle.spec.ts | 14 +---- src/lib/slider/slider.spec.ts | 57 ++++++----------- src/lib/tabs/tab-header.spec.ts | 19 ++---- src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts | 7 +-- src/lib/tsconfig-srcs.json | 3 + tools/gulp/tasks/components.ts | 2 +- 14 files changed, 184 insertions(+), 222 deletions(-) create mode 100644 src/lib/core/testing/dispatch-events.ts create mode 100644 src/lib/core/testing/event-objects.ts diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index 7d49d1fea780..7f6dd8c9672e 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -14,6 +14,8 @@ import {FakeViewportRuler} from '../core/overlay/position/fake-viewport-ruler'; import {MdAutocomplete} from './autocomplete'; import {MdInputContainer} from '../input/input-container'; import {Observable} from 'rxjs/Observable'; +import {dispatchFakeEvent} from '../core/testing/dispatch-events'; + import 'rxjs/add/operator/map'; describe('MdAutocomplete', () => { @@ -63,7 +65,7 @@ describe('MdAutocomplete', () => { expect(fixture.componentInstance.trigger.panelOpen) .toBe(false, `Expected panel state to start out closed.`); - dispatchEvent('focus', input); + dispatchFakeEvent(input, 'focus'); fixture.detectChanges(); expect(fixture.componentInstance.trigger.panelOpen) @@ -90,11 +92,11 @@ describe('MdAutocomplete', () => { }); it('should close the panel when blurred', async(() => { - dispatchEvent('focus', input); + dispatchFakeEvent(input, 'focus'); fixture.detectChanges(); fixture.whenStable().then(() => { - dispatchEvent('blur', input); + dispatchFakeEvent(input, 'blur'); fixture.detectChanges(); expect(fixture.componentInstance.trigger.panelOpen) @@ -105,7 +107,7 @@ describe('MdAutocomplete', () => { })); it('should close the panel when an option is clicked', async(() => { - dispatchEvent('focus', input); + dispatchFakeEvent(input, 'focus'); fixture.detectChanges(); fixture.whenStable().then(() => { @@ -121,7 +123,7 @@ describe('MdAutocomplete', () => { })); it('should close the panel when a newly created option is clicked', async(() => { - dispatchEvent('focus', input); + dispatchFakeEvent(input, 'focus'); fixture.detectChanges(); fixture.whenStable().then(() => { @@ -166,7 +168,7 @@ describe('MdAutocomplete', () => { }); it('should hide the panel when the options list is empty', async(() => { - dispatchEvent('focus', input); + dispatchFakeEvent(input, 'focus'); fixture.whenStable().then(() => { fixture.detectChanges(); @@ -214,7 +216,7 @@ describe('MdAutocomplete', () => { .toBe(false, `Expected panel state to start out closed.`); input.value = 'Alabama'; - dispatchEvent('input', input); + dispatchFakeEvent(input, 'input'); fixture.detectChanges(); expect(fixture.componentInstance.trigger.panelOpen) @@ -467,7 +469,7 @@ describe('MdAutocomplete', () => { expect(fixture.componentInstance.stateCtrl.touched) .toBe(false, `Expected control to start out untouched.`); - dispatchEvent('blur', input); + dispatchFakeEvent(input, 'blur'); fixture.detectChanges(); expect(fixture.componentInstance.stateCtrl.touched) @@ -487,8 +489,8 @@ describe('MdAutocomplete', () => { fixture.detectChanges(); input = fixture.debugElement.query(By.css('input')).nativeElement; - DOWN_ARROW_EVENT = new FakeKeyboardEvent(DOWN_ARROW) as KeyboardEvent; - ENTER_EVENT = new FakeKeyboardEvent(ENTER) as KeyboardEvent; + DOWN_ARROW_EVENT = new MockKeyboardEvent(DOWN_ARROW) as KeyboardEvent; + ENTER_EVENT = new MockKeyboardEvent(ENTER) as KeyboardEvent; fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); @@ -549,7 +551,7 @@ describe('MdAutocomplete', () => { const optionEls = overlayContainerElement.querySelectorAll('md-option') as NodeListOf; - const UP_ARROW_EVENT = new FakeKeyboardEvent(UP_ARROW) as KeyboardEvent; + const UP_ARROW_EVENT = new MockKeyboardEvent(UP_ARROW) as KeyboardEvent; fixture.componentInstance.trigger._handleKeydown(UP_ARROW_EVENT); fixture.whenStable().then(() => { @@ -615,7 +617,7 @@ describe('MdAutocomplete', () => { typeInElement('New', input); fixture.detectChanges(); - const SPACE_EVENT = new FakeKeyboardEvent(SPACE) as KeyboardEvent; + const SPACE_EVENT = new MockKeyboardEvent(SPACE) as KeyboardEvent; fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); fixture.componentInstance.trigger._handleKeydown(SPACE_EVENT); fixture.detectChanges(); @@ -724,7 +726,7 @@ describe('MdAutocomplete', () => { expect(input.hasAttribute('aria-activedescendant')) .toBe(false, 'Expected aria-activedescendant to be absent if no active item.'); - const DOWN_ARROW_EVENT = new FakeKeyboardEvent(DOWN_ARROW) as KeyboardEvent; + const DOWN_ARROW_EVENT = new MockKeyboardEvent(DOWN_ARROW) as KeyboardEvent; fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT); fixture.detectChanges(); @@ -866,7 +868,7 @@ describe('MdAutocomplete', () => { fixture.detectChanges(); const input = fixture.debugElement.query(By.css('input')).nativeElement; - dispatchEvent('focus', input); + dispatchFakeEvent(input, 'focus'); fixture.detectChanges(); expect(fixture.componentInstance.trigger.panelOpen) @@ -995,21 +997,6 @@ class AutocompleteWithoutForms { } -/** - * TODO: Move this to core testing utility until Angular has event faking - * support. - * - * Dispatches an event from an element. - * @param eventName Name of the event - * @param element The element from which the event will be dispatched. - */ -function dispatchEvent(eventName: string, element: HTMLElement): void { - let event = document.createEvent('Event'); - event.initEvent(eventName, true, true); - element.dispatchEvent(event); -} - - /** * Focuses an input, sets its value and dispatches * the `input` event, simulating the user typing. @@ -1019,11 +1006,11 @@ function dispatchEvent(eventName: string, element: HTMLElement): void { function typeInElement(value: string, element: HTMLInputElement) { element.focus(); element.value = value; - dispatchEvent('input', element); + dispatchFakeEvent(element, 'input'); } /** This is a mock keyboard event to test keyboard events in the autocomplete. */ -class FakeKeyboardEvent { +class MockKeyboardEvent { constructor(public keyCode: number) {} preventDefault() {} } diff --git a/src/lib/core/overlay/scroll/scroll-dispatcher.spec.ts b/src/lib/core/overlay/scroll/scroll-dispatcher.spec.ts index 77342af30f20..97c72048c95b 100644 --- a/src/lib/core/overlay/scroll/scroll-dispatcher.spec.ts +++ b/src/lib/core/overlay/scroll/scroll-dispatcher.spec.ts @@ -3,6 +3,7 @@ import {NgModule, Component, ViewChild, ElementRef} from '@angular/core'; import {ScrollDispatcher} from './scroll-dispatcher'; import {OverlayModule} from '../overlay-directives'; import {Scrollable} from './scrollable'; +import {dispatchFakeEvent} from '../../testing/dispatch-events'; describe('Scroll Dispatcher', () => { @@ -53,9 +54,7 @@ describe('Scroll Dispatcher', () => { // Emit a scroll event from the scrolling element in our component. // This event should be picked up by the scrollable directive and notify. // The notification should be picked up by the service. - const scrollEvent = document.createEvent('UIEvents'); - scrollEvent.initUIEvent('scroll', true, true, window, 0); - fixture.componentInstance.scrollingElement.nativeElement.dispatchEvent(scrollEvent); + dispatchFakeEvent(fixture.componentInstance.scrollingElement.nativeElement, 'scroll'); // The scrollable directive should have notified the service immediately. expect(hasDirectiveScrollNotified).toBe(true); diff --git a/src/lib/core/ripple/ripple.spec.ts b/src/lib/core/ripple/ripple.spec.ts index afd308a2aed3..ab2642801a86 100644 --- a/src/lib/core/ripple/ripple.spec.ts +++ b/src/lib/core/ripple/ripple.spec.ts @@ -3,6 +3,7 @@ import {Component, ViewChild} from '@angular/core'; import {MdRipple, MdRippleModule} from './ripple'; import {ViewportRuler} from '../overlay/position/viewport-ruler'; import {RIPPLE_FADE_OUT_DURATION, RIPPLE_FADE_IN_DURATION} from './ripple-renderer'; +import {dispatchMouseEvent} from '../testing/dispatch-events'; /** Creates a DOM mouse event. */ @@ -65,15 +66,6 @@ describe('MdRipple', () => { document.body.style.margin = originalBodyMargin; }); - function dispatchMouseEvent(type: string, offsetX = 0, offsetY = 0) { - let mouseEvent = createMouseEvent(type, { - clientX: rippleTarget.clientLeft + offsetX, - clientY: rippleTarget.clientTop + offsetY - }); - - rippleTarget.dispatchEvent(mouseEvent); - } - describe('basic ripple', () => { let rippleDirective: MdRipple; @@ -89,20 +81,20 @@ describe('MdRipple', () => { }); it('creates ripple on mousedown', () => { - dispatchMouseEvent('mousedown'); - dispatchMouseEvent('mouseup'); + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); - dispatchMouseEvent('mousedown'); - dispatchMouseEvent('mouseup'); + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(2); }); it('removes ripple after timeout', fakeAsync(() => { - dispatchMouseEvent('mousedown'); - dispatchMouseEvent('mouseup'); + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); @@ -140,8 +132,8 @@ describe('MdRipple', () => { let elementRect = rippleTarget.getBoundingClientRect(); // Dispatch a ripple at the following relative coordinates (X: 50| Y: 75) - dispatchMouseEvent('mousedown', 50, 75); - dispatchMouseEvent('mouseup'); + dispatchMouseEvent(rippleTarget, 'mousedown', 50, 75); + dispatchMouseEvent(rippleTarget, 'mouseup'); // Calculate distance from the click to farthest edge of the ripple target. let maxDistanceX = TARGET_WIDTH - 50; @@ -174,8 +166,8 @@ describe('MdRipple', () => { fixture.componentInstance.isDestroyed = true; fixture.detectChanges(); - dispatchMouseEvent('mousedown'); - dispatchMouseEvent('mouseup'); + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); }); @@ -245,15 +237,10 @@ describe('MdRipple', () => { rippleTarget.style.top = `${elementTop}px`; // Simulate a keyboard-triggered click by setting event coordinates to 0. - let clickEvent = createMouseEvent('mousedown', { - clientX: left + elementLeft - pageScrollLeft, - clientY: top + elementTop - pageScrollTop, - screenX: left + elementLeft, - screenY: top + elementTop - }); - - rippleTarget.dispatchEvent(clickEvent); - dispatchMouseEvent('mouseup'); + dispatchMouseEvent(rippleTarget, 'mousedown', + left + elementLeft - pageScrollLeft, + top + elementTop - pageScrollTop + ); let expectedRadius = Math.sqrt(250 * 250 + 125 * 125); let expectedLeft = left - expectedRadius; @@ -298,8 +285,8 @@ describe('MdRipple', () => { controller.color = backgroundColor; fixture.detectChanges(); - dispatchMouseEvent('mousedown'); - dispatchMouseEvent('mouseup'); + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); let ripple = rippleTarget.querySelector('.mat-ripple-element'); expect(window.getComputedStyle(ripple).backgroundColor).toBe(backgroundColor); @@ -309,16 +296,16 @@ describe('MdRipple', () => { controller.disabled = true; fixture.detectChanges(); - dispatchMouseEvent('mousedown'); - dispatchMouseEvent('mouseup'); + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); controller.disabled = false; fixture.detectChanges(); - dispatchMouseEvent('mousedown'); - dispatchMouseEvent('mouseup'); + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); }); @@ -352,8 +339,8 @@ describe('MdRipple', () => { let elementRect = rippleTarget.getBoundingClientRect(); // Click the ripple element 50 px to the right and 75px down from its upper left. - dispatchMouseEvent('mousedown', 50, 75); - dispatchMouseEvent('mouseup'); + dispatchMouseEvent(rippleTarget, 'mousedown', 50, 75); + dispatchMouseEvent(rippleTarget, 'mouseup'); // Because the centered input is true, the center of the ripple should be the midpoint of the // bounding rect. The ripple should expand to cover the rect corners, which are 150px @@ -379,8 +366,8 @@ describe('MdRipple', () => { let elementRect = rippleTarget.getBoundingClientRect(); // Click the ripple element 50 px to the right and 75px down from its upper left. - dispatchMouseEvent('mousedown', 50, 75); - dispatchMouseEvent('mouseup'); + dispatchMouseEvent(rippleTarget, 'mousedown', 50, 75); + dispatchMouseEvent(rippleTarget, 'mouseup'); let expectedLeft = elementRect.left + 50 - customRadius; let expectedTop = elementRect.top + 75 - customRadius; diff --git a/src/lib/core/style/focus-origin-monitor.spec.ts b/src/lib/core/style/focus-origin-monitor.spec.ts index bc20ecf413f1..4b0e3a7d6d94 100644 --- a/src/lib/core/style/focus-origin-monitor.spec.ts +++ b/src/lib/core/style/focus-origin-monitor.spec.ts @@ -4,6 +4,10 @@ import {StyleModule} from './index'; import {By} from '@angular/platform-browser'; import {TAB} from '../keyboard/keycodes'; import {FocusOrigin, FocusOriginMonitor, TOUCH_BUFFER_MS} from './focus-origin-monitor'; +import { + dispatchFakeEvent, dispatchKeyboardEvent, dispatchMouseEvent +} from '../testing/dispatch-events'; + describe('FocusOriginMonitor', () => { let fixture: ComponentFixture; @@ -53,7 +57,7 @@ describe('FocusOriginMonitor', () => { it('should detect focus via keyboard', async(() => { // Simulate focus via keyboard. - dispatchKeydownEvent(document, TAB); + dispatchKeyboardEvent(document, TAB); buttonElement.focus(); fixture.detectChanges(); @@ -72,7 +76,7 @@ describe('FocusOriginMonitor', () => { it('should detect focus via mouse', async(() => { // Simulate focus via mouse. - dispatchMousedownEvent(buttonElement); + dispatchMouseEvent(buttonElement, 'mousedown'); buttonElement.focus(); fixture.detectChanges(); @@ -268,7 +272,7 @@ describe('cdkMonitorFocus', () => { it('should detect focus via keyboard', async(() => { // Simulate focus via keyboard. - dispatchKeydownEvent(document, TAB); + dispatchKeyboardEvent(document, TAB, 'keydown'); buttonElement.focus(); fixture.detectChanges(); @@ -287,7 +291,7 @@ describe('cdkMonitorFocus', () => { it('should detect focus via mouse', async(() => { // Simulate focus via mouse. - dispatchMousedownEvent(buttonElement); + dispatchMouseEvent(buttonElement, 'mousedown'); buttonElement.focus(); fixture.detectChanges(); @@ -306,7 +310,7 @@ describe('cdkMonitorFocus', () => { it('should detect focus via touch', async(() => { // Simulate focus via touch. - dispatchTouchstartEvent(buttonElement); + dispatchMouseEvent(buttonElement, 'touchstart'); buttonElement.focus(); fixture.detectChanges(); @@ -476,42 +480,6 @@ class ComplexComponentWithMonitorElementFocus {} class ComplexComponentWithMonitorSubtreeFocus {} -// TODO(devversion): move helper functions into a global utility file. See #2902 - -/** Dispatches a mousedown event on the specified element. */ -function dispatchMousedownEvent(element: Node) { - let event = document.createEvent('MouseEvent'); - event.initMouseEvent( - 'mousedown', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); - element.dispatchEvent(event); -} - -/** Dispatches a mousedown event on the specified element. */ -function dispatchTouchstartEvent(element: Node) { - let event = document.createEvent('MouseEvent'); - event.initMouseEvent( - 'touchstart', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); - element.dispatchEvent(event); -} - -/** Dispatches a keydown event on the specified element. */ -function dispatchKeydownEvent(element: Node, keyCode: number) { - let event: any = document.createEvent('KeyboardEvent'); - (event.initKeyEvent || event.initKeyboardEvent).bind(event)( - 'keydown', true, true, window, 0, 0, 0, 0, 0, keyCode); - Object.defineProperty(event, 'keyCode', { - get: function() { return keyCode; } - }); - element.dispatchEvent(event); -} - -/** Dispatches a focus event on the specified element. */ -function dispatchFocusEvent(element: Node, type = 'focus') { - let event = document.createEvent('Event'); - event.initEvent(type, true, true); - element.dispatchEvent(event); -} - /** * Patches an elements focus and blur methods to properly emit focus events when the browser is * blurred. @@ -524,9 +492,9 @@ function patchElementFocus(element: HTMLElement) { let _nativeButtonBlur = element.blur.bind(element); element.focus = () => { - document.hasFocus() ? _nativeButtonFocus() : dispatchFocusEvent(element); + document.hasFocus() ? _nativeButtonFocus() : dispatchFakeEvent(element, 'focus'); }; element.blur = () => { - document.hasFocus() ? _nativeButtonBlur() : dispatchFocusEvent(element, 'blur'); + document.hasFocus() ? _nativeButtonBlur() : dispatchFakeEvent(element, 'blur'); }; } diff --git a/src/lib/core/testing/dispatch-events.ts b/src/lib/core/testing/dispatch-events.ts new file mode 100644 index 000000000000..78c973870155 --- /dev/null +++ b/src/lib/core/testing/dispatch-events.ts @@ -0,0 +1,26 @@ +import { + createFakeEvent, + createKeyboardEvent, + createMouseEvent, + createTransitionEndEvent +} from './event-objects'; + +/** Shorthand to dispatch a fake event on a specified node. */ +export function dispatchFakeEvent(node: Node, eventName: string) { + node.dispatchEvent(createFakeEvent(eventName)); +} + +/** Shorthand to dispatch a keyboard event with a specified key code. */ +export function dispatchKeyboardEvent(node: Node, keyCode: number, type = 'keydown') { + node.dispatchEvent(createKeyboardEvent(type, keyCode)); +} + +/** Shorthand to dispatch a mouse event on the specified coordinates. */ +export function dispatchMouseEvent(node: Node, type: string, x = 0, y = 0) { + node.dispatchEvent(createMouseEvent(type, x, y)); +} + +/** Shorthand to dispatch a transition event with a specified property. */ +export function dispatchTransitionEndEvent(node: Node, propertyName: string, elapsedTime = 0) { + node.dispatchEvent(createTransitionEndEvent(propertyName, elapsedTime)); +} diff --git a/src/lib/core/testing/event-objects.ts b/src/lib/core/testing/event-objects.ts new file mode 100644 index 000000000000..f07782dd6245 --- /dev/null +++ b/src/lib/core/testing/event-objects.ts @@ -0,0 +1,62 @@ +/** Creates a browser MouseEvent with the specified options. */ +export function createMouseEvent(type: string, x = 0, y = 0) { + let event = document.createEvent('MouseEvent'); + + event.initMouseEvent(type, + false, /* canBubble */ + false, /* cancelable */ + window, /* view */ + 0, /* detail */ + x, /* screenX */ + y, /* screenY */ + x, /* clientX */ + y, /* clientY */ + false, /* ctrlKey */ + false, /* altKey */ + false, /* shiftKey */ + false, /* metaKey */ + 0, /* button */ + null /* relatedTarget */); + + return event; +} + +/** Dispatches a keydown event from an element. */ +export function createKeyboardEvent(eventType: string, keyCode: number) { + let event = document.createEvent('KeyboardEvent') as any; + // Firefox does not support `initKeyboardEvent`, but supports `initKeyEvent`. + let initEventFn = (event.initKeyEvent || event.initKeyboardEvent).bind(event); + + initEventFn(eventType, true, true, window, 0, 0, 0, 0, 0, keyCode); + + // Webkit Browsers don't set the keyCode when calling the init function. + // See related bug https://bugs.webkit.org/show_bug.cgi?id=16735 + Object.defineProperty(event, 'keyCode', { + get: function() { return keyCode; } + }); + + return event; +} + +/** Creates a transition event with the specified property name. */ +export function createTransitionEndEvent(propertyName: string, elapsedTime = 0) { + // Some browsers have the TransitionEvent class, but once the class is being instantiated + // the browser will throw an exception. Those browsers don't support the constructor yet. + // To ensure that those browsers also work, the TransitionEvent is created by using the + // deprecated `initTransitionEvent` function. + try { + // TypeScript does not have valid types for the TransitionEvent class, so use `any`. + return new (TransitionEvent as any)('transitionend', {propertyName, elapsedTime}); + } catch (e) { + let event = document.createEvent('TransitionEvent'); + event.initTransitionEvent('transitionend', false, false, propertyName, elapsedTime); + return event; + } +} + +/** Creates a fake event object with any desired event type. */ +export function createFakeEvent(eventName: string) { + let event = document.createEvent('Event'); + event.initEvent(eventName, true, true); + return event; +} diff --git a/src/lib/radio/radio.spec.ts b/src/lib/radio/radio.spec.ts index 06494c5f2c11..92d0ee86d414 100644 --- a/src/lib/radio/radio.spec.ts +++ b/src/lib/radio/radio.spec.ts @@ -5,6 +5,7 @@ import {By} from '@angular/platform-browser'; import {MdRadioGroup, MdRadioButton, MdRadioChange, MdRadioModule} from './radio'; import {ViewportRuler} from '../core/overlay/position/viewport-ruler'; import {FakeViewportRuler} from '../core/overlay/position/fake-viewport-ruler'; +import {dispatchFakeEvent} from '../core/testing/dispatch-events'; describe('MdRadio', () => { @@ -183,12 +184,12 @@ describe('MdRadio', () => { expect(nativeRadioInput.classList).not.toContain('mat-radio-focused'); - dispatchEvent('focus', nativeRadioInput); + dispatchFakeEvent(nativeRadioInput, 'focus'); fixture.detectChanges(); expect(radioNativeElements[0].classList).toContain('mat-radio-focused'); - dispatchEvent('blur', nativeRadioInput); + dispatchFakeEvent(nativeRadioInput, 'blur'); fixture.detectChanges(); expect(radioNativeElements[0].classList).not.toContain('mat-radio-focused'); @@ -202,7 +203,7 @@ describe('MdRadio', () => { expect(radioNativeElements[0].classList).toContain('mat-radio-focused'); - dispatchEvent('blur', nativeRadioInput); + dispatchFakeEvent(nativeRadioInput, 'blur'); fixture.detectChanges(); expect(radioNativeElements[0].classList).not.toContain('mat-radio-focused'); @@ -421,7 +422,7 @@ describe('MdRadio', () => { })); it('should update the ngModel value when selecting a radio button', () => { - dispatchEvent('change', innerRadios[1].nativeElement); + dispatchFakeEvent(innerRadios[1].nativeElement, 'change'); fixture.detectChanges(); expect(testComponent.modelValue).toBe('chocolate'); }); @@ -430,11 +431,11 @@ describe('MdRadio', () => { expect(testComponent.modelValue).toBeUndefined(); expect(testComponent.lastEvent).toBeUndefined(); - dispatchEvent('change', innerRadios[1].nativeElement); + dispatchFakeEvent(innerRadios[1].nativeElement, 'change'); fixture.detectChanges(); expect(testComponent.lastEvent.value).toBe('chocolate'); - dispatchEvent('change', innerRadios[0].nativeElement); + dispatchFakeEvent(innerRadios[0].nativeElement, 'change'); fixture.detectChanges(); expect(testComponent.lastEvent.value).toBe('vanilla'); }); @@ -651,16 +652,3 @@ class RadioGroupWithNgModel { class RadioGroupWithFormControl { formControl = new FormControl(); } - -// TODO(jelbourn): remove everything below when Angular supports faking events. - -/** - * Dispatches an event from an element. - * @param eventName Name of the event - * @param element The element from which the event will be dispatched. - */ -function dispatchEvent(eventName: string, element: HTMLElement): void { - let event = document.createEvent('Event'); - event.initEvent(eventName, true, true); - element.dispatchEvent(event); -} diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index ab82b27c9634..63e1f8f848f6 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -18,6 +18,7 @@ import { ControlValueAccessor, FormControl, FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms'; import {ViewportRuler} from '../core/overlay/position/viewport-ruler'; +import {dispatchFakeEvent} from '../core/testing/dispatch-events'; describe('MdSelect', () => { let overlayContainerElement: HTMLElement; @@ -430,7 +431,7 @@ describe('MdSelect', () => { .toEqual(false, `Expected the control to start off as untouched.`); trigger.click(); - dispatchEvent('blur', trigger); + dispatchFakeEvent(trigger, 'blur'); fixture.detectChanges(); expect(fixture.componentInstance.control.touched) .toEqual(false, `Expected the control to stay untouched when menu opened.`); @@ -438,7 +439,7 @@ describe('MdSelect', () => { const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement; backdrop.click(); - dispatchEvent('blur', trigger); + dispatchFakeEvent(trigger, 'blur'); fixture.detectChanges(); expect(fixture.componentInstance.control.touched) .toEqual(true, `Expected the control to be touched as soon as focus left the select.`); @@ -1589,20 +1590,6 @@ class FloatPlaceholderSelect { } -/** - * TODO: Move this to core testing utility until Angular has event faking - * support. - * - * Dispatches an event from an element. - * @param eventName Name of the event - * @param element The element from which the event will be dispatched. - */ -function dispatchEvent(eventName: string, element: HTMLElement): void { - let event = document.createEvent('Event'); - event.initEvent(eventName, true, true); - element.dispatchEvent(event); -} - class FakeViewportRuler { getViewportRect() { return { diff --git a/src/lib/slide-toggle/slide-toggle.spec.ts b/src/lib/slide-toggle/slide-toggle.spec.ts index ed79ca9882af..d399053a8921 100644 --- a/src/lib/slide-toggle/slide-toggle.spec.ts +++ b/src/lib/slide-toggle/slide-toggle.spec.ts @@ -4,6 +4,7 @@ import {Component} from '@angular/core'; import {MdSlideToggle, MdSlideToggleChange, MdSlideToggleModule} from './slide-toggle'; import {FormsModule, NgControl, ReactiveFormsModule, FormControl} from '@angular/forms'; import {TestGestureConfig} from '../slider/test-gesture-config'; +import {dispatchFakeEvent} from '../core/testing/dispatch-events'; describe('MdSlideToggle', () => { @@ -329,7 +330,7 @@ describe('MdSlideToggle', () => { it('should correctly set the slide-toggle to checked on focus', () => { expect(slideToggleElement.classList).not.toContain('mat-slide-toggle-focused'); - dispatchFocusChangeEvent('focus', inputElement); + dispatchFakeEvent(inputElement, 'focus'); fixture.detectChanges(); expect(slideToggleElement.classList).toContain('mat-slide-toggle-focused'); @@ -616,17 +617,6 @@ describe('MdSlideToggle', () => { }); }); -/** - * Dispatches a focus change event from an element. - * @param eventName Name of the event, either 'focus' or 'blur'. - * @param element The element from which the event will be dispatched. - */ -function dispatchFocusChangeEvent(eventName: string, element: HTMLElement): void { - let event = document.createEvent('Event'); - event.initEvent(eventName, true, true); - element.dispatchEvent(event); -} - @Component({ selector: 'slide-toggle-test-app', template: ` diff --git a/src/lib/slider/slider.spec.ts b/src/lib/slider/slider.spec.ts index 5f839c8ef7f7..60e4d2b95ad4 100644 --- a/src/lib/slider/slider.spec.ts +++ b/src/lib/slider/slider.spec.ts @@ -15,6 +15,7 @@ import { HOME, LEFT_ARROW } from '../core/keyboard/keycodes'; +import {dispatchKeyboardEvent, dispatchMouseEvent} from '../core/testing/dispatch-events'; describe('MdSlider', () => { @@ -728,7 +729,7 @@ describe('MdSlider', () => { it('should update the model on keydown', () => { expect(testComponent.val).toBe(0); - dispatchKeydownEvent(sliderNativeElement, UP_ARROW); + dispatchKeyboardEvent(sliderNativeElement, UP_ARROW); fixture.detectChanges(); expect(testComponent.val).toBe(1); @@ -948,14 +949,14 @@ describe('MdSlider', () => { }); it('should increment slider by 1 on up arrow pressed', () => { - dispatchKeydownEvent(sliderNativeElement, UP_ARROW); + dispatchKeyboardEvent(sliderNativeElement, UP_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(1); }); it('should increment slider by 1 on right arrow pressed', () => { - dispatchKeydownEvent(sliderNativeElement, RIGHT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, RIGHT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(1); @@ -964,7 +965,7 @@ describe('MdSlider', () => { it('should decrement slider by 1 on down arrow pressed', () => { sliderInstance.value = 100; - dispatchKeydownEvent(sliderNativeElement, DOWN_ARROW); + dispatchKeyboardEvent(sliderNativeElement, DOWN_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(99); @@ -973,14 +974,14 @@ describe('MdSlider', () => { it('should decrement slider by 1 on left arrow pressed', () => { sliderInstance.value = 100; - dispatchKeydownEvent(sliderNativeElement, LEFT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, LEFT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(99); }); it('should increment slider by 10 on page up pressed', () => { - dispatchKeydownEvent(sliderNativeElement, PAGE_UP); + dispatchKeyboardEvent(sliderNativeElement, PAGE_UP); fixture.detectChanges(); expect(sliderInstance.value).toBe(10); @@ -989,14 +990,14 @@ describe('MdSlider', () => { it('should decrement slider by 10 on page down pressed', () => { sliderInstance.value = 100; - dispatchKeydownEvent(sliderNativeElement, PAGE_DOWN); + dispatchKeyboardEvent(sliderNativeElement, PAGE_DOWN); fixture.detectChanges(); expect(sliderInstance.value).toBe(90); }); it('should set slider to max on end pressed', () => { - dispatchKeydownEvent(sliderNativeElement, END); + dispatchKeyboardEvent(sliderNativeElement, END); fixture.detectChanges(); expect(sliderInstance.value).toBe(100); @@ -1005,7 +1006,7 @@ describe('MdSlider', () => { it('should set slider to min on home pressed', () => { sliderInstance.value = 100; - dispatchKeydownEvent(sliderNativeElement, HOME); + dispatchKeyboardEvent(sliderNativeElement, HOME); fixture.detectChanges(); expect(sliderInstance.value).toBe(0); @@ -1066,7 +1067,7 @@ describe('MdSlider', () => { testComponent.invert = true; fixture.detectChanges(); - dispatchKeydownEvent(sliderNativeElement, RIGHT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, RIGHT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(1); @@ -1077,7 +1078,7 @@ describe('MdSlider', () => { sliderInstance.value = 100; fixture.detectChanges(); - dispatchKeydownEvent(sliderNativeElement, LEFT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, LEFT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(99); @@ -1088,7 +1089,7 @@ describe('MdSlider', () => { sliderInstance.value = 100; fixture.detectChanges(); - dispatchKeydownEvent(sliderNativeElement, RIGHT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, RIGHT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(99); @@ -1098,7 +1099,7 @@ describe('MdSlider', () => { testComponent.dir = 'rtl'; fixture.detectChanges(); - dispatchKeydownEvent(sliderNativeElement, LEFT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, LEFT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(1); @@ -1110,7 +1111,7 @@ describe('MdSlider', () => { sliderInstance.value = 100; fixture.detectChanges(); - dispatchKeydownEvent(sliderNativeElement, RIGHT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, RIGHT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(99); @@ -1121,7 +1122,7 @@ describe('MdSlider', () => { testComponent.invert = true; fixture.detectChanges(); - dispatchKeydownEvent(sliderNativeElement, LEFT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, LEFT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(1); @@ -1334,11 +1335,7 @@ function dispatchClickEventSequence(sliderElement: HTMLElement, percentage: numb let y = dimensions.top + (dimensions.height * percentage); dispatchMouseenterEvent(sliderElement); - - let event = document.createEvent('MouseEvent'); - event.initMouseEvent( - 'click', true, true, window, 0, x, y, x, y, false, false, false, false, 0, null); - sliderElement.dispatchEvent(event); + dispatchMouseEvent(sliderElement, 'click', x, y); } /** @@ -1426,23 +1423,5 @@ function dispatchMouseenterEvent(element: HTMLElement): void { let y = dimensions.top; let x = dimensions.left; - let event = document.createEvent('MouseEvent'); - event.initMouseEvent( - 'mouseenter', true, true, window, 0, x, y, x, y, false, false, false, false, 0, null); - element.dispatchEvent(event); -} - -/** - * Dispatches a keydown event from an element. - * @param element The element from which the event will be dispatched. - * @param keyCode The key code of the key being pressed. - */ -function dispatchKeydownEvent(element: HTMLElement, keyCode: number): void { - let event: any = document.createEvent('KeyboardEvent'); - (event.initKeyEvent || event.initKeyboardEvent).bind(event)( - 'keydown', true, true, window, 0, 0, 0, 0, 0, keyCode); - Object.defineProperty(event, 'keyCode', { - get: function() { return keyCode; } - }); - element.dispatchEvent(event); + dispatchMouseEvent(element, 'mouseenter', x, y); } diff --git a/src/lib/tabs/tab-header.spec.ts b/src/lib/tabs/tab-header.spec.ts index aa74d9401ca1..dc687f41f923 100644 --- a/src/lib/tabs/tab-header.spec.ts +++ b/src/lib/tabs/tab-header.spec.ts @@ -10,6 +10,7 @@ import {MdTabLabelWrapper} from './tab-label-wrapper'; import {RIGHT_ARROW, LEFT_ARROW, ENTER} from '../core/keyboard/keycodes'; import {FakeViewportRuler} from '../core/overlay/position/fake-viewport-ruler'; import {ViewportRuler} from '../core/overlay/position/viewport-ruler'; +import {dispatchKeyboardEvent} from '../core/testing/dispatch-events'; describe('MdTabHeader', () => { @@ -106,18 +107,18 @@ describe('MdTabHeader', () => { expect(appComponent.mdTabHeader.focusIndex).toBe(0); // Move focus right to 2 - dispatchKeydownEvent(appComponent.mdTabHeader._tabListContainer.nativeElement, RIGHT_ARROW); + dispatchKeyboardEvent(appComponent.mdTabHeader._tabListContainer.nativeElement, RIGHT_ARROW); fixture.detectChanges(); expect(appComponent.mdTabHeader.focusIndex).toBe(2); // Select the focused index 2 expect(appComponent.selectedIndex).toBe(0); - dispatchKeydownEvent(appComponent.mdTabHeader._tabListContainer.nativeElement, ENTER); + dispatchKeyboardEvent(appComponent.mdTabHeader._tabListContainer.nativeElement, ENTER); fixture.detectChanges(); expect(appComponent.selectedIndex).toBe(2); // Move focus right to 0 - dispatchKeydownEvent(appComponent.mdTabHeader._tabListContainer.nativeElement, LEFT_ARROW); + dispatchKeyboardEvent(appComponent.mdTabHeader._tabListContainer.nativeElement, LEFT_ARROW); fixture.detectChanges(); expect(appComponent.mdTabHeader.focusIndex).toBe(0); }); @@ -193,18 +194,6 @@ describe('MdTabHeader', () => { }); - -/** Dispatches a keydown event from an element. */ -function dispatchKeydownEvent(element: HTMLElement, keyCode: number): void { - let event: any = document.createEvent('KeyboardEvent'); - (event.initKeyEvent || event.initKeyboardEvent).bind(event)( - 'keydown', true, true, window, 0, 0, 0, 0, 0, keyCode); - Object.defineProperty(event, 'keyCode', { - get: function() { return keyCode; } - }); - element.dispatchEvent(event); -} - interface Tab { label: string; disabled?: boolean; diff --git a/src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts b/src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts index fdd55a47c33b..facdca13203a 100644 --- a/src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts +++ b/src/lib/tabs/tab-nav-bar/tab-nav-bar.spec.ts @@ -4,6 +4,7 @@ import {Component} from '@angular/core'; import {By} from '@angular/platform-browser'; import {ViewportRuler} from '../../core/overlay/position/viewport-ruler'; import {FakeViewportRuler} from '../../core/overlay/position/fake-viewport-ruler'; +import {dispatchMouseEvent} from '../../core/testing/dispatch-events'; describe('MdTabNavBar', () => { @@ -50,15 +51,11 @@ describe('MdTabNavBar', () => { fixture.detectChanges(); let link = fixture.debugElement.nativeElement.querySelector('.mat-tab-link'); - let mouseEvent = document.createEvent('MouseEvents'); fixture.componentInstance.isDestroyed = true; fixture.detectChanges(); - mouseEvent.initMouseEvent('mousedown', false, false, window, 0, 0, 0, 0, 0, false, false, - false, false, 0, null); - - link.dispatchEvent(mouseEvent); + dispatchMouseEvent(link, 'mousedown'); expect(link.querySelector('.mat-ripple-element')) .toBeFalsy('Expected no ripple to be created when ripple target is destroyed.'); diff --git a/src/lib/tsconfig-srcs.json b/src/lib/tsconfig-srcs.json index fea7a527507f..f90842dfd36f 100644 --- a/src/lib/tsconfig-srcs.json +++ b/src/lib/tsconfig-srcs.json @@ -19,6 +19,9 @@ ] }, "exclude": [ + /* Exclude testing utilities in releases. */ + "core/testing/", + "**/*.spec.*", "system-config-spec.ts" ], diff --git a/tools/gulp/tasks/components.ts b/tools/gulp/tasks/components.ts index f2cc6030ef45..379e0b559256 100644 --- a/tools/gulp/tasks/components.ts +++ b/tools/gulp/tasks/components.ts @@ -25,7 +25,7 @@ const gulpIf = require('gulp-if'); // for unit tests (karma). /** Path to the tsconfig used for ESM output. */ -const tsconfigPath = path.relative(PROJECT_ROOT, path.join(COMPONENTS_DIR, 'tsconfig.json')); +const tsconfigPath = path.relative(PROJECT_ROOT, path.join(COMPONENTS_DIR, 'tsconfig-srcs.json')); /** [Watch task] Rebuilds (ESM output) whenever ts, scss, or html sources change. */ From 250204d15767fb038eb8a2afcb53c07de7dc2f64 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Mon, 27 Feb 2017 11:23:01 +0100 Subject: [PATCH 2/2] Provide consistent testing API --- src/lib/autocomplete/autocomplete.spec.ts | 2 +- src/lib/checkbox/checkbox.spec.ts | 8 +--- src/lib/core/ripple/ripple.spec.ts | 42 +++---------------- .../core/style/focus-origin-monitor.spec.ts | 6 +-- src/lib/core/testing/dispatch-events.ts | 14 ++----- src/lib/core/testing/event-objects.ts | 24 ++--------- src/lib/dialog/dialog.spec.ts | 17 ++------ src/lib/slide-toggle/slide-toggle.spec.ts | 7 ---- src/lib/slider/slider.spec.ts | 30 ++++++------- src/lib/tabs/tab-header.spec.ts | 8 ++-- src/lib/tooltip/tooltip.spec.ts | 5 +-- 11 files changed, 44 insertions(+), 119 deletions(-) diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index 7f6dd8c9672e..26c609fc1c7d 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -1003,7 +1003,7 @@ class AutocompleteWithoutForms { * @param value Value to be set on the input. * @param element Element onto which to set the value. */ -function typeInElement(value: string, element: HTMLInputElement) { +function typeInElement(value: string, element: HTMLInputElement, autoFocus = true) { element.focus(); element.value = value; dispatchFakeEvent(element, 'input'); diff --git a/src/lib/checkbox/checkbox.spec.ts b/src/lib/checkbox/checkbox.spec.ts index 32d255aa05f5..d1ae0a1e23a1 100644 --- a/src/lib/checkbox/checkbox.spec.ts +++ b/src/lib/checkbox/checkbox.spec.ts @@ -5,6 +5,7 @@ import {By} from '@angular/platform-browser'; import {MdCheckbox, MdCheckboxChange, MdCheckboxModule} from './checkbox'; import {ViewportRuler} from '../core/overlay/position/viewport-ruler'; import {FakeViewportRuler} from '../core/overlay/position/fake-viewport-ruler'; +import {dispatchFakeEvent} from '../core/testing/dispatch-events'; describe('MdCheckbox', () => { @@ -785,10 +786,3 @@ class CheckboxWithChangeEvent { class CheckboxWithFormControl { formControl = new FormControl(); } - -// TODO(devversion): replace with global utility once pull request #2943 is merged. -function dispatchFakeEvent(element: HTMLElement, eventName: string): void { - let event = document.createEvent('Event'); - event.initEvent(eventName, true, true); - element.dispatchEvent(event); -} diff --git a/src/lib/core/ripple/ripple.spec.ts b/src/lib/core/ripple/ripple.spec.ts index ab2642801a86..68e2c62238f4 100644 --- a/src/lib/core/ripple/ripple.spec.ts +++ b/src/lib/core/ripple/ripple.spec.ts @@ -5,33 +5,6 @@ import {ViewportRuler} from '../overlay/position/viewport-ruler'; import {RIPPLE_FADE_OUT_DURATION, RIPPLE_FADE_IN_DURATION} from './ripple-renderer'; import {dispatchMouseEvent} from '../testing/dispatch-events'; - -/** Creates a DOM mouse event. */ -const createMouseEvent = (eventType: string, dict: any = {}) => { - // Ideally this would just be "return new MouseEvent(eventType, dict)". But IE11 doesn't support - // the MouseEvent constructor, and Edge inexplicably divides clientX and clientY by 100 to get - // pageX and pageY. (Really. After "e = new MouseEvent('click', {clientX: 200, clientY: 300})", - // e.clientX is 200, e.pageX is 2, e.clientY is 300, and e.pageY is 3.) - // So instead we use the deprecated createEvent/initMouseEvent API, which works everywhere. - const event = document.createEvent('MouseEvents'); - event.initMouseEvent(eventType, - false, /* canBubble */ - false, /* cancelable */ - window, /* view */ - 0, /* detail */ - dict.screenX || 0, - dict.screenY || 0, - dict.clientX || 0, - dict.clientY || 0, - false, /* ctrlKey */ - false, /* altKey */ - false, /* shiftKey */ - false, /* metaKey */ - 0, /* button */ - null /* relatedTarget */); - return event; -}; - /** Extracts the numeric value of a pixel size string like '123px'. */ const pxStringToFloat = (s: string) => { return parseFloat(s.replace('px', '')); @@ -176,8 +149,8 @@ describe('MdRipple', () => { const spy = jasmine.createSpy('zone unstable callback'); const subscription = fixture.ngZone.onUnstable.subscribe(spy); - dispatchMouseEvent('mousedown'); - dispatchMouseEvent('mouseup'); + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); expect(spy).not.toHaveBeenCalled(); subscription.unsubscribe(); @@ -314,11 +287,8 @@ describe('MdRipple', () => { let alternateTrigger = fixture.debugElement.nativeElement .querySelector('.alternateTrigger') as HTMLElement; - let mousedownEvent = createMouseEvent('mousedown'); - let mouseupEvent = createMouseEvent('mouseup'); - - alternateTrigger.dispatchEvent(mousedownEvent); - alternateTrigger.dispatchEvent(mouseupEvent); + dispatchMouseEvent(alternateTrigger, 'mousedown'); + dispatchMouseEvent(alternateTrigger, 'mouseup'); expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); @@ -326,8 +296,8 @@ describe('MdRipple', () => { controller.trigger = alternateTrigger; fixture.detectChanges(); - alternateTrigger.dispatchEvent(mousedownEvent); - alternateTrigger.dispatchEvent(mouseupEvent); + dispatchMouseEvent(alternateTrigger, 'mousedown'); + dispatchMouseEvent(alternateTrigger, 'mouseup'); expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); }); diff --git a/src/lib/core/style/focus-origin-monitor.spec.ts b/src/lib/core/style/focus-origin-monitor.spec.ts index 4b0e3a7d6d94..7f17f3ea7cb6 100644 --- a/src/lib/core/style/focus-origin-monitor.spec.ts +++ b/src/lib/core/style/focus-origin-monitor.spec.ts @@ -57,7 +57,7 @@ describe('FocusOriginMonitor', () => { it('should detect focus via keyboard', async(() => { // Simulate focus via keyboard. - dispatchKeyboardEvent(document, TAB); + dispatchKeyboardEvent(document, 'keydown', TAB); buttonElement.focus(); fixture.detectChanges(); @@ -95,7 +95,7 @@ describe('FocusOriginMonitor', () => { it('should detect focus via touch', async(() => { // Simulate focus via touch. - dispatchTouchstartEvent(buttonElement); + dispatchMouseEvent(buttonElement, 'touchstart'); buttonElement.focus(); fixture.detectChanges(); @@ -272,7 +272,7 @@ describe('cdkMonitorFocus', () => { it('should detect focus via keyboard', async(() => { // Simulate focus via keyboard. - dispatchKeyboardEvent(document, TAB, 'keydown'); + dispatchKeyboardEvent(document, 'keydown', TAB); buttonElement.focus(); fixture.detectChanges(); diff --git a/src/lib/core/testing/dispatch-events.ts b/src/lib/core/testing/dispatch-events.ts index 78c973870155..6e57db2a66b1 100644 --- a/src/lib/core/testing/dispatch-events.ts +++ b/src/lib/core/testing/dispatch-events.ts @@ -1,17 +1,16 @@ import { createFakeEvent, createKeyboardEvent, - createMouseEvent, - createTransitionEndEvent + createMouseEvent } from './event-objects'; /** Shorthand to dispatch a fake event on a specified node. */ -export function dispatchFakeEvent(node: Node, eventName: string) { - node.dispatchEvent(createFakeEvent(eventName)); +export function dispatchFakeEvent(node: Node, type: string) { + node.dispatchEvent(createFakeEvent(type)); } /** Shorthand to dispatch a keyboard event with a specified key code. */ -export function dispatchKeyboardEvent(node: Node, keyCode: number, type = 'keydown') { +export function dispatchKeyboardEvent(node: Node, type: string, keyCode: number) { node.dispatchEvent(createKeyboardEvent(type, keyCode)); } @@ -19,8 +18,3 @@ export function dispatchKeyboardEvent(node: Node, keyCode: number, type = 'keydo export function dispatchMouseEvent(node: Node, type: string, x = 0, y = 0) { node.dispatchEvent(createMouseEvent(type, x, y)); } - -/** Shorthand to dispatch a transition event with a specified property. */ -export function dispatchTransitionEndEvent(node: Node, propertyName: string, elapsedTime = 0) { - node.dispatchEvent(createTransitionEndEvent(propertyName, elapsedTime)); -} diff --git a/src/lib/core/testing/event-objects.ts b/src/lib/core/testing/event-objects.ts index f07782dd6245..6bc030b5734a 100644 --- a/src/lib/core/testing/event-objects.ts +++ b/src/lib/core/testing/event-objects.ts @@ -22,12 +22,12 @@ export function createMouseEvent(type: string, x = 0, y = 0) { } /** Dispatches a keydown event from an element. */ -export function createKeyboardEvent(eventType: string, keyCode: number) { +export function createKeyboardEvent(type: string, keyCode: number) { let event = document.createEvent('KeyboardEvent') as any; // Firefox does not support `initKeyboardEvent`, but supports `initKeyEvent`. let initEventFn = (event.initKeyEvent || event.initKeyboardEvent).bind(event); - initEventFn(eventType, true, true, window, 0, 0, 0, 0, 0, keyCode); + initEventFn(type, true, true, window, 0, 0, 0, 0, 0, keyCode); // Webkit Browsers don't set the keyCode when calling the init function. // See related bug https://bugs.webkit.org/show_bug.cgi?id=16735 @@ -38,25 +38,9 @@ export function createKeyboardEvent(eventType: string, keyCode: number) { return event; } -/** Creates a transition event with the specified property name. */ -export function createTransitionEndEvent(propertyName: string, elapsedTime = 0) { - // Some browsers have the TransitionEvent class, but once the class is being instantiated - // the browser will throw an exception. Those browsers don't support the constructor yet. - // To ensure that those browsers also work, the TransitionEvent is created by using the - // deprecated `initTransitionEvent` function. - try { - // TypeScript does not have valid types for the TransitionEvent class, so use `any`. - return new (TransitionEvent as any)('transitionend', {propertyName, elapsedTime}); - } catch (e) { - let event = document.createEvent('TransitionEvent'); - event.initTransitionEvent('transitionend', false, false, propertyName, elapsedTime); - return event; - } -} - /** Creates a fake event object with any desired event type. */ -export function createFakeEvent(eventName: string) { +export function createFakeEvent(type: string) { let event = document.createEvent('Event'); - event.initEvent(eventName, true, true); + event.initEvent(type, true, true); return event; } diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts index 48abaa1f438c..53e67ca2066a 100644 --- a/src/lib/dialog/dialog.spec.ts +++ b/src/lib/dialog/dialog.spec.ts @@ -22,6 +22,7 @@ import {MdDialogContainer} from './dialog-container'; import {OverlayContainer, ESCAPE} from '../core'; import {MdDialogRef} from './dialog-ref'; import {MD_DIALOG_DATA} from './dialog-injector'; +import {dispatchKeyboardEvent} from '../core/testing/dispatch-events'; describe('MdDialog', () => { @@ -130,7 +131,7 @@ describe('MdDialog', () => { viewContainerRef: testViewContainerRef }); - dispatchKeydownEvent(document, ESCAPE); + dispatchKeyboardEvent(document, 'keydown', ESCAPE); viewContainerFixture.detectChanges(); viewContainerFixture.whenStable().then(() => { @@ -366,7 +367,7 @@ describe('MdDialog', () => { }); viewContainerFixture.detectChanges(); - dispatchKeydownEvent(document, ESCAPE); + dispatchKeyboardEvent(document, 'keydown', ESCAPE); expect(overlayContainerElement.querySelector('md-dialog-container')).toBeTruthy(); }); @@ -613,15 +614,3 @@ const TEST_DIRECTIVES = [ ], }) class DialogTestModule { } - - -// TODO(crisbeto): switch to using function from common testing utils once #2943 is merged. -function dispatchKeydownEvent(element: Node, keyCode: number) { - let event: any = document.createEvent('KeyboardEvent'); - (event.initKeyEvent || event.initKeyboardEvent).bind(event)( - 'keydown', true, true, window, 0, 0, 0, 0, 0, keyCode); - Object.defineProperty(event, 'keyCode', { - get: function() { return keyCode; } - }); - element.dispatchEvent(event); -} diff --git a/src/lib/slide-toggle/slide-toggle.spec.ts b/src/lib/slide-toggle/slide-toggle.spec.ts index d399053a8921..2d2441909dbf 100644 --- a/src/lib/slide-toggle/slide-toggle.spec.ts +++ b/src/lib/slide-toggle/slide-toggle.spec.ts @@ -684,10 +684,3 @@ class SlideToggleFormsTestApp { class SlideToggleWithFormControl { formControl = new FormControl(); } - -// TODO(devversion): replace with global utility once pull request #2943 is merged. -function dispatchFakeEvent(element: HTMLElement, eventName: string): void { - let event = document.createEvent('Event'); - event.initEvent(eventName, true, true); - element.dispatchEvent(event); -} diff --git a/src/lib/slider/slider.spec.ts b/src/lib/slider/slider.spec.ts index 60e4d2b95ad4..e029442694db 100644 --- a/src/lib/slider/slider.spec.ts +++ b/src/lib/slider/slider.spec.ts @@ -729,7 +729,7 @@ describe('MdSlider', () => { it('should update the model on keydown', () => { expect(testComponent.val).toBe(0); - dispatchKeyboardEvent(sliderNativeElement, UP_ARROW); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', UP_ARROW); fixture.detectChanges(); expect(testComponent.val).toBe(1); @@ -949,14 +949,14 @@ describe('MdSlider', () => { }); it('should increment slider by 1 on up arrow pressed', () => { - dispatchKeyboardEvent(sliderNativeElement, UP_ARROW); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', UP_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(1); }); it('should increment slider by 1 on right arrow pressed', () => { - dispatchKeyboardEvent(sliderNativeElement, RIGHT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', RIGHT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(1); @@ -965,7 +965,7 @@ describe('MdSlider', () => { it('should decrement slider by 1 on down arrow pressed', () => { sliderInstance.value = 100; - dispatchKeyboardEvent(sliderNativeElement, DOWN_ARROW); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', DOWN_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(99); @@ -974,14 +974,14 @@ describe('MdSlider', () => { it('should decrement slider by 1 on left arrow pressed', () => { sliderInstance.value = 100; - dispatchKeyboardEvent(sliderNativeElement, LEFT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', LEFT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(99); }); it('should increment slider by 10 on page up pressed', () => { - dispatchKeyboardEvent(sliderNativeElement, PAGE_UP); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', PAGE_UP); fixture.detectChanges(); expect(sliderInstance.value).toBe(10); @@ -990,14 +990,14 @@ describe('MdSlider', () => { it('should decrement slider by 10 on page down pressed', () => { sliderInstance.value = 100; - dispatchKeyboardEvent(sliderNativeElement, PAGE_DOWN); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', PAGE_DOWN); fixture.detectChanges(); expect(sliderInstance.value).toBe(90); }); it('should set slider to max on end pressed', () => { - dispatchKeyboardEvent(sliderNativeElement, END); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', END); fixture.detectChanges(); expect(sliderInstance.value).toBe(100); @@ -1006,7 +1006,7 @@ describe('MdSlider', () => { it('should set slider to min on home pressed', () => { sliderInstance.value = 100; - dispatchKeyboardEvent(sliderNativeElement, HOME); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', HOME); fixture.detectChanges(); expect(sliderInstance.value).toBe(0); @@ -1067,7 +1067,7 @@ describe('MdSlider', () => { testComponent.invert = true; fixture.detectChanges(); - dispatchKeyboardEvent(sliderNativeElement, RIGHT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', RIGHT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(1); @@ -1078,7 +1078,7 @@ describe('MdSlider', () => { sliderInstance.value = 100; fixture.detectChanges(); - dispatchKeyboardEvent(sliderNativeElement, LEFT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', LEFT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(99); @@ -1089,7 +1089,7 @@ describe('MdSlider', () => { sliderInstance.value = 100; fixture.detectChanges(); - dispatchKeyboardEvent(sliderNativeElement, RIGHT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', RIGHT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(99); @@ -1099,7 +1099,7 @@ describe('MdSlider', () => { testComponent.dir = 'rtl'; fixture.detectChanges(); - dispatchKeyboardEvent(sliderNativeElement, LEFT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', LEFT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(1); @@ -1111,7 +1111,7 @@ describe('MdSlider', () => { sliderInstance.value = 100; fixture.detectChanges(); - dispatchKeyboardEvent(sliderNativeElement, RIGHT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', RIGHT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(99); @@ -1122,7 +1122,7 @@ describe('MdSlider', () => { testComponent.invert = true; fixture.detectChanges(); - dispatchKeyboardEvent(sliderNativeElement, LEFT_ARROW); + dispatchKeyboardEvent(sliderNativeElement, 'keydown', LEFT_ARROW); fixture.detectChanges(); expect(sliderInstance.value).toBe(1); diff --git a/src/lib/tabs/tab-header.spec.ts b/src/lib/tabs/tab-header.spec.ts index dc687f41f923..ef7bac7e39c4 100644 --- a/src/lib/tabs/tab-header.spec.ts +++ b/src/lib/tabs/tab-header.spec.ts @@ -106,19 +106,21 @@ describe('MdTabHeader', () => { fixture.detectChanges(); expect(appComponent.mdTabHeader.focusIndex).toBe(0); + let tabListContainer = appComponent.mdTabHeader._tabListContainer.nativeElement; + // Move focus right to 2 - dispatchKeyboardEvent(appComponent.mdTabHeader._tabListContainer.nativeElement, RIGHT_ARROW); + dispatchKeyboardEvent(tabListContainer, 'keydown', RIGHT_ARROW); fixture.detectChanges(); expect(appComponent.mdTabHeader.focusIndex).toBe(2); // Select the focused index 2 expect(appComponent.selectedIndex).toBe(0); - dispatchKeyboardEvent(appComponent.mdTabHeader._tabListContainer.nativeElement, ENTER); + dispatchKeyboardEvent(tabListContainer, 'keydown', ENTER); fixture.detectChanges(); expect(appComponent.selectedIndex).toBe(2); // Move focus right to 0 - dispatchKeyboardEvent(appComponent.mdTabHeader._tabListContainer.nativeElement, LEFT_ARROW); + dispatchKeyboardEvent(tabListContainer, 'keydown', LEFT_ARROW); fixture.detectChanges(); expect(appComponent.mdTabHeader.focusIndex).toBe(0); }); diff --git a/src/lib/tooltip/tooltip.spec.ts b/src/lib/tooltip/tooltip.spec.ts index bfb852af3982..81f4c0c9e816 100644 --- a/src/lib/tooltip/tooltip.spec.ts +++ b/src/lib/tooltip/tooltip.spec.ts @@ -20,6 +20,7 @@ import {Dir, LayoutDirection} from '../core/rtl/dir'; import {OverlayModule} from '../core/overlay/overlay-directives'; import {Platform} from '../core/platform/platform'; import {Scrollable} from '../core/overlay/scroll/scrollable'; +import {dispatchFakeEvent} from '../core/testing/dispatch-events'; const initialTooltipMessage = 'initial tooltip message'; @@ -444,9 +445,7 @@ class ScrollableTooltipDemo { // Emit a scroll event from the scrolling element in our component. // This event should be picked up by the scrollable directive and notify. // The notification should be picked up by the service. - const scrollEvent = document.createEvent('UIEvents'); - scrollEvent.initUIEvent('scroll', true, true, window, 0); - scrollingContainerEl.dispatchEvent(scrollEvent); + dispatchFakeEvent(scrollingContainerEl, 'scroll'); } }