feat(test): Add an external version of the test library
Adds test adapters for TypeScript and JavaScript only, exported as part of the test_lib module. These work with the Jasmine test framework, and allow use of the test injector within test blocks via the `inject` function. See #4572, #4177, #4035, #2783 This includes the TestComponentBuilder. It allows using the test injector with Jasmine bindings, and waits for returned promises before completing async test blocks.
This commit is contained in:
112
modules/angular2/src/test_lib/matchers.dart
Normal file
112
modules/angular2/src/test_lib/matchers.dart
Normal file
@ -0,0 +1,112 @@
|
||||
library test_lib.matchers;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:guinness/guinness.dart' as gns;
|
||||
|
||||
import 'package:angular2/src/core/dom/dom_adapter.dart' show DOM;
|
||||
|
||||
Expect expect(actual, [matcher]) {
|
||||
final expect = new Expect(actual);
|
||||
if (matcher != null) expect.to(matcher);
|
||||
return expect;
|
||||
}
|
||||
|
||||
const _u = const Object();
|
||||
|
||||
expectErrorMessage(actual, expectedMessage) {
|
||||
expect(actual.toString()).toContain(expectedMessage);
|
||||
}
|
||||
|
||||
expectException(Function actual, expectedMessage) {
|
||||
try {
|
||||
actual();
|
||||
} catch (e, s) {
|
||||
expectErrorMessage(e, expectedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
class Expect extends gns.Expect {
|
||||
Expect(actual) : super(actual);
|
||||
|
||||
NotExpect get not => new NotExpect(actual);
|
||||
|
||||
void toEqual(expected) => toHaveSameProps(expected);
|
||||
void toContainError(message) => expectErrorMessage(this.actual, message);
|
||||
void toThrowError([message = ""]) => toThrowWith(message: message);
|
||||
void toThrowErrorWith(message) => expectException(this.actual, message);
|
||||
void toBePromise() => gns.guinness.matchers.toBeTrue(actual is Future);
|
||||
void toHaveCssClass(className) =>
|
||||
gns.guinness.matchers.toBeTrue(DOM.hasClass(actual, className));
|
||||
void toImplement(expected) => toBeA(expected);
|
||||
void toBeNaN() =>
|
||||
gns.guinness.matchers.toBeTrue(double.NAN.compareTo(actual) == 0);
|
||||
void toHaveText(expected) => _expect(elementText(actual), expected);
|
||||
void toHaveBeenCalledWith([a = _u, b = _u, c = _u, d = _u, e = _u, f = _u]) =>
|
||||
_expect(_argsMatch(actual, a, b, c, d, e, f), true,
|
||||
reason: 'method invoked with correct arguments');
|
||||
Function get _expect => gns.guinness.matchers.expect;
|
||||
|
||||
// TODO(tbosch): move this hack into Guinness
|
||||
_argsMatch(spyFn, [a0 = _u, a1 = _u, a2 = _u, a3 = _u, a4 = _u, a5 = _u]) {
|
||||
var calls = spyFn.calls;
|
||||
final toMatch = _takeDefined([a0, a1, a2, a3, a4, a5]);
|
||||
if (calls.isEmpty) {
|
||||
return false;
|
||||
} else {
|
||||
gns.SamePropsMatcher matcher = new gns.SamePropsMatcher(toMatch);
|
||||
for (var i = 0; i < calls.length; i++) {
|
||||
var call = calls[i];
|
||||
// TODO: create a better error message, not just 'Expected: <true> Actual: <false>'.
|
||||
// For hacking this is good:
|
||||
// print(call.positionalArguments);
|
||||
if (matcher.matches(call.positionalArguments, null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
List _takeDefined(List iter) => iter.takeWhile((_) => _ != _u).toList();
|
||||
}
|
||||
|
||||
class NotExpect extends gns.NotExpect {
|
||||
NotExpect(actual) : super(actual);
|
||||
|
||||
void toEqual(expected) => toHaveSameProps(expected);
|
||||
void toBePromise() => gns.guinness.matchers.toBeFalse(actual is Future);
|
||||
void toHaveCssClass(className) =>
|
||||
gns.guinness.matchers.toBeFalse(DOM.hasClass(actual, className));
|
||||
void toBeNull() => gns.guinness.matchers.toBeFalse(actual == null);
|
||||
Function get _expect => gns.guinness.matchers.expect;
|
||||
}
|
||||
|
||||
String elementText(n) {
|
||||
hasNodes(n) {
|
||||
var children = DOM.childNodes(n);
|
||||
return children != null && children.length > 0;
|
||||
}
|
||||
|
||||
if (n is Iterable) {
|
||||
return n.map(elementText).join("");
|
||||
}
|
||||
|
||||
if (DOM.isCommentNode(n)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (DOM.isElementNode(n) && DOM.tagName(n) == 'CONTENT') {
|
||||
return elementText(DOM.getDistributedNodes(n));
|
||||
}
|
||||
|
||||
if (DOM.hasShadowRoot(n)) {
|
||||
return elementText(DOM.childNodesAsList(DOM.getShadowRoot(n)));
|
||||
}
|
||||
|
||||
if (hasNodes(n)) {
|
||||
return elementText(DOM.childNodesAsList(n));
|
||||
}
|
||||
|
||||
return DOM.getText(n);
|
||||
}
|
191
modules/angular2/src/test_lib/matchers.ts
Normal file
191
modules/angular2/src/test_lib/matchers.ts
Normal file
@ -0,0 +1,191 @@
|
||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||
import {global} from 'angular2/src/core/facade/lang';
|
||||
|
||||
|
||||
export interface NgMatchers extends jasmine.Matchers {
|
||||
toBePromise(): boolean;
|
||||
toBeAnInstanceOf(expected: any): boolean;
|
||||
toHaveText(expected: any): boolean;
|
||||
toHaveCssClass(expected: any): boolean;
|
||||
toImplement(expected: any): boolean;
|
||||
toContainError(expected: any): boolean;
|
||||
toThrowErrorWith(expectedMessage: any): boolean;
|
||||
not: NgMatchers;
|
||||
}
|
||||
|
||||
var _global: jasmine.GlobalPolluter = <any>(typeof window === 'undefined' ? global : window);
|
||||
|
||||
export var 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.prototype['jasmineToString'] = function() {
|
||||
var m = this;
|
||||
if (!m) {
|
||||
return '' + m;
|
||||
}
|
||||
var res = [];
|
||||
m.forEach((v, k) => { 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, customEqualityTesters) {
|
||||
return {
|
||||
compare: function(actual, expected) {
|
||||
return {pass: util.equals(actual, expected, [compareMap])};
|
||||
}
|
||||
};
|
||||
|
||||
function compareMap(actual, expected) {
|
||||
if (actual instanceof Map) {
|
||||
var pass = actual.size === expected.size;
|
||||
if (pass) {
|
||||
actual.forEach((v, k) => { pass = pass && util.equals(v, expected.get(k)); });
|
||||
}
|
||||
return pass;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toBePromise: function() {
|
||||
return {
|
||||
compare: function(actual, expectedClass) {
|
||||
var 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, expectedClass) {
|
||||
var 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, expectedText) {
|
||||
var 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) {
|
||||
return function(actual, className) {
|
||||
return {
|
||||
pass: DOM.hasClass(actual, className) == !isNot,
|
||||
get message() {
|
||||
return `Expected ${actual.outerHTML} ${isNot ? 'not ' : ''}to contain the CSS class "${className}"`;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
toContainError: function() {
|
||||
return {
|
||||
compare: function(actual, expectedText) {
|
||||
var errorMessage = actual.toString();
|
||||
return {
|
||||
pass: errorMessage.indexOf(expectedText) > -1,
|
||||
get message() { return 'Expected ' + errorMessage + ' to contain ' + expectedText; }
|
||||
};
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
toThrowErrorWith: function() {
|
||||
return {
|
||||
compare: function(actual, expectedText) {
|
||||
try {
|
||||
actual();
|
||||
return {
|
||||
pass: false,
|
||||
get message() { return "Was expected to throw, but did not throw"; }
|
||||
};
|
||||
} catch (e) {
|
||||
var errorMessage = e.toString();
|
||||
return {
|
||||
pass: errorMessage.indexOf(expectedText) > -1,
|
||||
get message() { return 'Expected ' + errorMessage + ' to contain ' + expectedText; }
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
toImplement: function() {
|
||||
return {
|
||||
compare: function(actualObject, expectedInterface) {
|
||||
var objProps = Object.keys(actualObject.constructor.prototype);
|
||||
var intProps = Object.keys(expectedInterface.prototype);
|
||||
|
||||
var missedMethods = [];
|
||||
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) {
|
||||
var hasNodes = (n) => {
|
||||
var children = DOM.childNodes(n);
|
||||
return children && children.length > 0;
|
||||
};
|
||||
|
||||
if (n instanceof Array) {
|
||||
return n.map(elementText).join("");
|
||||
}
|
||||
|
||||
if (DOM.isCommentNode(n)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (DOM.isElementNode(n) && DOM.tagName(n) == 'CONTENT') {
|
||||
return elementText(Array.prototype.slice.apply(DOM.getDistributedNodes(n)));
|
||||
}
|
||||
|
||||
if (DOM.hasShadowRoot(n)) {
|
||||
return elementText(DOM.childNodesAsList(DOM.getShadowRoot(n)));
|
||||
}
|
||||
|
||||
if (hasNodes(n)) {
|
||||
return elementText(DOM.childNodesAsList(n));
|
||||
}
|
||||
|
||||
return DOM.getText(n);
|
||||
}
|
@ -159,11 +159,15 @@ export function createTestInjector(providers: Array<Type | Provider | any[]>): I
|
||||
* @return {FunctionWithParamTokens}
|
||||
*/
|
||||
export function inject(tokens: any[], fn: Function): FunctionWithParamTokens {
|
||||
return new FunctionWithParamTokens(tokens, fn);
|
||||
return new FunctionWithParamTokens(tokens, fn, false);
|
||||
}
|
||||
|
||||
export function injectAsync(tokens: any[], fn: Function): FunctionWithParamTokens {
|
||||
return new FunctionWithParamTokens(tokens, fn, true);
|
||||
}
|
||||
|
||||
export class FunctionWithParamTokens {
|
||||
constructor(private _tokens: any[], private _fn: Function) {}
|
||||
constructor(private _tokens: any[], private _fn: Function, public isAsync: boolean) {}
|
||||
|
||||
/**
|
||||
* Returns the value of the executed function.
|
||||
|
@ -15,7 +15,7 @@ export 'package:guinness/guinness.dart'
|
||||
SpyObject,
|
||||
SpyFunction;
|
||||
|
||||
import 'package:angular2/src/core/dom/dom_adapter.dart' show DOM;
|
||||
export 'matchers.dart' show expect, Expect, NotExpect;
|
||||
|
||||
import 'package:angular2/src/core/reflection/reflection.dart';
|
||||
import 'package:angular2/src/core/reflection/reflection_capabilities.dart';
|
||||
@ -67,88 +67,6 @@ void testSetup() {
|
||||
}, priority: 1);
|
||||
}
|
||||
|
||||
Expect expect(actual, [matcher]) {
|
||||
final expect = new Expect(actual);
|
||||
if (matcher != null) expect.to(matcher);
|
||||
return expect;
|
||||
}
|
||||
|
||||
const _u = const Object();
|
||||
|
||||
expectErrorMessage(actual, expectedMessage) {
|
||||
expect(actual.toString()).toContain(expectedMessage);
|
||||
}
|
||||
|
||||
expectException(Function actual, expectedMessage) {
|
||||
try {
|
||||
actual();
|
||||
} catch (e, s) {
|
||||
expectErrorMessage(e, expectedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
class Expect extends gns.Expect {
|
||||
Expect(actual) : super(actual);
|
||||
|
||||
NotExpect get not => new NotExpect(actual);
|
||||
|
||||
void toEqual(expected) => toHaveSameProps(expected);
|
||||
void toContainError(message) => expectErrorMessage(this.actual, message);
|
||||
void toThrowError([message = ""]) => toThrowWith(message: message);
|
||||
void toThrowErrorWith(message) => expectException(this.actual, message);
|
||||
void toBePromise() => gns.guinness.matchers.toBeTrue(actual is Future);
|
||||
void toHaveCssClass(className) =>
|
||||
gns.guinness.matchers.toBeTrue(DOM.hasClass(actual, className));
|
||||
void toImplement(expected) => toBeA(expected);
|
||||
void toBeNaN() =>
|
||||
gns.guinness.matchers.toBeTrue(double.NAN.compareTo(actual) == 0);
|
||||
void toHaveText(expected) => _expect(elementText(actual), expected);
|
||||
void toHaveBeenCalledWith([a = _u, b = _u, c = _u, d = _u, e = _u, f = _u]) =>
|
||||
_expect(_argsMatch(actual, a, b, c, d, e, f), true,
|
||||
reason: 'method invoked with correct arguments');
|
||||
Function get _expect => gns.guinness.matchers.expect;
|
||||
|
||||
// TODO(tbosch): move this hack into Guinness
|
||||
_argsMatch(spyFn, [a0 = _u, a1 = _u, a2 = _u, a3 = _u, a4 = _u, a5 = _u]) {
|
||||
var calls = spyFn.calls;
|
||||
final toMatch = _takeDefined([a0, a1, a2, a3, a4, a5]);
|
||||
if (calls.isEmpty) {
|
||||
return false;
|
||||
} else {
|
||||
gns.SamePropsMatcher matcher = new gns.SamePropsMatcher(toMatch);
|
||||
for (var i = 0; i < calls.length; i++) {
|
||||
var call = calls[i];
|
||||
// TODO: create a better error message, not just 'Expected: <true> Actual: <false>'.
|
||||
// For hacking this is good:
|
||||
// print(call.positionalArguments);
|
||||
if (matcher.matches(call.positionalArguments, null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
List _takeDefined(List iter) => iter.takeWhile((_) => _ != _u).toList();
|
||||
}
|
||||
|
||||
class NotExpect extends gns.NotExpect {
|
||||
NotExpect(actual) : super(actual);
|
||||
|
||||
void toEqual(expected) => toHaveSameProps(expected);
|
||||
void toBePromise() => gns.guinness.matchers.toBeFalse(actual is Future);
|
||||
void toHaveCssClass(className) =>
|
||||
gns.guinness.matchers.toBeFalse(DOM.hasClass(actual, className));
|
||||
void toBeNull() => gns.guinness.matchers.toBeFalse(actual == null);
|
||||
Function get _expect => gns.guinness.matchers.expect;
|
||||
}
|
||||
|
||||
void beforeEach(fn) {
|
||||
if (fn is! FunctionWithParamTokens) fn = new FunctionWithParamTokens([], fn);
|
||||
gns.beforeEach(() {
|
||||
fn.execute(_injector);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows overriding default bindings defined in test_injector.js.
|
||||
@ -174,8 +92,15 @@ void beforeEachBindings(Function fn) {
|
||||
beforeEachProviders(fn);
|
||||
}
|
||||
|
||||
void beforeEach(fn) {
|
||||
if (fn is! FunctionWithParamTokens) fn = new FunctionWithParamTokens([], fn, false);
|
||||
gns.beforeEach(() {
|
||||
fn.execute(_injector);
|
||||
});
|
||||
}
|
||||
|
||||
void _it(gnsFn, name, fn) {
|
||||
if (fn is! FunctionWithParamTokens) fn = new FunctionWithParamTokens([], fn);
|
||||
if (fn is! FunctionWithParamTokens) fn = new FunctionWithParamTokens([], fn, false);
|
||||
gnsFn(name, () {
|
||||
_inIt = true;
|
||||
fn.execute(_injector);
|
||||
@ -232,33 +157,4 @@ class SpyObject extends gns.SpyObject {
|
||||
}
|
||||
}
|
||||
|
||||
String elementText(n) {
|
||||
hasNodes(n) {
|
||||
var children = DOM.childNodes(n);
|
||||
return children != null && children.length > 0;
|
||||
}
|
||||
|
||||
if (n is Iterable) {
|
||||
return n.map((nn) => elementText(nn)).join("");
|
||||
}
|
||||
|
||||
if (DOM.isCommentNode(n)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (DOM.isElementNode(n) && DOM.tagName(n) == 'CONTENT') {
|
||||
return elementText(DOM.getDistributedNodes(n));
|
||||
}
|
||||
|
||||
if (DOM.hasShadowRoot(n)) {
|
||||
return elementText(DOM.childNodesAsList(DOM.getShadowRoot(n)));
|
||||
}
|
||||
|
||||
if (hasNodes(n)) {
|
||||
return elementText(DOM.childNodesAsList(n));
|
||||
}
|
||||
|
||||
return DOM.getText(n);
|
||||
}
|
||||
|
||||
bool isInInnerZone() => Zone.current['_innerZone'] == true;
|
||||
|
@ -10,6 +10,8 @@ import {browserDetection} from './utils';
|
||||
|
||||
export {inject} from './test_injector';
|
||||
|
||||
export {expect, NgMatchers} from './matchers';
|
||||
|
||||
export var proxy: ClassDecorator = (t) => t;
|
||||
|
||||
var _global: jasmine.GlobalPolluter = <any>(typeof window === 'undefined' ? global : window);
|
||||
@ -20,21 +22,6 @@ export type SyncTestFn = () => void;
|
||||
type AsyncTestFn = (done: () => void) => void;
|
||||
type AnyTestFn = SyncTestFn | AsyncTestFn;
|
||||
|
||||
export interface NgMatchers extends jasmine.Matchers {
|
||||
toBe(expected: any): boolean;
|
||||
toEqual(expected: any): boolean;
|
||||
toBePromise(): boolean;
|
||||
toBeAnInstanceOf(expected: any): boolean;
|
||||
toHaveText(expected: any): boolean;
|
||||
toHaveCssClass(expected: any): boolean;
|
||||
toImplement(expected: any): boolean;
|
||||
toContainError(expected: any): boolean;
|
||||
toThrowErrorWith(expectedMessage: any): boolean;
|
||||
not: NgMatchers;
|
||||
}
|
||||
|
||||
export var expect: (actual: any) => NgMatchers = <any>_global.expect;
|
||||
|
||||
export class AsyncTestCompleter {
|
||||
constructor(private _done: Function) {}
|
||||
|
||||
@ -201,148 +188,6 @@ export function iit(name, fn, timeOut = null): void {
|
||||
return _it(jsmIIt, name, fn, timeOut);
|
||||
}
|
||||
|
||||
// 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.prototype['jasmineToString'] = function() {
|
||||
var m = this;
|
||||
if (!m) {
|
||||
return '' + m;
|
||||
}
|
||||
var res = [];
|
||||
m.forEach((v, k) => { 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, customEqualityTesters) {
|
||||
return {
|
||||
compare: function(actual, expected) {
|
||||
return {pass: util.equals(actual, expected, [compareMap])};
|
||||
}
|
||||
};
|
||||
|
||||
function compareMap(actual, expected) {
|
||||
if (actual instanceof Map) {
|
||||
var pass = actual.size === expected.size;
|
||||
if (pass) {
|
||||
actual.forEach((v, k) => { pass = pass && util.equals(v, expected.get(k)); });
|
||||
}
|
||||
return pass;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toBePromise: function() {
|
||||
return {
|
||||
compare: function(actual, expectedClass) {
|
||||
var 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, expectedClass) {
|
||||
var 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, expectedText) {
|
||||
var 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) {
|
||||
return function(actual, className) {
|
||||
return {
|
||||
pass: DOM.hasClass(actual, className) == !isNot,
|
||||
get message() {
|
||||
return `Expected ${actual.outerHTML} ${isNot ? 'not ' : ''}to contain the CSS class "${className}"`;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
toContainError: function() {
|
||||
return {
|
||||
compare: function(actual, expectedText) {
|
||||
var errorMessage = actual.toString();
|
||||
return {
|
||||
pass: errorMessage.indexOf(expectedText) > -1,
|
||||
get message() { return 'Expected ' + errorMessage + ' to contain ' + expectedText; }
|
||||
};
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
toThrowErrorWith: function() {
|
||||
return {
|
||||
compare: function(actual, expectedText) {
|
||||
try {
|
||||
actual();
|
||||
return {
|
||||
pass: false,
|
||||
get message() { return "Was expected to throw, but did not throw"; }
|
||||
};
|
||||
} catch (e) {
|
||||
var errorMessage = e.toString();
|
||||
return {
|
||||
pass: errorMessage.indexOf(expectedText) > -1,
|
||||
get message() { return 'Expected ' + errorMessage + ' to contain ' + expectedText; }
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
toImplement: function() {
|
||||
return {
|
||||
compare: function(actualObject, expectedInterface) {
|
||||
var objProps = Object.keys(actualObject.constructor.prototype);
|
||||
var intProps = Object.keys(expectedInterface.prototype);
|
||||
|
||||
var missedMethods = [];
|
||||
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(", ");
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export interface GuinessCompatibleSpy extends jasmine.Spy {
|
||||
/** By chaining the spy with and.returnValue, all calls to the function will return a specific
|
||||
@ -410,35 +255,6 @@ export class SpyObject {
|
||||
}
|
||||
}
|
||||
|
||||
function elementText(n) {
|
||||
var hasNodes = (n) => {
|
||||
var children = DOM.childNodes(n);
|
||||
return children && children.length > 0;
|
||||
};
|
||||
|
||||
if (n instanceof Array) {
|
||||
return n.map((nn) => elementText(nn)).join("");
|
||||
}
|
||||
|
||||
if (DOM.isCommentNode(n)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (DOM.isElementNode(n) && DOM.tagName(n) == 'CONTENT') {
|
||||
return elementText(Array.prototype.slice.apply(DOM.getDistributedNodes(n)));
|
||||
}
|
||||
|
||||
if (DOM.hasShadowRoot(n)) {
|
||||
return elementText(DOM.childNodesAsList(DOM.getShadowRoot(n)));
|
||||
}
|
||||
|
||||
if (hasNodes(n)) {
|
||||
return elementText(DOM.childNodesAsList(n));
|
||||
}
|
||||
|
||||
return DOM.getText(n);
|
||||
}
|
||||
|
||||
export function isInInnerZone(): boolean {
|
||||
return (<NgZoneZone>global.zone)._innerZone === true;
|
||||
}
|
||||
|
3
modules/angular2/src/test_lib/test_lib_public.dart
Normal file
3
modules/angular2/src/test_lib/test_lib_public.dart
Normal file
@ -0,0 +1,3 @@
|
||||
library angular2.test_lib_public;
|
||||
|
||||
// empty as this file is for external TS/js users and should not be transpiled to dart
|
165
modules/angular2/src/test_lib/test_lib_public.ts
Normal file
165
modules/angular2/src/test_lib/test_lib_public.ts
Normal file
@ -0,0 +1,165 @@
|
||||
/**
|
||||
* Public Test Library for unit testing Angular2 Applications. Uses the
|
||||
* Jasmine framework.
|
||||
*/
|
||||
import {global} from 'angular2/src/core/facade/lang';
|
||||
|
||||
import {bind} from 'angular2/src/core/di';
|
||||
|
||||
import {createTestInjector, FunctionWithParamTokens, inject, injectAsync} from './test_injector';
|
||||
|
||||
export {inject, injectAsync} from './test_injector';
|
||||
|
||||
export {expect} from './matchers';
|
||||
|
||||
var _global: jasmine.GlobalPolluter = <any>(typeof window === 'undefined' ? global : window);
|
||||
|
||||
export var afterEach: Function = _global.afterEach;
|
||||
export var describe: Function = _global.describe;
|
||||
export var ddescribe: Function = _global.fdescribe;
|
||||
export var fdescribe: Function = _global.fdescribe;
|
||||
export var xdescribe: Function = _global.xdescribe;
|
||||
|
||||
export type SyncTestFn = () => void;
|
||||
export type AsyncTestFn = (done: () => void) => void;
|
||||
export type AnyTestFn = SyncTestFn | AsyncTestFn;
|
||||
|
||||
var jsmBeforeEach = _global.beforeEach;
|
||||
var jsmIt = _global.it;
|
||||
var jsmIIt = _global.fit;
|
||||
var jsmXIt = _global.xit;
|
||||
|
||||
var testProviders;
|
||||
var injector;
|
||||
|
||||
// Reset the test providers before each test.
|
||||
jsmBeforeEach(() => {
|
||||
testProviders = [];
|
||||
injector = null;
|
||||
});
|
||||
|
||||
/**
|
||||
* Allows overriding default providers of the test injector,
|
||||
* defined in test_injector.js.
|
||||
*
|
||||
* The given function must return a list of DI providers.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* beforeEachProviders(() => [
|
||||
* bind(Compiler).toClass(MockCompiler),
|
||||
* bind(SomeToken).toValue(myValue),
|
||||
* ]);
|
||||
*/
|
||||
export function beforeEachProviders(fn): void {
|
||||
jsmBeforeEach(() => {
|
||||
var providers = fn();
|
||||
if (!providers) return;
|
||||
testProviders = [...testProviders, ...providers];
|
||||
if (injector !== null) {
|
||||
throw new Error('beforeEachProviders was called after the injector had ' +
|
||||
'been used in a beforeEach or it block. This invalidates the ' +
|
||||
'test injector');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _isPromiseLike(input): boolean {
|
||||
return input && !!(input.then);
|
||||
}
|
||||
|
||||
function _it(jsmFn: Function, name: string, testFn: FunctionWithParamTokens | AnyTestFn,
|
||||
testTimeOut: number): void {
|
||||
var timeOut = testTimeOut;
|
||||
|
||||
if (testFn instanceof FunctionWithParamTokens) {
|
||||
// The test case uses inject(). ie `it('test', inject([ClassA], (a) => { ...
|
||||
// }));`
|
||||
if (testFn.isAsync) {
|
||||
jsmFn(name, (done) => {
|
||||
if (!injector) {
|
||||
injector = createTestInjector(testProviders);
|
||||
}
|
||||
var returned = testFn.execute(injector);
|
||||
if (_isPromiseLike(returned)) {
|
||||
returned.then(done, done.fail);
|
||||
} else {
|
||||
done.fail('Error: injectAsync was expected to return a promise, but the ' +
|
||||
' returned value was: ' + returned);
|
||||
}
|
||||
}, timeOut);
|
||||
} else {
|
||||
jsmFn(name, () => {
|
||||
if (!injector) {
|
||||
injector = createTestInjector(testProviders);
|
||||
}
|
||||
var returned = testFn.execute(injector);
|
||||
if (_isPromiseLike(returned)) {
|
||||
throw new Error('inject returned a promise. Did you mean to use injectAsync?');
|
||||
};
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// The test case doesn't use inject(). ie `it('test', (done) => { ... }));`
|
||||
jsmFn(name, testFn, timeOut);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function beforeEach(fn: FunctionWithParamTokens | AnyTestFn): void {
|
||||
if (fn instanceof FunctionWithParamTokens) {
|
||||
// The test case uses inject(). ie `beforeEach(inject([ClassA], (a) => { ...
|
||||
// }));`
|
||||
if (fn.isAsync) {
|
||||
jsmBeforeEach((done) => {
|
||||
if (!injector) {
|
||||
injector = createTestInjector(testProviders);
|
||||
}
|
||||
var returned = fn.execute(injector);
|
||||
if (_isPromiseLike(returned)) {
|
||||
returned.then(done, done.fail);
|
||||
} else {
|
||||
done.fail('Error: injectAsync was expected to return a promise, but the ' +
|
||||
' returned value was: ' + returned);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
jsmBeforeEach(() => {
|
||||
if (!injector) {
|
||||
injector = createTestInjector(testProviders);
|
||||
}
|
||||
var returned = fn.execute(injector);
|
||||
if (_isPromiseLike(returned)) {
|
||||
throw new Error('inject returned a promise. Did you mean to use injectAsync?');
|
||||
};
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// The test case doesn't use inject(). ie `beforeEach((done) => { ... }));`
|
||||
if ((<any>fn).length === 0) {
|
||||
jsmBeforeEach(() => { (<SyncTestFn>fn)(); });
|
||||
} else {
|
||||
jsmBeforeEach((done) => { (<AsyncTestFn>fn)(done); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function it(name: string, fn: FunctionWithParamTokens | AnyTestFn, timeOut: number = null):
|
||||
void {
|
||||
return _it(jsmIt, name, fn, timeOut);
|
||||
}
|
||||
|
||||
export function xit(name: string, fn: FunctionWithParamTokens | AnyTestFn, timeOut: number = null):
|
||||
void {
|
||||
return _it(jsmXIt, name, fn, timeOut);
|
||||
}
|
||||
|
||||
export function iit(name: string, fn: FunctionWithParamTokens | AnyTestFn, timeOut: number = null):
|
||||
void {
|
||||
return _it(jsmIIt, name, fn, timeOut);
|
||||
}
|
||||
|
||||
export function fit(name: string, fn: FunctionWithParamTokens | AnyTestFn, timeOut: number = null):
|
||||
void {
|
||||
return _it(jsmIIt, name, fn, timeOut);
|
||||
}
|
Reference in New Issue
Block a user