diff --git a/gulpfile.js b/gulpfile.js
index 44baa44792..50ca975a39 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -76,7 +76,8 @@ var _exampleBoilerplateFiles = [
'styles.css',
'tsconfig.json',
'tslint.json',
- 'typings.json'
+ 'typings.json',
+ 'wallaby.js'
];
var _exampleDartWebBoilerPlateFiles = ['styles.css'];
diff --git a/public/_includes/_hero.jade b/public/_includes/_hero.jade
index cddbb5e5e3..26c4ddd46b 100644
--- a/public/_includes/_hero.jade
+++ b/public/_includes/_hero.jade
@@ -3,23 +3,22 @@
- var headerTitle = title + (typeof varType !== 'undefined' ? (': ' + varType) : '');
- var capitalize = function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); }
- var useBadges = docType || stability;
-
+
if current.path[4] && current.path[3] == 'api'
- var textFormat = 'is-standard-case'
header(class="hero background-sky")
- span(class="hero-title-with-badges" layout="row" layout-xs="column" layout-align="start center" layout-align-xs="start start")
- h1(class="hero-title text-display-1 #{textFormat}") #{headerTitle}
- if useBadges
- span(class="badges")
- if docType
- span(class="status-badge").
- #{capitalize(docType)}
- if stability
- span(layout="row" class="status-badge")
- // badge circle is filled based on stability by matching a css selector in _hero.scss
- span(class="status-circle status-#{stability}")
- span Stability: #{capitalize(stability)}
+ h1(class="hero-title text-display-1 #{textFormat}") #{headerTitle}
+ if useBadges
+ span(class="badges")
+ if docType
+ span(class="status-badge").
+ #{capitalize(docType)}
+ if stability
+ span(layout="row" class="status-badge")
+ // badge circle is filled based on stability by matching a css selector in _hero.scss
+ span(class="status-circle status-#{stability}")
+ span Stability: #{capitalize(stability)}
if subtitle
h2.hero-subtitle.text-subhead #{subtitle}
diff --git a/public/_includes/_main-nav.jade b/public/_includes/_main-nav.jade
index 339d5f0e04..71cd401d91 100644
--- a/public/_includes/_main-nav.jade
+++ b/public/_includes/_main-nav.jade
@@ -1,3 +1,6 @@
+- var language = current.path[1] || 'ts'
+- if (language !== 'ts' || language !== 'js' || language !== 'dart') { language = 'ts'; }
+
md-toolbar(class="main-nav background-regal l-pinned-top l-layer-5",scroll-y-offset-element)
nav
h1 Angular by Google
@@ -6,10 +9,10 @@ md-toolbar(class="main-nav background-regal l-pinned-top l-layer-5",scroll-y-off
ul(ng-class="appCtrl.showMainNav ? 'is-visible' : ''")
li.l-left 特性
- li.l-left 文档
+ li.l-left 文档
li.l-left 关于
li.l-left 贡献
li.l-left 支持
li.l-left 新闻
li.l-left 事件
- li.l-right.feedback-button feedback
+ li.l-right.feedback-button feedback
diff --git a/public/_includes/_version-dropdown.jade b/public/_includes/_version-dropdown.jade
index 07d2bc6558..0525aa4fee 100644
--- a/public/_includes/_version-dropdown.jade
+++ b/public/_includes/_version-dropdown.jade
@@ -64,7 +64,7 @@ if language == 'dart'
-nav.hero-subtitle.text-subhead.dropdown
+nav.dropdown
button(aria-label="选择Angular版本" md-button class="dropdown-button" ng-click="appCtrl.toggleVersionMenu($event)") #{title}
div(class="overlay ng-hide" ng-click="appCtrl.toggleVersionMenu($event)" ng-show="appCtrl.showMenu")
diff --git a/public/docs/_examples/.gitignore b/public/docs/_examples/.gitignore
index a574567c52..07614a46ec 100644
--- a/public/docs/_examples/.gitignore
+++ b/public/docs/_examples/.gitignore
@@ -1,13 +1,21 @@
.editorconfig
+.idea
+.vscode
styles.css
typings
typings.json
+node_modules
+jspm_packages
*.js.map
package.json
karma.conf.js
karma-test-shim.js
tsconfig.json
tslint.json
+wallaby.js
npm-debug*.
-**/protractor.config.js
+protractor.config.js
_test-output
+_temp
+
+!**/*e2e-spec.js
diff --git a/public/docs/_examples/displaying-data/e2e-spec.js b/public/docs/_examples/displaying-data/e2e-spec.js
index 4768050501..0f01977bb1 100644
--- a/public/docs/_examples/displaying-data/e2e-spec.js
+++ b/public/docs/_examples/displaying-data/e2e-spec.js
@@ -1,5 +1,4 @@
describe('Displaying Data Tests', function () {
-
var _title = "Tour of Heroes";
var _defaultHero = 'Windstorm'
@@ -15,7 +14,12 @@ describe('Displaying Data Tests', function () {
expect(element(by.css('h2')).getText()).toContain(_defaultHero);
});
- it('should have many heroes', function () {
+ it('should have heroes', function () {
+ var heroEls = element.all(by.css('li'));
+ expect(heroEls.count()).not.toBe(0, 'should have heroes');
+ });
+
+ it('should display "there are many heroes!"', function () {
expect(element(by.css('ul ~ p')).getText()).toContain('There are many heroes!');
});
});
diff --git a/public/docs/_examples/displaying-data/ts/app/app.component.ts b/public/docs/_examples/displaying-data/ts/app/app.component.ts
index a85a09081e..ace957d9b4 100644
--- a/public/docs/_examples/displaying-data/ts/app/app.component.ts
+++ b/public/docs/_examples/displaying-data/ts/app/app.component.ts
@@ -3,7 +3,7 @@
// #docregion imports
import {Component} from 'angular2/core';
// #enddocregion imports
-import {Hero} from './hero'
+import {Hero} from './hero';
@Component({
selector: 'my-app',
diff --git a/public/docs/_examples/hierarchical-dependency-injection/dart/lib/hero.dart b/public/docs/_examples/hierarchical-dependency-injection/dart/lib/hero.dart
index d62e5548da..273bb21aba 100644
--- a/public/docs/_examples/hierarchical-dependency-injection/dart/lib/hero.dart
+++ b/public/docs/_examples/hierarchical-dependency-injection/dart/lib/hero.dart
@@ -1,6 +1,4 @@
// #docregion
-import 'package:angular2/core.dart';
-
class Hero {
String name;
String power;
diff --git a/public/docs/_examples/hierarchical-dependency-injection/dart/lib/restore_service.dart b/public/docs/_examples/hierarchical-dependency-injection/dart/lib/restore_service.dart
index 8ddb820f05..50f4ce9bdb 100644
--- a/public/docs/_examples/hierarchical-dependency-injection/dart/lib/restore_service.dart
+++ b/public/docs/_examples/hierarchical-dependency-injection/dart/lib/restore_service.dart
@@ -7,7 +7,6 @@ class RestoreService {
T _currentItem;
setItem(T item) {
- print(item.runtimeType);
_originalItem = item;
_currentItem = clone(item);
}
diff --git a/public/docs/_examples/karma-test-shim.js b/public/docs/_examples/karma-test-shim.js
index 932744bc69..e2aff07852 100644
--- a/public/docs/_examples/karma-test-shim.js
+++ b/public/docs/_examples/karma-test-shim.js
@@ -41,30 +41,29 @@ System.config({ packages: packages });
// Configure Angular for the browser and
// with test versions of the platform providers
-System.import('angular2/testing')
- .then(function (testing) {
- return System.import('angular2/platform/testing/browser')
- .then(function (providers) {
- testing.setBaseTestProviders(
- providers.TEST_BROWSER_PLATFORM_PROVIDERS,
- providers.TEST_BROWSER_APPLICATION_PROVIDERS
- );
- });
+ Promise.all([
+ System.import('angular2/testing'),
+ System.import('angular2/platform/testing/browser')
+ ])
+ .then(function (results) {
+ var testing = results[0];
+ var browser = results[1];
+ testing.setBaseTestProviders(
+ browser.TEST_BROWSER_PLATFORM_PROVIDERS,
+ browser.TEST_BROWSER_APPLICATION_PROVIDERS);
+
+ // Load all spec files
+ // (e.g. 'base/app/hero.service.spec.js')
+ return Promise.all(
+ Object.keys(window.__karma__.files)
+ .filter(onlySpecFiles)
+ .map(function (moduleName) {
+ moduleNames.push(moduleName);
+ return System.import(moduleName);
+ }));
})
-// Load all spec files
-// (e.g. 'base/app/hero.service.spec.js')
-.then(function () {
- return Promise.all(
- Object.keys(window.__karma__.files)
- .filter(onlySpecFiles)
- .map(function (moduleName) {
- moduleNames.push(moduleName);
- return System.import(moduleName);
- }));
-})
-
-.then(success, fail);
+ .then(success, fail);
////// Helpers //////
diff --git a/public/docs/_examples/karma.conf.js b/public/docs/_examples/karma.conf.js
index e128ca2fb3..0b36ed93c5 100644
--- a/public/docs/_examples/karma.conf.js
+++ b/public/docs/_examples/karma.conf.js
@@ -1,7 +1,7 @@
module.exports = function(config) {
- var appBase = 'app/'; // transpiled app JS files
- var appAssets ='base/app/'; // component assets fetched by Angular's compiler
+ var appBase = 'app/'; // transpiled app JS files
+ var appAssets ='/base/app/'; // component assets fetched by Angular's compiler
config.set({
basePath: '',
diff --git a/public/docs/_examples/lifecycle-hooks/ts/app/on-changes.component.ts b/public/docs/_examples/lifecycle-hooks/ts/app/on-changes.component.ts
index dd6c7bddbe..6fdae1269b 100644
--- a/public/docs/_examples/lifecycle-hooks/ts/app/on-changes.component.ts
+++ b/public/docs/_examples/lifecycle-hooks/ts/app/on-changes.component.ts
@@ -1,3 +1,4 @@
+/* tslint:disable:forin */
// #docregion
import {
Component, Input, ViewChild,
@@ -6,7 +7,7 @@ import {
class Hero {
- constructor(public name:string){}
+ constructor(public name: string) {}
}
@Component({
@@ -30,13 +31,13 @@ export class OnChangesComponent implements OnChanges {
@Input() power: string;
// #enddocregion inputs
- changeLog:string[] = [];
+ changeLog: string[] = [];
// #docregion ng-on-changes
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
for (let propName in changes) {
let prop = changes[propName];
- let cur = JSON.stringify(prop.currentValue)
+ let cur = JSON.stringify(prop.currentValue);
let prev = JSON.stringify(prop.previousValue);
this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
}
@@ -50,13 +51,13 @@ export class OnChangesComponent implements OnChanges {
@Component({
selector: 'on-changes-parent',
- templateUrl:'app/on-changes-parent.component.html',
+ templateUrl: 'app/on-changes-parent.component.html',
styles: ['.parent {background: Lavender;}'],
directives: [OnChangesComponent]
})
export class OnChangesParentComponent {
- hero:Hero;
- power:string;
+ hero: Hero;
+ power: string;
title = 'OnChanges';
@ViewChild(OnChangesComponent) childView:OnChangesComponent;
@@ -69,6 +70,6 @@ export class OnChangesParentComponent {
this.hero = new Hero('Windstorm');
// setting power only triggers onChanges if this value is different
this.power = 'sing';
- this.childView && this.childView.reset();
+ if (this.childView) { this.childView.reset(); }
}
}
diff --git a/public/docs/_examples/package.json b/public/docs/_examples/package.json
index 4a8db4dcb8..fd16231de6 100644
--- a/public/docs/_examples/package.json
+++ b/public/docs/_examples/package.json
@@ -2,19 +2,18 @@
"name": "angular2-examples-master",
"version": "1.0.0",
"description": "Master package.json, the superset of all dependencies for all of the _example package.json files.",
- "main": "index.js",
"scripts": {
"start": "tsc && concurrently \"tsc -w\" \"lite-server\" ",
- "tsc": "tsc",
- "tsc:w": "tsc -w",
- "lite": "lite-server",
- "live": "live-server",
- "test": "tsc && concurrently \"tsc -w\" \"karma start karma.conf.js\"",
- "build-and-test": "npm run tsc && npm run test",
+ "e2e": "tsc && concurrently \"http-server\" \"protractor protractor.config.js\"",
"http-server": "tsc && http-server",
"http-server:e2e": "http-server",
+ "lite": "lite-server",
+ "postinstall": "typings install",
+ "test": "tsc && concurrently \"tsc -w\" \"karma start karma.conf.js\"",
+ "tsc": "tsc",
+ "tsc:w": "tsc -w",
"typings": "typings",
- "postinstall": "typings install"
+ "webdriver:update": "webdriver-manager update"
},
"keywords": [],
"author": "",
@@ -37,6 +36,7 @@
"typescript": "^1.8.10",
"typings":"^0.7.12",
+ "canonical-path": "0.0.2",
"http-server": "^0.9.0",
"jasmine-core": "~2.4.1",
"karma": "^0.13.22",
@@ -44,7 +44,6 @@
"karma-cli": "^0.1.2",
"karma-htmlfile-reporter": "^0.2.2",
"karma-jasmine": "^0.3.8",
- "live-server": "^0.9.2",
"protractor": "^3.2.2",
"rimraf": "^2.5.2"
},
diff --git a/public/docs/_examples/protractor.config.js b/public/docs/_examples/protractor.config.js
index 47484d7028..a2198b2b7d 100644
--- a/public/docs/_examples/protractor.config.js
+++ b/public/docs/_examples/protractor.config.js
@@ -1,9 +1,14 @@
-// TO RUN THE TESTS
-//
-// The first time, run:
+// FIRST TIME ONLY- run:
// ./node_modules/.bin/webdriver-manager update
-// Make sure the test server is running. Then do.
-// ./node_modules/.bin/protractor protractor.config.js
+//
+// Try: `npm run webdriver:update`
+//
+// AND THEN EVERYTIME ...
+// 1. Compile with `tsc`
+// 2. Make sure the test server (e.g., http-server: localhost:8080) is running.
+// 3. ./node_modules/.bin/protractor protractor.config.js
+//
+// To do all steps, try: `npm run e2e`
var fs = require('fs');
var path = require('canonical-path');
@@ -92,17 +97,13 @@ function itIf(cond, name, func) {
}
}
-// Hack - because of bug with send keys
+// Hack - because of bug with protractor send keys
function sendKeys(element, str) {
return str.split('').reduce(function (promise, char) {
- return promise.then(function () {
- return element.sendKeys(char);
- });
+ return promise.resolve(element.sendKeys(char));
}, element.getAttribute('value'));
- // better to create a resolved promise here but ... don't know how with protractor;
}
-
function Reporter(options) {
var _defaultOutputFile = path.resolve(process.cwd(), "../../", 'protractor-results.txt');
options.outputFile = options.outputFile || _defaultOutputFile;
diff --git a/public/docs/_examples/quickstart/ts/package.1.json b/public/docs/_examples/quickstart/ts/package.1.json
index bff084cb9b..cd26500603 100644
--- a/public/docs/_examples/quickstart/ts/package.1.json
+++ b/public/docs/_examples/quickstart/ts/package.1.json
@@ -3,11 +3,11 @@
"version": "1.0.0",
"scripts": {
"start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",
+ "lite": "lite-server",
+ "postinstall": "typings install",
"tsc": "tsc",
"tsc:w": "tsc -w",
- "lite": "lite-server",
- "typings": "typings",
- "postinstall": "typings install"
+ "typings": "typings"
},
"license": "ISC",
"dependencies": {
diff --git a/public/docs/_examples/testing/ts/app/app.component.spec.ts b/public/docs/_examples/testing/ts/app/app.component.spec.ts
new file mode 100644
index 0000000000..caa77ad66e
--- /dev/null
+++ b/public/docs/_examples/testing/ts/app/app.component.spec.ts
@@ -0,0 +1,84 @@
+/* tslint:disable:no-unused-variable */
+import { AppComponent } from './app.component';
+
+import { By } from 'angular2/platform/browser';
+import { provide } from 'angular2/core';
+
+import {
+ beforeEach, beforeEachProviders,
+ describe, ddescribe, xdescribe,
+ expect, it, iit, xit,
+ inject, injectAsync,
+ ComponentFixture, TestComponentBuilder
+} from 'angular2/testing';
+
+import { Hero, HeroService, MockHeroService } from './mock-hero.service';
+
+import { Router, MockRouter,
+ RouterLink, MockRouterLink,
+ RouterOutlet, MockRouterOutlet} from './mock-router';
+
+describe('AppComponent', () => {
+ let fixture: ComponentFixture;
+ let comp: AppComponent;
+
+ beforeEach(injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+ return tcb
+ .overrideDirective(AppComponent, RouterLink, MockRouterLink)
+ .overrideDirective(AppComponent, RouterOutlet, MockRouterOutlet)
+ .overrideProviders(AppComponent, [
+ provide(HeroService, {useClass: MockHeroService}),
+ provide(Router, {useClass: MockRouter}),
+ ])
+ .createAsync(AppComponent)
+ .then(fix => {
+ fixture = fix;
+ comp = fixture.debugElement.componentInstance;
+ });
+ }));
+
+ it('can instantiate it', () => {
+ expect(comp).not.toBeNull();
+ });
+
+ it('can get title from template', () => {
+ fixture.detectChanges();
+ let titleEl = fixture.debugElement.query(By.css('h1')).nativeElement;
+ expect(titleEl).toHaveText(comp.title);
+ });
+
+ it('can get RouterLinks from template', () => {
+ fixture.detectChanges();
+
+ let links = fixture.debugElement
+ .queryAll(function (de) { return de.componentInstance instanceof MockRouterLink; })
+ .map(de => de.componentInstance);
+
+ expect(links.length).toEqual(2, 'should have 2 links');
+ expect(links[0].routeParams[0]).toEqual('Dashboard', '1st link should go to Dashboard');
+ expect(links[1].routeParams[0]).toEqual('Heroes', '1st link should go to Heroes');
+
+ let result = links[1].onClick();
+ expect(result).toEqual(false, 'click should prevent default browser behavior');
+ });
+
+ it('can click Heroes link in template', () => {
+ fixture.detectChanges();
+
+ // Heroes RouterLink DebugElement
+ let heroesDe = fixture.debugElement
+ .queryAll(function (de) { return de.componentInstance instanceof MockRouterLink; })[1];
+
+ expect(heroesDe).not.toBeNull('should 2nd link');
+
+ let link = heroesDe.componentInstance;
+ expect(link.navigatedTo).toBeNull('link should not have navigate yet');
+
+ heroesDe.triggerEventHandler('click', null);
+
+ fixture.detectChanges();
+ expect(link.navigatedTo[0]).toEqual('Heroes');
+
+ });
+});
+
diff --git a/public/docs/_examples/testing/ts/app/app.component.ts b/public/docs/_examples/testing/ts/app/app.component.ts
index df9985b5ee..9210f80650 100644
--- a/public/docs/_examples/testing/ts/app/app.component.ts
+++ b/public/docs/_examples/testing/ts/app/app.component.ts
@@ -1,18 +1,20 @@
// #docplaster
// #docregion
import { Component } from 'angular2/core';
-import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router';
-import { HeroService } from './hero.service';
-import { DashboardComponent } from './dashboard.component';
-import { HeroesComponent } from './heroes.component';
-// #docregion hero-detail-import
+// Can't test with ROUTER_DIRECTIVES yet
+// import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router';
+
+import { RouteConfig, RouterLink,
+ RouterOutlet, ROUTER_PROVIDERS } from 'angular2/router';
+
+import { DashboardComponent } from './dashboard.component';
+import { HeroesComponent } from './heroes.component';
import { HeroDetailComponent } from './hero-detail.component';
-// #enddocregion hero-detail-import
+import { HeroService } from './hero.service';
@Component({
selector: 'my-app',
-// #docregion template
template: `
{{title}}
`,
-// #enddocregion template
-// #docregion style-urls
styleUrls: ['app/app.component.css'],
-// #enddocregion style-urls
- directives: [ROUTER_DIRECTIVES],
+ directives: [RouterLink, RouterOutlet],
providers: [
ROUTER_PROVIDERS,
HeroService
]
})
@RouteConfig([
-// #docregion dashboard-route
- {
- path: '/dashboard',
- name: 'Dashboard',
- component: DashboardComponent,
- useAsDefault: true
- },
-// #enddocregion dashboard-route
-// #docregion hero-detail-route
- {
- path: '/detail/:id',
- name: 'HeroDetail',
- component: HeroDetailComponent
- },
-// #enddocregion hero-detail-route
- {
- path: '/heroes',
- name: 'Heroes',
- component: HeroesComponent
- }
+ { path: '/dashboard', name: 'Dashboard', component: DashboardComponent, useAsDefault: true },
+ { path: '/detail/:id', name: 'HeroDetail', component: HeroDetailComponent },
+ { path: '/heroes', name: 'Heroes', component: HeroesComponent }
])
export class AppComponent {
title = 'Tour of Heroes';
}
-// #enddocregion
diff --git a/public/docs/_examples/testing/ts/app/public-external-template.html b/public/docs/_examples/testing/ts/app/bag-external-template.html
similarity index 100%
rename from public/docs/_examples/testing/ts/app/public-external-template.html
rename to public/docs/_examples/testing/ts/app/bag-external-template.html
diff --git a/public/docs/_examples/testing/ts/app/public.spec.ts b/public/docs/_examples/testing/ts/app/bag.spec.ts
similarity index 66%
rename from public/docs/_examples/testing/ts/app/public.spec.ts
rename to public/docs/_examples/testing/ts/app/bag.spec.ts
index 1ccd8c89f4..f77e2c9703 100644
--- a/public/docs/_examples/testing/ts/app/public.spec.ts
+++ b/public/docs/_examples/testing/ts/app/bag.spec.ts
@@ -5,28 +5,22 @@ import {
ChildChildComp, ChildComp, ChildWithChildComp,
ExternalTemplateComp,
FancyService, MockFancyService,
- MyIfComp,
+ InputComp,
+ MyIfComp, MyIfChildComp, MyIfParentComp,
MockChildComp, MockChildChildComp,
ParentComp,
TestProvidersComp, TestViewProvidersComp
-} from './public';
+} from './bag';
+
+import { DebugElement } from 'angular2/core';
+import { By } from 'angular2/platform/browser';
import {
- it,
- iit,
- xit,
- describe,
- ddescribe,
- xdescribe,
- expect,
- fakeAsync,
- tick,
- beforeEach,
- inject,
- injectAsync,
- withProviders,
- beforeEachProviders,
- TestComponentBuilder
+ beforeEach, beforeEachProviders, withProviders,
+ describe, ddescribe, xdescribe,
+ expect, it, iit, xit,
+ inject, injectAsync, fakeAsync, tick,
+ ComponentFixture, TestComponentBuilder
} from 'angular2/testing';
import { provide } from 'angular2/core';
@@ -49,13 +43,13 @@ describe('angular2 jasmine matchers', () => {
describe('toHaveCssClass', () => {
it('should assert that the CSS class is present', () => {
let el = document.createElement('div');
- el.classList.add('matias');
- expect(el).toHaveCssClass('matias');
+ el.classList.add('bombasto');
+ expect(el).toHaveCssClass('bombasto');
});
it('should assert that the CSS class is not present', () => {
let el = document.createElement('div');
- el.classList.add('matias');
+ el.classList.add('bombasto');
expect(el).not.toHaveCssClass('fatias');
});
});
@@ -187,13 +181,39 @@ describe('test component builder', function() {
let comp = fixture.componentInstance;
expect(comp.wasClicked).toEqual(false, 'wasClicked should be false at start');
- let btn = fixture.debugElement.query(el => el.name === 'button');
+ let btn = fixture.debugElement.query(By.css('button'));
+ // let btn = fixture.debugElement.query(el => el.name === 'button'); // the hard way
+
btn.triggerEventHandler('click', null);
- // btn.nativeElement.click(); // this works too; which is "better"?
+ // btn.nativeElement.click(); // this often works too ... but not all the time!
expect(comp.wasClicked).toEqual(true, 'wasClicked should be true after click');
});
}));
+ it('should support entering text in input box (ngModel)',
+ injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+ let origName = 'John';
+ let newName = 'Sally';
+
+ return tcb.createAsync(InputComp).then(fixture => {
+
+ let comp = fixture.componentInstance;
+ expect(comp.name).toEqual(origName, `At start name should be ${origName} `);
+
+ let inputBox = fixture.debugElement.query(By.css('input')).nativeElement;
+ fixture.detectChanges();
+ expect(inputBox.value).toEqual(origName, `At start input box value should be ${origName} `);
+
+ inputBox.value = newName;
+ expect(comp.name).toEqual(origName,
+ `Name should still be ${origName} after value change, before detectChanges`);
+
+ fixture.detectChanges();
+ expect(inputBox.value).toEqual(newName,
+ `After value change and detectChanges, name should now be ${newName} `);
+ });
+ }));
+
it('should override a template',
injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
@@ -220,7 +240,7 @@ describe('test component builder', function() {
});
}));
- it('should override component dependencies',
+ it('should override component directives',
injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
return tcb.overrideDirective(ParentComp, ChildComp, MockChildComp)
@@ -233,7 +253,7 @@ describe('test component builder', function() {
}));
- it('should override child component\'s dependencies',
+ it('should override child component\'s directives',
injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
return tcb.overrideDirective(ParentComp, ChildComp, ChildWithChildComp)
@@ -262,7 +282,6 @@ describe('test component builder', function() {
});
}));
-
it('should override a viewProvider',
injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
@@ -288,16 +307,158 @@ describe('test component builder', function() {
.toHaveText('from external template\n');
});
}), 10000); // Long timeout because this test makes an actual XHR.
+
+ describe('(lifecycle hooks w/ MyIfParentComp)', () => {
+ let fixture: ComponentFixture;
+ let parent: MyIfParentComp;
+ let child: MyIfChildComp;
+
+ /**
+ * Get the MyIfChildComp from parent; fail w/ good message if cannot.
+ */
+ function getChild() {
+
+ let childDe: DebugElement; // DebugElement that should hold the MyIfChildComp
+
+ // The Hard Way: requires detailed knowledge of the parent template
+ try {
+ childDe = fixture.debugElement.children[4].children[0];
+ } catch (err) { /* we'll report the error */ }
+
+ // DebugElement.queryAll: if we wanted all of many instances:
+ childDe = fixture.debugElement
+ .queryAll(function (de) { return de.componentInstance instanceof MyIfChildComp; })[0];
+
+ // WE'LL USE THIS APPROACH !
+ // DebugElement.query: find first instance (if any)
+ childDe = fixture.debugElement
+ .query(function (de) { return de.componentInstance instanceof MyIfChildComp; });
+
+ if (childDe && childDe.componentInstance) {
+ child = childDe.componentInstance;
+ } else {
+ fail('Unable to find MyIfChildComp within MyIfParentComp');
+ }
+
+ return child;
+ }
+
+ // Create MyIfParentComp TCB and component instance before each test (async beforeEach)
+ beforeEach(injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+ return tcb.createAsync(MyIfParentComp)
+ .then(fix => {
+ fixture = fix;
+ parent = fixture.debugElement.componentInstance;
+ });
+ }));
+
+ it('should instantiate parent component', () => {
+ expect(parent).not.toBeNull('parent component should exist');
+ });
+
+ it('parent component OnInit should NOT be called before first detectChanges()', () => {
+ expect(parent.ngOnInitCalled).toEqual(false);
+ });
+
+ it('parent component OnInit should be called after first detectChanges()', () => {
+ fixture.detectChanges();
+ expect(parent.ngOnInitCalled).toEqual(true);
+ });
+
+ it('child component should exist after OnInit', () => {
+ fixture.detectChanges();
+ getChild();
+ expect(child instanceof MyIfChildComp).toEqual(true, 'should create child');
+ });
+
+ it('should have called child component\'s OnInit ', () => {
+ fixture.detectChanges();
+ getChild();
+ expect(child.ngOnInitCalled).toEqual(true);
+ });
+
+ it('child component called OnChanges once', () => {
+ fixture.detectChanges();
+ getChild();
+ expect(child.ngOnChangesCounter).toEqual(1);
+ });
+
+ it('changed parent value flows to child', () => {
+ fixture.detectChanges();
+ getChild();
+
+ parent.parentValue = 'foo';
+ fixture.detectChanges();
+
+ expect(child.ngOnChangesCounter).toEqual(2,
+ 'expected 2 changes: initial value and changed value');
+ expect(child.childValue).toEqual('foo',
+ 'childValue should eq changed parent value');
+ });
+
+ it('changed child value flows to parent', injectAsync([], () => {
+ fixture.detectChanges();
+ getChild();
+
+ child.childValue = 'bar';
+
+ let deferred = PromiseWrapper.completer();
+ let p = deferred.promise.then(() => {
+
+ fixture.detectChanges();
+
+ expect(child.ngOnChangesCounter).toEqual(2,
+ 'expected 2 changes: initial value and changed value');
+ expect(parent.parentValue).toEqual('bar',
+ 'parentValue should eq changed parent value');
+ });
+
+ // Wait one JS engine turn!
+ setTimeout(() => deferred.resolve(), 0);
+
+ return p;
+ }));
+
+/* Will soon be able to write it like this:
+ it('changed child value flows to parent', async(() => {
+ fixture.detectChanges();
+ getChild();
+
+ child.childValue = 'bar';
+
+ // Wait one JS engine turn!
+ setTimeout(() => {
+ fixture.detectChanges();
+ expect(child.ngOnChangesCounter).toEqual(2,
+ 'expected 2 changes: initial value and changed value');
+ expect(parent.parentValue).toEqual('bar',
+ 'parentValue should eq changed parent value');
+ }, 0);
+ }));
+*/
+
+ it('clicking "Close Child" triggers child OnDestroy', () => {
+ fixture.detectChanges();
+ getChild();
+
+ let btn = fixture.debugElement.query(By.css('button'));
+ btn.triggerEventHandler('click', null);
+
+ fixture.detectChanges();
+ expect(child.ngOnDestroyCalled).toEqual(true);
+ });
+
+ });
});
-describe('errors', () => {
+describe('inject/async testing errors', () => {
let originalJasmineIt: any;
let originalJasmineBeforeEach: any;
let patchJasmineIt = () => {
let deferred = PromiseWrapper.completer();
originalJasmineIt = jasmine.getEnv().it;
- jasmine.getEnv().it = (description: string, fn: Function) => {
+ jasmine.getEnv().it = (description: string, fn: Function): jasmine.Spec => {
let done = () => { deferred.resolve(); };
(done).fail = (err: any) => { deferred.reject(err); };
fn(done);
@@ -311,7 +472,7 @@ describe('errors', () => {
let patchJasmineBeforeEach = () => {
let deferred = PromiseWrapper.completer();
originalJasmineBeforeEach = jasmine.getEnv().beforeEach;
- jasmine.getEnv().beforeEach = (fn: any) => {
+ jasmine.getEnv().beforeEach = (fn: any): void => {
let done = () => { deferred.resolve(); };
(done).fail = (err: any) => { deferred.reject(err); };
fn(done);
@@ -456,3 +617,32 @@ describe('errors', () => {
});
});
});
+
+
+//////// Testing Framework Bugs? /////
+import { HeroService } from './hero.service';
+import { Component } from 'angular2/core';
+
+@Component({
+ selector: 'another-comp',
+ template: `AnotherProvidersComp()`,
+ providers: [FancyService] // <======= BOOM! if we comment out
+ // Failed: 'undefined' is not an object (evaluating 'dm.providers.concat')
+})
+export class AnotherProvidersComp {
+ constructor(
+ private _heroService: HeroService
+ ) { }
+}
+
+describe('tcb.overrideProviders', () => {
+ it('Component must have at least one provider else crash',
+ injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+
+ return tcb.overrideProviders(
+ AnotherProvidersComp,
+ [provide(HeroService, {useValue: {}})]
+ )
+ .createAsync(AnotherProvidersComp);
+ }));
+});
diff --git a/public/docs/_examples/testing/ts/app/public.ts b/public/docs/_examples/testing/ts/app/bag.ts
similarity index 50%
rename from public/docs/_examples/testing/ts/app/public.ts
rename to public/docs/_examples/testing/ts/app/bag.ts
index 382c2c700e..1b861d1223 100644
--- a/public/docs/_examples/testing/ts/app/public.ts
+++ b/public/docs/_examples/testing/ts/app/bag.ts
@@ -1,6 +1,7 @@
// Based on https://github.com/angular/angular/blob/master/modules/angular2/test/testing/testing_public_spec.ts
-import { Component, Injectable } from 'angular2/core';
-import { NgIf } from 'angular2/common';
+/* tslint:disable:forin */
+import { Component, EventEmitter, Injectable, Input, Output,
+ OnInit, OnChanges, OnDestroy, SimpleChange } from 'angular2/core';
// Let TypeScript know about the special SystemJS __moduleName variable
declare var __moduleName: string;
@@ -39,6 +40,13 @@ export class ButtonComp {
clicked() { this.wasClicked = true; }
}
+@Component({
+ selector: 'input-comp',
+ template: ``
+})
+export class InputComp {
+ name = 'John';
+}
@Component({
selector: 'child-comp',
@@ -66,14 +74,12 @@ export class ParentComp { }
@Component({
selector: 'my-if-comp',
- template: `MyIf(More)`,
- directives: [NgIf]
+ template: `MyIf(More)`
})
export class MyIfComp {
- showMore: boolean = false;
+ showMore = false;
}
-
@Component({
selector: 'child-child-comp',
template: 'ChildChild'
@@ -121,7 +127,7 @@ export class TestViewProvidersComp {
@Component({
moduleId: __moduleName,
selector: 'external-template-comp',
- templateUrl: 'public-external-template.html'
+ templateUrl: 'bag-external-template.html'
})
export class ExternalTemplateComp { }
@@ -131,3 +137,88 @@ export class ExternalTemplateComp { }
templateUrl: 'non-existant.html'
})
export class BadTemplateUrl { }
+
+
+///////// MyIfChildComp ////////
+@Component({
+ selector: 'my-if-child-comp',
+
+ template: `
+ MyIfChildComp
+
+
+
+ Change log:
+ {{i + 1}} - {{log}}
`
+})
+export class MyIfChildComp implements OnInit, OnChanges, OnDestroy {
+ @Input() value = '';
+ @Output() valueChange = new EventEmitter();
+
+ get childValue() { return this.value; }
+ set childValue(v: string) {
+ if (this.value === v) { return; }
+ this.value = v;
+ this.valueChange.emit(v);
+ }
+
+ changeLog: string[] = [];
+
+ ngOnInitCalled = false;
+ ngOnChangesCounter = 0;
+ ngOnDestroyCalled = false;
+
+ ngOnInit() {
+ this.ngOnInitCalled = true;
+ this.changeLog.push('ngOnInit called');
+ }
+
+ ngOnDestroy() {
+ this.ngOnDestroyCalled = true;
+ this.changeLog.push('ngOnDestroy called');
+ }
+
+ ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
+ for (let propName in changes) {
+ this.ngOnChangesCounter += 1;
+ let prop = changes[propName];
+ let cur = JSON.stringify(prop.currentValue);
+ let prev = JSON.stringify(prop.previousValue);
+ this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
+ }
+ }
+}
+
+///////// MyIfParentComp ////////
+
+@Component({
+ selector: 'my-if-parent-comp',
+ template: `
+ MyIfParentComp
+
+
+
+
+
+ `,
+ directives: [MyIfChildComp]
+})
+export class MyIfParentComp implements OnInit {
+ ngOnInitCalled = false;
+ parentValue = 'Hello, World';
+ showChild = false;
+ toggleLabel = 'Unknown';
+
+ ngOnInit() {
+ this.ngOnInitCalled = true;
+ this.clicked();
+ }
+
+ clicked() {
+ this.showChild = !this.showChild;
+ this.toggleLabel = this.showChild ? 'Close' : 'Show';
+ }
+}
diff --git a/public/docs/_examples/testing/ts/app/dashboard.component.spec.ts b/public/docs/_examples/testing/ts/app/dashboard.component.spec.ts
new file mode 100644
index 0000000000..134c2ae406
--- /dev/null
+++ b/public/docs/_examples/testing/ts/app/dashboard.component.spec.ts
@@ -0,0 +1,171 @@
+/* tslint:disable:no-unused-variable */
+import { DashboardComponent } from './dashboard.component';
+
+import { By } from 'angular2/platform/browser';
+import { provide } from 'angular2/core';
+
+import {
+ beforeEach, beforeEachProviders,
+ describe, ddescribe, xdescribe,
+ expect, it, iit, xit,
+ inject, injectAsync,
+ TestComponentBuilder
+} from 'angular2/testing';
+
+import { Hero, HeroService, MockHeroService } from './mock-hero.service';
+import { Router, MockRouter } from './mock-router';
+
+interface Done {
+ (): void;
+ fail: (err: any) => void;
+}
+
+describe('DashboardComponent', () => {
+
+ //////// WITHOUT ANGULAR INVOLVED ///////
+ describe('w/o Angular', () => {
+ let comp: DashboardComponent;
+ let mockHeroService: MockHeroService;
+ let router: MockRouter;
+
+ beforeEach(() => {
+ router = new MockRouter();
+ mockHeroService = new MockHeroService();
+ comp = new DashboardComponent(router, mockHeroService);
+ });
+
+ it('should NOT have heroes before calling OnInit', () => {
+ expect(comp.heroes.length).toEqual(0,
+ 'should not have heroes before OnInit');
+ });
+
+ it('should NOT have heroes immediately after OnInit', () => {
+ comp.ngOnInit(); // ngOnInit -> getHeroes
+ expect(comp.heroes.length).toEqual(0,
+ 'should not have heroes until service promise resolves');
+ });
+
+ it('should HAVE heroes after HeroService gets them', (done: Done) => {
+ comp.ngOnInit(); // ngOnInit -> getHeroes
+ mockHeroService.lastPromise // the one from getHeroes
+ .then(() => {
+ // throw new Error('deliberate error'); // see it fail gracefully
+ expect(comp.heroes.length).toBeGreaterThan(0,
+ 'should have heroes after service promise resolves');
+ })
+ .then(done, done.fail);
+ });
+
+ it('should tell ROUTER to navigate by hero id', () => {
+ let hero: Hero = {id: 42, name: 'Abbracadabra' };
+ let spy = spyOn(router, 'navigate').and.callThrough();
+
+ comp.gotoDetail(hero);
+
+ let linkParams = spy.calls.mostRecent().args[0];
+ expect(linkParams[0]).toEqual('HeroDetail', 'should nav to "HeroDetail"');
+ expect(linkParams[1].id).toEqual(hero.id, 'should nav to fake hero\'s id');
+ });
+
+ });
+
+
+ ////// WITH ANGULAR TEST INFRASTRUCTURE ///////
+ describe('using TCB', () => {
+ let comp: DashboardComponent;
+ let mockHeroService: MockHeroService;
+
+ beforeEachProviders(() => {
+ mockHeroService = new MockHeroService();
+ return [
+ provide(Router, {useClass: MockRouter}),
+ provide(MockRouter, {useExisting: Router}),
+ provide(HeroService, {useValue: mockHeroService})
+ ];
+ });
+
+ it('can instantiate it',
+ injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+ return tcb.createAsync(DashboardComponent);
+ }));
+
+ it('should NOT have heroes before OnInit',
+ injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+ return tcb.createAsync(DashboardComponent).then(fixture => {
+ comp = fixture.debugElement.componentInstance;
+
+ expect(comp.heroes.length).toEqual(0,
+ 'should not have heroes before OnInit');
+ });
+ }));
+
+ it('should NOT have heroes immediately after OnInit',
+ injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+ return tcb.createAsync(DashboardComponent).then(fixture => {
+ comp = fixture.debugElement.componentInstance;
+ fixture.detectChanges(); // runs initial lifecycle hooks
+
+ expect(comp.heroes.length).toEqual(0,
+ 'should not have heroes until service promise resolves');
+ });
+ }));
+
+ it('should HAVE heroes after HeroService gets them',
+ injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+
+ return tcb.createAsync(DashboardComponent).then(fixture => {
+ comp = fixture.debugElement.componentInstance;
+ fixture.detectChanges(); // runs ngOnInit -> getHeroes
+
+ return mockHeroService.lastPromise // the one from getHeroes
+ .then(() => {
+ expect(comp.heroes.length).toBeGreaterThan(0,
+ 'should have heroes after service promise resolves');
+ });
+
+ });
+ }));
+
+ it('should DISPLAY heroes after HeroService gets them',
+ injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+
+ return tcb.createAsync(DashboardComponent).then(fixture => {
+ comp = fixture.debugElement.componentInstance;
+ fixture.detectChanges(); // runs ngOnInit -> getHeroes
+
+ return mockHeroService.lastPromise // the one from getHeroes
+ .then(() => {
+
+ // Find and examine the displayed heroes
+ fixture.detectChanges(); // update bindings
+ let heroNames = fixture.debugElement.queryAll(By.css('h4'));
+
+ expect(heroNames.length).toEqual(4, 'should display 4 heroes');
+
+ // the 4th displayed hero should be the 5th mock hero
+ expect(heroNames[3].nativeElement)
+ .toHaveText(mockHeroService.mockHeroes[4].name);
+ });
+
+ });
+ }));
+
+ it('should tell ROUTER to navigate by hero id',
+ injectAsync([TestComponentBuilder, Router],
+ (tcb: TestComponentBuilder, router: MockRouter) => {
+
+ let spy = spyOn(router, 'navigate').and.callThrough();
+
+ return tcb.createAsync(DashboardComponent).then(fixture => {
+ let hero: Hero = {id: 42, name: 'Abbracadabra' };
+ comp = fixture.debugElement.componentInstance;
+ comp.gotoDetail(hero);
+
+ let linkParams = spy.calls.mostRecent().args[0];
+ expect(linkParams[0]).toEqual('HeroDetail', 'should nav to "HeroDetail"');
+ expect(linkParams[1].id).toEqual(hero.id, 'should nav to fake hero\'s id');
+
+ });
+ }));
+ });
+});
diff --git a/public/docs/_examples/testing/ts/app/dashboard.component.ts b/public/docs/_examples/testing/ts/app/dashboard.component.ts
index c4c516a864..95b7be50d2 100644
--- a/public/docs/_examples/testing/ts/app/dashboard.component.ts
+++ b/public/docs/_examples/testing/ts/app/dashboard.component.ts
@@ -31,7 +31,7 @@ export class DashboardComponent implements OnInit {
ngOnInit() {
this._heroService.getHeroes()
- .then(heroes => this.heroes = heroes.slice(1,5));
+ .then(heroes => this.heroes = heroes.slice(1, 5));
}
// #docregion goto-detail
@@ -41,4 +41,4 @@ export class DashboardComponent implements OnInit {
}
// #enddocregion goto-detail
}
-// #enddocregion
\ No newline at end of file
+// #enddocregion
diff --git a/public/docs/_examples/testing/ts/app/http-hero.service.spec.ts b/public/docs/_examples/testing/ts/app/http-hero.service.spec.ts
index f5d61b4955..7a80c5022a 100644
--- a/public/docs/_examples/testing/ts/app/http-hero.service.spec.ts
+++ b/public/docs/_examples/testing/ts/app/http-hero.service.spec.ts
@@ -1,19 +1,9 @@
/* tslint:disable:no-unused-variable */
import {
- it,
- iit,
- xit,
- describe,
- ddescribe,
- xdescribe,
- expect,
- fakeAsync,
- tick,
- beforeEach,
- inject,
- injectAsync,
- withProviders,
- beforeEachProviders
+ beforeEach, beforeEachProviders, withProviders,
+ describe, ddescribe, xdescribe,
+ expect, it, iit, xit,
+ inject, injectAsync, fakeAsync, TestComponentBuilder, tick
} from 'angular2/testing';
import { provide } from 'angular2/core';
@@ -23,17 +13,12 @@ import {
MockConnection } from 'angular2/src/http/backends/mock_backend';
import {
- BaseRequestOptions,
- ConnectionBackend,
- Request,
- RequestMethod,
- RequestOptions,
- Response,
- ResponseOptions,
- URLSearchParams,
- HTTP_PROVIDERS,
- XHRBackend,
- Http} from 'angular2/http';
+ Http, HTTP_PROVIDERS,
+ ConnectionBackend, XHRBackend,
+ Request, RequestMethod, BaseRequestOptions, RequestOptions,
+ Response, ResponseOptions,
+ URLSearchParams
+} from 'angular2/http';
// Add all operators to Observable
import 'rxjs/Rx';
@@ -45,10 +30,10 @@ import { HeroService } from './http-hero.service';
type HeroData = {id: string, name: string}
const makeHeroData = () => [
- { "id": "1", "name": "Windstorm" },
- { "id": "2", "name": "Bombasto" },
- { "id": "3", "name": "Magneta" },
- { "id": "4", "name": "Tornado" }
+ { id: '1', name: 'Windstorm' },
+ { id: '2', name: 'Bombasto' },
+ { id: '3', name: 'Magneta' },
+ { id: '4', name: 'Tornado' }
];
// HeroService expects response data like {data: {the-data}}
diff --git a/public/docs/_examples/testing/ts/app/main.ts b/public/docs/_examples/testing/ts/app/main.ts
index c469e18fd0..0b7adf17c7 100644
--- a/public/docs/_examples/testing/ts/app/main.ts
+++ b/public/docs/_examples/testing/ts/app/main.ts
@@ -1,4 +1,6 @@
-import { bootstrap } from 'angular2/platform/browser';
-import { AppComponent } from './app.component';
+import { bootstrap } from 'angular2/platform/browser';
+import { AppComponent } from './app.component';
+import { MyIfParentComp } from './bag';
-bootstrap(AppComponent);
\ No newline at end of file
+bootstrap(AppComponent);
+bootstrap(MyIfParentComp);
diff --git a/public/docs/_examples/testing/ts/app/mock-hero.service.ts b/public/docs/_examples/testing/ts/app/mock-hero.service.ts
new file mode 100644
index 0000000000..4fcd42118a
--- /dev/null
+++ b/public/docs/_examples/testing/ts/app/mock-hero.service.ts
@@ -0,0 +1,24 @@
+export { Hero } from './hero';
+export { HeroService } from './hero.service';
+
+import { HEROES } from './mock-heroes';
+import { Hero } from './hero';
+import { HeroService } from './hero.service';
+
+import { PromiseWrapper } from 'angular2/src/facade/promise';
+
+export class MockHeroService implements HeroService {
+
+ mockHeroes = HEROES.slice();
+ lastPromise: Promise; // so we can spy on promise calls
+
+ getHero(id: number) {
+ return this.lastPromise = PromiseWrapper.resolve(this.mockHeroes[0]);
+ }
+
+ getHeroes() {
+ return this.lastPromise = PromiseWrapper.resolve(this.mockHeroes);
+ }
+
+ getHeroesSlowly() { return this.getHeroes(); }
+}
diff --git a/public/docs/_examples/testing/ts/app/mock-heroes.ts b/public/docs/_examples/testing/ts/app/mock-heroes.ts
index cdcba35097..ddd36d7868 100644
--- a/public/docs/_examples/testing/ts/app/mock-heroes.ts
+++ b/public/docs/_examples/testing/ts/app/mock-heroes.ts
@@ -2,15 +2,15 @@
import { Hero } from './hero';
export var HEROES: Hero[] = [
- {"id": 11, "name": "Mr. Nice"},
- {"id": 12, "name": "Narco"},
- {"id": 13, "name": "Bombasto"},
- {"id": 14, "name": "Celeritas"},
- {"id": 15, "name": "Magneta"},
- {"id": 16, "name": "RubberMan"},
- {"id": 17, "name": "Dynama"},
- {"id": 18, "name": "Dr IQ"},
- {"id": 19, "name": "Magma"},
- {"id": 20, "name": "Tornado"}
+ {id: 11, name: 'Mr. Nice'},
+ {id: 12, name: 'Narco'},
+ {id: 13, name: 'Bombasto'},
+ {id: 14, name: 'Celeritas'},
+ {id: 15, name: 'Magneta'},
+ {id: 16, name: 'RubberMan'},
+ {id: 17, name: 'Dynama'},
+ {id: 18, name: 'Dr IQ'},
+ {id: 19, name: 'Magma'},
+ {id: 20, name: 'Tornado'}
];
-// #enddocregion
\ No newline at end of file
+// #enddocregion
diff --git a/public/docs/_examples/testing/ts/app/mock-router.ts b/public/docs/_examples/testing/ts/app/mock-router.ts
new file mode 100644
index 0000000000..3370ab0b4f
--- /dev/null
+++ b/public/docs/_examples/testing/ts/app/mock-router.ts
@@ -0,0 +1,219 @@
+export * from 'angular2/router';
+
+import { Directive, DynamicComponentLoader, ElementRef,
+ Injectable, Optional, Input } from 'angular2/core';
+
+import { PromiseWrapper } from 'angular2/src/facade/promise';
+import { isString} from 'angular2/src/facade/lang';
+
+import { ComponentInstruction, Instruction,
+ Router, RouterOutlet} from 'angular2/router';
+
+let _resolveToTrue = PromiseWrapper.resolve(true);
+
+const NOT_IMPLEMENTED = (what: string) => {
+ throw new Error (`"${what}" is not implemented`);
+};
+
+
+@Directive({
+ selector: '[routerLink]',
+ host: {
+ '(click)': 'onClick()',
+ '[attr.href]': 'visibleHref',
+ '[class.router-link-active]': 'isRouteActive'
+ }
+})
+export class MockRouterLink {
+
+ isRouteActive = false;
+ visibleHref: string; // the url displayed on the anchor element.
+
+ @Input('routerLink') routeParams: any[];
+ @Input() target: string;
+ navigatedTo: any[] = null;
+
+ constructor(public router: Router) { }
+
+ onClick() {
+ this.navigatedTo = null;
+
+ // If no target, or if target is _self, prevent default browser behavior
+ if (!isString(this.target) || this.target === '_self') {
+ this.navigatedTo = this.routeParams;
+ return false;
+ }
+ return true;
+ }
+}
+
+@Directive({selector: 'router-outlet'})
+export class MockRouterOutlet extends RouterOutlet {
+ name: string = null;
+
+ constructor(
+ _elementRef: ElementRef,
+ @Optional() _loader: DynamicComponentLoader,
+ _parentRouter: Router,
+ nameAttr: string) {
+ super(_elementRef, _loader, _parentRouter, nameAttr);
+ if (nameAttr) {
+ this.name = nameAttr;
+ }
+ }
+
+ /**
+ * Called by the Router to instantiate a new component during the commit phase of a navigation.
+ * This method in turn is responsible for calling the `routerOnActivate` hook of its child.
+ */
+ activate(nextInstruction: ComponentInstruction): Promise { NOT_IMPLEMENTED('activate'); return _resolveToTrue; }
+
+ /**
+ * Called by the {@link Router} during the commit phase of a navigation when an outlet
+ * reuses a component between different routes.
+ * This method in turn is responsible for calling the `routerOnReuse` hook of its child.
+ */
+ reuse(nextInstruction: ComponentInstruction): Promise { NOT_IMPLEMENTED('reuse'); return _resolveToTrue; }
+
+ /**
+ * Called by the {@link Router} when an outlet disposes of a component's contents.
+ * This method in turn is responsible for calling the `routerOnDeactivate` hook of its child.
+ */
+ deactivate(nextInstruction: ComponentInstruction): Promise { NOT_IMPLEMENTED('deactivate'); return _resolveToTrue; }
+
+ /**
+ * Called by the {@link Router} during recognition phase of a navigation.
+ *
+ * If this resolves to `false`, the given navigation is cancelled.
+ *
+ * This method delegates to the child component's `routerCanDeactivate` hook if it exists,
+ * and otherwise resolves to true.
+ */
+ routerCanDeactivate(nextInstruction: ComponentInstruction): Promise {
+ NOT_IMPLEMENTED('routerCanDeactivate'); return _resolveToTrue;
+ }
+
+ /**
+ * Called by the {@link Router} during recognition phase of a navigation.
+ *
+ * If the new child component has a different Type than the existing child component,
+ * this will resolve to `false`. You can't reuse an old component when the new component
+ * is of a different Type.
+ *
+ * Otherwise, this method delegates to the child component's `routerCanReuse` hook if it exists,
+ * or resolves to true if the hook is not present.
+ */
+ routerCanReuse(nextInstruction: ComponentInstruction): Promise { NOT_IMPLEMENTED('routerCanReuse'); return _resolveToTrue; }
+
+}
+
+@Injectable()
+export class MockRouter extends Router {
+
+ mockIsRouteActive = false;
+ mockRecognizedInstruction: Instruction;
+ outlet: RouterOutlet = null;
+
+ constructor() {
+ super(null, null, null, null);
+ }
+
+ auxRouter(hostComponent: any): Router { return new MockChildRouter(this, hostComponent); }
+ childRouter(hostComponent: any): Router { return new MockChildRouter(this, hostComponent); }
+
+ commit(instruction: Instruction, _skipLocationChange = false): Promise {
+ NOT_IMPLEMENTED('commit'); return _resolveToTrue;
+ }
+
+ deactivate(instruction: Instruction, _skipLocationChange = false): Promise {
+ NOT_IMPLEMENTED('deactivate'); return _resolveToTrue;
+ }
+
+ /**
+ * Generate an `Instruction` based on the provided Route Link DSL.
+ */
+ generate(linkParams: any[]): Instruction {
+ NOT_IMPLEMENTED('generate'); return null;
+ }
+
+ isRouteActive(instruction: Instruction): boolean { return this.mockIsRouteActive; }
+
+ /**
+ * Navigate based on the provided Route Link DSL. It's preferred to navigate with this method
+ * over `navigateByUrl`.
+ *
+ * ### Usage
+ *
+ * This method takes an array representing the Route Link DSL:
+ * ```
+ * ['./MyCmp', {param: 3}]
+ * ```
+ * See the {@link RouterLink} directive for more.
+ */
+ navigate(linkParams: any[]): Promise {
+ return PromiseWrapper.resolve(linkParams);
+ }
+
+ /**
+ * Navigate to a URL. Returns a promise that resolves when navigation is complete.
+ * It's preferred to navigate with `navigate` instead of this method, since URLs are more brittle.
+ *
+ * If the given URL begins with a `/`, router will navigate absolutely.
+ * If the given URL does not begin with `/`, the router will navigate relative to this component.
+ */
+ navigateByUrl(url: string, _skipLocationChange = false): Promise {
+ return PromiseWrapper.resolve(url);
+ }
+
+
+ /**
+ * Navigate via the provided instruction. Returns a promise that resolves when navigation is
+ * complete.
+ */
+ navigateByInstruction(instruction: Instruction, _skipLocationChange = false): Promise {
+ return PromiseWrapper.resolve(instruction);
+ }
+
+ /**
+ * Subscribe to URL updates from the router
+ */
+ subscribe(onNext: (v: any) => void, onError?: (v: any) => void) {
+ return {onNext, onError};
+ }
+
+ /**
+ * Given a URL, returns an instruction representing the component graph
+ */
+ recognize(url: string): Promise {
+ return PromiseWrapper.resolve(this.mockRecognizedInstruction);
+ }
+
+ registerPrimaryOutlet(outlet: RouterOutlet): Promise {
+ this.outlet = outlet;
+ return super.registerPrimaryOutlet(outlet);
+ }
+
+ unregisterPrimaryOutlet(outlet: RouterOutlet) {
+ super.unregisterPrimaryOutlet(outlet);
+ this.outlet = null;
+ }
+}
+
+class MockChildRouter extends MockRouter {
+ constructor(parent: MockRouter, hostComponent: any) {
+ super();
+ this.parent = parent;
+ }
+
+
+ navigateByUrl(url: string, _skipLocationChange = false): Promise {
+ // Delegate navigation to the root router
+ return this.parent.navigateByUrl(url, _skipLocationChange);
+ }
+
+ navigateByInstruction(instruction: Instruction, _skipLocationChange = false):
+ Promise {
+ // Delegate navigation to the root router
+ return this.parent.navigateByInstruction(instruction, _skipLocationChange);
+ }
+}
diff --git a/public/docs/_examples/testing/ts/app/my-uppercase.pipe.spec.ts b/public/docs/_examples/testing/ts/app/my-uppercase.pipe.spec.ts
index fc9952f6b2..731b2ed965 100644
--- a/public/docs/_examples/testing/ts/app/my-uppercase.pipe.spec.ts
+++ b/public/docs/_examples/testing/ts/app/my-uppercase.pipe.spec.ts
@@ -4,7 +4,7 @@
import { MyUppercasePipe } from './my-uppercase.pipe';
describe('MyUppercasePipe', () => {
- let pipe : MyUppercasePipe;
+ let pipe: MyUppercasePipe;
beforeEach(() => {
pipe = new MyUppercasePipe();
diff --git a/public/docs/_examples/testing/ts/index.html b/public/docs/_examples/testing/ts/index.html
index 0e91f05760..0f78ff7173 100644
--- a/public/docs/_examples/testing/ts/index.html
+++ b/public/docs/_examples/testing/ts/index.html
@@ -15,7 +15,6 @@
-
@@ -35,5 +34,7 @@
Loading...
+
+ Loading MyIfParentComp ...