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:
commit
72efc8f055
|
@ -18,7 +18,8 @@ before_script:
|
|||
install:
|
||||
- npm install --no-optional
|
||||
- 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
|
||||
script:
|
||||
- gulp $SCRIPT
|
|
@ -22,6 +22,10 @@
|
|||
"source": "/docs/ts/latest/guide/setup.html",
|
||||
"destination": "/docs/ts/latest/index.html"
|
||||
},
|
||||
{
|
||||
"source": "/docs/ts/latest/testing",
|
||||
"destination": "/docs/ts/latest/guide/testing.html"
|
||||
},
|
||||
{
|
||||
"source": "/cheatsheet",
|
||||
"destination": "/docs/ts/latest/guide/cheatsheet.html"
|
||||
|
|
166
gulpfile.js
166
gulpfile.js
|
@ -38,6 +38,7 @@ var TEMP_PATH = './_temp';
|
|||
var DOCS_PATH = path.join(PUBLIC_PATH, 'docs');
|
||||
|
||||
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 RESOURCES_PATH = path.join(PUBLIC_PATH, 'resources');
|
||||
var LIVE_EXAMPLES_PATH = path.join(RESOURCES_PATH, 'live-examples');
|
||||
|
@ -86,107 +87,134 @@ var _exampleBoilerplateFiles = [
|
|||
|
||||
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
|
||||
*
|
||||
* Flags
|
||||
* --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
|
||||
* 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
|
||||
* e.g. gulp run-e2e-tests --lang=ts // only TypeScript apps
|
||||
* e.g. gulp e2e --lang=ts // only TypeScript apps
|
||||
* default is (ts|js)
|
||||
* all means (ts|js|dart)
|
||||
*/
|
||||
gulp.task('run-e2e-tests', function() {
|
||||
function runE2e() {
|
||||
var promise;
|
||||
if (argv.fast) {
|
||||
// fast; skip all setup
|
||||
promise = Promise.resolve(true);
|
||||
} else {
|
||||
// Not 'fast'; do full setup
|
||||
/*
|
||||
// Not 'fast'; do full setup
|
||||
var spawnInfo = spawnExt('npm', ['install'], { cwd: EXAMPLES_PATH});
|
||||
promise = spawnInfo.promise.then(function() {
|
||||
copyExampleBoilerplate();
|
||||
spawnInfo = spawnExt('npm', ['run', 'webdriver:update'], {cwd: EXAMPLES_PATH});
|
||||
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() {
|
||||
return findAndRunE2eTests(argv.filter);
|
||||
return findAndRunE2eTests(argv.filter, outputFile);
|
||||
}).then(function(status) {
|
||||
reportStatus(status);
|
||||
reportStatus(status, outputFile);
|
||||
if (status.failed.length > 0){
|
||||
return Promise.reject('Some test suites failed');
|
||||
}
|
||||
}).catch(function(e) {
|
||||
gutil.log(e);
|
||||
process.exit(1);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
// finds all of the *e2e-spec.tests under the _examples folder along
|
||||
// with the corresponding apps that they should run under. Then run
|
||||
// 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();
|
||||
if (lang === 'all') { lang = '(ts|js|dart)'; }
|
||||
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`;
|
||||
header += argv.fast ?
|
||||
' Fast Mode (--fast): no npm install, webdriver update, or boilerplate copy\n' :
|
||||
' Slow Mode: npm install, webdriver update, and boilerplate copy\n';
|
||||
header += ` Filter: ${filter ? filter : 'All tests'}\n\n`;
|
||||
|
||||
fs.writeFileSync(outputFile, header);
|
||||
|
||||
// create an array of combos where each
|
||||
// combo consists of { examplePath: ... , protractorConfigFilename: ... }
|
||||
var exeConfigs = [];
|
||||
var examplePaths = [];
|
||||
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');
|
||||
fsExtra.copySync(srcConfig, destConfig);
|
||||
// get all of the examples under each dir where a pcFilename is found
|
||||
examplePaths = getExamplePaths(specPath, true);
|
||||
localExamplePaths = getExamplePaths(specPath, true);
|
||||
// Filter by language
|
||||
examplePaths = examplePaths.filter(function (fn) {
|
||||
localExamplePaths = localExamplePaths.filter(function (fn) {
|
||||
return fn.match('/'+lang+'$') != null;
|
||||
});
|
||||
if (filter) {
|
||||
examplePaths = examplePaths.filter(function (fn) {
|
||||
localExamplePaths = localExamplePaths.filter(function (fn) {
|
||||
return fn.match(filter) != null;
|
||||
})
|
||||
}
|
||||
examplePaths.forEach(function(exPath) {
|
||||
exeConfigs.push( { examplePath: exPath, protractorConfigFilename: destConfig });
|
||||
localExamplePaths.forEach(function(examplePath) {
|
||||
examplePaths.push(examplePath);
|
||||
})
|
||||
});
|
||||
|
||||
// run the tests sequentially
|
||||
var status = { passed: [], failed: [] };
|
||||
return exeConfigs.reduce(function (promise, combo) {
|
||||
return examplePaths.reduce(function (promise, examplePath) {
|
||||
return promise.then(function () {
|
||||
var isDart = combo.examplePath.indexOf('/dart') > -1;
|
||||
var isDart = examplePath.indexOf('/dart') > -1;
|
||||
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;
|
||||
arr.push(combo.examplePath);
|
||||
arr.push(examplePath);
|
||||
})
|
||||
});
|
||||
}, Q.resolve()).then(function() {
|
||||
var stopTime = new Date().getTime();
|
||||
status.elapsedTime = (stopTime - startTime)/1000;
|
||||
fs.appendFileSync(outputFile, '\nElaped Time: ' + status.elapsedTime + ' seconds');
|
||||
return status;
|
||||
});
|
||||
}
|
||||
|
@ -194,15 +222,15 @@ function findAndRunE2eTests(filter) {
|
|||
// start the example in appDir; then run protractor with the specified
|
||||
// fileName; then shut down the example. All protractor output is appended
|
||||
// to the outputFile.
|
||||
function runE2eTsTests(appDir, protractorConfigFilename, outputFile) {
|
||||
function runE2eTsTests(appDir, outputFile) {
|
||||
// start the app
|
||||
var appRunSpawnInfo = spawnExt('npm',['run','http-server:e2e', '--', '-s' ], { 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
|
||||
.catch(function(){
|
||||
var emsg = `AppDir failed during compile: ${appDir}\n\n`;
|
||||
|
@ -212,10 +240,10 @@ function runProtractor(prepPromise, appDir, appRunSpawnInfo, protractorConfigFil
|
|||
})
|
||||
.then(function (data) {
|
||||
// start protractor
|
||||
var pcFilename = path.resolve(protractorConfigFilename); // need to resolve because we are going to be running from a different dir
|
||||
var spawnInfo = spawnExt('npm', [ 'run', 'protractor', '--', pcFilename,
|
||||
'--params.appDir=' + appDir, '--params.outputFile=' + outputFile], { cwd: EXAMPLES_PATH });
|
||||
return spawnInfo.promise
|
||||
var specFilename = path.resolve(`${appDir}/../e2e-spec.ts`);
|
||||
var spawnInfo = spawnExt('npm', [ 'run', 'protractor', '--', 'protractor.config.js',
|
||||
`--specs=${specFilename}`, '--params.appDir=' + appDir, '--params.outputFile=' + outputFile], { cwd: EXAMPLES_PROTRACTOR_PATH });
|
||||
return spawnInfo.promise;
|
||||
})
|
||||
.then(
|
||||
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
|
||||
// fileName; then shut down the example. All protractor output is appended
|
||||
// to the outputFile.
|
||||
function runE2eDartTests(appDir, protractorConfigFilename, outputFile) {
|
||||
function runE2eDartTests(appDir, outputFile) {
|
||||
var deployDir = path.resolve(path.join(appDir, 'build/web'));
|
||||
gutil.log('AppDir for Dart e2e: ' + appDir);
|
||||
gutil.log('Deploying from: ' + deployDir);
|
||||
|
@ -247,24 +275,28 @@ function runE2eDartTests(appDir, protractorConfigFilename, outputFile) {
|
|||
var prepPromise = pubUpgradeSpawnInfo.promise.then(function (data) {
|
||||
return spawnExt('pub', ['build'], { cwd: appDir }).promise;
|
||||
});
|
||||
return runProtractor(prepPromise, appDir, appRunSpawnInfo, protractorConfigFilename, outputFile);
|
||||
return runProtractor(prepPromise, appDir, appRunSpawnInfo, outputFile);
|
||||
}
|
||||
|
||||
function reportStatus(status) {
|
||||
gutil.log('Suites passed:');
|
||||
function reportStatus(status, outputFile) {
|
||||
var log = [''];
|
||||
log.push('Suites passed:');
|
||||
status.passed.forEach(function(val) {
|
||||
gutil.log(' ' + val);
|
||||
log.push(' ' + val);
|
||||
});
|
||||
|
||||
if (status.failed.length == 0) {
|
||||
gutil.log('All tests passed');
|
||||
log.push('All tests passed');
|
||||
} else {
|
||||
gutil.log('Suites failed:');
|
||||
log.push('Suites failed:');
|
||||
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.
|
||||
|
@ -312,7 +344,7 @@ gulp.task('help', taskListing.withFilters(function(taskName) {
|
|||
return shouldRemove;
|
||||
}));
|
||||
|
||||
// requires admin access
|
||||
// requires admin access because it adds symlinks
|
||||
gulp.task('add-example-boilerplate', function() {
|
||||
var realPath = path.join(EXAMPLES_PATH, '/node_modules');
|
||||
var nodeModulesPaths = getNodeModulesPaths(EXAMPLES_PATH);
|
||||
|
@ -332,11 +364,18 @@ gulp.task('add-example-boilerplate', function() {
|
|||
return copyExampleBoilerplate();
|
||||
});
|
||||
|
||||
|
||||
// copies boilerplate files to locations
|
||||
// where an example app is found
|
||||
gulp.task('_copy-example-boilerplate', copyExampleBoilerplate);
|
||||
|
||||
|
||||
// copies boilerplate files to locations
|
||||
// where an example app is found
|
||||
// also copies certain web files (e.g., styles.css) to ~/_examples/**/dart/**/web
|
||||
// also copies protractor.config.js file
|
||||
function copyExampleBoilerplate() {
|
||||
gutil.log('Copying example boilerplate files');
|
||||
var sourceFiles = _exampleBoilerplateFiles.map(function(fn) {
|
||||
return path.join(EXAMPLES_PATH, fn);
|
||||
});
|
||||
|
@ -351,12 +390,14 @@ function copyExampleBoilerplate() {
|
|||
.then(function() {
|
||||
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.
|
||||
.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);
|
||||
return copyFiles(sourceFiles, e2eSpecPaths);
|
||||
return copyFiles(protractorSourceFiles, e2eSpecPaths);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -371,6 +412,15 @@ gulp.task('remove-example-boilerplate', function() {
|
|||
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 dartExampleWebPaths = getDartExampleWebPaths(EXAMPLES_PATH);
|
||||
|
||||
|
@ -379,10 +429,11 @@ gulp.task('remove-example-boilerplate', function() {
|
|||
return deleteFiles(_exampleDartWebBoilerPlateFiles, dartExampleWebPaths);
|
||||
})
|
||||
.then(function() {
|
||||
var protractorFiles = _exampleProtractorBoilerplateFiles;
|
||||
var e2eSpecPaths = getE2eSpecPaths(EXAMPLES_PATH);
|
||||
return deleteFiles(['protractor.config.js'], e2eSpecPaths);
|
||||
})
|
||||
});
|
||||
return deleteFiles(protractorFiles, e2eSpecPaths);
|
||||
});
|
||||
}
|
||||
|
||||
gulp.task('serve-and-sync', ['build-docs'], function (cb) {
|
||||
// watchAndSync({devGuide: true, apiDocs: true, apiExamples: true, localFiles: true}, cb);
|
||||
|
@ -640,7 +691,7 @@ function linkChecker(options) {
|
|||
var handlers = {
|
||||
robots: function(robots, 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){},
|
||||
|
||||
|
@ -657,8 +708,8 @@ function linkChecker(options) {
|
|||
}
|
||||
var msg = '\n [' + result.html.location.line + ', ' + result.brokenReason + '] ' + result.url.resolved;
|
||||
fs.appendFileSync(outputFile, msg);
|
||||
//gutil.log(msg);
|
||||
//gutil.log(result);
|
||||
// gutil.log(msg);
|
||||
// gutil.log(result);
|
||||
},
|
||||
|
||||
page: function(error, pageUrl, customData){},
|
||||
|
@ -686,8 +737,11 @@ function linkChecker(options) {
|
|||
var startTime = new Date().getTime();
|
||||
|
||||
try {
|
||||
gutil.log('link checker started');
|
||||
siteChecker.enqueue(siteUrl, customData);
|
||||
} catch (err) {
|
||||
gutil.log('link checker died');
|
||||
console.error('link checker died', err);
|
||||
deferred.reject(err);
|
||||
}
|
||||
return deferred.promise;
|
||||
|
@ -744,7 +798,7 @@ function deleteFiles(baseFileNames, destPaths) {
|
|||
// TODO: filter out all paths that are subdirs of another
|
||||
// path in the result.
|
||||
function getE2eSpecPaths(basePath) {
|
||||
var paths = getPaths(basePath, '*e2e-spec.js', true);
|
||||
var paths = getPaths(basePath, '*e2e-spec.+(js|ts)', true);
|
||||
return _.uniq(paths);
|
||||
}
|
||||
|
||||
|
|
|
@ -322,7 +322,7 @@
|
|||
"name": "Elad Bezalel",
|
||||
"picture": "/resources/images/bios/eladbezalel.jpg",
|
||||
"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"
|
||||
},
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"devDependencies": {
|
||||
"archiver": "^0.16.0",
|
||||
"assert-plus": "^0.1.5",
|
||||
"broken-link-checker": "0.7.0",
|
||||
"broken-link-checker": "0.7.1",
|
||||
"browser-sync": "^2.9.3",
|
||||
"canonical-path": "0.0.2",
|
||||
"cross-spawn": "^2.1.0",
|
||||
|
|
|
@ -24,13 +24,18 @@
|
|||
//- TS arrays vs. Dart lists
|
||||
- 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
|
||||
- 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';
|
||||
|
||||
//- Directories & folders
|
||||
- var _appDir = 'app';
|
||||
- var _indexHtmlDir = 'project root';
|
||||
- var _mainDir = _appDir;
|
||||
|
||||
//- Location of sample code
|
||||
- var _liveLink = 'live link';
|
||||
|
||||
|
|
|
@ -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 我们的目标是精益、轻量级、基于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/CONTRIBUTING.md" class="button" md-button).
|
||||
帮助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/.github/CONTRIBUTING.md" class="button" md-button) 帮助Angular Material
|
||||
|
||||
|
||||
.l-sub-section
|
||||
h3 AngularFire
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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": {}
|
||||
}
|
|
@ -26,10 +26,6 @@ exports.config = {
|
|||
// Framework to use. Jasmine is recommended.
|
||||
framework: 'jasmine',
|
||||
|
||||
// Spec patterns are relative to this config file
|
||||
specs: ['**/*e2e-spec.js' ],
|
||||
|
||||
|
||||
// For angular2 tests
|
||||
useAllAngular2AppRoots: true,
|
||||
|
||||
|
@ -70,7 +66,7 @@ exports.config = {
|
|||
global.setProtractorToNg1Mode = function() {
|
||||
browser.useAllAngular2AppRoots = false;
|
||||
browser.rootEl = 'body';
|
||||
|
||||
|
||||
var disableNgAnimate = function() {
|
||||
angular.module('disableNgAnimate', []).run(['$animate', function($animate) {
|
||||
$animate.enabled(false);
|
||||
|
@ -86,6 +82,11 @@ exports.config = {
|
|||
defaultTimeoutInterval: 10000,
|
||||
showTiming: true,
|
||||
print: function() {}
|
||||
},
|
||||
|
||||
beforeLaunch: function() {
|
||||
// add TS support for specs
|
||||
require('ts-node').register();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -117,7 +118,7 @@ function sendKeys(element, str) {
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
var _root = { appDir: options.appDir, suites: [] };
|
||||
|
@ -141,7 +142,7 @@ function Reporter(options) {
|
|||
};
|
||||
|
||||
this.specStarted = function(spec) {
|
||||
|
||||
|
||||
};
|
||||
|
||||
this.specDone = function(spec) {
|
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -1,48 +1,49 @@
|
|||
/// <reference path='../_protractor/e2e.d.ts' />
|
||||
describe('Architecture', function () {
|
||||
|
||||
var _title = "Hero List";
|
||||
let title = 'Hero List';
|
||||
|
||||
beforeAll(function () {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
function itReset(name, func) {
|
||||
function itReset(name: string, func: (v: void) => any) {
|
||||
it(name, function() {
|
||||
browser.get('').then(func);
|
||||
});
|
||||
}
|
||||
|
||||
it('should display correct title: ' + _title, function () {
|
||||
expect(element(by.css('h2')).getText()).toEqual(_title);
|
||||
it('should display correct title: ' + title, function () {
|
||||
expect(element(by.css('h2')).getText()).toEqual(title);
|
||||
});
|
||||
|
||||
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);
|
||||
// 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() {
|
||||
return selectEle.getText();
|
||||
}).then(function(selectedHeroName) {
|
||||
// works but too specific if we change the app
|
||||
// expect(selectedHeroName).toEqual('Mr. Nice');
|
||||
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);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
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);
|
||||
// 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 () {
|
||||
return selectEle.getText();
|
||||
}).then(function (selectedHeroName) {
|
||||
var detailTitleEle = element(by.css('hero-detail > h4'));
|
||||
let detailTitleEle = element(by.css('hero-detail > h4'));
|
||||
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
|
||||
// made to the input box.
|
||||
|
@ -56,6 +57,6 @@ describe('Architecture', function () {
|
|||
// expect(heroNameEle.getText()).toEqual(selectedHeroName);
|
||||
expect(heroNameEle.getAttribute('value')).toEqual(selectedHeroName + 'foo');
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
/// <reference path='../_protractor/e2e.d.ts' />
|
||||
describe('Attribute directives', function () {
|
||||
|
||||
var _title = "My First Attribute Directive";
|
||||
let _title = 'My First Attribute Directive';
|
||||
|
||||
beforeAll(function () {
|
||||
browser.get('');
|
||||
|
@ -11,14 +12,15 @@ describe('Attribute directives', function () {
|
|||
});
|
||||
|
||||
it('should be able to select green highlight', function () {
|
||||
var highlightedEle = element(by.cssContainingText('p', 'Highlight me'));
|
||||
var lightGreen = "rgba(144, 238, 144, 1)";
|
||||
let highlightedEle = element(by.cssContainingText('p', 'Highlight me'));
|
||||
let lightGreen = 'rgba(144, 238, 144, 1)';
|
||||
|
||||
expect(highlightedEle.getCssValue('background-color')).not.toEqual(lightGreen);
|
||||
// var greenRb = element(by.cssContainingText('input', 'Green'));
|
||||
var greenRb = element.all(by.css('input')).get(0);
|
||||
// let greenRb = element(by.cssContainingText('input', 'Green'));
|
||||
let greenRb = element.all(by.css('input')).get(0);
|
||||
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);
|
||||
});
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path='../_protractor/e2e.d.ts' />
|
||||
describe('Angular 1 to 2 Quick Reference Tests', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -10,7 +11,7 @@ describe('Angular 1 to 2 Quick Reference Tests', function () {
|
|||
|
||||
it('should display proper movie data', function () {
|
||||
// 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: 2, value: 'Celeritas'},
|
||||
{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
|
||||
var movieRows = getMovieRows();
|
||||
for (var i = 0; i < expectedSamples.length; i++) {
|
||||
var sample = expectedSamples[i];
|
||||
var tableCell = movieRows.get(sample.row)
|
||||
let movieRows = getMovieRows();
|
||||
for (let i = 0; i < expectedSamples.length; i++) {
|
||||
let sample = expectedSamples[i];
|
||||
let tableCell = movieRows.get(sample.row)
|
||||
.all(by.tagName('td')).get(sample.column);
|
||||
// Check the cell or its nested element
|
||||
var elementToCheck = sample.element
|
||||
let elementToCheck = sample.element
|
||||
? tableCell.element(by.tagName(sample.element))
|
||||
: tableCell;
|
||||
|
||||
// Check element attribute or text
|
||||
var valueToCheck = sample.attr
|
||||
let valueToCheck = sample.attr
|
||||
? elementToCheck.getAttribute(sample.attr)
|
||||
: elementToCheck.getText();
|
||||
|
||||
|
@ -48,38 +49,38 @@ describe('Angular 1 to 2 Quick Reference Tests', 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 () {
|
||||
testPosterButtonClick("Hide Poster", false);
|
||||
testPosterButtonClick('Hide Poster', false);
|
||||
});
|
||||
|
||||
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 () {
|
||||
testFavoriteHero("Magneta", "No movie, sorry!");
|
||||
testFavoriteHero('Magneta', 'No movie, sorry!');
|
||||
});
|
||||
|
||||
it('should display a movie for Mr. Nice', function () {
|
||||
testFavoriteHero("Mr. Nice", "Excellent choice!");
|
||||
testFavoriteHero('Mr. Nice', 'Excellent choice!');
|
||||
});
|
||||
|
||||
function testImagesAreDisplayed(isDisplayed) {
|
||||
var expectedMovieCount = 3;
|
||||
function testImagesAreDisplayed(isDisplayed: boolean) {
|
||||
let expectedMovieCount = 3;
|
||||
|
||||
var movieRows = getMovieRows();
|
||||
let movieRows = getMovieRows();
|
||||
expect(movieRows.count()).toBe(expectedMovieCount);
|
||||
for (var i = 0; i < expectedMovieCount; i++) {
|
||||
var movieImage = movieRows.get(i).element(by.css('td > img'));
|
||||
for (let i = 0; i < expectedMovieCount; i++) {
|
||||
let movieImage = movieRows.get(i).element(by.css('td > img'));
|
||||
expect(movieImage.isDisplayed()).toBe(isDisplayed);
|
||||
}
|
||||
}
|
||||
|
||||
function testPosterButtonClick(expectedButtonText, isDisplayed) {
|
||||
var posterButton = element(by.css('movie-list tr > th > button'));
|
||||
function testPosterButtonClick(expectedButtonText: string, isDisplayed: boolean) {
|
||||
let posterButton = element(by.css('movie-list tr > th > button'));
|
||||
expect(posterButton.getText()).toBe(expectedButtonText);
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
function testFavoriteHero(heroName, expectedLabel) {
|
||||
var movieListComp = element(by.tagName('movie-list'));
|
||||
var heroInput = movieListComp.element(by.tagName('input'));
|
||||
var favoriteHeroLabel = movieListComp.element(by.tagName('h3'));
|
||||
var resultLabel = movieListComp.element(by.css('span > p'));
|
||||
function testFavoriteHero(heroName: string, expectedLabel: string) {
|
||||
let movieListComp = element(by.tagName('movie-list'));
|
||||
let heroInput = movieListComp.element(by.tagName('input'));
|
||||
let favoriteHeroLabel = movieListComp.element(by.tagName('h3'));
|
||||
let resultLabel = movieListComp.element(by.css('span > p'));
|
||||
|
||||
heroInput.clear().then(function () {
|
||||
sendKeys(heroInput, heroName || '').then(function () {
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path='../_protractor/e2e.d.ts' />
|
||||
describe('Component Communication Cookbook Tests', function () {
|
||||
|
||||
|
||||
|
@ -8,16 +9,16 @@ describe('Component Communication Cookbook Tests', function () {
|
|||
describe('Parent-to-child communication', function() {
|
||||
// #docregion parent-to-child
|
||||
// ...
|
||||
var _heroNames = ['Mr. IQ', 'Magneta', 'Bombasto'];
|
||||
var _masterName = 'Master';
|
||||
let _heroNames = ['Mr. IQ', 'Magneta', 'Bombasto'];
|
||||
let _masterName = 'Master';
|
||||
|
||||
it('should pass properties to children properly', function () {
|
||||
var parent = element.all(by.tagName('hero-parent')).get(0);
|
||||
var heroes = parent.all(by.tagName('hero-child'));
|
||||
let parent = element.all(by.tagName('hero-parent')).get(0);
|
||||
let heroes = parent.all(by.tagName('hero-child'));
|
||||
|
||||
for (var i = 0; i < _heroNames.length; i++) {
|
||||
var childTitle = heroes.get(i).element(by.tagName('h3')).getText();
|
||||
var childDetail = heroes.get(i).element(by.tagName('p')).getText();
|
||||
for (let i = 0; i < _heroNames.length; i++) {
|
||||
let childTitle = heroes.get(i).element(by.tagName('h3')).getText();
|
||||
let childDetail = heroes.get(i).element(by.tagName('p')).getText();
|
||||
expect(childTitle).toEqual(_heroNames[i] + ' says:')
|
||||
expect(childDetail).toContain(_masterName)
|
||||
}
|
||||
|
@ -30,22 +31,22 @@ describe('Component Communication Cookbook Tests', function () {
|
|||
// #docregion parent-to-child-setter
|
||||
// ...
|
||||
it('should display trimmed, non-empty names', function () {
|
||||
var _nonEmptyNameIndex = 0;
|
||||
var _nonEmptyName = '"Mr. IQ"';
|
||||
var parent = element.all(by.tagName('name-parent')).get(0);
|
||||
var hero = parent.all(by.tagName('name-child')).get(_nonEmptyNameIndex);
|
||||
let _nonEmptyNameIndex = 0;
|
||||
let _nonEmptyName = '"Mr. IQ"';
|
||||
let parent = element.all(by.tagName('name-parent')).get(0);
|
||||
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)
|
||||
});
|
||||
|
||||
it('should replace empty name with default name', function () {
|
||||
var _emptyNameIndex = 1;
|
||||
var _defaultName = '"<no name set>"';
|
||||
var parent = element.all(by.tagName('name-parent')).get(0);
|
||||
var hero = parent.all(by.tagName('name-child')).get(_emptyNameIndex);
|
||||
let _emptyNameIndex = 1;
|
||||
let _defaultName = '"<no name set>"';
|
||||
let parent = element.all(by.tagName('name-parent')).get(0);
|
||||
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)
|
||||
});
|
||||
// ...
|
||||
|
@ -57,26 +58,26 @@ describe('Component Communication Cookbook Tests', function () {
|
|||
// ...
|
||||
// Test must all execute in this exact order
|
||||
it('should set expected initial values', function () {
|
||||
var actual = getActual();
|
||||
let actual = getActual();
|
||||
|
||||
var initialLabel = "Version 1.23";
|
||||
var initialLog = 'major changed from {} to 1, minor changed from {} to 23';
|
||||
let initialLabel = 'Version 1.23';
|
||||
let initialLog = 'major changed from {} to 1, minor changed from {} to 23';
|
||||
|
||||
expect(actual.label).toBe(initialLabel);
|
||||
expect(actual.count).toBe(1);
|
||||
expect(actual.logs.get(0).getText()).toBe(initialLog);
|
||||
});
|
||||
|
||||
it('should set expected values after clicking "Minor" twice', function () {
|
||||
var repoTag = element(by.tagName('version-parent'));
|
||||
var newMinorButton = repoTag.all(by.tagName('button')).get(0);
|
||||
it('should set expected values after clicking \'Minor\' twice', function () {
|
||||
let repoTag = element(by.tagName('version-parent'));
|
||||
let newMinorButton = repoTag.all(by.tagName('button')).get(0);
|
||||
|
||||
newMinorButton.click().then(function() {
|
||||
newMinorButton.click().then(function() {
|
||||
var actual = getActual();
|
||||
let actual = getActual();
|
||||
|
||||
var labelAfter2Minor = "Version 1.25";
|
||||
var logAfter2Minor = 'minor changed from 24 to 25';
|
||||
let labelAfter2Minor = 'Version 1.25';
|
||||
let logAfter2Minor = 'minor changed from 24 to 25';
|
||||
|
||||
expect(actual.label).toBe(labelAfter2Minor);
|
||||
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 () {
|
||||
var repoTag = element(by.tagName('version-parent'));
|
||||
var newMajorButton = repoTag.all(by.tagName('button')).get(1);
|
||||
it('should set expected values after clicking \'Major\' once', function () {
|
||||
let repoTag = element(by.tagName('version-parent'));
|
||||
let newMajorButton = repoTag.all(by.tagName('button')).get(1);
|
||||
|
||||
newMajorButton.click().then(function() {
|
||||
var actual = getActual();
|
||||
let actual = getActual();
|
||||
|
||||
var labelAfterMajor = "Version 2.0";
|
||||
var logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
|
||||
let labelAfterMajor = 'Version 2.0';
|
||||
let logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
|
||||
|
||||
expect(actual.label).toBe(labelAfterMajor);
|
||||
expect(actual.count).toBe(4);
|
||||
|
@ -102,10 +103,10 @@ describe('Component Communication Cookbook Tests', function () {
|
|||
});
|
||||
|
||||
function getActual() {
|
||||
var versionTag = element(by.tagName('version-child'));
|
||||
var label = versionTag.element(by.tagName('h3')).getText();
|
||||
var ul = versionTag.element((by.tagName('ul')));
|
||||
var logs = ul.all(by.tagName('li'));
|
||||
let versionTag = element(by.tagName('version-child'));
|
||||
let label = versionTag.element(by.tagName('h3')).getText();
|
||||
let ul = versionTag.element((by.tagName('ul')));
|
||||
let logs = ul.all(by.tagName('li'));
|
||||
|
||||
return {
|
||||
label: label,
|
||||
|
@ -122,28 +123,28 @@ describe('Component Communication Cookbook Tests', function () {
|
|||
// #docregion child-to-parent
|
||||
// ...
|
||||
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();
|
||||
expect(voteLabel).toBe("Agree: 0, Disagree: 0");
|
||||
expect(voteLabel).toBe('Agree: 0, Disagree: 0');
|
||||
});
|
||||
|
||||
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);
|
||||
agreeButton1.click().then(function() {
|
||||
var voteLabel = element(by.tagName('vote-taker'))
|
||||
let voteLabel = element(by.tagName('vote-taker'))
|
||||
.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 () {
|
||||
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);
|
||||
agreeButton1.click().then(function() {
|
||||
var voteLabel = element(by.tagName('vote-taker'))
|
||||
let voteLabel = element(by.tagName('vote-taker'))
|
||||
.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() {
|
||||
countDownTimerTests('countdown-parent-vc')
|
||||
});
|
||||
|
||||
function countDownTimerTests(parentTag) {
|
||||
// #docregion countdown-timer-tests
|
||||
|
||||
function countDownTimerTests(parentTag: string) {
|
||||
// #docregion countdown-timer-tests
|
||||
// ...
|
||||
it('timer and parent seconds should match', function () {
|
||||
var parent = element(by.tagName(parentTag));
|
||||
var message = parent.element(by.tagName('countdown-timer')).getText();
|
||||
let parent = element(by.tagName(parentTag));
|
||||
let message = parent.element(by.tagName('countdown-timer')).getText();
|
||||
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);
|
||||
});
|
||||
|
||||
it('should stop the countdown', function () {
|
||||
var parent = element(by.tagName(parentTag));
|
||||
var stopButton = parent.all(by.tagName('button')).get(1);
|
||||
let parent = element(by.tagName(parentTag));
|
||||
let stopButton = parent.all(by.tagName('button')).get(1);
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
@ -187,10 +188,10 @@ describe('Component Communication Cookbook Tests', function () {
|
|||
// #docregion bidirectional-service
|
||||
// ...
|
||||
it('should announce a mission', function () {
|
||||
var missionControl = element(by.tagName('mission-control'));
|
||||
var announceButton = missionControl.all(by.tagName('button')).get(0);
|
||||
let missionControl = element(by.tagName('mission-control'));
|
||||
let announceButton = missionControl.all(by.tagName('button')).get(0);
|
||||
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.get(0).getText()).toMatch(/Mission.* announced/);
|
||||
});
|
||||
|
@ -208,14 +209,14 @@ describe('Component Communication Cookbook Tests', function () {
|
|||
testConfirmMission(2, 4, 'Swigert');
|
||||
});
|
||||
|
||||
function testConfirmMission(buttonIndex, expectedLogCount, astronaut) {
|
||||
var _confirmedLog = ' confirmed the mission';
|
||||
var missionControl = element(by.tagName('mission-control'));
|
||||
var confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
|
||||
function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
|
||||
let _confirmedLog = ' confirmed the mission';
|
||||
let missionControl = element(by.tagName('mission-control'));
|
||||
let confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
|
||||
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.get(expectedLogCount-1).getText()).toBe(astronaut + _confirmedLog);
|
||||
expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + _confirmedLog);
|
||||
});
|
||||
}
|
||||
// ...
|
|
@ -1,5 +1,12 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Cookbook: component-relative paths', function () {
|
||||
|
||||
interface Page {
|
||||
title: protractor.ElementFinder;
|
||||
absComp: protractor.ElementFinder;
|
||||
relComp: protractor.ElementFinder;
|
||||
|
||||
}
|
||||
function getPageStruct() {
|
||||
return {
|
||||
title: element( by.tagName( 'h1' )),
|
||||
|
@ -8,7 +15,7 @@ describe('Cookbook: component-relative paths', function () {
|
|||
}
|
||||
}
|
||||
|
||||
var page;
|
||||
let page: Page;
|
||||
beforeAll(function () {
|
||||
browser.get('');
|
||||
page = getPageStruct();
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Dependency Injection Cookbook', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -5,104 +6,104 @@ describe('Dependency Injection Cookbook', 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();
|
||||
});
|
||||
|
||||
it('"Bombasto" should be the logged in user', function () {
|
||||
loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0);
|
||||
|
||||
it('"Bombasto" should be the logged in user', function () {
|
||||
let loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0);
|
||||
expect(loggedInUser).toBeDefined();
|
||||
});
|
||||
|
||||
it('should render sorted heroes', function () {
|
||||
var sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0);
|
||||
|
||||
it('should render sorted heroes', function () {
|
||||
let sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0);
|
||||
expect(sortedHeroes).toBeDefined();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
it('RubberMan should be in sorted heroes', function () {
|
||||
sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0);
|
||||
|
||||
it('RubberMan should be in sorted heroes', function () {
|
||||
let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0);
|
||||
expect(sortedHero).toBeDefined();
|
||||
});
|
||||
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
it('should render Hero of the Month when DI deps are defined using provide()', function () {
|
||||
let heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0);
|
||||
expect(heroOfTheMonth).toBeDefined();
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
it('should render Hero of the Month when DI deps are defined using provide object literal', function () {
|
||||
let heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month 2"]')).get(0);
|
||||
expect(heroOfTheMonth).toBeDefined();
|
||||
});
|
||||
|
||||
it('should render Hero Bios', function () {
|
||||
var heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0);
|
||||
|
||||
it('should render Hero Bios', function () {
|
||||
let heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0);
|
||||
expect(heroBios).toBeDefined();
|
||||
});
|
||||
|
||||
it('should render Magma\'s description in Hero Bios', function () {
|
||||
var magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0);
|
||||
|
||||
it('should render Magma\'s description in Hero Bios', function () {
|
||||
let magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0);
|
||||
expect(magmaText).toBeDefined();
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
it('should render Magma\'s phone in Hero Bios and Contacts', function () {
|
||||
let magmaPhone = element.all(by.xpath('//div[text()="Phone #: 555-555-5555"]')).get(0);
|
||||
expect(magmaPhone).toBeDefined();
|
||||
});
|
||||
|
||||
it('should render Hero-of-the-Month runner-ups when DI deps are defined using provide()', function () {
|
||||
var runnersUp = element(by.id('rups1')).getText();
|
||||
|
||||
it('should render Hero-of-the-Month runner-ups when DI deps are defined using provide()', function () {
|
||||
let runnersUp = element(by.id('rups1')).getText();
|
||||
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 () {
|
||||
var runnersUp = element(by.id('rups2')).getText();
|
||||
|
||||
it('should render Hero-of-the-Month runner-ups when DI deps are defined using provide object literal', function () {
|
||||
let runnersUp = element(by.id('rups2')).getText();
|
||||
expect(runnersUp).toContain('RubberMan, Mr. Nice');
|
||||
});
|
||||
|
||||
it('should render DateLogger log entry in Hero-of-the-Month', function () {
|
||||
var logs = element.all(by.id('logs')).get(0).getText();
|
||||
|
||||
it('should render DateLogger log entry in Hero-of-the-Month', function () {
|
||||
let logs = element.all(by.id('logs')).get(0).getText();
|
||||
expect(logs).toContain('INFO: starting up at');
|
||||
});
|
||||
|
||||
|
||||
it('should highlight Hero Bios and Contacts container when mouseover', function () {
|
||||
var target = element(by.css('div[myHighlight="yellow"]'))
|
||||
var yellow = "rgba(255, 255, 0, 1)";
|
||||
|
||||
let target = element(by.css('div[myHighlight="yellow"]'))
|
||||
let yellow = 'rgba(255, 255, 0, 1)';
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
describe('in Parent Finder', function () {
|
||||
var cathy1 = element(by.css('alex cathy'));
|
||||
var craig1 = element(by.css('alex craig'));
|
||||
var carol1 = element(by.css('alex carol p'));
|
||||
var carol2 = element(by.css('barry carol p'));
|
||||
|
||||
it('"Cathy" should find "Alex" via the component class', function () {
|
||||
let cathy1 = element(by.css('alex cathy'));
|
||||
let craig1 = element(by.css('alex craig'));
|
||||
let carol1 = element(by.css('alex carol p'));
|
||||
let carol2 = element(by.css('barry carol p'));
|
||||
|
||||
it('"Cathy" should find "Alex" via the component class', function () {
|
||||
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');
|
||||
});
|
||||
|
||||
it('"Carol" within "Alex" should have "Alex" parent', function () {
|
||||
|
||||
it('"Carol" within "Alex" should have "Alex" parent', function () {
|
||||
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');
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
|
@ -1,3 +1,5 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
/* tslint:disable:quotemark */
|
||||
describe('Dynamic Form', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -5,17 +7,17 @@ describe('Dynamic 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');
|
||||
|
||||
var emailElement = element.all(by.css('input[id=emailAddress]')).get(0);
|
||||
var email = 'test@test.com';
|
||||
|
||||
let emailElement = element.all(by.css('input[id=emailAddress]')).get(0);
|
||||
let email = 'test@test.com';
|
||||
emailElement.sendKeys(email);
|
||||
expect(emailElement.getAttribute('value')).toEqual(email);
|
||||
|
||||
expect(emailElement.getAttribute('value')).toEqual(email);
|
||||
|
||||
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(){
|
||||
expect(element(by.xpath("//strong[contains(text(),'Saved the following values')]")).isPresent()).toBe(true);
|
||||
});
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
// gulp run-e2e-tests --filter=cb-set-document-title
|
||||
describe('Set Document Title', function () {
|
||||
|
||||
|
@ -7,7 +8,7 @@ describe('Set Document Title', function () {
|
|||
|
||||
it('should set the document title', function () {
|
||||
|
||||
var titles = [
|
||||
let titles = [
|
||||
'Good morning!',
|
||||
'Good afternoon!',
|
||||
'Good evening!'
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('TypeScript to Javascript tests', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -21,9 +22,9 @@ describe('TypeScript to Javascript tests', function () {
|
|||
});
|
||||
|
||||
it('should support optional, attribute, and query injections', function () {
|
||||
var app = element(by.css('hero-di-inject-additional'));
|
||||
var h1 = app.element(by.css('h1'));
|
||||
var okMsg = app.element(by.css('.ok-msg'));
|
||||
let app = element(by.css('hero-di-inject-additional'));
|
||||
let h1 = app.element(by.css('h1'));
|
||||
let okMsg = app.element(by.css('.ok-msg'));
|
||||
|
||||
expect(h1.getText()).toBe('Tour of Heroes');
|
||||
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 () {
|
||||
var app = element(by.css('hero-io'));
|
||||
var confirmComponent = app.element(by.css('my-confirm'));
|
||||
let app = element(by.css('hero-io'));
|
||||
let confirmComponent = app.element(by.css('my-confirm'));
|
||||
|
||||
confirmComponent.element(by.buttonText('OK')).click();
|
||||
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() {
|
||||
var app = element(by.css('heroes-bindings'));
|
||||
var h1 = app.element(by.css('h1'));
|
||||
let app = element(by.css('heroes-bindings'));
|
||||
let h1 = app.element(by.css('h1'));
|
||||
|
||||
expect(app.getAttribute('class')).toBe('heading');
|
||||
expect(app.getAttribute('title')).toBe('Tooltip content');
|
||||
|
@ -52,21 +53,21 @@ describe('TypeScript to Javascript tests', function () {
|
|||
expect(h1.getAttribute('class')).toBe('active');
|
||||
|
||||
h1.click();
|
||||
browser.actions().doubleClick(h1).perform();
|
||||
browser.actions().doubleClick(h1 as any as webdriver.WebElement).perform();
|
||||
expect(h1.getAttribute('class')).toBe('active');
|
||||
});
|
||||
|
||||
it('should support content and view queries', function() {
|
||||
var app = element(by.css('heroes-queries'));
|
||||
var windstorm = app.element(by.css('hero:first-child'));
|
||||
let app = element(by.css('heroes-queries'));
|
||||
let windstorm = app.element(by.css('hero:first-child'));
|
||||
|
||||
app.element(by.buttonText('Activate')).click();
|
||||
expect(windstorm.element(by.css('h2')).getAttribute('class')).toBe('active');
|
||||
expect(windstorm.element(by.css('active-label')).getText()).toBe('Active');
|
||||
});
|
||||
|
||||
function testTag(selector, expectedText) {
|
||||
var component = element(by.css(selector));
|
||||
function testTag(selector: string, expectedText: string) {
|
||||
let component = element(by.css(selector));
|
||||
expect(component.getText()).toBe(expectedText);
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Component Style Tests', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -5,8 +6,8 @@ describe('Component Style Tests', function () {
|
|||
});
|
||||
|
||||
it('scopes component styles to component view', function() {
|
||||
var componentH1 = element(by.css('hero-app > h1'));
|
||||
var externalH1 = element(by.css('body > h1'));
|
||||
let componentH1 = element(by.css('hero-app > h1'));
|
||||
let externalH1 = element(by.css('body > h1'));
|
||||
|
||||
expect(componentH1.getCssValue('fontWeight')).toEqual('normal');
|
||||
expect(externalH1.getCssValue('fontWeight')).not.toEqual('normal');
|
||||
|
@ -14,55 +15,55 @@ describe('Component Style Tests', 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');
|
||||
});
|
||||
|
||||
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();
|
||||
expect(host.getCssValue('borderWidth')).toEqual('3px');
|
||||
});
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
it('styles both view and content children with /deep/', function() {
|
||||
var viewH3 = element(by.css('hero-team h3'));
|
||||
var contentH3 = element(by.css('hero-controls h3'));
|
||||
let viewH3 = element(by.css('hero-team h3'));
|
||||
let contentH3 = element(by.css('hero-controls h3'));
|
||||
|
||||
expect(viewH3.getCssValue('fontStyle')).toEqual('italic');
|
||||
expect(contentH3.getCssValue('fontStyle')).toEqual('italic');
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
|
||||
it('processes template inline styles', function() {
|
||||
var button = element(by.css('hero-controls button'));
|
||||
var externalButton = element(by.css('body > button'));
|
||||
let button = element(by.css('hero-controls button'));
|
||||
let externalButton = element(by.css('body > button'));
|
||||
expect(button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff
|
||||
expect(externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)');
|
||||
});
|
||||
|
||||
it('processes template <link>s', function() {
|
||||
var li = element(by.css('hero-team li:first-child'));
|
||||
var externalLi = element(by.css('body > ul li'));
|
||||
let li = element(by.css('hero-team li:first-child'));
|
||||
let externalLi = element(by.css('body > ul li'));
|
||||
|
||||
expect(li.getCssValue('listStyleType')).toEqual('square');
|
||||
expect(externalLi.getCssValue('listStyleType')).not.toEqual('square');
|
||||
});
|
||||
|
||||
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
|
||||
});
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
|
||||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Dependency Injection Tests', function () {
|
||||
|
||||
|
||||
var expectedMsg;
|
||||
let expectedMsg: string;
|
||||
let expectedMsgRx: RegExp;
|
||||
|
||||
beforeAll(function () {
|
||||
browser.get('');
|
||||
|
@ -66,8 +67,8 @@ describe('Dependency Injection Tests', function () {
|
|||
describe('Tests:', function() {
|
||||
|
||||
it('Tests display as expected', function () {
|
||||
expectedMsg = /Tests passed/;
|
||||
expect(element(by.css('#tests')).getText()).toMatch(expectedMsg);
|
||||
expectedMsgRx = /Tests passed/;
|
||||
expect(element(by.css('#tests')).getText()).toMatch(expectedMsgRx);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -88,7 +89,7 @@ describe('Dependency Injection Tests', function () {
|
|||
expectedMsg = 'Hello from logger provided with useClass';
|
||||
expect(element(by.css('#p3')).getText()).toEqual(expectedMsg);
|
||||
});
|
||||
|
||||
|
||||
it('P3a (provide) displays as expected', function () {
|
||||
expectedMsg = 'Hello from logger provided with {provide: Logger, useClass: Logger}';
|
||||
expect(element(by.css('#p3a')).getText()).toEqual(expectedMsg);
|
||||
|
@ -147,22 +148,22 @@ describe('Dependency Injection Tests', function () {
|
|||
|
||||
describe('User/Heroes:', function() {
|
||||
it('User is Bob - unauthorized', function () {
|
||||
expectedMsg = /Bob, is not authorized/;
|
||||
expect(element(by.css('#user')).getText()).toMatch(expectedMsg);
|
||||
expectedMsgRx = /Bob, is not authorized/;
|
||||
expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
|
||||
});
|
||||
|
||||
it('should have button', function () {
|
||||
expect(element.all(by.cssContainingText('button','Next User'))
|
||||
.get(0).isDisplayed()).toBe(true, "'Next User' button should be displayed");
|
||||
expect(element.all(by.cssContainingText('button', 'Next User'))
|
||||
.get(0).isDisplayed()).toBe(true, '\'Next User\' button should be displayed');
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
heroes.filter(function(elem, index){
|
||||
|
@ -170,7 +171,7 @@ describe('Dependency Injection Tests', function () {
|
|||
return /secret/.test(text);
|
||||
});
|
||||
}).then(function(filteredElements) {
|
||||
//console.log("******Secret heroes count: "+filteredElements.length);
|
||||
// console.log("******Secret heroes count: "+filteredElements.length);
|
||||
expect(filteredElements.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
@ -182,22 +183,22 @@ describe('Dependency Injection Tests', function () {
|
|||
describe('after button click', function() {
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
it('User is Alice - authorized', function () {
|
||||
expectedMsg = /Alice, is authorized/;
|
||||
expect(element(by.css('#user')).getText()).toMatch(expectedMsg);
|
||||
expectedMsgRx = /Alice, is authorized/;
|
||||
expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
heroes.filter(function(elem, index){
|
||||
|
@ -205,7 +206,7 @@ describe('Dependency Injection Tests', function () {
|
|||
return /secret/.test(text);
|
||||
});
|
||||
}).then(function(filteredElements) {
|
||||
//console.log("******Secret heroes count: "+filteredElements.length);
|
||||
// console.log("******Secret heroes count: "+filteredElements.length);
|
||||
expect(filteredElements.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
/// <reference path='../_protractor/e2e.d.ts' />
|
||||
describe('Displaying Data Tests', function () {
|
||||
var _title = "Tour of Heroes";
|
||||
var _defaultHero = 'Windstorm'
|
||||
let _title = 'Tour of Heroes';
|
||||
let _defaultHero = 'Windstorm';
|
||||
|
||||
beforeAll(function () {
|
||||
browser.get('');
|
||||
|
@ -15,7 +16,7 @@ describe('Displaying Data Tests', 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');
|
||||
});
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describeIf(browser.appIsTs || browser.appIsJs, 'Forms Tests', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
|
@ -10,51 +11,51 @@ describeIf(browser.appIsTs || browser.appIsJs, 'Forms Tests', 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);
|
||||
});
|
||||
|
||||
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);
|
||||
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() {
|
||||
expect(ele.isDisplayed()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
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() {
|
||||
expect(element(by.css('h2')).getText()).toContain('You submitted the following');
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
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() {
|
||||
expect(alterEgoEle.isDisplayed()).toBe(false);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('should reflect submitted data after submit', function () {
|
||||
var test = 'testing 1 2 3';
|
||||
var newValue;
|
||||
var alterEgoEle = element.all(by.css('input[ngcontrol=alterEgo]')).get(0);
|
||||
let test = 'testing 1 2 3';
|
||||
let newValue: string;
|
||||
let alterEgoEle = element.all(by.css('input[ngcontrol=alterEgo]')).get(0);
|
||||
alterEgoEle.getAttribute('value').then(function(value) {
|
||||
// alterEgoEle.sendKeys(test);
|
||||
sendKeys(alterEgoEle, test);
|
||||
newValue = value + test;
|
||||
expect(alterEgoEle.getAttribute('value')).toEqual(newValue);
|
||||
}).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();
|
||||
}).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');
|
||||
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);
|
||||
});
|
||||
});
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Hierarchical dependency injection', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
|
@ -14,7 +15,7 @@ describe('Hierarchical dependency injection', 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() {
|
||||
expect(editButtonEle.isDisplayed()).toBe(false, "edit button should be hidden after selection");
|
||||
})
|
||||
|
@ -29,19 +30,19 @@ describe('Hierarchical dependency injection', function () {
|
|||
});
|
||||
|
||||
function testEdit(shouldSave) {
|
||||
var inputEle;
|
||||
let inputEle;
|
||||
// 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
|
||||
var heroNameEle = heroEle.all(by.css('hero-card span')).get(1);
|
||||
var editButtonEle = heroEle.element(by.cssContainingText('button','edit'));
|
||||
let heroNameEle = heroEle.all(by.css('hero-card span')).get(1);
|
||||
let editButtonEle = heroEle.element(by.cssContainingText('button','edit'));
|
||||
editButtonEle.click().then(function() {
|
||||
inputEle = heroEle.element(by.css('hero-editor input'));
|
||||
// return inputEle.sendKeys("foo");
|
||||
return sendKeys(inputEle, "foo");
|
||||
}).then(function() {
|
||||
buttonName = shouldSave ? 'save' : 'cancel';
|
||||
var buttonEle = heroEle.element(by.cssContainingText('button', buttonName));
|
||||
let buttonEle = heroEle.element(by.cssContainingText('button', buttonName));
|
||||
return buttonEle.click();
|
||||
}).then(function() {
|
||||
if (shouldSave) {
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Homepage Hello World', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -6,15 +6,15 @@ describe('Homepage Hello World', function () {
|
|||
});
|
||||
|
||||
// Does it even launch?
|
||||
var expectedLabel = 'Name:';
|
||||
let expectedLabel = 'Name:';
|
||||
it('should display the label: ' + expectedLabel, function () {
|
||||
expect(element(by.css('label')).getText()).toEqual(expectedLabel);
|
||||
});
|
||||
|
||||
it('should display entered name', function () {
|
||||
var testName = 'Bobby Joe';
|
||||
var newValue;
|
||||
var nameEle = element.all(by.css('input')).get(0);
|
||||
let testName = 'Bobby Joe';
|
||||
let newValue;
|
||||
let nameEle = element.all(by.css('input')).get(0);
|
||||
nameEle.getAttribute('value').then(function(value) {
|
||||
// nameEle.sendKeys(testName); // should work but doesn't
|
||||
sendKeys(nameEle, testName); // utility that does work
|
||||
|
@ -22,7 +22,7 @@ describe('Homepage Hello World', function () {
|
|||
expect(nameEle.getAttribute('value')).toEqual(newValue);
|
||||
}).then(function() {
|
||||
// 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 + '!');
|
||||
});
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Homepage Tabs', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -6,7 +6,7 @@ describe('Homepage Tabs', function () {
|
|||
});
|
||||
|
||||
// Does it even launch?
|
||||
var expectedAppTitle = 'Tabs Demo';
|
||||
let expectedAppTitle = 'Tabs Demo';
|
||||
it('should display app title: ' + expectedAppTitle, function () {
|
||||
expect(element(by.css('h4')).getText()).toEqual(expectedAppTitle);
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Homepage Todo', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -6,7 +6,7 @@ describe('Homepage Todo', function () {
|
|||
});
|
||||
|
||||
// Does it even launch?
|
||||
var expectedAppTitle = 'Todo';
|
||||
let expectedAppTitle = 'Todo';
|
||||
it('should display app title: ' + expectedAppTitle, function () {
|
||||
expect(element(by.css('h2')).getText()).toEqual(expectedAppTitle);
|
||||
});
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Lifecycle hooks', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -9,10 +10,10 @@ describe('Lifecycle hooks', 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");
|
||||
var 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 pabButton = element.all(by.css('peek-a-boo-parent button')).get(0);
|
||||
let updateHeroButton = element.all(by.css('peek-a-boo-parent button')).get(1);
|
||||
expect(pabButton.getText()).toContain('Create Peek');
|
||||
pabButton.click().then(function () {
|
||||
expect(pabButton.getText()).toContain('Destroy Peek');
|
||||
|
@ -30,12 +31,12 @@ describe('Lifecycle hooks', function () {
|
|||
});
|
||||
|
||||
it('should support OnChanges hook', function () {
|
||||
var onChangesViewEle = element.all(by.css('on-changes div')).get(0);
|
||||
var inputEles = element.all(by.css('on-changes-parent input'));
|
||||
var heroNameInputEle = inputEles.get(1);
|
||||
var powerInputEle = inputEles.get(0);
|
||||
var titleEle = onChangesViewEle.element(by.css('p'));
|
||||
var changeLogEles = onChangesViewEle.all(by.css('div'));
|
||||
let onChangesViewEle = element.all(by.css('on-changes div')).get(0);
|
||||
let inputEles = element.all(by.css('on-changes-parent input'));
|
||||
let heroNameInputEle = inputEles.get(1);
|
||||
let powerInputEle = inputEles.get(0);
|
||||
let titleEle = onChangesViewEle.element(by.css('p'));
|
||||
let changeLogEles = onChangesViewEle.all(by.css('div'));
|
||||
|
||||
expect(titleEle.getText()).toContain('Windstorm can sing');
|
||||
expect(changeLogEles.count()).toEqual(2, "should start with 2 messages");
|
||||
|
@ -54,13 +55,13 @@ describe('Lifecycle hooks', function () {
|
|||
});
|
||||
|
||||
it('should support DoCheck hook', function () {
|
||||
var doCheckViewEle = element.all(by.css('do-check div')).get(0);
|
||||
var inputEles = element.all(by.css('do-check-parent input'));
|
||||
var heroNameInputEle = inputEles.get(1);
|
||||
var powerInputEle = inputEles.get(0);
|
||||
var titleEle = doCheckViewEle.element(by.css('p'));
|
||||
var changeLogEles = doCheckViewEle.all(by.css('div'));
|
||||
var logCount;
|
||||
let doCheckViewEle = element.all(by.css('do-check div')).get(0);
|
||||
let inputEles = element.all(by.css('do-check-parent input'));
|
||||
let heroNameInputEle = inputEles.get(1);
|
||||
let powerInputEle = inputEles.get(0);
|
||||
let titleEle = doCheckViewEle.element(by.css('p'));
|
||||
let changeLogEles = doCheckViewEle.all(by.css('div'));
|
||||
let logCount;
|
||||
|
||||
expect(titleEle.getText()).toContain('Windstorm can sing');
|
||||
changeLogEles.count().then(function(count) {
|
||||
|
@ -86,12 +87,12 @@ describe('Lifecycle hooks', function () {
|
|||
});
|
||||
|
||||
it('should support AfterView hooks', function () {
|
||||
var parentEle = element(by.tagName('after-view-parent'));
|
||||
var buttonEle = parentEle.element(by.tagName('button')); // Reset
|
||||
var commentEle = parentEle.element(by.className('comment'));
|
||||
var logEles = parentEle.all(by.css('h4 ~ div'));
|
||||
var childViewInputEle = parentEle.element(by.css('my-child input'));
|
||||
var logCount;
|
||||
let parentEle = element(by.tagName('after-view-parent'));
|
||||
let buttonEle = parentEle.element(by.tagName('button')); // Reset
|
||||
let commentEle = parentEle.element(by.className('comment'));
|
||||
let logEles = parentEle.all(by.css('h4 ~ div'));
|
||||
let childViewInputEle = parentEle.element(by.css('my-child input'));
|
||||
let logCount;
|
||||
|
||||
expect(childViewInputEle.getAttribute('value')).toContain('Magneta');
|
||||
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 () {
|
||||
var parentEle = element(by.tagName('after-content-parent'));
|
||||
var buttonEle = parentEle.element(by.tagName('button')); // Reset
|
||||
var commentEle = parentEle.element(by.className('comment'));
|
||||
var logEles = parentEle.all(by.css('h4 ~ div'));
|
||||
var childViewInputEle = parentEle.element(by.css('my-child input'));
|
||||
var logCount;
|
||||
let parentEle = element(by.tagName('after-content-parent'));
|
||||
let buttonEle = parentEle.element(by.tagName('button')); // Reset
|
||||
let commentEle = parentEle.element(by.className('comment'));
|
||||
let logEles = parentEle.all(by.css('h4 ~ div'));
|
||||
let childViewInputEle = parentEle.element(by.css('my-child input'));
|
||||
let logCount;
|
||||
|
||||
expect(childViewInputEle.getAttribute('value')).toContain('Magneta');
|
||||
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 () {
|
||||
var inputEle = element(by.css('spy-parent input'));
|
||||
var addHeroButtonEle = element(by.cssContainingText('spy-parent button','Add Hero'));
|
||||
var resetHeroesButtonEle = element(by.cssContainingText('spy-parent button','Reset Heroes'));
|
||||
var heroEles = element.all(by.css('spy-parent div[mySpy'));
|
||||
var logEles = element.all(by.css('spy-parent h4 ~ div'));
|
||||
let inputEle = element(by.css('spy-parent input'));
|
||||
let addHeroButtonEle = element(by.cssContainingText('spy-parent button','Add Hero'));
|
||||
let resetHeroesButtonEle = element(by.cssContainingText('spy-parent button','Reset Heroes'));
|
||||
let heroEles = element.all(by.css('spy-parent div[mySpy'));
|
||||
let logEles = element.all(by.css('spy-parent h4 ~ div'));
|
||||
expect(heroEles.count()).toBe(2, 'should have two heroes displayed');
|
||||
expect(logEles.count()).toBe(2, 'should have two log entries');
|
||||
sendKeys(inputEle, "-test-").then(function() {
|
||||
|
@ -164,10 +165,10 @@ describe('Lifecycle hooks', function () {
|
|||
});
|
||||
|
||||
it('should support "spy counter"', function () {
|
||||
var updateCounterButtonEle = element(by.cssContainingText('counter-parent button','Update'));
|
||||
var resetCounterButtonEle = element(by.cssContainingText('counter-parent button','Reset'));
|
||||
var textEle = element(by.css('counter-parent my-counter > div'));
|
||||
var logEles = element.all(by.css('counter-parent h4 ~ div'));
|
||||
let updateCounterButtonEle = element(by.cssContainingText('counter-parent button','Update'));
|
||||
let resetCounterButtonEle = element(by.cssContainingText('counter-parent button','Reset'));
|
||||
let textEle = element(by.css('counter-parent my-counter > div'));
|
||||
let logEles = element.all(by.css('counter-parent h4 ~ div'));
|
||||
expect(textEle.getText()).toContain('Counter = 0');
|
||||
expect(logEles.count()).toBe(2, 'should start with two log entries');
|
||||
updateCounterButtonEle.click().then(function() {
|
|
@ -69,6 +69,7 @@
|
|||
"rimraf": "^2.5.2",
|
||||
"style-loader": "^0.13.1",
|
||||
"ts-loader": "^0.8.2",
|
||||
"ts-node": "^0.7.3",
|
||||
"typescript": "^1.8.10",
|
||||
"typings": "^1.0.4",
|
||||
"webpack": "^1.13.0",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Pipes', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -23,9 +24,9 @@ describe('Pipes', 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");
|
||||
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);
|
||||
buttonEle.click().then(function() {
|
||||
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 () {
|
||||
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.get(0).getText()).toContain('APR 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 () {
|
||||
var ele = element(by.css('power-booster p'));
|
||||
let ele = element(by.css('power-booster p'));
|
||||
expect(ele.getText()).toContain('Super power boost: 1024');
|
||||
});
|
||||
|
||||
it('should be able to use the exponential calculator', function () {
|
||||
var eles = element.all(by.css('power-boost-calculator input'));
|
||||
var baseInputEle = eles.get(0);
|
||||
var factorInputEle = eles.get(1);
|
||||
var outputEle = element(by.css('power-boost-calculator p'));
|
||||
let eles = element.all(by.css('power-boost-calculator input'));
|
||||
let baseInputEle = eles.get(0);
|
||||
let factorInputEle = eles.get(1);
|
||||
let outputEle = element(by.css('power-boost-calculator p'));
|
||||
baseInputEle.clear().then(function() {
|
||||
return sendKeys(baseInputEle, "7");
|
||||
}).then(function() {
|
||||
|
@ -63,11 +64,11 @@ describe('Pipes', function () {
|
|||
|
||||
|
||||
it('should support flying heroes (pure) ', function () {
|
||||
var nameEle = element(by.css('flying-heroes input[type="text"]'));
|
||||
var canFlyCheckEle = element(by.css('flying-heroes #can-fly'));
|
||||
var mutateCheckEle = element(by.css('flying-heroes #mutate'));
|
||||
var resetEle = element(by.css('flying-heroes button'));
|
||||
var flyingHeroesEle = element.all(by.css('flying-heroes #flyers div'));
|
||||
let nameEle = element(by.css('flying-heroes input[type="text"]'));
|
||||
let canFlyCheckEle = element(by.css('flying-heroes #can-fly'));
|
||||
let mutateCheckEle = element(by.css('flying-heroes #mutate'));
|
||||
let resetEle = element(by.css('flying-heroes button'));
|
||||
let flyingHeroesEle = element.all(by.css('flying-heroes #flyers div'));
|
||||
|
||||
expect(canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"');
|
||||
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 () {
|
||||
var nameEle = element(by.css('flying-heroes-impure input[type="text"]'));
|
||||
var canFlyCheckEle = element(by.css('flying-heroes-impure #can-fly'));
|
||||
var mutateCheckEle = element(by.css('flying-heroes-impure #mutate'));
|
||||
var resetEle = element(by.css('flying-heroes-impure button'));
|
||||
var flyingHeroesEle = element.all(by.css('flying-heroes-impure #flyers div'));
|
||||
let nameEle = element(by.css('flying-heroes-impure input[type="text"]'));
|
||||
let canFlyCheckEle = element(by.css('flying-heroes-impure #can-fly'));
|
||||
let mutateCheckEle = element(by.css('flying-heroes-impure #mutate'));
|
||||
let resetEle = element(by.css('flying-heroes-impure button'));
|
||||
let flyingHeroesEle = element.all(by.css('flying-heroes-impure #flyers div'));
|
||||
|
||||
expect(canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"');
|
||||
expect(mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array');
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('QuickStart E2E Tests', function () {
|
||||
|
||||
var expectedMsg = 'My First Angular 2 App';
|
||||
let expectedMsg = 'My First Angular 2 App';
|
||||
|
||||
|
||||
beforeEach(function () {
|
|
@ -1,123 +1,111 @@
|
|||
/// <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')),
|
||||
|
||||
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 () {
|
||||
var 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 () {
|
||||
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 the start screen', function () {
|
||||
var 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 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 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 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');
|
||||
}
|
||||
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 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
|
|
@ -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');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
|
@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
|||
/**
|
||||
* Async modal dialog 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()
|
||||
export class DialogService {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Router', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -26,19 +27,19 @@ describe('Router', 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.crisisHref.getText()).toEqual("Crisis Center");
|
||||
expect(page.heroesHref.getText()).toEqual("Heroes");
|
||||
});
|
||||
|
||||
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");
|
||||
});
|
||||
|
||||
it('should be able to see hero items', function () {
|
||||
var page = getPageStruct();
|
||||
let page = getPageStruct();
|
||||
page.heroesHref.click().then(function() {
|
||||
expect(page.routerTitle.getText()).toContain('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 () {
|
||||
var page = getPageStruct();
|
||||
let page = getPageStruct();
|
||||
page.crisisHref.click().then(function() {
|
||||
expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries");
|
||||
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 () {
|
||||
var page = getPageStruct();
|
||||
var heroEle, heroText;
|
||||
let page = getPageStruct();
|
||||
let heroEle, heroText;
|
||||
page.heroesHref.click().then(function() {
|
||||
heroEle = page.heroesList.get(4);
|
||||
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.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'));
|
||||
let 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'));
|
||||
let buttonEle = page.heroDetail.element(by.css('button'));
|
||||
return buttonEle.click();
|
||||
}).then(function() {
|
||||
expect(heroEle.getText()).toContain(heroText + '-foo');
|
||||
|
@ -90,8 +91,8 @@ describe('Router', function () {
|
|||
});
|
||||
|
||||
function crisisCenterEdit(index, shouldSave) {
|
||||
var page = getPageStruct();
|
||||
var crisisEle, crisisText;
|
||||
let page = getPageStruct();
|
||||
let crisisEle, crisisText;
|
||||
page.crisisHref.click()
|
||||
.then(function () {
|
||||
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.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'));
|
||||
let 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'));
|
||||
let buttonEle = page.crisisDetail.element(by.cssContainingText('button', shouldSave ? 'Save' : 'Cancel'));
|
||||
return buttonEle.click();
|
||||
}).then(function () {
|
||||
if (shouldSave) {
|
|
@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
|||
/**
|
||||
* Async modal dialog 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()
|
||||
export class DialogService {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Server Communication', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -6,14 +7,14 @@ describe('Server Communication', function () {
|
|||
|
||||
describe('Tour of Heroes (Observable)', function () {
|
||||
|
||||
var initialHeroCount = 4;
|
||||
var newHeroName = 'Mr. IQ';
|
||||
var heroCountAfterAdd = 5;
|
||||
let initialHeroCount = 4;
|
||||
let newHeroName = 'Mr. IQ';
|
||||
let heroCountAfterAdd = 5;
|
||||
|
||||
var heroListComp = element(by.tagName('hero-list'));
|
||||
var addButton = heroListComp.element(by.tagName('button'));
|
||||
var heroTags = heroListComp.all(by.tagName('li'));
|
||||
var heroNameInput = heroListComp.element(by.tagName('input'));
|
||||
let heroListComp = element(by.tagName('hero-list'));
|
||||
let addButton = heroListComp.element(by.tagName('button'));
|
||||
let heroTags = heroListComp.all(by.tagName('li'));
|
||||
let heroNameInput = heroListComp.element(by.tagName('input'));
|
||||
|
||||
it('should exist', function() {
|
||||
expect(heroListComp).toBeDefined('<hero-list> must exist');
|
||||
|
@ -36,7 +37,7 @@ describe('Server Communication', function () {
|
|||
sendKeys(heroNameInput, newHeroName);
|
||||
addButton.click().then(function() {
|
||||
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');
|
||||
});
|
||||
})
|
||||
|
@ -45,9 +46,9 @@ describe('Server Communication', function () {
|
|||
describe('Wikipedia Demo', 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');
|
||||
var resultList = myWikiComp.all(by.tagName('li'));
|
||||
let resultList = myWikiComp.all(by.tagName('li'));
|
||||
expect(resultList.count()).toBe(0, 'result list must be empty');
|
||||
});
|
||||
|
||||
|
@ -77,9 +78,9 @@ describe('Server Communication', function () {
|
|||
describe('Smarter Wikipedia Demo', 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');
|
||||
var resultList = myWikiSmartComp.all(by.tagName('li'));
|
||||
let resultList = myWikiSmartComp.all(by.tagName('li'));
|
||||
expect(resultList.count()).toBe(0, 'result list must be empty');
|
||||
});
|
||||
|
||||
|
@ -111,14 +112,14 @@ describe('Server Communication', function () {
|
|||
});
|
||||
|
||||
function testForResult(componentTagName, keyPressed, hasListBeforeSearch, done) {
|
||||
var searchWait = 1000; // Wait for wikipedia but not so long that tests timeout
|
||||
var wikiComponent = element(by.tagName(componentTagName));
|
||||
let searchWait = 1000; // Wait for wikipedia but not so long that tests timeout
|
||||
let wikiComponent = element(by.tagName(componentTagName));
|
||||
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');
|
||||
|
||||
searchBox.sendKeys(keyPressed).then(function () {
|
||||
var resultList = wikiComponent.all(by.tagName('li'));
|
||||
let resultList = wikiComponent.all(by.tagName('li'));
|
||||
|
||||
if (hasListBeforeSearch) {
|
||||
expect(resultList.count()).toBeGreaterThan(0, 'result list should not be empty before search');
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Structural Directives', function () {
|
||||
|
||||
// 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 () {
|
||||
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(1).getText()).toEqual('Mr. Nice');
|
||||
expect(allDivEles.get(4).getText()).toEqual('Ready');
|
||||
});
|
||||
|
||||
it('should be able to toggle ngIf with a button', function () {
|
||||
var setConditionButtonEle = element.all(by.css('button')).get(0);
|
||||
var conditionTrueEles = element.all(by.cssContainingText('p', 'condition is true'));
|
||||
var conditionFalseEles = element.all(by.cssContainingText('p', 'condition is false'));
|
||||
let setConditionButtonEle = element.all(by.css('button')).get(0);
|
||||
let conditionTrueEles = element.all(by.cssContainingText('p', 'condition is true'));
|
||||
let conditionFalseEles = element.all(by.cssContainingText('p', 'condition is false'));
|
||||
expect(conditionTrueEles.count()).toBe(2, 'should be two condition true elements');
|
||||
expect(conditionFalseEles.count()).toBe(0, 'should be no condition false elements');
|
||||
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 () {
|
||||
var setConditionButtonEle = element.all(by.css('button')).get(0);
|
||||
var ngIfButtonEle = element(by.cssContainingText('button', 'if | !if'));
|
||||
var ngIfParentEle = ngIfButtonEle.element(by.xpath('..'));
|
||||
var ngIfSiblingEle = ngIfParentEle.element(by.css('heavy-loader'));
|
||||
var cssButtonEle = element(by.cssContainingText('button', 'show | hide'));
|
||||
var cssSiblingEle = cssButtonEle.element(by.xpath('..')).element(by.css('heavy-loader'));
|
||||
var setConditionText;
|
||||
let setConditionButtonEle = element.all(by.css('button')).get(0);
|
||||
let ngIfButtonEle = element(by.cssContainingText('button', 'if | !if'));
|
||||
let ngIfParentEle = ngIfButtonEle.element(by.xpath('..'));
|
||||
let ngIfSiblingEle = ngIfParentEle.element(by.css('heavy-loader'));
|
||||
let cssButtonEle = element(by.cssContainingText('button', 'show | hide'));
|
||||
let cssSiblingEle = cssButtonEle.element(by.xpath('..')).element(by.css('heavy-loader'));
|
||||
let setConditionText;
|
||||
setConditionButtonEle.getText().then(function(text) {
|
||||
setConditionText = text;
|
||||
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 () {
|
||||
var setConditionButtonEle = element.all(by.css('button')).get(0);
|
||||
var displayEles = element.all(by.cssContainingText('p', 'Our heroes are true!'));
|
||||
let setConditionButtonEle = element.all(by.css('button')).get(0);
|
||||
let displayEles = element.all(by.cssContainingText('p', 'Our heroes are true!'));
|
||||
expect(displayEles.count()).toBe(2, "should be displaying two ngIf elements");
|
||||
setConditionButtonEle.click().then(function() {
|
||||
expect(displayEles.count()).toBe(0, "should nog longer be displaying ngIf elements");
|
|
@ -1,8 +1,9 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Style Guide', function () {
|
||||
it('01-01', function () {
|
||||
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('Tornado');
|
||||
expect(pre.getText()).toContain('Magneta');
|
||||
|
@ -11,8 +12,8 @@ describe('Style Guide', function () {
|
|||
it('02-07', function () {
|
||||
browser.get('#/02-07');
|
||||
|
||||
var hero = element(by.tagName('toh-hero > div'));
|
||||
var users = element(by.tagName('admin-users > div'));
|
||||
let hero = element(by.tagName('toh-hero > div'));
|
||||
let users = element(by.tagName('admin-users > div'));
|
||||
|
||||
expect(hero.getText()).toBe('hero component');
|
||||
expect(users.getText()).toBe('users component');
|
||||
|
@ -21,21 +22,21 @@ describe('Style Guide', function () {
|
|||
it('02-08', function () {
|
||||
browser.get('#/02-08');
|
||||
|
||||
var input = element(by.tagName('input[tohvalidate]'));
|
||||
let input = element(by.tagName('input[tohvalidate]'));
|
||||
expect(input.isPresent()).toBe(true);
|
||||
});
|
||||
|
||||
it('03-01', function () {
|
||||
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');
|
||||
});
|
||||
|
||||
it('03-02', function () {
|
||||
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(1).getText()).toBe('Villains url: api/villains');
|
||||
});
|
||||
|
@ -43,14 +44,14 @@ describe('Style Guide', function () {
|
|||
it('03-03', function () {
|
||||
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');
|
||||
});
|
||||
|
||||
it('03-04', function () {
|
||||
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(1).getText()).toBe('Hide toast');
|
||||
});
|
||||
|
@ -58,10 +59,10 @@ describe('Style Guide', function () {
|
|||
it('03-05', function () {
|
||||
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');
|
||||
|
||||
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(1).getText()).toBe('Bombasto');
|
||||
expect(lis.get(2).getText()).toBe('Magneta');
|
||||
|
@ -71,10 +72,10 @@ describe('Style Guide', function () {
|
|||
it('03-06', function () {
|
||||
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');
|
||||
|
||||
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(1).getText()).toBe('Bombasto');
|
||||
expect(lis.get(2).getText()).toBe('Magneta');
|
||||
|
@ -84,77 +85,77 @@ describe('Style Guide', function () {
|
|||
it('04-10', function () {
|
||||
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');
|
||||
});
|
||||
|
||||
it('04-14', function () {
|
||||
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');
|
||||
});
|
||||
|
||||
it('05-02', function () {
|
||||
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');
|
||||
});
|
||||
|
||||
it('05-03', function () {
|
||||
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');
|
||||
});
|
||||
|
||||
it('05-04', function () {
|
||||
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');
|
||||
});
|
||||
|
||||
it('05-12', function () {
|
||||
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');
|
||||
});
|
||||
|
||||
it('05-13', function () {
|
||||
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');
|
||||
});
|
||||
|
||||
it('05-14', function () {
|
||||
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('...');
|
||||
});
|
||||
|
||||
it('05-15', function () {
|
||||
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('...');
|
||||
});
|
||||
|
||||
it('05-16', function () {
|
||||
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('...');
|
||||
});
|
||||
|
||||
it('05-17', function () {
|
||||
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('Total powers');
|
||||
expect(section.getText()).toContain('Average power');
|
||||
|
@ -163,21 +164,21 @@ describe('Style Guide', function () {
|
|||
it('06-01', function () {
|
||||
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');
|
||||
});
|
||||
|
||||
it('06-03', function () {
|
||||
browser.get('#/06-03');
|
||||
|
||||
var input = element(by.tagName('input[tohvalidator]'));
|
||||
let input = element(by.tagName('input[tohvalidator]'));
|
||||
expect(input.isPresent()).toBe(true);
|
||||
});
|
||||
|
||||
it('07-01', function () {
|
||||
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(1).getText()).toBe('Bombasto');
|
||||
expect(lis.get(2).getText()).toBe('Magneta');
|
||||
|
@ -187,21 +188,21 @@ describe('Style Guide', function () {
|
|||
it('07-03', function () {
|
||||
browser.get('#/07-03');
|
||||
|
||||
var pre = element(by.tagName('toh-heroes > pre'));
|
||||
let pre = element(by.tagName('toh-heroes > pre'));
|
||||
expect(pre.getText()).toContain('[]');
|
||||
});
|
||||
|
||||
it('07-04', function () {
|
||||
browser.get('#/07-04');
|
||||
|
||||
var pre = element(by.tagName('toh-app > pre'));
|
||||
let pre = element(by.tagName('toh-app > pre'));
|
||||
expect(pre.getText()).toContain('[]');
|
||||
});
|
||||
|
||||
it('09-01', function () {
|
||||
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');
|
||||
});
|
||||
});
|
|
@ -8,7 +8,7 @@ import { Component } from '@angular/core';
|
|||
template: `<button>OK<button>`
|
||||
})
|
||||
export class HeroButtonComponent {
|
||||
onInit() { // mispelled
|
||||
onInit() { // misspelled
|
||||
console.log('The component is initialized');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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/');
|
||||
});
|
||||
|
||||
});
|
|
@ -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/');
|
||||
});
|
||||
|
||||
});
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
// Not yet complete
|
||||
describe('Template Syntax', function () {
|
||||
|
||||
|
@ -6,24 +7,24 @@ describe('Template Syntax', 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');
|
||||
});
|
||||
|
||||
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.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');
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
|
@ -197,13 +197,18 @@ button</button>
|
|||
<!-- #enddocregion property-binding-7 -->
|
||||
|
||||
<!-- #docregion property-binding-vs-interpolation -->
|
||||
Interpolated: <img src="{{heroImageUrl}}"><br>
|
||||
Property bound: <img [src]="heroImageUrl">
|
||||
<p><img src="{{heroImageUrl}}"> is the <i>interpolated</i> image.</p>
|
||||
<p><img [src]="heroImageUrl"> is the <i>property bound</i> image.</p>
|
||||
|
||||
<div>The interpolated title is {{title}}</div>
|
||||
<div [innerHTML]="'The [innerHTML] title is '+title"></div>
|
||||
<p><span>"{{title}}" is the <i>interpolated</i> title.</span></p>
|
||||
<p>"<span [innerHTML]="title"></span>" is the <i>property bound</i> title.</p>
|
||||
<!-- #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>
|
||||
|
||||
<!-- attribute binding -->
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
//#docplaster
|
||||
/* tslint:disable:member-ordering forin */
|
||||
// #docplaster
|
||||
|
||||
import { AfterViewInit, Component, ElementRef, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { NgForm } from '@angular/common';
|
||||
|
@ -8,7 +9,7 @@ import { HeroDetailComponent, BigHeroDetailComponent } from './hero-detail.compo
|
|||
import { MyClickDirective, MyClickDirective2 } from './my-click.directive';
|
||||
|
||||
// Alerter fn: monkey patch during test
|
||||
export function alerter(msg?:string) {
|
||||
export function alerter(msg?: string) {
|
||||
window.alert(msg);
|
||||
}
|
||||
|
||||
|
@ -27,7 +28,7 @@ export enum Color {Red, Green, Blue};
|
|||
})
|
||||
export class AppComponent implements AfterViewInit, OnInit {
|
||||
|
||||
ngOnInit(){
|
||||
ngOnInit() {
|
||||
this.refreshHeroes();
|
||||
}
|
||||
|
||||
|
@ -40,43 +41,48 @@ export class AppComponent implements AfterViewInit, OnInit {
|
|||
badCurly = 'bad curly';
|
||||
classes = 'special';
|
||||
|
||||
callFax(value:string) {this.alert(`Faxing ${value} ...`)}
|
||||
callPhone(value:string) {this.alert(`Calling ${value} ...`)}
|
||||
callFax(value: string) {this.alert(`Faxing ${value} ...`); }
|
||||
callPhone(value: string) {this.alert(`Calling ${value} ...`); }
|
||||
canSave = true;
|
||||
|
||||
Color = Color;
|
||||
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];
|
||||
|
||||
deleteHero(hero:Hero){
|
||||
this.alert('Deleted hero: '+ (hero && hero.firstName))
|
||||
deleteHero(hero: Hero) {
|
||||
this.alert('Deleted hero: ' + (hero && hero.firstName));
|
||||
}
|
||||
|
||||
// DevMode memoization fields
|
||||
private priorClasses:{};
|
||||
private _priorStyles:{};
|
||||
private _priorStyles2:{};
|
||||
// #docregion evil-title
|
||||
evilTitle = 'Template <script>alert("evil never sleeps")</script>Syntax';
|
||||
// #enddocregion evil-title
|
||||
|
||||
getStyles(el:Element){
|
||||
title = 'Template Syntax';
|
||||
|
||||
// DevMode memoization fields
|
||||
private priorClasses: {};
|
||||
private _priorStyles: {};
|
||||
|
||||
getStyles(el: Element) {
|
||||
let styles = window.getComputedStyle(el);
|
||||
let showStyles = {};
|
||||
for (var p in this.setStyles()){
|
||||
for (let p in this.setStyles()) {
|
||||
showStyles[p] = styles[p];
|
||||
}
|
||||
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';
|
||||
// Public Domain terms of use: http://www.wpclipart.com/terms.html
|
||||
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 = '';
|
||||
clickMessage = '';
|
||||
clickMessage2 = '';
|
||||
|
@ -85,28 +91,28 @@ export class AppComponent implements AfterViewInit, OnInit {
|
|||
isSpecial = true;
|
||||
isUnchanged = true;
|
||||
|
||||
nullHero:Hero = null; // or undefined
|
||||
nullHero: Hero = null; // or undefined
|
||||
|
||||
onCancel(event:KeyboardEvent){
|
||||
let evtMsg = event ? ' Event target is '+ (<HTMLElement>event.target).innerHTML : '';
|
||||
this.alert('Canceled.'+evtMsg)
|
||||
onCancel(event: KeyboardEvent) {
|
||||
let evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).innerHTML : '';
|
||||
this.alert('Canceled.' + evtMsg);
|
||||
}
|
||||
|
||||
onClickMe(event:KeyboardEvent){
|
||||
let evtMsg = event ? ' Event target class is '+ (<HTMLElement>event.target).className : '';
|
||||
this.alert('Click me.'+evtMsg)
|
||||
onClickMe(event: KeyboardEvent) {
|
||||
let evtMsg = event ? ' Event target class is ' + (<HTMLElement>event.target).className : '';
|
||||
this.alert('Click me.' + evtMsg);
|
||||
}
|
||||
|
||||
onSave(event:KeyboardEvent){
|
||||
let evtMsg = event ? ' Event target is '+ (<HTMLElement>event.target).innerText : '';
|
||||
this.alert('Saved.'+evtMsg)
|
||||
onSave(event: KeyboardEvent) {
|
||||
let evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).innerText : '';
|
||||
this.alert('Saved.' + evtMsg);
|
||||
}
|
||||
|
||||
onSubmit(form:NgForm){
|
||||
onSubmit(form: NgForm) {
|
||||
let evtMsg = form.valid ?
|
||||
' Form value is '+ JSON.stringify(form.value) :
|
||||
' Form value is ' + JSON.stringify(form.value) :
|
||||
' Form is invalid';
|
||||
this.alert('Form submitted.'+evtMsg)
|
||||
this.alert('Form submitted.' + evtMsg);
|
||||
}
|
||||
|
||||
product = {
|
||||
|
@ -123,10 +129,10 @@ export class AppComponent implements AfterViewInit, OnInit {
|
|||
|
||||
// #docregion same-as-it-ever-was
|
||||
private samenessCount = 5;
|
||||
moreOfTheSame() {this.samenessCount++;};
|
||||
moreOfTheSame() { this.samenessCount++; };
|
||||
get sameAsItEverWas() {
|
||||
var result:string[] = Array(this.samenessCount);
|
||||
for (var i=result.length; i-- > 0;){result[i]='same as it ever was ...'}
|
||||
let result: string[] = Array(this.samenessCount);
|
||||
for ( let i = result.length; i-- > 0; ) { result[i] = 'same as it ever was ...'; }
|
||||
return result;
|
||||
// return [1,2,3,4,5].map(id => {
|
||||
// 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
|
||||
|
||||
setUpperCaseFirstName(firstName:string){
|
||||
//console.log(firstName);
|
||||
setUpperCaseFirstName(firstName: string) {
|
||||
// console.log(firstName);
|
||||
this.currentHero.firstName = firstName.toUpperCase();
|
||||
}
|
||||
|
||||
|
@ -145,10 +151,10 @@ export class AppComponent implements AfterViewInit, OnInit {
|
|||
saveable: this.canSave, // true
|
||||
modified: !this.isUnchanged, // false
|
||||
special: this.isSpecial, // true
|
||||
}
|
||||
};
|
||||
// #enddocregion setClasses
|
||||
// compensate for DevMode (sigh)
|
||||
if (JSON.stringify(classes) === JSON.stringify(this.priorClasses)){
|
||||
if (JSON.stringify(classes) === JSON.stringify(this.priorClasses)) {
|
||||
return this.priorClasses;
|
||||
}
|
||||
this.priorClasses = classes;
|
||||
|
@ -165,10 +171,10 @@ export class AppComponent implements AfterViewInit, OnInit {
|
|||
'font-style': this.canSave ? 'italic' : 'normal', // italic
|
||||
'font-weight': !this.isUnchanged ? 'bold' : 'normal', // normal
|
||||
'font-size': this.isSpecial ? '24px' : '8px', // 24px
|
||||
}
|
||||
};
|
||||
// #enddocregion setStyles
|
||||
// compensate for DevMode (sigh)
|
||||
if (JSON.stringify(styles) === JSON.stringify(this._priorStyles)){
|
||||
if (JSON.stringify(styles) === JSON.stringify(this._priorStyles)) {
|
||||
return this._priorStyles;
|
||||
}
|
||||
this._priorStyles = styles;
|
||||
|
@ -178,15 +184,14 @@ export class AppComponent implements AfterViewInit, OnInit {
|
|||
// #enddocregion setStyles
|
||||
|
||||
toeChoice = '';
|
||||
toeChooser(picker:HTMLFieldSetElement){
|
||||
toeChooser(picker: HTMLFieldSetElement) {
|
||||
let choices = picker.children;
|
||||
for (let i=0; i<choices.length; i++){
|
||||
var choice = <HTMLInputElement>choices[i];
|
||||
if (choice.checked) {return this.toeChoice = choice.value}
|
||||
for (let i = 0; i < choices.length; i++) {
|
||||
let choice = <HTMLInputElement>choices[i];
|
||||
if (choice.checked) {return this.toeChoice = choice.value; }
|
||||
}
|
||||
}
|
||||
|
||||
title = 'Template Syntax';
|
||||
|
||||
// #docregion trackByHeroes
|
||||
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']; }
|
||||
// #enddocregion trackById
|
||||
|
||||
val=2;
|
||||
// villainImageUrl = 'http://www.clker.com/cliparts/u/s/y/L/x/9/villain-man-hi.png'
|
||||
val = 2;
|
||||
// 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
|
||||
villainImageUrl = 'images/villain.png'
|
||||
villainImageUrl = 'images/villain.png';
|
||||
|
||||
|
||||
//////// Detect effects of NgForTrackBy ///////////////
|
||||
@ViewChildren('noTrackBy') childrenNoTrackBy:QueryList<ElementRef>;
|
||||
@ViewChildren('withTrackBy') childrenWithTrackBy:QueryList<ElementRef>;
|
||||
@ViewChildren('noTrackBy') childrenNoTrackBy: QueryList<ElementRef>;
|
||||
@ViewChildren('withTrackBy') childrenWithTrackBy: QueryList<ElementRef>;
|
||||
|
||||
private _oldNoTrackBy:HTMLElement[];
|
||||
private _oldWithTrackBy:HTMLElement[];
|
||||
private _oldNoTrackBy: HTMLElement[];
|
||||
private _oldWithTrackBy: HTMLElement[];
|
||||
|
||||
heroesNoTrackByChangeCount = 0;
|
||||
heroesWithTrackByChangeCount = 0;
|
||||
|
@ -216,32 +221,32 @@ export class AppComponent implements AfterViewInit, OnInit {
|
|||
this._oldNoTrackBy = toArray(this.childrenNoTrackBy);
|
||||
this._oldWithTrackBy = toArray(this.childrenWithTrackBy);
|
||||
|
||||
this.childrenNoTrackBy.changes.subscribe((changes:any) => {
|
||||
this.childrenNoTrackBy.changes.subscribe((changes: any) => {
|
||||
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) {
|
||||
this._oldNoTrackBy = newNoTrackBy;
|
||||
this.heroesNoTrackByChangeCount++;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
this.childrenWithTrackBy.changes.subscribe((changes:any) => {
|
||||
this.childrenWithTrackBy.changes.subscribe((changes: any) => {
|
||||
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) {
|
||||
this._oldWithTrackBy = newWithTrackBy;
|
||||
this.heroesWithTrackByChangeCount++;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
///////////////////
|
||||
|
||||
}
|
||||
|
||||
// helper to convert viewChildren to an array of HTMLElements
|
||||
function toArray(viewChildren:QueryList<ElementRef>) {
|
||||
function toArray(viewChildren: QueryList<ElementRef>) {
|
||||
let result: HTMLElement[] = [];
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Tutorial', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -23,19 +24,19 @@ describe('Tutorial', 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.myDashboardHref.getText()).toEqual("Dashboard");
|
||||
expect(page.myHeroesHref.getText()).toEqual("Heroes");
|
||||
});
|
||||
|
||||
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");
|
||||
});
|
||||
|
||||
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');
|
||||
page.myHeroesHref.click().then(function() {
|
||||
|
@ -49,11 +50,11 @@ describe('Tutorial', 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');
|
||||
var heroEle = page.topHeroes.get(3);
|
||||
var heroDescrEle = heroEle.element(by.css('h4'));
|
||||
var heroDescr;
|
||||
let heroEle = page.topHeroes.get(3);
|
||||
let heroDescrEle = heroEle.element(by.css('h4'));
|
||||
let heroDescr;
|
||||
return heroDescrEle.getText().then(function(text) {
|
||||
heroDescr = text;
|
||||
return heroEle.click();
|
||||
|
@ -66,10 +67,10 @@ describe('Tutorial', 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');
|
||||
var viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details'));
|
||||
var heroEle, heroDescr;
|
||||
let viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details'));
|
||||
let heroEle, heroDescr;
|
||||
page.myHeroesHref.click().then(function() {
|
||||
expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT 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.myHeroesParent.isPresent()).toBe(false, 'myHeroes element should NOT be present');
|
||||
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');
|
||||
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');
|
||||
var detailTextEle = page.heroDetail.element(by.css('div h2'));
|
||||
let detailTextEle = page.heroDetail.element(by.css('div h2'));
|
||||
expect(detailTextEle.getText()).toContain(origValue);
|
||||
return sendKeys(inputEle, textToAdd).then(function () {
|
||||
expect(detailTextEle.getText()).toContain(origValue + textToAdd);
|
|
@ -19,9 +19,8 @@ export class HeroService {
|
|||
|
||||
//#docregion get-hero
|
||||
getHero(id: number) {
|
||||
return Promise.resolve(HEROES).then(
|
||||
heroes => heroes.filter(hero => hero.id === id)[0]
|
||||
);
|
||||
return this.getHeroes()
|
||||
.then(heroes => heroes.filter(hero => hero.id === id)[0]);
|
||||
}
|
||||
//#enddocregion get-hero
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path='../_protractor/e2e.d.ts' />
|
||||
describe('TOH Http Chapter', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
|
@ -5,7 +6,7 @@ describe('TOH Http Chapter', function () {
|
|||
});
|
||||
|
||||
function getPageStruct() {
|
||||
hrefEles = element.all(by.css('my-app a'));
|
||||
let hrefEles = element.all(by.css('my-app a'));
|
||||
|
||||
return {
|
||||
hrefs: hrefEles,
|
||||
|
@ -22,12 +23,12 @@ describe('TOH Http Chapter', function () {
|
|||
addButton: element.all(by.buttonText('Add New Hero')).get(0),
|
||||
|
||||
heroDetail: element(by.css('my-app my-hero-detail'))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
it('should be able to add a hero from the "Heroes" view', function(){
|
||||
var page = getPageStruct();
|
||||
var heroCount;
|
||||
let page = getPageStruct();
|
||||
let heroCount: webdriver.promise.Promise<number>;
|
||||
|
||||
page.myHeroesHref.click().then(function() {
|
||||
browser.waitForAngular();
|
||||
|
@ -43,14 +44,14 @@ describe('TOH Http Chapter', function () {
|
|||
heroCount = page.allHeroes.count();
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to delete hero from "Heroes" view', function(){
|
||||
var page = getPageStruct();
|
||||
var heroCount;
|
||||
let page = getPageStruct();
|
||||
let heroCount: webdriver.promise.Promise<number>;
|
||||
|
||||
page.myHeroesHref.click().then(function() {
|
||||
browser.waitForAngular();
|
||||
|
@ -66,11 +67,11 @@ describe('TOH Http Chapter', 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');
|
||||
var heroEle = page.topHeroes.get(2);
|
||||
var heroDescrEle = heroEle.element(by.css('h4'));
|
||||
var heroDescr;
|
||||
let heroEle = page.topHeroes.get(2);
|
||||
let heroDescrEle = heroEle.element(by.css('h4'));
|
||||
let heroDescr: string;
|
||||
|
||||
return heroDescrEle.getText().then(function(text) {
|
||||
heroDescr = text;
|
||||
|
@ -88,10 +89,10 @@ describe('TOH Http Chapter', 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'));
|
||||
var heroEle, heroDescr;
|
||||
let viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details'));
|
||||
let heroEle: protractor.ElementFinder, heroDescr: string;
|
||||
|
||||
page.myHeroesHref.click().then(function() {
|
||||
expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present');
|
||||
|
@ -101,7 +102,7 @@ describe('TOH Http Chapter', function () {
|
|||
return heroEle.getText();
|
||||
}).then(function(text) {
|
||||
// remove leading 'id' from the element
|
||||
heroDescr = text.substr(text.indexOf(' ')+1);
|
||||
heroDescr = text.substr(text.indexOf(' ') + 1);
|
||||
return heroEle.click();
|
||||
}).then(function() {
|
||||
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) {
|
||||
var inputEle = page.heroDetail.element(by.css('input'));
|
||||
function save(page: any, origValue: string, textToAdd: string) {
|
||||
let inputEle = page.heroDetail.element(by.css('input'));
|
||||
expect(inputEle.isDisplayed()).toBe(true, 'should be able to see the input box');
|
||||
var saveButtonEle = page.heroDetail.element(by.buttonText('Save'));
|
||||
var backButtonEle = page.heroDetail.element(by.buttonText('Back'));
|
||||
let saveButtonEle = page.heroDetail.element(by.buttonText('Save'));
|
||||
let backButtonEle = page.heroDetail.element(by.buttonText('Back'));
|
||||
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);
|
||||
return sendKeys(inputEle, textToAdd).then(function () {
|
||||
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 () {
|
||||
var page = getPageStruct();
|
||||
let page = getPageStruct();
|
||||
expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices');
|
||||
expect(page.myDashboardHref.getText()).toEqual("Dashboard");
|
||||
expect(page.myHeroesHref.getText()).toEqual("Heroes");
|
||||
expect(page.myDashboardHref.getText()).toEqual('Dashboard');
|
||||
expect(page.myHeroesHref.getText()).toEqual('Heroes');
|
||||
});
|
||||
|
||||
it('should be able to see dashboard choices', function () {
|
||||
var page = getPageStruct();
|
||||
expect(page.topHeroes.count()).toBe(4, "should be 4 dashboard hero choices");
|
||||
let page = getPageStruct();
|
||||
expect(page.topHeroes.count()).toBe(4, 'should be 4 dashboard hero choices');
|
||||
});
|
||||
|
||||
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');
|
||||
page.myHeroesHref.click().then(function() {
|
||||
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();
|
||||
}).then(function() {
|
||||
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 () {
|
||||
var page = getPageStruct();
|
||||
let page = getPageStruct();
|
||||
expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be available');
|
||||
var heroEle = page.topHeroes.get(3);
|
||||
var heroDescrEle = heroEle.element(by.css('h4'));
|
||||
var heroDescr;
|
||||
let heroEle = page.topHeroes.get(3);
|
||||
let heroDescrEle = heroEle.element(by.css('h4'));
|
||||
let heroDescr: string;
|
||||
return heroDescrEle.getText().then(function(text) {
|
||||
heroDescr = text;
|
||||
return heroEle.click();
|
||||
|
@ -175,10 +176,10 @@ describe('TOH Http Chapter', 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');
|
||||
var viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details'));
|
||||
var heroEle, heroDescr;
|
||||
let viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details'));
|
||||
let heroEle: protractor.ElementFinder, heroDescr: string;
|
||||
page.myHeroesHref.click().then(function() {
|
||||
expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT 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();
|
||||
}).then(function(text) {
|
||||
// remove leading 'id' from the element
|
||||
heroDescr = text.substr(text.indexOf(' ')+1);
|
||||
heroDescr = text.substr(text.indexOf(' ') + 1);
|
||||
return heroEle.click();
|
||||
}).then(function() {
|
||||
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.myHeroesParent.isPresent()).toBe(false, 'myHeroes element should NOT be present');
|
||||
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');
|
||||
var buttons = page.heroDetail.all(by.css('button'));
|
||||
var backButtonEle = buttons.get(0);
|
||||
var saveButtonEle = buttons.get(1);
|
||||
let buttons = page.heroDetail.all(by.css('button'));
|
||||
let backButtonEle = buttons.get(0);
|
||||
let saveButtonEle = buttons.get(1);
|
||||
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');
|
||||
var detailTextEle = page.heroDetail.element(by.css('div h2'));
|
||||
let detailTextEle = page.heroDetail.element(by.css('div h2'));
|
||||
expect(detailTextEle.getText()).toContain(origValue);
|
||||
return sendKeys(inputEle, textToAdd).then(function () {
|
||||
expect(detailTextEle.getText()).toContain(origValue + textToAdd);
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('Upgrade Tests', function () {
|
||||
|
||||
// Protractor doesn't support the UpgradeAdapter's asynchronous
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
'use strict';
|
||||
|
||||
// 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() {
|
||||
var phoneList = element.all(by.repeater('phone in $ctrl.phones'));
|
||||
var query = element(by.model('$ctrl.query'));
|
||||
let phoneList = element.all(by.repeater('phone in $ctrl.phones'));
|
||||
let query = element(by.model('$ctrl.query'));
|
||||
|
||||
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() {
|
||||
var queryField = element(by.model('$ctrl.query'));
|
||||
var orderSelect = element(by.model('$ctrl.orderProp'));
|
||||
var nameOption = orderSelect.element(by.css('option[value="name"]'));
|
||||
var phoneNameColumn = element.all(by.repeater('phone in $ctrl.phones').column('phone.name'));
|
||||
let queryField = element(by.model('$ctrl.query'));
|
||||
let orderSelect = element(by.model('$ctrl.orderProp'));
|
||||
let nameOption = orderSelect.element(by.css('option[value="name"]'));
|
||||
let phoneNameColumn = element.all(by.repeater('phone in $ctrl.phones').column('phone.name'));
|
||||
|
||||
function getNames() {
|
||||
return phoneNameColumn.map(function(elem) {
|
||||
|
@ -63,7 +64,7 @@ describe('PhoneCat Application', 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');
|
||||
|
||||
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() {
|
||||
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/);
|
||||
});
|
||||
|
||||
it('should swap the main image when clicking on a thumbnail image', function() {
|
||||
var mainImage = element(by.css('img.phone.selected'));
|
||||
var thumbnails = element.all(by.css('.phone-thumbs img'));
|
||||
let mainImage = element(by.css('img.phone.selected'));
|
||||
let thumbnails = element.all(by.css('.phone-thumbs img'));
|
||||
|
||||
thumbnails.get(2).click();
|
||||
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/typings/index.d.ts" />
|
||||
'use strict';
|
||||
|
||||
// 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() {
|
||||
var phoneList = element.all(by.css('.phones li'));
|
||||
var query = element(by.css('input'));
|
||||
let phoneList = element.all(by.css('.phones li'));
|
||||
let query = element(by.css('input'));
|
||||
|
||||
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() {
|
||||
var queryField = element(by.css('input'));
|
||||
var orderSelect = element(by.css('select'));
|
||||
var nameOption = orderSelect.element(by.css('option[value="name"]'));
|
||||
var phoneNameColumn = element.all(by.css('.phones .name'));
|
||||
let queryField = element(by.css('input'));
|
||||
let orderSelect = element(by.css('select'));
|
||||
let nameOption = orderSelect.element(by.css('option[value="name"]'));
|
||||
let phoneNameColumn = element.all(by.css('.phones .name'));
|
||||
|
||||
function getNames() {
|
||||
return phoneNameColumn.map(function(elem) {
|
||||
|
@ -62,7 +63,7 @@ describe('PhoneCat Application', function() {
|
|||
});
|
||||
|
||||
it('should render phone specific links', function() {
|
||||
var query = element(by.css('input'));
|
||||
let query = element(by.css('input'));
|
||||
sendKeys(query, 'nexus');
|
||||
|
||||
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() {
|
||||
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/);
|
||||
});
|
||||
|
||||
it('should swap the main image when clicking on a thumbnail image', function() {
|
||||
var mainImage = element(by.css('img.phone.selected'));
|
||||
var thumbnails = element.all(by.css('.phone-thumbs img'));
|
||||
let mainImage = element(by.css('img.phone.selected'));
|
||||
let thumbnails = element.all(by.css('.phone-thumbs img'));
|
||||
|
||||
thumbnails.get(2).click();
|
||||
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
'use strict';
|
||||
|
||||
// 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() {
|
||||
var phoneList = element.all(by.css('.phones li'));
|
||||
var query = element(by.css('input'));
|
||||
let phoneList = element.all(by.css('.phones li'));
|
||||
let query = element(by.css('input'));
|
||||
|
||||
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() {
|
||||
var queryField = element(by.css('input'));
|
||||
var orderSelect = element(by.css('select'));
|
||||
var nameOption = orderSelect.element(by.css('option[value="name"]'));
|
||||
var phoneNameColumn = element.all(by.css('.phones .name'));
|
||||
let queryField = element(by.css('input'));
|
||||
let orderSelect = element(by.css('select'));
|
||||
let nameOption = orderSelect.element(by.css('option[value="name"]'));
|
||||
let phoneNameColumn = element.all(by.css('.phones .name'));
|
||||
|
||||
function getNames() {
|
||||
return phoneNameColumn.map(function(elem) {
|
||||
|
@ -64,10 +65,10 @@ describe('PhoneCat Application', function() {
|
|||
|
||||
// #docregion links
|
||||
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
|
||||
var str = 'nexus';
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
let str = 'nexus';
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
query.sendKeys(str.charAt(i));
|
||||
}
|
||||
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() {
|
||||
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/);
|
||||
});
|
||||
|
||||
it('should swap the main image when clicking on a thumbnail image', function() {
|
||||
var mainImage = element(by.css('img.phone.selected'));
|
||||
var thumbnails = element.all(by.css('.phone-thumbs img'));
|
||||
let mainImage = element(by.css('img.phone.selected'));
|
||||
let thumbnails = element.all(by.css('.phone-thumbs img'));
|
||||
|
||||
thumbnails.get(2).click();
|
||||
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../_protractor/e2e.d.ts" />
|
||||
describe('User Input Tests', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
|
@ -5,8 +6,8 @@ describe('User Input Tests', function () {
|
|||
});
|
||||
|
||||
it('should support the click event', function () {
|
||||
var mainEle = element(by.css('click-me'));
|
||||
var buttonEle =element(by.css('click-me button'));
|
||||
let mainEle = element(by.css('click-me'));
|
||||
let buttonEle =element(by.css('click-me button'));
|
||||
expect(mainEle.getText()).not.toContain('You are my hero!');
|
||||
buttonEle.click().then(function() {
|
||||
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 () {
|
||||
var mainEle = element(by.css('click-me2'));
|
||||
var buttonEle =element(by.css('click-me2 button'));
|
||||
let mainEle = element(by.css('click-me2'));
|
||||
let buttonEle =element(by.css('click-me2 button'));
|
||||
expect(mainEle.getText()).not.toContain('Event target is ');
|
||||
buttonEle.click().then(function() {
|
||||
expect(mainEle.getText()).toContain('Event target is BUTTON');
|
||||
|
@ -23,19 +24,19 @@ describe('User Input Tests', function () {
|
|||
});
|
||||
|
||||
it('should support the keyup event ', function () {
|
||||
var mainEle = element(by.css('key-up1'));
|
||||
var inputEle = mainEle.element(by.css('input'));
|
||||
var outputTextEle = mainEle.element(by.css('p'));
|
||||
let mainEle = element(by.css('key-up1'));
|
||||
let inputEle = mainEle.element(by.css('input'));
|
||||
let outputTextEle = mainEle.element(by.css('p'));
|
||||
expect(outputTextEle.getText()).toEqual('');
|
||||
return sendKeys(inputEle,'abc').then(function() {
|
||||
expect(outputTextEle.getText()).toEqual('a | ab | abc |');
|
||||
});
|
||||
});
|
||||
|
||||
it('should support user input from a local template var (loopback)', function () {
|
||||
var mainEle = element(by.css('loop-back'));
|
||||
var inputEle = mainEle.element(by.css('input'));
|
||||
var outputTextEle = mainEle.element(by.css('p'));
|
||||
it('should support user input from a local template let (loopback)', function () {
|
||||
let mainEle = element(by.css('loop-back'));
|
||||
let inputEle = mainEle.element(by.css('input'));
|
||||
let outputTextEle = mainEle.element(by.css('p'));
|
||||
expect(outputTextEle.getText()).toEqual('');
|
||||
return sendKeys(inputEle,'abc').then(function() {
|
||||
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 () {
|
||||
var mainEle = element(by.css('key-up2'));
|
||||
var inputEle = mainEle.element(by.css('input'));
|
||||
var outputTextEle = mainEle.element(by.css('p'));
|
||||
let mainEle = element(by.css('key-up2'));
|
||||
let inputEle = mainEle.element(by.css('input'));
|
||||
let outputTextEle = mainEle.element(by.css('p'));
|
||||
expect(outputTextEle.getText()).toEqual('');
|
||||
return sendKeys(inputEle,'abc').then(function() {
|
||||
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 () {
|
||||
var mainEle = element(by.css('key-up3'));
|
||||
var inputEle = mainEle.element(by.css('input'));
|
||||
var outputTextEle = mainEle.element(by.css('p'));
|
||||
let mainEle = element(by.css('key-up3'));
|
||||
let inputEle = mainEle.element(by.css('input'));
|
||||
let outputTextEle = mainEle.element(by.css('p'));
|
||||
expect(outputTextEle.getText()).toEqual('');
|
||||
return sendKeys(inputEle,'abc').then(function() {
|
||||
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 () {
|
||||
var prevInputEle = element(by.css('key-up3 input'));
|
||||
var mainEle = element(by.css('key-up4'));
|
||||
var inputEle = mainEle.element(by.css('input'));
|
||||
var outputTextEle = mainEle.element(by.css('p'));
|
||||
let prevInputEle = element(by.css('key-up3 input'));
|
||||
let mainEle = element(by.css('key-up4'));
|
||||
let inputEle = mainEle.element(by.css('input'));
|
||||
let outputTextEle = mainEle.element(by.css('p'));
|
||||
expect(outputTextEle.getText()).toEqual('');
|
||||
return sendKeys(inputEle,'abc').then(function() {
|
||||
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 () {
|
||||
var mainEle = element(by.css('little-tour'));
|
||||
var inputEle = mainEle.element(by.css('input'));
|
||||
var addButtonEle = mainEle.element(by.css('button'));
|
||||
var heroEles = mainEle.all(by.css('li'));
|
||||
var numHeroes;
|
||||
let mainEle = element(by.css('little-tour'));
|
||||
let inputEle = mainEle.element(by.css('input'));
|
||||
let addButtonEle = mainEle.element(by.css('button'));
|
||||
let heroEles = mainEle.all(by.css('li'));
|
||||
let numHeroes;
|
||||
expect(heroEles.count()).toBeGreaterThan(0);
|
||||
heroEles.count().then(function(count) {
|
||||
numHeroes = count;
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
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.
|
||||
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
|
||||
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.
|
||||
Etsy artisan Thundercats, authentic sustainable bitters
|
||||
|
|
|
@ -12,7 +12,7 @@ include ../../../_includes/_util-fns
|
|||
:marked
|
||||
### 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
|
||||
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
|
||||
`#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
|
||||
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.
|
||||
|
||||
code-example(format="linenums" language="js" escape="html").
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
header.showcase-header
|
||||
h2 Basic Layouts
|
||||
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.
|
||||
|
||||
.showcase-content
|
||||
|
@ -23,4 +23,4 @@
|
|||
code-example(language="html" format="linenums").
|
||||
.l-sub-section
|
||||
h3 Sub Section Title
|
||||
p sub section content...
|
||||
p sub section content...
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
header.showcase-header
|
||||
h2 Tables
|
||||
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.
|
||||
|
||||
.showcase-content
|
||||
|
@ -37,4 +37,4 @@
|
|||
tr
|
||||
td Angular 1.3
|
||||
td Routing
|
||||
td fast
|
||||
td fast
|
||||
|
|
|
@ -5,16 +5,20 @@ include ../../../_includes/_util-fns
|
|||
- var _decorator = 'annotation';
|
||||
- var _Array = 'List';
|
||||
- var _array = 'list';
|
||||
- var _an_array = 'a list'; //- Deprecate now that we have the articles
|
||||
- var _a = 'an';
|
||||
- var _an = 'a';
|
||||
- var _priv = '_';
|
||||
- var _Lang = 'Dart';
|
||||
- var _Promise = 'Future';
|
||||
- var _FutureUrl = 'https://api.dartlang.org/dart_async/Future.html';
|
||||
- var _PromiseLinked = '<a href="' + _FutureUrl + '">' + _Promise + '</a>';
|
||||
- var _Observable = 'Stream';
|
||||
- var _liveLink = 'sample repo';
|
||||
- var _truthy = 'true';
|
||||
- var _falsey = 'false';
|
||||
- var _appDir = 'lib';
|
||||
- var _indexHtmlDir = 'web';
|
||||
- var _mainDir = 'web';
|
||||
|
||||
mixin liveExampleLink(linkText, exampleUrlPartName)
|
||||
- var text = linkText || '在线例子';
|
||||
|
@ -36,7 +40,7 @@ mixin liveExampleLink2(linkText, exampleUrlPartName)
|
|||
- // if(extn == 'dart') return path;
|
||||
- var baseName = getBaseFileName(path) || path; // TODO: have getBaseFileName() return path
|
||||
- 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
|
||||
- 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
|
||||
|
|
|
@ -6,8 +6,6 @@ block includes
|
|||
- var _prereq = 'the Dart SDK'
|
||||
- var _angular_browser_uri = 'package:angular2/platform/browser.dart'
|
||||
- var _angular_core_uri = 'package:angular2/core.dart'
|
||||
- var _appDir = 'lib'
|
||||
- var _indexHtmlDir = 'web'
|
||||
|
||||
block setup-tooling
|
||||
:marked
|
||||
|
|
|
@ -29,5 +29,10 @@
|
|||
"title": "Routing",
|
||||
"intro": "We add the Angular Component Router and learn to navigate among the views",
|
||||
"nextable": true
|
||||
},
|
||||
"toh-pt6": {
|
||||
"title": "Http",
|
||||
"intro": "We convert our service and components to use Http",
|
||||
"nextable": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ code-example(format=".").
|
|||
.l-sub-section
|
||||
:marked
|
||||
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.
|
||||
:marked
|
||||
There are a couple of ways we can declare that `hero` is an *input*.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
!= partial("../../../_includes/_ts-temp")
|
|
@ -489,7 +489,7 @@ figure.image-display
|
|||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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
|
||||
wire the button's enable/disabled state to the form's validity without Angular's help?
|
||||
|
|
|
@ -448,7 +448,7 @@ code-example(format="").
|
|||
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.
|
||||
|
||||
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 wish to render the first page of our application on the server
|
||||
to improve launch performance or facilitate
|
||||
|
|
|
@ -29,5 +29,10 @@
|
|||
"title": "Routing",
|
||||
"intro": "We add the Angular Component Router and learn to navigate among the views",
|
||||
"nextable": true
|
||||
},
|
||||
"toh-pt6": {
|
||||
"title": "Http",
|
||||
"intro": "We convert our service and components to use Http",
|
||||
"nextable": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
!= partial("../../../_includes/_ts-temp")
|
|
@ -129,7 +129,7 @@ figure.image-display
|
|||
我们通常会推荐在应用程序的根组件`AppComponent`中提供应用程序级的服务。
|
||||
|
||||
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运行环境而保留的。
|
||||
|
||||
|
|
|
@ -253,25 +253,25 @@ include _util-fns
|
|||
|
||||
The many forms of binding include:
|
||||
绑定形式包括:
|
||||
* [Interpolation](guide/template-syntax.html#interpolation)
|
||||
* [插值表达式(Interpolation)](guide/template-syntax.html#interpolation)
|
||||
* [Property Binding](guide/template-syntax.html#property-binding)
|
||||
* [属性绑定(Property Binding)](guide/template-syntax.html#property-binding)
|
||||
* [Event Binding](guide/template-syntax.html#event-binding)
|
||||
* [事件绑定(Event Binding)](guide/template-syntax.html#event-binding)
|
||||
* [Attribute Binding](guide/template-syntax.html#attribute-binding)
|
||||
* [Attribute绑定(Attribute Binding)](guide/template-syntax.html#attribute-binding)
|
||||
* [Class Binding](guide/template-syntax.html#class-binding)
|
||||
* [类绑定(Class Binding)](guide/template-syntax.html#class-binding)
|
||||
* [Style Binding](guide/template-syntax.html#style-binding)
|
||||
* [样式绑定(Style Binding)](guide/template-syntax.html#style-binding)
|
||||
* [Two-way data binding with ngModel](guide/template-syntax.html#ng-model)
|
||||
* [基于ngModel的双向数据绑定(Two-way data binding with ngModel)](guide/template-syntax.html#ng-model)
|
||||
* [Interpolation](/docs/ts/latest/guide/template-syntax.html#interpolation)
|
||||
* [插值表达式(Interpolation)](/docs/ts/latest/guide/template-syntax.html#interpolation)
|
||||
* [Property Binding](/docs/ts/latest/guide/template-syntax.html#property-binding)
|
||||
* [属性绑定(Property Binding)](/docs/ts/latest/guide/template-syntax.html#property-binding)
|
||||
* [Event Binding](/docs/ts/latest/guide/template-syntax.html#event-binding)
|
||||
* [事件绑定(Event Binding)](/docs/ts/latest/guide/template-syntax.html#event-binding)
|
||||
* [Attribute Binding](/docs/ts/latest/guide/template-syntax.html#attribute-binding)
|
||||
* [Attribute绑定(Attribute Binding)](/docs/ts/latest/guide/template-syntax.html#attribute-binding)
|
||||
* [Class Binding](/docs/ts/latest/guide/template-syntax.html#class-binding)
|
||||
* [类绑定(Class Binding)](/docs/ts/latest/guide/template-syntax.html#class-binding)
|
||||
* [Style Binding](/docs/ts/latest/guide/template-syntax.html#style-binding)
|
||||
* [样式绑定(Style Binding)](/docs/ts/latest/guide/template-syntax.html#style-binding)
|
||||
* [Two-way data binding with ngModel](/docs/ts/latest/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
|
||||
[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
|
||||
<a id="decorator"></a> <a id="decoration"></a>
|
||||
|
@ -407,7 +407,7 @@ include _util-fns
|
|||
Angular会为每个注册器注册很多自己的内建Provider。我们也可以注册自己的Provider。通常注册Provider的最佳时间是在应用程序开始[引导](#bootstrap)的时候。
|
||||
当然,我们也有其它很多机会注册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)一章。
|
||||
|
||||
|
@ -494,7 +494,7 @@ include _util-fns
|
|||
## ECMAScript 2015
|
||||
.l-sub-section
|
||||
:marked
|
||||
The lastest released version of JavaScript,
|
||||
The latest released version of JavaScript,
|
||||
[ECMAScript 2015](http://www.ecma-international.org/ecma-262/6.0/)
|
||||
(AKA "ES2015" or "ES6")
|
||||
|
||||
|
@ -545,15 +545,15 @@ include _util-fns
|
|||
.l-sub-section
|
||||
:marked
|
||||
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
|
||||
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
|
||||
## Interpolation
|
||||
|
@ -573,9 +573,9 @@ include _util-fns
|
|||
|
||||
:marked
|
||||
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>
|
||||
|
||||
|
@ -641,9 +641,9 @@ include _util-fns
|
|||
* `ngOnDestroy` - just before the directive is destroyed.
|
||||
* `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
|
||||
|
||||
// #docregion m1
|
||||
|
@ -691,7 +691,7 @@ include _util-fns
|
|||
应用程序开发者可以自己选择任何与这个标准兼容的模块化库。
|
||||
|
||||
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`.
|
||||
|
||||
模块一般与它用于导出东西的文件同名。比如, 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
|
||||
:marked
|
||||
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
|
||||
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
|
||||
<a id="P"></a>
|
||||
|
@ -768,9 +768,9 @@ include _util-fns
|
|||
code-example(language="html" escape="html").
|
||||
<label>Price: </label>{{product.price | currency}}
|
||||
: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
|
||||
## Provider
|
||||
|
@ -807,7 +807,7 @@ include _util-fns
|
|||
|
||||
大部分应用程序包含多个屏或视图[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
|
||||
of views.
|
||||
|
||||
|
@ -885,9 +885,9 @@ include _util-fns
|
|||
|
||||
模板是一块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
|
||||
## Template Expression
|
||||
|
@ -896,7 +896,7 @@ include _util-fns
|
|||
:marked
|
||||
An expression in a JavaScript-like syntax that Angular evaluates within
|
||||
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)章节了解更多模板表达式的知识。
|
||||
// #enddocregion t1
|
||||
|
@ -987,8 +987,7 @@ include _util-fns
|
|||
|
||||
The browser DOM and JavaScript have a limited number
|
||||
of asynchronous activities, activities such as DOM events (e.g., clicks),
|
||||
[promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/
|
||||
Promise), and
|
||||
[promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), and
|
||||
[XHR](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
|
||||
calls to remote servers.
|
||||
|
||||
|
|
|
@ -519,20 +519,20 @@ figure
|
|||
在[范例](#template)模板中,我们看到了数据绑定的三种形式:
|
||||
+makeExample('architecture/ts/app/hero-list.component.1.html', 'binding', 'app/hero-list.component.html (节选)')(format=".")
|
||||
: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.
|
||||
|
||||
* `{{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`.
|
||||
|
||||
* `[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
|
||||
|
||||
* `(click)`[事件绑定](user-input.html#click):当用户点击英雄的名字时,调用组件的`selectHero`方法。
|
||||
* `(click)`[*事件绑定*](user-input.html#click):当用户点击英雄的名字时,调用组件的`selectHero`方法。
|
||||
|
||||
* **Two-way data binding** is an important fourth form
|
||||
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`。
|
||||
|
||||
>**[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.
|
||||
|
||||
>**[Testing](../testing/index.html)** - Angular提供了一个测试库,在程序各个部分与Angular框架交互同时,用来“单元测试”它们。
|
||||
>**[Testing](testing.html)** - Angular提供了一个测试库,在程序各个部分与Angular框架交互同时,用来“单元测试”它们。
|
||||
|
|
|
@ -49,7 +49,7 @@ p 运行本章这些代码的#[+liveExampleLink2()]
|
|||
只要需要,我们就可以指定任何选择器、规则和媒体查询。
|
||||
|
||||
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:
|
||||
|
||||
它的实现方式之一,是在组件的元数据中设置`styles`属性。
|
||||
|
|
|
@ -60,7 +60,7 @@ include ../_util-fns
|
|||
We didn't care about `Engine` constructor parameters when we first wrote `Car`.
|
||||
We don't really care about them now.
|
||||
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.
|
||||
|
||||
如果`Engine`类升级了,并且它的构造函数要求传入一个参数了,该怎么办?
|
||||
|
|
|
@ -1066,9 +1066,10 @@ figure.image-display
|
|||
重新运行应用。表单打开时,状态是有效的,按钮是可用的。
|
||||
|
||||
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
|
||||
wire the button's enable/disabled state to the form's validity without Angular's help?
|
||||
|
|
|
@ -93,8 +93,8 @@ a(id="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)*** - 填充包弥合了不同浏览器上的JavaScript实现方面的差异。
|
||||
|
@ -290,8 +290,7 @@ a(id="why-peer-dependencies")
|
|||
|
||||
We don't have a *peerDependencies* section in the QuickStart `package.json`.
|
||||
But Angular itself has a *peerDependencies* section in
|
||||
[*its* package.json](https://github.com/angular/angular/blob/master/modules/angular2/package.json)
|
||||
and that has important consequences for our application.
|
||||
*its* package.json and that has important consequences for our application.
|
||||
|
||||
在“快速起步”的`package.json`文件中,并没有*peerDependencies*区。
|
||||
但是Angular本身在[*它自己的* package.json](https://github.com/angular/angular/blob/master/modules/angular2/package.json)中有,
|
||||
|
|
|
@ -969,9 +969,11 @@ a(id="one-time-initialization")
|
|||
|
||||
### 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=".")
|
||||
:marked
|
||||
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
|
||||
:marked
|
||||
<a id="other-bindings"></a>
|
||||
|
|
|
@ -368,7 +368,7 @@ a(id="common-configuration")
|
|||
但是大多数`import`语句完全不会去引用扩展名。
|
||||
所以我们要告诉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`扩展名(为了查找标准的JavaScript文件和预编译过的TypeScript文件),或者
|
||||
|
@ -512,7 +512,7 @@ a(id="development-configuration")
|
|||
我们这些CSS默认情况下被埋没在JavaScript包儿中。`ExtractTextPlugin`会把它们提取成外部`.css`文件,
|
||||
这样`HtmlWebpackPlugin`插件就会转而把一个<link>标签写进`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文档。
|
||||
|
||||
|
|
|
@ -5,9 +5,6 @@ block includes
|
|||
- var _prereq = 'Node.js'
|
||||
- var _angular_browser_uri = '@angular/platform-browser-dynamic'
|
||||
- var _angular_core_uri = '@angular/core'
|
||||
- var _appDir = 'app'
|
||||
- var _indexHtmlDir = 'project root'
|
||||
- var _indexHtmlDirCn = '项目的根'
|
||||
|
||||
:marked
|
||||
Our QuickStart goal is to build and run a super-simple
|
||||
|
|
|
@ -468,11 +468,11 @@ code-example(format="." language="bash").
|
|||
:marked
|
||||
We specify the path _all the way back to the application root_ — `app/` in this case —
|
||||
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/`)的_。
|
||||
因为Angular_默认_不支持使用相对于当前模块的路径。
|
||||
只要喜欢,我们也_可以_切换成[相对于组件的路径](../cookbook/component-relative-paths)模式。
|
||||
只要喜欢,我们也_可以_切换成[相对于组件的路径](../cookbook/component-relative-paths.html)模式。
|
||||
:marked
|
||||
Create that file with these contents:
|
||||
|
||||
|
@ -719,9 +719,10 @@ code-example(format='').
|
|||
|
||||
这段代码的问题在于`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=".")
|
||||
:marked
|
||||
Return to the `HeroDetailComponent` to clean up loose ends.
|
||||
|
|
|
@ -2,15 +2,15 @@ include ../_util-fns
|
|||
|
||||
:marked
|
||||
# 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,
|
||||
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.
|
||||
: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
|
||||
:marked
|
||||
## Where We Left Off
|
||||
|
@ -27,25 +27,25 @@ code-example(format="." language="bash").
|
|||
:marked
|
||||
The application runs and updates automatically as we continue to build the Tour of Heroes.
|
||||
|
||||
.l-main-section
|
||||
.l-main-section
|
||||
:marked
|
||||
## 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`,
|
||||
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.
|
||||
|
||||
:marked
|
||||
### Register (provide) *http* 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.
|
||||
|
||||
|
||||
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
|
||||
launch the application and its root `AppComponent`.
|
||||
|
||||
|
||||
+makeExample('toh-6/ts/app/main.ts','v1','app/main.ts (v1)')(format='.')
|
||||
:marked
|
||||
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*.
|
||||
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.
|
||||
Until we do, *we'll have to fake it*.
|
||||
|
||||
|
||||
We're going to *trick* the http client into fetching and saving data from
|
||||
a demo/development service, the *in-memory web api*.
|
||||
|
||||
|
||||
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`.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
:marked
|
||||
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).
|
||||
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.
|
||||
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Heroes and Http
|
||||
|
||||
|
||||
Look at our current `HeroService` implementation
|
||||
+makeExample('toh-4/ts/app/hero.service.ts', 'get-heroes', 'app/hero.service.ts (getHeroes - old)')(format=".")
|
||||
: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
|
||||
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:
|
||||
|
||||
|
||||
+makeExample('toh-6/ts/app/hero.service.ts', 'get-heroes', 'app/hero.service.ts (getHeroes using Http)')(format=".")
|
||||
|
||||
|
||||
:marked
|
||||
### Http Promise
|
||||
|
||||
We're still returning a promise but we're creating it differently.
|
||||
|
||||
|
||||
The Angular `http.get` returns an RxJS `Observable`.
|
||||
*Observables* are a powerful way to manage asynchronous data flows.
|
||||
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.
|
||||
+makeExample('toh-6/ts/app/hero.service.ts', 'to-promise')(format=".")
|
||||
:marked
|
||||
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.
|
||||
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:
|
||||
+makeExample('toh-6/ts/app/hero.service.ts', 'rxjs')(format=".")
|
||||
|
||||
: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
|
||||
data within the response.
|
||||
+makeExample('toh-6/ts/app/hero.service.ts', 'to-data')(format=".")
|
||||
: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.
|
||||
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.
|
||||
This particular *in-memory web api* example happens to return an object with a `data` property.
|
||||
Your api might return something else.
|
||||
|
||||
Adjust the code to match *your web api*.
|
||||
|
||||
Adjust the code to match *your web api*.
|
||||
: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 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`.
|
||||
:marked
|
||||
### Error Handling
|
||||
|
||||
|
||||
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=".")
|
||||
: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.
|
||||
|
||||
+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
|
||||
to the caller in a rejected promise so that the caller can display a proper error message to the user.
|
||||
|
||||
|
||||
### 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()`.
|
||||
|
||||
.l-main-section
|
||||
.l-main-section
|
||||
:marked
|
||||
## 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.
|
||||
|
||||
We'll complete `HeroService` by creating `post`, `put` and `delete` http calls to meet our new requirements.
|
||||
|
||||
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.
|
||||
|
||||
:marked
|
||||
### 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:
|
||||
|
||||
|
||||
+makeExample('toh-6/ts/app/hero.service.ts', 'post-hero', 'app/hero.service.ts (post hero)')(format=".")
|
||||
|
||||
: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` 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=".")
|
||||
|
||||
:marked
|
||||
### 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=".")
|
||||
|
||||
:marked
|
||||
We add a `catch` to handle our errors for all three cases.
|
||||
|
||||
|
||||
:marked
|
||||
### 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.
|
||||
|
||||
+makeExample('toh-6/ts/app/hero.service.ts', 'save', 'app/hero.service.ts (save hero)')(format=".")
|
||||
|
||||
|
||||
:marked
|
||||
After these additions our `HeroService` looks like this:
|
||||
|
||||
|
@ -213,27 +213,30 @@ code-example(format="." language="bash").
|
|||
.l-main-section
|
||||
:marked
|
||||
## 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.
|
||||
|
||||
|
||||
### 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=".")
|
||||
|
||||
: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.
|
||||
|
||||
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=".")
|
||||
|
||||
: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.
|
||||
|
||||
|
||||
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=".")
|
||||
|
||||
:marked
|
||||
|
@ -241,58 +244,58 @@ code-example(format="." language="bash").
|
|||
|
||||
.l-sub-section
|
||||
: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
|
||||
Here is `HeroDetailComponent` with its new save button.
|
||||
|
||||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/toh/hero-details-save-button.png' alt="Hero Details With Save Button")
|
||||
|
||||
:marked
|
||||
### Add/Delete in the *HeroesComponent*
|
||||
|
||||
|
||||
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`.
|
||||
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.
|
||||
|
||||
|
||||
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=".")
|
||||
:marked
|
||||
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:
|
||||
+makeExample('toh-6/ts/app/heroes.component.html', 'delete-hero', 'app/heroes.component.html (delete)')(format=".")
|
||||
|
||||
:marked
|
||||
Now let's fix-up the `HeroesComponent` to support the *add* and *delete* actions in the template.
|
||||
Let's start with *add*.
|
||||
|
||||
|
||||
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.
|
||||
+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
|
||||
:marked
|
||||
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.
|
||||
|
||||
|
||||
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.
|
||||
:marked
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
The *delete* logic is a bit trickier.
|
||||
+makeExample('toh-6/ts/app/heroes.component.ts', 'delete', 'app/heroes.component.ts (delete)')(format=".")
|
||||
|
||||
: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.
|
||||
So the *delete* method removes the deleted hero from the list.
|
||||
|
||||
|
@ -300,12 +303,12 @@ figure.image-display
|
|||
### Let's see it
|
||||
Here are the fruits of labor in action:
|
||||
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
|
||||
### Review the App Structure
|
||||
Let’s verify that we have the following structure after all of our good refactoring in this chapter:
|
||||
|
||||
|
||||
.filetree
|
||||
.file angular2-tour-of-heroes
|
||||
.children
|
||||
|
@ -335,20 +338,20 @@ figure.image-display
|
|||
.file systemjs.config.json
|
||||
.file tsconfig.json
|
||||
.file typings.json
|
||||
|
||||
.l-main-section
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Home Stretch
|
||||
|
||||
|
||||
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 refactored HeroService to load heroes from an api.
|
||||
- We extended HeroService to support post, put and delete calls.
|
||||
- We updated our components to allow adding, editing and deleting of heroes.
|
||||
- We configured an in-memory web api.
|
||||
|
||||
|
||||
Below is a summary of the files we changed.
|
||||
|
||||
|
||||
+makeTabs(
|
||||
`toh-6/ts/app/app.component.ts,
|
||||
toh-6/ts/app/heroes.component.ts,
|
||||
|
@ -363,5 +366,4 @@ figure.image-display
|
|||
hero-detail.comp...ts,
|
||||
hero-detail.comp...html,
|
||||
hero.service.ts`
|
||||
)
|
||||
|
||||
)
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
|
@ -198,7 +198,7 @@ var createShredMapPackage = function(mapOptions) {
|
|||
.config(function(readFilesProcessor, extractPathsReader ) {
|
||||
readFilesProcessor.fileReaders = [ extractPathsReader];
|
||||
})
|
||||
// default configs - may be overriden
|
||||
// default configs - may be overridden
|
||||
.config(function(readFilesProcessor) {
|
||||
// Specify the base path used when resolving relative paths to source and output files
|
||||
readFilesProcessor.basePath = '/';
|
||||
|
|
Loading…
Reference in New Issue