refactor: move angular source to /packages rather than modules/@angular

This commit is contained in:
Jason Aden
2017-03-02 10:48:42 -08:00
parent 5ad5301a3e
commit 3e51a19983
1051 changed files with 18 additions and 18 deletions

View File

@ -0,0 +1,42 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {APP_ID, NgModule, NgZone, PLATFORM_INITIALIZER, PlatformRef, Provider, createPlatformFactory, platformCore} from '@angular/core';
import {BrowserModule, ɵBrowserDomAdapter as BrowserDomAdapter, ɵELEMENT_PROBE_PROVIDERS as ELEMENT_PROBE_PROVIDERS} from '@angular/platform-browser';
import {BrowserDetection, createNgZone} from './browser_util';
function initBrowserTests() {
BrowserDomAdapter.makeCurrent();
BrowserDetection.setup();
}
const _TEST_BROWSER_PLATFORM_PROVIDERS: Provider[] =
[{provide: PLATFORM_INITIALIZER, useValue: initBrowserTests, multi: true}];
/**
* Platform for testing
*
* @stable
*/
export const platformBrowserTesting =
createPlatformFactory(platformCore, 'browserTesting', _TEST_BROWSER_PLATFORM_PROVIDERS);
/**
* NgModule for testing.
*
* @stable
*/
@NgModule({
exports: [BrowserModule],
providers: [
{provide: APP_ID, useValue: 'a'},
ELEMENT_PROBE_PROVIDERS,
{provide: NgZone, useFactory: createNgZone},
]
})
export class BrowserTestingModule {
}

View File

@ -0,0 +1,137 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgZone, ɵglobal as global} from '@angular/core';
import {ɵgetDOM as getDOM} from '@angular/platform-browser';
export let browserDetection: BrowserDetection;
export class BrowserDetection {
private _overrideUa: string;
private get _ua(): string {
if (typeof this._overrideUa === 'string') {
return this._overrideUa;
}
return getDOM() ? getDOM().getUserAgent() : '';
}
static setup() { browserDetection = new BrowserDetection(null); }
constructor(ua: string) { this._overrideUa = ua; }
get isFirefox(): boolean { return this._ua.indexOf('Firefox') > -1; }
get isAndroid(): boolean {
return this._ua.indexOf('Mozilla/5.0') > -1 && this._ua.indexOf('Android') > -1 &&
this._ua.indexOf('AppleWebKit') > -1 && this._ua.indexOf('Chrome') == -1 &&
this._ua.indexOf('IEMobile') == -1;
}
get isEdge(): boolean { return this._ua.indexOf('Edge') > -1; }
get isIE(): boolean { return this._ua.indexOf('Trident') > -1; }
get isWebkit(): boolean {
return this._ua.indexOf('AppleWebKit') > -1 && this._ua.indexOf('Edge') == -1 &&
this._ua.indexOf('IEMobile') == -1;
}
get isIOS7(): boolean {
return (this._ua.indexOf('iPhone OS 7') > -1 || this._ua.indexOf('iPad OS 7') > -1) &&
this._ua.indexOf('IEMobile') == -1;
}
get isSlow(): boolean { return this.isAndroid || this.isIE || this.isIOS7; }
// The Intl API is only natively supported in Chrome, Firefox, IE11 and Edge.
// This detector is needed in tests to make the difference between:
// 1) IE11/Edge: they have a native Intl API, but with some discrepancies
// 2) IE9/IE10: they use the polyfill, and so no discrepancies
get supportsNativeIntlApi(): boolean {
return !!(<any>global).Intl && (<any>global).Intl !== (<any>global).IntlPolyfill;
}
get isChromeDesktop(): boolean {
return this._ua.indexOf('Chrome') > -1 && this._ua.indexOf('Mobile Safari') == -1 &&
this._ua.indexOf('Edge') == -1;
}
// "Old Chrome" means Chrome 3X, where there are some discrepancies in the Intl API.
// Android 4.4 and 5.X have such browsers by default (respectively 30 and 39).
get isOldChrome(): boolean {
return this._ua.indexOf('Chrome') > -1 && this._ua.indexOf('Chrome/3') > -1 &&
this._ua.indexOf('Edge') == -1;
}
}
BrowserDetection.setup();
export function dispatchEvent(element: any, eventType: any): void {
getDOM().dispatchEvent(element, getDOM().createEvent(eventType));
}
export function el(html: string): HTMLElement {
return <HTMLElement>getDOM().firstChild(getDOM().content(getDOM().createTemplate(html)));
}
export function normalizeCSS(css: string): string {
return css.replace(/\s+/g, ' ')
.replace(/:\s/g, ':')
.replace(/'/g, '"')
.replace(/ }/g, '}')
.replace(/url\((\"|\s)(.+)(\"|\s)\)(\s*)/g, (...match: string[]) => `url("${match[2]}")`)
.replace(/\[(.+)=([^"\]]+)\]/g, (...match: string[]) => `[${match[1]}="${match[2]}"]`);
}
const _singleTagWhitelist = ['br', 'hr', 'input'];
export function stringifyElement(el: any /** TODO #9100 */): string {
let result = '';
if (getDOM().isElementNode(el)) {
const tagName = getDOM().tagName(el).toLowerCase();
// Opening tag
result += `<${tagName}`;
// Attributes in an ordered way
const attributeMap = getDOM().attributeMap(el);
const keys: string[] = Array.from(attributeMap.keys()).sort();
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const attValue = attributeMap.get(key);
if (typeof attValue !== 'string') {
result += ` ${key}`;
} else {
result += ` ${key}="${attValue}"`;
}
}
result += '>';
// Children
const childrenRoot = getDOM().templateAwareRoot(el);
const children = childrenRoot ? getDOM().childNodes(childrenRoot) : [];
for (let j = 0; j < children.length; j++) {
result += stringifyElement(children[j]);
}
// Closing tag
if (_singleTagWhitelist.indexOf(tagName) == -1) {
result += `</${tagName}>`;
}
} else if (getDOM().isCommentNode(el)) {
result += `<!--${getDOM().nodeValue(el)}-->`;
} else {
result += getDOM().getText(el);
}
return result;
}
export function createNgZone(): NgZone {
return new NgZone({enableLongStackTrace: true});
}

View File

@ -0,0 +1,14 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @module
* @description
* Entry point for all public APIs of the platform-browser/testing package.
*/
export * from './browser';

View File

@ -0,0 +1,274 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ɵglobal as global} from '@angular/core';
import {ɵgetDOM as getDOM} from '@angular/platform-browser';
/**
* Jasmine matchers that check Angular specific conditions.
*/
export interface NgMatchers extends jasmine.Matchers {
/**
* Expect the value to be a `Promise`.
*
* ## Example
*
* {@example testing/ts/matchers.ts region='toBePromise'}
*/
toBePromise(): boolean;
/**
* Expect the value to be an instance of a class.
*
* ## Example
*
* {@example testing/ts/matchers.ts region='toBeAnInstanceOf'}
*/
toBeAnInstanceOf(expected: any): boolean;
/**
* Expect the element to have exactly the given text.
*
* ## Example
*
* {@example testing/ts/matchers.ts region='toHaveText'}
*/
toHaveText(expected: string): boolean;
/**
* Expect the element to have the given CSS class.
*
* ## Example
*
* {@example testing/ts/matchers.ts region='toHaveCssClass'}
*/
toHaveCssClass(expected: string): boolean;
/**
* Expect the element to have the given CSS styles.
*
* ## Example
*
* {@example testing/ts/matchers.ts region='toHaveCssStyle'}
*/
toHaveCssStyle(expected: {[k: string]: string}|string): boolean;
/**
* Expect a class to implement the interface of the given class.
*
* ## Example
*
* {@example testing/ts/matchers.ts region='toImplement'}
*/
toImplement(expected: any): boolean;
/**
* Expect an exception to contain the given error text.
*
* ## Example
*
* {@example testing/ts/matchers.ts region='toContainError'}
*/
toContainError(expected: any): boolean;
/**
* Invert the matchers.
*/
not: NgMatchers;
}
const _global = <any>(typeof window === 'undefined' ? global : window);
/**
* Jasmine matching function with Angular matchers mixed in.
*
* ## Example
*
* {@example testing/ts/matchers.ts region='toHaveText'}
*/
export const expect: (actual: any) => NgMatchers = <any>_global.expect;
// Some Map polyfills don't polyfill Map.toString correctly, which
// gives us bad error messages in tests.
// The only way to do this in Jasmine is to monkey patch a method
// to the object :-(
(Map as any).prototype['jasmineToString'] = function() {
const m = this;
if (!m) {
return '' + m;
}
const res: any[] = [];
m.forEach((v: any, k: any) => { res.push(`${k}:${v}`); });
return `{ ${res.join(',')} }`;
};
_global.beforeEach(function() {
jasmine.addMatchers({
// Custom handler for Map as Jasmine does not support it yet
toEqual: function(util) {
return {
compare: function(actual: any, expected: any) {
return {pass: util.equals(actual, expected, [compareMap])};
}
};
function compareMap(actual: any, expected: any) {
if (actual instanceof Map) {
let pass = actual.size === expected.size;
if (pass) {
actual.forEach((v: any, k: any) => { pass = pass && util.equals(v, expected.get(k)); });
}
return pass;
} else {
return undefined;
}
}
},
toBePromise: function() {
return {
compare: function(actual: any) {
const pass = typeof actual === 'object' && typeof actual.then === 'function';
return {pass: pass, get message() { return 'Expected ' + actual + ' to be a promise'; }};
}
};
},
toBeAnInstanceOf: function() {
return {
compare: function(actual: any, expectedClass: any) {
const pass = typeof actual === 'object' && actual instanceof expectedClass;
return {
pass: pass,
get message() {
return 'Expected ' + actual + ' to be an instance of ' + expectedClass;
}
};
}
};
},
toHaveText: function() {
return {
compare: function(actual: any, expectedText: string) {
const actualText = elementText(actual);
return {
pass: actualText == expectedText,
get message() { return 'Expected ' + actualText + ' to be equal to ' + expectedText; }
};
}
};
},
toHaveCssClass: function() {
return {compare: buildError(false), negativeCompare: buildError(true)};
function buildError(isNot: boolean) {
return function(actual: any, className: string) {
return {
pass: getDOM().hasClass(actual, className) == !isNot,
get message() {
return `Expected ${actual.outerHTML} ${isNot ? 'not ' : ''}to contain the CSS class "${className}"`;
}
};
};
}
},
toHaveCssStyle: function() {
return {
compare: function(actual: any, styles: {[k: string]: string}|string) {
let allPassed: boolean;
if (typeof styles === 'string') {
allPassed = getDOM().hasStyle(actual, styles);
} else {
allPassed = Object.keys(styles).length !== 0;
Object.keys(styles).forEach(prop => {
allPassed = allPassed && getDOM().hasStyle(actual, prop, styles[prop]);
});
}
return {
pass: allPassed,
get message() {
const expectedValueStr = typeof styles === 'string' ? styles : JSON.stringify(styles);
return `Expected ${actual.outerHTML} ${!allPassed ? ' ' : 'not '}to contain the
CSS ${typeof styles === 'string' ? 'property' : 'styles'} "${expectedValueStr}"`;
}
};
}
};
},
toContainError: function() {
return {
compare: function(actual: any, expectedText: any) {
const errorMessage = actual.toString();
return {
pass: errorMessage.indexOf(expectedText) > -1,
get message() { return 'Expected ' + errorMessage + ' to contain ' + expectedText; }
};
}
};
},
toImplement: function() {
return {
compare: function(actualObject: any, expectedInterface: any) {
const intProps = Object.keys(expectedInterface.prototype);
const missedMethods: any[] = [];
intProps.forEach((k) => {
if (!actualObject.constructor.prototype[k]) missedMethods.push(k);
});
return {
pass: missedMethods.length == 0,
get message() {
return 'Expected ' + actualObject + ' to have the following methods: ' +
missedMethods.join(', ');
}
};
}
};
}
});
});
function elementText(n: any): string {
const hasNodes = (n: any) => {
const children = getDOM().childNodes(n);
return children && children.length > 0;
};
if (n instanceof Array) {
return n.map(elementText).join('');
}
if (getDOM().isCommentNode(n)) {
return '';
}
if (getDOM().isElementNode(n) && getDOM().tagName(n) == 'CONTENT') {
return elementText(Array.prototype.slice.apply(getDOM().getDistributedNodes(n)));
}
if (getDOM().hasShadowRoot(n)) {
return elementText(getDOM().childNodesAsList(getDOM().getShadowRoot(n)));
}
if (hasNodes(n)) {
return elementText(getDOM().childNodesAsList(n));
}
return getDOM().getText(n);
}