Merge remote-tracking branch 'remotes/angular.io/master'
# Conflicts: # README.md # public/contribute.jade # public/docs/_includes/_side-nav.jade # public/docs/_includes/_ts-temp.jade # public/docs/index.jade # public/docs/js/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/cookbook/index.jade # public/docs/ts/latest/cookbook/set-document-title.jade # public/docs/ts/latest/cookbook/visual-studio-2015.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/animations.jade # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/browser-support.jade # public/docs/ts/latest/guide/component-styles.jade # public/docs/ts/latest/guide/forms.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/pipes.jade # public/docs/ts/latest/guide/router.jade # public/docs/ts/latest/guide/style-guide.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/testing.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/guide/upgrade.jade # public/docs/ts/latest/guide/webpack.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/index.jade # public/support.jade
This commit is contained in:
commit
d9af771f8d
|
@ -461,7 +461,7 @@ gulp.task('_copy-example-boilerplate', function (done) {
|
|||
return argv.fast ? done() : buildStyles(copyExampleBoilerplate, done);
|
||||
});
|
||||
|
||||
//Builds Angular 2 Docs CSS file from Bootstrap npm LESS source
|
||||
//Builds Angular Docs CSS file from Bootstrap npm LESS source
|
||||
//and copies the result to the _examples folder to be included as
|
||||
//part of the example boilerplate.
|
||||
function buildStyles(cb, done){
|
||||
|
|
14
harp.json
14
harp.json
|
@ -85,7 +85,7 @@
|
|||
"picture": "/resources/images/bios/tobias.jpg",
|
||||
"twitter": "tbosch1009",
|
||||
"website": "https://plus.google.com/+TobiasBosch",
|
||||
"bio": "Tobias Bosch is a software engineer at Google. He is part of the Angular core team and works on Angular 2.",
|
||||
"bio": "Tobias Bosch is a software engineer at Google. He is part of the Angular core team and works on Angular.",
|
||||
"type": "Google"
|
||||
},
|
||||
|
||||
|
@ -184,7 +184,7 @@
|
|||
"picture": "/resources/images/bios/hansl.jpg",
|
||||
"twitter": "hanslatwork",
|
||||
"website": "http://www.codingatwork.com/",
|
||||
"bio": "Hans is a software engineer at Google on the Angular team and was previously at Slack. He works everyday to help make it easier for everyone to create beautiful, consistent web applications using Angular2, using Material Design components and the CLI tool.",
|
||||
"bio": "Hans is a software engineer at Google on the Angular team and was previously at Slack. He works everyday to help make it easier for everyone to create beautiful, consistent web applications using Angular, using Material Design components and the CLI tool.",
|
||||
"type": "Google"
|
||||
},
|
||||
|
||||
|
@ -316,6 +316,12 @@
|
|||
"bio": "Max Sills is Angular's Open Source lawyer.",
|
||||
"type": "Google"
|
||||
},
|
||||
"shannon": {
|
||||
"name": "Shannon Ayres",
|
||||
"picture": "/resources/images/bios/shannon.jpg",
|
||||
"bio": "Shannon is a technical editor in Developer Relations at Google. She loves movies, especially Sunset Boulevard, and her favorite TV show is The Walking Dead. Her mission: Righting wrong writing!",
|
||||
"type": "Google"
|
||||
},
|
||||
|
||||
"pawel": {
|
||||
"name": "Pawel Kozlowski",
|
||||
|
@ -354,7 +360,7 @@
|
|||
"picture": "/resources/images/bios/marclaval.jpg",
|
||||
"twitter": "marclaval",
|
||||
"website": "https://github.com/mlaval",
|
||||
"bio": "Marc is a manager at Amadeus where he leads the team in charge of developing and recommending UI frameworks for the company. He is also an open source developer and a contributor to Angular 2.",
|
||||
"bio": "Marc is a manager at Amadeus where he leads the team in charge of developing and recommending UI frameworks for the company. He is also an open source developer and a contributor to Angular.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
|
@ -372,7 +378,7 @@
|
|||
"picture": "/resources/images/bios/patrick-stapleton.jpg",
|
||||
"twitter": "gdi2290",
|
||||
"website": "https://angularclass.com",
|
||||
"bio": "Also know as PatrickJS where JS stands for his middle and last names. Patrick is very active in Open-Source with over 4,300+ contributions in the last year alone on projects such as Angular2, AngularJS, FalcorJS, Docker, Bootstrap, gulp, and redis to name a few. He is also working on the development of Angular 2 server-side rendering as Universal Angular 2 and teaching Modern Web Development at AngularClass. He was previously the CTO of Keychain Logistics, a HackReactor Instructor and Alum.",
|
||||
"bio": "Also know as PatrickJS where JS stands for his middle and last names. Patrick is very active in Open-Source with over 4,300+ contributions in the last year alone on projects such as Angular2, AngularJS, FalcorJS, Docker, Bootstrap, gulp, and redis to name a few. He is also working on the development of Angular server-side rendering as Universal Angular and teaching Modern Web Development at AngularClass. He was previously the CTO of Keychain Logistics, a HackReactor Instructor and Alum.",
|
||||
"type": "Community"
|
||||
},
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "angular.io",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "Angular 2 documentation",
|
||||
"description": "Angular documentation",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
|
@ -48,7 +48,7 @@
|
|||
"gulp-tslint": "^5.0.0",
|
||||
"gulp-util": "^3.0.6",
|
||||
"gulp-watch": "^4.3.4",
|
||||
"harp": "git://github.com/filipesilva/harp.git#8da8d3497ddbfcbcbadd8be63e0fd731d7310cc4",
|
||||
"harp": "0.21.0-pre.1",
|
||||
"html2jade": "^0.8.4",
|
||||
"indent-string": "^2.1.0",
|
||||
"jasmine-core": "^2.3.4",
|
||||
|
|
|
@ -18,7 +18,7 @@ else
|
|||
h3.text-headline 资源库
|
||||
|
||||
ul.text-body
|
||||
// TODO: (ericjim) make a libraries page to showcase all angular 2 libraries
|
||||
// TODO: (ericjim) make a libraries page to showcase all angular libraries
|
||||
//li <a href="/libraries.html">Libraries</a>
|
||||
li <a href="/about/">About</a>
|
||||
li <a href="/about/">关于</a>
|
||||
|
|
|
@ -12,7 +12,7 @@ if title == "Angular"
|
|||
else if language
|
||||
title #{title} - #{language}
|
||||
else
|
||||
title #{title} - Angular 2
|
||||
title #{title} - Angular
|
||||
|
||||
meta(charset="utf-8")
|
||||
meta(http-equiv="X-UA-Compatible" content="IE=edge")
|
||||
|
@ -22,14 +22,14 @@ meta(name="robots" content="all")
|
|||
meta(name="referrer" content="origin")
|
||||
meta(name="viewport" id="viewport" content="width=device-width, initial-scale=1")
|
||||
|
||||
meta(property="og:title" content="Angular 2")
|
||||
meta(property="og:title" content="Angular")
|
||||
meta(property="og:image" content="/resources/images/logos/standard/shield-large.png")
|
||||
meta(property="og:image:type" content="image/png")
|
||||
meta(property="og:image:width" content="184")
|
||||
meta(property="og:image:height" content="200")
|
||||
meta(property="og:description" content="#{description}")
|
||||
|
||||
meta(itemprop="name" content="Angular 2")
|
||||
meta(itemprop="name" content="Angular")
|
||||
meta(itemprop="description" content="#{description}")
|
||||
meta(itemprop="image" content="/resources/images/logos/standard/shield-large.png")
|
||||
|
||||
|
|
|
@ -44,20 +44,20 @@ mixin tree(directory, urlPrefix, name, latest)
|
|||
//- BUTTON TITLE GENERATION
|
||||
if language == 'ts'
|
||||
if version == "latest"
|
||||
- var title = 'Angular 2 for TypeScript'
|
||||
- var title = 'Angular for TypeScript'
|
||||
else
|
||||
- var title = 'Angular ' + version + ' for TypeScript'
|
||||
|
||||
if language == 'js'
|
||||
if version == "latest"
|
||||
- var title = 'Angular 2 for JavaScript'
|
||||
- var title = 'Angular for JavaScript'
|
||||
else
|
||||
- var title = 'Angular ' + version + ' for JavaScript'
|
||||
|
||||
|
||||
if language == 'dart'
|
||||
if version == "latest"
|
||||
- var title = 'Angular 2 for Dart'
|
||||
- var title = 'Angular for Dart'
|
||||
else
|
||||
- var title = 'Angular ' + version + ' for Dart'
|
||||
|
||||
|
@ -69,8 +69,8 @@ nav.dropdown
|
|||
|
||||
<!-- DROPDOWN MENU -->
|
||||
ul(class="dropdown-menu" ng-class="appCtrl.showMenu ? 'is-visible' : ''")
|
||||
mixin tree(public.docs.ts, "/docs/ts", "Angular 2 for TypeScript")
|
||||
mixin tree(public.docs.js, "/docs/js", "Angular 2 for JavaScript")
|
||||
mixin tree(public.docs.ts, "/docs/ts", "Angular for TypeScript")
|
||||
mixin tree(public.docs.js, "/docs/js", "Angular for JavaScript")
|
||||
//- Disable cross-language link for API entry pages (but keep for top API search page):
|
||||
if ! (current.path[3] === 'api' && public.docs[current.path[1]][current.path[2]][current.path[3]][current.path[4]])
|
||||
mixin tree(public.docs.dart, "/docs/dart", "Angular 2 for Dart")
|
||||
mixin tree(public.docs.dart, "/docs/dart", "Angular for Dart")
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
p 我们希望你能给我们的源代码做出贡献,让Angular项目变得更好。
|
||||
|
||||
.l-sub-section
|
||||
h3 Angular 2
|
||||
h3 Angular
|
||||
|
||||
p Angular 2 is a next generation mobile and desktop application development platform.
|
||||
p Angular is a next generation mobile and desktop application development platform.
|
||||
|
||||
p Angular 2是下一代移动与桌面应用开发平台。
|
||||
p Angular是下一代移动与桌面应用开发平台。
|
||||
|
||||
a(href="https://github.com/angular/angular/blob/master/CONTRIBUTING.md" class="button" md-button) Contribute to Angular 2
|
||||
a(href="https://github.com/angular/angular/blob/master/CONTRIBUTING.md" class="button" md-button) 为Angular 2做贡献
|
||||
a(href="https://github.com/angular/angular/blob/master/CONTRIBUTING.md" class="button" md-button) Contribute to Angular
|
||||
a(href="https://github.com/angular/angular/blob/master/CONTRIBUTING.md" class="button" md-button) 为Angular做贡献
|
||||
|
||||
.l-sub-section
|
||||
h3 Angular for JavaScript or Dart
|
||||
|
|
|
@ -26,7 +26,7 @@ exports.config = {
|
|||
// Framework to use. Jasmine is recommended.
|
||||
framework: 'jasmine',
|
||||
|
||||
// For angular2 tests
|
||||
// For angular tests
|
||||
useAllAngular2AppRoots: true,
|
||||
|
||||
baseUrl: 'http://localhost:8080',
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* The tests here basically just checking that the end styles
|
||||
* of each animation are in effect.
|
||||
*
|
||||
* Relies on the Angular 2 testability only becoming stable once
|
||||
* Relies on the Angular testability only becoming stable once
|
||||
* animation(s) have finished.
|
||||
*
|
||||
* Ideally we'd use https://developer.mozilla.org/en-US/docs/Web/API/Document/getAnimations
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"description": "Angular 2 Animations",
|
||||
"description": "Angular Animations",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Architecture of Angular 2</title>
|
||||
<title>Architecture of Angular</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -10,7 +10,7 @@ class Hero {
|
|||
|
||||
describe('Architecture', () => {
|
||||
|
||||
const expectedTitle = 'Architecture of Angular 2';
|
||||
const expectedTitle = 'Architecture of Angular';
|
||||
const expectedH2 = ['Hero List', 'Sales Tax Calculator'];
|
||||
|
||||
beforeAll(() => browser.get(''));
|
||||
|
|
|
@ -13,7 +13,7 @@ import { Component } from '@angular/core';
|
|||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: 'Welcome to Angular 2'
|
||||
template: 'Welcome to Angular'
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor(logger: Logger) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Architecture of Angular 2</title>
|
||||
<title>Architecture of Angular</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Architecture of Angular 2</title>
|
||||
<title>Architecture of Angular</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"description": "Intro to Angular2",
|
||||
"description": "Intro to Angular",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
|
|
|
@ -9,7 +9,7 @@ describe('AOT Compilation', function () {
|
|||
|
||||
it('should load page and click button', function (done) {
|
||||
let headingSelector = element.all(by.css('h1')).get(0);
|
||||
expect(headingSelector.getText()).toEqual('My First Angular 2 App');
|
||||
expect(headingSelector.getText()).toEqual('My First Angular App');
|
||||
|
||||
expect(element.all(by.xpath('//div[text()="Magneta"]')).get(0).isPresent()).toBe(true);
|
||||
expect(element.all(by.xpath('//div[text()="Bombasto"]')).get(0).isPresent()).toBe(true);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!-- #docregion -->
|
||||
<button (click)="toggleHeading()">Toggle Heading</button>
|
||||
<h1 *ngIf="showHeading">My First Angular 2 App</h1>
|
||||
<h1 *ngIf="showHeading">My First Angular App</h1>
|
||||
|
||||
<h3>List of Heroes</h3>
|
||||
<div *ngFor="let hero of heroes">{{hero}}</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"description": "Set The Document Title In Angular 2",
|
||||
"description": "Set The Document Title In Angular",
|
||||
"files": [
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
|
|
|
@ -7,6 +7,6 @@ describe('cli-quickstart App', () => {
|
|||
|
||||
it('should display message saying app works', () => {
|
||||
let pageTitle = element(by.css('cli-quickstart-app h1')).getText();
|
||||
expect(pageTitle).toEqual('My First Angular 2 App');
|
||||
expect(pageTitle).toEqual('My First Angular App');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,6 +12,6 @@ import { Component } from '@angular/core';
|
|||
// #enddocregion metadata
|
||||
// #docregion title, class
|
||||
export class CliQuickstartAppComponent {
|
||||
title = 'My First Angular 2 App';
|
||||
title = 'My First Angular App';
|
||||
}
|
||||
// #enddocregion title, class
|
||||
|
|
|
@ -48,5 +48,5 @@
|
|||
|
||||
// #docregion first, final
|
||||
});
|
||||
// #enddocregion first, final
|
||||
})(window.app || (window.app = {}));
|
||||
// #enddocregion first, final
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Hello World</title>
|
||||
<title>Angular Hello World</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Hello World</title>
|
||||
<title>Angular Hello World</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Tabs</title>
|
||||
<title>Angular Tabs</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Tabs</title>
|
||||
<title>Angular Tabs</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Todos</title>
|
||||
<title>Angular Todos</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Todos</title>
|
||||
<title>Angular Todos</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!-- #docregion -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Lifecycle Hooks</title>
|
||||
<title>Angular Lifecycle Hooks</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
### Angular 2 Documentation Example
|
||||
### Angular Documentation Example
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
describe('QuickStart E2E Tests', function () {
|
||||
|
||||
let expectedMsg = 'My First Angular 2 App';
|
||||
let expectedMsg = 'My First Angular App';
|
||||
|
||||
beforeEach(function () {
|
||||
browser.get('');
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
ng.core.Component({
|
||||
// #enddocregion ng-namespace-funcs
|
||||
selector: 'my-app',
|
||||
template: '<h1>My First Angular 2 App</h1>'
|
||||
template: '<h1>My First Angular App</h1>'
|
||||
// #docregion ng-namespace-funcs
|
||||
})
|
||||
// #enddocregion component
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!-- #docregion -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 QuickStart JS</title>
|
||||
<title>Angular QuickStart JS</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Component } from '@angular/core';
|
|||
// #docregion metadata
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: '<h1>My First Angular 2 App</h1>'
|
||||
template: '<h1>My First Angular App</h1>'
|
||||
})
|
||||
// #enddocregion metadata
|
||||
// #docregion class
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!-- #docregion -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 QuickStart</title>
|
||||
<title>Angular QuickStart</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "angular2-quickstart",
|
||||
"name": "angular-quickstart",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// #docregion
|
||||
/**
|
||||
* System configuration for Angular 2 samples
|
||||
* System configuration for Angular samples
|
||||
* Adjust as necessary for your application needs.
|
||||
*/
|
||||
(function (global) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!-- #docregion -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Http Demo</title>
|
||||
<title>Angular Http Demo</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!-- #docregion -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Structural Directives</title>
|
||||
<title>Angular Structural Directives</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
describe('Documentation StyleGuide E2E Tests', function() {
|
||||
|
||||
let expectedMsg = 'My First Angular 2 App';
|
||||
let expectedMsg = 'My First Angular App';
|
||||
|
||||
beforeEach(function () {
|
||||
browser.get('');
|
||||
|
|
|
@ -8,7 +8,7 @@ app.AppComponent =
|
|||
selector: 'my-app',
|
||||
// #enddocregion
|
||||
// #docregion view
|
||||
template: '<h1 id="output">My First Angular 2 App</h1>'
|
||||
template: '<h1 id="output">My First Angular App</h1>'
|
||||
})
|
||||
// #enddocregion
|
||||
// #docregion class
|
||||
|
@ -48,7 +48,7 @@ app.AppComponent = function AppComponent () {}
|
|||
app.AppComponent.annotations = [
|
||||
new ng.core.Component({
|
||||
selector: 'my-app',
|
||||
template: '<h1 id="output">My First Angular 2 App</h1>'
|
||||
template: '<h1 id="output">My First Angular App</h1>'
|
||||
})
|
||||
];
|
||||
// #enddocregion
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Component } from '@angular/core';
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: '<h1 id="output">My First Angular 2 App</h1>'
|
||||
template: '<h1 id="output">My First Angular App</h1>'
|
||||
})
|
||||
export class AppComponent { }
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* System configuration for Angular 2 samples
|
||||
* System configuration for Angular samples
|
||||
* Adjust as necessary for your application needs.
|
||||
*/
|
||||
(function (global) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* PLUNKER VERSION FOR CURRENT ANGULAR BUILD
|
||||
* (based on systemjs.config.js in angular.io)
|
||||
* System configuration for Angular 2 samples
|
||||
* System configuration for Angular samples
|
||||
* Adjust as necessary for your application needs.
|
||||
*
|
||||
* UNTESTED !
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* PLUNKER VERSION
|
||||
* (based on systemjs.config.js in angular.io)
|
||||
* System configuration for Angular 2 samples
|
||||
* System configuration for Angular samples
|
||||
* Adjust as necessary for your application needs.
|
||||
*/
|
||||
(function (global) {
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
<script>
|
||||
var __spec_files__ = [
|
||||
'app/about.component.spec',
|
||||
'app/app.component.spec',
|
||||
'app/app.component.router.spec',
|
||||
'app/banner.component.spec',
|
||||
|
@ -42,7 +43,7 @@
|
|||
'app/model/hero.spec',
|
||||
'app/model/http-hero.service.spec',
|
||||
'app/shared/title-case.pipe.spec',
|
||||
'app/twain.component.spec',
|
||||
'app/shared/twain.component.spec',
|
||||
'app/welcome.component.spec'
|
||||
];
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { AboutComponent } from './about.component';
|
||||
import { HighlightDirective } from './shared/highlight.directive';
|
||||
|
||||
let fixture: ComponentFixture<AboutComponent>;
|
||||
|
||||
describe('AboutComponent (highlightDirective)', () => {
|
||||
// #docregion tests
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.configureTestingModule({
|
||||
declarations: [ AboutComponent, HighlightDirective],
|
||||
schemas: [ NO_ERRORS_SCHEMA ]
|
||||
})
|
||||
.createComponent(AboutComponent);
|
||||
fixture.detectChanges(); // initial binding
|
||||
});
|
||||
|
||||
it('should have skyblue <h2>', () => {
|
||||
const de = fixture.debugElement.query(By.css('h2'));
|
||||
expect(de.styles['backgroundColor']).toBe('skyblue');
|
||||
});
|
||||
// #enddocregion tests
|
||||
});
|
|
@ -1,12 +1,9 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<h2 highlight="skyblue">About</h2>
|
||||
<twain-quote></twain-quote>
|
||||
<p>All about this sample</p>
|
||||
`,
|
||||
styleUrls: ['app/shared/styles.css']
|
||||
<p>All about this sample</p>`
|
||||
})
|
||||
export class AboutComponent { }
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { AboutComponent } from './about.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot([
|
||||
{ path: '', redirectTo: 'dashboard', pathMatch: 'full'},
|
||||
{ path: 'about', component: AboutComponent },
|
||||
{ path: 'heroes', loadChildren: 'app/hero/hero.module#HeroModule'}
|
||||
])
|
||||
],
|
||||
exports: [ RouterModule ] // re-export the module declarations
|
||||
})
|
||||
export class AppRoutingModule { };
|
|
@ -1,3 +1,4 @@
|
|||
<!-- #docregion -->
|
||||
<app-banner></app-banner>
|
||||
<app-welcome></app-welcome>
|
||||
|
||||
|
|
|
@ -7,9 +7,7 @@ import { async, ComponentFixture, fakeAsync, TestBed, tick,
|
|||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { SpyLocation } from '@angular/common/testing';
|
||||
|
||||
// tslint:disable:no-unused-variable
|
||||
import { newEvent } from '../testing';
|
||||
// tslint:enable:no-unused-variable
|
||||
import { click } from '../testing';
|
||||
|
||||
// r - for relatively obscure router symbols
|
||||
import * as r from '@angular/router';
|
||||
|
@ -48,9 +46,8 @@ describe('AppComponent & RouterTestingModule', () => {
|
|||
|
||||
it('should navigate to "About" on click', fakeAsync(() => {
|
||||
createComponent();
|
||||
// page.aboutLinkDe.triggerEventHandler('click', null); // fails
|
||||
// page.aboutLinkDe.nativeElement.dispatchEvent(newEvent('click')); // fails
|
||||
page.aboutLinkDe.nativeElement.click(); // fails in phantom
|
||||
click(page.aboutLinkDe);
|
||||
// page.aboutLinkDe.nativeElement.click(); // ok but fails in phantom
|
||||
|
||||
advance();
|
||||
expectPathToBe('/about');
|
||||
|
|
|
@ -1,108 +1,94 @@
|
|||
// #docplaster
|
||||
import { async, ComponentFixture, TestBed
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
// #docregion setup-schemas
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
// #enddocregion setup-schemas
|
||||
// #docregion setup-stubs-w-imports
|
||||
import { Component } from '@angular/core';
|
||||
// #docregion setup-schemas
|
||||
import { AppComponent } from './app.component';
|
||||
// #enddocregion setup-schemas
|
||||
import { BannerComponent } from './banner.component';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
import { RouterLinkStubDirective } from '../testing';
|
||||
// #docregion setup-schemas
|
||||
import { RouterOutletStubComponent } from '../testing';
|
||||
|
||||
import { Router, FakeRouter, FakeRouterLinkDirective, FakeRouterOutletComponent
|
||||
} from '../testing';
|
||||
// #enddocregion setup-schemas
|
||||
@Component({selector: 'app-welcome', template: ''})
|
||||
class WelcomeStubComponent {}
|
||||
|
||||
// #enddocregion setup-stubs-w-imports
|
||||
|
||||
let comp: AppComponent;
|
||||
let fixture: ComponentFixture<AppComponent>;
|
||||
|
||||
describe('AppComponent & TestModule', () => {
|
||||
// #docregion setup-stubs, setup-stubs-w-imports
|
||||
beforeEach( async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
AppComponent, BannerComponent,
|
||||
FakeRouterLinkDirective, FakeRouterOutletComponent
|
||||
],
|
||||
providers: [{ provide: Router, useClass: FakeRouter }],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
AppComponent,
|
||||
BannerComponent, WelcomeStubComponent,
|
||||
RouterLinkStubDirective, RouterOutletStubComponent
|
||||
]
|
||||
})
|
||||
|
||||
.compileComponents()
|
||||
|
||||
.then(() => {
|
||||
fixture = TestBed.createComponent(AppComponent);
|
||||
comp = fixture.componentInstance;
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
// #enddocregion setup-stubs, setup-stubs-w-imports
|
||||
tests();
|
||||
});
|
||||
|
||||
function tests() {
|
||||
//////// Testing w/ NO_ERRORS_SCHEMA //////
|
||||
describe('AppComponent & NO_ERRORS_SCHEMA', () => {
|
||||
// #docregion setup-schemas
|
||||
beforeEach( async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ AppComponent, RouterLinkStubDirective ],
|
||||
schemas: [ NO_ERRORS_SCHEMA ]
|
||||
})
|
||||
|
||||
it('can instantiate it', () => {
|
||||
expect(comp).not.toBeNull();
|
||||
.compileComponents()
|
||||
.then(() => {
|
||||
fixture = TestBed.createComponent(AppComponent);
|
||||
comp = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('can get RouterLinks from template', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const links = fixture.debugElement
|
||||
// find all elements with an attached FakeRouterLink directive
|
||||
.queryAll(By.directive(FakeRouterLinkDirective))
|
||||
// use injector to get the RouterLink directive instance attached to each element
|
||||
.map(de => de.injector.get(FakeRouterLinkDirective) as FakeRouterLinkDirective);
|
||||
|
||||
expect(links.length).toBe(3, 'should have 3 links');
|
||||
expect(links[0].linkParams).toBe('/dashboard', '1st link should go to Dashboard');
|
||||
expect(links[1].linkParams).toBe('/heroes', '1st link should go to Heroes');
|
||||
}));
|
||||
// #enddocregion setup-schemas
|
||||
tests();
|
||||
});
|
||||
|
||||
it('can click Heroes link in template', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
// Heroes RouterLink DebugElement
|
||||
const heroesLinkDe = fixture.debugElement
|
||||
.queryAll(By.directive(FakeRouterLinkDirective))[1];
|
||||
|
||||
expect(heroesLinkDe).toBeDefined('should have a 2nd RouterLink');
|
||||
|
||||
const link = heroesLinkDe.injector.get(FakeRouterLinkDirective) as FakeRouterLinkDirective;
|
||||
|
||||
expect(link.navigatedTo).toBeNull('link should not have navigate yet');
|
||||
|
||||
heroesLinkDe.triggerEventHandler('click', null);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(link.navigatedTo).toBe('/heroes');
|
||||
});
|
||||
}
|
||||
|
||||
//////// Testing w/ real root module //////
|
||||
// Best to avoid
|
||||
// Tricky because we are disabling the router and its configuration
|
||||
// Better to use RouterTestingModule
|
||||
import { AppModule } from './app.module';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
describe('AppComponent & AppModule', () => {
|
||||
|
||||
beforeEach( async(() => {
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ AppModule ],
|
||||
imports: [ AppModule ]
|
||||
})
|
||||
|
||||
// Get rid of app's Router configuration otherwise many failures.
|
||||
// Doing so removes Router declarations; add the Router stubs
|
||||
.overrideModule(AppModule, {
|
||||
// Must get rid of `RouterModule.forRoot` to prevent attempt to configure a router
|
||||
// Can't remove it because it doesn't have a known type (`forRoot` returns an object)
|
||||
// therefore, must reset the entire `imports` with just the necessary stuff
|
||||
set: { imports: [ SharedModule ]}
|
||||
})
|
||||
|
||||
// Separate override because cannot both `set` and `add/remove` in same override
|
||||
.overrideModule(AppModule, {
|
||||
remove: {
|
||||
imports: [ AppRoutingModule ]
|
||||
},
|
||||
add: {
|
||||
declarations: [ FakeRouterLinkDirective, FakeRouterOutletComponent ],
|
||||
providers: [{ provide: Router, useClass: FakeRouter }]
|
||||
declarations: [ RouterLinkStubDirective, RouterOutletStubComponent ]
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -117,3 +103,46 @@ describe('AppComponent & AppModule', () => {
|
|||
tests();
|
||||
});
|
||||
|
||||
function tests() {
|
||||
let links: RouterLinkStubDirective[];
|
||||
let linkDes: DebugElement[];
|
||||
|
||||
// #docregion test-setup
|
||||
beforeEach(() => {
|
||||
// trigger initial data binding
|
||||
fixture.detectChanges();
|
||||
|
||||
// find DebugElements with an attached RouterLinkStubDirective
|
||||
linkDes = fixture.debugElement
|
||||
.queryAll(By.directive(RouterLinkStubDirective));
|
||||
|
||||
// get the attached link directive instances using the DebugElement injectors
|
||||
links = linkDes
|
||||
.map(de => de.injector.get(RouterLinkStubDirective) as RouterLinkStubDirective);
|
||||
});
|
||||
// #enddocregion test-setup
|
||||
|
||||
it('can instantiate it', () => {
|
||||
expect(comp).not.toBeNull();
|
||||
});
|
||||
|
||||
// #docregion tests
|
||||
it('can get RouterLinks from template', () => {
|
||||
expect(links.length).toBe(3, 'should have 3 links');
|
||||
expect(links[0].linkParams).toBe('/dashboard', '1st link should go to Dashboard');
|
||||
expect(links[1].linkParams).toBe('/heroes', '1st link should go to Heroes');
|
||||
});
|
||||
|
||||
it('can click Heroes link in template', () => {
|
||||
const heroesLinkDe = linkDes[1];
|
||||
const heroesLink = links[1];
|
||||
|
||||
expect(heroesLink.navigatedTo).toBeNull('link should not have navigated yet');
|
||||
|
||||
heroesLinkDe.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(heroesLink.navigatedTo).toBe('/heroes');
|
||||
});
|
||||
// #docregion tests
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
templateUrl: 'app/app.component.html'
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
import { AboutComponent } from './about.component';
|
||||
import { BannerComponent } from './banner.component';
|
||||
import { HeroService,
|
||||
|
@ -19,11 +19,7 @@ import { SharedModule } from './shared/shared.module';
|
|||
imports: [
|
||||
BrowserModule,
|
||||
DashboardModule,
|
||||
RouterModule.forRoot([
|
||||
{ path: '', redirectTo: 'dashboard', pathMatch: 'full'},
|
||||
{ path: 'about', component: AboutComponent },
|
||||
{ path: 'heroes', loadChildren: 'app/hero/hero.module#HeroModule'}
|
||||
]),
|
||||
AppRoutingModule,
|
||||
SharedModule
|
||||
],
|
||||
providers: [ HeroService, TwainService, UserService ],
|
||||
|
|
|
@ -26,7 +26,7 @@ import { NgModel, NgControl } from '@angular/forms';
|
|||
import { async, ComponentFixture, fakeAsync, inject, TestBed, tick
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { addMatchers, newEvent } from '../../testing';
|
||||
import { addMatchers, newEvent, click } from '../../testing';
|
||||
|
||||
beforeEach( addMatchers );
|
||||
|
||||
|
@ -180,7 +180,7 @@ describe('TestBed Component Tests', () => {
|
|||
const comp = fixture.componentInstance;
|
||||
const hero = comp.heroes[0];
|
||||
|
||||
heroes[0].triggerEventHandler('click', null);
|
||||
click(heroes[0]);
|
||||
fixture.detectChanges();
|
||||
|
||||
const selected = fixture.debugElement.query(By.css('p'));
|
||||
|
@ -213,7 +213,7 @@ describe('TestBed Component Tests', () => {
|
|||
fixture.detectChanges();
|
||||
expect(span.textContent).toMatch(/is off/i, 'before click');
|
||||
|
||||
btn.triggerEventHandler('click', null);
|
||||
click(btn);
|
||||
fixture.detectChanges();
|
||||
expect(span.textContent).toMatch(/is on/i, 'after click');
|
||||
});
|
||||
|
@ -610,7 +610,7 @@ describe('Lifecycle hooks w/ MyIfParentComp', () => {
|
|||
getChild();
|
||||
|
||||
const btn = fixture.debugElement.query(By.css('button'));
|
||||
btn.triggerEventHandler('click', null);
|
||||
click(btn);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(child.ngOnDestroyCalled).toBe(true);
|
||||
|
|
|
@ -4,7 +4,7 @@ import { async, ComponentFixture, TestBed
|
|||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { addMatchers } from '../../testing';
|
||||
import { addMatchers, click } from '../../testing';
|
||||
|
||||
import { Hero } from '../model/hero';
|
||||
import { DashboardHeroComponent } from './dashboard-hero.component';
|
||||
|
@ -53,10 +53,22 @@ describe('DashboardHeroComponent when tested directly', () => {
|
|||
let selectedHero: Hero;
|
||||
comp.selected.subscribe((hero: Hero) => selectedHero = hero);
|
||||
|
||||
// #docregion trigger-event-handler
|
||||
heroEl.triggerEventHandler('click', null);
|
||||
// #enddocregion trigger-event-handler
|
||||
expect(selectedHero).toBe(expectedHero);
|
||||
});
|
||||
// #enddocregion click-test
|
||||
|
||||
// #docregion click-test-2
|
||||
it('should raise selected event when clicked', () => {
|
||||
let selectedHero: Hero;
|
||||
comp.selected.subscribe((hero: Hero) => selectedHero = hero);
|
||||
|
||||
click(heroEl); // triggerEventHandler helper
|
||||
expect(selectedHero).toBe(expectedHero);
|
||||
});
|
||||
// #enddocregion click-test-2
|
||||
});
|
||||
|
||||
//////////////////
|
||||
|
@ -89,7 +101,7 @@ describe('DashboardHeroComponent when inside a test host', () => {
|
|||
});
|
||||
|
||||
it('should raise selected event when clicked', () => {
|
||||
heroEl.triggerEventHandler('click', null);
|
||||
click(heroEl);
|
||||
// selected hero should be the same data bound hero
|
||||
expect(testHost.selectedHero).toBe(testHost.hero);
|
||||
});
|
||||
|
@ -102,8 +114,7 @@ import { Component } from '@angular/core';
|
|||
// #docregion test-host
|
||||
@Component({
|
||||
template: `
|
||||
<dashboard-hero [hero]="hero" (selected)="onSelected($event)">
|
||||
</dashboard-hero>`
|
||||
<dashboard-hero [hero]="hero" (selected)="onSelected($event)"></dashboard-hero>`
|
||||
})
|
||||
class TestHostComponent {
|
||||
hero = new Hero(42, 'Test Name');
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { async, inject, ComponentFixture, TestBed
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { addMatchers } from '../../testing';
|
||||
import { addMatchers, click } from '../../testing';
|
||||
import { HeroService } from '../model';
|
||||
import { FakeHeroService } from '../model/testing';
|
||||
|
||||
|
@ -12,11 +12,11 @@ import { Router } from '@angular/router';
|
|||
import { DashboardComponent } from './dashboard.component';
|
||||
import { DashboardModule } from './dashboard.module';
|
||||
|
||||
// #docregion fake-router
|
||||
class FakeRouter {
|
||||
// #docregion router-stub
|
||||
class RouterStub {
|
||||
navigateByUrl(url: string) { return url; }
|
||||
}
|
||||
// #enddocregion fake-router
|
||||
// #enddocregion router-stub
|
||||
|
||||
beforeEach ( addMatchers );
|
||||
|
||||
|
@ -39,7 +39,7 @@ describe('DashboardComponent (deep)', () => {
|
|||
function clickForDeep() {
|
||||
// get first <div class="hero"> DebugElement
|
||||
const heroEl = fixture.debugElement.query(By.css('.hero'));
|
||||
heroEl.triggerEventHandler('click', null);
|
||||
click(heroEl);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -73,7 +73,7 @@ function compileAndCreate() {
|
|||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{ provide: HeroService, useClass: FakeHeroService },
|
||||
{ provide: Router, useClass: FakeRouter }
|
||||
{ provide: Router, useClass: RouterStub }
|
||||
]
|
||||
})
|
||||
.compileComponents().then(() => {
|
||||
|
|
|
@ -7,10 +7,7 @@ import { Hero, HeroService } from '../model';
|
|||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
templateUrl: 'app/dashboard/dashboard.component.html',
|
||||
styleUrls: [
|
||||
'app/shared/styles.css',
|
||||
'app/dashboard/dashboard.component.css'
|
||||
]
|
||||
styleUrls: ['app/dashboard/dashboard.component.css']
|
||||
})
|
||||
export class DashboardComponent implements OnInit {
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<!-- #docregion -->
|
||||
<div *ngIf="hero">
|
||||
<h2><span>{{hero.name | titlecase}}</span> Details</h2>
|
||||
<div>
|
||||
<label>id: </label>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name: </label>
|
||||
<input [(ngModel)]="hero.name" placeholder="name" />
|
||||
<label for="name">name: </label>
|
||||
<input id="name" [(ngModel)]="hero.name" placeholder="name" />
|
||||
</div>
|
||||
<button (click)="save()">Save</button>
|
||||
<button (click)="cancel()">Cancel</button>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { HeroDetailComponent } from './hero-detail.component';
|
||||
import { Hero } from '../model';
|
||||
|
||||
import { FakeActivatedRoute } from '../../testing';
|
||||
import { ActivatedRouteStub } from '../../testing';
|
||||
|
||||
////////// Tests ////////////////////
|
||||
|
||||
describe('HeroDetailComponent - no TestBed', () => {
|
||||
let activatedRoute: FakeActivatedRoute;
|
||||
let activatedRoute: ActivatedRouteStub;
|
||||
let comp: HeroDetailComponent;
|
||||
let expectedHero: Hero;
|
||||
let hds: any;
|
||||
|
@ -14,7 +14,7 @@ describe('HeroDetailComponent - no TestBed', () => {
|
|||
|
||||
beforeEach( done => {
|
||||
expectedHero = new Hero(42, 'Bubba');
|
||||
activatedRoute = new FakeActivatedRoute();
|
||||
activatedRoute = new ActivatedRouteStub();
|
||||
activatedRoute.testParams = { id: expectedHero.id };
|
||||
|
||||
router = jasmine.createSpyObj('router', ['navigate']);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// #docplaster
|
||||
import {
|
||||
async, ComponentFixture, fakeAsync, inject, TestBed, tick
|
||||
} from '@angular/core/testing';
|
||||
|
@ -6,100 +7,197 @@ import { By } from '@angular/platform-browser';
|
|||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import {
|
||||
addMatchers, newEvent,
|
||||
ActivatedRoute, FakeActivatedRoute, Router, FakeRouter
|
||||
ActivatedRoute, ActivatedRouteStub, click, newEvent, Router, RouterStub
|
||||
} from '../../testing';
|
||||
|
||||
import { HEROES, FakeHeroService } from '../model/testing';
|
||||
|
||||
import { HeroModule } from './hero.module';
|
||||
import { Hero } from '../model';
|
||||
import { HeroDetailComponent } from './hero-detail.component';
|
||||
import { HeroDetailService } from './hero-detail.service';
|
||||
import { Hero, HeroService } from '../model';
|
||||
import { HeroModule } from './hero.module';
|
||||
|
||||
////// Testing Vars //////
|
||||
let activatedRoute: FakeActivatedRoute;
|
||||
let activatedRoute: ActivatedRouteStub;
|
||||
let comp: HeroDetailComponent;
|
||||
let fixture: ComponentFixture<HeroDetailComponent>;
|
||||
let page: Page;
|
||||
|
||||
////////// Tests ////////////////////
|
||||
|
||||
////// Tests //////
|
||||
describe('HeroDetailComponent', () => {
|
||||
beforeEach(() => {
|
||||
activatedRoute = new ActivatedRouteStub();
|
||||
});
|
||||
describe('with HeroModule setup', heroModuleSetup);
|
||||
describe('when override its provided HeroDetailService', overrideSetup);
|
||||
describe('with FormsModule setup', formsModuleSetup);
|
||||
describe('with SharedModule setup', sharedModuleSetup);
|
||||
});
|
||||
|
||||
////////////////////
|
||||
function overrideSetup() {
|
||||
// #docregion stub-hds
|
||||
class StubHeroDetailService {
|
||||
testHero = new Hero(42, 'Test Hero');
|
||||
|
||||
getHero(id: number | string): Promise<Hero> {
|
||||
return Promise.resolve(true).then(() => Object.assign({}, this.testHero) );
|
||||
}
|
||||
|
||||
saveHero(hero: Hero): Promise<Hero> {
|
||||
return Promise.resolve(true).then(() => Object.assign(this.testHero, hero) );
|
||||
}
|
||||
}
|
||||
// #enddocregion stub-hds
|
||||
|
||||
// the `id` value is irrelevant because ignored by service stub
|
||||
beforeEach(() => activatedRoute.testParams = { id: 99999 } );
|
||||
|
||||
// #docregion setup-override
|
||||
beforeEach( async(() => {
|
||||
addMatchers();
|
||||
activatedRoute = new FakeActivatedRoute();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ HeroModule ],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: Router, useClass: RouterStub},
|
||||
// #enddocregion setup-override
|
||||
// HeroDetailService at this level is IRRELEVANT!
|
||||
{ provide: HeroDetailService, useValue: {} }
|
||||
// #docregion setup-override
|
||||
]
|
||||
})
|
||||
|
||||
// DON'T RE-DECLARE because already declared in HeroModule
|
||||
// declarations: [HeroDetailComponent, TitleCasePipe], // No!
|
||||
// Override component's own provider
|
||||
// #docregion override-component-method
|
||||
.overrideComponent(HeroDetailComponent, {
|
||||
set: {
|
||||
providers: [
|
||||
{ provide: HeroDetailService, useClass: StubHeroDetailService }
|
||||
]
|
||||
}
|
||||
})
|
||||
// #enddocregion override-component-method
|
||||
|
||||
.compileComponents();
|
||||
}));
|
||||
// #enddocregion setup-override
|
||||
|
||||
// #docregion override-tests
|
||||
let hds: StubHeroDetailService;
|
||||
|
||||
beforeEach( async(() => {
|
||||
createComponent();
|
||||
// get the component's injected StubHeroDetailService
|
||||
hds = fixture.debugElement.injector.get(HeroDetailService);
|
||||
}));
|
||||
|
||||
it('should display stub hero\'s name', () => {
|
||||
expect(page.nameDisplay.textContent).toBe(hds.testHero.name);
|
||||
});
|
||||
|
||||
it('should save stub hero change', fakeAsync(() => {
|
||||
const origName = hds.testHero.name;
|
||||
const newName = 'New Name';
|
||||
|
||||
page.nameInput.value = newName;
|
||||
page.nameInput.dispatchEvent(newEvent('input')); // tell Angular
|
||||
|
||||
expect(comp.hero.name).toBe(newName, 'component hero has new name');
|
||||
expect(hds.testHero.name).toBe(origName, 'service hero unchanged before save');
|
||||
|
||||
click(page.saveBtn);
|
||||
tick(); // wait for async save to complete
|
||||
expect(hds.testHero.name).toBe(newName, 'service hero has new name after save');
|
||||
expect(page.navSpy.calls.any()).toBe(true, 'router.navigate called');
|
||||
}));
|
||||
// #enddocregion override-tests
|
||||
|
||||
it('fixture injected service is not the component injected service',
|
||||
inject([HeroDetailService], (service: HeroDetailService) => {
|
||||
|
||||
expect(service).toEqual({}, 'service injected from fixture');
|
||||
expect(hds).toBeTruthy('service injected into component');
|
||||
}));
|
||||
}
|
||||
|
||||
////////////////////
|
||||
import { HEROES, FakeHeroService } from '../model/testing';
|
||||
import { HeroService } from '../model';
|
||||
|
||||
const firstHero = HEROES[0];
|
||||
|
||||
function heroModuleSetup() {
|
||||
// #docregion setup-hero-module
|
||||
beforeEach( async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ HeroModule ],
|
||||
// #enddocregion setup-hero-module
|
||||
// declarations: [ HeroDetailComponent ], // NO! DOUBLE DECLARATION
|
||||
// #docregion setup-hero-module
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: HeroService, useClass: FakeHeroService },
|
||||
{ provide: Router, useClass: FakeRouter},
|
||||
{ provide: Router, useClass: RouterStub},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
// #enddocregion setup-hero-module
|
||||
|
||||
describe('when navigate to hero id=' + HEROES[0].id, () => {
|
||||
// #docregion route-good-id
|
||||
describe('when navigate to existing hero', () => {
|
||||
let expectedHero: Hero;
|
||||
|
||||
beforeEach( async(() => {
|
||||
expectedHero = HEROES[0];
|
||||
expectedHero = firstHero;
|
||||
activatedRoute.testParams = { id: expectedHero.id };
|
||||
createComponent();
|
||||
}));
|
||||
|
||||
// #docregion selected-tests
|
||||
it('should display that hero\'s name', () => {
|
||||
expect(page.nameDisplay.textContent).toBe(expectedHero.name);
|
||||
});
|
||||
// #enddocregion route-good-id
|
||||
|
||||
it('should navigate when click cancel', () => {
|
||||
page.cancelBtn.triggerEventHandler('click', null);
|
||||
click(page.cancelBtn);
|
||||
expect(page.navSpy.calls.any()).toBe(true, 'router.navigate called');
|
||||
});
|
||||
|
||||
it('should save when click save', () => {
|
||||
page.saveBtn.triggerEventHandler('click', null);
|
||||
it('should save when click save but not navigate immediately', () => {
|
||||
click(page.saveBtn);
|
||||
expect(page.saveSpy.calls.any()).toBe(true, 'HeroDetailService.save called');
|
||||
expect(page.navSpy.calls.any()).toBe(false, 'router.navigate not called');
|
||||
});
|
||||
|
||||
it('should navigate when click click save resolves', fakeAsync(() => {
|
||||
page.saveBtn.triggerEventHandler('click', null);
|
||||
tick(); // waits for async save to "complete" before navigating
|
||||
it('should navigate when click save and save resolves', fakeAsync(() => {
|
||||
click(page.saveBtn);
|
||||
tick(); // wait for async save to complete
|
||||
expect(page.navSpy.calls.any()).toBe(true, 'router.navigate called');
|
||||
}));
|
||||
|
||||
|
||||
// #docregion title-case-pipe
|
||||
it('should convert original hero name to Title Case', () => {
|
||||
expect(page.nameDisplay.textContent).toBe(comp.hero.name);
|
||||
});
|
||||
// #enddocregion title-case-pipe
|
||||
|
||||
it('should convert hero name to Title Case', fakeAsync(() => {
|
||||
const inputName = 'quick BROWN fox';
|
||||
const expectedName = 'Quick Brown Fox';
|
||||
const titleCaseName = 'Quick Brown Fox';
|
||||
|
||||
// simulate user entering new name in input
|
||||
// simulate user entering new name into the input box
|
||||
page.nameInput.value = inputName;
|
||||
|
||||
// dispatch a DOM event so that Angular learns of input value change.
|
||||
// detectChanges() makes ngModel push input value to component property
|
||||
// and Angular updates the output span
|
||||
page.nameInput.dispatchEvent(newEvent('input'));
|
||||
|
||||
// Tell Angular to update the output span through the title pipe
|
||||
fixture.detectChanges();
|
||||
expect(page.nameDisplay.textContent).toBe(expectedName, 'hero name display');
|
||||
expect(comp.hero.name).toBe(inputName, 'comp.hero.name');
|
||||
|
||||
expect(page.nameDisplay.textContent).toBe(titleCaseName);
|
||||
}));
|
||||
|
||||
// #enddocregion title-case-pipe
|
||||
// #enddocregion selected-tests
|
||||
// #docregion route-good-id
|
||||
});
|
||||
// #enddocregion route-good-id
|
||||
|
||||
// #docregion route-no-id
|
||||
describe('when navigate with no hero id', () => {
|
||||
beforeEach( async( createComponent ));
|
||||
|
||||
|
@ -111,7 +209,9 @@ describe('HeroDetailComponent', () => {
|
|||
expect(page.nameDisplay.textContent).toBe('');
|
||||
});
|
||||
});
|
||||
// #enddocregion route-no-id
|
||||
|
||||
// #docregion route-bad-id
|
||||
describe('when navigate to non-existant hero id', () => {
|
||||
beforeEach( async(() => {
|
||||
activatedRoute.testParams = { id: 99999 };
|
||||
|
@ -123,11 +223,10 @@ describe('HeroDetailComponent', () => {
|
|||
expect(page.navSpy.calls.any()).toBe(true, 'router.navigate called');
|
||||
});
|
||||
});
|
||||
|
||||
///////////////////////////
|
||||
// #enddocregion route-bad-id
|
||||
|
||||
// Why we must use `fixture.debugElement.injector` in `Page()`
|
||||
it('cannot use `inject` to get component\'s provided service', () => {
|
||||
it('cannot use `inject` to get component\'s provided HeroDetailService', () => {
|
||||
let service: HeroDetailService;
|
||||
fixture = TestBed.createComponent(HeroDetailComponent);
|
||||
expect(
|
||||
|
@ -141,26 +240,85 @@ describe('HeroDetailComponent', () => {
|
|||
service = fixture.debugElement.injector.get(HeroDetailService);
|
||||
expect(service).toBeDefined('debugElement.injector');
|
||||
});
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { TitleCasePipe } from '../shared/title-case.pipe';
|
||||
|
||||
function formsModuleSetup() {
|
||||
// #docregion setup-forms-module
|
||||
beforeEach( async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ FormsModule ],
|
||||
declarations: [ HeroDetailComponent, TitleCasePipe ],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: HeroService, useClass: FakeHeroService },
|
||||
{ provide: Router, useClass: RouterStub},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
// #enddocregion setup-forms-module
|
||||
|
||||
it('should display 1st hero\'s name', fakeAsync(() => {
|
||||
const expectedHero = firstHero;
|
||||
activatedRoute.testParams = { id: expectedHero.id };
|
||||
createComponent().then(() => {
|
||||
expect(page.nameDisplay.textContent).toBe(expectedHero.name);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
function sharedModuleSetup() {
|
||||
// #docregion setup-shared-module
|
||||
beforeEach( async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ SharedModule ],
|
||||
declarations: [ HeroDetailComponent ],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: HeroService, useClass: FakeHeroService },
|
||||
{ provide: Router, useClass: RouterStub},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
// #enddocregion setup-shared-module
|
||||
|
||||
it('should display 1st hero\'s name', fakeAsync(() => {
|
||||
const expectedHero = firstHero;
|
||||
activatedRoute.testParams = { id: expectedHero.id };
|
||||
createComponent().then(() => {
|
||||
expect(page.nameDisplay.textContent).toBe(expectedHero.name);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
/////////// Helpers /////
|
||||
|
||||
// #docregion create-component
|
||||
/** Create the HeroDetailComponent, initialize it, set test variables */
|
||||
function createComponent() {
|
||||
fixture = TestBed.createComponent(HeroDetailComponent);
|
||||
comp = fixture.componentInstance;
|
||||
page = new Page();
|
||||
|
||||
// change detection triggers ngOnInit which gets a hero
|
||||
// 1st change detection triggers ngOnInit which gets a hero
|
||||
fixture.detectChanges();
|
||||
return fixture.whenStable().then(() => {
|
||||
// got the hero and updated component
|
||||
// change detection updates the view
|
||||
// 2nd change detection displays the async-fetched hero
|
||||
fixture.detectChanges();
|
||||
page.addPageElements();
|
||||
});
|
||||
}
|
||||
// #enddocregion create-component
|
||||
|
||||
// #docregion page
|
||||
class Page {
|
||||
gotoSpy: jasmine.Spy;
|
||||
navSpy: jasmine.Spy;
|
||||
|
@ -173,19 +331,20 @@ class Page {
|
|||
|
||||
constructor() {
|
||||
// Use component's injector to see the services it injected.
|
||||
let compInjector = fixture.debugElement.injector;
|
||||
let hds = compInjector.get(HeroDetailService);
|
||||
let router = compInjector.get(Router);
|
||||
const compInjector = fixture.debugElement.injector;
|
||||
const hds = compInjector.get(HeroDetailService);
|
||||
const router = compInjector.get(Router);
|
||||
|
||||
this.gotoSpy = spyOn(comp, 'gotoList').and.callThrough();
|
||||
this.navSpy = spyOn(router, 'navigate');
|
||||
this.saveSpy = spyOn(hds, 'saveHero').and.callThrough();
|
||||
this.navSpy = spyOn(router, 'navigate').and.callThrough();
|
||||
}
|
||||
|
||||
/** Add page elements after page initializes */
|
||||
/** Add page elements after hero arrives */
|
||||
addPageElements() {
|
||||
if (comp.hero) {
|
||||
// have a hero so these DOM elements can be reached
|
||||
let buttons = fixture.debugElement.queryAll(By.css('button'));
|
||||
// have a hero so these elements are now in the DOM
|
||||
const buttons = fixture.debugElement.queryAll(By.css('button'));
|
||||
this.saveBtn = buttons[0];
|
||||
this.cancelBtn = buttons[1];
|
||||
this.nameDisplay = fixture.debugElement.query(By.css('span')).nativeElement;
|
||||
|
@ -193,4 +352,4 @@ class Page {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #enddocregion page
|
||||
|
|
|
@ -1,35 +1,41 @@
|
|||
/* tslint:disable:member-ordering */
|
||||
// #docplaster
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import 'rxjs/add/operator/pluck';
|
||||
|
||||
import { Hero } from '../model';
|
||||
import { HeroDetailService } from './hero-detail.service';
|
||||
|
||||
// #docregion prototype
|
||||
@Component({
|
||||
selector: 'app-hero-detail',
|
||||
templateUrl: 'app/hero/hero-detail.component.html',
|
||||
styleUrls: [
|
||||
'app/shared/styles.css',
|
||||
'app/hero/hero-detail.component.css'
|
||||
],
|
||||
styleUrls: ['app/hero/hero-detail.component.css'],
|
||||
providers: [ HeroDetailService ]
|
||||
})
|
||||
export class HeroDetailComponent implements OnInit {
|
||||
@Input() hero: Hero;
|
||||
|
||||
// #docregion ctor
|
||||
constructor(
|
||||
private heroDetailService: HeroDetailService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router) {
|
||||
}
|
||||
// #enddocregion ctor
|
||||
// #enddocregion prototype
|
||||
|
||||
ngOnInit() {
|
||||
let id = this.route.snapshot.params['id'];
|
||||
@Input() hero: Hero;
|
||||
|
||||
// tslint:disable-next-line:triple-equals
|
||||
if (id == undefined) {
|
||||
// no id; act as if is new
|
||||
this.hero = new Hero();
|
||||
} else {
|
||||
// #docregion ng-on-init
|
||||
ngOnInit(): void {
|
||||
// get hero when `id` param changes
|
||||
this.route.params.pluck<string>('id')
|
||||
.forEach(id => this.getHero(id))
|
||||
.catch(() => this.hero = new Hero()); // no id; should edit new hero
|
||||
}
|
||||
// #enddocregion ng-on-init
|
||||
|
||||
private getHero(id: string): void {
|
||||
this.heroDetailService.getHero(id).then(hero => {
|
||||
if (hero) {
|
||||
this.hero = hero;
|
||||
|
@ -38,9 +44,8 @@ export class HeroDetailComponent implements OnInit {
|
|||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
save() {
|
||||
save(): void {
|
||||
this.heroDetailService.saveHero(this.hero).then(() => this.gotoList());
|
||||
}
|
||||
|
||||
|
@ -49,4 +54,6 @@ export class HeroDetailComponent implements OnInit {
|
|||
gotoList() {
|
||||
this.router.navigate(['../'], {relativeTo: this.route});
|
||||
}
|
||||
// #docregion prototype
|
||||
}
|
||||
// #enddocregion prototype
|
||||
|
|
|
@ -2,10 +2,13 @@ import { Injectable } from '@angular/core';
|
|||
|
||||
import { Hero, HeroService } from '../model';
|
||||
|
||||
// #docregion prototype
|
||||
@Injectable()
|
||||
export class HeroDetailService {
|
||||
constructor(private heroService: HeroService) { }
|
||||
// #enddocregion prototype
|
||||
|
||||
// Returns a clone which caller may modify safely
|
||||
getHero(id: number | string): Promise<Hero> {
|
||||
if (typeof id === 'string') {
|
||||
id = parseInt(id as string, 10);
|
||||
|
@ -18,4 +21,6 @@ export class HeroDetailService {
|
|||
saveHero(hero: Hero) {
|
||||
return this.heroService.updateHero(hero);
|
||||
}
|
||||
// #docregion prototype
|
||||
}
|
||||
// #enddocregion prototype
|
||||
|
|
|
@ -4,7 +4,7 @@ import { async, ComponentFixture, fakeAsync, TestBed, tick
|
|||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { addMatchers, newEvent, Router, FakeRouter
|
||||
import { addMatchers, newEvent, Router, RouterStub
|
||||
} from '../../testing';
|
||||
|
||||
import { HEROES, FakeHeroService } from '../model/testing';
|
||||
|
@ -28,7 +28,7 @@ describe('HeroListComponent', () => {
|
|||
imports: [HeroModule],
|
||||
providers: [
|
||||
{ provide: HeroService, useClass: FakeHeroService },
|
||||
{ provide: Router, useClass: FakeRouter}
|
||||
{ provide: Router, useClass: RouterStub}
|
||||
]
|
||||
})
|
||||
.compileComponents()
|
||||
|
@ -132,7 +132,7 @@ class Page {
|
|||
|
||||
// Get the component's injected router and spy on it
|
||||
const router = fixture.debugElement.injector.get(Router);
|
||||
this.navSpy = spyOn(router, 'navigate').and.callThrough();
|
||||
this.navSpy = spyOn(router, 'navigate');
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,7 @@ import { Hero, HeroService } from '../model';
|
|||
@Component({
|
||||
selector: 'app-heroes',
|
||||
templateUrl: 'app/hero/hero-list.component.html',
|
||||
styleUrls: [
|
||||
'app/shared/styles.css',
|
||||
'app/hero/hero-list.component.css'
|
||||
]
|
||||
styleUrls: ['app/hero/hero-list.component.css']
|
||||
})
|
||||
export class HeroListComponent implements OnInit {
|
||||
heroes: Promise<Hero[]>;
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Hero } from './hero';
|
|||
import { HEROES } from './test-heroes';
|
||||
|
||||
@Injectable()
|
||||
/** Dummy HeroService that pretends to be real */
|
||||
/** Dummy HeroService. Pretend it makes real http requests */
|
||||
export class HeroService {
|
||||
getHeroes() {
|
||||
return Promise.resolve(HEROES);
|
||||
|
@ -21,9 +21,10 @@ export class HeroService {
|
|||
|
||||
updateHero(hero: Hero): Promise<Hero> {
|
||||
return this.getHero(hero.id).then(h => {
|
||||
return h ?
|
||||
Object.assign(h, hero) :
|
||||
Promise.reject(`Hero ${hero.id} not found`) as any as Promise<Hero>;
|
||||
if (!h) {
|
||||
throw new Error(`Hero ${hero.id} not found`);
|
||||
}
|
||||
return Object.assign(h, hero);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,56 +3,98 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { HighlightDirective } from './highlight.directive';
|
||||
import { newEvent } from '../../testing';
|
||||
|
||||
// Component to test directive
|
||||
// #docregion test-component
|
||||
@Component({
|
||||
template: `
|
||||
<h2 highlight="yellow">Something Yellow</h2>
|
||||
<h2 highlight>Something Gray</h2>
|
||||
<h2>Something White</h2>
|
||||
`
|
||||
|
||||
<h2 highlight>The Default (Gray)</h2>
|
||||
<h2>No Highlight</h2>
|
||||
<input #box [highlight]="box.value" value="cyan"/>`
|
||||
})
|
||||
class TestComponent { }
|
||||
// #enddocregion test-component
|
||||
|
||||
////// Tests //////////
|
||||
describe('HighlightDirective', () => {
|
||||
|
||||
let fixture: ComponentFixture<TestComponent>;
|
||||
let h2Des: DebugElement[];
|
||||
let des: DebugElement[]; // the three elements w/ the directive
|
||||
let bareH2: DebugElement; // the <h2> w/o the directive
|
||||
|
||||
// #docregion selected-tests
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.configureTestingModule({
|
||||
declarations: [ HighlightDirective, TestComponent ]
|
||||
})
|
||||
.createComponent(TestComponent);
|
||||
|
||||
h2Des = fixture.debugElement.queryAll(By.css('h2'));
|
||||
fixture.detectChanges(); // initial binding
|
||||
|
||||
// all elements with an attached HighlightDirective
|
||||
des = fixture.debugElement.queryAll(By.directive(HighlightDirective));
|
||||
|
||||
// the h2 without the HighlightDirective
|
||||
bareH2 = fixture.debugElement.query(By.css('h2:not([highlight])'));
|
||||
});
|
||||
|
||||
it('should have `HighlightDirective`', () => {
|
||||
// The HighlightDirective listed in <h2> tokens means it is attached
|
||||
expect(h2Des[0].providerTokens).toContain(HighlightDirective, 'HighlightDirective');
|
||||
// color tests
|
||||
it('should have three highlighted elements', () => {
|
||||
expect(des.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should color first <h2> background "yellow"', () => {
|
||||
fixture.detectChanges();
|
||||
const h2 = h2Des[0].nativeElement as HTMLElement;
|
||||
expect(h2.style.backgroundColor).toBe('yellow');
|
||||
it('should color 1st <h2> background "yellow"', () => {
|
||||
expect(des[0].styles['backgroundColor']).toBe('yellow');
|
||||
});
|
||||
|
||||
it('should color second <h2> background w/ default color', () => {
|
||||
fixture.detectChanges();
|
||||
const h2 = h2Des[1].nativeElement as HTMLElement;
|
||||
expect(h2.style.backgroundColor).toBe(HighlightDirective.defaultColor);
|
||||
it('should color 2nd <h2> background w/ default color', () => {
|
||||
const dir = des[1].injector.get(HighlightDirective) as HighlightDirective;
|
||||
expect(des[1].styles['backgroundColor']).toBe(dir.defaultColor);
|
||||
});
|
||||
|
||||
it('should NOT color third <h2> (no directive)', () => {
|
||||
// no directive
|
||||
expect(h2Des[2].providerTokens).not.toContain(HighlightDirective, 'HighlightDirective');
|
||||
it('should bind <input> background to value color', () => {
|
||||
// easier to work with nativeElement
|
||||
const input = des[2].nativeElement as HTMLInputElement;
|
||||
expect(input.style.backgroundColor).toBe('cyan', 'initial backgroundColor');
|
||||
|
||||
// dispatch a DOM event so that Angular responds to the input value change.
|
||||
input.value = 'green';
|
||||
input.dispatchEvent(newEvent('input'));
|
||||
fixture.detectChanges();
|
||||
|
||||
const h2 = h2Des[2].nativeElement as HTMLElement;
|
||||
expect(h2.style.backgroundColor).toBe('', 'backgroundColor');
|
||||
expect(input.style.backgroundColor).toBe('green', 'changed backgroundColor');
|
||||
});
|
||||
|
||||
// customProperty tests
|
||||
it('all highlighted elements should have a true customProperty', () => {
|
||||
const allTrue = des.map(de => !!de.properties['customProperty']).every(v => v === true);
|
||||
expect(allTrue).toBe(true);
|
||||
});
|
||||
|
||||
it('bare <h2> should not have a customProperty', () => {
|
||||
expect(bareH2.properties['customProperty']).toBeUndefined();
|
||||
});
|
||||
// #enddocregion selected-tests
|
||||
|
||||
// injected directive
|
||||
// attached HighlightDirective can be injected
|
||||
it('can inject `HighlightDirective` in 1st <h2>', () => {
|
||||
const dir = des[0].injector.get(HighlightDirective);
|
||||
expect(dir).toBeTruthy();
|
||||
});
|
||||
|
||||
it('cannot inject `HighlightDirective` in 3rd <h2>', () => {
|
||||
const dir = bareH2.injector.get(HighlightDirective, null);
|
||||
expect(dir).toBe(null);
|
||||
});
|
||||
|
||||
// DebugElement.providerTokens
|
||||
// attached HighlightDirective should be listed in the providerTokens
|
||||
it('should have `HighlightDirective` in 1st <h2> providerTokens', () => {
|
||||
expect(des[0].providerTokens).toContain(HighlightDirective);
|
||||
});
|
||||
|
||||
it('should not have `HighlightDirective` in 3rd <h2> providerTokens', () => {
|
||||
expect(bareH2.providerTokens).not.toContain(HighlightDirective);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
// #docregion
|
||||
import { Directive, ElementRef, Input, OnChanges, Renderer } from '@angular/core';
|
||||
|
||||
@Directive({ selector: '[highlight]' })
|
||||
/**
|
||||
* Set backgroundColor for the attached element ton highlight color and
|
||||
* set element `customProperty` = true
|
||||
*/
|
||||
/** Set backgroundColor for the attached element to highlight color
|
||||
* and set the element's customProperty to true */
|
||||
export class HighlightDirective implements OnChanges {
|
||||
|
||||
static defaultColor = 'rgb(211, 211, 211)'; // lightgray
|
||||
defaultColor = 'rgb(211, 211, 211)'; // lightgray
|
||||
|
||||
@Input('highlight') bgColor: string;
|
||||
|
||||
|
@ -18,7 +17,6 @@ export class HighlightDirective implements OnChanges {
|
|||
ngOnChanges() {
|
||||
this.renderer.setElementStyle(
|
||||
this.el.nativeElement, 'backgroundColor',
|
||||
this.bgColor || HighlightDirective.defaultColor );
|
||||
this.bgColor || this.defaultColor );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
/* MISSING */
|
|
@ -13,15 +13,20 @@ describe('WelcomeComponent', () => {
|
|||
let userService: UserService; // the actually injected service
|
||||
let welcomeEl: DebugElement; // the element with the welcome message
|
||||
|
||||
let userServiceStub: {
|
||||
isLoggedIn: boolean;
|
||||
user: { name: string}
|
||||
};
|
||||
|
||||
// #docregion setup
|
||||
beforeEach(() => {
|
||||
// fake UserService for test purposes
|
||||
// #docregion fake-userservice
|
||||
const fakeUserService = {
|
||||
// stub UserService for test purposes
|
||||
// #docregion user-service-stub
|
||||
userServiceStub = {
|
||||
isLoggedIn: true,
|
||||
user: { name: 'Test User'}
|
||||
};
|
||||
// #enddocregion fake-userservice
|
||||
// #enddocregion user-service-stub
|
||||
|
||||
// #docregion config-test-module
|
||||
TestBed.configureTestingModule({
|
||||
|
@ -29,7 +34,7 @@ describe('WelcomeComponent', () => {
|
|||
// #enddocregion setup
|
||||
// providers: [ UserService ] // a real service would be a problem!
|
||||
// #docregion setup
|
||||
providers: [ {provide: UserService, useValue: fakeUserService } ]
|
||||
providers: [ {provide: UserService, useValue: userServiceStub } ]
|
||||
});
|
||||
// #enddocregion config-test-module
|
||||
|
||||
|
@ -80,4 +85,8 @@ describe('WelcomeComponent', () => {
|
|||
expect(content).toMatch(/log in/i, '"log in"');
|
||||
});
|
||||
// #enddocregion tests
|
||||
|
||||
it('orig stub and injected UserService are not the same object', () => {
|
||||
expect(userServiceStub === userService).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing.
|
|||
// Uncomment to get full stacktrace output. Sometimes helpful, usually not.
|
||||
// Error.stackTraceLimit = Infinity; //
|
||||
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000;
|
||||
|
||||
var baseURL = document.baseURI;
|
||||
baseURL = baseURL + baseURL[baseURL.length-1] ? '' : '/';
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
// export for convenience.
|
||||
export { ActivatedRoute, Router, RouterLink, RouterOutlet} from '@angular/router';
|
||||
|
||||
import { Component, Directive, Injectable, Input } from '@angular/core';
|
||||
import { NavigationExtras } from '@angular/router';
|
||||
|
||||
@Directive({
|
||||
selector: '[routerLink]',
|
||||
host: {
|
||||
'(click)': 'onClick()',
|
||||
'[attr.href]': 'visibleHref',
|
||||
'[class.router-link-active]': 'isRouteActive'
|
||||
}
|
||||
})
|
||||
export class FakeRouterLinkDirective {
|
||||
|
||||
isRouteActive = false;
|
||||
visibleHref: string; // the url displayed on the anchor element.
|
||||
|
||||
@Input('routerLink') linkParams: any;
|
||||
navigatedTo: any = null;
|
||||
|
||||
onClick() {
|
||||
this.navigatedTo = this.linkParams;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'router-outlet', template: ''})
|
||||
export class FakeRouterOutletComponent { }
|
||||
|
||||
@Injectable()
|
||||
export class FakeRouter {
|
||||
lastCommand: any[];
|
||||
navigate(commands: any[], extras?: NavigationExtras) {
|
||||
this.lastCommand = commands;
|
||||
return commands;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class FakeActivatedRoute {
|
||||
testParams: {} = {};
|
||||
|
||||
get snapshot() {
|
||||
return {
|
||||
params: this.testParams
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,9 +1,17 @@
|
|||
import { DebugElement } from '@angular/core';
|
||||
import { tick, ComponentFixture } from '@angular/core/testing';
|
||||
|
||||
export * from './jasmine-matchers';
|
||||
export * from './fake-router';
|
||||
export * from './router-stubs';
|
||||
|
||||
///// Short utilities /////
|
||||
|
||||
/** Wait a tick, then detect changes */
|
||||
export function advance(f: ComponentFixture<any>): void {
|
||||
tick();
|
||||
f.detectChanges();
|
||||
}
|
||||
|
||||
// Short utilities
|
||||
/**
|
||||
* Create custom DOM event the old fashioned way
|
||||
*
|
||||
|
@ -16,8 +24,20 @@ export function newEvent(eventName: string, bubbles = false, cancelable = false)
|
|||
return evt;
|
||||
}
|
||||
|
||||
/** Wait a tick, then detect changes */
|
||||
export function advance(f: ComponentFixture<any>): void {
|
||||
tick();
|
||||
f.detectChanges();
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
||||
// #docregion click-event
|
||||
/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */
|
||||
export const ButtonClickEvents = {
|
||||
left: { button: 0 },
|
||||
right: { button: 2 }
|
||||
};
|
||||
|
||||
/** Simulate element click. Defaults to mouse left-button click event. */
|
||||
export function click(el: DebugElement | HTMLElement, eventObj: any = ButtonClickEvents.left): void {
|
||||
if (el instanceof HTMLElement) {
|
||||
el.click();
|
||||
} else {
|
||||
el.triggerEventHandler('click', eventObj);
|
||||
}
|
||||
}
|
||||
// #enddocregion click-event
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
// export for convenience.
|
||||
export { ActivatedRoute, Router, RouterLink, RouterOutlet} from '@angular/router';
|
||||
|
||||
import { Component, Directive, Injectable, Input } from '@angular/core';
|
||||
import { NavigationExtras } from '@angular/router';
|
||||
|
||||
// #docregion router-link
|
||||
@Directive({
|
||||
selector: '[routerLink]',
|
||||
host: {
|
||||
'(click)': 'onClick()'
|
||||
}
|
||||
})
|
||||
export class RouterLinkStubDirective {
|
||||
@Input('routerLink') linkParams: any;
|
||||
navigatedTo: any = null;
|
||||
|
||||
onClick() {
|
||||
this.navigatedTo = this.linkParams;
|
||||
}
|
||||
}
|
||||
// #enddocregion router-link
|
||||
|
||||
@Component({selector: 'router-outlet', template: ''})
|
||||
export class RouterOutletStubComponent { }
|
||||
|
||||
@Injectable()
|
||||
export class RouterStub {
|
||||
navigate(commands: any[], extras?: NavigationExtras) { }
|
||||
}
|
||||
|
||||
|
||||
// Only implements params and part of snapshot.params
|
||||
// #docregion activated-route-stub
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
@Injectable()
|
||||
export class ActivatedRouteStub {
|
||||
|
||||
// ActivatedRoute.params is Observable
|
||||
private subject = new BehaviorSubject(this.testParams);
|
||||
params = this.subject.asObservable();
|
||||
|
||||
// Test parameters
|
||||
private _testParams: {};
|
||||
get testParams() { return this._testParams; }
|
||||
set testParams(params: {}) {
|
||||
this._testParams = params;
|
||||
this.subject.next(params);
|
||||
}
|
||||
|
||||
// ActivatedRoute.snapshot.params
|
||||
get snapshot() {
|
||||
return { params: this.testParams };
|
||||
}
|
||||
}
|
||||
// #enddocregion activated-route-stub
|
|
@ -4,7 +4,7 @@
|
|||
type WPromise<T> = webdriver.promise.Promise<T>;
|
||||
|
||||
const expectedH1 = 'Tour of Heroes';
|
||||
const expectedTitle = `Angular 2 ${expectedH1}`;
|
||||
const expectedTitle = `Angular ${expectedH1}`;
|
||||
|
||||
class Hero {
|
||||
id: number;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Tour of Heroes</title>
|
||||
<title>Angular Tour of Heroes</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
|
||||
const expectedH1 = 'Tour of Heroes';
|
||||
const expectedTitle = `Angular 2 ${expectedH1}`;
|
||||
const expectedTitle = `Angular ${expectedH1}`;
|
||||
const expectedH2 = 'My Heroes';
|
||||
const targetHero = { id: 16, name: 'RubberMan' };
|
||||
const nameSuffix = 'X';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Tour of Heroes</title>
|
||||
<title>Angular Tour of Heroes</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
|
||||
const expectedH1 = 'Tour of Heroes';
|
||||
const expectedTitle = `Angular 2 ${expectedH1}`;
|
||||
const expectedTitle = `Angular ${expectedH1}`;
|
||||
const expectedH2 = 'My Heroes';
|
||||
const targetHero = { id: 16, name: 'RubberMan' };
|
||||
const nameSuffix = 'X';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Tour of Heroes</title>
|
||||
<title>Angular Tour of Heroes</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
|
||||
const expectedH1 = 'Tour of Heroes';
|
||||
const expectedTitle = `Angular 2 ${expectedH1}`;
|
||||
const expectedTitle = `Angular ${expectedH1}`;
|
||||
const expectedH2 = 'My Heroes';
|
||||
const targetHero = { id: 16, name: 'RubberMan' };
|
||||
const nameSuffix = 'X';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Tour of Heroes</title>
|
||||
<title>Angular Tour of Heroes</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
|
||||
const expectedH1 = 'Tour of Heroes';
|
||||
const expectedTitle = `Angular 2 ${expectedH1}`;
|
||||
const expectedTitle = `Angular ${expectedH1}`;
|
||||
const targetHero = { id: 15, name: 'Magneta' };
|
||||
const targetHeroDashboardIndex = 3;
|
||||
const nameSuffix = 'X';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<head>
|
||||
<base href="/">
|
||||
<!-- #enddocregion base-href -->
|
||||
<title>Angular 2 Tour of Heroes</title>
|
||||
<title>Angular Tour of Heroes</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
|
|
@ -23,9 +23,6 @@ void main() {
|
|||
bootstrap(AppComponent, [
|
||||
provide(BrowserClient, useFactory: () => new BrowserClient(), deps: [])
|
||||
]);
|
||||
// Simplify bootstrap provider list to [BrowserClient]
|
||||
// once there is a fix for:
|
||||
// https://github.com/dart-lang/angular2/issues/37
|
||||
}
|
||||
// #enddocregion v1
|
||||
*/
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
|
||||
const expectedH1 = 'Tour of Heroes';
|
||||
const expectedTitle = `Angular 2 ${expectedH1}`;
|
||||
const expectedTitle = `Angular ${expectedH1}`;
|
||||
const targetHero = { id: 15, name: 'Magneta' };
|
||||
const targetHeroDashboardIndex = 3;
|
||||
const nameSuffix = 'X';
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<base href="/">
|
||||
<title>Angular 2 Tour of Heroes</title>
|
||||
<title>Angular Tour of Heroes</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
describe('QuickStart E2E Tests', function () {
|
||||
|
||||
let expectedMsg = 'Hello from Angular 2 App with Webpack';
|
||||
let expectedMsg = 'Hello from Angular App with Webpack';
|
||||
|
||||
beforeEach(function () {
|
||||
browser.get('');
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Error.stackTraceLimit = Infinity;
|
||||
|
||||
require('core-js/es6');
|
||||
require('reflect-metadata');
|
||||
require('core-js/es7/reflect');
|
||||
|
||||
require('zone.js/dist/zone');
|
||||
require('zone.js/dist/long-stack-trace-zone');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "angular2-webpack",
|
||||
"version": "1.0.0",
|
||||
"description": "A webpack starter for angular 2",
|
||||
"description": "A webpack starter for Angular",
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --inline --progress --port 8080",
|
||||
"test": "karma start",
|
||||
|
@ -41,7 +41,6 @@
|
|||
"raw-loader": "^0.5.1",
|
||||
"rimraf": "^2.5.2",
|
||||
"style-loader": "^0.13.1",
|
||||
"ts-loader": "^0.8.1",
|
||||
"typescript": "^2.0.2",
|
||||
"typings": "^1.3.2",
|
||||
"webpack": "^1.13.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!-- #docregion -->
|
||||
<main>
|
||||
<h1>Hello from Angular 2 App with Webpack</h1>
|
||||
<h1>Hello from Angular App with Webpack</h1>
|
||||
|
||||
<img src="../../public/images/angular.png">
|
||||
</main>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// #docregion
|
||||
// Angular 2
|
||||
// Angular
|
||||
import '@angular/platform-browser';
|
||||
import '@angular/platform-browser-dynamic';
|
||||
import '@angular/core';
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue