",
+ "license": "MIT",
+ "devDependencies": {
+ "angular2-hot-loader": "0.0.17",
+ "async": "^1.4.2",
+ "browser-sync": "^2.11.1",
+ "browserify": "^13.0.0",
+ "chalk": "^1.1.1",
+ "connect": "^3.4.1",
+ "connect-livereload": "^0.5.3",
+ "del": "^2.2.0",
+ "event-stream": "^3.3.2",
+ "express": "~4.13.1",
+ "extend": "^3.0.0",
+ "gulp": "^3.9.0",
+ "gulp-concat": "^2.5.2",
+ "gulp-cssnano": "^2.0.0",
+ "gulp-filter": "^2.0.2",
+ "gulp-inject": "^1.3.1",
+ "gulp-inline-ng2-template": "^0.0.7",
+ "gulp-load-plugins": "^0.10.0",
+ "gulp-plumber": "~1.0.1",
+ "gulp-shell": "~0.4.3",
+ "gulp-sourcemaps": "~1.5.2",
+ "gulp-template": "^3.0.0",
+ "gulp-tslint": "^3.3.0",
+ "gulp-tslint-stylish": "^1.0.4",
+ "gulp-typedoc": "^1.2.1",
+ "gulp-typescript": "~2.8.2",
+ "gulp-uglify": "^1.2.0",
+ "gulp-util": "^3.0.7",
+ "gulp-watch": "^4.2.4",
+ "jasmine-core": "~2.3.4",
+ "jasmine-spec-reporter": "^2.4.0",
+ "karma": "~0.13.15",
+ "karma-chrome-launcher": "~0.2.0",
+ "karma-ie-launcher": "^0.2.0",
+ "karma-jasmine": "~0.3.6",
+ "karma-mocha-reporter": "^1.1.1",
+ "karma-phantomjs2-launcher": "^0.4.0",
+ "merge-stream": "^1.0.0",
+ "open": "0.0.5",
+ "protractor": "^3.0.0",
+ "rimraf": "^2.5.1",
+ "run-sequence": "^1.1.0",
+ "semver": "^5.0.3",
+ "serve-static": "^1.9.2",
+ "slash": "~1.0.0",
+ "stream-series": "^0.1.1",
+ "tiny-lr": "^0.2.1",
+ "traceur": "^0.0.91",
+ "ts-node": "^0.5.4",
+ "typedoc": "^0.3.12",
+ "typescript": "~1.7.3",
+ "typings": "^0.6.2",
+ "vinyl-buffer": "^1.0.0",
+ "vinyl-source-stream": "^1.1.0",
+ "yargs": "^3.32.0"
+ },
+ "dependencies": {
+ "angular2": "2.0.0-beta.2",
+ "bootstrap": "^3.3.5",
+ "es6-module-loader": "^0.17.8",
+ "es6-shim": "^0.33.3",
+ "reflect-metadata": "0.1.2",
+ "rxjs": "5.0.0-beta.0",
+ "systemjs": "~0.19.18",
+ "zone.js": "0.5.10"
+ }
+}
diff --git a/protractor.conf.js b/protractor.conf.js
new file mode 100644
index 0000000..78f5fe9
--- /dev/null
+++ b/protractor.conf.js
@@ -0,0 +1,43 @@
+exports.config = {
+ baseUrl: 'http://localhost:5555',
+
+ specs: [
+ 'dist/dev/**/*.e2e.js'
+ ],
+ exclude: [],
+
+ framework: 'jasmine2',
+
+ allScriptsTimeout: 110000,
+
+ jasmineNodeOpts: {
+ showTiming: true,
+ showColors: true,
+ isVerbose: false,
+ includeStackTrace: false,
+ defaultTimeoutInterval: 400000
+ },
+ directConnect: true,
+
+ capabilities: {
+ 'browserName': 'chrome'
+ },
+
+ onPrepare: function() {
+ var SpecReporter = require('jasmine-spec-reporter');
+ // add jasmine spec reporter
+ jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: true}));
+
+ browser.ignoreSynchronization = false;
+ },
+
+
+ /**
+ * Angular 2 configuration
+ *
+ * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching
+ * `rootEl`
+ *
+ */
+ useAllAngular2AppRoots: true
+};
diff --git a/src/about/components/about.e2e.ts b/src/about/components/about.e2e.ts
new file mode 100644
index 0000000..4b9bb20
--- /dev/null
+++ b/src/about/components/about.e2e.ts
@@ -0,0 +1,20 @@
+describe('About', function() {
+
+ beforeEach(function() {
+ browser.get('#/about');
+ });
+
+ it('should have an input', function() {
+ expect(element(by.css('app section about form input')).isPresent()).toEqual(true);
+ });
+
+ it('should have a list of computer scientists', function() {
+ expect(element(by.css('app section about ul')).getText()).toEqual('Dijkstra\nKnuth\nTuring\nHopper');
+ });
+
+ it('should add a name to the list using the form', function() {
+ element(by.css('app section about form input')).sendKeys('Tim Berners-Lee');
+ element(by.css('app section about form button')).click();
+ expect(element(by.css('app section about ul')).getText()).toEqual('Dijkstra\nKnuth\nTuring\nHopper\nTim Berners-Lee');
+ });
+});
diff --git a/src/about/components/about.html b/src/about/components/about.html
new file mode 100644
index 0000000..8c141b3
--- /dev/null
+++ b/src/about/components/about.html
@@ -0,0 +1,14 @@
+
+ For reward, here is a list of awesome computer scientists!
+
+
+
+ You want more? Add them yourself!
+
+
+
diff --git a/src/about/components/about.spec.ts b/src/about/components/about.spec.ts
new file mode 100644
index 0000000..a9aed45
--- /dev/null
+++ b/src/about/components/about.spec.ts
@@ -0,0 +1,51 @@
+import {
+ TestComponentBuilder,
+ describe,
+ expect,
+ injectAsync,
+ it
+} from 'angular2/testing';
+import {Component} from 'angular2/core';
+import {DOM} from 'angular2/src/platform/dom/dom_adapter';
+import {AboutCmp} from './about';
+import {NameList} from '../../shared/services/name_list';
+
+
+export function main() {
+ describe('About component', () => {
+ it('should work',
+ injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
+ return tcb.createAsync(TestComponent)
+ .then(rootTC => {
+ rootTC.detectChanges();
+
+ let aboutInstance = rootTC.debugElement.componentViewChildren[0].componentInstance;
+ let aboutDOMEl = rootTC.debugElement.componentViewChildren[0].nativeElement;
+ let nameListLen = function () {
+ return aboutInstance.list.names.length;
+ };
+
+ expect(aboutInstance.list).toEqual(jasmine.any(NameList));
+ expect(nameListLen()).toEqual(4);
+ expect(DOM.querySelectorAll(aboutDOMEl, 'li').length).toEqual(nameListLen());
+
+ aboutInstance.newName = 'Minko';
+ aboutInstance.addName();
+ rootTC.detectChanges();
+
+ expect(nameListLen()).toEqual(5);
+ expect(DOM.querySelectorAll(aboutDOMEl, 'li').length).toEqual(nameListLen());
+
+ expect(DOM.querySelectorAll(aboutDOMEl, 'li')[4].textContent).toEqual('Minko');
+ });
+ }));
+ });
+}
+
+@Component({
+ providers: [NameList],
+ selector: 'test-cmp',
+ template: '',
+ directives: [AboutCmp]
+})
+class TestComponent {}
diff --git a/src/about/components/about.ts b/src/about/components/about.ts
new file mode 100644
index 0000000..35925a0
--- /dev/null
+++ b/src/about/components/about.ts
@@ -0,0 +1,23 @@
+import {Component} from 'angular2/core';
+import {CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/common';
+
+import {NameList} from '../../shared/services/name_list';
+
+@Component({
+ selector: 'about',
+ templateUrl: './about/components/about.html',
+ directives: [FORM_DIRECTIVES, CORE_DIRECTIVES]
+})
+export class AboutCmp {
+ newName: string;
+ constructor(public list: NameList) {}
+ /*
+ * @param newname any text as input.
+ * @returns return false to prevent default form submit behavior to refresh the page.
+ */
+ addName(): boolean {
+ this.list.add(this.newName);
+ this.newName = '';
+ return false;
+ }
+}
diff --git a/src/app/components/app.css b/src/app/components/app.css
new file mode 100644
index 0000000..f6815c2
--- /dev/null
+++ b/src/app/components/app.css
@@ -0,0 +1,33 @@
+.sample-app-content {
+ font-family: Verdana;
+}
+.sample-app-content h1 {
+ color: #999;
+ font-size: 3em;
+}
+.sample-app-content h2 {
+ color: #990000;
+ font-size: 2em;
+}
+.sample-app-content p,
+.sample-app-content nav {
+ padding: 30px;
+}
+.sample-app-content li,
+.sample-app-content p {
+ font-size: 1.2em;
+}
+.sample-app-content li {
+ font-family: Consolas;
+}
+.sample-app-content nav a {
+ display: inline-block;
+ margin-right: 15px;
+}
+.sample-app-content input,
+.sample-app-content button {
+ padding: 5px;
+ font-size: 1em;
+ outline: none;
+}
+
diff --git a/src/app/components/app.e2e.ts b/src/app/components/app.e2e.ts
new file mode 100644
index 0000000..0c043de
--- /dev/null
+++ b/src/app/components/app.e2e.ts
@@ -0,0 +1,27 @@
+describe('App', function() {
+
+ beforeEach(function() {
+ browser.get('');
+ });
+
+ it('should have a title', function() {
+ expect(browser.getTitle()).toEqual('My Angular2 App');
+ });
+
+ it('should have ', function() {
+ expect(element(by.css('app section')).isPresent()).toEqual(true);
+ });
+
+ it('should have