Merge remote-tracking branch 'remotes/angular.io/master'

# Conflicts:
#	README.md
#	public/contribute.jade
#	public/docs/_examples/cb-component-communication/e2e-spec.ts
#	public/docs/ts/latest/glossary.jade
#	public/docs/ts/latest/guide/architecture.jade
#	public/docs/ts/latest/guide/forms.jade
#	public/docs/ts/latest/guide/npm-packages.jade
#	public/docs/ts/latest/guide/template-syntax.jade
#	public/docs/ts/latest/guide/webpack.jade
#	public/docs/ts/latest/quickstart.jade
#	public/docs/ts/latest/tutorial/toh-pt5.jade
This commit is contained in:
Zhimin YE (Rex) 2016-06-01 09:51:27 +01:00
commit 72efc8f055
83 changed files with 1254 additions and 937 deletions

View File

@ -18,7 +18,8 @@ before_script:
install: install:
- npm install --no-optional - npm install --no-optional
- npm install --prefix public/docs/_examples - npm install --prefix public/docs/_examples
- npm run webdriver:update --prefix public/docs/_examples - npm install --prefix public/docs/_examples/_protractor
- npm run webdriver:update --prefix public/docs/_examples/_protractor
- gulp add-example-boilerplate - gulp add-example-boilerplate
script: script:
- gulp $SCRIPT - gulp $SCRIPT

View File

@ -22,6 +22,10 @@
"source": "/docs/ts/latest/guide/setup.html", "source": "/docs/ts/latest/guide/setup.html",
"destination": "/docs/ts/latest/index.html" "destination": "/docs/ts/latest/index.html"
}, },
{
"source": "/docs/ts/latest/testing",
"destination": "/docs/ts/latest/guide/testing.html"
},
{ {
"source": "/cheatsheet", "source": "/cheatsheet",
"destination": "/docs/ts/latest/guide/cheatsheet.html" "destination": "/docs/ts/latest/guide/cheatsheet.html"

View File

@ -38,6 +38,7 @@ var TEMP_PATH = './_temp';
var DOCS_PATH = path.join(PUBLIC_PATH, 'docs'); var DOCS_PATH = path.join(PUBLIC_PATH, 'docs');
var EXAMPLES_PATH = path.join(DOCS_PATH, '_examples'); var EXAMPLES_PATH = path.join(DOCS_PATH, '_examples');
var EXAMPLES_PROTRACTOR_PATH = path.join(EXAMPLES_PATH, '_protractor');
var NOT_API_DOCS_GLOB = path.join(PUBLIC_PATH, './{docs/*/latest/!(api),!(docs)}/**/*'); var NOT_API_DOCS_GLOB = path.join(PUBLIC_PATH, './{docs/*/latest/!(api),!(docs)}/**/*');
var RESOURCES_PATH = path.join(PUBLIC_PATH, 'resources'); var RESOURCES_PATH = path.join(PUBLIC_PATH, 'resources');
var LIVE_EXAMPLES_PATH = path.join(RESOURCES_PATH, 'live-examples'); var LIVE_EXAMPLES_PATH = path.join(RESOURCES_PATH, 'live-examples');
@ -86,107 +87,134 @@ var _exampleBoilerplateFiles = [
var _exampleDartWebBoilerPlateFiles = ['styles.css']; var _exampleDartWebBoilerPlateFiles = ['styles.css'];
var _exampleProtractorBoilerplateFiles = [
'tsconfig.json'
];
/**
* Run Protractor End-to-End Specs for Doc Samples
* Alias for 'run-e2e-tests'
*/
gulp.task('e2e', runE2e);
gulp.task('run-e2e-tests', runE2e);
/** /**
* Run Protractor End-to-End Tests for Doc Samples * Run Protractor End-to-End Tests for Doc Samples
* *
* Flags * Flags
* --filter to filter/select _example app subdir names * --filter to filter/select _example app subdir names
* e.g. gulp run-e2e-tests --filter=foo // all example apps with 'foo' in their folder names. * e.g. gulp e2e --filter=foo // all example apps with 'foo' in their folder names.
* *
* --fast by-passes the npm install and webdriver update * --fast by-passes the npm install and webdriver update
* Use it for repeated test runs (but not the FIRST run) * Use it for repeated test runs (but not the FIRST run)
* e.g. gulp run-e2e-tests --fast * e.g. gulp e2e --fast
* *
* --lang to filter by code language * --lang to filter by code language
* e.g. gulp run-e2e-tests --lang=ts // only TypeScript apps * e.g. gulp e2e --lang=ts // only TypeScript apps
* default is (ts|js) * default is (ts|js)
* all means (ts|js|dart) * all means (ts|js|dart)
*/ */
gulp.task('run-e2e-tests', function() { function runE2e() {
var promise; var promise;
if (argv.fast) { if (argv.fast) {
// fast; skip all setup // fast; skip all setup
promise = Promise.resolve(true); promise = Promise.resolve(true);
} else { } else {
// Not 'fast'; do full setup /*
// Not 'fast'; do full setup
var spawnInfo = spawnExt('npm', ['install'], { cwd: EXAMPLES_PATH}); var spawnInfo = spawnExt('npm', ['install'], { cwd: EXAMPLES_PATH});
promise = spawnInfo.promise.then(function() { promise = spawnInfo.promise.then(function() {
copyExampleBoilerplate(); copyExampleBoilerplate();
spawnInfo = spawnExt('npm', ['run', 'webdriver:update'], {cwd: EXAMPLES_PATH}); spawnInfo = spawnExt('npm', ['run', 'webdriver:update'], {cwd: EXAMPLES_PATH});
return spawnInfo.promise; return spawnInfo.promise;
}); });
} */
// Not 'fast'; do full setup
gutil.log('runE2e: install _protractor stuff');
var spawnInfo = spawnExt('npm', ['install'], { cwd: EXAMPLES_PROTRACTOR_PATH});
promise = spawnInfo.promise
.then(function() {
gutil.log('runE2e: install _examples stuff');
spawnInfo = spawnExt('npm', ['install'], { cwd: EXAMPLES_PATH})
return spawnInfo.promise;
})
.then(function() {
copyExampleBoilerplate();
gutil.log('runE2e: update webdriver');
spawnInfo = spawnExt('npm', ['run', 'webdriver:update'], {cwd: EXAMPLES_PROTRACTOR_PATH});
return spawnInfo.promise;
});
};
var outputFile = path.join(process.cwd(), 'protractor-results.txt');
promise.then(function() { promise.then(function() {
return findAndRunE2eTests(argv.filter); return findAndRunE2eTests(argv.filter, outputFile);
}).then(function(status) { }).then(function(status) {
reportStatus(status); reportStatus(status, outputFile);
if (status.failed.length > 0){ if (status.failed.length > 0){
return Promise.reject('Some test suites failed'); return Promise.reject('Some test suites failed');
} }
}).catch(function(e) { }).catch(function(e) {
gutil.log(e); gutil.log(e);
process.exit(1); process.exitCode = 1;
}); });
}); return promise;
}
// finds all of the *e2e-spec.tests under the _examples folder along // finds all of the *e2e-spec.tests under the _examples folder along
// with the corresponding apps that they should run under. Then run // with the corresponding apps that they should run under. Then run
// each app/spec collection sequentially. // each app/spec collection sequentially.
function findAndRunE2eTests(filter) { function findAndRunE2eTests(filter, outputFile) {
// create an output file with header.
var lang = (argv.lang || '(ts|js)').toLowerCase(); var lang = (argv.lang || '(ts|js)').toLowerCase();
if (lang === 'all') { lang = '(ts|js|dart)'; } if (lang === 'all') { lang = '(ts|js|dart)'; }
var startTime = new Date().getTime(); var startTime = new Date().getTime();
// create an output file with header.
var outputFile = path.join(process.cwd(), 'protractor-results.txt');
var header = `Doc Sample Protractor Results for ${lang} on ${new Date().toLocaleString()}\n`; var header = `Doc Sample Protractor Results for ${lang} on ${new Date().toLocaleString()}\n`;
header += argv.fast ? header += argv.fast ?
' Fast Mode (--fast): no npm install, webdriver update, or boilerplate copy\n' : ' Fast Mode (--fast): no npm install, webdriver update, or boilerplate copy\n' :
' Slow Mode: npm install, webdriver update, and boilerplate copy\n'; ' Slow Mode: npm install, webdriver update, and boilerplate copy\n';
header += ` Filter: ${filter ? filter : 'All tests'}\n\n`; header += ` Filter: ${filter ? filter : 'All tests'}\n\n`;
fs.writeFileSync(outputFile, header); fs.writeFileSync(outputFile, header);
// create an array of combos where each // create an array of combos where each
// combo consists of { examplePath: ... , protractorConfigFilename: ... } // combo consists of { examplePath: ... , protractorConfigFilename: ... }
var exeConfigs = []; var examplePaths = [];
var e2eSpecPaths = getE2eSpecPaths(EXAMPLES_PATH); var e2eSpecPaths = getE2eSpecPaths(EXAMPLES_PATH);
var srcConfig = path.join(EXAMPLES_PATH, 'protractor.config.js'); e2eSpecPaths.forEach(function(specPath) {
e2eSpecPaths.forEach(function(specPath) {
var destConfig = path.join(specPath, 'protractor.config.js'); var destConfig = path.join(specPath, 'protractor.config.js');
fsExtra.copySync(srcConfig, destConfig);
// get all of the examples under each dir where a pcFilename is found // get all of the examples under each dir where a pcFilename is found
examplePaths = getExamplePaths(specPath, true); localExamplePaths = getExamplePaths(specPath, true);
// Filter by language // Filter by language
examplePaths = examplePaths.filter(function (fn) { localExamplePaths = localExamplePaths.filter(function (fn) {
return fn.match('/'+lang+'$') != null; return fn.match('/'+lang+'$') != null;
}); });
if (filter) { if (filter) {
examplePaths = examplePaths.filter(function (fn) { localExamplePaths = localExamplePaths.filter(function (fn) {
return fn.match(filter) != null; return fn.match(filter) != null;
}) })
} }
examplePaths.forEach(function(exPath) { localExamplePaths.forEach(function(examplePath) {
exeConfigs.push( { examplePath: exPath, protractorConfigFilename: destConfig }); examplePaths.push(examplePath);
}) })
}); });
// run the tests sequentially // run the tests sequentially
var status = { passed: [], failed: [] }; var status = { passed: [], failed: [] };
return exeConfigs.reduce(function (promise, combo) { return examplePaths.reduce(function (promise, examplePath) {
return promise.then(function () { return promise.then(function () {
var isDart = combo.examplePath.indexOf('/dart') > -1; var isDart = examplePath.indexOf('/dart') > -1;
var runTests = isDart ? runE2eDartTests : runE2eTsTests; var runTests = isDart ? runE2eDartTests : runE2eTsTests;
return runTests(combo.examplePath, combo.protractorConfigFilename, outputFile).then(function(ok) { return runTests(examplePath, outputFile).then(function(ok) {
var arr = ok ? status.passed : status.failed; var arr = ok ? status.passed : status.failed;
arr.push(combo.examplePath); arr.push(examplePath);
}) })
}); });
}, Q.resolve()).then(function() { }, Q.resolve()).then(function() {
var stopTime = new Date().getTime(); var stopTime = new Date().getTime();
status.elapsedTime = (stopTime - startTime)/1000; status.elapsedTime = (stopTime - startTime)/1000;
fs.appendFileSync(outputFile, '\nElaped Time: ' + status.elapsedTime + ' seconds');
return status; return status;
}); });
} }
@ -194,15 +222,15 @@ function findAndRunE2eTests(filter) {
// start the example in appDir; then run protractor with the specified // start the example in appDir; then run protractor with the specified
// fileName; then shut down the example. All protractor output is appended // fileName; then shut down the example. All protractor output is appended
// to the outputFile. // to the outputFile.
function runE2eTsTests(appDir, protractorConfigFilename, outputFile) { function runE2eTsTests(appDir, outputFile) {
// start the app // start the app
var appRunSpawnInfo = spawnExt('npm',['run','http-server:e2e', '--', '-s' ], { cwd: appDir }); var appRunSpawnInfo = spawnExt('npm',['run','http-server:e2e', '--', '-s' ], { cwd: appDir });
var tscRunSpawnInfo = spawnExt('npm',['run','tsc'], { cwd: appDir }); var tscRunSpawnInfo = spawnExt('npm',['run','tsc'], { cwd: appDir });
return runProtractor(tscRunSpawnInfo.promise, appDir, appRunSpawnInfo, protractorConfigFilename, outputFile); return runProtractor(tscRunSpawnInfo.promise, appDir, appRunSpawnInfo, outputFile);
} }
function runProtractor(prepPromise, appDir, appRunSpawnInfo, protractorConfigFilename, outputFile) { function runProtractor(prepPromise, appDir, appRunSpawnInfo, outputFile) {
return prepPromise return prepPromise
.catch(function(){ .catch(function(){
var emsg = `AppDir failed during compile: ${appDir}\n\n`; var emsg = `AppDir failed during compile: ${appDir}\n\n`;
@ -212,10 +240,10 @@ function runProtractor(prepPromise, appDir, appRunSpawnInfo, protractorConfigFil
}) })
.then(function (data) { .then(function (data) {
// start protractor // start protractor
var pcFilename = path.resolve(protractorConfigFilename); // need to resolve because we are going to be running from a different dir var specFilename = path.resolve(`${appDir}/../e2e-spec.ts`);
var spawnInfo = spawnExt('npm', [ 'run', 'protractor', '--', pcFilename, var spawnInfo = spawnExt('npm', [ 'run', 'protractor', '--', 'protractor.config.js',
'--params.appDir=' + appDir, '--params.outputFile=' + outputFile], { cwd: EXAMPLES_PATH }); `--specs=${specFilename}`, '--params.appDir=' + appDir, '--params.outputFile=' + outputFile], { cwd: EXAMPLES_PROTRACTOR_PATH });
return spawnInfo.promise return spawnInfo.promise;
}) })
.then( .then(
function() { return finish(true);}, function() { return finish(true);},
@ -233,7 +261,7 @@ function runProtractor(prepPromise, appDir, appRunSpawnInfo, protractorConfigFil
// start the server in appDir/build/web; then run protractor with the specified // start the server in appDir/build/web; then run protractor with the specified
// fileName; then shut down the example. All protractor output is appended // fileName; then shut down the example. All protractor output is appended
// to the outputFile. // to the outputFile.
function runE2eDartTests(appDir, protractorConfigFilename, outputFile) { function runE2eDartTests(appDir, outputFile) {
var deployDir = path.resolve(path.join(appDir, 'build/web')); var deployDir = path.resolve(path.join(appDir, 'build/web'));
gutil.log('AppDir for Dart e2e: ' + appDir); gutil.log('AppDir for Dart e2e: ' + appDir);
gutil.log('Deploying from: ' + deployDir); gutil.log('Deploying from: ' + deployDir);
@ -247,24 +275,28 @@ function runE2eDartTests(appDir, protractorConfigFilename, outputFile) {
var prepPromise = pubUpgradeSpawnInfo.promise.then(function (data) { var prepPromise = pubUpgradeSpawnInfo.promise.then(function (data) {
return spawnExt('pub', ['build'], { cwd: appDir }).promise; return spawnExt('pub', ['build'], { cwd: appDir }).promise;
}); });
return runProtractor(prepPromise, appDir, appRunSpawnInfo, protractorConfigFilename, outputFile); return runProtractor(prepPromise, appDir, appRunSpawnInfo, outputFile);
} }
function reportStatus(status) { function reportStatus(status, outputFile) {
gutil.log('Suites passed:'); var log = [''];
log.push('Suites passed:');
status.passed.forEach(function(val) { status.passed.forEach(function(val) {
gutil.log(' ' + val); log.push(' ' + val);
}); });
if (status.failed.length == 0) { if (status.failed.length == 0) {
gutil.log('All tests passed'); log.push('All tests passed');
} else { } else {
gutil.log('Suites failed:'); log.push('Suites failed:');
status.failed.forEach(function (val) { status.failed.forEach(function (val) {
gutil.log(' ' + val); log.push(' ' + val);
}); });
} }
gutil.log('Elapsed time: ' + status.elapsedTime + ' seconds'); log.push('\nElapsed time: ' + status.elapsedTime + ' seconds');
var log = log.join('\n');
gutil.log(log);
fs.appendFileSync(outputFile, log);
} }
// returns both a promise and the spawned process so that it can be killed if needed. // returns both a promise and the spawned process so that it can be killed if needed.
@ -312,7 +344,7 @@ gulp.task('help', taskListing.withFilters(function(taskName) {
return shouldRemove; return shouldRemove;
})); }));
// requires admin access // requires admin access because it adds symlinks
gulp.task('add-example-boilerplate', function() { gulp.task('add-example-boilerplate', function() {
var realPath = path.join(EXAMPLES_PATH, '/node_modules'); var realPath = path.join(EXAMPLES_PATH, '/node_modules');
var nodeModulesPaths = getNodeModulesPaths(EXAMPLES_PATH); var nodeModulesPaths = getNodeModulesPaths(EXAMPLES_PATH);
@ -332,11 +364,18 @@ gulp.task('add-example-boilerplate', function() {
return copyExampleBoilerplate(); return copyExampleBoilerplate();
}); });
// copies boilerplate files to locations
// where an example app is found
gulp.task('_copy-example-boilerplate', copyExampleBoilerplate);
// copies boilerplate files to locations // copies boilerplate files to locations
// where an example app is found // where an example app is found
// also copies certain web files (e.g., styles.css) to ~/_examples/**/dart/**/web // also copies certain web files (e.g., styles.css) to ~/_examples/**/dart/**/web
// also copies protractor.config.js file // also copies protractor.config.js file
function copyExampleBoilerplate() { function copyExampleBoilerplate() {
gutil.log('Copying example boilerplate files');
var sourceFiles = _exampleBoilerplateFiles.map(function(fn) { var sourceFiles = _exampleBoilerplateFiles.map(function(fn) {
return path.join(EXAMPLES_PATH, fn); return path.join(EXAMPLES_PATH, fn);
}); });
@ -351,12 +390,14 @@ function copyExampleBoilerplate() {
.then(function() { .then(function() {
return copyFiles(dartWebSourceFiles, dartExampleWebPaths); return copyFiles(dartWebSourceFiles, dartExampleWebPaths);
}) })
// copy protractor.config.js from _examples dir to each subdir that // copy files from _examples/_protractor dir to each subdir that
// contains a e2e-spec file. // contains a e2e-spec file.
.then(function() { .then(function() {
var sourceFiles = [ path.join(EXAMPLES_PATH, 'protractor.config.js') ]; var protractorSourceFiles =
_exampleProtractorBoilerplateFiles
.map(function(name) {return path.join(EXAMPLES_PROTRACTOR_PATH, name);});;
var e2eSpecPaths = getE2eSpecPaths(EXAMPLES_PATH); var e2eSpecPaths = getE2eSpecPaths(EXAMPLES_PATH);
return copyFiles(sourceFiles, e2eSpecPaths); return copyFiles(protractorSourceFiles, e2eSpecPaths);
}); });
} }
@ -371,6 +412,15 @@ gulp.task('remove-example-boilerplate', function() {
fsUtils.removeSymlink(linkPath); fsUtils.removeSymlink(linkPath);
}); });
deleteExampleBoilerPlate();
});
// deletes boilerplate files that were added by copyExampleBoilerplate
// from locations where an example app is found
gulp.task('_delete-example-boilerplate', deleteExampleBoilerPlate);
function deleteExampleBoilerPlate() {
gutil.log('Deleting example boilerplate files');
var examplePaths = getExamplePaths(EXAMPLES_PATH); var examplePaths = getExamplePaths(EXAMPLES_PATH);
var dartExampleWebPaths = getDartExampleWebPaths(EXAMPLES_PATH); var dartExampleWebPaths = getDartExampleWebPaths(EXAMPLES_PATH);
@ -379,10 +429,11 @@ gulp.task('remove-example-boilerplate', function() {
return deleteFiles(_exampleDartWebBoilerPlateFiles, dartExampleWebPaths); return deleteFiles(_exampleDartWebBoilerPlateFiles, dartExampleWebPaths);
}) })
.then(function() { .then(function() {
var protractorFiles = _exampleProtractorBoilerplateFiles;
var e2eSpecPaths = getE2eSpecPaths(EXAMPLES_PATH); var e2eSpecPaths = getE2eSpecPaths(EXAMPLES_PATH);
return deleteFiles(['protractor.config.js'], e2eSpecPaths); return deleteFiles(protractorFiles, e2eSpecPaths);
}) });
}); }
gulp.task('serve-and-sync', ['build-docs'], function (cb) { gulp.task('serve-and-sync', ['build-docs'], function (cb) {
// watchAndSync({devGuide: true, apiDocs: true, apiExamples: true, localFiles: true}, cb); // watchAndSync({devGuide: true, apiDocs: true, apiExamples: true, localFiles: true}, cb);
@ -640,7 +691,7 @@ function linkChecker(options) {
var handlers = { var handlers = {
robots: function(robots, customData){}, robots: function(robots, customData){},
html: function(tree, robots, response, pageUrl, customData){ html: function(tree, robots, response, pageUrl, customData){
//gutil.log('Scanning ' + pageUrl);docs/ts/latest/api/core/ // gutil.log('Scanning ' + pageUrl);
}, },
junk: function(result, customData){}, junk: function(result, customData){},
@ -657,8 +708,8 @@ function linkChecker(options) {
} }
var msg = '\n [' + result.html.location.line + ', ' + result.brokenReason + '] ' + result.url.resolved; var msg = '\n [' + result.html.location.line + ', ' + result.brokenReason + '] ' + result.url.resolved;
fs.appendFileSync(outputFile, msg); fs.appendFileSync(outputFile, msg);
//gutil.log(msg); // gutil.log(msg);
//gutil.log(result); // gutil.log(result);
}, },
page: function(error, pageUrl, customData){}, page: function(error, pageUrl, customData){},
@ -686,8 +737,11 @@ function linkChecker(options) {
var startTime = new Date().getTime(); var startTime = new Date().getTime();
try { try {
gutil.log('link checker started');
siteChecker.enqueue(siteUrl, customData); siteChecker.enqueue(siteUrl, customData);
} catch (err) { } catch (err) {
gutil.log('link checker died');
console.error('link checker died', err);
deferred.reject(err); deferred.reject(err);
} }
return deferred.promise; return deferred.promise;
@ -744,7 +798,7 @@ function deleteFiles(baseFileNames, destPaths) {
// TODO: filter out all paths that are subdirs of another // TODO: filter out all paths that are subdirs of another
// path in the result. // path in the result.
function getE2eSpecPaths(basePath) { function getE2eSpecPaths(basePath) {
var paths = getPaths(basePath, '*e2e-spec.js', true); var paths = getPaths(basePath, '*e2e-spec.+(js|ts)', true);
return _.uniq(paths); return _.uniq(paths);
} }

View File

@ -322,7 +322,7 @@
"name": "Elad Bezalel", "name": "Elad Bezalel",
"picture": "/resources/images/bios/eladbezalel.jpg", "picture": "/resources/images/bios/eladbezalel.jpg",
"website": "https://github.com/EladBezalel", "website": "https://github.com/EladBezalel",
"bio": "Elad is a fullstack developer with a very storng love for design. Since 8 years old, he's been designing in Photoshop and later on fell in love with programing. This strong bond between design and computer programming gave birth to a new kind of love. And he is currently doing the combination of both, as a core member of the ngMaterial project.", "bio": "Elad is a fullstack developer with a very strong love for design. Since 8 years old, he's been designing in Photoshop and later on fell in love with programing. This strong bond between design and computer programming gave birth to a new kind of love. And he is currently doing the combination of both, as a core member of the ngMaterial project.",
"type": "Community" "type": "Community"
}, },

View File

@ -27,7 +27,7 @@
"devDependencies": { "devDependencies": {
"archiver": "^0.16.0", "archiver": "^0.16.0",
"assert-plus": "^0.1.5", "assert-plus": "^0.1.5",
"broken-link-checker": "0.7.0", "broken-link-checker": "0.7.1",
"browser-sync": "^2.9.3", "browser-sync": "^2.9.3",
"canonical-path": "0.0.2", "canonical-path": "0.0.2",
"cross-spawn": "^2.1.0", "cross-spawn": "^2.1.0",

View File

@ -24,13 +24,18 @@
//- TS arrays vs. Dart lists //- TS arrays vs. Dart lists
- var _Array = 'Array'; - var _Array = 'Array';
- var _array = 'array'; - var _array = 'array';
//- Deprecate now that we have the articles _a and _an
- var _an_array = 'an array';
//- Promise vs. Future, etc //- Promise vs. Future, etc
- var _Promise = 'Promise'; - var _Promise = 'Promise';
- var _PromiseUrl = 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise';
- var _PromiseLinked = '<a href="' + _PromiseUrl + '">' + _Promise + '</a>';
- var _Observable = 'Observable'; - var _Observable = 'Observable';
//- Directories & folders
- var _appDir = 'app';
- var _indexHtmlDir = 'project root';
- var _mainDir = _appDir;
//- Location of sample code //- Location of sample code
- var _liveLink = 'live link'; - var _liveLink = 'live link';

View File

@ -34,9 +34,9 @@
p Our goal is to deliver a lean, lightweight set of Angular-based UI elements that implement the material design specification for use in Angular single-page applications (SPAs). p Our goal is to deliver a lean, lightweight set of Angular-based UI elements that implement the material design specification for use in Angular single-page applications (SPAs).
p 我们的目标是精益、轻量级、基于Angular的一组UI组件它们实现了material设计规范可以用在Angular单页面应用中。 p 我们的目标是精益、轻量级、基于Angular的一组UI组件它们实现了material设计规范可以用在Angular单页面应用中。
a(href="https://github.com/angular/material/blob/master/CONTRIBUTING.md" class="button" md-button) Contribute to Angular Material a(href="https://github.com/angular/material/blob/master/.github/CONTRIBUTING.md" class="button" md-button) Contribute to Angular Material
a(href="https://github.com/angular/material/blob/master/CONTRIBUTING.md" class="button" md-button). a(href="https://github.com/angular/material/blob/master/.github/CONTRIBUTING.md" class="button" md-button) 帮助Angular Material
帮助Angular Material
.l-sub-section .l-sub-section
h3 AngularFire h3 AngularFire

View File

@ -0,0 +1,14 @@
/// <reference path="typings/index.d.ts" />
// Defined in protractor.config.js
declare function setProtractorToNg1Mode(): void;
declare function sendKeys(element: protractor.ElementFinder, str: string): webdriver.promise.Promise<void>;
declare function describeIf(cond: boolean, name: string, func: Function): void;
declare function itIf(cond: boolean, name: string, func: Function): void;
declare namespace protractor {
interface IBrowser {
appIsTs: boolean;
appIsJs: boolean;
}
}

View File

@ -0,0 +1,19 @@
{
"name": "angular2-examples-protractor",
"version": "1.0.0",
"description": "Manage _protractor folder installations",
"scripts": {
"postinstall": "typings install",
"typings": "typings",
"protractor": "protractor",
"webdriver:update": "webdriver-manager update"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"protractor": "^3.3.0",
"typings": "^1.0.4"
},
"repository": {}
}

View File

@ -26,10 +26,6 @@ exports.config = {
// Framework to use. Jasmine is recommended. // Framework to use. Jasmine is recommended.
framework: 'jasmine', framework: 'jasmine',
// Spec patterns are relative to this config file
specs: ['**/*e2e-spec.js' ],
// For angular2 tests // For angular2 tests
useAllAngular2AppRoots: true, useAllAngular2AppRoots: true,
@ -70,7 +66,7 @@ exports.config = {
global.setProtractorToNg1Mode = function() { global.setProtractorToNg1Mode = function() {
browser.useAllAngular2AppRoots = false; browser.useAllAngular2AppRoots = false;
browser.rootEl = 'body'; browser.rootEl = 'body';
var disableNgAnimate = function() { var disableNgAnimate = function() {
angular.module('disableNgAnimate', []).run(['$animate', function($animate) { angular.module('disableNgAnimate', []).run(['$animate', function($animate) {
$animate.enabled(false); $animate.enabled(false);
@ -86,6 +82,11 @@ exports.config = {
defaultTimeoutInterval: 10000, defaultTimeoutInterval: 10000,
showTiming: true, showTiming: true,
print: function() {} print: function() {}
},
beforeLaunch: function() {
// add TS support for specs
require('ts-node').register();
} }
}; };
@ -117,7 +118,7 @@ function sendKeys(element, str) {
} }
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;
var _root = { appDir: options.appDir, suites: [] }; var _root = { appDir: options.appDir, suites: [] };
@ -141,7 +142,7 @@ function Reporter(options) {
}; };
this.specStarted = function(spec) { this.specStarted = function(spec) {
}; };
this.specDone = function(spec) { this.specDone = function(spec) {

View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
},
"files": [
"e2e-spec.ts"
]
}

View File

@ -0,0 +1,9 @@
{
"globalDependencies": {
"angular-protractor": "registry:dt/angular-protractor#1.5.0+20160425143459",
"core-js": "registry:dt/core-js#0.0.0+20160317120654",
"jasmine": "registry:dt/jasmine#2.2.0+20160505161446",
"node": "registry:dt/node#4.0.0+20160509154515",
"selenium-webdriver": "registry:dt/selenium-webdriver#2.44.0+20160317120654"
}
}

View File

@ -1,48 +1,49 @@
/// <reference path='../_protractor/e2e.d.ts' />
describe('Architecture', function () { describe('Architecture', function () {
var _title = "Hero List"; let title = 'Hero List';
beforeAll(function () { beforeAll(function () {
browser.get(''); browser.get('');
}); });
function itReset(name, func) { function itReset(name: string, func: (v: void) => any) {
it(name, function() { it(name, function() {
browser.get('').then(func); browser.get('').then(func);
}); });
} }
it('should display correct title: ' + _title, function () { it('should display correct title: ' + title, function () {
expect(element(by.css('h2')).getText()).toEqual(_title); expect(element(by.css('h2')).getText()).toEqual(title);
}); });
it('should display correct detail after selection', function() { it('should display correct detail after selection', function() {
var detailView = element(by.css('hero-detail')); let detailView = element(by.css('hero-detail'));
expect(detailView.isPresent()).toBe(false); expect(detailView.isPresent()).toBe(false);
// select the 2nd element // select the 2nd element
var selectEle = element.all(by.css('hero-list > div')).get(1); let selectEle = element.all(by.css('hero-list > div')).get(1);
selectEle.click().then(function() { selectEle.click().then(function() {
return selectEle.getText(); return selectEle.getText();
}).then(function(selectedHeroName) { }).then(function(selectedHeroName) {
// works but too specific if we change the app // works but too specific if we change the app
// expect(selectedHeroName).toEqual('Mr. Nice'); // expect(selectedHeroName).toEqual('Mr. Nice');
expect(detailView.isDisplayed()).toBe(true); expect(detailView.isDisplayed()).toBe(true);
var detailTitleEle = element(by.css('hero-detail > h4')); let detailTitleEle = element(by.css('hero-detail > h4'));
expect(detailTitleEle.getText()).toContain(selectedHeroName); expect(detailTitleEle.getText()).toContain(selectedHeroName);
}); });
}) });
itReset('should display correct detail after modification', function() { itReset('should display correct detail after modification', function() {
var detailView = element(by.css('hero-detail')); let detailView = element(by.css('hero-detail'));
expect(detailView.isPresent()).toBe(false); expect(detailView.isPresent()).toBe(false);
// select the 2nd element // select the 2nd element
var selectEle = element.all(by.css('hero-list > div')).get(1); let selectEle = element.all(by.css('hero-list > div')).get(1);
selectEle.click().then(function () { selectEle.click().then(function () {
return selectEle.getText(); return selectEle.getText();
}).then(function (selectedHeroName) { }).then(function (selectedHeroName) {
var detailTitleEle = element(by.css('hero-detail > h4')); let detailTitleEle = element(by.css('hero-detail > h4'));
expect(detailTitleEle.getText()).toContain(selectedHeroName); expect(detailTitleEle.getText()).toContain(selectedHeroName);
var heroNameEle = element.all(by.css('hero-detail input')).get(0); let heroNameEle = element.all(by.css('hero-detail input')).get(0);
// check that both the initial selected item and the detail title reflect changes // check that both the initial selected item and the detail title reflect changes
// made to the input box. // made to the input box.
@ -56,6 +57,6 @@ describe('Architecture', function () {
// expect(heroNameEle.getText()).toEqual(selectedHeroName); // expect(heroNameEle.getText()).toEqual(selectedHeroName);
expect(heroNameEle.getAttribute('value')).toEqual(selectedHeroName + 'foo'); expect(heroNameEle.getAttribute('value')).toEqual(selectedHeroName + 'foo');
}); });
}) });
}); });

View File

@ -1,6 +1,7 @@
/// <reference path='../_protractor/e2e.d.ts' />
describe('Attribute directives', function () { describe('Attribute directives', function () {
var _title = "My First Attribute Directive"; let _title = 'My First Attribute Directive';
beforeAll(function () { beforeAll(function () {
browser.get(''); browser.get('');
@ -11,14 +12,15 @@ describe('Attribute directives', function () {
}); });
it('should be able to select green highlight', function () { it('should be able to select green highlight', function () {
var highlightedEle = element(by.cssContainingText('p', 'Highlight me')); let highlightedEle = element(by.cssContainingText('p', 'Highlight me'));
var lightGreen = "rgba(144, 238, 144, 1)"; let lightGreen = 'rgba(144, 238, 144, 1)';
expect(highlightedEle.getCssValue('background-color')).not.toEqual(lightGreen); expect(highlightedEle.getCssValue('background-color')).not.toEqual(lightGreen);
// var greenRb = element(by.cssContainingText('input', 'Green')); // let greenRb = element(by.cssContainingText('input', 'Green'));
var greenRb = element.all(by.css('input')).get(0); let greenRb = element.all(by.css('input')).get(0);
greenRb.click().then(function() { greenRb.click().then(function() {
browser.actions().mouseMove(highlightedEle).perform(); // TypeScript Todo: find the right type for highlightedEle
browser.actions().mouseMove(highlightedEle as any).perform();
expect(highlightedEle.getCssValue('background-color')).toEqual(lightGreen); expect(highlightedEle.getCssValue('background-color')).toEqual(lightGreen);
}); });

View File

@ -1,3 +1,4 @@
/// <reference path='../_protractor/e2e.d.ts' />
describe('Angular 1 to 2 Quick Reference Tests', function () { describe('Angular 1 to 2 Quick Reference Tests', function () {
beforeAll(function () { beforeAll(function () {
@ -10,7 +11,7 @@ describe('Angular 1 to 2 Quick Reference Tests', function () {
it('should display proper movie data', function () { it('should display proper movie data', function () {
// We check only a few samples // We check only a few samples
var expectedSamples = [ let expectedSamples: any[] = [
{row: 0, column: 0, element: 'img', attr: 'src', value: 'images/hero.png', contains: true}, {row: 0, column: 0, element: 'img', attr: 'src', value: 'images/hero.png', contains: true},
{row: 0, column: 2, value: 'Celeritas'}, {row: 0, column: 2, value: 'Celeritas'},
{row: 1, column: 3, matches: /Dec 1[678], 2015/}, // absorb timezone dif; we care about date format {row: 1, column: 3, matches: /Dec 1[678], 2015/}, // absorb timezone dif; we care about date format
@ -21,18 +22,18 @@ describe('Angular 1 to 2 Quick Reference Tests', function () {
]; ];
// Go through the samples // Go through the samples
var movieRows = getMovieRows(); let movieRows = getMovieRows();
for (var i = 0; i < expectedSamples.length; i++) { for (let i = 0; i < expectedSamples.length; i++) {
var sample = expectedSamples[i]; let sample = expectedSamples[i];
var tableCell = movieRows.get(sample.row) let tableCell = movieRows.get(sample.row)
.all(by.tagName('td')).get(sample.column); .all(by.tagName('td')).get(sample.column);
// Check the cell or its nested element // Check the cell or its nested element
var elementToCheck = sample.element let elementToCheck = sample.element
? tableCell.element(by.tagName(sample.element)) ? tableCell.element(by.tagName(sample.element))
: tableCell; : tableCell;
// Check element attribute or text // Check element attribute or text
var valueToCheck = sample.attr let valueToCheck = sample.attr
? elementToCheck.getAttribute(sample.attr) ? elementToCheck.getAttribute(sample.attr)
: elementToCheck.getText(); : elementToCheck.getText();
@ -48,38 +49,38 @@ describe('Angular 1 to 2 Quick Reference Tests', function () {
}); });
it('should display images after Show Poster', function () { it('should display images after Show Poster', function () {
testPosterButtonClick("Show Poster", true); testPosterButtonClick('Show Poster', true);
}); });
it('should hide images after Hide Poster', function () { it('should hide images after Hide Poster', function () {
testPosterButtonClick("Hide Poster", false); testPosterButtonClick('Hide Poster', false);
}); });
it('should display no movie when no favorite hero is specified', function () { it('should display no movie when no favorite hero is specified', function () {
testFavoriteHero(null, "Please enter your favorite hero."); testFavoriteHero(null, 'Please enter your favorite hero.');
}); });
it('should display no movie for Magneta', function () { it('should display no movie for Magneta', function () {
testFavoriteHero("Magneta", "No movie, sorry!"); testFavoriteHero('Magneta', 'No movie, sorry!');
}); });
it('should display a movie for Mr. Nice', function () { it('should display a movie for Mr. Nice', function () {
testFavoriteHero("Mr. Nice", "Excellent choice!"); testFavoriteHero('Mr. Nice', 'Excellent choice!');
}); });
function testImagesAreDisplayed(isDisplayed) { function testImagesAreDisplayed(isDisplayed: boolean) {
var expectedMovieCount = 3; let expectedMovieCount = 3;
var movieRows = getMovieRows(); let movieRows = getMovieRows();
expect(movieRows.count()).toBe(expectedMovieCount); expect(movieRows.count()).toBe(expectedMovieCount);
for (var i = 0; i < expectedMovieCount; i++) { for (let i = 0; i < expectedMovieCount; i++) {
var movieImage = movieRows.get(i).element(by.css('td > img')); let movieImage = movieRows.get(i).element(by.css('td > img'));
expect(movieImage.isDisplayed()).toBe(isDisplayed); expect(movieImage.isDisplayed()).toBe(isDisplayed);
} }
} }
function testPosterButtonClick(expectedButtonText, isDisplayed) { function testPosterButtonClick(expectedButtonText: string, isDisplayed: boolean) {
var posterButton = element(by.css('movie-list tr > th > button')); let posterButton = element(by.css('movie-list tr > th > button'));
expect(posterButton.getText()).toBe(expectedButtonText); expect(posterButton.getText()).toBe(expectedButtonText);
posterButton.click().then(function () { posterButton.click().then(function () {
@ -91,11 +92,11 @@ describe('Angular 1 to 2 Quick Reference Tests', function () {
return element.all(by.css('movie-list tbody > tr')); return element.all(by.css('movie-list tbody > tr'));
} }
function testFavoriteHero(heroName, expectedLabel) { function testFavoriteHero(heroName: string, expectedLabel: string) {
var movieListComp = element(by.tagName('movie-list')); let movieListComp = element(by.tagName('movie-list'));
var heroInput = movieListComp.element(by.tagName('input')); let heroInput = movieListComp.element(by.tagName('input'));
var favoriteHeroLabel = movieListComp.element(by.tagName('h3')); let favoriteHeroLabel = movieListComp.element(by.tagName('h3'));
var resultLabel = movieListComp.element(by.css('span > p')); let resultLabel = movieListComp.element(by.css('span > p'));
heroInput.clear().then(function () { heroInput.clear().then(function () {
sendKeys(heroInput, heroName || '').then(function () { sendKeys(heroInput, heroName || '').then(function () {

View File

@ -1,3 +1,4 @@
/// <reference path='../_protractor/e2e.d.ts' />
describe('Component Communication Cookbook Tests', function () { describe('Component Communication Cookbook Tests', function () {
@ -8,16 +9,16 @@ describe('Component Communication Cookbook Tests', function () {
describe('Parent-to-child communication', function() { describe('Parent-to-child communication', function() {
// #docregion parent-to-child // #docregion parent-to-child
// ... // ...
var _heroNames = ['Mr. IQ', 'Magneta', 'Bombasto']; let _heroNames = ['Mr. IQ', 'Magneta', 'Bombasto'];
var _masterName = 'Master'; let _masterName = 'Master';
it('should pass properties to children properly', function () { it('should pass properties to children properly', function () {
var parent = element.all(by.tagName('hero-parent')).get(0); let parent = element.all(by.tagName('hero-parent')).get(0);
var heroes = parent.all(by.tagName('hero-child')); let heroes = parent.all(by.tagName('hero-child'));
for (var i = 0; i < _heroNames.length; i++) { for (let i = 0; i < _heroNames.length; i++) {
var childTitle = heroes.get(i).element(by.tagName('h3')).getText(); let childTitle = heroes.get(i).element(by.tagName('h3')).getText();
var childDetail = heroes.get(i).element(by.tagName('p')).getText(); let childDetail = heroes.get(i).element(by.tagName('p')).getText();
expect(childTitle).toEqual(_heroNames[i] + ' says:') expect(childTitle).toEqual(_heroNames[i] + ' says:')
expect(childDetail).toContain(_masterName) expect(childDetail).toContain(_masterName)
} }
@ -30,22 +31,22 @@ describe('Component Communication Cookbook Tests', function () {
// #docregion parent-to-child-setter // #docregion parent-to-child-setter
// ... // ...
it('should display trimmed, non-empty names', function () { it('should display trimmed, non-empty names', function () {
var _nonEmptyNameIndex = 0; let _nonEmptyNameIndex = 0;
var _nonEmptyName = '"Mr. IQ"'; let _nonEmptyName = '"Mr. IQ"';
var parent = element.all(by.tagName('name-parent')).get(0); let parent = element.all(by.tagName('name-parent')).get(0);
var hero = parent.all(by.tagName('name-child')).get(_nonEmptyNameIndex); let hero = parent.all(by.tagName('name-child')).get(_nonEmptyNameIndex);
var displayName = hero.element(by.tagName('h3')).getText(); let displayName = hero.element(by.tagName('h3')).getText();
expect(displayName).toEqual(_nonEmptyName) expect(displayName).toEqual(_nonEmptyName)
}); });
it('should replace empty name with default name', function () { it('should replace empty name with default name', function () {
var _emptyNameIndex = 1; let _emptyNameIndex = 1;
var _defaultName = '"<no name set>"'; let _defaultName = '"<no name set>"';
var parent = element.all(by.tagName('name-parent')).get(0); let parent = element.all(by.tagName('name-parent')).get(0);
var hero = parent.all(by.tagName('name-child')).get(_emptyNameIndex); let hero = parent.all(by.tagName('name-child')).get(_emptyNameIndex);
var displayName = hero.element(by.tagName('h3')).getText(); let displayName = hero.element(by.tagName('h3')).getText();
expect(displayName).toEqual(_defaultName) expect(displayName).toEqual(_defaultName)
}); });
// ... // ...
@ -57,26 +58,26 @@ describe('Component Communication Cookbook Tests', function () {
// ... // ...
// Test must all execute in this exact order // Test must all execute in this exact order
it('should set expected initial values', function () { it('should set expected initial values', function () {
var actual = getActual(); let actual = getActual();
var initialLabel = "Version 1.23"; let initialLabel = 'Version 1.23';
var initialLog = 'major changed from {} to 1, minor changed from {} to 23'; let initialLog = 'major changed from {} to 1, minor changed from {} to 23';
expect(actual.label).toBe(initialLabel); expect(actual.label).toBe(initialLabel);
expect(actual.count).toBe(1); expect(actual.count).toBe(1);
expect(actual.logs.get(0).getText()).toBe(initialLog); expect(actual.logs.get(0).getText()).toBe(initialLog);
}); });
it('should set expected values after clicking "Minor" twice', function () { it('should set expected values after clicking \'Minor\' twice', function () {
var repoTag = element(by.tagName('version-parent')); let repoTag = element(by.tagName('version-parent'));
var newMinorButton = repoTag.all(by.tagName('button')).get(0); let newMinorButton = repoTag.all(by.tagName('button')).get(0);
newMinorButton.click().then(function() { newMinorButton.click().then(function() {
newMinorButton.click().then(function() { newMinorButton.click().then(function() {
var actual = getActual(); let actual = getActual();
var labelAfter2Minor = "Version 1.25"; let labelAfter2Minor = 'Version 1.25';
var logAfter2Minor = 'minor changed from 24 to 25'; let logAfter2Minor = 'minor changed from 24 to 25';
expect(actual.label).toBe(labelAfter2Minor); expect(actual.label).toBe(labelAfter2Minor);
expect(actual.count).toBe(3); expect(actual.count).toBe(3);
@ -85,15 +86,15 @@ describe('Component Communication Cookbook Tests', function () {
}); });
}); });
it('should set expected values after clicking "Major" once', function () { it('should set expected values after clicking \'Major\' once', function () {
var repoTag = element(by.tagName('version-parent')); let repoTag = element(by.tagName('version-parent'));
var newMajorButton = repoTag.all(by.tagName('button')).get(1); let newMajorButton = repoTag.all(by.tagName('button')).get(1);
newMajorButton.click().then(function() { newMajorButton.click().then(function() {
var actual = getActual(); let actual = getActual();
var labelAfterMajor = "Version 2.0"; let labelAfterMajor = 'Version 2.0';
var logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0'; let logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
expect(actual.label).toBe(labelAfterMajor); expect(actual.label).toBe(labelAfterMajor);
expect(actual.count).toBe(4); expect(actual.count).toBe(4);
@ -102,10 +103,10 @@ describe('Component Communication Cookbook Tests', function () {
}); });
function getActual() { function getActual() {
var versionTag = element(by.tagName('version-child')); let versionTag = element(by.tagName('version-child'));
var label = versionTag.element(by.tagName('h3')).getText(); let label = versionTag.element(by.tagName('h3')).getText();
var ul = versionTag.element((by.tagName('ul'))); let ul = versionTag.element((by.tagName('ul')));
var logs = ul.all(by.tagName('li')); let logs = ul.all(by.tagName('li'));
return { return {
label: label, label: label,
@ -122,28 +123,28 @@ describe('Component Communication Cookbook Tests', function () {
// #docregion child-to-parent // #docregion child-to-parent
// ... // ...
it('should not emit the event initially', function () { it('should not emit the event initially', function () {
var voteLabel = element(by.tagName('vote-taker')) let voteLabel = element(by.tagName('vote-taker'))
.element(by.tagName('h3')).getText(); .element(by.tagName('h3')).getText();
expect(voteLabel).toBe("Agree: 0, Disagree: 0"); expect(voteLabel).toBe('Agree: 0, Disagree: 0');
}); });
it('should process Agree vote', function () { it('should process Agree vote', function () {
var agreeButton1 = element.all(by.tagName('my-voter')).get(0) let agreeButton1 = element.all(by.tagName('my-voter')).get(0)
.all(by.tagName('button')).get(0); .all(by.tagName('button')).get(0);
agreeButton1.click().then(function() { agreeButton1.click().then(function() {
var voteLabel = element(by.tagName('vote-taker')) let voteLabel = element(by.tagName('vote-taker'))
.element(by.tagName('h3')).getText(); .element(by.tagName('h3')).getText();
expect(voteLabel).toBe("Agree: 1, Disagree: 0"); expect(voteLabel).toBe('Agree: 1, Disagree: 0');
}); });
}); });
it('should process Disagree vote', function () { it('should process Disagree vote', function () {
var agreeButton1 = element.all(by.tagName('my-voter')).get(1) let agreeButton1 = element.all(by.tagName('my-voter')).get(1)
.all(by.tagName('button')).get(1); .all(by.tagName('button')).get(1);
agreeButton1.click().then(function() { agreeButton1.click().then(function() {
var voteLabel = element(by.tagName('vote-taker')) let voteLabel = element(by.tagName('vote-taker'))
.element(by.tagName('h3')).getText(); .element(by.tagName('h3')).getText();
expect(voteLabel).toBe("Agree: 1, Disagree: 1"); expect(voteLabel).toBe('Agree: 1, Disagree: 1');
}); });
}); });
// ... // ...
@ -157,24 +158,24 @@ describe('Component Communication Cookbook Tests', function () {
describe('Parent calls ViewChild', function() { describe('Parent calls ViewChild', function() {
countDownTimerTests('countdown-parent-vc') countDownTimerTests('countdown-parent-vc')
}); });
function countDownTimerTests(parentTag) { function countDownTimerTests(parentTag: string) {
// #docregion countdown-timer-tests // #docregion countdown-timer-tests
// ... // ...
it('timer and parent seconds should match', function () { it('timer and parent seconds should match', function () {
var parent = element(by.tagName(parentTag)); let parent = element(by.tagName(parentTag));
var message = parent.element(by.tagName('countdown-timer')).getText(); let message = parent.element(by.tagName('countdown-timer')).getText();
browser.sleep(10); // give `seconds` a chance to catchup with `message` browser.sleep(10); // give `seconds` a chance to catchup with `message`
var seconds = parent.element(by.className('seconds')).getText(); let seconds = parent.element(by.className('seconds')).getText();
expect(message).toContain(seconds); expect(message).toContain(seconds);
}); });
it('should stop the countdown', function () { it('should stop the countdown', function () {
var parent = element(by.tagName(parentTag)); let parent = element(by.tagName(parentTag));
var stopButton = parent.all(by.tagName('button')).get(1); let stopButton = parent.all(by.tagName('button')).get(1);
stopButton.click().then(function() { stopButton.click().then(function() {
var message = parent.element(by.tagName('countdown-timer')).getText(); let message = parent.element(by.tagName('countdown-timer')).getText();
expect(message).toContain('Holding'); expect(message).toContain('Holding');
}); });
}); });
@ -187,10 +188,10 @@ describe('Component Communication Cookbook Tests', function () {
// #docregion bidirectional-service // #docregion bidirectional-service
// ... // ...
it('should announce a mission', function () { it('should announce a mission', function () {
var missionControl = element(by.tagName('mission-control')); let missionControl = element(by.tagName('mission-control'));
var announceButton = missionControl.all(by.tagName('button')).get(0); let announceButton = missionControl.all(by.tagName('button')).get(0);
announceButton.click().then(function () { announceButton.click().then(function () {
var history = missionControl.all(by.tagName('li')); let history = missionControl.all(by.tagName('li'));
expect(history.count()).toBe(1); expect(history.count()).toBe(1);
expect(history.get(0).getText()).toMatch(/Mission.* announced/); expect(history.get(0).getText()).toMatch(/Mission.* announced/);
}); });
@ -208,14 +209,14 @@ describe('Component Communication Cookbook Tests', function () {
testConfirmMission(2, 4, 'Swigert'); testConfirmMission(2, 4, 'Swigert');
}); });
function testConfirmMission(buttonIndex, expectedLogCount, astronaut) { function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
var _confirmedLog = ' confirmed the mission'; let _confirmedLog = ' confirmed the mission';
var missionControl = element(by.tagName('mission-control')); let missionControl = element(by.tagName('mission-control'));
var confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex); let confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
confirmButton.click().then(function () { confirmButton.click().then(function () {
var history = missionControl.all(by.tagName('li')); let history = missionControl.all(by.tagName('li'));
expect(history.count()).toBe(expectedLogCount); expect(history.count()).toBe(expectedLogCount);
expect(history.get(expectedLogCount-1).getText()).toBe(astronaut + _confirmedLog); expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + _confirmedLog);
}); });
} }
// ... // ...

View File

@ -1,5 +1,12 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Cookbook: component-relative paths', function () { describe('Cookbook: component-relative paths', function () {
interface Page {
title: protractor.ElementFinder;
absComp: protractor.ElementFinder;
relComp: protractor.ElementFinder;
}
function getPageStruct() { function getPageStruct() {
return { return {
title: element( by.tagName( 'h1' )), title: element( by.tagName( 'h1' )),
@ -8,7 +15,7 @@ describe('Cookbook: component-relative paths', function () {
} }
} }
var page; let page: Page;
beforeAll(function () { beforeAll(function () {
browser.get(''); browser.get('');
page = getPageStruct(); page = getPageStruct();

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Dependency Injection Cookbook', function () { describe('Dependency Injection Cookbook', function () {
beforeAll(function () { beforeAll(function () {
@ -5,104 +6,104 @@ describe('Dependency Injection Cookbook', function () {
}); });
it('should render Logged in User example', function () { it('should render Logged in User example', function () {
var loggedInUser = element.all(by.xpath('//h3[text()="Logged in user"]')).get(0); let loggedInUser = element.all(by.xpath('//h3[text()="Logged in user"]')).get(0);
expect(loggedInUser).toBeDefined(); expect(loggedInUser).toBeDefined();
}); });
it('"Bombasto" should be the logged in user', function () { it('"Bombasto" should be the logged in user', function () {
loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0); let loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0);
expect(loggedInUser).toBeDefined(); expect(loggedInUser).toBeDefined();
}); });
it('should render sorted heroes', function () { it('should render sorted heroes', function () {
var sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0); let sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0);
expect(sortedHeroes).toBeDefined(); expect(sortedHeroes).toBeDefined();
}); });
it('Mr. Nice should be in sorted heroes', function () { it('Mr. Nice should be in sorted heroes', function () {
var sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Mr. Nice" and position()=2]')).get(0); let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Mr. Nice" and position()=2]')).get(0);
expect(sortedHero).toBeDefined(); expect(sortedHero).toBeDefined();
}); });
it('RubberMan should be in sorted heroes', function () { it('RubberMan should be in sorted heroes', function () {
sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0); let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0);
expect(sortedHero).toBeDefined(); expect(sortedHero).toBeDefined();
}); });
it('Magma should be in sorted heroes', function () { it('Magma should be in sorted heroes', function () {
sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Magma"]')).get(0); let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Magma"]')).get(0);
expect(sortedHero).toBeDefined(); expect(sortedHero).toBeDefined();
}); });
it('should render Hero of the Month when DI deps are defined using provide()', function () { it('should render Hero of the Month when DI deps are defined using provide()', function () {
var heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0); let heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0);
expect(heroOfTheMonth).toBeDefined(); expect(heroOfTheMonth).toBeDefined();
}); });
it('should render Hero of the Month when DI deps are defined using provide object literal', function () { it('should render Hero of the Month when DI deps are defined using provide object literal', function () {
var heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month 2"]')).get(0); let heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month 2"]')).get(0);
expect(heroOfTheMonth).toBeDefined(); expect(heroOfTheMonth).toBeDefined();
}); });
it('should render Hero Bios', function () { it('should render Hero Bios', function () {
var heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0); let heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0);
expect(heroBios).toBeDefined(); expect(heroBios).toBeDefined();
}); });
it('should render Magma\'s description in Hero Bios', function () { it('should render Magma\'s description in Hero Bios', function () {
var magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0); let magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0);
expect(magmaText).toBeDefined(); expect(magmaText).toBeDefined();
}); });
it('should render Magma\'s phone in Hero Bios and Contacts', function () { it('should render Magma\'s phone in Hero Bios and Contacts', function () {
var magmaPhone = element.all(by.xpath('//div[text()="Phone #: 555-555-5555"]')).get(0); let magmaPhone = element.all(by.xpath('//div[text()="Phone #: 555-555-5555"]')).get(0);
expect(magmaPhone).toBeDefined(); expect(magmaPhone).toBeDefined();
}); });
it('should render Hero-of-the-Month runner-ups when DI deps are defined using provide()', function () { it('should render Hero-of-the-Month runner-ups when DI deps are defined using provide()', function () {
var runnersUp = element(by.id('rups1')).getText(); let runnersUp = element(by.id('rups1')).getText();
expect(runnersUp).toContain('RubberMan, Mr. Nice'); expect(runnersUp).toContain('RubberMan, Mr. Nice');
}); });
it('should render Hero-of-the-Month runner-ups when DI deps are defined using provide object literal', function () { it('should render Hero-of-the-Month runner-ups when DI deps are defined using provide object literal', function () {
var runnersUp = element(by.id('rups2')).getText(); let runnersUp = element(by.id('rups2')).getText();
expect(runnersUp).toContain('RubberMan, Mr. Nice'); expect(runnersUp).toContain('RubberMan, Mr. Nice');
}); });
it('should render DateLogger log entry in Hero-of-the-Month', function () { it('should render DateLogger log entry in Hero-of-the-Month', function () {
var logs = element.all(by.id('logs')).get(0).getText(); let logs = element.all(by.id('logs')).get(0).getText();
expect(logs).toContain('INFO: starting up at'); expect(logs).toContain('INFO: starting up at');
}); });
it('should highlight Hero Bios and Contacts container when mouseover', function () { it('should highlight Hero Bios and Contacts container when mouseover', function () {
var target = element(by.css('div[myHighlight="yellow"]')) let target = element(by.css('div[myHighlight="yellow"]'))
var yellow = "rgba(255, 255, 0, 1)"; let yellow = 'rgba(255, 255, 0, 1)';
expect(target.getCssValue('background-color')).not.toEqual(yellow); expect(target.getCssValue('background-color')).not.toEqual(yellow);
browser.actions().mouseMove(target).perform(); browser.actions().mouseMove(target as any as webdriver.WebElement).perform();
expect(target.getCssValue('background-color')).toEqual(yellow); expect(target.getCssValue('background-color')).toEqual(yellow);
}); });
describe('in Parent Finder', function () { describe('in Parent Finder', function () {
var cathy1 = element(by.css('alex cathy')); let cathy1 = element(by.css('alex cathy'));
var craig1 = element(by.css('alex craig')); let craig1 = element(by.css('alex craig'));
var carol1 = element(by.css('alex carol p')); let carol1 = element(by.css('alex carol p'));
var carol2 = element(by.css('barry carol p')); let carol2 = element(by.css('barry carol p'));
it('"Cathy" should find "Alex" via the component class', function () { it('"Cathy" should find "Alex" via the component class', function () {
expect(cathy1.getText()).toContain('Found Alex via the component'); expect(cathy1.getText()).toContain('Found Alex via the component');
}); });
it('"Craig" should not find "Alex" via the base class', function () { it('"Craig" should not find "Alex" via the base class', function () {
expect(craig1.getText()).toContain('Did not find Alex via the base'); expect(craig1.getText()).toContain('Did not find Alex via the base');
}); });
it('"Carol" within "Alex" should have "Alex" parent', function () { it('"Carol" within "Alex" should have "Alex" parent', function () {
expect(carol1.getText()).toContain('Alex'); expect(carol1.getText()).toContain('Alex');
}); });
it('"Carol" within "Barry" should have "Barry" parent', function () { it('"Carol" within "Barry" should have "Barry" parent', function () {
expect(carol2.getText()).toContain('Barry'); expect(carol2.getText()).toContain('Barry');
}); });
}) });
}); });

View File

@ -1,3 +1,5 @@
/// <reference path="../_protractor/e2e.d.ts" />
/* tslint:disable:quotemark */
describe('Dynamic Form', function () { describe('Dynamic Form', function () {
beforeAll(function () { beforeAll(function () {
@ -5,17 +7,17 @@ describe('Dynamic Form', function () {
}); });
it('should submit form', function () { it('should submit form', function () {
var firstNameElement = element.all(by.css('input[id=firstName]')).get(0); let firstNameElement = element.all(by.css('input[id=firstName]')).get(0);
expect(firstNameElement.getAttribute('value')).toEqual('Bombasto'); expect(firstNameElement.getAttribute('value')).toEqual('Bombasto');
var emailElement = element.all(by.css('input[id=emailAddress]')).get(0); let emailElement = element.all(by.css('input[id=emailAddress]')).get(0);
var email = 'test@test.com'; let email = 'test@test.com';
emailElement.sendKeys(email); emailElement.sendKeys(email);
expect(emailElement.getAttribute('value')).toEqual(email); expect(emailElement.getAttribute('value')).toEqual(email);
element(by.css('select option[value="solid"]')).click() element(by.css('select option[value="solid"]')).click()
var saveButton = element.all(by.css('button')).get(0); let saveButton = element.all(by.css('button')).get(0);
saveButton.click().then(function(){ saveButton.click().then(function(){
expect(element(by.xpath("//strong[contains(text(),'Saved the following values')]")).isPresent()).toBe(true); expect(element(by.xpath("//strong[contains(text(),'Saved the following values')]")).isPresent()).toBe(true);
}); });

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
// gulp run-e2e-tests --filter=cb-set-document-title // gulp run-e2e-tests --filter=cb-set-document-title
describe('Set Document Title', function () { describe('Set Document Title', function () {
@ -7,7 +8,7 @@ describe('Set Document Title', function () {
it('should set the document title', function () { it('should set the document title', function () {
var titles = [ let titles = [
'Good morning!', 'Good morning!',
'Good afternoon!', 'Good afternoon!',
'Good evening!' 'Good evening!'

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('TypeScript to Javascript tests', function () { describe('TypeScript to Javascript tests', function () {
beforeAll(function () { beforeAll(function () {
@ -21,9 +22,9 @@ describe('TypeScript to Javascript tests', function () {
}); });
it('should support optional, attribute, and query injections', function () { it('should support optional, attribute, and query injections', function () {
var app = element(by.css('hero-di-inject-additional')); let app = element(by.css('hero-di-inject-additional'));
var h1 = app.element(by.css('h1')); let h1 = app.element(by.css('h1'));
var okMsg = app.element(by.css('.ok-msg')); let okMsg = app.element(by.css('.ok-msg'));
expect(h1.getText()).toBe('Tour of Heroes'); expect(h1.getText()).toBe('Tour of Heroes');
app.element(by.buttonText('OK')).click(); app.element(by.buttonText('OK')).click();
@ -31,8 +32,8 @@ describe('TypeScript to Javascript tests', function () {
}); });
it('should support component with inputs and outputs', function () { it('should support component with inputs and outputs', function () {
var app = element(by.css('hero-io')); let app = element(by.css('hero-io'));
var confirmComponent = app.element(by.css('my-confirm')); let confirmComponent = app.element(by.css('my-confirm'));
confirmComponent.element(by.buttonText('OK')).click(); confirmComponent.element(by.buttonText('OK')).click();
expect(app.element(by.cssContainingText('span', 'OK clicked')).isPresent()).toBe(true); expect(app.element(by.cssContainingText('span', 'OK clicked')).isPresent()).toBe(true);
@ -42,8 +43,8 @@ describe('TypeScript to Javascript tests', function () {
}); });
it('should support host bindings and host listeners', function() { it('should support host bindings and host listeners', function() {
var app = element(by.css('heroes-bindings')); let app = element(by.css('heroes-bindings'));
var h1 = app.element(by.css('h1')); let h1 = app.element(by.css('h1'));
expect(app.getAttribute('class')).toBe('heading'); expect(app.getAttribute('class')).toBe('heading');
expect(app.getAttribute('title')).toBe('Tooltip content'); expect(app.getAttribute('title')).toBe('Tooltip content');
@ -52,21 +53,21 @@ describe('TypeScript to Javascript tests', function () {
expect(h1.getAttribute('class')).toBe('active'); expect(h1.getAttribute('class')).toBe('active');
h1.click(); h1.click();
browser.actions().doubleClick(h1).perform(); browser.actions().doubleClick(h1 as any as webdriver.WebElement).perform();
expect(h1.getAttribute('class')).toBe('active'); expect(h1.getAttribute('class')).toBe('active');
}); });
it('should support content and view queries', function() { it('should support content and view queries', function() {
var app = element(by.css('heroes-queries')); let app = element(by.css('heroes-queries'));
var windstorm = app.element(by.css('hero:first-child')); let windstorm = app.element(by.css('hero:first-child'));
app.element(by.buttonText('Activate')).click(); app.element(by.buttonText('Activate')).click();
expect(windstorm.element(by.css('h2')).getAttribute('class')).toBe('active'); expect(windstorm.element(by.css('h2')).getAttribute('class')).toBe('active');
expect(windstorm.element(by.css('active-label')).getText()).toBe('Active'); expect(windstorm.element(by.css('active-label')).getText()).toBe('Active');
}); });
function testTag(selector, expectedText) { function testTag(selector: string, expectedText: string) {
var component = element(by.css(selector)); let component = element(by.css(selector));
expect(component.getText()).toBe(expectedText); expect(component.getText()).toBe(expectedText);
} }

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Component Style Tests', function () { describe('Component Style Tests', function () {
beforeAll(function () { beforeAll(function () {
@ -5,8 +6,8 @@ describe('Component Style Tests', function () {
}); });
it('scopes component styles to component view', function() { it('scopes component styles to component view', function() {
var componentH1 = element(by.css('hero-app > h1')); let componentH1 = element(by.css('hero-app > h1'));
var externalH1 = element(by.css('body > h1')); let externalH1 = element(by.css('body > h1'));
expect(componentH1.getCssValue('fontWeight')).toEqual('normal'); expect(componentH1.getCssValue('fontWeight')).toEqual('normal');
expect(externalH1.getCssValue('fontWeight')).not.toEqual('normal'); expect(externalH1.getCssValue('fontWeight')).not.toEqual('normal');
@ -14,55 +15,55 @@ describe('Component Style Tests', function () {
it('allows styling :host element', function() { it('allows styling :host element', function() {
var host = element(by.css('hero-details')); let host = element(by.css('hero-details'));
expect(host.getCssValue('borderWidth')).toEqual('1px'); expect(host.getCssValue('borderWidth')).toEqual('1px');
}); });
it('supports :host() in function form', function() { it('supports :host() in function form', function() {
var host = element(by.css('hero-details')); let host = element(by.css('hero-details'));
host.element(by.buttonText('Activate')).click(); host.element(by.buttonText('Activate')).click();
expect(host.getCssValue('borderWidth')).toEqual('3px'); expect(host.getCssValue('borderWidth')).toEqual('3px');
}); });
it('allows conditional :host-context() styling', function() { it('allows conditional :host-context() styling', function() {
var h2 = element(by.css('hero-details h2')); let h2 = element(by.css('hero-details h2'));
expect(h2.getCssValue('backgroundColor')).toEqual('rgba(238, 238, 255, 1)'); // #eeeeff expect(h2.getCssValue('backgroundColor')).toEqual('rgba(238, 238, 255, 1)'); // #eeeeff
}); });
it('styles both view and content children with /deep/', function() { it('styles both view and content children with /deep/', function() {
var viewH3 = element(by.css('hero-team h3')); let viewH3 = element(by.css('hero-team h3'));
var contentH3 = element(by.css('hero-controls h3')); let contentH3 = element(by.css('hero-controls h3'));
expect(viewH3.getCssValue('fontStyle')).toEqual('italic'); expect(viewH3.getCssValue('fontStyle')).toEqual('italic');
expect(contentH3.getCssValue('fontStyle')).toEqual('italic'); expect(contentH3.getCssValue('fontStyle')).toEqual('italic');
}); });
it('includes styles loaded with CSS @import', function() { it('includes styles loaded with CSS @import', function() {
var host = element(by.css('hero-details')); let host = element(by.css('hero-details'));
expect(host.getCssValue('padding')).toEqual('10px'); expect(host.getCssValue('padding')).toEqual('10px');
}); });
it('processes template inline styles', function() { it('processes template inline styles', function() {
var button = element(by.css('hero-controls button')); let button = element(by.css('hero-controls button'));
var externalButton = element(by.css('body > button')); let externalButton = element(by.css('body > button'));
expect(button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff expect(button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff
expect(externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)'); expect(externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)');
}); });
it('processes template <link>s', function() { it('processes template <link>s', function() {
var li = element(by.css('hero-team li:first-child')); let li = element(by.css('hero-team li:first-child'));
var externalLi = element(by.css('body > ul li')); let externalLi = element(by.css('body > ul li'));
expect(li.getCssValue('listStyleType')).toEqual('square'); expect(li.getCssValue('listStyleType')).toEqual('square');
expect(externalLi.getCssValue('listStyleType')).not.toEqual('square'); expect(externalLi.getCssValue('listStyleType')).not.toEqual('square');
}); });
it('supports relative loading with moduleId', function() { it('supports relative loading with moduleId', function() {
var host = element(by.css('quest-summary')); let host = element(by.css('quest-summary'));
expect(host.getCssValue('color')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff expect(host.getCssValue('color')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff
}); });

View File

@ -1,8 +1,9 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Dependency Injection Tests', function () { describe('Dependency Injection Tests', function () {
var expectedMsg; let expectedMsg: string;
let expectedMsgRx: RegExp;
beforeAll(function () { beforeAll(function () {
browser.get(''); browser.get('');
@ -66,8 +67,8 @@ describe('Dependency Injection Tests', function () {
describe('Tests:', function() { describe('Tests:', function() {
it('Tests display as expected', function () { it('Tests display as expected', function () {
expectedMsg = /Tests passed/; expectedMsgRx = /Tests passed/;
expect(element(by.css('#tests')).getText()).toMatch(expectedMsg); expect(element(by.css('#tests')).getText()).toMatch(expectedMsgRx);
}); });
}); });
@ -88,7 +89,7 @@ describe('Dependency Injection Tests', function () {
expectedMsg = 'Hello from logger provided with useClass'; expectedMsg = 'Hello from logger provided with useClass';
expect(element(by.css('#p3')).getText()).toEqual(expectedMsg); expect(element(by.css('#p3')).getText()).toEqual(expectedMsg);
}); });
it('P3a (provide) displays as expected', function () { it('P3a (provide) displays as expected', function () {
expectedMsg = 'Hello from logger provided with {provide: Logger, useClass: Logger}'; expectedMsg = 'Hello from logger provided with {provide: Logger, useClass: Logger}';
expect(element(by.css('#p3a')).getText()).toEqual(expectedMsg); expect(element(by.css('#p3a')).getText()).toEqual(expectedMsg);
@ -147,22 +148,22 @@ describe('Dependency Injection Tests', function () {
describe('User/Heroes:', function() { describe('User/Heroes:', function() {
it('User is Bob - unauthorized', function () { it('User is Bob - unauthorized', function () {
expectedMsg = /Bob, is not authorized/; expectedMsgRx = /Bob, is not authorized/;
expect(element(by.css('#user')).getText()).toMatch(expectedMsg); expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
}); });
it('should have button', function () { it('should have button', function () {
expect(element.all(by.cssContainingText('button','Next User')) expect(element.all(by.cssContainingText('button', 'Next User'))
.get(0).isDisplayed()).toBe(true, "'Next User' button should be displayed"); .get(0).isDisplayed()).toBe(true, '\'Next User\' button should be displayed');
}); });
it('unauthorized user should have multiple unauthorized heroes', function () { it('unauthorized user should have multiple unauthorized heroes', function () {
var heroes = element.all(by.css('#unauthorized hero-list div')); let heroes = element.all(by.css('#unauthorized hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
}); });
it('unauthorized user should have no secret heroes', function () { it('unauthorized user should have no secret heroes', function () {
var heroes = element.all(by.css('#unauthorized hero-list div')); let heroes = element.all(by.css('#unauthorized hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
heroes.filter(function(elem, index){ heroes.filter(function(elem, index){
@ -170,7 +171,7 @@ describe('Dependency Injection Tests', function () {
return /secret/.test(text); return /secret/.test(text);
}); });
}).then(function(filteredElements) { }).then(function(filteredElements) {
//console.log("******Secret heroes count: "+filteredElements.length); // console.log("******Secret heroes count: "+filteredElements.length);
expect(filteredElements.length).toEqual(0); expect(filteredElements.length).toEqual(0);
}); });
}); });
@ -182,22 +183,22 @@ describe('Dependency Injection Tests', function () {
describe('after button click', function() { describe('after button click', function() {
beforeAll(function (done) { beforeAll(function (done) {
var buttonEle = element.all(by.cssContainingText('button','Next User')).get(0); let buttonEle = element.all(by.cssContainingText('button','Next User')).get(0);
buttonEle.click().then(done,done); buttonEle.click().then(done,done);
}); });
it('User is Alice - authorized', function () { it('User is Alice - authorized', function () {
expectedMsg = /Alice, is authorized/; expectedMsgRx = /Alice, is authorized/;
expect(element(by.css('#user')).getText()).toMatch(expectedMsg); expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
}); });
it('authorized user should have multiple authorized heroes ', function () { it('authorized user should have multiple authorized heroes ', function () {
var heroes = element.all(by.css('#authorized hero-list div')); let heroes = element.all(by.css('#authorized hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
}); });
it('authorized user should have secret heroes', function () { it('authorized user should have secret heroes', function () {
var heroes = element.all(by.css('#authorized hero-list div')); let heroes = element.all(by.css('#authorized hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
heroes.filter(function(elem, index){ heroes.filter(function(elem, index){
@ -205,7 +206,7 @@ describe('Dependency Injection Tests', function () {
return /secret/.test(text); return /secret/.test(text);
}); });
}).then(function(filteredElements) { }).then(function(filteredElements) {
//console.log("******Secret heroes count: "+filteredElements.length); // console.log("******Secret heroes count: "+filteredElements.length);
expect(filteredElements.length).toBeGreaterThan(0); expect(filteredElements.length).toBeGreaterThan(0);
}); });
}); });

View File

@ -1,6 +1,7 @@
/// <reference path='../_protractor/e2e.d.ts' />
describe('Displaying Data Tests', function () { describe('Displaying Data Tests', function () {
var _title = "Tour of Heroes"; let _title = 'Tour of Heroes';
var _defaultHero = 'Windstorm' let _defaultHero = 'Windstorm';
beforeAll(function () { beforeAll(function () {
browser.get(''); browser.get('');
@ -15,7 +16,7 @@ describe('Displaying Data Tests', function () {
}); });
it('should have heroes', function () { it('should have heroes', function () {
var heroEls = element.all(by.css('li')); let heroEls = element.all(by.css('li'));
expect(heroEls.count()).not.toBe(0, 'should have heroes'); expect(heroEls.count()).not.toBe(0, 'should have heroes');
}); });

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describeIf(browser.appIsTs || browser.appIsJs, 'Forms Tests', function () { describeIf(browser.appIsTs || browser.appIsJs, 'Forms Tests', function () {
beforeEach(function () { beforeEach(function () {
@ -10,51 +11,51 @@ describeIf(browser.appIsTs || browser.appIsJs, 'Forms Tests', function () {
it('should not display message before submit', function () { it('should not display message before submit', function () {
var ele = element(by.css('h2')); let ele = element(by.css('h2'));
expect(ele.isDisplayed()).toBe(false); expect(ele.isDisplayed()).toBe(false);
}); });
it('should hide form after submit', function () { it('should hide form after submit', function () {
var ele = element.all(by.css('h1')).get(0); let ele = element.all(by.css('h1')).get(0);
expect(ele.isDisplayed()).toBe(true); expect(ele.isDisplayed()).toBe(true);
var b = element.all(by.css('button[type=submit]')).get(0); let b = element.all(by.css('button[type=submit]')).get(0);
b.click().then(function() { b.click().then(function() {
expect(ele.isDisplayed()).toBe(false); expect(ele.isDisplayed()).toBe(false);
}); });
}); });
it('should display message after submit', function () { it('should display message after submit', function () {
var b = element.all(by.css('button[type=submit]')).get(0); let b = element.all(by.css('button[type=submit]')).get(0);
b.click().then(function() { b.click().then(function() {
expect(element(by.css('h2')).getText()).toContain('You submitted the following'); expect(element(by.css('h2')).getText()).toContain('You submitted the following');
}); });
}); });
it('should hide form after submit', function () { it('should hide form after submit', function () {
var alterEgoEle = element.all(by.css('input[ngcontrol=alterEgo]')).get(0); let alterEgoEle = element.all(by.css('input[ngcontrol=alterEgo]')).get(0);
expect(alterEgoEle.isDisplayed()).toBe(true); expect(alterEgoEle.isDisplayed()).toBe(true);
var submitButtonEle = element.all(by.css('button[type=submit]')).get(0); let submitButtonEle = element.all(by.css('button[type=submit]')).get(0);
submitButtonEle.click().then(function() { submitButtonEle.click().then(function() {
expect(alterEgoEle.isDisplayed()).toBe(false); expect(alterEgoEle.isDisplayed()).toBe(false);
}) });
}); });
it('should reflect submitted data after submit', function () { it('should reflect submitted data after submit', function () {
var test = 'testing 1 2 3'; let test = 'testing 1 2 3';
var newValue; let newValue: string;
var alterEgoEle = element.all(by.css('input[ngcontrol=alterEgo]')).get(0); let alterEgoEle = element.all(by.css('input[ngcontrol=alterEgo]')).get(0);
alterEgoEle.getAttribute('value').then(function(value) { alterEgoEle.getAttribute('value').then(function(value) {
// alterEgoEle.sendKeys(test); // alterEgoEle.sendKeys(test);
sendKeys(alterEgoEle, test); sendKeys(alterEgoEle, test);
newValue = value + test; newValue = value + test;
expect(alterEgoEle.getAttribute('value')).toEqual(newValue); expect(alterEgoEle.getAttribute('value')).toEqual(newValue);
}).then(function() { }).then(function() {
var b = element.all(by.css('button[type=submit]')).get(0); let b = element.all(by.css('button[type=submit]')).get(0);
return b.click(); return b.click();
}).then(function() { }).then(function() {
var alterEgoTextEle = element(by.cssContainingText('div', 'Alter Ego')); let alterEgoTextEle = element(by.cssContainingText('div', 'Alter Ego'));
expect(alterEgoTextEle.isPresent()).toBe(true, 'cannot locate "Alter Ego" label'); expect(alterEgoTextEle.isPresent()).toBe(true, 'cannot locate "Alter Ego" label');
var divEle = element(by.cssContainingText('div', newValue)); let divEle = element(by.cssContainingText('div', newValue));
expect(divEle.isPresent()).toBe(true, 'cannot locate div with this text: ' + newValue); expect(divEle.isPresent()).toBe(true, 'cannot locate div with this text: ' + newValue);
}); });
}); });

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Hierarchical dependency injection', function () { describe('Hierarchical dependency injection', function () {
beforeEach(function () { beforeEach(function () {
@ -14,7 +15,7 @@ describe('Hierarchical dependency injection', function () {
}); });
it('should change to editor view after selection', function () { it('should change to editor view after selection', function () {
var editButtonEle = element.all(by.cssContainingText('button','edit')).get(0); let editButtonEle = element.all(by.cssContainingText('button','edit')).get(0);
editButtonEle.click().then(function() { editButtonEle.click().then(function() {
expect(editButtonEle.isDisplayed()).toBe(false, "edit button should be hidden after selection"); expect(editButtonEle.isDisplayed()).toBe(false, "edit button should be hidden after selection");
}) })
@ -29,19 +30,19 @@ describe('Hierarchical dependency injection', function () {
}); });
function testEdit(shouldSave) { function testEdit(shouldSave) {
var inputEle; let inputEle;
// select 2nd ele // select 2nd ele
var heroEle = element.all(by.css('heroes-list li')).get(1); let heroEle = element.all(by.css('heroes-list li')).get(1);
// get the 2nd span which is the name of the hero // get the 2nd span which is the name of the hero
var heroNameEle = heroEle.all(by.css('hero-card span')).get(1); let heroNameEle = heroEle.all(by.css('hero-card span')).get(1);
var editButtonEle = heroEle.element(by.cssContainingText('button','edit')); let editButtonEle = heroEle.element(by.cssContainingText('button','edit'));
editButtonEle.click().then(function() { editButtonEle.click().then(function() {
inputEle = heroEle.element(by.css('hero-editor input')); inputEle = heroEle.element(by.css('hero-editor input'));
// return inputEle.sendKeys("foo"); // return inputEle.sendKeys("foo");
return sendKeys(inputEle, "foo"); return sendKeys(inputEle, "foo");
}).then(function() { }).then(function() {
buttonName = shouldSave ? 'save' : 'cancel'; buttonName = shouldSave ? 'save' : 'cancel';
var buttonEle = heroEle.element(by.cssContainingText('button', buttonName)); let buttonEle = heroEle.element(by.cssContainingText('button', buttonName));
return buttonEle.click(); return buttonEle.click();
}).then(function() { }).then(function() {
if (shouldSave) { if (shouldSave) {

View File

@ -1,4 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Homepage Hello World', function () { describe('Homepage Hello World', function () {
beforeAll(function () { beforeAll(function () {
@ -6,15 +6,15 @@ describe('Homepage Hello World', function () {
}); });
// Does it even launch? // Does it even launch?
var expectedLabel = 'Name:'; let expectedLabel = 'Name:';
it('should display the label: ' + expectedLabel, function () { it('should display the label: ' + expectedLabel, function () {
expect(element(by.css('label')).getText()).toEqual(expectedLabel); expect(element(by.css('label')).getText()).toEqual(expectedLabel);
}); });
it('should display entered name', function () { it('should display entered name', function () {
var testName = 'Bobby Joe'; let testName = 'Bobby Joe';
var newValue; let newValue;
var nameEle = element.all(by.css('input')).get(0); let nameEle = element.all(by.css('input')).get(0);
nameEle.getAttribute('value').then(function(value) { nameEle.getAttribute('value').then(function(value) {
// nameEle.sendKeys(testName); // should work but doesn't // nameEle.sendKeys(testName); // should work but doesn't
sendKeys(nameEle, testName); // utility that does work sendKeys(nameEle, testName); // utility that does work
@ -22,7 +22,7 @@ describe('Homepage Hello World', function () {
expect(nameEle.getAttribute('value')).toEqual(newValue); expect(nameEle.getAttribute('value')).toEqual(newValue);
}).then(function() { }).then(function() {
// Check the interpolated message built from name // Check the interpolated message built from name
var helloEle = element.all(by.css('h1')).get(0); let helloEle = element.all(by.css('h1')).get(0);
expect(helloEle.getText()).toEqual('Hello ' + testName + '!'); expect(helloEle.getText()).toEqual('Hello ' + testName + '!');
}); });
}); });

View File

@ -1,4 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Homepage Tabs', function () { describe('Homepage Tabs', function () {
beforeAll(function () { beforeAll(function () {
@ -6,7 +6,7 @@ describe('Homepage Tabs', function () {
}); });
// Does it even launch? // Does it even launch?
var expectedAppTitle = 'Tabs Demo'; let expectedAppTitle = 'Tabs Demo';
it('should display app title: ' + expectedAppTitle, function () { it('should display app title: ' + expectedAppTitle, function () {
expect(element(by.css('h4')).getText()).toEqual(expectedAppTitle); expect(element(by.css('h4')).getText()).toEqual(expectedAppTitle);
}); });

View File

@ -1,4 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Homepage Todo', function () { describe('Homepage Todo', function () {
beforeAll(function () { beforeAll(function () {
@ -6,7 +6,7 @@ describe('Homepage Todo', function () {
}); });
// Does it even launch? // Does it even launch?
var expectedAppTitle = 'Todo'; let expectedAppTitle = 'Todo';
it('should display app title: ' + expectedAppTitle, function () { it('should display app title: ' + expectedAppTitle, function () {
expect(element(by.css('h2')).getText()).toEqual(expectedAppTitle); expect(element(by.css('h2')).getText()).toEqual(expectedAppTitle);
}); });

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Lifecycle hooks', function () { describe('Lifecycle hooks', function () {
beforeAll(function () { beforeAll(function () {
@ -9,10 +10,10 @@ describe('Lifecycle hooks', function () {
}); });
it('should support peek-a-boo', function () { it('should support peek-a-boo', function () {
var pabComp = element(by.css('peek-a-boo-parent peek-a-boo')); let pabComp = element(by.css('peek-a-boo-parent peek-a-boo'));
expect(pabComp.isPresent()).toBe(false, "should not be able to find the 'peek-a-boo' component"); expect(pabComp.isPresent()).toBe(false, "should not be able to find the 'peek-a-boo' component");
var pabButton = element.all(by.css('peek-a-boo-parent button')).get(0); let pabButton = element.all(by.css('peek-a-boo-parent button')).get(0);
var updateHeroButton = element.all(by.css('peek-a-boo-parent button')).get(1); let updateHeroButton = element.all(by.css('peek-a-boo-parent button')).get(1);
expect(pabButton.getText()).toContain('Create Peek'); expect(pabButton.getText()).toContain('Create Peek');
pabButton.click().then(function () { pabButton.click().then(function () {
expect(pabButton.getText()).toContain('Destroy Peek'); expect(pabButton.getText()).toContain('Destroy Peek');
@ -30,12 +31,12 @@ describe('Lifecycle hooks', function () {
}); });
it('should support OnChanges hook', function () { it('should support OnChanges hook', function () {
var onChangesViewEle = element.all(by.css('on-changes div')).get(0); let onChangesViewEle = element.all(by.css('on-changes div')).get(0);
var inputEles = element.all(by.css('on-changes-parent input')); let inputEles = element.all(by.css('on-changes-parent input'));
var heroNameInputEle = inputEles.get(1); let heroNameInputEle = inputEles.get(1);
var powerInputEle = inputEles.get(0); let powerInputEle = inputEles.get(0);
var titleEle = onChangesViewEle.element(by.css('p')); let titleEle = onChangesViewEle.element(by.css('p'));
var changeLogEles = onChangesViewEle.all(by.css('div')); let changeLogEles = onChangesViewEle.all(by.css('div'));
expect(titleEle.getText()).toContain('Windstorm can sing'); expect(titleEle.getText()).toContain('Windstorm can sing');
expect(changeLogEles.count()).toEqual(2, "should start with 2 messages"); expect(changeLogEles.count()).toEqual(2, "should start with 2 messages");
@ -54,13 +55,13 @@ describe('Lifecycle hooks', function () {
}); });
it('should support DoCheck hook', function () { it('should support DoCheck hook', function () {
var doCheckViewEle = element.all(by.css('do-check div')).get(0); let doCheckViewEle = element.all(by.css('do-check div')).get(0);
var inputEles = element.all(by.css('do-check-parent input')); let inputEles = element.all(by.css('do-check-parent input'));
var heroNameInputEle = inputEles.get(1); let heroNameInputEle = inputEles.get(1);
var powerInputEle = inputEles.get(0); let powerInputEle = inputEles.get(0);
var titleEle = doCheckViewEle.element(by.css('p')); let titleEle = doCheckViewEle.element(by.css('p'));
var changeLogEles = doCheckViewEle.all(by.css('div')); let changeLogEles = doCheckViewEle.all(by.css('div'));
var logCount; let logCount;
expect(titleEle.getText()).toContain('Windstorm can sing'); expect(titleEle.getText()).toContain('Windstorm can sing');
changeLogEles.count().then(function(count) { changeLogEles.count().then(function(count) {
@ -86,12 +87,12 @@ describe('Lifecycle hooks', function () {
}); });
it('should support AfterView hooks', function () { it('should support AfterView hooks', function () {
var parentEle = element(by.tagName('after-view-parent')); let parentEle = element(by.tagName('after-view-parent'));
var buttonEle = parentEle.element(by.tagName('button')); // Reset let buttonEle = parentEle.element(by.tagName('button')); // Reset
var commentEle = parentEle.element(by.className('comment')); let commentEle = parentEle.element(by.className('comment'));
var logEles = parentEle.all(by.css('h4 ~ div')); let logEles = parentEle.all(by.css('h4 ~ div'));
var childViewInputEle = parentEle.element(by.css('my-child input')); let childViewInputEle = parentEle.element(by.css('my-child input'));
var logCount; let logCount;
expect(childViewInputEle.getAttribute('value')).toContain('Magneta'); expect(childViewInputEle.getAttribute('value')).toContain('Magneta');
expect(commentEle.isPresent()).toBe(false, 'comment should not be in DOM'); expect(commentEle.isPresent()).toBe(false, 'comment should not be in DOM');
@ -115,12 +116,12 @@ describe('Lifecycle hooks', function () {
it('should support AfterContent hooks', function () { it('should support AfterContent hooks', function () {
var parentEle = element(by.tagName('after-content-parent')); let parentEle = element(by.tagName('after-content-parent'));
var buttonEle = parentEle.element(by.tagName('button')); // Reset let buttonEle = parentEle.element(by.tagName('button')); // Reset
var commentEle = parentEle.element(by.className('comment')); let commentEle = parentEle.element(by.className('comment'));
var logEles = parentEle.all(by.css('h4 ~ div')); let logEles = parentEle.all(by.css('h4 ~ div'));
var childViewInputEle = parentEle.element(by.css('my-child input')); let childViewInputEle = parentEle.element(by.css('my-child input'));
var logCount; let logCount;
expect(childViewInputEle.getAttribute('value')).toContain('Magneta'); expect(childViewInputEle.getAttribute('value')).toContain('Magneta');
expect(commentEle.isPresent()).toBe(false, 'comment should not be in DOM'); expect(commentEle.isPresent()).toBe(false, 'comment should not be in DOM');
@ -143,11 +144,11 @@ describe('Lifecycle hooks', function () {
}); });
it('should support spy\'s OnInit & OnDestroy hooks', function () { it('should support spy\'s OnInit & OnDestroy hooks', function () {
var inputEle = element(by.css('spy-parent input')); let inputEle = element(by.css('spy-parent input'));
var addHeroButtonEle = element(by.cssContainingText('spy-parent button','Add Hero')); let addHeroButtonEle = element(by.cssContainingText('spy-parent button','Add Hero'));
var resetHeroesButtonEle = element(by.cssContainingText('spy-parent button','Reset Heroes')); let resetHeroesButtonEle = element(by.cssContainingText('spy-parent button','Reset Heroes'));
var heroEles = element.all(by.css('spy-parent div[mySpy')); let heroEles = element.all(by.css('spy-parent div[mySpy'));
var logEles = element.all(by.css('spy-parent h4 ~ div')); let logEles = element.all(by.css('spy-parent h4 ~ div'));
expect(heroEles.count()).toBe(2, 'should have two heroes displayed'); expect(heroEles.count()).toBe(2, 'should have two heroes displayed');
expect(logEles.count()).toBe(2, 'should have two log entries'); expect(logEles.count()).toBe(2, 'should have two log entries');
sendKeys(inputEle, "-test-").then(function() { sendKeys(inputEle, "-test-").then(function() {
@ -164,10 +165,10 @@ describe('Lifecycle hooks', function () {
}); });
it('should support "spy counter"', function () { it('should support "spy counter"', function () {
var updateCounterButtonEle = element(by.cssContainingText('counter-parent button','Update')); let updateCounterButtonEle = element(by.cssContainingText('counter-parent button','Update'));
var resetCounterButtonEle = element(by.cssContainingText('counter-parent button','Reset')); let resetCounterButtonEle = element(by.cssContainingText('counter-parent button','Reset'));
var textEle = element(by.css('counter-parent my-counter > div')); let textEle = element(by.css('counter-parent my-counter > div'));
var logEles = element.all(by.css('counter-parent h4 ~ div')); let logEles = element.all(by.css('counter-parent h4 ~ div'));
expect(textEle.getText()).toContain('Counter = 0'); expect(textEle.getText()).toContain('Counter = 0');
expect(logEles.count()).toBe(2, 'should start with two log entries'); expect(logEles.count()).toBe(2, 'should start with two log entries');
updateCounterButtonEle.click().then(function() { updateCounterButtonEle.click().then(function() {

View File

@ -69,6 +69,7 @@
"rimraf": "^2.5.2", "rimraf": "^2.5.2",
"style-loader": "^0.13.1", "style-loader": "^0.13.1",
"ts-loader": "^0.8.2", "ts-loader": "^0.8.2",
"ts-node": "^0.7.3",
"typescript": "^1.8.10", "typescript": "^1.8.10",
"typings": "^1.0.4", "typings": "^1.0.4",
"webpack": "^1.13.0", "webpack": "^1.13.0",

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Pipes', function () { describe('Pipes', function () {
beforeAll(function () { beforeAll(function () {
@ -23,9 +24,9 @@ describe('Pipes', function () {
}); });
it('should be able to toggle birthday formats', function () { it('should be able to toggle birthday formats', function () {
var birthDayEle = element(by.css('hero-birthday2 > p')); let birthDayEle = element(by.css('hero-birthday2 > p'));
expect(birthDayEle.getText()).toEqual("The hero's birthday is 4/15/1988"); expect(birthDayEle.getText()).toEqual("The hero's birthday is 4/15/1988");
var buttonEle = element(by.cssContainingText('hero-birthday2 > button', "Toggle Format")); let buttonEle = element(by.cssContainingText('hero-birthday2 > button', "Toggle Format"));
expect(buttonEle.isDisplayed()).toBe(true); expect(buttonEle.isDisplayed()).toBe(true);
buttonEle.click().then(function() { buttonEle.click().then(function() {
expect(birthDayEle.getText()).toEqual("The hero's birthday is Friday, April 15, 1988"); expect(birthDayEle.getText()).toEqual("The hero's birthday is Friday, April 15, 1988");
@ -33,7 +34,7 @@ describe('Pipes', function () {
}); });
it('should be able to chain and compose pipes', function () { it('should be able to chain and compose pipes', function () {
var chainedPipeEles = element.all(by.cssContainingText('my-app p', "The chained hero's")); let chainedPipeEles = element.all(by.cssContainingText('my-app p', "The chained hero's"));
expect(chainedPipeEles.count()).toBe(3, "should have 3 chained pipe examples"); expect(chainedPipeEles.count()).toBe(3, "should have 3 chained pipe examples");
expect(chainedPipeEles.get(0).getText()).toContain('APR 15, 1988'); expect(chainedPipeEles.get(0).getText()).toContain('APR 15, 1988');
expect(chainedPipeEles.get(1).getText()).toContain('FRIDAY, APRIL 15, 1988'); expect(chainedPipeEles.get(1).getText()).toContain('FRIDAY, APRIL 15, 1988');
@ -41,15 +42,15 @@ describe('Pipes', function () {
}); });
it('should be able to use ExponentialStrengthPipe pipe', function () { it('should be able to use ExponentialStrengthPipe pipe', function () {
var ele = element(by.css('power-booster p')); let ele = element(by.css('power-booster p'));
expect(ele.getText()).toContain('Super power boost: 1024'); expect(ele.getText()).toContain('Super power boost: 1024');
}); });
it('should be able to use the exponential calculator', function () { it('should be able to use the exponential calculator', function () {
var eles = element.all(by.css('power-boost-calculator input')); let eles = element.all(by.css('power-boost-calculator input'));
var baseInputEle = eles.get(0); let baseInputEle = eles.get(0);
var factorInputEle = eles.get(1); let factorInputEle = eles.get(1);
var outputEle = element(by.css('power-boost-calculator p')); let outputEle = element(by.css('power-boost-calculator p'));
baseInputEle.clear().then(function() { baseInputEle.clear().then(function() {
return sendKeys(baseInputEle, "7"); return sendKeys(baseInputEle, "7");
}).then(function() { }).then(function() {
@ -63,11 +64,11 @@ describe('Pipes', function () {
it('should support flying heroes (pure) ', function () { it('should support flying heroes (pure) ', function () {
var nameEle = element(by.css('flying-heroes input[type="text"]')); let nameEle = element(by.css('flying-heroes input[type="text"]'));
var canFlyCheckEle = element(by.css('flying-heroes #can-fly')); let canFlyCheckEle = element(by.css('flying-heroes #can-fly'));
var mutateCheckEle = element(by.css('flying-heroes #mutate')); let mutateCheckEle = element(by.css('flying-heroes #mutate'));
var resetEle = element(by.css('flying-heroes button')); let resetEle = element(by.css('flying-heroes button'));
var flyingHeroesEle = element.all(by.css('flying-heroes #flyers div')); let flyingHeroesEle = element.all(by.css('flying-heroes #flyers div'));
expect(canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"'); expect(canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"');
expect(mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array'); expect(mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array');
@ -94,11 +95,11 @@ describe('Pipes', function () {
it('should support flying heroes (impure) ', function () { it('should support flying heroes (impure) ', function () {
var nameEle = element(by.css('flying-heroes-impure input[type="text"]')); let nameEle = element(by.css('flying-heroes-impure input[type="text"]'));
var canFlyCheckEle = element(by.css('flying-heroes-impure #can-fly')); let canFlyCheckEle = element(by.css('flying-heroes-impure #can-fly'));
var mutateCheckEle = element(by.css('flying-heroes-impure #mutate')); let mutateCheckEle = element(by.css('flying-heroes-impure #mutate'));
var resetEle = element(by.css('flying-heroes-impure button')); let resetEle = element(by.css('flying-heroes-impure button'));
var flyingHeroesEle = element.all(by.css('flying-heroes-impure #flyers div')); let flyingHeroesEle = element.all(by.css('flying-heroes-impure #flyers div'));
expect(canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"'); expect(canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"');
expect(mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array'); expect(mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array');

View File

@ -1,7 +1,7 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('QuickStart E2E Tests', function () { describe('QuickStart E2E Tests', function () {
var expectedMsg = 'My First Angular 2 App'; let expectedMsg = 'My First Angular 2 App';
beforeEach(function () { beforeEach(function () {

View File

@ -1,123 +1,111 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Router', function () { describe('Router', function () {
beforeAll(function () {
beforeAll(function () { browser.get('');
browser.get(''); });
}); function getPageStruct() {
hrefEles = element.all(by.css('my-app a'));
function getPageStruct() { return {
hrefEles = element.all(by.css('my-app a')); hrefs: hrefEles,
routerParent: element(by.css('my-app > undefined')),
return { routerTitle: element(by.css('my-app > undefined > h2')),
hrefs: hrefEles, crisisHref: hrefEles.get(0),
routerParent: element(by.css('my-app > undefined')), crisisList: element.all(by.css('my-app > undefined > undefined li')),
routerTitle: element(by.css('my-app > undefined > h2')), crisisDetail: element(by.css('my-app > undefined > undefined > div')),
crisisDetailTitle: element(by.css('my-app > undefined > undefined > div > h3')),
crisisHref: hrefEles.get(0), heroesHref: hrefEles.get(1),
crisisList: element.all(by.css('my-app > undefined > undefined li')), heroesList: element.all(by.css('my-app > undefined li')),
crisisDetail: element(by.css('my-app > undefined > undefined > div')), heroDetail: element(by.css('my-app > undefined > div')),
crisisDetailTitle: element(by.css('my-app > undefined > undefined > div > h3')), heroDetailTitle: element(by.css('my-app > undefined > div > h3')),
};
heroesHref: hrefEles.get(1),
heroesList: element.all(by.css('my-app > undefined li')),
heroDetail: element(by.css('my-app > undefined > div')),
heroDetailTitle: element(by.css('my-app > undefined > div > h3')),
} }
} it('should be able to see the start screen', function () {
var page = getPageStruct();
it('should be able to see the start screen', function () { expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices');
var page = getPageStruct(); expect(page.crisisHref.getText()).toEqual("Crisis Center");
expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices'); expect(page.heroesHref.getText()).toEqual("Heroes");
expect(page.crisisHref.getText()).toEqual("Crisis Center");
expect(page.heroesHref.getText()).toEqual("Heroes");
});
it('should be able to see crises center items', function () {
var page = getPageStruct();
expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries at start");
});
it('should be able to see hero items', function () {
var page = getPageStruct();
page.heroesHref.click().then(function() {
expect(page.routerTitle.getText()).toContain('HEROES');
expect(page.heroesList.count()).toBe(6, "should be 6 heroes");
}); });
}); it('should be able to see crises center items', function () {
var page = getPageStruct();
it('should be able to toggle the views', function () { expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries at start");
var page = getPageStruct();
page.crisisHref.click().then(function() {
expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries");
return page.heroesHref.click();
}).then(function() {
expect(page.heroesList.count()).toBe(6, "should be 6 heroes");
}); });
}); it('should be able to see hero items', function () {
var page = getPageStruct();
it('should be able to edit and save details from the crisis center view', function () { page.heroesHref.click().then(function () {
crisisCenterEdit(2, true); expect(page.routerTitle.getText()).toContain('HEROES');
}); expect(page.heroesList.count()).toBe(6, "should be 6 heroes");
});
it('should be able to edit and cancel details from the crisis center view', function () {
crisisCenterEdit(3, false);
});
it('should be able to edit and save details from the heroes view', function () {
var page = getPageStruct();
var heroEle, heroText;
page.heroesHref.click().then(function() {
heroEle = page.heroesList.get(4);
return heroEle.getText();
}).then(function(text) {
expect(text.length).toBeGreaterThan(0, 'should have some text');
// remove leading id from text
heroText = text.substr(text.indexOf(' ')).trim();
return heroEle.click();
}).then(function() {
expect(page.heroesList.count()).toBe(0, "should no longer see crisis center entries");
expect(page.heroDetail.isPresent()).toBe(true, 'should be able to see crisis detail');
expect(page.heroDetailTitle.getText()).toContain(heroText);
var inputEle = page.heroDetail.element(by.css('input'));
return sendKeys(inputEle, '-foo');
}).then(function() {
expect(page.heroDetailTitle.getText()).toContain(heroText + '-foo');
var buttonEle = page.heroDetail.element(by.css('button'));
return buttonEle.click();
}).then(function() {
expect(heroEle.getText()).toContain(heroText + '-foo');
})
});
function crisisCenterEdit(index, shouldSave) {
var page = getPageStruct();
var crisisEle, crisisText;
page.crisisHref.click()
.then(function () {
crisisEle = page.crisisList.get(index);
return crisisEle.getText();
}).then(function (text) {
expect(text.length).toBeGreaterThan(0, 'should have some text');
// remove leading id from text
crisisText = text.substr(text.indexOf(' ')).trim();
return crisisEle.click();
}).then(function () {
expect(page.crisisList.count()).toBe(0, "should no longer see crisis center entries");
expect(page.crisisDetail.isPresent()).toBe(true, 'should be able to see crisis detail');
expect(page.crisisDetailTitle.getText()).toContain(crisisText);
var inputEle = page.crisisDetail.element(by.css('input'));
return sendKeys(inputEle, '-foo');
}).then(function () {
expect(page.crisisDetailTitle.getText()).toContain(crisisText + '-foo');
var buttonEle = page.crisisDetail.element(by.cssContainingText('button', shouldSave ? 'Save' : 'Cancel'));
return buttonEle.click();
}).then(function () {
if (shouldSave) {
expect(crisisEle.getText()).toContain(crisisText + '-foo');
} else {
expect(crisisEle.getText()).not.toContain(crisisText + '-foo');
}
}); });
} it('should be able to toggle the views', function () {
var page = getPageStruct();
page.crisisHref.click().then(function () {
expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries");
return page.heroesHref.click();
}).then(function () {
expect(page.heroesList.count()).toBe(6, "should be 6 heroes");
});
});
it('should be able to edit and save details from the crisis center view', function () {
crisisCenterEdit(2, true);
});
it('should be able to edit and cancel details from the crisis center view', function () {
crisisCenterEdit(3, false);
});
it('should be able to edit and save details from the heroes view', function () {
var page = getPageStruct();
var heroEle, heroText;
page.heroesHref.click().then(function () {
heroEle = page.heroesList.get(4);
return heroEle.getText();
}).then(function (text) {
expect(text.length).toBeGreaterThan(0, 'should have some text');
// remove leading id from text
heroText = text.substr(text.indexOf(' ')).trim();
return heroEle.click();
}).then(function () {
expect(page.heroesList.count()).toBe(0, "should no longer see crisis center entries");
expect(page.heroDetail.isPresent()).toBe(true, 'should be able to see crisis detail');
expect(page.heroDetailTitle.getText()).toContain(heroText);
var inputEle = page.heroDetail.element(by.css('input'));
return sendKeys(inputEle, '-foo');
}).then(function () {
expect(page.heroDetailTitle.getText()).toContain(heroText + '-foo');
var buttonEle = page.heroDetail.element(by.css('button'));
return buttonEle.click();
}).then(function () {
expect(heroEle.getText()).toContain(heroText + '-foo');
});
});
function crisisCenterEdit(index, shouldSave) {
var page = getPageStruct();
var crisisEle, crisisText;
page.crisisHref.click()
.then(function () {
crisisEle = page.crisisList.get(index);
return crisisEle.getText();
}).then(function (text) {
expect(text.length).toBeGreaterThan(0, 'should have some text');
// remove leading id from text
crisisText = text.substr(text.indexOf(' ')).trim();
return crisisEle.click();
}).then(function () {
expect(page.crisisList.count()).toBe(0, "should no longer see crisis center entries");
expect(page.crisisDetail.isPresent()).toBe(true, 'should be able to see crisis detail');
expect(page.crisisDetailTitle.getText()).toContain(crisisText);
var inputEle = page.crisisDetail.element(by.css('input'));
return sendKeys(inputEle, '-foo');
}).then(function () {
expect(page.crisisDetailTitle.getText()).toContain(crisisText + '-foo');
var buttonEle = page.crisisDetail.element(by.cssContainingText('button', shouldSave ? 'Save' : 'Cancel'));
return buttonEle.click();
}).then(function () {
if (shouldSave) {
expect(crisisEle.getText()).toContain(crisisText + '-foo');
}
else {
expect(crisisEle.getText()).not.toContain(crisisText + '-foo');
}
});
}
}); });
//# sourceMappingURL=e2e-spec.js.map

View File

@ -0,0 +1,124 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Router', function () {
beforeAll(function () {
browser.get('');
});
function getPageStruct() {
hrefEles = element.all(by.css('my-app a'));
return {
hrefs: hrefEles,
routerParent: element(by.css('my-app > undefined')),
routerTitle: element(by.css('my-app > undefined > h2')),
crisisHref: hrefEles.get(0),
crisisList: element.all(by.css('my-app > undefined > undefined li')),
crisisDetail: element(by.css('my-app > undefined > undefined > div')),
crisisDetailTitle: element(by.css('my-app > undefined > undefined > div > h3')),
heroesHref: hrefEles.get(1),
heroesList: element.all(by.css('my-app > undefined li')),
heroDetail: element(by.css('my-app > undefined > div')),
heroDetailTitle: element(by.css('my-app > undefined > div > h3')),
}
}
it('should be able to see the start screen', function () {
let page = getPageStruct();
expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices');
expect(page.crisisHref.getText()).toEqual("Crisis Center");
expect(page.heroesHref.getText()).toEqual("Heroes");
});
it('should be able to see crises center items', function () {
let page = getPageStruct();
expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries at start");
});
it('should be able to see hero items', function () {
let page = getPageStruct();
page.heroesHref.click().then(function() {
expect(page.routerTitle.getText()).toContain('HEROES');
expect(page.heroesList.count()).toBe(6, "should be 6 heroes");
});
});
it('should be able to toggle the views', function () {
let page = getPageStruct();
page.crisisHref.click().then(function() {
expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries");
return page.heroesHref.click();
}).then(function() {
expect(page.heroesList.count()).toBe(6, "should be 6 heroes");
});
});
it('should be able to edit and save details from the crisis center view', function () {
crisisCenterEdit(2, true);
});
it('should be able to edit and cancel details from the crisis center view', function () {
crisisCenterEdit(3, false);
});
it('should be able to edit and save details from the heroes view', function () {
let page = getPageStruct();
let heroEle, heroText;
page.heroesHref.click().then(function() {
heroEle = page.heroesList.get(4);
return heroEle.getText();
}).then(function(text) {
expect(text.length).toBeGreaterThan(0, 'should have some text');
// remove leading id from text
heroText = text.substr(text.indexOf(' ')).trim();
return heroEle.click();
}).then(function() {
expect(page.heroesList.count()).toBe(0, "should no longer see crisis center entries");
expect(page.heroDetail.isPresent()).toBe(true, 'should be able to see crisis detail');
expect(page.heroDetailTitle.getText()).toContain(heroText);
let inputEle = page.heroDetail.element(by.css('input'));
return sendKeys(inputEle, '-foo');
}).then(function() {
expect(page.heroDetailTitle.getText()).toContain(heroText + '-foo');
let buttonEle = page.heroDetail.element(by.css('button'));
return buttonEle.click();
}).then(function() {
expect(heroEle.getText()).toContain(heroText + '-foo');
})
});
function crisisCenterEdit(index, shouldSave) {
let page = getPageStruct();
let crisisEle, crisisText;
page.crisisHref.click()
.then(function () {
crisisEle = page.crisisList.get(index);
return crisisEle.getText();
}).then(function (text) {
expect(text.length).toBeGreaterThan(0, 'should have some text');
// remove leading id from text
crisisText = text.substr(text.indexOf(' ')).trim();
return crisisEle.click();
}).then(function () {
expect(page.crisisList.count()).toBe(0, "should no longer see crisis center entries");
expect(page.crisisDetail.isPresent()).toBe(true, 'should be able to see crisis detail');
expect(page.crisisDetailTitle.getText()).toContain(crisisText);
let inputEle = page.crisisDetail.element(by.css('input'));
return sendKeys(inputEle, '-foo');
}).then(function () {
expect(page.crisisDetailTitle.getText()).toContain(crisisText + '-foo');
let buttonEle = page.crisisDetail.element(by.cssContainingText('button', shouldSave ? 'Save' : 'Cancel'));
return buttonEle.click();
}).then(function () {
if (shouldSave) {
expect(crisisEle.getText()).toContain(crisisText + '-foo');
} else {
expect(crisisEle.getText()).not.toContain(crisisText + '-foo');
}
});
}
});

View File

@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
/** /**
* Async modal dialog service * Async modal dialog service
* DialogService makes this app easier to test by faking this service. * DialogService makes this app easier to test by faking this service.
* TODO: better modal implemenation that doesn't use window.confirm * TODO: better modal implementation that doesn't use window.confirm
*/ */
@Injectable() @Injectable()
export class DialogService { export class DialogService {

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Router', function () { describe('Router', function () {
beforeAll(function () { beforeAll(function () {
@ -26,19 +27,19 @@ describe('Router', function () {
} }
it('should be able to see the start screen', function () { it('should be able to see the start screen', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices'); expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices');
expect(page.crisisHref.getText()).toEqual("Crisis Center"); expect(page.crisisHref.getText()).toEqual("Crisis Center");
expect(page.heroesHref.getText()).toEqual("Heroes"); expect(page.heroesHref.getText()).toEqual("Heroes");
}); });
it('should be able to see crises center items', function () { it('should be able to see crises center items', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries at start"); expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries at start");
}); });
it('should be able to see hero items', function () { it('should be able to see hero items', function () {
var page = getPageStruct(); let page = getPageStruct();
page.heroesHref.click().then(function() { page.heroesHref.click().then(function() {
expect(page.routerTitle.getText()).toContain('HEROES'); expect(page.routerTitle.getText()).toContain('HEROES');
expect(page.heroesList.count()).toBe(6, "should be 6 heroes"); expect(page.heroesList.count()).toBe(6, "should be 6 heroes");
@ -46,7 +47,7 @@ describe('Router', function () {
}); });
it('should be able to toggle the views', function () { it('should be able to toggle the views', function () {
var page = getPageStruct(); let page = getPageStruct();
page.crisisHref.click().then(function() { page.crisisHref.click().then(function() {
expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries"); expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries");
return page.heroesHref.click(); return page.heroesHref.click();
@ -64,8 +65,8 @@ describe('Router', function () {
}); });
it('should be able to edit and save details from the heroes view', function () { it('should be able to edit and save details from the heroes view', function () {
var page = getPageStruct(); let page = getPageStruct();
var heroEle, heroText; let heroEle, heroText;
page.heroesHref.click().then(function() { page.heroesHref.click().then(function() {
heroEle = page.heroesList.get(4); heroEle = page.heroesList.get(4);
return heroEle.getText(); return heroEle.getText();
@ -78,11 +79,11 @@ describe('Router', function () {
expect(page.heroesList.count()).toBe(0, "should no longer see crisis center entries"); expect(page.heroesList.count()).toBe(0, "should no longer see crisis center entries");
expect(page.heroDetail.isPresent()).toBe(true, 'should be able to see crisis detail'); expect(page.heroDetail.isPresent()).toBe(true, 'should be able to see crisis detail');
expect(page.heroDetailTitle.getText()).toContain(heroText); expect(page.heroDetailTitle.getText()).toContain(heroText);
var inputEle = page.heroDetail.element(by.css('input')); let inputEle = page.heroDetail.element(by.css('input'));
return sendKeys(inputEle, '-foo'); return sendKeys(inputEle, '-foo');
}).then(function() { }).then(function() {
expect(page.heroDetailTitle.getText()).toContain(heroText + '-foo'); expect(page.heroDetailTitle.getText()).toContain(heroText + '-foo');
var buttonEle = page.heroDetail.element(by.css('button')); let buttonEle = page.heroDetail.element(by.css('button'));
return buttonEle.click(); return buttonEle.click();
}).then(function() { }).then(function() {
expect(heroEle.getText()).toContain(heroText + '-foo'); expect(heroEle.getText()).toContain(heroText + '-foo');
@ -90,8 +91,8 @@ describe('Router', function () {
}); });
function crisisCenterEdit(index, shouldSave) { function crisisCenterEdit(index, shouldSave) {
var page = getPageStruct(); let page = getPageStruct();
var crisisEle, crisisText; let crisisEle, crisisText;
page.crisisHref.click() page.crisisHref.click()
.then(function () { .then(function () {
crisisEle = page.crisisList.get(index); crisisEle = page.crisisList.get(index);
@ -105,11 +106,11 @@ describe('Router', function () {
expect(page.crisisList.count()).toBe(0, "should no longer see crisis center entries"); expect(page.crisisList.count()).toBe(0, "should no longer see crisis center entries");
expect(page.crisisDetail.isPresent()).toBe(true, 'should be able to see crisis detail'); expect(page.crisisDetail.isPresent()).toBe(true, 'should be able to see crisis detail');
expect(page.crisisDetailTitle.getText()).toContain(crisisText); expect(page.crisisDetailTitle.getText()).toContain(crisisText);
var inputEle = page.crisisDetail.element(by.css('input')); let inputEle = page.crisisDetail.element(by.css('input'));
return sendKeys(inputEle, '-foo'); return sendKeys(inputEle, '-foo');
}).then(function () { }).then(function () {
expect(page.crisisDetailTitle.getText()).toContain(crisisText + '-foo'); expect(page.crisisDetailTitle.getText()).toContain(crisisText + '-foo');
var buttonEle = page.crisisDetail.element(by.cssContainingText('button', shouldSave ? 'Save' : 'Cancel')); let buttonEle = page.crisisDetail.element(by.cssContainingText('button', shouldSave ? 'Save' : 'Cancel'));
return buttonEle.click(); return buttonEle.click();
}).then(function () { }).then(function () {
if (shouldSave) { if (shouldSave) {

View File

@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
/** /**
* Async modal dialog service * Async modal dialog service
* DialogService makes this app easier to test by faking this service. * DialogService makes this app easier to test by faking this service.
* TODO: better modal implemenation that doesn't use window.confirm * TODO: better modal implementation that doesn't use window.confirm
*/ */
@Injectable() @Injectable()
export class DialogService { export class DialogService {

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Server Communication', function () { describe('Server Communication', function () {
beforeAll(function () { beforeAll(function () {
@ -6,14 +7,14 @@ describe('Server Communication', function () {
describe('Tour of Heroes (Observable)', function () { describe('Tour of Heroes (Observable)', function () {
var initialHeroCount = 4; let initialHeroCount = 4;
var newHeroName = 'Mr. IQ'; let newHeroName = 'Mr. IQ';
var heroCountAfterAdd = 5; let heroCountAfterAdd = 5;
var heroListComp = element(by.tagName('hero-list')); let heroListComp = element(by.tagName('hero-list'));
var addButton = heroListComp.element(by.tagName('button')); let addButton = heroListComp.element(by.tagName('button'));
var heroTags = heroListComp.all(by.tagName('li')); let heroTags = heroListComp.all(by.tagName('li'));
var heroNameInput = heroListComp.element(by.tagName('input')); let heroNameInput = heroListComp.element(by.tagName('input'));
it('should exist', function() { it('should exist', function() {
expect(heroListComp).toBeDefined('<hero-list> must exist'); expect(heroListComp).toBeDefined('<hero-list> must exist');
@ -36,7 +37,7 @@ describe('Server Communication', function () {
sendKeys(heroNameInput, newHeroName); sendKeys(heroNameInput, newHeroName);
addButton.click().then(function() { addButton.click().then(function() {
expect(heroTags.count()).toBe(heroCountAfterAdd, 'A new hero should be added'); expect(heroTags.count()).toBe(heroCountAfterAdd, 'A new hero should be added');
var newHeroInList = heroTags.get(heroCountAfterAdd - 1).getText(); let newHeroInList = heroTags.get(heroCountAfterAdd - 1).getText();
expect(newHeroInList).toBe(newHeroName, 'The hero should be added to the end of the list'); expect(newHeroInList).toBe(newHeroName, 'The hero should be added to the end of the list');
}); });
}) })
@ -45,9 +46,9 @@ describe('Server Communication', function () {
describe('Wikipedia Demo', function () { describe('Wikipedia Demo', function () {
it('should initialize the demo with empty result list', function () { it('should initialize the demo with empty result list', function () {
var myWikiComp = element(by.tagName('my-wiki')); let myWikiComp = element(by.tagName('my-wiki'));
expect(myWikiComp).toBeDefined('<my-wiki> must exist'); expect(myWikiComp).toBeDefined('<my-wiki> must exist');
var resultList = myWikiComp.all(by.tagName('li')); let resultList = myWikiComp.all(by.tagName('li'));
expect(resultList.count()).toBe(0, 'result list must be empty'); expect(resultList.count()).toBe(0, 'result list must be empty');
}); });
@ -77,9 +78,9 @@ describe('Server Communication', function () {
describe('Smarter Wikipedia Demo', function () { describe('Smarter Wikipedia Demo', function () {
it('should initialize the demo with empty result list', function () { it('should initialize the demo with empty result list', function () {
var myWikiSmartComp = element(by.tagName('my-wiki-smart')); let myWikiSmartComp = element(by.tagName('my-wiki-smart'));
expect(myWikiSmartComp).toBeDefined('<my-wiki-smart> must exist'); expect(myWikiSmartComp).toBeDefined('<my-wiki-smart> must exist');
var resultList = myWikiSmartComp.all(by.tagName('li')); let resultList = myWikiSmartComp.all(by.tagName('li'));
expect(resultList.count()).toBe(0, 'result list must be empty'); expect(resultList.count()).toBe(0, 'result list must be empty');
}); });
@ -111,14 +112,14 @@ describe('Server Communication', function () {
}); });
function testForResult(componentTagName, keyPressed, hasListBeforeSearch, done) { function testForResult(componentTagName, keyPressed, hasListBeforeSearch, done) {
var searchWait = 1000; // Wait for wikipedia but not so long that tests timeout let searchWait = 1000; // Wait for wikipedia but not so long that tests timeout
var wikiComponent = element(by.tagName(componentTagName)); let wikiComponent = element(by.tagName(componentTagName));
expect(wikiComponent).toBeDefined('<' + componentTagName + '> must exist'); expect(wikiComponent).toBeDefined('<' + componentTagName + '> must exist');
var searchBox = wikiComponent.element(by.tagName('input')); let searchBox = wikiComponent.element(by.tagName('input'));
expect(searchBox).toBeDefined('<input> for search must exist'); expect(searchBox).toBeDefined('<input> for search must exist');
searchBox.sendKeys(keyPressed).then(function () { searchBox.sendKeys(keyPressed).then(function () {
var resultList = wikiComponent.all(by.tagName('li')); let resultList = wikiComponent.all(by.tagName('li'));
if (hasListBeforeSearch) { if (hasListBeforeSearch) {
expect(resultList.count()).toBeGreaterThan(0, 'result list should not be empty before search'); expect(resultList.count()).toBeGreaterThan(0, 'result list should not be empty before search');

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Structural Directives', function () { describe('Structural Directives', function () {
// tests interact - so we need beforeEach instead of beforeAll // tests interact - so we need beforeEach instead of beforeAll
@ -6,16 +7,16 @@ describe('Structural Directives', function () {
}); });
it('should be able to use ngFor, ngIf and ngWhen together', function () { it('should be able to use ngFor, ngIf and ngWhen together', function () {
var allDivEles = element.all(by.css('structural-directives > div')); let allDivEles = element.all(by.css('structural-directives > div'));
expect(allDivEles.get(0).getText()).toEqual('Mr. Nice'); expect(allDivEles.get(0).getText()).toEqual('Mr. Nice');
expect(allDivEles.get(1).getText()).toEqual('Mr. Nice'); expect(allDivEles.get(1).getText()).toEqual('Mr. Nice');
expect(allDivEles.get(4).getText()).toEqual('Ready'); expect(allDivEles.get(4).getText()).toEqual('Ready');
}); });
it('should be able to toggle ngIf with a button', function () { it('should be able to toggle ngIf with a button', function () {
var setConditionButtonEle = element.all(by.css('button')).get(0); let setConditionButtonEle = element.all(by.css('button')).get(0);
var conditionTrueEles = element.all(by.cssContainingText('p', 'condition is true')); let conditionTrueEles = element.all(by.cssContainingText('p', 'condition is true'));
var conditionFalseEles = element.all(by.cssContainingText('p', 'condition is false')); let conditionFalseEles = element.all(by.cssContainingText('p', 'condition is false'));
expect(conditionTrueEles.count()).toBe(2, 'should be two condition true elements'); expect(conditionTrueEles.count()).toBe(2, 'should be two condition true elements');
expect(conditionFalseEles.count()).toBe(0, 'should be no condition false elements'); expect(conditionFalseEles.count()).toBe(0, 'should be no condition false elements');
setConditionButtonEle.click().then(function() { setConditionButtonEle.click().then(function() {
@ -25,13 +26,13 @@ describe('Structural Directives', function () {
}); });
it('should be able to compare use of ngIf with changing css visibility', function () { it('should be able to compare use of ngIf with changing css visibility', function () {
var setConditionButtonEle = element.all(by.css('button')).get(0); let setConditionButtonEle = element.all(by.css('button')).get(0);
var ngIfButtonEle = element(by.cssContainingText('button', 'if | !if')); let ngIfButtonEle = element(by.cssContainingText('button', 'if | !if'));
var ngIfParentEle = ngIfButtonEle.element(by.xpath('..')); let ngIfParentEle = ngIfButtonEle.element(by.xpath('..'));
var ngIfSiblingEle = ngIfParentEle.element(by.css('heavy-loader')); let ngIfSiblingEle = ngIfParentEle.element(by.css('heavy-loader'));
var cssButtonEle = element(by.cssContainingText('button', 'show | hide')); let cssButtonEle = element(by.cssContainingText('button', 'show | hide'));
var cssSiblingEle = cssButtonEle.element(by.xpath('..')).element(by.css('heavy-loader')); let cssSiblingEle = cssButtonEle.element(by.xpath('..')).element(by.css('heavy-loader'));
var setConditionText; let setConditionText;
setConditionButtonEle.getText().then(function(text) { setConditionButtonEle.getText().then(function(text) {
setConditionText = text; setConditionText = text;
expect(ngIfButtonEle.isPresent()).toBe(true, 'should be able to find ngIfButton'); expect(ngIfButtonEle.isPresent()).toBe(true, 'should be able to find ngIfButton');
@ -54,8 +55,8 @@ describe('Structural Directives', function () {
}); });
it('should be able to use *ngIf ', function () { it('should be able to use *ngIf ', function () {
var setConditionButtonEle = element.all(by.css('button')).get(0); let setConditionButtonEle = element.all(by.css('button')).get(0);
var displayEles = element.all(by.cssContainingText('p', 'Our heroes are true!')); let displayEles = element.all(by.cssContainingText('p', 'Our heroes are true!'));
expect(displayEles.count()).toBe(2, "should be displaying two ngIf elements"); expect(displayEles.count()).toBe(2, "should be displaying two ngIf elements");
setConditionButtonEle.click().then(function() { setConditionButtonEle.click().then(function() {
expect(displayEles.count()).toBe(0, "should nog longer be displaying ngIf elements"); expect(displayEles.count()).toBe(0, "should nog longer be displaying ngIf elements");

View File

@ -1,8 +1,9 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Style Guide', function () { describe('Style Guide', function () {
it('01-01', function () { it('01-01', function () {
browser.get('#/01-01'); browser.get('#/01-01');
var pre = element(by.tagName('toh-heroes > pre')); let pre = element(by.tagName('toh-heroes > pre'));
expect(pre.getText()).toContain('Bombasto'); expect(pre.getText()).toContain('Bombasto');
expect(pre.getText()).toContain('Tornado'); expect(pre.getText()).toContain('Tornado');
expect(pre.getText()).toContain('Magneta'); expect(pre.getText()).toContain('Magneta');
@ -11,8 +12,8 @@ describe('Style Guide', function () {
it('02-07', function () { it('02-07', function () {
browser.get('#/02-07'); browser.get('#/02-07');
var hero = element(by.tagName('toh-hero > div')); let hero = element(by.tagName('toh-hero > div'));
var users = element(by.tagName('admin-users > div')); let users = element(by.tagName('admin-users > div'));
expect(hero.getText()).toBe('hero component'); expect(hero.getText()).toBe('hero component');
expect(users.getText()).toBe('users component'); expect(users.getText()).toBe('users component');
@ -21,21 +22,21 @@ describe('Style Guide', function () {
it('02-08', function () { it('02-08', function () {
browser.get('#/02-08'); browser.get('#/02-08');
var input = element(by.tagName('input[tohvalidate]')); let input = element(by.tagName('input[tohvalidate]'));
expect(input.isPresent()).toBe(true); expect(input.isPresent()).toBe(true);
}); });
it('03-01', function () { it('03-01', function () {
browser.get('#/03-01'); browser.get('#/03-01');
var div = element(by.tagName('sg-app > div')); let div = element(by.tagName('sg-app > div'));
expect(div.getText()).toBe('The expected error is 42'); expect(div.getText()).toBe('The expected error is 42');
}); });
it('03-02', function () { it('03-02', function () {
browser.get('#/03-02'); browser.get('#/03-02');
var divs = element.all(by.tagName('sg-app > div')); let divs = element.all(by.tagName('sg-app > div'));
expect(divs.get(0).getText()).toBe('Heroes url: api/heroes'); expect(divs.get(0).getText()).toBe('Heroes url: api/heroes');
expect(divs.get(1).getText()).toBe('Villains url: api/villains'); expect(divs.get(1).getText()).toBe('Villains url: api/villains');
}); });
@ -43,14 +44,14 @@ describe('Style Guide', function () {
it('03-03', function () { it('03-03', function () {
browser.get('#/03-03'); browser.get('#/03-03');
var div = element(by.tagName('sg-app > div')); let div = element(by.tagName('sg-app > div'));
expect(div.getText()).toBe('Our hero is RubberMan and He is so elastic'); expect(div.getText()).toBe('Our hero is RubberMan and He is so elastic');
}); });
it('03-04', function () { it('03-04', function () {
browser.get('#/03-04'); browser.get('#/03-04');
var buttons = element.all(by.tagName('sg-app > button')); let buttons = element.all(by.tagName('sg-app > button'));
expect(buttons.get(0).getText()).toBe('Show toast'); expect(buttons.get(0).getText()).toBe('Show toast');
expect(buttons.get(1).getText()).toBe('Hide toast'); expect(buttons.get(1).getText()).toBe('Hide toast');
}); });
@ -58,10 +59,10 @@ describe('Style Guide', function () {
it('03-05', function () { it('03-05', function () {
browser.get('#/03-05'); browser.get('#/03-05');
var div = element(by.tagName('sg-app > div')); let div = element(by.tagName('sg-app > div'));
expect(div.getText()).toBe('Actual favorite: Windstorm'); expect(div.getText()).toBe('Actual favorite: Windstorm');
var lis = element.all(by.tagName('sg-app > ul > li')); let lis = element.all(by.tagName('sg-app > ul > li'));
expect(lis.get(0).getText()).toBe('Windstorm'); expect(lis.get(0).getText()).toBe('Windstorm');
expect(lis.get(1).getText()).toBe('Bombasto'); expect(lis.get(1).getText()).toBe('Bombasto');
expect(lis.get(2).getText()).toBe('Magneta'); expect(lis.get(2).getText()).toBe('Magneta');
@ -71,10 +72,10 @@ describe('Style Guide', function () {
it('03-06', function () { it('03-06', function () {
browser.get('#/03-06'); browser.get('#/03-06');
var div = element(by.tagName('sg-app > div')); let div = element(by.tagName('sg-app > div'));
expect(div.getText()).toBe('Actual favorite: Windstorm'); expect(div.getText()).toBe('Actual favorite: Windstorm');
var lis = element.all(by.tagName('sg-app > ul > li')); let lis = element.all(by.tagName('sg-app > ul > li'));
expect(lis.get(0).getText()).toBe('Windstorm'); expect(lis.get(0).getText()).toBe('Windstorm');
expect(lis.get(1).getText()).toBe('Bombasto'); expect(lis.get(1).getText()).toBe('Bombasto');
expect(lis.get(2).getText()).toBe('Magneta'); expect(lis.get(2).getText()).toBe('Magneta');
@ -84,77 +85,77 @@ describe('Style Guide', function () {
it('04-10', function () { it('04-10', function () {
browser.get('#/04-10'); browser.get('#/04-10');
var div = element(by.tagName('sg-app > toh-heroes > div')); let div = element(by.tagName('sg-app > toh-heroes > div'));
expect(div.getText()).toBe('This is heroes component'); expect(div.getText()).toBe('This is heroes component');
}); });
it('04-14', function () { it('04-14', function () {
browser.get('#/04-14'); browser.get('#/04-14');
var h2 = element(by.tagName('sg-app > toh-heroes > div > h2')); let h2 = element(by.tagName('sg-app > toh-heroes > div > h2'));
expect(h2.getText()).toBe('My Heroes'); expect(h2.getText()).toBe('My Heroes');
}); });
it('05-02', function () { it('05-02', function () {
browser.get('#/05-02'); browser.get('#/05-02');
var button = element(by.tagName('sg-app > toh-hero-button > button')); let button = element(by.tagName('sg-app > toh-hero-button > button'));
expect(button.getText()).toBe('Hero button'); expect(button.getText()).toBe('Hero button');
}); });
it('05-03', function () { it('05-03', function () {
browser.get('#/05-03'); browser.get('#/05-03');
var button = element(by.tagName('sg-app > toh-hero-button > button')); let button = element(by.tagName('sg-app > toh-hero-button > button'));
expect(button.getText()).toBe('Hero button'); expect(button.getText()).toBe('Hero button');
}); });
it('05-04', function () { it('05-04', function () {
browser.get('#/05-04'); browser.get('#/05-04');
var h2 = element(by.tagName('sg-app > toh-heroes > div > h2')); let h2 = element(by.tagName('sg-app > toh-heroes > div > h2'));
expect(h2.getText()).toBe('My Heroes'); expect(h2.getText()).toBe('My Heroes');
}); });
it('05-12', function () { it('05-12', function () {
browser.get('#/05-12'); browser.get('#/05-12');
var button = element(by.tagName('sg-app > toh-hero-button > button')); let button = element(by.tagName('sg-app > toh-hero-button > button'));
expect(button.getText()).toBe('OK'); expect(button.getText()).toBe('OK');
}); });
it('05-13', function () { it('05-13', function () {
browser.get('#/05-13'); browser.get('#/05-13');
var button = element(by.tagName('sg-app > toh-hero-button > button')); let button = element(by.tagName('sg-app > toh-hero-button > button'));
expect(button.getText()).toBe('OK'); expect(button.getText()).toBe('OK');
}); });
it('05-14', function () { it('05-14', function () {
browser.get('#/05-14'); browser.get('#/05-14');
var toast = element(by.tagName('sg-app > toh-toast')); let toast = element(by.tagName('sg-app > toh-toast'));
expect(toast.getText()).toBe('...'); expect(toast.getText()).toBe('...');
}); });
it('05-15', function () { it('05-15', function () {
browser.get('#/05-15'); browser.get('#/05-15');
var heroList = element(by.tagName('sg-app > toh-hero-list')); let heroList = element(by.tagName('sg-app > toh-hero-list'));
expect(heroList.getText()).toBe('...'); expect(heroList.getText()).toBe('...');
}); });
it('05-16', function () { it('05-16', function () {
browser.get('#/05-16'); browser.get('#/05-16');
var hero = element(by.tagName('sg-app > toh-hero')); let hero = element(by.tagName('sg-app > toh-hero'));
expect(hero.getText()).toBe('...'); expect(hero.getText()).toBe('...');
}); });
it('05-17', function () { it('05-17', function () {
browser.get('#/05-17'); browser.get('#/05-17');
var section = element(by.tagName('sg-app > toh-hero-list > section')); let section = element(by.tagName('sg-app > toh-hero-list > section'));
expect(section.getText()).toContain('Our list of heroes'); expect(section.getText()).toContain('Our list of heroes');
expect(section.getText()).toContain('Total powers'); expect(section.getText()).toContain('Total powers');
expect(section.getText()).toContain('Average power'); expect(section.getText()).toContain('Average power');
@ -163,21 +164,21 @@ describe('Style Guide', function () {
it('06-01', function () { it('06-01', function () {
browser.get('#/06-01'); browser.get('#/06-01');
var div = element(by.tagName('sg-app > div[tohhighlight]')); let div = element(by.tagName('sg-app > div[tohhighlight]'));
expect(div.getText()).toBe('Bombasta'); expect(div.getText()).toBe('Bombasta');
}); });
it('06-03', function () { it('06-03', function () {
browser.get('#/06-03'); browser.get('#/06-03');
var input = element(by.tagName('input[tohvalidator]')); let input = element(by.tagName('input[tohvalidator]'));
expect(input.isPresent()).toBe(true); expect(input.isPresent()).toBe(true);
}); });
it('07-01', function () { it('07-01', function () {
browser.get('#/07-01'); browser.get('#/07-01');
var lis = element.all(by.tagName('sg-app > ul > li')); let lis = element.all(by.tagName('sg-app > ul > li'));
expect(lis.get(0).getText()).toBe('Windstorm'); expect(lis.get(0).getText()).toBe('Windstorm');
expect(lis.get(1).getText()).toBe('Bombasto'); expect(lis.get(1).getText()).toBe('Bombasto');
expect(lis.get(2).getText()).toBe('Magneta'); expect(lis.get(2).getText()).toBe('Magneta');
@ -187,21 +188,21 @@ describe('Style Guide', function () {
it('07-03', function () { it('07-03', function () {
browser.get('#/07-03'); browser.get('#/07-03');
var pre = element(by.tagName('toh-heroes > pre')); let pre = element(by.tagName('toh-heroes > pre'));
expect(pre.getText()).toContain('[]'); expect(pre.getText()).toContain('[]');
}); });
it('07-04', function () { it('07-04', function () {
browser.get('#/07-04'); browser.get('#/07-04');
var pre = element(by.tagName('toh-app > pre')); let pre = element(by.tagName('toh-app > pre'));
expect(pre.getText()).toContain('[]'); expect(pre.getText()).toContain('[]');
}); });
it('09-01', function () { it('09-01', function () {
browser.get('#/09-01'); browser.get('#/09-01');
var button = element(by.tagName('sg-app > toh-hero-button > button')); let button = element(by.tagName('sg-app > toh-hero-button > button'));
expect(button.getText()).toBe('OK'); expect(button.getText()).toBe('OK');
}); });
}); });

View File

@ -8,7 +8,7 @@ import { Component } from '@angular/core';
template: `<button>OK<button>` template: `<button>OK<button>`
}) })
export class HeroButtonComponent { export class HeroButtonComponent {
onInit() { // mispelled onInit() { // misspelled
console.log('The component is initialized'); console.log('The component is initialized');
} }
} }

View File

@ -1,27 +0,0 @@
/*global browser, element, by */
describe('Getting Started E2E Tests', function() {
// #docregion shared
var expectedMsg = 'My First Angular 2 App';
// tests shared across languages
function sharedTests(basePath) {
beforeEach(function () {
browser.get(basePath + 'index.html');
});
it('should display: '+ expectedMsg, function() {
expect(element(by.id('output')).getText()).toEqual(expectedMsg);
});
}
// #enddocregion
describe('Getting Started in JavaScript', function() {
sharedTests('gettingstarted/js/');
});
describe('Getting Started in TypeScript', function() {
sharedTests('gettingstarted/ts/');
});
});

View File

@ -0,0 +1,28 @@
/// <reference path="../_protractor/e2e.d.ts" />
/*global browser, element, by */
describe('Getting Started E2E Tests', function() {
// #docregion shared
let expectedMsg = 'My First Angular 2 App';
// tests shared across languages
function sharedTests(basePath) {
beforeEach(function () {
browser.get(basePath + 'index.html');
});
it('should display: '+ expectedMsg, function() {
expect(element(by.id('output')).getText()).toEqual(expectedMsg);
});
}
// #enddocregion
describe('Getting Started in JavaScript', function() {
sharedTests('gettingstarted/js/');
});
describe('Getting Started in TypeScript', function() {
sharedTests('gettingstarted/ts/');
});
});

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
// Not yet complete // Not yet complete
describe('Template Syntax', function () { describe('Template Syntax', function () {
@ -6,24 +7,24 @@ describe('Template Syntax', function () {
}); });
it('should be able to use interpolation with a hero', function () { it('should be able to use interpolation with a hero', function () {
var heroInterEle = element.all(by.css('h2+p')).get(0); let heroInterEle = element.all(by.css('h2+p')).get(0);
expect(heroInterEle.getText()).toEqual('My current hero is Hercules'); expect(heroInterEle.getText()).toEqual('My current hero is Hercules');
}); });
it('should be able to use interpolation with a calculation', function () { it('should be able to use interpolation with a calculation', function () {
var theSumEles = element.all(by.cssContainingText('h3~p','The sum of')); let theSumEles = element.all(by.cssContainingText('h3~p','The sum of'));
expect(theSumEles.count()).toBe(2); expect(theSumEles.count()).toBe(2);
expect(theSumEles.get(0).getText()).toEqual('The sum of 1 + 1 is 2'); expect(theSumEles.get(0).getText()).toEqual('The sum of 1 + 1 is 2');
expect(theSumEles.get(1).getText()).toEqual('The sum of 1 + 1 is not 4'); expect(theSumEles.get(1).getText()).toEqual('The sum of 1 + 1 is not 4');
}); });
it('should be able to use class binding syntax', function () { it('should be able to use class binding syntax', function () {
var specialEle = element(by.cssContainingText('div','Special')); let specialEle = element(by.cssContainingText('div','Special'));
expect(specialEle.getAttribute('class')).toMatch('special'); expect(specialEle.getAttribute('class')).toMatch('special');
}); });
it('should be able to use style binding syntax', function () { it('should be able to use style binding syntax', function () {
var specialButtonEle = element(by.cssContainingText('div.special~button', 'button')); let specialButtonEle = element(by.cssContainingText('div.special~button', 'button'));
expect(specialButtonEle.getAttribute('style')).toMatch('color: red'); expect(specialButtonEle.getAttribute('style')).toMatch('color: red');
}); });
}); });

View File

@ -197,13 +197,18 @@ button</button>
<!-- #enddocregion property-binding-7 --> <!-- #enddocregion property-binding-7 -->
<!-- #docregion property-binding-vs-interpolation --> <!-- #docregion property-binding-vs-interpolation -->
Interpolated: <img src="{{heroImageUrl}}"><br> <p><img src="{{heroImageUrl}}"> is the <i>interpolated</i> image.</p>
Property bound: <img [src]="heroImageUrl"> <p><img [src]="heroImageUrl"> is the <i>property bound</i> image.</p>
<div>The interpolated title is {{title}}</div> <p><span>"{{title}}" is the <i>interpolated</i> title.</span></p>
<div [innerHTML]="'The [innerHTML] title is '+title"></div> <p>"<span [innerHTML]="title"></span>" is the <i>property bound</i> title.</p>
<!-- #enddocregion property-binding-vs-interpolation --> <!-- #enddocregion property-binding-vs-interpolation -->
<!-- #docregion property-binding-vs-interpolation-sanitization -->
<p><span>"{{evilTitle}}" is the <i>interpolated</i> evil title.</span></p>
<p>"<span [innerHTML]="evilTitle"></span>" is the <i>property bound</i> evil title.</p>
<!-- #enddocregion property-binding-vs-interpolation-sanitization -->
<a class="to-toc" href="#toc">top</a> <a class="to-toc" href="#toc">top</a>
<!-- attribute binding --> <!-- attribute binding -->

View File

@ -1,4 +1,5 @@
//#docplaster /* tslint:disable:member-ordering forin */
// #docplaster
import { AfterViewInit, Component, ElementRef, OnInit, QueryList, ViewChildren } from '@angular/core'; import { AfterViewInit, Component, ElementRef, OnInit, QueryList, ViewChildren } from '@angular/core';
import { NgForm } from '@angular/common'; import { NgForm } from '@angular/common';
@ -8,7 +9,7 @@ import { HeroDetailComponent, BigHeroDetailComponent } from './hero-detail.compo
import { MyClickDirective, MyClickDirective2 } from './my-click.directive'; import { MyClickDirective, MyClickDirective2 } from './my-click.directive';
// Alerter fn: monkey patch during test // Alerter fn: monkey patch during test
export function alerter(msg?:string) { export function alerter(msg?: string) {
window.alert(msg); window.alert(msg);
} }
@ -27,7 +28,7 @@ export enum Color {Red, Green, Blue};
}) })
export class AppComponent implements AfterViewInit, OnInit { export class AppComponent implements AfterViewInit, OnInit {
ngOnInit(){ ngOnInit() {
this.refreshHeroes(); this.refreshHeroes();
} }
@ -40,43 +41,48 @@ export class AppComponent implements AfterViewInit, OnInit {
badCurly = 'bad curly'; badCurly = 'bad curly';
classes = 'special'; classes = 'special';
callFax(value:string) {this.alert(`Faxing ${value} ...`)} callFax(value: string) {this.alert(`Faxing ${value} ...`); }
callPhone(value:string) {this.alert(`Calling ${value} ...`)} callPhone(value: string) {this.alert(`Calling ${value} ...`); }
canSave = true; canSave = true;
Color = Color; Color = Color;
color = Color.Red; color = Color.Red;
colorToggle() {this.color = (this.color === Color.Red)? Color.Blue : Color.Red} colorToggle() {this.color = (this.color === Color.Red) ? Color.Blue : Color.Red; }
currentHero = Hero.MockHeroes[0]; currentHero = Hero.MockHeroes[0];
deleteHero(hero:Hero){ deleteHero(hero: Hero) {
this.alert('Deleted hero: '+ (hero && hero.firstName)) this.alert('Deleted hero: ' + (hero && hero.firstName));
} }
// DevMode memoization fields // #docregion evil-title
private priorClasses:{}; evilTitle = 'Template <script>alert("evil never sleeps")</script>Syntax';
private _priorStyles:{}; // #enddocregion evil-title
private _priorStyles2:{};
getStyles(el:Element){ title = 'Template Syntax';
// DevMode memoization fields
private priorClasses: {};
private _priorStyles: {};
getStyles(el: Element) {
let styles = window.getComputedStyle(el); let styles = window.getComputedStyle(el);
let showStyles = {}; let showStyles = {};
for (var p in this.setStyles()){ for (let p in this.setStyles()) {
showStyles[p] = styles[p]; showStyles[p] = styles[p];
} }
return JSON.stringify(showStyles); return JSON.stringify(showStyles);
} }
getVal() {return this.val}; getVal() { return this.val; }
heroes:Hero[]; heroes: Hero[];
// heroImageUrl = 'http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png'; // heroImageUrl = 'http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png';
// Public Domain terms of use: http://www.wpclipart.com/terms.html // Public Domain terms of use: http://www.wpclipart.com/terms.html
heroImageUrl = 'images/hero.png'; heroImageUrl = 'images/hero.png';
//iconUrl = 'https://angular.io/resources/images/logos/standard/shield-large.png'; // iconUrl = 'https://angular.io/resources/images/logos/standard/shield-large.png';
clicked = ''; clicked = '';
clickMessage = ''; clickMessage = '';
clickMessage2 = ''; clickMessage2 = '';
@ -85,28 +91,28 @@ export class AppComponent implements AfterViewInit, OnInit {
isSpecial = true; isSpecial = true;
isUnchanged = true; isUnchanged = true;
nullHero:Hero = null; // or undefined nullHero: Hero = null; // or undefined
onCancel(event:KeyboardEvent){ onCancel(event: KeyboardEvent) {
let evtMsg = event ? ' Event target is '+ (<HTMLElement>event.target).innerHTML : ''; let evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).innerHTML : '';
this.alert('Canceled.'+evtMsg) this.alert('Canceled.' + evtMsg);
} }
onClickMe(event:KeyboardEvent){ onClickMe(event: KeyboardEvent) {
let evtMsg = event ? ' Event target class is '+ (<HTMLElement>event.target).className : ''; let evtMsg = event ? ' Event target class is ' + (<HTMLElement>event.target).className : '';
this.alert('Click me.'+evtMsg) this.alert('Click me.' + evtMsg);
} }
onSave(event:KeyboardEvent){ onSave(event: KeyboardEvent) {
let evtMsg = event ? ' Event target is '+ (<HTMLElement>event.target).innerText : ''; let evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).innerText : '';
this.alert('Saved.'+evtMsg) this.alert('Saved.' + evtMsg);
} }
onSubmit(form:NgForm){ onSubmit(form: NgForm) {
let evtMsg = form.valid ? let evtMsg = form.valid ?
' Form value is '+ JSON.stringify(form.value) : ' Form value is ' + JSON.stringify(form.value) :
' Form is invalid'; ' Form is invalid';
this.alert('Form submitted.'+evtMsg) this.alert('Form submitted.' + evtMsg);
} }
product = { product = {
@ -123,10 +129,10 @@ export class AppComponent implements AfterViewInit, OnInit {
// #docregion same-as-it-ever-was // #docregion same-as-it-ever-was
private samenessCount = 5; private samenessCount = 5;
moreOfTheSame() {this.samenessCount++;}; moreOfTheSame() { this.samenessCount++; };
get sameAsItEverWas() { get sameAsItEverWas() {
var result:string[] = Array(this.samenessCount); let result: string[] = Array(this.samenessCount);
for (var i=result.length; i-- > 0;){result[i]='same as it ever was ...'} for ( let i = result.length; i-- > 0; ) { result[i] = 'same as it ever was ...'; }
return result; return result;
// return [1,2,3,4,5].map(id => { // return [1,2,3,4,5].map(id => {
// return {id:id, text: 'same as it ever was ...'}; // return {id:id, text: 'same as it ever was ...'};
@ -134,8 +140,8 @@ export class AppComponent implements AfterViewInit, OnInit {
} }
// #enddocregion same-as-it-ever-was // #enddocregion same-as-it-ever-was
setUpperCaseFirstName(firstName:string){ setUpperCaseFirstName(firstName: string) {
//console.log(firstName); // console.log(firstName);
this.currentHero.firstName = firstName.toUpperCase(); this.currentHero.firstName = firstName.toUpperCase();
} }
@ -145,10 +151,10 @@ export class AppComponent implements AfterViewInit, OnInit {
saveable: this.canSave, // true saveable: this.canSave, // true
modified: !this.isUnchanged, // false modified: !this.isUnchanged, // false
special: this.isSpecial, // true special: this.isSpecial, // true
} };
// #enddocregion setClasses // #enddocregion setClasses
// compensate for DevMode (sigh) // compensate for DevMode (sigh)
if (JSON.stringify(classes) === JSON.stringify(this.priorClasses)){ if (JSON.stringify(classes) === JSON.stringify(this.priorClasses)) {
return this.priorClasses; return this.priorClasses;
} }
this.priorClasses = classes; this.priorClasses = classes;
@ -165,10 +171,10 @@ export class AppComponent implements AfterViewInit, OnInit {
'font-style': this.canSave ? 'italic' : 'normal', // italic 'font-style': this.canSave ? 'italic' : 'normal', // italic
'font-weight': !this.isUnchanged ? 'bold' : 'normal', // normal 'font-weight': !this.isUnchanged ? 'bold' : 'normal', // normal
'font-size': this.isSpecial ? '24px' : '8px', // 24px 'font-size': this.isSpecial ? '24px' : '8px', // 24px
} };
// #enddocregion setStyles // #enddocregion setStyles
// compensate for DevMode (sigh) // compensate for DevMode (sigh)
if (JSON.stringify(styles) === JSON.stringify(this._priorStyles)){ if (JSON.stringify(styles) === JSON.stringify(this._priorStyles)) {
return this._priorStyles; return this._priorStyles;
} }
this._priorStyles = styles; this._priorStyles = styles;
@ -178,15 +184,14 @@ export class AppComponent implements AfterViewInit, OnInit {
// #enddocregion setStyles // #enddocregion setStyles
toeChoice = ''; toeChoice = '';
toeChooser(picker:HTMLFieldSetElement){ toeChooser(picker: HTMLFieldSetElement) {
let choices = picker.children; let choices = picker.children;
for (let i=0; i<choices.length; i++){ for (let i = 0; i < choices.length; i++) {
var choice = <HTMLInputElement>choices[i]; let choice = <HTMLInputElement>choices[i];
if (choice.checked) {return this.toeChoice = choice.value} if (choice.checked) {return this.toeChoice = choice.value; }
} }
} }
title = 'Template Syntax';
// #docregion trackByHeroes // #docregion trackByHeroes
trackByHeroes(index: number, hero: Hero) { return hero.id; } trackByHeroes(index: number, hero: Hero) { return hero.id; }
@ -196,18 +201,18 @@ export class AppComponent implements AfterViewInit, OnInit {
trackById(index: number, item: any): string { return item['id']; } trackById(index: number, item: any): string { return item['id']; }
// #enddocregion trackById // #enddocregion trackById
val=2; val = 2;
// villainImageUrl = 'http://www.clker.com/cliparts/u/s/y/L/x/9/villain-man-hi.png' // villainImageUrl = 'http://www.clker.com/cliparts/u/s/y/L/x/9/villain-man-hi.png'
// Public Domain terms of use http://www.clker.com/disclaimer.html // Public Domain terms of use http://www.clker.com/disclaimer.html
villainImageUrl = 'images/villain.png' villainImageUrl = 'images/villain.png';
//////// Detect effects of NgForTrackBy /////////////// //////// Detect effects of NgForTrackBy ///////////////
@ViewChildren('noTrackBy') childrenNoTrackBy:QueryList<ElementRef>; @ViewChildren('noTrackBy') childrenNoTrackBy: QueryList<ElementRef>;
@ViewChildren('withTrackBy') childrenWithTrackBy:QueryList<ElementRef>; @ViewChildren('withTrackBy') childrenWithTrackBy: QueryList<ElementRef>;
private _oldNoTrackBy:HTMLElement[]; private _oldNoTrackBy: HTMLElement[];
private _oldWithTrackBy:HTMLElement[]; private _oldWithTrackBy: HTMLElement[];
heroesNoTrackByChangeCount = 0; heroesNoTrackByChangeCount = 0;
heroesWithTrackByChangeCount = 0; heroesWithTrackByChangeCount = 0;
@ -216,32 +221,32 @@ export class AppComponent implements AfterViewInit, OnInit {
this._oldNoTrackBy = toArray(this.childrenNoTrackBy); this._oldNoTrackBy = toArray(this.childrenNoTrackBy);
this._oldWithTrackBy = toArray(this.childrenWithTrackBy); this._oldWithTrackBy = toArray(this.childrenWithTrackBy);
this.childrenNoTrackBy.changes.subscribe((changes:any) => { this.childrenNoTrackBy.changes.subscribe((changes: any) => {
let newNoTrackBy = toArray(changes); let newNoTrackBy = toArray(changes);
let isSame = this._oldNoTrackBy.every((v:any, i:number) => v === newNoTrackBy[i]); let isSame = this._oldNoTrackBy.every((v: any, i: number) => v === newNoTrackBy[i]);
if (!isSame) { if (!isSame) {
this._oldNoTrackBy = newNoTrackBy; this._oldNoTrackBy = newNoTrackBy;
this.heroesNoTrackByChangeCount++; this.heroesNoTrackByChangeCount++;
} }
}) });
this.childrenWithTrackBy.changes.subscribe((changes:any) => { this.childrenWithTrackBy.changes.subscribe((changes: any) => {
let newWithTrackBy = toArray(changes); let newWithTrackBy = toArray(changes);
let isSame = this._oldWithTrackBy.every((v:any, i:number) => v === newWithTrackBy[i]); let isSame = this._oldWithTrackBy.every((v: any, i: number) => v === newWithTrackBy[i]);
if (!isSame) { if (!isSame) {
this._oldWithTrackBy = newWithTrackBy; this._oldWithTrackBy = newWithTrackBy;
this.heroesWithTrackByChangeCount++; this.heroesWithTrackByChangeCount++;
} }
}) });
} }
/////////////////// ///////////////////
} }
// helper to convert viewChildren to an array of HTMLElements // helper to convert viewChildren to an array of HTMLElements
function toArray(viewChildren:QueryList<ElementRef>) { function toArray(viewChildren: QueryList<ElementRef>) {
let result: HTMLElement[] = []; let result: HTMLElement[] = [];
let children = viewChildren.toArray()[0].nativeElement.children; let children = viewChildren.toArray()[0].nativeElement.children;
for (var i = 0; i < children.length; i++) { result.push(children[i]); } for (let i = 0; i < children.length; i++) { result.push(children[i]); }
return result; return result;
} }

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Tutorial', function () { describe('Tutorial', function () {
beforeAll(function () { beforeAll(function () {
@ -23,19 +24,19 @@ describe('Tutorial', function () {
} }
it('should be able to see the start screen', function () { it('should be able to see the start screen', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices'); expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices');
expect(page.myDashboardHref.getText()).toEqual("Dashboard"); expect(page.myDashboardHref.getText()).toEqual("Dashboard");
expect(page.myHeroesHref.getText()).toEqual("Heroes"); expect(page.myHeroesHref.getText()).toEqual("Heroes");
}); });
it('should be able to see dashboard choices', function () { it('should be able to see dashboard choices', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.topHeroes.count()).toBe(4, "should be 4 dashboard hero choices"); expect(page.topHeroes.count()).toBe(4, "should be 4 dashboard hero choices");
}); });
it('should be able to toggle the views', function () { it('should be able to toggle the views', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.myDashboardParent.element(by.css('h3')).getText()).toEqual('Top Heroes'); expect(page.myDashboardParent.element(by.css('h3')).getText()).toEqual('Top Heroes');
page.myHeroesHref.click().then(function() { page.myHeroesHref.click().then(function() {
@ -49,11 +50,11 @@ describe('Tutorial', function () {
}); });
it('should be able to edit details from "Dashboard" view', function () { it('should be able to edit details from "Dashboard" view', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be available'); expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be available');
var heroEle = page.topHeroes.get(3); let heroEle = page.topHeroes.get(3);
var heroDescrEle = heroEle.element(by.css('h4')); let heroDescrEle = heroEle.element(by.css('h4'));
var heroDescr; let heroDescr;
return heroDescrEle.getText().then(function(text) { return heroDescrEle.getText().then(function(text) {
heroDescr = text; heroDescr = text;
return heroEle.click(); return heroEle.click();
@ -66,10 +67,10 @@ describe('Tutorial', function () {
}); });
it('should be able to edit details from "Heroes" view', function () { it('should be able to edit details from "Heroes" view', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be present'); expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be present');
var viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details')); let viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details'));
var heroEle, heroDescr; let heroEle, heroDescr;
page.myHeroesHref.click().then(function() { page.myHeroesHref.click().then(function() {
expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present'); expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present');
expect(page.myHeroesParent.isPresent()).toBe(true, 'myHeroes element should be present'); expect(page.myHeroesParent.isPresent()).toBe(true, 'myHeroes element should be present');
@ -96,11 +97,11 @@ describe('Tutorial', function () {
expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present'); expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present');
expect(page.myHeroesParent.isPresent()).toBe(false, 'myHeroes element should NOT be present'); expect(page.myHeroesParent.isPresent()).toBe(false, 'myHeroes element should NOT be present');
expect(page.heroDetail.isDisplayed()).toBe(true, 'should be able to see hero-details'); expect(page.heroDetail.isDisplayed()).toBe(true, 'should be able to see hero-details');
var inputEle = page.heroDetail.element(by.css('input')); let inputEle = page.heroDetail.element(by.css('input'));
expect(inputEle.isDisplayed()).toBe(true, 'should be able to see the input box'); expect(inputEle.isDisplayed()).toBe(true, 'should be able to see the input box');
var backButtonEle = page.heroDetail.element(by.css('button')); let backButtonEle = page.heroDetail.element(by.css('button'));
expect(backButtonEle.isDisplayed()).toBe(true, 'should be able to see the back button'); expect(backButtonEle.isDisplayed()).toBe(true, 'should be able to see the back button');
var detailTextEle = page.heroDetail.element(by.css('div h2')); let detailTextEle = page.heroDetail.element(by.css('div h2'));
expect(detailTextEle.getText()).toContain(origValue); expect(detailTextEle.getText()).toContain(origValue);
return sendKeys(inputEle, textToAdd).then(function () { return sendKeys(inputEle, textToAdd).then(function () {
expect(detailTextEle.getText()).toContain(origValue + textToAdd); expect(detailTextEle.getText()).toContain(origValue + textToAdd);

View File

@ -19,9 +19,8 @@ export class HeroService {
//#docregion get-hero //#docregion get-hero
getHero(id: number) { getHero(id: number) {
return Promise.resolve(HEROES).then( return this.getHeroes()
heroes => heroes.filter(hero => hero.id === id)[0] .then(heroes => heroes.filter(hero => hero.id === id)[0]);
);
} }
//#enddocregion get-hero //#enddocregion get-hero
} }

View File

@ -1,3 +1,4 @@
/// <reference path='../_protractor/e2e.d.ts' />
describe('TOH Http Chapter', function () { describe('TOH Http Chapter', function () {
beforeEach(function () { beforeEach(function () {
@ -5,7 +6,7 @@ describe('TOH Http Chapter', function () {
}); });
function getPageStruct() { function getPageStruct() {
hrefEles = element.all(by.css('my-app a')); let hrefEles = element.all(by.css('my-app a'));
return { return {
hrefs: hrefEles, hrefs: hrefEles,
@ -22,12 +23,12 @@ describe('TOH Http Chapter', function () {
addButton: element.all(by.buttonText('Add New Hero')).get(0), addButton: element.all(by.buttonText('Add New Hero')).get(0),
heroDetail: element(by.css('my-app my-hero-detail')) heroDetail: element(by.css('my-app my-hero-detail'))
} };
} }
it('should be able to add a hero from the "Heroes" view', function(){ it('should be able to add a hero from the "Heroes" view', function(){
var page = getPageStruct(); let page = getPageStruct();
var heroCount; let heroCount: webdriver.promise.Promise<number>;
page.myHeroesHref.click().then(function() { page.myHeroesHref.click().then(function() {
browser.waitForAngular(); browser.waitForAngular();
@ -43,14 +44,14 @@ describe('TOH Http Chapter', function () {
heroCount = page.allHeroes.count(); heroCount = page.allHeroes.count();
expect(heroCount).toBe(11, 'should show 11'); expect(heroCount).toBe(11, 'should show 11');
var newHero = element(by.xpath('//span[@class="hero-element" and contains(text(),"The New Hero")]')); let newHero = element(by.xpath('//span[@class="hero-element" and contains(text(),"The New Hero")]'));
expect(newHero).toBeDefined(); expect(newHero).toBeDefined();
}); });
}); });
it('should be able to delete hero from "Heroes" view', function(){ it('should be able to delete hero from "Heroes" view', function(){
var page = getPageStruct(); let page = getPageStruct();
var heroCount; let heroCount: webdriver.promise.Promise<number>;
page.myHeroesHref.click().then(function() { page.myHeroesHref.click().then(function() {
browser.waitForAngular(); browser.waitForAngular();
@ -66,11 +67,11 @@ describe('TOH Http Chapter', function () {
}); });
it('should be able to save details from "Dashboard" view', function () { it('should be able to save details from "Dashboard" view', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be available'); expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be available');
var heroEle = page.topHeroes.get(2); let heroEle = page.topHeroes.get(2);
var heroDescrEle = heroEle.element(by.css('h4')); let heroDescrEle = heroEle.element(by.css('h4'));
var heroDescr; let heroDescr: string;
return heroDescrEle.getText().then(function(text) { return heroDescrEle.getText().then(function(text) {
heroDescr = text; heroDescr = text;
@ -88,10 +89,10 @@ describe('TOH Http Chapter', function () {
}); });
it('should be able to save details from "Heroes" view', function () { it('should be able to save details from "Heroes" view', function () {
var page = getPageStruct(); let page = getPageStruct();
var viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details')); let viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details'));
var heroEle, heroDescr; let heroEle: protractor.ElementFinder, heroDescr: string;
page.myHeroesHref.click().then(function() { page.myHeroesHref.click().then(function() {
expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present'); expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present');
@ -101,7 +102,7 @@ describe('TOH Http Chapter', function () {
return heroEle.getText(); return heroEle.getText();
}).then(function(text) { }).then(function(text) {
// remove leading 'id' from the element // remove leading 'id' from the element
heroDescr = text.substr(text.indexOf(' ')+1); heroDescr = text.substr(text.indexOf(' ') + 1);
return heroEle.click(); return heroEle.click();
}).then(function() { }).then(function() {
expect(viewDetailsButtonEle.isDisplayed()).toBe(true, 'viewDetails button should now be visible'); expect(viewDetailsButtonEle.isDisplayed()).toBe(true, 'viewDetails button should now be visible');
@ -117,13 +118,13 @@ describe('TOH Http Chapter', function () {
}); });
}); });
function save(page, origValue, textToAdd) { function save(page: any, origValue: string, textToAdd: string) {
var inputEle = page.heroDetail.element(by.css('input')); let inputEle = page.heroDetail.element(by.css('input'));
expect(inputEle.isDisplayed()).toBe(true, 'should be able to see the input box'); expect(inputEle.isDisplayed()).toBe(true, 'should be able to see the input box');
var saveButtonEle = page.heroDetail.element(by.buttonText('Save')); let saveButtonEle = page.heroDetail.element(by.buttonText('Save'));
var backButtonEle = page.heroDetail.element(by.buttonText('Back')); let backButtonEle = page.heroDetail.element(by.buttonText('Back'));
expect(backButtonEle.isDisplayed()).toBe(true, 'should be able to see the back button'); expect(backButtonEle.isDisplayed()).toBe(true, 'should be able to see the back button');
var detailTextEle = page.heroDetail.element(by.css('div h2')); let detailTextEle = page.heroDetail.element(by.css('div h2'));
expect(detailTextEle.getText()).toContain(origValue); expect(detailTextEle.getText()).toContain(origValue);
return sendKeys(inputEle, textToAdd).then(function () { return sendKeys(inputEle, textToAdd).then(function () {
expect(detailTextEle.getText()).toContain(origValue + textToAdd); expect(detailTextEle.getText()).toContain(origValue + textToAdd);
@ -132,24 +133,24 @@ describe('TOH Http Chapter', function () {
} }
it('should be able to see the start screen', function () { it('should be able to see the start screen', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices'); expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices');
expect(page.myDashboardHref.getText()).toEqual("Dashboard"); expect(page.myDashboardHref.getText()).toEqual('Dashboard');
expect(page.myHeroesHref.getText()).toEqual("Heroes"); expect(page.myHeroesHref.getText()).toEqual('Heroes');
}); });
it('should be able to see dashboard choices', function () { it('should be able to see dashboard choices', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.topHeroes.count()).toBe(4, "should be 4 dashboard hero choices"); expect(page.topHeroes.count()).toBe(4, 'should be 4 dashboard hero choices');
}); });
it('should be able to toggle the views', function () { it('should be able to toggle the views', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.myDashboardParent.element(by.css('h3')).getText()).toEqual('Top Heroes'); expect(page.myDashboardParent.element(by.css('h3')).getText()).toEqual('Top Heroes');
page.myHeroesHref.click().then(function() { page.myHeroesHref.click().then(function() {
expect(page.myDashboardParent.isPresent()).toBe(false, 'should no longer see dashboard element'); expect(page.myDashboardParent.isPresent()).toBe(false, 'should no longer see dashboard element');
expect(page.allHeroes.count()).toBeGreaterThan(4, "should be more than 4 heroes shown"); expect(page.allHeroes.count()).toBeGreaterThan(4, 'should be more than 4 heroes shown');
return page.myDashboardHref.click(); return page.myDashboardHref.click();
}).then(function() { }).then(function() {
expect(page.myDashboardParent.isPresent()).toBe(true, 'should once again see the dashboard element'); expect(page.myDashboardParent.isPresent()).toBe(true, 'should once again see the dashboard element');
@ -158,11 +159,11 @@ describe('TOH Http Chapter', function () {
}); });
it('should be able to edit details from "Dashboard" view', function () { it('should be able to edit details from "Dashboard" view', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be available'); expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be available');
var heroEle = page.topHeroes.get(3); let heroEle = page.topHeroes.get(3);
var heroDescrEle = heroEle.element(by.css('h4')); let heroDescrEle = heroEle.element(by.css('h4'));
var heroDescr; let heroDescr: string;
return heroDescrEle.getText().then(function(text) { return heroDescrEle.getText().then(function(text) {
heroDescr = text; heroDescr = text;
return heroEle.click(); return heroEle.click();
@ -175,10 +176,10 @@ describe('TOH Http Chapter', function () {
}); });
it('should be able to edit details from "Heroes" view', function () { it('should be able to edit details from "Heroes" view', function () {
var page = getPageStruct(); let page = getPageStruct();
expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be present'); expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be present');
var viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details')); let viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details'));
var heroEle, heroDescr; let heroEle: protractor.ElementFinder, heroDescr: string;
page.myHeroesHref.click().then(function() { page.myHeroesHref.click().then(function() {
expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present'); expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present');
expect(page.myHeroesParent.isPresent()).toBe(true, 'myHeroes element should be present'); expect(page.myHeroesParent.isPresent()).toBe(true, 'myHeroes element should be present');
@ -187,7 +188,7 @@ describe('TOH Http Chapter', function () {
return heroEle.getText(); return heroEle.getText();
}).then(function(text) { }).then(function(text) {
// remove leading 'id' from the element // remove leading 'id' from the element
heroDescr = text.substr(text.indexOf(' ')+1); heroDescr = text.substr(text.indexOf(' ') + 1);
return heroEle.click(); return heroEle.click();
}).then(function() { }).then(function() {
expect(viewDetailsButtonEle.isDisplayed()).toBe(true, 'viewDetails button should now be visible'); expect(viewDetailsButtonEle.isDisplayed()).toBe(true, 'viewDetails button should now be visible');
@ -201,18 +202,18 @@ describe('TOH Http Chapter', function () {
}); });
}); });
function editDetails(page, origValue, textToAdd) { function editDetails(page: any, origValue: string, textToAdd: string) {
expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present'); expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present');
expect(page.myHeroesParent.isPresent()).toBe(false, 'myHeroes element should NOT be present'); expect(page.myHeroesParent.isPresent()).toBe(false, 'myHeroes element should NOT be present');
expect(page.heroDetail.isDisplayed()).toBe(true, 'should be able to see hero-details'); expect(page.heroDetail.isDisplayed()).toBe(true, 'should be able to see hero-details');
var inputEle = page.heroDetail.element(by.css('input')); let inputEle = page.heroDetail.element(by.css('input'));
expect(inputEle.isDisplayed()).toBe(true, 'should be able to see the input box'); expect(inputEle.isDisplayed()).toBe(true, 'should be able to see the input box');
var buttons = page.heroDetail.all(by.css('button')); let buttons = page.heroDetail.all(by.css('button'));
var backButtonEle = buttons.get(0); let backButtonEle = buttons.get(0);
var saveButtonEle = buttons.get(1); let saveButtonEle = buttons.get(1);
expect(backButtonEle.isDisplayed()).toBe(true, 'should be able to see the back button'); expect(backButtonEle.isDisplayed()).toBe(true, 'should be able to see the back button');
expect(saveButtonEle.isDisplayed()).toBe(true, 'should be able to see the save button'); expect(saveButtonEle.isDisplayed()).toBe(true, 'should be able to see the save button');
var detailTextEle = page.heroDetail.element(by.css('div h2')); let detailTextEle = page.heroDetail.element(by.css('div h2'));
expect(detailTextEle.getText()).toContain(origValue); expect(detailTextEle.getText()).toContain(origValue);
return sendKeys(inputEle, textToAdd).then(function () { return sendKeys(inputEle, textToAdd).then(function () {
expect(detailTextEle.getText()).toContain(origValue + textToAdd); expect(detailTextEle.getText()).toContain(origValue + textToAdd);

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('Upgrade Tests', function () { describe('Upgrade Tests', function () {
// Protractor doesn't support the UpgradeAdapter's asynchronous // Protractor doesn't support the UpgradeAdapter's asynchronous

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
'use strict'; 'use strict';
// Angular E2E Testing Guide: // Angular E2E Testing Guide:
@ -22,8 +23,8 @@ describe('PhoneCat Application', function() {
}); });
it('should filter the phone list as a user types into the search box', function() { it('should filter the phone list as a user types into the search box', function() {
var phoneList = element.all(by.repeater('phone in $ctrl.phones')); let phoneList = element.all(by.repeater('phone in $ctrl.phones'));
var query = element(by.model('$ctrl.query')); let query = element(by.model('$ctrl.query'));
expect(phoneList.count()).toBe(20); expect(phoneList.count()).toBe(20);
@ -36,10 +37,10 @@ describe('PhoneCat Application', function() {
}); });
it('should be possible to control phone order via the drop-down menu', function() { it('should be possible to control phone order via the drop-down menu', function() {
var queryField = element(by.model('$ctrl.query')); let queryField = element(by.model('$ctrl.query'));
var orderSelect = element(by.model('$ctrl.orderProp')); let orderSelect = element(by.model('$ctrl.orderProp'));
var nameOption = orderSelect.element(by.css('option[value="name"]')); let nameOption = orderSelect.element(by.css('option[value="name"]'));
var phoneNameColumn = element.all(by.repeater('phone in $ctrl.phones').column('phone.name')); let phoneNameColumn = element.all(by.repeater('phone in $ctrl.phones').column('phone.name'));
function getNames() { function getNames() {
return phoneNameColumn.map(function(elem) { return phoneNameColumn.map(function(elem) {
@ -63,7 +64,7 @@ describe('PhoneCat Application', function() {
}); });
it('should render phone specific links', function() { it('should render phone specific links', function() {
var query = element(by.model('$ctrl.query')); let query = element(by.model('$ctrl.query'));
query.sendKeys('nexus'); query.sendKeys('nexus');
element.all(by.css('.phones li a')).first().click(); element.all(by.css('.phones li a')).first().click();
@ -83,14 +84,14 @@ describe('PhoneCat Application', function() {
}); });
it('should display the first phone image as the main phone image', function() { it('should display the first phone image as the main phone image', function() {
var mainImage = element(by.css('img.phone.selected')); let mainImage = element(by.css('img.phone.selected'));
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/); expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
}); });
it('should swap the main image when clicking on a thumbnail image', function() { it('should swap the main image when clicking on a thumbnail image', function() {
var mainImage = element(by.css('img.phone.selected')); let mainImage = element(by.css('img.phone.selected'));
var thumbnails = element.all(by.css('.phone-thumbs img')); let thumbnails = element.all(by.css('.phone-thumbs img'));
thumbnails.get(2).click(); thumbnails.get(2).click();
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/); expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/typings/index.d.ts" />
'use strict'; 'use strict';
// Angular E2E Testing Guide: // Angular E2E Testing Guide:
@ -21,8 +22,8 @@ describe('PhoneCat Application', function() {
}); });
it('should filter the phone list as a user types into the search box', function() { it('should filter the phone list as a user types into the search box', function() {
var phoneList = element.all(by.css('.phones li')); let phoneList = element.all(by.css('.phones li'));
var query = element(by.css('input')); let query = element(by.css('input'));
expect(phoneList.count()).toBe(20); expect(phoneList.count()).toBe(20);
@ -35,10 +36,10 @@ describe('PhoneCat Application', function() {
}); });
it('should be possible to control phone order via the drop-down menu', function() { it('should be possible to control phone order via the drop-down menu', function() {
var queryField = element(by.css('input')); let queryField = element(by.css('input'));
var orderSelect = element(by.css('select')); let orderSelect = element(by.css('select'));
var nameOption = orderSelect.element(by.css('option[value="name"]')); let nameOption = orderSelect.element(by.css('option[value="name"]'));
var phoneNameColumn = element.all(by.css('.phones .name')); let phoneNameColumn = element.all(by.css('.phones .name'));
function getNames() { function getNames() {
return phoneNameColumn.map(function(elem) { return phoneNameColumn.map(function(elem) {
@ -62,7 +63,7 @@ describe('PhoneCat Application', function() {
}); });
it('should render phone specific links', function() { it('should render phone specific links', function() {
var query = element(by.css('input')); let query = element(by.css('input'));
sendKeys(query, 'nexus'); sendKeys(query, 'nexus');
element.all(by.css('.phones li a')).first().click(); element.all(by.css('.phones li a')).first().click();
@ -83,14 +84,14 @@ describe('PhoneCat Application', function() {
}); });
it('should display the first phone image as the main phone image', function() { it('should display the first phone image as the main phone image', function() {
var mainImage = element(by.css('img.phone.selected')); let mainImage = element(by.css('img.phone.selected'));
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/); expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
}); });
it('should swap the main image when clicking on a thumbnail image', function() { it('should swap the main image when clicking on a thumbnail image', function() {
var mainImage = element(by.css('img.phone.selected')); let mainImage = element(by.css('img.phone.selected'));
var thumbnails = element.all(by.css('.phone-thumbs img')); let thumbnails = element.all(by.css('.phone-thumbs img'));
thumbnails.get(2).click(); thumbnails.get(2).click();
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/); expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
'use strict'; 'use strict';
// Angular E2E Testing Guide: // Angular E2E Testing Guide:
@ -22,8 +23,8 @@ describe('PhoneCat Application', function() {
}); });
it('should filter the phone list as a user types into the search box', function() { it('should filter the phone list as a user types into the search box', function() {
var phoneList = element.all(by.css('.phones li')); let phoneList = element.all(by.css('.phones li'));
var query = element(by.css('input')); let query = element(by.css('input'));
expect(phoneList.count()).toBe(20); expect(phoneList.count()).toBe(20);
@ -36,10 +37,10 @@ describe('PhoneCat Application', function() {
}); });
it('should be possible to control phone order via the drop-down menu', function() { it('should be possible to control phone order via the drop-down menu', function() {
var queryField = element(by.css('input')); let queryField = element(by.css('input'));
var orderSelect = element(by.css('select')); let orderSelect = element(by.css('select'));
var nameOption = orderSelect.element(by.css('option[value="name"]')); let nameOption = orderSelect.element(by.css('option[value="name"]'));
var phoneNameColumn = element.all(by.css('.phones .name')); let phoneNameColumn = element.all(by.css('.phones .name'));
function getNames() { function getNames() {
return phoneNameColumn.map(function(elem) { return phoneNameColumn.map(function(elem) {
@ -64,10 +65,10 @@ describe('PhoneCat Application', function() {
// #docregion links // #docregion links
it('should render phone specific links', function() { it('should render phone specific links', function() {
var query = element(by.css('input')); let query = element(by.css('input'));
// https://github.com/angular/protractor/issues/2019 // https://github.com/angular/protractor/issues/2019
var str = 'nexus'; let str = 'nexus';
for (var i = 0; i < str.length; i++) { for (let i = 0; i < str.length; i++) {
query.sendKeys(str.charAt(i)); query.sendKeys(str.charAt(i));
} }
element.all(by.css('.phones li a')).first().click(); element.all(by.css('.phones li a')).first().click();
@ -90,14 +91,14 @@ describe('PhoneCat Application', function() {
}); });
it('should display the first phone image as the main phone image', function() { it('should display the first phone image as the main phone image', function() {
var mainImage = element(by.css('img.phone.selected')); let mainImage = element(by.css('img.phone.selected'));
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/); expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
}); });
it('should swap the main image when clicking on a thumbnail image', function() { it('should swap the main image when clicking on a thumbnail image', function() {
var mainImage = element(by.css('img.phone.selected')); let mainImage = element(by.css('img.phone.selected'));
var thumbnails = element.all(by.css('.phone-thumbs img')); let thumbnails = element.all(by.css('.phone-thumbs img'));
thumbnails.get(2).click(); thumbnails.get(2).click();
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/); expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);

View File

@ -1,3 +1,4 @@
/// <reference path="../_protractor/e2e.d.ts" />
describe('User Input Tests', function () { describe('User Input Tests', function () {
beforeAll(function () { beforeAll(function () {
@ -5,8 +6,8 @@ describe('User Input Tests', function () {
}); });
it('should support the click event', function () { it('should support the click event', function () {
var mainEle = element(by.css('click-me')); let mainEle = element(by.css('click-me'));
var buttonEle =element(by.css('click-me button')); let buttonEle =element(by.css('click-me button'));
expect(mainEle.getText()).not.toContain('You are my hero!'); expect(mainEle.getText()).not.toContain('You are my hero!');
buttonEle.click().then(function() { buttonEle.click().then(function() {
expect(mainEle.getText()).toContain('You are my hero!'); expect(mainEle.getText()).toContain('You are my hero!');
@ -14,8 +15,8 @@ describe('User Input Tests', function () {
}); });
it('should support the click event with an event payload', function () { it('should support the click event with an event payload', function () {
var mainEle = element(by.css('click-me2')); let mainEle = element(by.css('click-me2'));
var buttonEle =element(by.css('click-me2 button')); let buttonEle =element(by.css('click-me2 button'));
expect(mainEle.getText()).not.toContain('Event target is '); expect(mainEle.getText()).not.toContain('Event target is ');
buttonEle.click().then(function() { buttonEle.click().then(function() {
expect(mainEle.getText()).toContain('Event target is BUTTON'); expect(mainEle.getText()).toContain('Event target is BUTTON');
@ -23,19 +24,19 @@ describe('User Input Tests', function () {
}); });
it('should support the keyup event ', function () { it('should support the keyup event ', function () {
var mainEle = element(by.css('key-up1')); let mainEle = element(by.css('key-up1'));
var inputEle = mainEle.element(by.css('input')); let inputEle = mainEle.element(by.css('input'));
var outputTextEle = mainEle.element(by.css('p')); let outputTextEle = mainEle.element(by.css('p'));
expect(outputTextEle.getText()).toEqual(''); expect(outputTextEle.getText()).toEqual('');
return sendKeys(inputEle,'abc').then(function() { return sendKeys(inputEle,'abc').then(function() {
expect(outputTextEle.getText()).toEqual('a | ab | abc |'); expect(outputTextEle.getText()).toEqual('a | ab | abc |');
}); });
}); });
it('should support user input from a local template var (loopback)', function () { it('should support user input from a local template let (loopback)', function () {
var mainEle = element(by.css('loop-back')); let mainEle = element(by.css('loop-back'));
var inputEle = mainEle.element(by.css('input')); let inputEle = mainEle.element(by.css('input'));
var outputTextEle = mainEle.element(by.css('p')); let outputTextEle = mainEle.element(by.css('p'));
expect(outputTextEle.getText()).toEqual(''); expect(outputTextEle.getText()).toEqual('');
return sendKeys(inputEle,'abc').then(function() { return sendKeys(inputEle,'abc').then(function() {
expect(outputTextEle.getText()).toEqual('abc'); expect(outputTextEle.getText()).toEqual('abc');
@ -43,9 +44,9 @@ describe('User Input Tests', function () {
}); });
it('should be able to combine click event with a local template var', function () { it('should be able to combine click event with a local template var', function () {
var mainEle = element(by.css('key-up2')); let mainEle = element(by.css('key-up2'));
var inputEle = mainEle.element(by.css('input')); let inputEle = mainEle.element(by.css('input'));
var outputTextEle = mainEle.element(by.css('p')); let outputTextEle = mainEle.element(by.css('p'));
expect(outputTextEle.getText()).toEqual(''); expect(outputTextEle.getText()).toEqual('');
return sendKeys(inputEle,'abc').then(function() { return sendKeys(inputEle,'abc').then(function() {
expect(outputTextEle.getText()).toEqual('a | ab | abc |'); expect(outputTextEle.getText()).toEqual('a | ab | abc |');
@ -53,9 +54,9 @@ describe('User Input Tests', function () {
}); });
it('should be able to filter key events', function () { it('should be able to filter key events', function () {
var mainEle = element(by.css('key-up3')); let mainEle = element(by.css('key-up3'));
var inputEle = mainEle.element(by.css('input')); let inputEle = mainEle.element(by.css('input'));
var outputTextEle = mainEle.element(by.css('p')); let outputTextEle = mainEle.element(by.css('p'));
expect(outputTextEle.getText()).toEqual(''); expect(outputTextEle.getText()).toEqual('');
return sendKeys(inputEle,'abc').then(function() { return sendKeys(inputEle,'abc').then(function() {
expect(outputTextEle.getText()).toEqual('', 'should be blank - have not sent enter yet'); expect(outputTextEle.getText()).toEqual('', 'should be blank - have not sent enter yet');
@ -66,10 +67,10 @@ describe('User Input Tests', function () {
}); });
it('should be able to filter blur events', function () { it('should be able to filter blur events', function () {
var prevInputEle = element(by.css('key-up3 input')); let prevInputEle = element(by.css('key-up3 input'));
var mainEle = element(by.css('key-up4')); let mainEle = element(by.css('key-up4'));
var inputEle = mainEle.element(by.css('input')); let inputEle = mainEle.element(by.css('input'));
var outputTextEle = mainEle.element(by.css('p')); let outputTextEle = mainEle.element(by.css('p'));
expect(outputTextEle.getText()).toEqual(''); expect(outputTextEle.getText()).toEqual('');
return sendKeys(inputEle,'abc').then(function() { return sendKeys(inputEle,'abc').then(function() {
expect(outputTextEle.getText()).toEqual('', 'should be blank - have not sent enter yet'); expect(outputTextEle.getText()).toEqual('', 'should be blank - have not sent enter yet');
@ -81,11 +82,11 @@ describe('User Input Tests', function () {
}); });
it('should be able to compose little tour of heroes', function () { it('should be able to compose little tour of heroes', function () {
var mainEle = element(by.css('little-tour')); let mainEle = element(by.css('little-tour'));
var inputEle = mainEle.element(by.css('input')); let inputEle = mainEle.element(by.css('input'));
var addButtonEle = mainEle.element(by.css('button')); let addButtonEle = mainEle.element(by.css('button'));
var heroEles = mainEle.all(by.css('li')); let heroEles = mainEle.all(by.css('li'));
var numHeroes; let numHeroes;
expect(heroEles.count()).toBeGreaterThan(0); expect(heroEles.count()).toBeGreaterThan(0);
heroEles.count().then(function(count) { heroEles.count().then(function(count) {
numHeroes = count; numHeroes = count;

View File

@ -11,7 +11,7 @@
h3 Adding an aside h3 Adding an aside
aside.is-right Did you know that hipsum is a replacment for Lorem Ipsum? To find out more visit <a href="http://hipsum.co/">hipsum.co</a> aside.is-right Did you know that hipsum is a replacement for Lorem Ipsum? To find out more visit <a href="http://hipsum.co/">hipsum.co</a>
p. p.
Etsy artisan Thundercats, authentic sustainable bitters wolf roof party meditation 90's asymmetrical XOXO hoodie. Twee umami cray iPhone. Chillwave shabby chic tilde occupy sriracha squid Brooklyn street art. Selvage heirloom kogi American Apparel bicycle rights. Carles Etsy Truffaut mlkshk trust fund. Jean shorts fashion axe Williamsburg wolf cardigan beard, twee blog locavore organic. Cred skateboard dreamcatcher, taxidermy Bushwick actually aesthetic normcore fanny pack. Etsy artisan Thundercats, authentic sustainable bitters wolf roof party meditation 90's asymmetrical XOXO hoodie. Twee umami cray iPhone. Chillwave shabby chic tilde occupy sriracha squid Brooklyn street art. Selvage heirloom kogi American Apparel bicycle rights. Carles Etsy Truffaut mlkshk trust fund. Jean shorts fashion axe Williamsburg wolf cardigan beard, twee blog locavore organic. Cred skateboard dreamcatcher, taxidermy Bushwick actually aesthetic normcore fanny pack.
@ -19,7 +19,7 @@
pre.prettyprint.linenums.lang-html pre.prettyprint.linenums.lang-html
code. code.
aside.is-right Did you know that hipsum is a replacment for Lorem Ipsum? To find out more visit <a href="http://hipsum.co/">hipsum.co</a> aside.is-right Did you know that hipsum is a replacement for Lorem Ipsum? To find out more visit <a href="http://hipsum.co/">hipsum.co</a>
p. p.
Etsy artisan Thundercats, authentic sustainable bitters Etsy artisan Thundercats, authentic sustainable bitters

View File

@ -12,7 +12,7 @@ include ../../../_includes/_util-fns
:marked :marked
### Including a code example from the `_examples` folder ### Including a code example from the `_examples` folder
One of the design goals for this documention was that any code samples that appear within the documentation be 'testable'. One of the design goals for this documentation was that any code samples that appear within the documentation be 'testable'.
In practice this means that a set of standalone testable examples exist somewhere in the same repository as the rest In practice this means that a set of standalone testable examples exist somewhere in the same repository as the rest
of the documentation. These examples will each typically consist of a collection of html, javascript and css files. of the documentation. These examples will each typically consist of a collection of html, javascript and css files.
@ -130,7 +130,7 @@ include ../../../_includes/_util-fns
Multiple `#docregion` tags may be defined on a single line as shown below. In addition, anytime a file contains multiple Multiple `#docregion` tags may be defined on a single line as shown below. In addition, anytime a file contains multiple
`#docregion` tags with the same name they will automatically be combined. Each of the individually tagged sections of the combined document `#docregion` tags with the same name they will automatically be combined. Each of the individually tagged sections of the combined document
will be separated from one another by a comment consisting of '. . .'. This default separator, known will be separated from one another by a comment consisting of '. . .'. This default separator, known
as 'plaster' can be overriden anywhere within the affected file via a `#docplaster` comment as shown below. This example creates as 'plaster' can be overridden anywhere within the affected file via a `#docplaster` comment as shown below. This example creates
a separator that consists of `/* more code here */` in the output file. a separator that consists of `/* more code here */` in the output file.
code-example(format="linenums" language="js" escape="html"). code-example(format="linenums" language="js" escape="html").

View File

@ -2,7 +2,7 @@
header.showcase-header header.showcase-header
h2 Basic Layouts h2 Basic Layouts
p. p.
You will use the following layouts throughout your documenation You will use the following layouts throughout your documentation
to specify sections and sub-sections of content. to specify sections and sub-sections of content.
.showcase-content .showcase-content
@ -23,4 +23,4 @@
code-example(language="html" format="linenums"). code-example(language="html" format="linenums").
.l-sub-section .l-sub-section
h3 Sub Section Title h3 Sub Section Title
p sub section content... p sub section content...

View File

@ -2,7 +2,7 @@
header.showcase-header header.showcase-header
h2 Tables h2 Tables
p. p.
Tables can be used to present tablular data as it relates Tables can be used to present tabular data as it relates
to each other. to each other.
.showcase-content .showcase-content
@ -37,4 +37,4 @@
tr tr
td Angular 1.3 td Angular 1.3
td Routing td Routing
td fast td fast

View File

@ -5,16 +5,20 @@ include ../../../_includes/_util-fns
- var _decorator = 'annotation'; - var _decorator = 'annotation';
- var _Array = 'List'; - var _Array = 'List';
- var _array = 'list'; - var _array = 'list';
- var _an_array = 'a list'; //- Deprecate now that we have the articles
- var _a = 'an'; - var _a = 'an';
- var _an = 'a'; - var _an = 'a';
- var _priv = '_'; - var _priv = '_';
- var _Lang = 'Dart'; - var _Lang = 'Dart';
- var _Promise = 'Future'; - var _Promise = 'Future';
- var _FutureUrl = 'https://api.dartlang.org/dart_async/Future.html';
- var _PromiseLinked = '<a href="' + _FutureUrl + '">' + _Promise + '</a>';
- var _Observable = 'Stream'; - var _Observable = 'Stream';
- var _liveLink = 'sample repo'; - var _liveLink = 'sample repo';
- var _truthy = 'true'; - var _truthy = 'true';
- var _falsey = 'false'; - var _falsey = 'false';
- var _appDir = 'lib';
- var _indexHtmlDir = 'web';
- var _mainDir = 'web';
mixin liveExampleLink(linkText, exampleUrlPartName) mixin liveExampleLink(linkText, exampleUrlPartName)
- var text = linkText || '在线例子'; - var text = linkText || '在线例子';
@ -36,7 +40,7 @@ mixin liveExampleLink2(linkText, exampleUrlPartName)
- // if(extn == 'dart') return path; - // if(extn == 'dart') return path;
- var baseName = getBaseFileName(path) || path; // TODO: have getBaseFileName() return path - var baseName = getBaseFileName(path) || path; // TODO: have getBaseFileName() return path
- var baseNameNoExt = baseName.substr(0,baseName.length - (extn.length + 1)); - var baseNameNoExt = baseName.substr(0,baseName.length - (extn.length + 1));
- var inWebFolder = baseNameNoExt.match(/^(main|index(\.\d)?)$/); - var inWebFolder = baseNameNoExt.match(/^(main|index)(\.\d)?$/);
- // Adjust the folder path, e.g., ts -> dart - // Adjust the folder path, e.g., ts -> dart
- folder = folder.replace(/(^|\/)ts($|\/)/, '$1dart$2').replace(/(^|\/)app($|\/)/, inWebFolder ? '$1web$2' : '$1lib$2'); - folder = folder.replace(/(^|\/)ts($|\/)/, '$1dart$2').replace(/(^|\/)app($|\/)/, inWebFolder ? '$1web$2' : '$1lib$2');
- // Special case not handled above: e.g., index.html -> web/index.html - // Special case not handled above: e.g., index.html -> web/index.html

View File

@ -6,8 +6,6 @@ block includes
- var _prereq = 'the Dart SDK' - var _prereq = 'the Dart SDK'
- var _angular_browser_uri = 'package:angular2/platform/browser.dart' - var _angular_browser_uri = 'package:angular2/platform/browser.dart'
- var _angular_core_uri = 'package:angular2/core.dart' - var _angular_core_uri = 'package:angular2/core.dart'
- var _appDir = 'lib'
- var _indexHtmlDir = 'web'
block setup-tooling block setup-tooling
:marked :marked

View File

@ -29,5 +29,10 @@
"title": "Routing", "title": "Routing",
"intro": "We add the Angular Component Router and learn to navigate among the views", "intro": "We add the Angular Component Router and learn to navigate among the views",
"nextable": true "nextable": true
},
"toh-pt6": {
"title": "Http",
"intro": "We convert our service and components to use Http",
"nextable": true
} }
} }

View File

@ -131,7 +131,7 @@ code-example(format=".").
.l-sub-section .l-sub-section
:marked :marked
We explain input properties in more detail [here](../guide/attribute-directives.html#why-input) We explain input properties in more detail [here](../guide/attribute-directives.html#why-input)
where we also explain why *target* properties require this special treament and where we also explain why *target* properties require this special treatment and
*source* properties do not. *source* properties do not.
:marked :marked
There are a couple of ways we can declare that `hero` is an *input*. There are a couple of ways we can declare that `hero` is an *input*.

View File

@ -0,0 +1 @@
!= partial("../../../_includes/_ts-temp")

View File

@ -489,7 +489,7 @@ figure.image-display
We just set a template local variable with the value of an `NgForm` directive. We just set a template local variable with the value of an `NgForm` directive.
Why did that work? We didn't add the **[`NgForm`](../api/common/NgForm-directive.html) directive** explicitly. Why did that work? We didn't add the **[`NgForm`](../api/common/NgForm-directive.html) directive** explicitly.
Angular added it surreptiously, wrapping it around the `<form>` element Angular added it surreptitiously, wrapping it around the `<form>` element
The `NgForm` directive supplements the `form` element with additional features. The `NgForm` directive supplements the `form` element with additional features.
It collects `Controls` (elements identified by an `ngControl` directive) It collects `Controls` (elements identified by an `ngControl` directive)
@ -531,7 +531,7 @@ figure.image-display
Re-run the application. The form opens in a valid state and the button is enabled. Re-run the application. The form opens in a valid state and the button is enabled.
Now delete the *Name*. We violate the "name required" rule which Now delete the *Name*. We violate the "name required" rule which
is duely noted in our error message as before. And now the Submit button is also disabled. is duly noted in our error message as before. And now the Submit button is also disabled.
Not impressed? Think about it for a moment. What would we have to do to Not impressed? Think about it for a moment. What would we have to do to
wire the button's enable/disabled state to the form's validity without Angular's help? wire the button's enable/disabled state to the form's validity without Angular's help?

View File

@ -448,7 +448,7 @@ code-example(format="").
True, most Angular applications run only in a browser and we'll call the bootstrap function from True, most Angular applications run only in a browser and we'll call the bootstrap function from
this library most of the time. It's pretty "core" if we're always writing for a browser. this library most of the time. It's pretty "core" if we're always writing for a browser.
But it is possible to load a component in a different enviroment. But it is possible to load a component in a different environment.
We might load it on a mobile device with [Apache Cordova](https://cordova.apache.org/) or [NativeScript](https://www.nativescript.org/). We might load it on a mobile device with [Apache Cordova](https://cordova.apache.org/) or [NativeScript](https://www.nativescript.org/).
We might wish to render the first page of our application on the server We might wish to render the first page of our application on the server
to improve launch performance or facilitate to improve launch performance or facilitate

View File

@ -29,5 +29,10 @@
"title": "Routing", "title": "Routing",
"intro": "We add the Angular Component Router and learn to navigate among the views", "intro": "We add the Angular Component Router and learn to navigate among the views",
"nextable": true "nextable": true
},
"toh-pt6": {
"title": "Http",
"intro": "We convert our service and components to use Http",
"nextable": true
} }
} }

View File

@ -0,0 +1 @@
!= partial("../../../_includes/_ts-temp")

View File

@ -129,7 +129,7 @@ figure.image-display
我们通常会推荐在应用程序的根组件`AppComponent`中提供应用程序级的服务。 我们通常会推荐在应用程序的根组件`AppComponent`中提供应用程序级的服务。
Here we recommend registering the title service during bootstrapping, Here we recommend registering the title service during bootstrapping,
a location we reserve for configuring the runtime Angular enviroment. a location we reserve for configuring the runtime Angular environment.
但这里我们推荐在引导过程中注册这个Title服务这个位置是我们为设置Angular运行环境而保留的。 但这里我们推荐在引导过程中注册这个Title服务这个位置是我们为设置Angular运行环境而保留的。

View File

@ -253,25 +253,25 @@ include _util-fns
The many forms of binding include: The many forms of binding include:
绑定形式包括: 绑定形式包括:
* [Interpolation](guide/template-syntax.html#interpolation) * [Interpolation](/docs/ts/latest/guide/template-syntax.html#interpolation)
* [插值表达式Interpolation](guide/template-syntax.html#interpolation) * [插值表达式Interpolation](/docs/ts/latest/guide/template-syntax.html#interpolation)
* [Property Binding](guide/template-syntax.html#property-binding) * [Property Binding](/docs/ts/latest/guide/template-syntax.html#property-binding)
* [属性绑定Property Binding](guide/template-syntax.html#property-binding) * [属性绑定Property Binding](/docs/ts/latest/guide/template-syntax.html#property-binding)
* [Event Binding](guide/template-syntax.html#event-binding) * [Event Binding](/docs/ts/latest/guide/template-syntax.html#event-binding)
* [事件绑定Event Binding](guide/template-syntax.html#event-binding) * [事件绑定Event Binding](/docs/ts/latest/guide/template-syntax.html#event-binding)
* [Attribute Binding](guide/template-syntax.html#attribute-binding) * [Attribute Binding](/docs/ts/latest/guide/template-syntax.html#attribute-binding)
* [Attribute绑定Attribute Binding](guide/template-syntax.html#attribute-binding) * [Attribute绑定Attribute Binding](/docs/ts/latest/guide/template-syntax.html#attribute-binding)
* [Class Binding](guide/template-syntax.html#class-binding) * [Class Binding](/docs/ts/latest/guide/template-syntax.html#class-binding)
* [类绑定Class Binding](guide/template-syntax.html#class-binding) * [类绑定Class Binding](/docs/ts/latest/guide/template-syntax.html#class-binding)
* [Style Binding](guide/template-syntax.html#style-binding) * [Style Binding](/docs/ts/latest/guide/template-syntax.html#style-binding)
* [样式绑定Style Binding](guide/template-syntax.html#style-binding) * [样式绑定Style Binding](/docs/ts/latest/guide/template-syntax.html#style-binding)
* [Two-way data binding with ngModel](guide/template-syntax.html#ng-model) * [Two-way data binding with ngModel](/docs/ts/latest/guide/template-syntax.html#ng-model)
* [基于ngModel的双向数据绑定Two-way data binding with ngModel](guide/template-syntax.html#ng-model) * [基于ngModel的双向数据绑定Two-way data binding with ngModel](/docs/ts/latest/guide/template-syntax.html#ng-model)
Learn more about data binding in the Learn more about data binding in the
[Template Syntax](guide/template-syntax.html#data-binding) chapter. [Template Syntax](/docs/ts/latest/guide/template-syntax.html#data-binding) chapter.
要了解关于数据绑定的更多知识,请参见[模板语法](guide/template-syntax.html#data-binding)一章。 要了解关于数据绑定的更多知识,请参见[模板语法](guide/template-syntax.html#data-binding)一章。
// #enddocregion d1 // #enddocregion d1
<a id="decorator"></a> <a id="decoration"></a> <a id="decorator"></a> <a id="decoration"></a>
@ -407,7 +407,7 @@ include _util-fns
Angular会为每个注册器注册很多自己的内建Provider。我们也可以注册自己的Provider。通常注册Provider的最佳时间是在应用程序开始[引导](#bootstrap)的时候。 Angular会为每个注册器注册很多自己的内建Provider。我们也可以注册自己的Provider。通常注册Provider的最佳时间是在应用程序开始[引导](#bootstrap)的时候。
当然我们也有其它很多机会注册Provider。 当然我们也有其它很多机会注册Provider。
Learn more in the [Dependency Injection](guide/dependency-injection.html) chapter. Learn more in the [Dependency Injection](/docs/ts/latest/guide/dependency-injection.html) chapter.
要了解关于依赖注入的更多知识,请参见[依赖注入](guide/dependency-injection.html)一章。 要了解关于依赖注入的更多知识,请参见[依赖注入](guide/dependency-injection.html)一章。
@ -494,7 +494,7 @@ include _util-fns
## ECMAScript 2015 ## ECMAScript 2015
.l-sub-section .l-sub-section
:marked :marked
The lastest released version of JavaScript, The latest released version of JavaScript,
[ECMAScript 2015](http://www.ecma-international.org/ecma-262/6.0/) [ECMAScript 2015](http://www.ecma-international.org/ecma-262/6.0/)
(AKA "ES2015" or "ES6") (AKA "ES2015" or "ES6")
@ -545,15 +545,15 @@ include _util-fns
.l-sub-section .l-sub-section
:marked :marked
A directive property that can be the ***target*** of a A directive property that can be the ***target*** of a
[Property Binding](guide/template-syntax.html#property-binding). [Property Binding](/docs/ts/latest/guide/template-syntax.html#property-binding).
Data values flow *into* this property from the data source identified Data values flow *into* this property from the data source identified
in the template expression to the right of the equal sign. in the template expression to the right of the equal sign.
指令属性可以作为[属性绑定](guide/template-syntax.html#property-binding)的目标。数据值会从模板表达式等号右侧的数据源中,流入这个属性。 指令属性可以作为[属性绑定](/docs/ts/latest/guide/template-syntax.html#property-binding)的目标。数据值会从模板表达式等号右侧的数据源中,流入这个属性。
See the [Template Syntax](guide/template-syntax.html#inputs-outputs) chapter. See the [Template Syntax](/docs/ts/latest/guide/template-syntax.html#inputs-outputs) chapter.
参见[模板语法Template Syntax](guide/template-syntax.html#inputs-outputs)一章。 参见[模板语法Template Syntax](/docs/ts/latest/guide/template-syntax.html#inputs-outputs)一章。
:marked :marked
## Interpolation ## Interpolation
@ -573,9 +573,9 @@ include _util-fns
:marked :marked
Learn more about interpolation in the Learn more about interpolation in the
[Template Syntax](guide/template-syntax.html#interpolation) chapter. [Template Syntax](/docs/ts/latest/guide/template-syntax.html#interpolation) chapter.
要学习关于插值表达式的更多知识,参见[模板语法](guide/template-syntax.html#interpolation)一章。 要学习关于插值表达式的更多知识,参见[模板语法](/docs/ts/latest/guide/template-syntax.html#interpolation)一章。
<a id="J"></a> <a id="J"></a>
@ -641,9 +641,9 @@ include _util-fns
* `ngOnDestroy` - just before the directive is destroyed. * `ngOnDestroy` - just before the directive is destroyed.
* `ngOnDestroy` - 在指令销毁前调用。 * `ngOnDestroy` - 在指令销毁前调用。
Learn more in the [Lifecycle Hooks](guide/lifecycle-hooks.html) chapter. Learn more in the [Lifecycle Hooks](/docs/ts/latest/guide/lifecycle-hooks.html) chapter.
要了解更多,参见[生命周期钩子Lifecycle Hooks](guide/lifecycle-hooks.html)一章。 要了解更多,参见[生命周期钩子Lifecycle Hooks](/docs/ts/latest/guide/lifecycle-hooks.html)一章。
// #enddocregion f-l // #enddocregion f-l
// #docregion m1 // #docregion m1
@ -691,7 +691,7 @@ include _util-fns
应用程序开发者可以自己选择任何与这个标准兼容的模块化库。 应用程序开发者可以自己选择任何与这个标准兼容的模块化库。
Modules are typically named after the file in which the exported thing is defined. Modules are typically named after the file in which the exported thing is defined.
The Angular [DatePipe](https://github.com/angular/angular/blob/master/modules/angular2/src/common/pipes/date_pipe.ts) The Angular [DatePipe](https://github.com/angular/angular/blob/master/modules/@angular/common/src/pipes/date_pipe.ts)
class belongs to a feature module named `date_pipe` in the file `date_pipe.ts`. class belongs to a feature module named `date_pipe` in the file `date_pipe.ts`.
模块一般与它用于导出东西的文件同名。比如, Angular的[日期管道DatePipe](https://github.com/angular/angular/blob/master/modules/angular2/src/common/pipes/date_pipe.ts)类属于名叫`date_pipe`的特性模块,位于文件`date_pipe.ts`中。 模块一般与它用于导出东西的文件同名。比如, Angular的[日期管道DatePipe](https://github.com/angular/angular/blob/master/modules/angular2/src/common/pipes/date_pipe.ts)类属于名叫`date_pipe`的特性模块,位于文件`date_pipe.ts`中。
@ -718,16 +718,16 @@ include _util-fns
.l-sub-section .l-sub-section
:marked :marked
A directive property that can be the ***target*** of an A directive property that can be the ***target*** of an
[Event Binding](guide/template-syntax.html#property-binding). [Event Binding](/docs/ts/latest/guide/template-syntax.html#property-binding).
Events stream *out* of this property to the receiver identified Events stream *out* of this property to the receiver identified
in the template expression to the right of the equal sign. in the template expression to the right of the equal sign.
输出Output是指令的一个属性它可作为[事件绑定Event Binding](guide/template-syntax.html#property-binding)的 **目标** 。 输出Output是指令的一个属性它可作为[事件绑定Event Binding](/docs/ts/latest/guide/template-syntax.html#property-binding)的 **目标** 。
事件流可以通过这个属性,流出到接收者(模板表达式等号的右边就是接收者)。 事件流可以通过这个属性,流出到接收者(模板表达式等号的右边就是接收者)。
See the [Template Syntax](guide/template-syntax.html#inputs-outputs) chapter. See the [Template Syntax](/docs/ts/latest/guide/template-syntax.html#inputs-outputs) chapter.
参见[模板语法Template Syntax](guide/template-syntax.html#inputs-outputs)一章。 参见[模板语法Template Syntax](/docs/ts/latest/guide/template-syntax.html#inputs-outputs)一章。
.l-main-section .l-main-section
<a id="P"></a> <a id="P"></a>
@ -768,9 +768,9 @@ include _util-fns
code-example(language="html" escape="html"). code-example(language="html" escape="html").
<label>Price: </label>{{product.price | currency}} <label>Price: </label>{{product.price | currency}}
:marked :marked
Learn more in the chapter on [pipes](guide/pipes.html) . Learn more in the chapter on [pipes](/docs/ts/latest/guide/pipes.html) .
要了解更多,参见[管道pipes](guide/pipes.html)一章。 要了解更多,参见[管道pipes](/docs/ts/latest/guide/pipes.html)一章。
:marked :marked
## Provider ## Provider
@ -807,7 +807,7 @@ include _util-fns
大部分应用程序包含多个屏或视图[views](#view)。用户通过点击链接、按钮和其它类似动作,在它们之间穿梭,这样应用程序从一个视图变换到另一个视图。 大部分应用程序包含多个屏或视图[views](#view)。用户通过点击链接、按钮和其它类似动作,在它们之间穿梭,这样应用程序从一个视图变换到另一个视图。
The Angular [Component Router](guide/router.html) is a richly featured mechanism for configuring The Angular [Component Router](/docs/ts/latest/guide/router.html) is a richly featured mechanism for configuring
and managing the entire view navigation process including the creation and destruction and managing the entire view navigation process including the creation and destruction
of views. of views.
@ -885,9 +885,9 @@ include _util-fns
模板是一块HTML。在Angular指令最典型的 指令[组件Component](#component)的支持和范围下Angular用它来渲染试图。 模板是一块HTML。在Angular指令最典型的 指令[组件Component](#component)的支持和范围下Angular用它来渲染试图。
We write templates in a special [Template Syntax](guide/template-syntax.html). We write templates in a special [Template Syntax](/docs/ts/latest/guide/template-syntax.html).
我们使用特殊的[模板语法Template Syntax](guide/template-syntax.html)来编写模板。 我们使用特殊的[模板语法Template Syntax](/docs/ts/latest/guide/template-syntax.html)来编写模板。
:marked :marked
## Template Expression ## Template Expression
@ -896,7 +896,7 @@ include _util-fns
:marked :marked
An expression in a JavaScript-like syntax that Angular evaluates within An expression in a JavaScript-like syntax that Angular evaluates within
a [data binding](#data-binding). Learn how to write template expressions a [data binding](#data-binding). Learn how to write template expressions
in the [Template Syntax](guide/template-syntax.html#template-expressions) chapter. in the [Template Syntax](/docs/ts/latest/guide/template-syntax.html#template-expressions) chapter.
Angular在[数据绑定data binding](#data-binding)内求值的类似JavaScript语法的表达式。在[模板语法Template Syntax](guide/template-syntax.html#template-expressions)章节了解更多模板表达式的知识。 Angular在[数据绑定data binding](#data-binding)内求值的类似JavaScript语法的表达式。在[模板语法Template Syntax](guide/template-syntax.html#template-expressions)章节了解更多模板表达式的知识。
// #enddocregion t1 // #enddocregion t1
@ -987,8 +987,7 @@ include _util-fns
The browser DOM and JavaScript have a limited number The browser DOM and JavaScript have a limited number
of asynchronous activities, activities such as DOM events (e.g., clicks), of asynchronous activities, activities such as DOM events (e.g., clicks),
[promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ [promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), and
Promise), and
[XHR](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) [XHR](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
calls to remote servers. calls to remote servers.

View File

@ -519,20 +519,20 @@ figure
在[范例](#template)模板中,我们看到了数据绑定的三种形式: 在[范例](#template)模板中,我们看到了数据绑定的三种形式:
+makeExample('architecture/ts/app/hero-list.component.1.html', 'binding', 'app/hero-list.component.html (节选)')(format=".") +makeExample('architecture/ts/app/hero-list.component.1.html', 'binding', 'app/hero-list.component.html (节选)')(format=".")
:marked :marked
* The `{{hero.name}}` "[interpolation](displaying-data.html#interpolation)" * The `{{hero.name}}` [*interpolation*](displaying-data.html#interpolation)
displays the component's `hero.name` property value within the `<div>` tags. displays the component's `hero.name` property value within the `<div>` tags.
* `{{hero.name}}`[插值表达式](displaying-data.html#interpolation):在`<div>`标签中显示了组件的`hero.name`属性的值。 * `{{hero.name}}`[*插值表达式*](displaying-data.html#interpolation):在`<div>`标签中显示了组件的`hero.name`属性的值。
* The `[hero]` [property binding](template-syntax.html#property-binding) passes the `selectedHero` from * The `[hero]` [*property binding*](template-syntax.html#property-binding) passes the `selectedHero` from
the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`. the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
* `[hero]`[属性绑定](template-syntax.html#property-binding):把父组件`HeroListComponent`的`selectedHero`传到子组件`HeroDetailComponent`的`hero`属性中。 * `[hero]`[*属性绑定*](template-syntax.html#property-binding):把父组件`HeroListComponent`的`selectedHero`传到子组件`HeroDetailComponent`的`hero`属性中。
* The `(click)` [event binding](user-input.html#click) calls the Component's `selectHero` method when the user clicks * The `(click)` [*event binding*](user-input.html#click) calls the Component's `selectHero` method when the user clicks
on a hero's name on a hero's name
* `(click)`[事件绑定](user-input.html#click):当用户点击英雄的名字时,调用组件的`selectHero`方法。 * `(click)`[*事件绑定*](user-input.html#click):当用户点击英雄的名字时,调用组件的`selectHero`方法。
* **Two-way data binding** is an important fourth form * **Two-way data binding** is an important fourth form
that combines property and event binding in a single notation using the `ngModel` directive. that combines property and event binding in a single notation using the `ngModel` directive.
@ -996,7 +996,7 @@ code-example(language="javascript" linenumbers=".").
>把"42.33"显示为`$42.33`。 >把"42.33"显示为`$42.33`。
>**[Testing](../testing/index.html)** - Angular provides a testing library for "unit testing" our application parts as they >**[Testing](testing.html)** - Angular provides a testing library for "unit testing" our application parts as they
interact with the Angular framework. interact with the Angular framework.
>**[Testing](../testing/index.html)** - Angular提供了一个测试库在程序各个部分与Angular框架交互同时用来“单元测试”它们。 >**[Testing](testing.html)** - Angular提供了一个测试库在程序各个部分与Angular框架交互同时用来“单元测试”它们。

View File

@ -49,7 +49,7 @@ p 运行本章这些代码的#[+liveExampleLink2()]
只要需要,我们就可以指定任何选择器、规则和媒体查询。 只要需要,我们就可以指定任何选择器、规则和媒体查询。
One way to do this is to set the `styles` property in the component metadata. One way to do this is to set the `styles` property in the component metadata.
The `styles` property takes #{_an_array} of strings that contain CSS code. The `styles` property takes #{_an} #{_array} of strings that contain CSS code.
Usually we give it one string as in this example: Usually we give it one string as in this example:
它的实现方式之一,是在组件的元数据中设置`styles`属性。 它的实现方式之一,是在组件的元数据中设置`styles`属性。

View File

@ -60,7 +60,7 @@ include ../_util-fns
We didn't care about `Engine` constructor parameters when we first wrote `Car`. We didn't care about `Engine` constructor parameters when we first wrote `Car`.
We don't really care about them now. We don't really care about them now.
But we'll *have* to start caring because But we'll *have* to start caring because
when the definion of `Engine` changes, our `Car` class must change. when the definition of `Engine` changes, our `Car` class must change.
That makes `Car` brittle. That makes `Car` brittle.
如果`Engine`类升级了,并且它的构造函数要求传入一个参数了,该怎么办? 如果`Engine`类升级了,并且它的构造函数要求传入一个参数了,该怎么办?

View File

@ -1066,9 +1066,10 @@ figure.image-display
重新运行应用。表单打开时,状态是有效的,按钮是可用的。 重新运行应用。表单打开时,状态是有效的,按钮是可用的。
Now delete the *Name*. We violate the "name required" rule which Now delete the *Name*. We violate the "name required" rule which
is duely noted in our error message as before. And now the Submit button is also disabled. is duly noted in our error message as before. And now the Submit button is also disabled.
现在,删除*姓名*。我们违反了“必填姓名”规则,它还是像以前那样显示了错误信息来提醒我们。同时,“提交”按钮也被禁用了。 现在,删除*姓名*。我们违反了“必填姓名”规则,它还是像以前那样显示了错误信息来提醒我们。同时,“提交”按钮也被禁用了。
Not impressed? Think about it for a moment. What would we have to do to Not impressed? Think about it for a moment. What would we have to do to
wire the button's enable/disabled state to the form's validity without Angular's help? wire the button's enable/disabled state to the form's validity without Angular's help?

View File

@ -93,8 +93,8 @@ a(id="dependencies")
应用程序的`package.json`文件中,`dependencies`区下有三类包: 应用程序的`package.json`文件中,`dependencies`区下有三类包:
* ***Features*** - Feature packages provide our application with framework and utility capabilites. * ***Features*** - Feature packages provide our application with framework and utility capabilities.
* ***特性*** - 特性包为我们的应用程序提供了框架和工具方面的能力。 * ***特性*** - 特性包为我们的应用程序提供了框架和工具方面的能力。
* ***Polyfills*** - Polyfills plug gaps in the browser's JavaScript implementation. * ***Polyfills*** - Polyfills plug gaps in the browser's JavaScript implementation.
* ***填充(Polyfills)*** - 填充包弥合了不同浏览器上的JavaScript实现方面的差异。 * ***填充(Polyfills)*** - 填充包弥合了不同浏览器上的JavaScript实现方面的差异。
@ -290,8 +290,7 @@ a(id="why-peer-dependencies")
We don't have a *peerDependencies* section in the QuickStart `package.json`. We don't have a *peerDependencies* section in the QuickStart `package.json`.
But Angular itself has a *peerDependencies* section in But Angular itself has a *peerDependencies* section in
[*its* package.json](https://github.com/angular/angular/blob/master/modules/angular2/package.json) *its* package.json and that has important consequences for our application.
and that has important consequences for our application.
在“快速起步”的`package.json`文件中,并没有*peerDependencies*区。 在“快速起步”的`package.json`文件中,并没有*peerDependencies*区。
但是Angular本身在[*它自己的* package.json](https://github.com/angular/angular/blob/master/modules/angular2/package.json)中有, 但是Angular本身在[*它自己的* package.json](https://github.com/angular/angular/blob/master/modules/angular2/package.json)中有,

View File

@ -969,9 +969,11 @@ a(id="one-time-initialization")
### Property binding or interpolation? ### Property binding or interpolation?
### 属性绑定还是插值表达式? ### 属性绑定还是插值表达式?
We often have a choice between interpolation and property binding. The following binding pairs do the same thing: We often have a choice between interpolation and property binding.
The following binding pairs do the same thing:
我们通常得在插值表达式和属性绑定之间做出选择。下列这几对绑定做的事情完全相同: 我们通常得在插值表达式和属性绑定之间做出选择。
下列这几对绑定做的事情完全相同:
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-vs-interpolation')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-vs-interpolation')(format=".")
:marked :marked
Interpolation is a convenient alternative for property binding in many cases. Interpolation is a convenient alternative for property binding in many cases.
@ -989,6 +991,26 @@ a(id="one-time-initialization")
我们倾向于可读性,所以倾向于插值表达式。 我们倾向于可读性,所以倾向于插值表达式。
我们建议建立组织级的代码风格规定,然后选择一种形式,既能遵循规则,又能让手头的任务做起来更自然。 我们建议建立组织级的代码风格规定,然后选择一种形式,既能遵循规则,又能让手头的任务做起来更自然。
:marked
#### Content Security
#### 内容安全
Imagine the following *malicious content*.
假设下面的*恶毒内容*
+makeExample('template-syntax/ts/app/app.component.ts', 'evil-title')(format=".")
:marked
Fortunately, Angular data binding is on alert for dangerous HTML.
It *sanitizes* the values before displaying them.
It **will not** allow HTML with script tags to leak into the browser, neither with interpolation
nor property binding.
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-vs-interpolation-sanitization')(format=".")
:marked
Interpolation handles the script tags differently than property binding but both approaches render the
content harmlessly.
figure.image-display
img(src='/resources/images/devguide/template-syntax/evil-title.png' alt="evil title made safe" width='500px')
.l-main-section .l-main-section
:marked :marked
<a id="other-bindings"></a> <a id="other-bindings"></a>

View File

@ -368,7 +368,7 @@ a(id="common-configuration")
但是大多数`import`语句完全不会去引用扩展名。 但是大多数`import`语句完全不会去引用扩展名。
所以我们要告诉Webpack如何通过查找匹配的文件来_解析_模块文件的加载请求 所以我们要告诉Webpack如何通过查找匹配的文件来_解析_模块文件的加载请求
* an explicit extention (signified by the empty extension string, `''`) or * an explicit extension (signified by the empty extension string, `''`) or
* 一个明确的扩展名(通过一个空白的扩展名字符串`''`标记出来),或者 * 一个明确的扩展名(通过一个空白的扩展名字符串`''`标记出来),或者
* `.js` extension (for regular JavaScript files and pre-compiled TypeScript files) or * `.js` extension (for regular JavaScript files and pre-compiled TypeScript files) or
* `.js`扩展名(为了查找标准的JavaScript文件和预编译过的TypeScript文件),或者 * `.js`扩展名(为了查找标准的JavaScript文件和预编译过的TypeScript文件),或者
@ -512,7 +512,7 @@ a(id="development-configuration")
我们这些CSS默认情况下被埋没在JavaScript包儿中。`ExtractTextPlugin`会把它们提取成外部`.css`文件, 我们这些CSS默认情况下被埋没在JavaScript包儿中。`ExtractTextPlugin`会把它们提取成外部`.css`文件,
这样`HtmlWebpackPlugin`插件就会转而把一个&lt;link&gt;标签写进`index.html`了。 这样`HtmlWebpackPlugin`插件就会转而把一个&lt;link&gt;标签写进`index.html`了。
Refer to the Webpack documentation for details on these and other configuation options in this file Refer to the Webpack documentation for details on these and other configuration options in this file
要了解本文件中这些以及其它配置项的详情请参阅Webpack文档。 要了解本文件中这些以及其它配置项的详情请参阅Webpack文档。

View File

@ -5,9 +5,6 @@ block includes
- var _prereq = 'Node.js' - var _prereq = 'Node.js'
- var _angular_browser_uri = '@angular/platform-browser-dynamic' - var _angular_browser_uri = '@angular/platform-browser-dynamic'
- var _angular_core_uri = '@angular/core' - var _angular_core_uri = '@angular/core'
- var _appDir = 'app'
- var _indexHtmlDir = 'project root'
- var _indexHtmlDirCn = '项目的根'
:marked :marked
Our QuickStart goal is to build and run a super-simple Our QuickStart goal is to build and run a super-simple

View File

@ -468,11 +468,11 @@ code-example(format="." language="bash").
:marked :marked
We specify the path _all the way back to the application root_ &mdash; `app/` in this case &mdash; We specify the path _all the way back to the application root_ &mdash; `app/` in this case &mdash;
because Angular doesn't support relative paths _by default_. because Angular doesn't support relative paths _by default_.
We _can_ switch to [component-relative paths](../cookbook/component-relative-paths) if we prefer. We _can_ switch to [component-relative paths](../cookbook/component-relative-paths.html) if we prefer.
我们指定的所有路径_都是相对于该应用的根目录这里是`app/`的_。 我们指定的所有路径_都是相对于该应用的根目录这里是`app/`的_。
因为Angular_默认_不支持使用相对于当前模块的路径。 因为Angular_默认_不支持使用相对于当前模块的路径。
只要喜欢我们也_可以_切换成[相对于组件的路径](../cookbook/component-relative-paths)模式。 只要喜欢我们也_可以_切换成[相对于组件的路径](../cookbook/component-relative-paths.html)模式。
:marked :marked
Create that file with these contents: Create that file with these contents:
@ -719,9 +719,10 @@ code-example(format='').
这段代码的问题在于`HeroService`并没有一个叫`getHero`的方法,我们最好在别人报告应用出问题之前赶快修复它。 这段代码的问题在于`HeroService`并没有一个叫`getHero`的方法,我们最好在别人报告应用出问题之前赶快修复它。
Open `HeroService` and add the `getHero` method. It's trivial given that we're still faking data access: Open `HeroService` and add a `getHero` method that filters the heroes list from `getHeroes` by `id`:
打开`HeroService`,并添加一个`getHero`方法,用来通过`id`从`getHeros`过滤英雄列表:
打开`HeroService`,并添加一个`getHero`方法。对于我们的假数据存取逻辑来说,这点修改是微不足道的:
+makeExample('toh-5/ts/app/hero.service.ts', 'get-hero', 'app/hero.service.ts (getHero)')(format=".") +makeExample('toh-5/ts/app/hero.service.ts', 'get-hero', 'app/hero.service.ts (getHero)')(format=".")
:marked :marked
Return to the `HeroDetailComponent` to clean up loose ends. Return to the `HeroDetailComponent` to clean up loose ends.

View File

@ -2,15 +2,15 @@ include ../_util-fns
:marked :marked
# Getting and Saving Data with HTTP # Getting and Saving Data with HTTP
Our stakeholders appreciate our progress. Our stakeholders appreciate our progress.
Now they want to get the hero data from a server, let users add, edit, and delete heroes, Now they want to get the hero data from a server, let users add, edit, and delete heroes,
and save these changes back to the server. and save these changes back to the server.
In this chapter we teach our application to make the corresponding http calls to a remote server's web api. In this chapter we teach our application to make the corresponding http calls to a remote server's web api.
:marked :marked
[Run the live example](/resources/live-examples/toh-6/ts/plnkr.html). [Run the live example](/resources/live-examples/toh-6/ts/plnkr.html).
.l-main-section .l-main-section
:marked :marked
## Where We Left Off ## Where We Left Off
@ -27,25 +27,25 @@ code-example(format="." language="bash").
:marked :marked
The application runs and updates automatically as we continue to build the Tour of Heroes. The application runs and updates automatically as we continue to build the Tour of Heroes.
.l-main-section .l-main-section
:marked :marked
## Prepare for Http ## Prepare for Http
`Http` is ***not*** a core Angular module. `Http` is ***not*** a core Angular module.
It's Angular's optional approach to web access and it exists as a separate add-on module called `@angular/http`, It's Angular's optional approach to web access and it exists as a separate add-on module called `@angular/http`,
shipped in a separate script file as part of the Angular npm package. shipped in a separate script file as part of the Angular npm package.
Fortunately we're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when we need it. Fortunately we're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when we need it.
:marked :marked
### Register (provide) *http* services ### Register (provide) *http* services
Our app will depend upon the Angular `http` service which itself depends upon other supporting services. Our app will depend upon the Angular `http` service which itself depends upon other supporting services.
The `HTTP_PROVIDERS` array from `@angular/http` library holds providers for the complete set of http services. The `HTTP_PROVIDERS` array from `@angular/http` library holds providers for the complete set of http services.
We should be able to access these services from anywhere in the application. We should be able to access these services from anywhere in the application.
So we register them in the `bootstrap` method of `main.ts` where we So we register them in the `bootstrap` method of `main.ts` where we
launch the application and its root `AppComponent`. launch the application and its root `AppComponent`.
+makeExample('toh-6/ts/app/main.ts','v1','app/main.ts (v1)')(format='.') +makeExample('toh-6/ts/app/main.ts','v1','app/main.ts (v1)')(format='.')
:marked :marked
Notice that we supply the `HTTP_PROVIDERS` in an array as the second parameter to the `bootstrap` method. Notice that we supply the `HTTP_PROVIDERS` in an array as the second parameter to the `bootstrap` method.
@ -57,19 +57,19 @@ code-example(format="." language="bash").
We generally recommend registering application-wide services in the root `AppComponent` *providers*. We generally recommend registering application-wide services in the root `AppComponent` *providers*.
Here we're registering in `main` for a special reason. Here we're registering in `main` for a special reason.
Our application is in the early stages of development and far from ready for production. Our application is in the early stages of development and far from ready for production.
We don't even have a web server that can handle requests for heroes. We don't even have a web server that can handle requests for heroes.
Until we do, *we'll have to fake it*. Until we do, *we'll have to fake it*.
We're going to *trick* the http client into fetching and saving data from We're going to *trick* the http client into fetching and saving data from
a demo/development service, the *in-memory web api*. a demo/development service, the *in-memory web api*.
The application itself doesn't need to know and shouldn't know about this. The application itself doesn't need to know and shouldn't know about this.
So we'll slip the *in-memory web api* into the configuration *above* the `AppComponent`. So we'll slip the *in-memory web api* into the configuration *above* the `AppComponent`.
Here is a version of `main` that performs this trick Here is a version of `main` that performs this trick
+makeExample('toh-6/ts/app/main.ts', 'final', 'app/main.ts (final)')(format=".") +makeExample('toh-6/ts/app/main.ts', 'final', 'app/main.ts (final)')(format=".")
:marked :marked
We're replacing the default `XHRBackend`, the service that talks to the remote server, We're replacing the default `XHRBackend`, the service that talks to the remote server,
@ -81,55 +81,55 @@ code-example(format="." language="bash").
.alert.is-helpful .alert.is-helpful
:marked :marked
This chaper is an introduction to the Angular http client. This chaper is an introduction to the Angular http client.
Please don't be distracted by the details of this backend substitution. Just follow along with the example. Please don't be distracted by the details of this backend substitution. Just follow along with the example.
Learn more later about the *in-memory web api* in the [Http chapter](../guide/server-communication.html#!#in-mem-web-api). Learn more later about the *in-memory web api* in the [Http chapter](../guide/server-communication.html#!#in-mem-web-api).
Remember, the *in-memory web api* is only useful in the early stages of development and for demonstrations such as this Tour of Heroes. Remember, the *in-memory web api* is only useful in the early stages of development and for demonstrations such as this Tour of Heroes.
Skip it when you have a real web api server. Skip it when you have a real web api server.
.l-main-section .l-main-section
:marked :marked
## Heroes and Http ## Heroes and Http
Look at our current `HeroService` implementation Look at our current `HeroService` implementation
+makeExample('toh-4/ts/app/hero.service.ts', 'get-heroes', 'app/hero.service.ts (getHeroes - old)')(format=".") +makeExample('toh-4/ts/app/hero.service.ts', 'get-heroes', 'app/hero.service.ts (getHeroes - old)')(format=".")
:marked :marked
We returned a promise resolved with mock heroes. We returned a promise resolved with mock heroes.
It may have seemed like overkill at the time, but we were anticipating the It may have seemed like overkill at the time, but we were anticipating the
day when we fetched heroes with an http client and we knew that would have to be an asynchronous operation. day when we fetched heroes with an http client and we knew that would have to be an asynchronous operation.
That day has arrived! Let's convert `getHeroes()` to use Angular's `Http` client: That day has arrived! Let's convert `getHeroes()` to use Angular's `Http` client:
+makeExample('toh-6/ts/app/hero.service.ts', 'get-heroes', 'app/hero.service.ts (getHeroes using Http)')(format=".") +makeExample('toh-6/ts/app/hero.service.ts', 'get-heroes', 'app/hero.service.ts (getHeroes using Http)')(format=".")
:marked :marked
### Http Promise ### Http Promise
We're still returning a promise but we're creating it differently. We're still returning a promise but we're creating it differently.
The Angular `http.get` returns an RxJS `Observable`. The Angular `http.get` returns an RxJS `Observable`.
*Observables* are a powerful way to manage asynchronous data flows. *Observables* are a powerful way to manage asynchronous data flows.
We'll learn about `Observables` *later*. We'll learn about `Observables` *later*.
For *now* we get back on familiar ground by immediately converting that `Observable` to a `Promise` using the `toPromise` operator. For *now* we get back on familiar ground by immediately converting that `Observable` to a `Promise` using the `toPromise` operator.
+makeExample('toh-6/ts/app/hero.service.ts', 'to-promise')(format=".") +makeExample('toh-6/ts/app/hero.service.ts', 'to-promise')(format=".")
:marked :marked
Unfortunately, the Angular `Observable` doesn't have a `toPromise` operator ... not out of the box. Unfortunately, the Angular `Observable` doesn't have a `toPromise` operator ... not out of the box.
The Angular `Observable` is a bare-bones implementation. The Angular `Observable` is a bare-bones implementation.
There are scores of operators like `toPromise` that extend `Observable` with useful capabilities. There are scores of operators like `toPromise` that extend `Observable` with useful capabilities.
If we want those capabilities, we have to add the operators ourselves. If we want those capabilities, we have to add the operators ourselves.
That's as easy as importing them from the RxJS library like this: That's as easy as importing them from the RxJS library like this:
+makeExample('toh-6/ts/app/hero.service.ts', 'rxjs')(format=".") +makeExample('toh-6/ts/app/hero.service.ts', 'rxjs')(format=".")
:marked :marked
### Extracting the data in the *then* callback ### Extracting the data in the *then* callback
In the *promise*'s `then` callback we call the `json` method of the http `Response` to extract the In the *promise*'s `then` callback we call the `json` method of the http `Response` to extract the
data within the response. data within the response.
+makeExample('toh-6/ts/app/hero.service.ts', 'to-data')(format=".") +makeExample('toh-6/ts/app/hero.service.ts', 'to-data')(format=".")
:marked :marked
That object returned by `json` has a single `data` property. That object returned by `json` has a single `data` property.
The `data` property holds the array of *heroes* that the caller really wants. The `data` property holds the array of *heroes* that the caller really wants.
So we grab that array and return it as the resolved promise value. So we grab that array and return it as the resolved promise value.
@ -138,20 +138,20 @@ code-example(format="." language="bash").
Pay close attention to the shape of the data returned by the server. Pay close attention to the shape of the data returned by the server.
This particular *in-memory web api* example happens to return an object with a `data` property. This particular *in-memory web api* example happens to return an object with a `data` property.
Your api might return something else. Your api might return something else.
Adjust the code to match *your web api*. Adjust the code to match *your web api*.
:marked :marked
The caller is unaware of these machinations. It receives a promise of *heroes* just as it did before. The caller is unaware of these machinations. It receives a promise of *heroes* just as it did before.
It has no idea that we fetched the heroes from the server. It has no idea that we fetched the heroes from the server.
It knows nothing of the twists and turns required to turn the http response into heroes. It knows nothing of the twists and turns required to turn the http response into heroes.
Such is the beauty and purpose of delegating data access to a service like this `HeroService`. Such is the beauty and purpose of delegating data access to a service like this `HeroService`.
:marked :marked
### Error Handling ### Error Handling
At the end of `getHeroes` we `catch` server failures and pass them to an error handler: At the end of `getHeroes` we `catch` server failures and pass them to an error handler:
+makeExample('toh-6/ts/app/hero.service.ts', 'catch')(format=".") +makeExample('toh-6/ts/app/hero.service.ts', 'catch')(format=".")
:marked :marked
This is a critical step! This is a critical step!
We must anticipate http failures as they happen frequently for reasons beyond our control. We must anticipate http failures as they happen frequently for reasons beyond our control.
+makeExample('toh-6/ts/app/hero.service.ts', 'error-handler', 'app/hero.service.ts (Error handler)')(format=".") +makeExample('toh-6/ts/app/hero.service.ts', 'error-handler', 'app/hero.service.ts (Error handler)')(format=".")
@ -160,51 +160,51 @@ code-example(format="." language="bash").
We've also decided to return a user friendly form of the error to We've also decided to return a user friendly form of the error to
to the caller in a rejected promise so that the caller can display a proper error message to the user. to the caller in a rejected promise so that the caller can display a proper error message to the user.
### Promises are Promises ### Promises are Promises
Although we made significant *internal* changes to `getHeroes()`, the public signature did not change. Although we made significant *internal* changes to `getHeroes()`, the public signature did not change.
We still return a promise. We won't have to update any of the components that call `getHeroes()`. We still return a promise. We won't have to update any of the components that call `getHeroes()`.
.l-main-section .l-main-section
:marked :marked
## Add, Edit, Delete ## Add, Edit, Delete
Our stakeholders are incredibly pleased with the added flexibility from the api integration, but it doesn't stop there. Next we want to add the capability to add, edit and delete heroes. Our stakeholders are incredibly pleased with the added flexibility from the api integration, but it doesn't stop there. Next we want to add the capability to add, edit and delete heroes.
We'll complete `HeroService` by creating `post`, `put` and `delete` http calls to meet our new requirements. We'll complete `HeroService` by creating `post`, `put` and `delete` http calls to meet our new requirements.
:marked :marked
### Post ### Post
We are using `post` to add new heroes. Post requests require a little bit more setup than Get requests, but the format is as follows: We are using `post` to add new heroes. Post requests require a little bit more setup than Get requests, but the format is as follows:
+makeExample('toh-6/ts/app/hero.service.ts', 'post-hero', 'app/hero.service.ts (post hero)')(format=".") +makeExample('toh-6/ts/app/hero.service.ts', 'post-hero', 'app/hero.service.ts (post hero)')(format=".")
:marked :marked
Now we create a header and set the content type to `application/json`. We'll call `JSON.stringify` before we post to convert the hero object to a string. Now we create a header and set the content type to `application/json`. We'll call `JSON.stringify` before we post to convert the hero object to a string.
### Put ### Put
`put` is used to edit a specific hero, but the structure is very similar to a `post` request. The only difference is that we have to change the url slightly by appending the id of the hero we want to edit. `put` is used to edit a specific hero, but the structure is very similar to a `post` request. The only difference is that we have to change the url slightly by appending the id of the hero we want to edit.
+makeExample('toh-6/ts/app/hero.service.ts', 'put-hero', 'app/hero.service.ts (put hero)')(format=".") +makeExample('toh-6/ts/app/hero.service.ts', 'put-hero', 'app/hero.service.ts (put hero)')(format=".")
:marked :marked
### Delete ### Delete
`delete` is used to delete heroes and the format is identical to `put` except for the function name. `delete` is used to delete heroes and the format is identical to `put` except for the function name.
+makeExample('toh-6/ts/app/hero.service.ts', 'delete-hero', 'app/hero.service.ts (delete hero)')(format=".") +makeExample('toh-6/ts/app/hero.service.ts', 'delete-hero', 'app/hero.service.ts (delete hero)')(format=".")
:marked :marked
We add a `catch` to handle our errors for all three cases. We add a `catch` to handle our errors for all three cases.
:marked :marked
### Save ### Save
We combine the call to the private `post` and `put` methods in a single `save` method. This simplifies the public api and makes the integration with `HeroDetailComponent` easier. `HeroService` determines which method to call based on the state of the `hero` object. If the hero already has an id we know it's an edit. Otherwise we know it's an add. We combine the call to the private `post` and `put` methods in a single `save` method. This simplifies the public api and makes the integration with `HeroDetailComponent` easier. `HeroService` determines which method to call based on the state of the `hero` object. If the hero already has an id we know it's an edit. Otherwise we know it's an add.
+makeExample('toh-6/ts/app/hero.service.ts', 'save', 'app/hero.service.ts (save hero)')(format=".") +makeExample('toh-6/ts/app/hero.service.ts', 'save', 'app/hero.service.ts (save hero)')(format=".")
:marked :marked
After these additions our `HeroService` looks like this: After these additions our `HeroService` looks like this:
@ -213,27 +213,30 @@ code-example(format="." language="bash").
.l-main-section .l-main-section
:marked :marked
## Updating Components ## Updating Components
Loading heroes using `Http` required no changes outside of `HeroService`, but we added a few new features as well. Loading heroes using `Http` required no changes outside of `HeroService`, but we added a few new features as well.
In the following section we will update our components to use our new methods to add, edit and delete heroes. In the following section we will update our components to use our new methods to add, edit and delete heroes.
### Add/Edit in the *HeroDetailComponent* ### Add/Edit in the *HeroDetailComponent*
We already have `HeroDetailComponent` for viewing details about a specific hero. Add and Edit are natural extensions of the detail view, so we are able to reuse `DetailHeroComponent` with a few tweaks. The original component was created to render existing data, but to add new data we have to initialize the `hero` property to an empty `Hero` object.
We already have `HeroDetailComponent` for viewing details about a specific hero.
Add and Edit are natural extensions of the detail view, so we are able to reuse `HeroDetailComponent` with a few tweaks.
The original component was created to render existing data, but to add new data we have to initialize the `hero` property to an empty `Hero` object.
+makeExample('toh-6/ts/app/hero-detail.component.ts', 'ngOnInit', 'app/hero-detail.component.ts (ngOnInit)')(format=".") +makeExample('toh-6/ts/app/hero-detail.component.ts', 'ngOnInit', 'app/hero-detail.component.ts (ngOnInit)')(format=".")
:marked :marked
In order to differentiate between add and edit we are adding a check to see if an id is passed in the url. If the id is absent we bind `HeroDetailComponent` to an empty `Hero` object. In either case, any edits made through the UI will be bound back to the same `hero` property. In order to differentiate between add and edit we are adding a check to see if an id is passed in the url. If the id is absent we bind `HeroDetailComponent` to an empty `Hero` object. In either case, any edits made through the UI will be bound back to the same `hero` property.
The next step is to add a save method to `HeroDetailComponent` and call the corresponding save method in `HeroesService`. The next step is to add a save method to `HeroDetailComponent` and call the corresponding save method in `HeroesService`.
+makeExample('toh-6/ts/app/hero-detail.component.ts', 'save', 'app/hero-detail.component.ts (save)')(format=".") +makeExample('toh-6/ts/app/hero-detail.component.ts', 'save', 'app/hero-detail.component.ts (save)')(format=".")
:marked :marked
The same save method is used for both add and edit since `HeroService` will know when to call `post` vs `put` based on the state of the `Hero` object. The same save method is used for both add and edit since `HeroService` will know when to call `post` vs `put` based on the state of the `Hero` object.
After we save a hero, we redirect the browser back to the to the previous page using the `goBack()` method. After we save a hero, we redirect the browser back to the to the previous page using the `goBack()` method.
+makeExample('toh-6/ts/app/hero-detail.component.ts', 'goback', 'app/hero-detail.component.ts (goBack)')(format=".") +makeExample('toh-6/ts/app/hero-detail.component.ts', 'goback', 'app/hero-detail.component.ts (goBack)')(format=".")
:marked :marked
@ -241,58 +244,58 @@ code-example(format="." language="bash").
.l-sub-section .l-sub-section
:marked :marked
The `emit` "handshake" between `HeroDetailComponent` and `HeroesComponent` is an example of component to component communication. This is a topic for another day, but we have detailed information in our <a href="/docs/ts/latest/cookbook/component-communication.html#!#child-to-parent">Component Interaction Cookbook</a> The `emit` "handshake" between `HeroDetailComponent` and `HeroesComponent` is an example of component to component communication. This is a topic for another day, but we have detailed information in our <a href="/docs/ts/latest/cookbook/component-communication.html#!#child-to-parent">Component Interaction Cookbook</a>
:marked :marked
Here is `HeroDetailComponent` with its new save button. Here is `HeroDetailComponent` with its new save button.
figure.image-display figure.image-display
img(src='/resources/images/devguide/toh/hero-details-save-button.png' alt="Hero Details With Save Button") img(src='/resources/images/devguide/toh/hero-details-save-button.png' alt="Hero Details With Save Button")
:marked :marked
### Add/Delete in the *HeroesComponent* ### Add/Delete in the *HeroesComponent*
The user can *add* a new hero by clicking a button and entering a name. The user can *add* a new hero by clicking a button and entering a name.
When the user clicks the *Add New Hero* button, we display the `HeroDetailComponent`. When the user clicks the *Add New Hero* button, we display the `HeroDetailComponent`.
We aren't navigating to the component so it won't receive a hero `id`; We aren't navigating to the component so it won't receive a hero `id`;
As we noted above, that is the component's cue to create and present an empty hero. As we noted above, that is the component's cue to create and present an empty hero.
Add the following HTML to the `heroes.component.html`, just below the hero list (the `*ngFor`). Add the following HTML to the `heroes.component.html`, just below the hero list (the `*ngFor`).
+makeExample('toh-6/ts/app/heroes.component.html', 'add-hero', 'app/heroes.component.html (add)')(format=".") +makeExample('toh-6/ts/app/heroes.component.html', 'add-hero', 'app/heroes.component.html (add)')(format=".")
:marked :marked
The user can *delete* an existing hero by clicking a delete button next to the hero's name. The user can *delete* an existing hero by clicking a delete button next to the hero's name.
Add the following HTML to the `heroes.component.html` right after the name in the repeated `<li>` tag: Add the following HTML to the `heroes.component.html` right after the name in the repeated `<li>` tag:
+makeExample('toh-6/ts/app/heroes.component.html', 'delete-hero', 'app/heroes.component.html (delete)')(format=".") +makeExample('toh-6/ts/app/heroes.component.html', 'delete-hero', 'app/heroes.component.html (delete)')(format=".")
:marked :marked
Now let's fix-up the `HeroesComponent` to support the *add* and *delete* actions in the template. Now let's fix-up the `HeroesComponent` to support the *add* and *delete* actions in the template.
Let's start with *add*. Let's start with *add*.
We're using the `HeroDetailComponent` to capture the new hero information. We're using the `HeroDetailComponent` to capture the new hero information.
We have to tell Angular about that by importing the `HeroDetailComponent` and referencing it in the component metadata `directives` array. We have to tell Angular about that by importing the `HeroDetailComponent` and referencing it in the component metadata `directives` array.
+makeExample('toh-6/ts/app/heroes.component.ts', 'hero-detail-component', 'app/heroes.component.ts (HeroDetailComponent)')(format=".") +makeExample('toh-6/ts/app/heroes.component.ts', 'hero-detail-component', 'app/heroes.component.ts (HeroDetailComponent)')(format=".")
.l-sub-section .l-sub-section
:marked :marked
These are the same lines that we removed in the previous [Routing](toh-pt5.html) chapter. These are the same lines that we removed in the previous [Routing](toh-pt5.html) chapter.
We didn't know at the time that we'd need the *HeroDetailComponent* again. So we tidied up. We didn't know at the time that we'd need the *HeroDetailComponent* again. So we tidied up.
Now we *must* put these lines back. If we don't, Angular will ignore the `<my-hero-detail>` Now we *must* put these lines back. If we don't, Angular will ignore the `<my-hero-detail>`
tag and pushing the *Add New Hero* button will have no visible effect. tag and pushing the *Add New Hero* button will have no visible effect.
:marked :marked
Next we implement the click handler for the *Add New Hero* button. Next we implement the click handler for the *Add New Hero* button.
+makeExample('toh-6/ts/app/heroes.component.ts', 'add', 'app/heroes.component.ts (add)')(format=".") +makeExample('toh-6/ts/app/heroes.component.ts', 'add', 'app/heroes.component.ts (add)')(format=".")
:marked :marked
The `HeroDetailComponent` does most of the work. All we do is toggle an `*ngIf` flag that The `HeroDetailComponent` does most of the work. All we do is toggle an `*ngIf` flag that
swaps it into the DOM when were add a hero and remove it from the DOM when the user is done. swaps it into the DOM when were add a hero and remove it from the DOM when the user is done.
The *delete* logic is a bit trickier. The *delete* logic is a bit trickier.
+makeExample('toh-6/ts/app/heroes.component.ts', 'delete', 'app/heroes.component.ts (delete)')(format=".") +makeExample('toh-6/ts/app/heroes.component.ts', 'delete', 'app/heroes.component.ts (delete)')(format=".")
:marked :marked
Of course we delegate the persistence of hero deletion to the `HeroService`. Of course we delegate the persistence of hero deletion to the `HeroService`.
But the component is still responsible for updating the display. But the component is still responsible for updating the display.
So the *delete* method removes the deleted hero from the list. So the *delete* method removes the deleted hero from the list.
@ -300,12 +303,12 @@ figure.image-display
### Let's see it ### Let's see it
Here are the fruits of labor in action: Here are the fruits of labor in action:
figure.image-display figure.image-display
img(src='/resources/images/devguide/toh/toh-http.anim.gif' alt="Heroes List Editting w/ HTTP") img(src='/resources/images/devguide/toh/toh-http.anim.gif' alt="Heroes List Editting w/ HTTP")
:marked :marked
### Review the App Structure ### Review the App Structure
Lets verify that we have the following structure after all of our good refactoring in this chapter: Lets verify that we have the following structure after all of our good refactoring in this chapter:
.filetree .filetree
.file angular2-tour-of-heroes .file angular2-tour-of-heroes
.children .children
@ -335,20 +338,20 @@ figure.image-display
.file systemjs.config.json .file systemjs.config.json
.file tsconfig.json .file tsconfig.json
.file typings.json .file typings.json
.l-main-section .l-main-section
:marked :marked
## Home Stretch ## Home Stretch
We are at the end of our journey for now, but we have accomplished a lot. We are at the end of our journey for now, but we have accomplished a lot.
- We added the necessary dependencies to use Http in our application. - We added the necessary dependencies to use Http in our application.
- We refactored HeroService to load heroes from an api. - We refactored HeroService to load heroes from an api.
- We extended HeroService to support post, put and delete calls. - We extended HeroService to support post, put and delete calls.
- We updated our components to allow adding, editing and deleting of heroes. - We updated our components to allow adding, editing and deleting of heroes.
- We configured an in-memory web api. - We configured an in-memory web api.
Below is a summary of the files we changed. Below is a summary of the files we changed.
+makeTabs( +makeTabs(
`toh-6/ts/app/app.component.ts, `toh-6/ts/app/app.component.ts,
toh-6/ts/app/heroes.component.ts, toh-6/ts/app/heroes.component.ts,
@ -363,5 +366,4 @@ figure.image-display
hero-detail.comp...ts, hero-detail.comp...ts,
hero-detail.comp...html, hero-detail.comp...html,
hero.service.ts` hero.service.ts`
) )

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -198,7 +198,7 @@ var createShredMapPackage = function(mapOptions) {
.config(function(readFilesProcessor, extractPathsReader ) { .config(function(readFilesProcessor, extractPathsReader ) {
readFilesProcessor.fileReaders = [ extractPathsReader]; readFilesProcessor.fileReaders = [ extractPathsReader];
}) })
// default configs - may be overriden // default configs - may be overridden
.config(function(readFilesProcessor) { .config(function(readFilesProcessor) {
// Specify the base path used when resolving relative paths to source and output files // Specify the base path used when resolving relative paths to source and output files
readFilesProcessor.basePath = '/'; readFilesProcessor.basePath = '/';