build: reformat repo to new clang@1.4.0 (#36613)

PR Close #36613
This commit is contained in:
Joey Perrott
2020-04-13 16:40:21 -07:00
committed by atscott
parent 5e80e7e216
commit 698b0288be
1160 changed files with 31667 additions and 24000 deletions

View File

@ -7,12 +7,12 @@
*/
import {DOCUMENT, isPlatformBrowser, ɵgetDOM as getDOM} from '@angular/common';
import {APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, Directive, ErrorHandler, Inject, Injector, Input, LOCALE_ID, NgModule, OnDestroy, PLATFORM_ID, PLATFORM_INITIALIZER, Pipe, Provider, Sanitizer, StaticProvider, Type, VERSION, createPlatformFactory} from '@angular/core';
import {APP_INITIALIZER, Compiler, Component, createPlatformFactory, CUSTOM_ELEMENTS_SCHEMA, Directive, ErrorHandler, Inject, Injector, Input, LOCALE_ID, NgModule, OnDestroy, Pipe, PLATFORM_ID, PLATFORM_INITIALIZER, Provider, Sanitizer, StaticProvider, Type, VERSION} from '@angular/core';
import {ApplicationRef, destroyPlatform} from '@angular/core/src/application_ref';
import {Console} from '@angular/core/src/console';
import {ComponentRef} from '@angular/core/src/linker/component_factory';
import {Testability, TestabilityRegistry} from '@angular/core/src/testability/testability';
import {AsyncTestCompleter, Log, afterEach, beforeEach, beforeEachProviders, describe, inject, it} from '@angular/core/testing/src/testing_internal';
import {afterEach, AsyncTestCompleter, beforeEach, beforeEachProviders, describe, inject, it, Log} from '@angular/core/testing/src/testing_internal';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {expect} from '@angular/platform-browser/testing/src/matchers';
@ -25,7 +25,9 @@ class NonExistentComp {
@Component({selector: 'hello-app', template: '{{greeting}} world!'})
class HelloRootCmp {
greeting: string;
constructor() { this.greeting = 'hello'; }
constructor() {
this.greeting = 'hello';
}
}
@Component({selector: 'hello-app', template: 'before: <ng-content></ng-content> after: done'})
@ -36,7 +38,9 @@ class HelloRootCmpContent {
@Component({selector: 'hello-app-2', template: '{{greeting}} world, again!'})
class HelloRootCmp2 {
greeting: string;
constructor() { this.greeting = 'hello'; }
constructor() {
this.greeting = 'hello';
}
}
@Component({selector: 'hello-app', template: ''})
@ -52,7 +56,9 @@ class HelloRootCmp3 {
class HelloRootCmp4 {
appRef: any /** TODO #9100 */;
constructor(@Inject(ApplicationRef) appRef: ApplicationRef) { this.appRef = appRef; }
constructor(@Inject(ApplicationRef) appRef: ApplicationRef) {
this.appRef = appRef;
}
}
@Component({selector: 'hello-app'})
@ -66,9 +72,13 @@ class HelloRootDirectiveIsNotCmp {
@Component({selector: 'hello-app', template: ''})
class HelloOnDestroyTickCmp implements OnDestroy {
appRef: ApplicationRef;
constructor(@Inject(ApplicationRef) appRef: ApplicationRef) { this.appRef = appRef; }
constructor(@Inject(ApplicationRef) appRef: ApplicationRef) {
this.appRef = appRef;
}
ngOnDestroy(): void { this.appRef.tick(); }
ngOnDestroy(): void {
this.appRef.tick();
}
}
@Component({selector: 'hello-app', templateUrl: './sometemplate.html'})
@ -79,13 +89,14 @@ class HelloUrlCmp {
@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}})
class SomeDirective {
// TODO(issue/24571): remove '!'.
@Input()
someDir !: string;
@Input() someDir!: string;
}
@Pipe({name: 'somePipe'})
class SomePipe {
transform(value: string): any { return `transformed ${value}`; }
transform(value: string): any {
return `transformed ${value}`;
}
}
@Component({selector: 'hello-app', template: `<div [someDir]="'someValue' | somePipe"></div>`})
@ -99,7 +110,9 @@ class HelloCmpUsingCustomElement {
class MockConsole {
res: any[][] = [];
error(...s: any[]): void { this.res.push(s); }
error(...s: any[]): void {
this.res.push(s);
}
}
@ -107,7 +120,9 @@ class DummyConsole implements Console {
public warnings: string[] = [];
log(message: string) {}
warn(message: string) { this.warnings.push(message); }
warn(message: string) {
this.warnings.push(message);
}
}
@ -135,7 +150,9 @@ function bootstrap(
if (isNode) return;
let compilerConsole: DummyConsole;
beforeEachProviders(() => { return [Log]; });
beforeEachProviders(() => {
return [Log];
});
beforeEach(inject([DOCUMENT], (doc: any) => {
destroyPlatform();
@ -419,7 +436,6 @@ function bootstrap(
it('should remove styles when transitioning from a server render',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
@Component({
selector: 'root',
template: 'root',
@ -449,8 +465,9 @@ function bootstrap(
platform.bootstrapModule(TestModule).then(() => {
const styles: HTMLElement[] =
Array.prototype.slice.apply(document.getElementsByTagName('style') || []);
styles.forEach(
style => { expect(style.getAttribute('ng-transition')).not.toBe('my-app'); });
styles.forEach(style => {
expect(style.getAttribute('ng-transition')).not.toBe('my-app');
});
async.done();
});
}));
@ -487,7 +504,9 @@ function bootstrap(
})
class CompA {
title: string = '';
ngDoCheck() { log.push('CompA:ngDoCheck'); }
ngDoCheck() {
log.push('CompA:ngDoCheck');
}
onClick() {
this.title = 'CompA';
log.push('CompA:onClick');
@ -500,7 +519,9 @@ function bootstrap(
})
class CompB {
title: string = '';
ngDoCheck() { log.push('CompB:ngDoCheck'); }
ngDoCheck() {
log.push('CompB:ngDoCheck');
}
onClick() {
this.title = 'CompB';
log.push('CompB:onClick');

View File

@ -30,14 +30,14 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
afterEach(() => getDOM().remove(defaultMeta));
it('should return meta tag matching selector', () => {
const actual: HTMLMetaElement = metaService.getTag('property="fb:app_id"') !;
const actual: HTMLMetaElement = metaService.getTag('property="fb:app_id"')!;
expect(actual).not.toBeNull();
expect(actual.getAttribute('content')).toEqual('123456789');
});
it('should return all meta tags matching selector', () => {
const tag1 = metaService.addTag({name: 'author', content: 'page author'}) !;
const tag2 = metaService.addTag({name: 'author', content: 'another page author'}) !;
const tag1 = metaService.addTag({name: 'author', content: 'page author'})!;
const tag2 = metaService.addTag({name: 'author', content: 'another page author'})!;
const actual: HTMLMetaElement[] = metaService.getTags('name=author');
expect(actual.length).toEqual(2);
@ -50,7 +50,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
});
it('should return null if meta tag does not exist', () => {
const actual: HTMLMetaElement = metaService.getTag('fake=fake') !;
const actual: HTMLMetaElement = metaService.getTag('fake=fake')!;
expect(actual).toBeNull();
});
@ -73,7 +73,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
metaService.addTags([{name: 'keywords', content: 'meta test'}]);
const meta = metaService.getTag(selector) !;
const meta = metaService.getTag(selector)!;
expect(meta).not.toBeNull();
metaService.removeTagElement(meta);
@ -87,7 +87,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
const actual = metaService.getTag(selector);
expect(actual).not.toBeNull();
expect(actual !.getAttribute('content')).toEqual('4321');
expect(actual!.getAttribute('content')).toEqual('4321');
});
it('should extract selector from the tag definition', () => {
@ -96,7 +96,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
const actual = metaService.getTag(selector);
expect(actual).not.toBeNull();
expect(actual !.getAttribute('content')).toEqual('666');
expect(actual!.getAttribute('content')).toEqual('666');
});
it('should create meta tag if it does not exist', () => {
@ -104,7 +104,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
metaService.updateTag({name: 'twitter:title', content: 'Content Title'}, selector);
const actual = metaService.getTag(selector) !;
const actual = metaService.getTag(selector)!;
expect(actual).not.toBeNull();
expect(actual.getAttribute('content')).toEqual('Content Title');
@ -118,7 +118,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
metaService.addTag({name: 'og:title', content: 'Content Title'});
const actual = metaService.getTag(selector) !;
const actual = metaService.getTag(selector)!;
expect(actual).not.toBeNull();
expect(actual.getAttribute('content')).toEqual('Content Title');
@ -136,8 +136,8 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
{name: 'twitter:title', content: 'Content Title'},
{property: 'og:title', content: 'Content Title'}
]);
const twitterMeta = metaService.getTag(nameSelector) !;
const fbMeta = metaService.getTag(propertySelector) !;
const twitterMeta = metaService.getTag(nameSelector)!;
const fbMeta = metaService.getTag(propertySelector)!;
expect(twitterMeta).not.toBeNull();
expect(fbMeta).not.toBeNull();
@ -160,7 +160,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
const selector = 'property="fb:app_id"';
expect(metaService.getTags(selector).length).toEqual(1);
const meta = metaService.addTag({property: 'fb:app_id', content: '666'}) !;
const meta = metaService.addTag({property: 'fb:app_id', content: '666'})!;
expect(metaService.getTags(selector).length).toEqual(2);
@ -172,18 +172,16 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
const selector = 'property="fb:app_id"';
expect(metaService.getTags(selector).length).toEqual(1);
const meta = metaService.addTag({property: 'fb:app_id', content: '123456789'}, true) !;
const meta = metaService.addTag({property: 'fb:app_id', content: '123456789'}, true)!;
expect(metaService.getTags(selector).length).toEqual(2);
// clean up
metaService.removeTagElement(meta);
});
});
describe('integration test', () => {
@Injectable()
class DependsOnMeta {
constructor(public meta: Meta) {}

View File

@ -24,10 +24,13 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
titleService = new Title(doc);
});
afterEach(() => { doc.title = initialTitle; });
afterEach(() => {
doc.title = initialTitle;
});
it('should allow reading initial title',
() => { expect(titleService.getTitle()).toEqual(initialTitle); });
it('should allow reading initial title', () => {
expect(titleService.getTitle()).toEqual(initialTitle);
});
it('should set a title on the injected document', () => {
titleService.setTitle('test title');
@ -36,13 +39,12 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
});
it('should reset title to empty string if title not provided', () => {
titleService.setTitle(null !);
titleService.setTitle(null!);
expect(doc.title).toEqual('');
});
});
describe('integration test', () => {
@Injectable()
class DependsOnTitle {
constructor(public title: Title) {}
@ -55,7 +57,8 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
});
});
it('should inject Title service when using BrowserModule',
() => { expect(TestBed.inject(DependsOnTitle).title).toBeAnInstanceOf(Title); });
it('should inject Title service when using BrowserModule', () => {
expect(TestBed.inject(DependsOnTitle).title).toBeAnInstanceOf(Title);
});
});
}

View File

@ -11,7 +11,9 @@ import {ApplicationRef} from '@angular/core/src/application_ref';
import {SpyObject} from '@angular/core/testing/src/testing_internal';
export class SpyApplicationRef extends SpyObject {
constructor() { super(ApplicationRef); }
constructor() {
super(ApplicationRef);
}
}
export class SpyComponentRef extends SpyObject {

View File

@ -8,18 +8,25 @@
import {disableDebugTools, enableDebugTools} from '@angular/platform-browser';
import {SpyComponentRef, callNgProfilerTimeChangeDetection} from './spies';
import {callNgProfilerTimeChangeDetection, SpyComponentRef} from './spies';
{
describe('profiler', () => {
if (isNode) return;
beforeEach(() => { enableDebugTools((<any>new SpyComponentRef())); });
beforeEach(() => {
enableDebugTools((<any>new SpyComponentRef()));
});
afterEach(() => { disableDebugTools(); });
afterEach(() => {
disableDebugTools();
});
it('should time change detection', () => { callNgProfilerTimeChangeDetection(); });
it('should time change detection', () => {
callNgProfilerTimeChangeDetection();
});
it('should time change detection with recording',
() => { callNgProfilerTimeChangeDetection({'record': true}); });
it('should time change detection with recording', () => {
callNgProfilerTimeChangeDetection({'record': true});
});
});
}

View File

@ -9,124 +9,126 @@
import {DOCUMENT} from '@angular/common';
import {TestBed} from '@angular/core/testing';
import {BrowserModule, BrowserTransferStateModule, TransferState} from '@angular/platform-browser';
import {StateKey, escapeHtml, makeStateKey, unescapeHtml} from '@angular/platform-browser/src/browser/transfer_state';
import {escapeHtml, makeStateKey, StateKey, unescapeHtml} from '@angular/platform-browser/src/browser/transfer_state';
(function() {
function removeScriptTag(doc: Document, id: string) {
const existing = doc.getElementById(id);
if (existing) {
doc.body.removeChild(existing);
}
function removeScriptTag(doc: Document, id: string) {
const existing = doc.getElementById(id);
if (existing) {
doc.body.removeChild(existing);
}
}
function addScriptTag(doc: Document, appId: string, data: {}) {
const script = doc.createElement('script');
const id = appId + '-state';
script.id = id;
script.setAttribute('type', 'application/json');
script.textContent = escapeHtml(JSON.stringify(data));
function addScriptTag(doc: Document, appId: string, data: {}) {
const script = doc.createElement('script');
const id = appId + '-state';
script.id = id;
script.setAttribute('type', 'application/json');
script.textContent = escapeHtml(JSON.stringify(data));
// Remove any stale script tags.
removeScriptTag(doc, id);
// Remove any stale script tags.
removeScriptTag(doc, id);
doc.body.appendChild(script);
}
doc.body.appendChild(script);
}
describe('TransferState', () => {
const APP_ID = 'test-app';
let doc: Document;
describe('TransferState', () => {
const APP_ID = 'test-app';
let doc: Document;
const TEST_KEY = makeStateKey<number>('test');
const DELAYED_KEY = makeStateKey<string>('delayed');
const TEST_KEY = makeStateKey<number>('test');
const DELAYED_KEY = makeStateKey<string>('delayed');
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
BrowserModule.withServerTransition({appId: APP_ID}),
BrowserTransferStateModule,
]
});
doc = TestBed.inject(DOCUMENT);
});
afterEach(() => { removeScriptTag(doc, APP_ID + '-state'); });
it('is initialized from script tag', () => {
addScriptTag(doc, APP_ID, {test: 10});
const transferState: TransferState = TestBed.inject(TransferState);
expect(transferState.get(TEST_KEY, 0)).toBe(10);
});
it('is initialized to empty state if script tag not found', () => {
const transferState: TransferState = TestBed.inject(TransferState);
expect(transferState.get(TEST_KEY, 0)).toBe(0);
});
it('supports adding new keys using set', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
expect(transferState.get(TEST_KEY, 0)).toBe(20);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports setting and accessing value \'0\' via get', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 0);
expect(transferState.get(TEST_KEY, 20)).toBe(0);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports setting and accessing value \'false\' via get', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, false);
expect(transferState.get(TEST_KEY, true)).toBe(false);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports setting and accessing value \'null\' via get', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, null);
expect(transferState.get(TEST_KEY, 20 as any)).toBe(null);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports removing keys', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
transferState.remove(TEST_KEY);
expect(transferState.get(TEST_KEY, 0)).toBe(0);
expect(transferState.hasKey(TEST_KEY)).toBe(false);
});
it('supports serialization using toJson()', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
expect(transferState.toJson()).toBe('{"test":20}');
});
it('calls onSerialize callbacks when calling toJson()', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
let value = 'initial';
transferState.onSerialize(DELAYED_KEY, () => value);
value = 'changed';
expect(transferState.toJson()).toBe('{"test":20,"delayed":"changed"}');
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
BrowserModule.withServerTransition({appId: APP_ID}),
BrowserTransferStateModule,
]
});
doc = TestBed.inject(DOCUMENT);
});
describe('escape/unescape', () => {
it('works with all escaped characters', () => {
const testString = '</script><script>alert(\'Hello&\' + "World");';
const testObj = {testString};
const escaped = escapeHtml(JSON.stringify(testObj));
expect(escaped).toBe(
'{&q;testString&q;:&q;&l;/script&g;&l;script&g;' +
'alert(&s;Hello&a;&s; + \\&q;World\\&q;);&q;}');
const unescapedObj = JSON.parse(unescapeHtml(escaped));
expect(unescapedObj['testString']).toBe(testString);
});
afterEach(() => {
removeScriptTag(doc, APP_ID + '-state');
});
it('is initialized from script tag', () => {
addScriptTag(doc, APP_ID, {test: 10});
const transferState: TransferState = TestBed.inject(TransferState);
expect(transferState.get(TEST_KEY, 0)).toBe(10);
});
it('is initialized to empty state if script tag not found', () => {
const transferState: TransferState = TestBed.inject(TransferState);
expect(transferState.get(TEST_KEY, 0)).toBe(0);
});
it('supports adding new keys using set', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
expect(transferState.get(TEST_KEY, 0)).toBe(20);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports setting and accessing value \'0\' via get', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 0);
expect(transferState.get(TEST_KEY, 20)).toBe(0);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports setting and accessing value \'false\' via get', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, false);
expect(transferState.get(TEST_KEY, true)).toBe(false);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports setting and accessing value \'null\' via get', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, null);
expect(transferState.get(TEST_KEY, 20 as any)).toBe(null);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports removing keys', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
transferState.remove(TEST_KEY);
expect(transferState.get(TEST_KEY, 0)).toBe(0);
expect(transferState.hasKey(TEST_KEY)).toBe(false);
});
it('supports serialization using toJson()', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
expect(transferState.toJson()).toBe('{"test":20}');
});
it('calls onSerialize callbacks when calling toJson()', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
let value = 'initial';
transferState.onSerialize(DELAYED_KEY, () => value);
value = 'changed';
expect(transferState.toJson()).toBe('{"test":20,"delayed":"changed"}');
});
});
describe('escape/unescape', () => {
it('works with all escaped characters', () => {
const testString = '</script><script>alert(\'Hello&\' + "World");';
const testObj = {testString};
const escaped = escapeHtml(JSON.stringify(testObj));
expect(escaped).toBe(
'{&q;testString&q;:&q;&l;/script&g;&l;script&g;' +
'alert(&s;Hello&a;&s; + \\&q;World\\&q;);&q;}');
const unescapedObj = JSON.parse(unescapeHtml(escaped));
expect(unescapedObj['testString']).toBe(testString);
});
});
})();

View File

@ -11,7 +11,6 @@ import {BrowserDetection} from '../testing/src/browser_util';
{
describe('BrowserDetection', () => {
const browsers = [
{
name: 'Chrome',
@ -224,7 +223,7 @@ import {BrowserDetection} from '../testing/src/browser_util';
];
browsers.forEach((browser: {[key: string]: any}) => {
it(`should detect ${browser[ 'name']}`, () => {
it(`should detect ${browser['name']}`, () => {
const bd = new BrowserDetection(<string>browser['ua']);
expect(bd.isFirefox).toBe(browser['isFirefox']);
expect(bd.isAndroid).toBe(browser['isAndroid']);

View File

@ -14,361 +14,393 @@ import {EventManager, EventManagerPlugin} from '@angular/platform-browser/src/do
import {createMouseEvent, el} from '../../../testing/src/browser_util';
(function() {
if (isNode) return;
let domEventPlugin: DomEventsPlugin;
let doc: any;
let zone: NgZone;
if (isNode) return;
let domEventPlugin: DomEventsPlugin;
let doc: any;
let zone: NgZone;
describe('EventManager', () => {
beforeEach(() => {
doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
zone = new NgZone({});
domEventPlugin = new DomEventsPlugin(doc);
describe('EventManager', () => {
beforeEach(() => {
doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
zone = new NgZone({});
domEventPlugin = new DomEventsPlugin(doc);
});
it('should delegate event bindings to plugins that are passed in from the most generic one to the most specific one',
() => {
const element = el('<div></div>');
const handler = (e: any /** TODO #9100 */) => e;
const plugin = new FakeEventManagerPlugin(doc, ['click']);
const manager = new EventManager([domEventPlugin, plugin], new FakeNgZone());
manager.addEventListener(element, 'click', handler);
expect(plugin.eventHandler['click']).toBe(handler);
});
it('should delegate event bindings to the first plugin supporting the event', () => {
const element = el('<div></div>');
const clickHandler = (e: any /** TODO #9100 */) => e;
const dblClickHandler = (e: any /** TODO #9100 */) => e;
const plugin1 = new FakeEventManagerPlugin(doc, ['dblclick']);
const plugin2 = new FakeEventManagerPlugin(doc, ['click', 'dblclick']);
const manager = new EventManager([plugin2, plugin1], new FakeNgZone());
manager.addEventListener(element, 'click', clickHandler);
manager.addEventListener(element, 'dblclick', dblClickHandler);
expect(plugin2.eventHandler['click']).toBe(clickHandler);
expect(plugin1.eventHandler['dblclick']).toBe(dblClickHandler);
});
it('should throw when no plugin can handle the event', () => {
const element = el('<div></div>');
const plugin = new FakeEventManagerPlugin(doc, ['dblclick']);
const manager = new EventManager([plugin], new FakeNgZone());
expect(() => manager.addEventListener(element, 'click', null!))
.toThrowError('No event manager plugin found for event click');
});
it('events are caught when fired from a child', () => {
const element = el('<div><div></div></div>');
// Workaround for https://bugs.webkit.org/show_bug.cgi?id=122755
doc.body.appendChild(element);
const child = element.firstChild as Element;
const dispatchedEvent = createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
const handler = (e: any /** TODO #9100 */) => {
receivedEvent = e;
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
manager.addEventListener(element, 'click', handler);
getDOM().dispatchEvent(child, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
});
it('should add and remove global event listeners', () => {
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
const handler = (e: any /** TODO #9100 */) => {
receivedEvent = e;
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
const remover = manager.addGlobalEventListener('document', 'click', handler);
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
receivedEvent = null;
remover();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
});
it('should keep zone when addEventListener', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
let receivedZone: any = null;
const handler = (e: any /** TODO #9100 */) => {
receivedEvent = e;
receivedZone = Zone.current;
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover: any = null;
Zone.root.run(() => {
remover = manager.addEventListener(element, 'click', handler);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
expect(receivedZone.name).toBe(Zone.root.name);
it('should delegate event bindings to plugins that are passed in from the most generic one to the most specific one',
() => {
const element = el('<div></div>');
const handler = (e: any /** TODO #9100 */) => e;
const plugin = new FakeEventManagerPlugin(doc, ['click']);
const manager = new EventManager([domEventPlugin, plugin], new FakeNgZone());
manager.addEventListener(element, 'click', handler);
expect(plugin.eventHandler['click']).toBe(handler);
});
receivedEvent = null;
remover && remover();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
});
it('should delegate event bindings to the first plugin supporting the event', () => {
const element = el('<div></div>');
const clickHandler = (e: any /** TODO #9100 */) => e;
const dblClickHandler = (e: any /** TODO #9100 */) => e;
const plugin1 = new FakeEventManagerPlugin(doc, ['dblclick']);
const plugin2 = new FakeEventManagerPlugin(doc, ['click', 'dblclick']);
const manager = new EventManager([plugin2, plugin1], new FakeNgZone());
manager.addEventListener(element, 'click', clickHandler);
manager.addEventListener(element, 'dblclick', dblClickHandler);
expect(plugin2.eventHandler['click']).toBe(clickHandler);
expect(plugin1.eventHandler['dblclick']).toBe(dblClickHandler);
it('should keep zone when addEventListener multiple times', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover1: any = null;
let remover2: any = null;
Zone.root.run(() => {
remover1 = manager.addEventListener(element, 'click', handler1);
});
it('should throw when no plugin can handle the event', () => {
const element = el('<div></div>');
const plugin = new FakeEventManagerPlugin(doc, ['dblclick']);
const manager = new EventManager([plugin], new FakeNgZone());
expect(() => manager.addEventListener(element, 'click', null !))
.toThrowError('No event manager plugin found for event click');
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name, 'test']);
it('events are caught when fired from a child', () => {
const element = el('<div><div></div></div>');
// Workaround for https://bugs.webkit.org/show_bug.cgi?id=122755
doc.body.appendChild(element);
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
});
const child = element.firstChild as Element;
const dispatchedEvent = createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
const handler = (e: any /** TODO #9100 */) => { receivedEvent = e; };
const manager = new EventManager([domEventPlugin], new FakeNgZone());
manager.addEventListener(element, 'click', handler);
getDOM().dispatchEvent(child, dispatchedEvent);
it('should support event.stopImmediatePropagation', () => {
const Zone = (window as any)['Zone'];
expect(receivedEvent).toBe(dispatchedEvent);
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
e.stopImmediatePropagation();
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover1: any = null;
let remover2: any = null;
Zone.root.run(() => {
remover1 = manager.addEventListener(element, 'click', handler1);
});
it('should add and remove global event listeners', () => {
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
const handler = (e: any /** TODO #9100 */) => { receivedEvent = e; };
const manager = new EventManager([domEventPlugin], new FakeNgZone());
const remover = manager.addGlobalEventListener('document', 'click', handler);
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
receivedEvent = null;
remover();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name]);
it('should keep zone when addEventListener', () => {
const Zone = (window as any)['Zone'];
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
});
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
let receivedZone: any = null;
const handler = (e: any /** TODO #9100 */) => {
receivedEvent = e;
receivedZone = Zone.current;
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
it('should handle event correctly when one handler remove itself ', () => {
const Zone = (window as any)['Zone'];
let remover: any = null;
Zone.root.run(() => { remover = manager.addEventListener(element, 'click', handler); });
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
expect(receivedZone.name).toBe(Zone.root.name);
receivedEvent = null;
remover && remover();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
});
it('should keep zone when addEventListener multiple times', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover1: any = null;
let remover2: any = null;
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); });
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name, 'test']);
receivedEvents = [];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
let remover1: any = null;
let remover2: any = null;
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
Zone.root.run(() => {
remover1 = manager.addEventListener(element, 'click', handler1);
});
it('should support event.stopImmediatePropagation', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
e.stopImmediatePropagation();
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover1: any = null;
let remover2: any = null;
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); });
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name]);
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name, 'test']);
it('should handle event correctly when one handler remove itself ', () => {
const Zone = (window as any)['Zone'];
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
});
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
let remover1: any = null;
let remover2: any = null;
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
remover1 && remover1();
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
it('should only add same callback once when addEventListener', () => {
const Zone = (window as any)['Zone'];
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); });
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name, 'test']);
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
let remover1: any = null;
let remover2: any = null;
Zone.root.run(() => {
remover1 = manager.addEventListener(element, 'click', handler);
});
it('should only add same callback once when addEventListener', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover1: any = null;
let remover2: any = null;
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler); });
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name]);
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name]);
it('should be able to remove event listener which was added inside of ngZone', () => {
const Zone = (window as any)['Zone'];
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
});
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
it('should be able to remove event listener which was added inside of ngZone', () => {
const Zone = (window as any)['Zone'];
let remover1: any = null;
let remover2: any = null;
// handler1 is added in root zone
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); });
// handler2 is added in 'angular' zone
Zone.root.fork({name: 'fakeAngularZone', properties: {isAngularZone: true}}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name, 'fakeAngularZone']);
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
// handler1 and handler2 are added in different zone
// one is angular zone, the other is not
// should still be able to remove them correctly
expect(receivedEvents).toEqual([]);
let remover1: any = null;
let remover2: any = null;
// handler1 is added in root zone
Zone.root.run(() => {
remover1 = manager.addEventListener(element, 'click', handler1);
});
it('should run blackListedEvents handler outside of ngZone', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('scroll');
let receivedEvent: any /** TODO #9100 */ = null;
let receivedZone: any = null;
const handler = (e: any /** TODO #9100 */) => {
receivedEvent = e;
receivedZone = Zone.current;
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover = manager.addEventListener(element, 'scroll', handler);
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
expect(receivedZone.name).not.toEqual('angular');
receivedEvent = null;
remover && remover();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
// handler2 is added in 'angular' zone
Zone.root.fork({name: 'fakeAngularZone', properties: {isAngularZone: true}}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name, 'fakeAngularZone']);
it('should only trigger one Change detection when bubbling', (done: DoneFn) => {
doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
zone = new NgZone({shouldCoalesceEventChangeDetection: true});
domEventPlugin = new DomEventsPlugin(doc);
const element = el('<div></div>');
const child = el('<div></div>');
element.appendChild(child);
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any = [];
let stables: any = [];
const handler = (e: any) => { receivedEvents.push(e); };
const manager = new EventManager([domEventPlugin], zone);
let removerChild: any;
let removerParent: any;
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
// handler1 and handler2 are added in different zone
// one is angular zone, the other is not
// should still be able to remove them correctly
expect(receivedEvents).toEqual([]);
});
zone.run(() => {
removerChild = manager.addEventListener(child, 'click', handler);
removerParent = manager.addEventListener(element, 'click', handler);
});
zone.onStable.subscribe((isStable: any) => { stables.push(isStable); });
getDOM().dispatchEvent(child, dispatchedEvent);
requestAnimationFrame(() => {
expect(receivedEvents.length).toBe(2);
expect(stables.length).toBe(1);
it('should run blackListedEvents handler outside of ngZone', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('scroll');
let receivedEvent: any /** TODO #9100 */ = null;
let receivedZone: any = null;
const handler = (e: any /** TODO #9100 */) => {
receivedEvent = e;
receivedZone = Zone.current;
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
removerChild && removerChild();
removerParent && removerParent();
done();
});
let remover = manager.addEventListener(element, 'scroll', handler);
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
expect(receivedZone.name).not.toEqual('angular');
receivedEvent = null;
remover && remover();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
});
it('should only trigger one Change detection when bubbling', (done: DoneFn) => {
doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
zone = new NgZone({shouldCoalesceEventChangeDetection: true});
domEventPlugin = new DomEventsPlugin(doc);
const element = el('<div></div>');
const child = el('<div></div>');
element.appendChild(child);
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any = [];
let stables: any = [];
const handler = (e: any) => {
receivedEvents.push(e);
};
const manager = new EventManager([domEventPlugin], zone);
let removerChild: any;
let removerParent: any;
zone.run(() => {
removerChild = manager.addEventListener(child, 'click', handler);
removerParent = manager.addEventListener(element, 'click', handler);
});
zone.onStable.subscribe((isStable: any) => {
stables.push(isStable);
});
getDOM().dispatchEvent(child, dispatchedEvent);
requestAnimationFrame(() => {
expect(receivedEvents.length).toBe(2);
expect(stables.length).toBe(1);
removerChild && removerChild();
removerParent && removerParent();
done();
});
});
});
})();
/** @internal */
class FakeEventManagerPlugin extends EventManagerPlugin {
eventHandler: {[event: string]: Function} = {};
constructor(doc: any, public supportedEvents: string[]) { super(doc); }
constructor(doc: any, public supportedEvents: string[]) {
super(doc);
}
supports(eventName: string): boolean { return this.supportedEvents.indexOf(eventName) > -1; }
supports(eventName: string): boolean {
return this.supportedEvents.indexOf(eventName) > -1;
}
addEventListener(element: any, eventName: string, handler: Function) {
this.eventHandler[eventName] = handler;
return () => { delete this.eventHandler[eventName]; };
return () => {
delete this.eventHandler[eventName];
};
}
}
class FakeNgZone extends NgZone {
constructor() { super({enableLongStackTrace: false, shouldCoalesceEventChangeDetection: true}); }
run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T { return fn(); }
runOutsideAngular(fn: Function) { return fn(); }
constructor() {
super({enableLongStackTrace: false, shouldCoalesceEventChangeDetection: true});
}
run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T {
return fn();
}
runOutsideAngular(fn: Function) {
return fn();
}
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, EventEmitter, Injector, Input, NgModule, Output, Renderer2, ViewEncapsulation, destroyPlatform} from '@angular/core';
import {Component, destroyPlatform, EventEmitter, Injector, Input, NgModule, Output, Renderer2, ViewEncapsulation} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {BrowserModule} from '@angular/platform-browser';
import {browserDetection} from '@angular/platform-browser/testing/src/browser_util';
@ -15,14 +15,15 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
if (browserDetection.supportsShadowDom) {
describe('ShadowDOM Support', () => {
let testContainer: HTMLDivElement;
beforeEach(() => { TestBed.configureTestingModule({imports: [TestModule]}); });
beforeEach(() => {
TestBed.configureTestingModule({imports: [TestModule]});
});
it('should attach and use a shadowRoot when ViewEncapsulation.Native is set', () => {
const compEl = TestBed.createComponent(ShadowComponent).nativeElement;
expect(compEl.shadowRoot !.textContent).toEqual('Hello World');
expect(compEl.shadowRoot!.textContent).toEqual('Hello World');
});
it('should use the shadow root to encapsulate styles', () => {
@ -40,10 +41,10 @@ if (browserDetection.supportsShadowDom) {
const el = TestBed.createComponent(ShadowSlotComponent).nativeElement;
const projectedContent = document.createTextNode('Hello Slot!');
el.appendChild(projectedContent);
const slot = el.shadowRoot !.querySelector('slot');
const slot = el.shadowRoot!.querySelector('slot');
expect(slot !.assignedNodes().length).toBe(1);
expect(slot !.assignedNodes()[0].textContent).toBe('Hello Slot!');
expect(slot!.assignedNodes().length).toBe(1);
expect(slot!.assignedNodes()[0].textContent).toBe('Hello Slot!');
});
it('should allow the usage of named <slot> elements', () => {
@ -65,16 +66,16 @@ if (browserDetection.supportsShadowDom) {
el.appendChild(articleContent);
el.appendChild(articleSubcontent);
const headerSlot = el.shadowRoot !.querySelector('slot[name=header]') as HTMLSlotElement;
const articleSlot = el.shadowRoot !.querySelector('slot[name=article]') as HTMLSlotElement;
const headerSlot = el.shadowRoot!.querySelector('slot[name=header]') as HTMLSlotElement;
const articleSlot = el.shadowRoot!.querySelector('slot[name=article]') as HTMLSlotElement;
expect(headerSlot !.assignedNodes().length).toBe(1);
expect(headerSlot !.assignedNodes()[0].textContent).toBe('Header Text!');
expect(headerSlot!.assignedNodes().length).toBe(1);
expect(headerSlot!.assignedNodes()[0].textContent).toBe('Header Text!');
expect(headerContent.assignedSlot).toBe(headerSlot);
expect(articleSlot !.assignedNodes().length).toBe(2);
expect(articleSlot !.assignedNodes()[0].textContent).toBe('Article Text!');
expect(articleSlot !.assignedNodes()[1].textContent).toBe('Article Subtext!');
expect(articleSlot!.assignedNodes().length).toBe(2);
expect(articleSlot!.assignedNodes()[0].textContent).toBe('Article Text!');
expect(articleSlot!.assignedNodes()[1].textContent).toBe('Article Subtext!');
expect(articleContent.assignedSlot).toBe(articleSlot);
expect(articleSubcontent.assignedSlot).toBe(articleSlot);
});

View File

@ -7,8 +7,8 @@
*/
import {CompilerConfig, ResourceLoader} from '@angular/compiler';
import {CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, Directive, Inject, Injectable, InjectionToken, Injector, Input, NgModule, Optional, Pipe, SkipSelf, ɵstringify as stringify} from '@angular/core';
import {TestBed, async, fakeAsync, getTestBed, inject, tick, withModule} from '@angular/core/testing';
import {Compiler, Component, ComponentFactoryResolver, CUSTOM_ELEMENTS_SCHEMA, Directive, Inject, Injectable, InjectionToken, Injector, Input, NgModule, Optional, Pipe, SkipSelf, ɵstringify as stringify} from '@angular/core';
import {async, fakeAsync, getTestBed, inject, TestBed, tick, withModule} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {ivyEnabled, modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/private/testing';
@ -18,7 +18,9 @@ import {ivyEnabled, modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/priv
@Injectable()
class ChildComp {
childBinding: string;
constructor() { this.childBinding = 'Child'; }
constructor() {
this.childBinding = 'Child';
}
}
@Component({selector: 'child-comp', template: `<span>Mock</span>`})
@ -52,12 +54,16 @@ class ChildChildComp {
@Injectable()
class ChildWithChildComp {
childBinding: string;
constructor() { this.childBinding = 'Child'; }
constructor() {
this.childBinding = 'Child';
}
}
class FancyService {
value: string = 'real value';
getAsyncValue() { return Promise.resolve('async value'); }
getAsyncValue() {
return Promise.resolve('async value');
}
getTimeoutValue() {
return new Promise<string>((resolve, reject) => setTimeout(() => resolve('timeout value'), 10));
}
@ -88,13 +94,14 @@ class TestViewProvidersComp {
@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}})
class SomeDirective {
// TODO(issue/24571): remove '!'.
@Input()
someDir !: string;
@Input() someDir!: string;
}
@Pipe({name: 'somePipe'})
class SomePipe {
transform(value: string) { return `transformed ${value}`; }
transform(value: string) {
return `transformed ${value}`;
}
}
@Component({selector: 'comp', template: `<div [someDir]="'someValue' | somePipe"></div>`})
@ -120,11 +127,17 @@ const bTok = new InjectionToken<string>('b');
describe('using the async helper with context passing', () => {
type TestContext = {actuallyDone: boolean};
beforeEach(function(this: TestContext) { this.actuallyDone = false; });
beforeEach(function(this: TestContext) {
this.actuallyDone = false;
});
afterEach(function(this: TestContext) { expect(this.actuallyDone).toEqual(true); });
afterEach(function(this: TestContext) {
expect(this.actuallyDone).toEqual(true);
});
it('should run normal tests', function(this: TestContext) { this.actuallyDone = true; });
it('should run normal tests', function(this: TestContext) {
this.actuallyDone = true;
});
it('should run normal async tests', function(this: TestContext, done) {
setTimeout(() => {
@ -133,8 +146,9 @@ const bTok = new InjectionToken<string>('b');
}, 0);
});
it('should run async tests with tasks',
async(function(this: TestContext) { setTimeout(() => this.actuallyDone = true, 0); }));
it('should run async tests with tasks', async(function(this: TestContext) {
setTimeout(() => this.actuallyDone = true, 0);
}));
it('should run async tests with promises', async(function(this: TestContext) {
const p = new Promise((resolve, reject) => setTimeout(resolve, 10));
@ -149,18 +163,26 @@ const bTok = new InjectionToken<string>('b');
type TestContext = {contextModified: boolean};
beforeEach(function(this: TestContext) { this.contextModified = false; });
beforeEach(function(this: TestContext) {
this.contextModified = false;
});
afterEach(function(this: TestContext) { expect(this.contextModified).toEqual(true); });
afterEach(function(this: TestContext) {
expect(this.contextModified).toEqual(true);
});
it('should pass context to inject helper',
inject([], function(this: TestContext) { this.contextModified = true; }));
it('should pass context to inject helper', inject([], function(this: TestContext) {
this.contextModified = true;
}));
it('should pass context to fakeAsync helper',
fakeAsync(function(this: TestContext) { this.contextModified = true; }));
it('should pass context to fakeAsync helper', fakeAsync(function(this: TestContext) {
this.contextModified = true;
}));
it('should pass context to withModule helper - simple',
withModule(moduleConfig, function(this: TestContext) { this.contextModified = true; }));
withModule(moduleConfig, function(this: TestContext) {
this.contextModified = true;
}));
it('should pass context to withModule helper - advanced',
withModule(moduleConfig)
@ -199,7 +221,7 @@ const bTok = new InjectionToken<string>('b');
it('should allow the use of fakeAsync',
fakeAsync(inject([FancyService], (service: FancyService) => {
let value: string = undefined !;
let value: string = undefined!;
service.getAsyncValue().then((val) => value = val);
tick();
expect(value).toEqual('async value');
@ -329,7 +351,9 @@ const bTok = new InjectionToken<string>('b');
describe('overwriting metadata', () => {
@Pipe({name: 'undefined'})
class SomePipe {
transform(value: string): string { return `transformed ${value}`; }
transform(value: string): string {
return `transformed ${value}`;
}
}
@Directive({selector: '[undefined]'})
@ -418,7 +442,6 @@ const bTok = new InjectionToken<string>('b');
});
describe('overriding providers', () => {
describe('in core', () => {
it('ComponentFactoryResolver', () => {
const componentFactoryMock =
@ -429,7 +452,6 @@ const bTok = new InjectionToken<string>('b');
});
describe('in NgModules', () => {
it('should support useValue', () => {
TestBed.configureTestingModule({
providers: [
@ -501,7 +523,9 @@ const bTok = new InjectionToken<string>('b');
@NgModule()
class SomeModule {
constructor() { someModule = this; }
constructor() {
someModule = this;
}
}
TestBed.configureTestingModule({
@ -731,11 +755,12 @@ const bTok = new InjectionToken<string>('b');
@Directive({selector: '[test]'})
class TestDir {
constructor() { testDir = this; }
constructor() {
testDir = this;
}
// TODO(issue/24571): remove '!'.
@Input('test')
test !: string;
@Input('test') test!: string;
}
TestBed.overrideTemplateUsingTestingModule(
@ -746,7 +771,7 @@ const bTok = new InjectionToken<string>('b');
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('Hello world!');
expect(testDir).toBeAnInstanceOf(TestDir);
expect(testDir !.test).toBe('some prop');
expect(testDir!.test).toBe('some prop');
});
it('should throw if the TestBed is already created', () => {
@ -776,9 +801,7 @@ const bTok = new InjectionToken<string>('b');
});
describe('setting up the compiler', () => {
describe('providers', () => {
it('should use set up providers', fakeAsync(() => {
// Keeping this component inside the test is needed to make sure it's not resolved
// prior to this test, thus having ɵcmp and a reference in resource
@ -869,8 +892,9 @@ const bTok = new InjectionToken<string>('b');
const itPromise = patchJasmineIt();
const barError = new Error('bar');
it('throws an async error',
async(inject([], () => setTimeout(() => { throw barError; }, 0))));
it('throws an async error', async(inject([], () => setTimeout(() => {
throw barError;
}, 0))));
itPromise.then(() => done.fail('Expected test to fail, but it did not'), (err) => {
expect(err).toEqual(barError);
@ -883,7 +907,7 @@ const bTok = new InjectionToken<string>('b');
const itPromise = patchJasmineIt();
it('should fail with an error from a promise', async(inject([], () => {
let reject: (error: any) => void = undefined !;
let reject: (error: any) => void = undefined!;
const promise = new Promise((_, rej) => reject = rej);
const p = promise.then(() => expect(1).toEqual(2));
@ -919,21 +943,23 @@ const bTok = new InjectionToken<string>('b');
}
expect(
() => it(
'should fail', withModule(
{declarations: [InlineCompWithUrlTemplate]},
() => TestBed.createComponent(InlineCompWithUrlTemplate))))
() =>
it('should fail',
withModule(
{declarations: [InlineCompWithUrlTemplate]},
() => TestBed.createComponent(InlineCompWithUrlTemplate))))
.toThrowError(
ivyEnabled ?
`Component 'InlineCompWithUrlTemplate' is not resolved:
- templateUrl: /base/angular/packages/platform-browser/test/static_assets/test.html
Did you run and wait for 'resolveComponentResources()'?` :
`This test module uses the component ${stringify(InlineCompWithUrlTemplate)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` +
`This test module uses the component ${
stringify(
InlineCompWithUrlTemplate)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` +
`Please call "TestBed.compileComponents" before your test.`);
restoreJasmineIt();
});
});
@ -972,7 +998,6 @@ Did you run and wait for 'resolveComponentResources()'?` :
});
describe('creating components', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
@ -1008,7 +1033,6 @@ Did you run and wait for 'resolveComponentResources()'?` :
const componentFixture = TestBed.createComponent(ChildComp);
componentFixture.detectChanges();
expect(componentFixture.nativeElement).toHaveText('Mock');
}));
it('should override a provider', async(() => {