docs(testing): more testing samples and infrastructure setup
added wallaby config; revised karma-test-shim
This commit is contained in:
parent
96137efd79
commit
e1862887ae
|
@ -76,7 +76,8 @@ var _exampleBoilerplateFiles = [
|
||||||
'styles.css',
|
'styles.css',
|
||||||
'tsconfig.json',
|
'tsconfig.json',
|
||||||
'tslint.json',
|
'tslint.json',
|
||||||
'typings.json'
|
'typings.json',
|
||||||
|
'wallaby.js'
|
||||||
];
|
];
|
||||||
|
|
||||||
var _exampleDartWebBoilerPlateFiles = ['styles.css'];
|
var _exampleDartWebBoilerPlateFiles = ['styles.css'];
|
||||||
|
|
|
@ -1,13 +1,21 @@
|
||||||
.editorconfig
|
.editorconfig
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
styles.css
|
styles.css
|
||||||
typings
|
typings
|
||||||
typings.json
|
typings.json
|
||||||
|
node_modules
|
||||||
|
jspm_packages
|
||||||
*.js.map
|
*.js.map
|
||||||
package.json
|
package.json
|
||||||
karma.conf.js
|
karma.conf.js
|
||||||
karma-test-shim.js
|
karma-test-shim.js
|
||||||
tsconfig.json
|
tsconfig.json
|
||||||
tslint.json
|
tslint.json
|
||||||
|
wallaby.js
|
||||||
npm-debug*.
|
npm-debug*.
|
||||||
**/protractor.config.js
|
protractor.config.js
|
||||||
_test-output
|
_test-output
|
||||||
|
_temp
|
||||||
|
|
||||||
|
!**/*e2e-spec.js
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
describe('Displaying Data Tests', function () {
|
describe('Displaying Data Tests', function () {
|
||||||
|
|
||||||
var _title = "Tour of Heroes";
|
var _title = "Tour of Heroes";
|
||||||
var _defaultHero = 'Windstorm'
|
var _defaultHero = 'Windstorm'
|
||||||
|
|
||||||
|
@ -15,7 +14,12 @@ describe('Displaying Data Tests', function () {
|
||||||
expect(element(by.css('h2')).getText()).toContain(_defaultHero);
|
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!');
|
expect(element(by.css('ul ~ p')).getText()).toContain('There are many heroes!');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// #docregion imports
|
// #docregion imports
|
||||||
import {Component} from 'angular2/core';
|
import {Component} from 'angular2/core';
|
||||||
// #enddocregion imports
|
// #enddocregion imports
|
||||||
import {Hero} from './hero'
|
import {Hero} from './hero';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-app',
|
selector: 'my-app',
|
||||||
|
|
|
@ -41,30 +41,29 @@ System.config({ packages: packages });
|
||||||
|
|
||||||
// Configure Angular for the browser and
|
// Configure Angular for the browser and
|
||||||
// with test versions of the platform providers
|
// with test versions of the platform providers
|
||||||
System.import('angular2/testing')
|
Promise.all([
|
||||||
.then(function (testing) {
|
System.import('angular2/testing'),
|
||||||
return System.import('angular2/platform/testing/browser')
|
System.import('angular2/platform/testing/browser')
|
||||||
.then(function (providers) {
|
])
|
||||||
testing.setBaseTestProviders(
|
.then(function (results) {
|
||||||
providers.TEST_BROWSER_PLATFORM_PROVIDERS,
|
var testing = results[0];
|
||||||
providers.TEST_BROWSER_APPLICATION_PROVIDERS
|
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
|
.then(success, fail);
|
||||||
// (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);
|
|
||||||
|
|
||||||
////// Helpers //////
|
////// Helpers //////
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module.exports = function(config) {
|
module.exports = function(config) {
|
||||||
|
|
||||||
var appBase = 'app/'; // transpiled app JS files
|
var appBase = 'app/'; // transpiled app JS files
|
||||||
var appAssets ='base/app/'; // component assets fetched by Angular's compiler
|
var appAssets ='/base/app/'; // component assets fetched by Angular's compiler
|
||||||
|
|
||||||
config.set({
|
config.set({
|
||||||
basePath: '',
|
basePath: '',
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* tslint:disable:forin */
|
||||||
// #docregion
|
// #docregion
|
||||||
import {
|
import {
|
||||||
Component, Input, ViewChild,
|
Component, Input, ViewChild,
|
||||||
|
@ -6,7 +7,7 @@ import {
|
||||||
|
|
||||||
|
|
||||||
class Hero {
|
class Hero {
|
||||||
constructor(public name:string){}
|
constructor(public name: string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -30,13 +31,13 @@ export class OnChangesComponent implements OnChanges {
|
||||||
@Input() power: string;
|
@Input() power: string;
|
||||||
// #enddocregion inputs
|
// #enddocregion inputs
|
||||||
|
|
||||||
changeLog:string[] = [];
|
changeLog: string[] = [];
|
||||||
|
|
||||||
// #docregion ng-on-changes
|
// #docregion ng-on-changes
|
||||||
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
|
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
|
||||||
for (let propName in changes) {
|
for (let propName in changes) {
|
||||||
let prop = changes[propName];
|
let prop = changes[propName];
|
||||||
let cur = JSON.stringify(prop.currentValue)
|
let cur = JSON.stringify(prop.currentValue);
|
||||||
let prev = JSON.stringify(prop.previousValue);
|
let prev = JSON.stringify(prop.previousValue);
|
||||||
this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
|
this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
|
||||||
}
|
}
|
||||||
|
@ -50,13 +51,13 @@ export class OnChangesComponent implements OnChanges {
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'on-changes-parent',
|
selector: 'on-changes-parent',
|
||||||
templateUrl:'app/on-changes-parent.component.html',
|
templateUrl: 'app/on-changes-parent.component.html',
|
||||||
styles: ['.parent {background: Lavender;}'],
|
styles: ['.parent {background: Lavender;}'],
|
||||||
directives: [OnChangesComponent]
|
directives: [OnChangesComponent]
|
||||||
})
|
})
|
||||||
export class OnChangesParentComponent {
|
export class OnChangesParentComponent {
|
||||||
hero:Hero;
|
hero: Hero;
|
||||||
power:string;
|
power: string;
|
||||||
title = 'OnChanges';
|
title = 'OnChanges';
|
||||||
@ViewChild(OnChangesComponent) childView:OnChangesComponent;
|
@ViewChild(OnChangesComponent) childView:OnChangesComponent;
|
||||||
|
|
||||||
|
@ -69,6 +70,6 @@ export class OnChangesParentComponent {
|
||||||
this.hero = new Hero('Windstorm');
|
this.hero = new Hero('Windstorm');
|
||||||
// setting power only triggers onChanges if this value is different
|
// setting power only triggers onChanges if this value is different
|
||||||
this.power = 'sing';
|
this.power = 'sing';
|
||||||
this.childView && this.childView.reset();
|
if (this.childView) { this.childView.reset(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,19 +2,18 @@
|
||||||
"name": "angular2-examples-master",
|
"name": "angular2-examples-master",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Master package.json, the superset of all dependencies for all of the _example package.json files.",
|
"description": "Master package.json, the superset of all dependencies for all of the _example package.json files.",
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "tsc && concurrently \"tsc -w\" \"lite-server\" ",
|
"start": "tsc && concurrently \"tsc -w\" \"lite-server\" ",
|
||||||
"tsc": "tsc",
|
"e2e": "tsc && concurrently \"http-server\" \"protractor protractor.config.js\"",
|
||||||
"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",
|
|
||||||
"http-server": "tsc && http-server",
|
"http-server": "tsc && http-server",
|
||||||
"http-server:e2e": "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",
|
"typings": "typings",
|
||||||
"postinstall": "typings install"
|
"webdriver:update": "webdriver-manager update"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
@ -37,6 +36,7 @@
|
||||||
"typescript": "^1.8.10",
|
"typescript": "^1.8.10",
|
||||||
"typings":"^0.7.12",
|
"typings":"^0.7.12",
|
||||||
|
|
||||||
|
"canonical-path": "0.0.2",
|
||||||
"http-server": "^0.9.0",
|
"http-server": "^0.9.0",
|
||||||
"jasmine-core": "~2.4.1",
|
"jasmine-core": "~2.4.1",
|
||||||
"karma": "^0.13.22",
|
"karma": "^0.13.22",
|
||||||
|
@ -44,7 +44,6 @@
|
||||||
"karma-cli": "^0.1.2",
|
"karma-cli": "^0.1.2",
|
||||||
"karma-htmlfile-reporter": "^0.2.2",
|
"karma-htmlfile-reporter": "^0.2.2",
|
||||||
"karma-jasmine": "^0.3.8",
|
"karma-jasmine": "^0.3.8",
|
||||||
"live-server": "^0.9.2",
|
|
||||||
"protractor": "^3.2.2",
|
"protractor": "^3.2.2",
|
||||||
"rimraf": "^2.5.2"
|
"rimraf": "^2.5.2"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
// TO RUN THE TESTS
|
// FIRST TIME ONLY- run:
|
||||||
//
|
|
||||||
// The first time, run:
|
|
||||||
// ./node_modules/.bin/webdriver-manager update
|
// ./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 fs = require('fs');
|
||||||
var path = require('canonical-path');
|
var path = require('canonical-path');
|
||||||
|
@ -93,17 +98,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) {
|
function sendKeys(element, str) {
|
||||||
return str.split('').reduce(function (promise, char) {
|
return str.split('').reduce(function (promise, char) {
|
||||||
return promise.then(function () {
|
return promise.resolve(element.sendKeys(char));
|
||||||
return element.sendKeys(char);
|
|
||||||
});
|
|
||||||
}, element.getAttribute('value'));
|
}, element.getAttribute('value'));
|
||||||
// better to create a resolved promise here but ... don't know how with protractor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function Reporter(options) {
|
function Reporter(options) {
|
||||||
var _defaultOutputFile = path.resolve(process.cwd(), "../../", 'protractor-results.txt');
|
var _defaultOutputFile = path.resolve(process.cwd(), "../../", 'protractor-results.txt');
|
||||||
options.outputFile = options.outputFile || _defaultOutputFile;
|
options.outputFile = options.outputFile || _defaultOutputFile;
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",
|
"start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",
|
||||||
|
"lite": "lite-server",
|
||||||
|
"postinstall": "typings install",
|
||||||
"tsc": "tsc",
|
"tsc": "tsc",
|
||||||
"tsc:w": "tsc -w",
|
"tsc:w": "tsc -w",
|
||||||
"lite": "lite-server",
|
"typings": "typings"
|
||||||
"typings": "typings",
|
|
||||||
"postinstall": "typings install"
|
|
||||||
},
|
},
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -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 => <MockRouterLink> 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 = <MockRouterLink> heroesDe.componentInstance;
|
||||||
|
expect(link.navigatedTo).toBeNull('link should not have navigate yet');
|
||||||
|
|
||||||
|
heroesDe.triggerEventHandler('click', null);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(link.navigatedTo[0]).toEqual('Heroes');
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from 'angular2/core';
|
import { Component } from 'angular2/core';
|
||||||
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router';
|
|
||||||
|
|
||||||
import { HeroService } from './hero.service';
|
// Can't test with ROUTER_DIRECTIVES yet
|
||||||
import { DashboardComponent } from './dashboard.component';
|
// import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router';
|
||||||
import { HeroesComponent } from './heroes.component';
|
|
||||||
// #docregion hero-detail-import
|
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';
|
import { HeroDetailComponent } from './hero-detail.component';
|
||||||
// #enddocregion hero-detail-import
|
import { HeroService } from './hero.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-app',
|
selector: 'my-app',
|
||||||
// #docregion template
|
|
||||||
template: `
|
template: `
|
||||||
<h1>{{title}}</h1>
|
<h1>{{title}}</h1>
|
||||||
<nav>
|
<nav>
|
||||||
|
@ -21,39 +23,18 @@ import { HeroDetailComponent } from './hero-detail.component';
|
||||||
</nav>
|
</nav>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
`,
|
`,
|
||||||
// #enddocregion template
|
|
||||||
// #docregion style-urls
|
|
||||||
styleUrls: ['app/app.component.css'],
|
styleUrls: ['app/app.component.css'],
|
||||||
// #enddocregion style-urls
|
directives: [RouterLink, RouterOutlet],
|
||||||
directives: [ROUTER_DIRECTIVES],
|
|
||||||
providers: [
|
providers: [
|
||||||
ROUTER_PROVIDERS,
|
ROUTER_PROVIDERS,
|
||||||
HeroService
|
HeroService
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@RouteConfig([
|
@RouteConfig([
|
||||||
// #docregion dashboard-route
|
{ path: '/dashboard', name: 'Dashboard', component: DashboardComponent, useAsDefault: true },
|
||||||
{
|
{ path: '/detail/:id', name: 'HeroDetail', component: HeroDetailComponent },
|
||||||
path: '/dashboard',
|
{ path: '/heroes', name: 'Heroes', component: HeroesComponent }
|
||||||
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
|
|
||||||
}
|
|
||||||
])
|
])
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'Tour of Heroes';
|
title = 'Tour of Heroes';
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
|
|
@ -5,28 +5,22 @@ import {
|
||||||
ChildChildComp, ChildComp, ChildWithChildComp,
|
ChildChildComp, ChildComp, ChildWithChildComp,
|
||||||
ExternalTemplateComp,
|
ExternalTemplateComp,
|
||||||
FancyService, MockFancyService,
|
FancyService, MockFancyService,
|
||||||
MyIfComp,
|
InputComp,
|
||||||
|
MyIfComp, MyIfChildComp, MyIfParentComp,
|
||||||
MockChildComp, MockChildChildComp,
|
MockChildComp, MockChildChildComp,
|
||||||
ParentComp,
|
ParentComp,
|
||||||
TestProvidersComp, TestViewProvidersComp
|
TestProvidersComp, TestViewProvidersComp
|
||||||
} from './public';
|
} from './bag';
|
||||||
|
|
||||||
|
import { DebugElement } from 'angular2/core';
|
||||||
|
import { By } from 'angular2/platform/browser';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
it,
|
beforeEach, beforeEachProviders, withProviders,
|
||||||
iit,
|
describe, ddescribe, xdescribe,
|
||||||
xit,
|
expect, it, iit, xit,
|
||||||
describe,
|
inject, injectAsync, fakeAsync, tick,
|
||||||
ddescribe,
|
ComponentFixture, TestComponentBuilder
|
||||||
xdescribe,
|
|
||||||
expect,
|
|
||||||
fakeAsync,
|
|
||||||
tick,
|
|
||||||
beforeEach,
|
|
||||||
inject,
|
|
||||||
injectAsync,
|
|
||||||
withProviders,
|
|
||||||
beforeEachProviders,
|
|
||||||
TestComponentBuilder
|
|
||||||
} from 'angular2/testing';
|
} from 'angular2/testing';
|
||||||
|
|
||||||
import { provide } from 'angular2/core';
|
import { provide } from 'angular2/core';
|
||||||
|
@ -49,13 +43,13 @@ describe('angular2 jasmine matchers', () => {
|
||||||
describe('toHaveCssClass', () => {
|
describe('toHaveCssClass', () => {
|
||||||
it('should assert that the CSS class is present', () => {
|
it('should assert that the CSS class is present', () => {
|
||||||
let el = document.createElement('div');
|
let el = document.createElement('div');
|
||||||
el.classList.add('matias');
|
el.classList.add('bombasto');
|
||||||
expect(el).toHaveCssClass('matias');
|
expect(el).toHaveCssClass('bombasto');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should assert that the CSS class is not present', () => {
|
it('should assert that the CSS class is not present', () => {
|
||||||
let el = document.createElement('div');
|
let el = document.createElement('div');
|
||||||
el.classList.add('matias');
|
el.classList.add('bombasto');
|
||||||
expect(el).not.toHaveCssClass('fatias');
|
expect(el).not.toHaveCssClass('fatias');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -187,13 +181,39 @@ describe('test component builder', function() {
|
||||||
let comp = <ButtonComp> fixture.componentInstance;
|
let comp = <ButtonComp> fixture.componentInstance;
|
||||||
expect(comp.wasClicked).toEqual(false, 'wasClicked should be false at start');
|
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.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');
|
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 = <InputComp> fixture.componentInstance;
|
||||||
|
expect(comp.name).toEqual(origName, `At start name should be ${origName} `);
|
||||||
|
|
||||||
|
let inputBox = <HTMLInputElement> 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',
|
it('should override a template',
|
||||||
injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
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) => {
|
injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
|
|
||||||
return tcb.overrideDirective(ParentComp, ChildComp, MockChildComp)
|
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) => {
|
injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
|
|
||||||
return tcb.overrideDirective(ParentComp, ChildComp, ChildWithChildComp)
|
return tcb.overrideDirective(ParentComp, ChildComp, ChildWithChildComp)
|
||||||
|
@ -262,7 +282,6 @@ describe('test component builder', function() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
it('should override a viewProvider',
|
it('should override a viewProvider',
|
||||||
injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
|
|
||||||
|
@ -288,16 +307,158 @@ describe('test component builder', function() {
|
||||||
.toHaveText('from external template\n');
|
.toHaveText('from external template\n');
|
||||||
});
|
});
|
||||||
}), 10000); // Long timeout because this test makes an actual XHR.
|
}), 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 originalJasmineIt: any;
|
||||||
let originalJasmineBeforeEach: any;
|
let originalJasmineBeforeEach: any;
|
||||||
|
|
||||||
let patchJasmineIt = () => {
|
let patchJasmineIt = () => {
|
||||||
let deferred = PromiseWrapper.completer();
|
let deferred = PromiseWrapper.completer();
|
||||||
originalJasmineIt = jasmine.getEnv().it;
|
originalJasmineIt = jasmine.getEnv().it;
|
||||||
jasmine.getEnv().it = (description: string, fn: Function) => {
|
jasmine.getEnv().it = (description: string, fn: Function): jasmine.Spec => {
|
||||||
let done = () => { deferred.resolve(); };
|
let done = () => { deferred.resolve(); };
|
||||||
(<any>done).fail = (err: any) => { deferred.reject(err); };
|
(<any>done).fail = (err: any) => { deferred.reject(err); };
|
||||||
fn(done);
|
fn(done);
|
||||||
|
@ -311,7 +472,7 @@ describe('errors', () => {
|
||||||
let patchJasmineBeforeEach = () => {
|
let patchJasmineBeforeEach = () => {
|
||||||
let deferred = PromiseWrapper.completer();
|
let deferred = PromiseWrapper.completer();
|
||||||
originalJasmineBeforeEach = jasmine.getEnv().beforeEach;
|
originalJasmineBeforeEach = jasmine.getEnv().beforeEach;
|
||||||
jasmine.getEnv().beforeEach = (fn: any) => {
|
jasmine.getEnv().beforeEach = (fn: any): void => {
|
||||||
let done = () => { deferred.resolve(); };
|
let done = () => { deferred.resolve(); };
|
||||||
(<any>done).fail = (err: any) => { deferred.reject(err); };
|
(<any>done).fail = (err: any) => { deferred.reject(err); };
|
||||||
fn(done);
|
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);
|
||||||
|
}));
|
||||||
|
});
|
|
@ -1,6 +1,7 @@
|
||||||
// Based on https://github.com/angular/angular/blob/master/modules/angular2/test/testing/testing_public_spec.ts
|
// Based on https://github.com/angular/angular/blob/master/modules/angular2/test/testing/testing_public_spec.ts
|
||||||
import { Component, Injectable } from 'angular2/core';
|
/* tslint:disable:forin */
|
||||||
import { NgIf } from 'angular2/common';
|
import { Component, EventEmitter, Injectable, Input, Output,
|
||||||
|
OnInit, OnChanges, OnDestroy, SimpleChange } from 'angular2/core';
|
||||||
|
|
||||||
// Let TypeScript know about the special SystemJS __moduleName variable
|
// Let TypeScript know about the special SystemJS __moduleName variable
|
||||||
declare var __moduleName: string;
|
declare var __moduleName: string;
|
||||||
|
@ -39,6 +40,13 @@ export class ButtonComp {
|
||||||
clicked() { this.wasClicked = true; }
|
clicked() { this.wasClicked = true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'input-comp',
|
||||||
|
template: `<input [(ngModel)]="name">`
|
||||||
|
})
|
||||||
|
export class InputComp {
|
||||||
|
name = 'John';
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'child-comp',
|
selector: 'child-comp',
|
||||||
|
@ -66,14 +74,12 @@ export class ParentComp { }
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-if-comp',
|
selector: 'my-if-comp',
|
||||||
template: `MyIf(<span *ngIf="showMore">More</span>)`,
|
template: `MyIf(<span *ngIf="showMore">More</span>)`
|
||||||
directives: [NgIf]
|
|
||||||
})
|
})
|
||||||
export class MyIfComp {
|
export class MyIfComp {
|
||||||
showMore: boolean = false;
|
showMore = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'child-child-comp',
|
selector: 'child-child-comp',
|
||||||
template: '<span>ChildChild</span>'
|
template: '<span>ChildChild</span>'
|
||||||
|
@ -121,7 +127,7 @@ export class TestViewProvidersComp {
|
||||||
@Component({
|
@Component({
|
||||||
moduleId: __moduleName,
|
moduleId: __moduleName,
|
||||||
selector: 'external-template-comp',
|
selector: 'external-template-comp',
|
||||||
templateUrl: 'public-external-template.html'
|
templateUrl: 'bag-external-template.html'
|
||||||
})
|
})
|
||||||
export class ExternalTemplateComp { }
|
export class ExternalTemplateComp { }
|
||||||
|
|
||||||
|
@ -131,3 +137,88 @@ export class ExternalTemplateComp { }
|
||||||
templateUrl: 'non-existant.html'
|
templateUrl: 'non-existant.html'
|
||||||
})
|
})
|
||||||
export class BadTemplateUrl { }
|
export class BadTemplateUrl { }
|
||||||
|
|
||||||
|
|
||||||
|
///////// MyIfChildComp ////////
|
||||||
|
@Component({
|
||||||
|
selector: 'my-if-child-comp',
|
||||||
|
|
||||||
|
template: `
|
||||||
|
<h4>MyIfChildComp</h4>
|
||||||
|
<div>
|
||||||
|
<label>Child value: <input [(ngModel)]="childValue"> </label>
|
||||||
|
</div>
|
||||||
|
<p><i>Change log:</i></p>
|
||||||
|
<div *ngFor="#log of changeLog; #i=index">{{i + 1}} - {{log}}</div>`
|
||||||
|
})
|
||||||
|
export class MyIfChildComp implements OnInit, OnChanges, OnDestroy {
|
||||||
|
@Input() value = '';
|
||||||
|
@Output() valueChange = new EventEmitter<string>();
|
||||||
|
|
||||||
|
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: `
|
||||||
|
<h3>MyIfParentComp</h3>
|
||||||
|
<label>Parent value:
|
||||||
|
<input [(ngModel)]="parentValue">
|
||||||
|
</label>
|
||||||
|
<button (click)='clicked()'>{{toggleLabel}} Child</button><br>
|
||||||
|
<div *ngIf="showChild"
|
||||||
|
style="margin: 4px; padding: 4px; background-color: aliceblue;">
|
||||||
|
<my-if-child-comp [(value)]="parentValue"></my-if-child-comp>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
}
|
|
@ -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');
|
||||||
|
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
|
@ -31,7 +31,7 @@ export class DashboardComponent implements OnInit {
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this._heroService.getHeroes()
|
this._heroService.getHeroes()
|
||||||
.then(heroes => this.heroes = heroes.slice(1,5));
|
.then(heroes => this.heroes = heroes.slice(1, 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
// #docregion goto-detail
|
// #docregion goto-detail
|
||||||
|
|
|
@ -1,19 +1,9 @@
|
||||||
/* tslint:disable:no-unused-variable */
|
/* tslint:disable:no-unused-variable */
|
||||||
import {
|
import {
|
||||||
it,
|
beforeEach, beforeEachProviders, withProviders,
|
||||||
iit,
|
describe, ddescribe, xdescribe,
|
||||||
xit,
|
expect, it, iit, xit,
|
||||||
describe,
|
inject, injectAsync, fakeAsync, TestComponentBuilder, tick
|
||||||
ddescribe,
|
|
||||||
xdescribe,
|
|
||||||
expect,
|
|
||||||
fakeAsync,
|
|
||||||
tick,
|
|
||||||
beforeEach,
|
|
||||||
inject,
|
|
||||||
injectAsync,
|
|
||||||
withProviders,
|
|
||||||
beforeEachProviders
|
|
||||||
} from 'angular2/testing';
|
} from 'angular2/testing';
|
||||||
|
|
||||||
import { provide } from 'angular2/core';
|
import { provide } from 'angular2/core';
|
||||||
|
@ -23,17 +13,12 @@ import {
|
||||||
MockConnection } from 'angular2/src/http/backends/mock_backend';
|
MockConnection } from 'angular2/src/http/backends/mock_backend';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BaseRequestOptions,
|
Http, HTTP_PROVIDERS,
|
||||||
ConnectionBackend,
|
ConnectionBackend, XHRBackend,
|
||||||
Request,
|
Request, RequestMethod, BaseRequestOptions, RequestOptions,
|
||||||
RequestMethod,
|
Response, ResponseOptions,
|
||||||
RequestOptions,
|
URLSearchParams
|
||||||
Response,
|
} from 'angular2/http';
|
||||||
ResponseOptions,
|
|
||||||
URLSearchParams,
|
|
||||||
HTTP_PROVIDERS,
|
|
||||||
XHRBackend,
|
|
||||||
Http} from 'angular2/http';
|
|
||||||
|
|
||||||
// Add all operators to Observable
|
// Add all operators to Observable
|
||||||
import 'rxjs/Rx';
|
import 'rxjs/Rx';
|
||||||
|
@ -45,10 +30,10 @@ import { HeroService } from './http-hero.service';
|
||||||
type HeroData = {id: string, name: string}
|
type HeroData = {id: string, name: string}
|
||||||
|
|
||||||
const makeHeroData = () => [
|
const makeHeroData = () => [
|
||||||
{ "id": "1", "name": "Windstorm" },
|
{ id: '1', name: 'Windstorm' },
|
||||||
{ "id": "2", "name": "Bombasto" },
|
{ id: '2', name: 'Bombasto' },
|
||||||
{ "id": "3", "name": "Magneta" },
|
{ id: '3', name: 'Magneta' },
|
||||||
{ "id": "4", "name": "Tornado" }
|
{ id: '4', name: 'Tornado' }
|
||||||
];
|
];
|
||||||
|
|
||||||
// HeroService expects response data like {data: {the-data}}
|
// HeroService expects response data like {data: {the-data}}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { bootstrap } from 'angular2/platform/browser';
|
import { bootstrap } from 'angular2/platform/browser';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
import { MyIfParentComp } from './bag';
|
||||||
|
|
||||||
bootstrap(AppComponent);
|
bootstrap(AppComponent);
|
||||||
|
bootstrap(MyIfParentComp);
|
||||||
|
|
|
@ -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<any>; // so we can spy on promise calls
|
||||||
|
|
||||||
|
getHero(id: number) {
|
||||||
|
return this.lastPromise = PromiseWrapper.resolve(this.mockHeroes[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeroes() {
|
||||||
|
return this.lastPromise = PromiseWrapper.resolve<Hero[]>(this.mockHeroes);
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeroesSlowly() { return this.getHeroes(); }
|
||||||
|
}
|
|
@ -2,15 +2,15 @@
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
|
|
||||||
export var HEROES: Hero[] = [
|
export var HEROES: Hero[] = [
|
||||||
{"id": 11, "name": "Mr. Nice"},
|
{id: 11, name: 'Mr. Nice'},
|
||||||
{"id": 12, "name": "Narco"},
|
{id: 12, name: 'Narco'},
|
||||||
{"id": 13, "name": "Bombasto"},
|
{id: 13, name: 'Bombasto'},
|
||||||
{"id": 14, "name": "Celeritas"},
|
{id: 14, name: 'Celeritas'},
|
||||||
{"id": 15, "name": "Magneta"},
|
{id: 15, name: 'Magneta'},
|
||||||
{"id": 16, "name": "RubberMan"},
|
{id: 16, name: 'RubberMan'},
|
||||||
{"id": 17, "name": "Dynama"},
|
{id: 17, name: 'Dynama'},
|
||||||
{"id": 18, "name": "Dr IQ"},
|
{id: 18, name: 'Dr IQ'},
|
||||||
{"id": 19, "name": "Magma"},
|
{id: 19, name: 'Magma'},
|
||||||
{"id": 20, "name": "Tornado"}
|
{id: 20, name: 'Tornado'}
|
||||||
];
|
];
|
||||||
// #enddocregion
|
// #enddocregion
|
|
@ -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<any> { 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<any> { 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<any> { 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<any> {
|
||||||
|
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<any> { 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<any> {
|
||||||
|
NOT_IMPLEMENTED('commit'); return _resolveToTrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
deactivate(instruction: Instruction, _skipLocationChange = false): Promise<any> {
|
||||||
|
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<any> {
|
||||||
|
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<any> {
|
||||||
|
return PromiseWrapper.resolve(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate via the provided instruction. Returns a promise that resolves when navigation is
|
||||||
|
* complete.
|
||||||
|
*/
|
||||||
|
navigateByInstruction(instruction: Instruction, _skipLocationChange = false): Promise<any> {
|
||||||
|
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<Instruction> {
|
||||||
|
return PromiseWrapper.resolve(this.mockRecognizedInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerPrimaryOutlet(outlet: RouterOutlet): Promise<any> {
|
||||||
|
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<any> {
|
||||||
|
// Delegate navigation to the root router
|
||||||
|
return this.parent.navigateByUrl(url, _skipLocationChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateByInstruction(instruction: Instruction, _skipLocationChange = false):
|
||||||
|
Promise<any> {
|
||||||
|
// Delegate navigation to the root router
|
||||||
|
return this.parent.navigateByInstruction(instruction, _skipLocationChange);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
import { MyUppercasePipe } from './my-uppercase.pipe';
|
import { MyUppercasePipe } from './my-uppercase.pipe';
|
||||||
|
|
||||||
describe('MyUppercasePipe', () => {
|
describe('MyUppercasePipe', () => {
|
||||||
let pipe : MyUppercasePipe;
|
let pipe: MyUppercasePipe;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
pipe = new MyUppercasePipe();
|
pipe = new MyUppercasePipe();
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
<!-- #docregion head -->
|
<!-- #docregion head -->
|
||||||
<!-- IE required polyfills, in this exact order -->
|
<!-- IE required polyfills, in this exact order -->
|
||||||
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
|
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
|
||||||
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
|
|
||||||
<script src="node_modules/angular2/es6/dev/src/testing/shims_for_IE.js"></script>
|
<script src="node_modules/angular2/es6/dev/src/testing/shims_for_IE.js"></script>
|
||||||
|
|
||||||
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
|
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
|
||||||
|
@ -35,5 +34,7 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<my-app>Loading...</my-app>
|
<my-app>Loading...</my-app>
|
||||||
|
<hr>
|
||||||
|
<my-if-parent-comp>Loading MyIfParentComp ...</my-if-parent-comp>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
||||||
<title>Angular Public Unit Tests</title>
|
<title>Bag of Unit Tests</title>
|
||||||
<link rel="stylesheet" href="node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
|
<link rel="stylesheet" href="node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
|
||||||
|
|
||||||
<script src="node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
|
<script src="node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
<script src="node_modules/angular2/bundles/testing.dev.js"></script>
|
<script src="node_modules/angular2/bundles/testing.dev.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var __spec_files__ = ['app/public.spec'];
|
var __spec_files__ = ['app/bag.spec'];
|
||||||
</script>
|
</script>
|
||||||
<script src="test-shim.js"></script>
|
<script src="test-shim.js"></script>
|
||||||
</body>
|
</body>
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Configuration for the Wallaby Visual Studio Code testing extension
|
||||||
|
// https://marketplace.visualstudio.com/items?itemName=WallabyJs.wallaby-vscode
|
||||||
|
// Note: Wallaby is not open source and costs money
|
||||||
|
module.exports = function () {
|
||||||
|
|
||||||
|
return {
|
||||||
|
files: [
|
||||||
|
{pattern: 'node_modules/es6-shim/es6-shim.js', instrument: false},
|
||||||
|
{pattern: 'node_modules/systemjs/dist/system-polyfills.js', instrument: false},
|
||||||
|
{pattern: 'node_modules/systemjs/dist/system.js', instrument: false},
|
||||||
|
{pattern: 'node_modules/reflect-metadata/Reflect.js', instrument: false},
|
||||||
|
{pattern: 'node_modules/zone.js/dist/zone.js', instrument: false},
|
||||||
|
{pattern: 'node_modules/zone.js/dist/long-stack-trace-zone.js', instrument: false},
|
||||||
|
{pattern: 'node_modules/zone.js/dist/jasmine-patch.js', instrument: false},
|
||||||
|
|
||||||
|
{pattern: 'app/**/*+(ts|html|css)', load: false},
|
||||||
|
{pattern: 'app/**/*.spec.ts', ignore: true}
|
||||||
|
],
|
||||||
|
|
||||||
|
tests: [
|
||||||
|
{pattern: 'app/**/*.spec.ts', load: false}
|
||||||
|
],
|
||||||
|
|
||||||
|
middleware: function (app, express) {
|
||||||
|
app.use('/node_modules', express.static(require('path').join(__dirname, 'node_modules')));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFramework: 'jasmine',
|
||||||
|
|
||||||
|
bootstrap: function (wallaby) {
|
||||||
|
wallaby.delayStart();
|
||||||
|
|
||||||
|
System.config({
|
||||||
|
defaultJSExtensions: true,
|
||||||
|
packages: {
|
||||||
|
app: {
|
||||||
|
meta: {
|
||||||
|
'*': {
|
||||||
|
scriptLoad: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
paths: {
|
||||||
|
'npm:*': 'node_modules/*'
|
||||||
|
},
|
||||||
|
map: {
|
||||||
|
'angular2': 'npm:angular2',
|
||||||
|
'rxjs': 'npm:rxjs'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure Angular for the browser and
|
||||||
|
// with test versions of the platform 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
|
||||||
|
return Promise.all(wallaby.tests.map(function (specFile) {
|
||||||
|
return System.import(specFile);
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
wallaby.start();
|
||||||
|
})
|
||||||
|
.catch(function (e) {
|
||||||
|
setTimeout(function () {
|
||||||
|
throw e;
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
debug: true
|
||||||
|
};
|
||||||
|
};
|
Loading…
Reference in New Issue