diff --git a/README.md b/README.md
index 7cd3bb7ae7db..e6e659216886 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
[![Dependency Status][david-badge]][david-badge-url]
[![devDependency Status][david-dev-badge]][david-dev-badge-url]
[![npm][npm-badge]][npm-badge-url]
-
+
Prototype of a CLI for Angular 2 applications based on the [ember-cli](http://www.ember-cli.com/) project.
## Note
@@ -59,6 +59,50 @@ Directive | `ng g directive my-new-directive`
Pipe | `ng g pipe my-new-pipe`
Service | `ng g service my-new-service`
+### Generating a route
+
+You can generate a new route by with the following command (note the singular
+used in `hero`):
+
+```bash
+ng generate route hero
+```
+
+This will create a folder with a routable component (`hero-root.component.ts`)
+with two sub-routes. The file structure will be as follows:
+
+```
+...
+|-- app
+| |-- hero
+| | |-- hero-detail.component.html
+| | |-- hero-detail.component.css
+| | |-- hero-detail.component.spec.ts
+| | |-- hero-detail.component.ts
+| | |-- hero-list.component.html
+| | |-- hero-list.component.css
+| | |-- hero-list.component.spec.ts
+| | |-- hero-list.component.ts
+| | |-- hero-root.component.spec.ts
+| | |-- hero-root.component.ts
+| | |-- hero.service.spec.ts
+| | |-- hero.service.ts
+| |-- ...
+|-- app.ts
+...
+```
+
+Afterwards to use the new route open your main app component, import
+`hero-root.component.ts` and add it in the route config:
+
+```
+@RouteConfig([
+ {path:'/hero/...', name: 'HeroRoot', component: HeroRoot}
+])
+```
+
+Visiting `http://localhost:4200/hero` will show the hero list.
+
### Creating a build
diff --git a/addon/ng2/blueprints/ng2/files/karma-test-shim.js b/addon/ng2/blueprints/ng2/files/karma-test-shim.js
index b11d12c8bf38..286323376d55 100644
--- a/addon/ng2/blueprints/ng2/files/karma-test-shim.js
+++ b/addon/ng2/blueprints/ng2/files/karma-test-shim.js
@@ -37,7 +37,7 @@ System.import('angular2/platform/browser').then(function(browser_adapter) {
});
function onlyAppFiles(filePath) {
- return /^\/base\/dist\/app\/(?!spec)([a-z0-9-_\/]+)\.js$/.test(filePath);
+ return /^\/base\/dist\/app\/(?!.*\.spec\.js$)([a-z0-9-_\.\/]+)\.js$/.test(filePath);
}
function onlySpecFiles(path) {
diff --git a/addon/ng2/blueprints/ng2/files/karma.conf.js b/addon/ng2/blueprints/ng2/files/karma.conf.js
index 3f753269efe7..e370613e5255 100644
--- a/addon/ng2/blueprints/ng2/files/karma.conf.js
+++ b/addon/ng2/blueprints/ng2/files/karma.conf.js
@@ -11,6 +11,8 @@ module.exports = function(config) {
{pattern: 'node_modules/systemjs/dist/system.src.js', included: true, watched: true},
{pattern: 'node_modules/rxjs/bundles/Rx.js', included: true, watched: true},
{pattern: 'node_modules/angular2/bundles/angular2.js', included: true, watched: true},
+ {pattern: 'node_modules/angular2/bundles/http.dev.js', included: true, watched: true},
+ {pattern: 'node_modules/angular2/bundles/router.dev.js', included: true, watched: true},
{pattern: 'node_modules/angular2/bundles/testing.dev.js', included: true, watched: true},
{pattern: 'karma-test-shim.js', included: true, watched: true},
diff --git a/addon/ng2/blueprints/ng2/files/src/app.ts b/addon/ng2/blueprints/ng2/files/src/app.ts
index f752e09bdcc8..01383e781c51 100644
--- a/addon/ng2/blueprints/ng2/files/src/app.ts
+++ b/addon/ng2/blueprints/ng2/files/src/app.ts
@@ -1,5 +1,7 @@
import {bootstrap} from 'angular2/platform/browser';
import {<%= jsComponentName %>App} from './app/<%= htmlComponentName %>';
+import {ROUTER_PROVIDERS} from 'angular2/router';
-
-bootstrap(<%= jsComponentName %>App);
+bootstrap(<%= jsComponentName %>App, [
+ ROUTER_PROVIDERS
+]);
diff --git a/addon/ng2/blueprints/ng2/files/src/app/__name__.html b/addon/ng2/blueprints/ng2/files/src/app/__name__.html
index f206e8e44c93..2661fb937f37 100644
--- a/addon/ng2/blueprints/ng2/files/src/app/__name__.html
+++ b/addon/ng2/blueprints/ng2/files/src/app/__name__.html
@@ -1,3 +1,5 @@
<%= htmlComponentName %> Works!
-
\ No newline at end of file
+
+
+
diff --git a/addon/ng2/blueprints/ng2/files/src/app/__name__.ts b/addon/ng2/blueprints/ng2/files/src/app/__name__.ts
index 832a90bb1239..79ea23dcb529 100644
--- a/addon/ng2/blueprints/ng2/files/src/app/__name__.ts
+++ b/addon/ng2/blueprints/ng2/files/src/app/__name__.ts
@@ -1,16 +1,20 @@
import {Component} from 'angular2/core';
+import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
@Component({
selector: '<%= htmlComponentName %>-app',
providers: [],
templateUrl: 'app/<%= htmlComponentName %>.html',
- directives: [],
+ directives: [ROUTER_DIRECTIVES],
pipes: []
})
+@RouteConfig([
+
+])
export class <%= jsComponentName %>App {
defaultMeaning: number = 42;
-
+
meaningOfLife(meaning?: number) {
return `The meaning of life is ${meaning || this.defaultMeaning}`;
}
diff --git a/addon/ng2/blueprints/ng2/files/src/index.html b/addon/ng2/blueprints/ng2/files/src/index.html
index d5a9a9a49d91..bc847cb45a05 100644
--- a/addon/ng2/blueprints/ng2/files/src/index.html
+++ b/addon/ng2/blueprints/ng2/files/src/index.html
@@ -3,7 +3,7 @@
<%= jsComponentName %>
-
+
{{content-for 'head'}}
diff --git a/addon/ng2/blueprints/route/files/src/app/__name__/__name__-detail.component.css b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-detail.component.css
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/addon/ng2/blueprints/route/files/src/app/__name__/__name__-detail.component.html b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-detail.component.html
new file mode 100644
index 000000000000..163f61d7719c
--- /dev/null
+++ b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-detail.component.html
@@ -0,0 +1,13 @@
+">
+
"{{editName}}"
+
+
+ {{<%= camelizedModuleName %>.id}}
+
+
+
+
+
+
+
+
diff --git a/addon/ng2/blueprints/route/files/src/app/__name__/__name__-detail.component.spec.ts b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-detail.component.spec.ts
new file mode 100644
index 000000000000..54c0541d6bf3
--- /dev/null
+++ b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-detail.component.spec.ts
@@ -0,0 +1,25 @@
+import {
+ it,
+ iit,
+ describe,
+ ddescribe,
+ expect,
+ inject,
+ injectAsync,
+ TestComponentBuilder,
+ beforeEachProviders
+} from 'angular2/testing';
+import {provide} from 'angular2/core';
+import {<%= classifiedModuleName %>DetailComponent} from './<%= dasherizedModuleName %>-detail.component';
+
+describe('<%= classifiedModuleName %>DetailComponent', () => {
+
+ beforeEachProviders(() => []);
+
+ it('should ...', injectAsync([TestComponentBuilder], (tcb:TestComponentBuilder) => {
+ return tcb.createAsync(<%= classifiedModuleName %>DetailComponent).then((fixture) => {
+ fixture.detectChanges();
+ });
+ }));
+
+});
diff --git a/addon/ng2/blueprints/route/files/src/app/__name__/__name__-detail.component.ts b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-detail.component.ts
new file mode 100644
index 000000000000..ed6f1fa4db33
--- /dev/null
+++ b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-detail.component.ts
@@ -0,0 +1,54 @@
+import {Component, OnInit} from 'angular2/core';
+import {<%= classifiedModuleName %>, <%= classifiedModuleName %>Service} from './<%= dasherizedModuleName %>.service';
+import {RouteParams, Router} from 'angular2/router';
+import {CanDeactivate, ComponentInstruction} from 'angular2/router';
+
+@Component({
+ templateUrl: 'app/<%= dasherizedModuleName %>/<%= dasherizedModuleName %>-detail.component.html',
+ styleUrls: ['app/<%= dasherizedModuleName %>/<%= dasherizedModuleName %>-detail.component.css']
+})
+export class <%= classifiedModuleName %>DetailComponent implements OnInit, CanDeactivate {
+
+ <%= camelizedModuleName %>: <%= classifiedModuleName %>;
+ editName: string;
+
+ constructor(
+ private _service: <%= classifiedModuleName %>Service,
+ private _router: Router,
+ private _routeParams: RouteParams
+ ) { }
+
+ ngOnInit() {
+ let id = +this._routeParams.get('id');
+ this._service.get(id).then(<%= camelizedModuleName %> => {
+ if (<%= camelizedModuleName %>) {
+ this.editName = <%= camelizedModuleName %>.name;
+ this.<%= camelizedModuleName %> = <%= camelizedModuleName %>;
+ } else {
+ this.gotoList();
+ }
+ });
+ }
+
+ routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction): any {
+ if (!this.<%= camelizedModuleName %> || this.<%= camelizedModuleName %>.name === this.editName) {
+ return true;
+ }
+
+ return new Promise((resolve, reject) => resolve(window.confirm('Discard changes?')));
+ }
+
+ cancel() {
+ this.editName = this.<%= camelizedModuleName %>.name;
+ this.gotoList();
+ }
+
+ save() {
+ this.<%= camelizedModuleName %>.name = this.editName;
+ this.gotoList();
+ }
+
+ gotoList() {
+ this._router.navigate(['<%= classifiedModuleName %>List']);
+ }
+}
diff --git a/addon/ng2/blueprints/route/files/src/app/__name__/__name__-list.component.css b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-list.component.css
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/addon/ng2/blueprints/route/files/src/app/__name__/__name__-list.component.html b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-list.component.html
new file mode 100644
index 000000000000..1a7902798519
--- /dev/null
+++ b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-list.component.html
@@ -0,0 +1,12 @@
+<%= classifiedModuleName %> List
+
+
diff --git a/addon/ng2/blueprints/route/files/src/app/__name__/__name__-list.component.spec.ts b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-list.component.spec.ts
new file mode 100644
index 000000000000..514f40caa4f0
--- /dev/null
+++ b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-list.component.spec.ts
@@ -0,0 +1,25 @@
+import {
+ it,
+ iit,
+ describe,
+ ddescribe,
+ expect,
+ inject,
+ injectAsync,
+ TestComponentBuilder,
+ beforeEachProviders
+} from 'angular2/testing';
+import {provide} from 'angular2/core';
+import {<%= classifiedModuleName %>ListComponent} from './<%= dasherizedModuleName %>-list.component';
+
+describe('<%= classifiedModuleName %>ListComponent', () => {
+
+ beforeEachProviders(() => []);
+
+ it('should ...', injectAsync([TestComponentBuilder], (tcb:TestComponentBuilder) => {
+ return tcb.createAsync(<%= classifiedModuleName %>ListComponent).then((fixture) => {
+ fixture.detectChanges();
+ });
+ }));
+
+});
diff --git a/addon/ng2/blueprints/route/files/src/app/__name__/__name__-list.component.ts b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-list.component.ts
new file mode 100644
index 000000000000..b2feeae1cac6
--- /dev/null
+++ b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-list.component.ts
@@ -0,0 +1,17 @@
+import {Component, OnInit} from 'angular2/core';
+import {<%= classifiedModuleName %>, <%= classifiedModuleName %>Service} from './<%= dasherizedModuleName %>.service';
+import {ROUTER_DIRECTIVES} from 'angular2/router';
+
+@Component({
+ templateUrl: 'app/<%= dasherizedModuleName %>/<%= dasherizedModuleName %>-list.component.html',
+ styleUrls: ['app/<%= dasherizedModuleName %>/<%= dasherizedModuleName %>-list.component.css'],
+ directives: [ROUTER_DIRECTIVES]
+})
+export class <%= classifiedModuleName %>ListComponent implements OnInit {
+ <%= camelizedModuleName %>s: <%= classifiedModuleName %>[];
+ constructor(
+ private _service: <%= classifiedModuleName %>Service) {}
+ ngOnInit() {
+ this._service.getAll().then(<%= camelizedModuleName %>s => this.<%= camelizedModuleName %>s = <%= camelizedModuleName %>s);
+ }
+}
diff --git a/addon/ng2/blueprints/route/files/src/app/__name__/__name__-root.component.ts b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-root.component.ts
new file mode 100644
index 000000000000..515ad419a4ce
--- /dev/null
+++ b/addon/ng2/blueprints/route/files/src/app/__name__/__name__-root.component.ts
@@ -0,0 +1,19 @@
+import {Component} from 'angular2/core';
+import {RouteConfig, RouterOutlet} from 'angular2/router';
+
+import {<%= classifiedModuleName %>ListComponent} from './<%= dasherizedModuleName %>-list.component';
+import {<%= classifiedModuleName %>DetailComponent} from './<%= dasherizedModuleName %>-detail.component';
+import {<%= classifiedModuleName %>Service} from './<%= dasherizedModuleName %>.service';
+
+@Component({
+ template: '',
+ providers: [<%= classifiedModuleName %>Service],
+ directives: [RouterOutlet]
+})
+@RouteConfig([
+ {path:'/', name: '<%= classifiedModuleName %>List', component: <%= classifiedModuleName %>ListComponent, useAsDefault: true},
+ {path:'/:id', name: '<%= classifiedModuleName %>Detail', component: <%= classifiedModuleName %>DetailComponent}
+])
+export class <%= classifiedModuleName %>Root {
+ constructor() {}
+}
diff --git a/addon/ng2/blueprints/route/files/src/app/__name__/__name__.service.spec.ts b/addon/ng2/blueprints/route/files/src/app/__name__/__name__.service.spec.ts
new file mode 100644
index 000000000000..09fc4e9916aa
--- /dev/null
+++ b/addon/ng2/blueprints/route/files/src/app/__name__/__name__.service.spec.ts
@@ -0,0 +1,17 @@
+import {describe, it, expect, beforeEachProviders, inject} from 'angular2/testing';
+import {provide} from 'angular2/core';
+import {<%= classifiedModuleName %>Service} from './<%= dasherizedModuleName %>.service';
+
+describe('<%= classifiedModuleName %>Service', () => {
+
+ beforeEachProviders(() => [<%= classifiedModuleName %>Service]);
+
+ it('should get all <%= camelizedModuleName %>s', inject([<%= classifiedModuleName %>Service], (<%= camelizedModuleName %>Service:<%= classifiedModuleName %>Service) => {
+ <%= camelizedModuleName %>Service.getAll().then(<%= camelizedModuleName %>s => expect(<%= camelizedModuleName %>s.length).toBe(3));
+ }));
+
+ it('should get one <%= camelizedModuleName %>', inject([<%= classifiedModuleName %>Service], (<%= camelizedModuleName %>Service:<%= classifiedModuleName %>Service) => {
+ <%= camelizedModuleName %>Service.get(1).then(<%= camelizedModuleName %> => expect(<%= camelizedModuleName %>.id).toBe(1));
+ }));
+
+});
diff --git a/addon/ng2/blueprints/route/files/src/app/__name__/__name__.service.ts b/addon/ng2/blueprints/route/files/src/app/__name__/__name__.service.ts
new file mode 100644
index 000000000000..7a1122395f97
--- /dev/null
+++ b/addon/ng2/blueprints/route/files/src/app/__name__/__name__.service.ts
@@ -0,0 +1,21 @@
+import {Injectable} from 'angular2/core';
+
+export class <%= classifiedModuleName %> {
+ constructor(public id: number, public name: string) { }
+}
+
+@Injectable()
+export class <%= classifiedModuleName %>Service {
+ getAll() { return promise; }
+ get(id: number) {
+ return promise.then(all => all.find(e => e.id === id));
+ }
+}
+
+let mock = [
+ new <%= classifiedModuleName %>(1, 'one'),
+ new <%= classifiedModuleName %>(2, 'two'),
+ new <%= classifiedModuleName %>(3, 'three')
+];
+
+let promise = Promise.resolve(mock);
diff --git a/addon/ng2/blueprints/route/index.js b/addon/ng2/blueprints/route/index.js
new file mode 100644
index 000000000000..8540d5f2f309
--- /dev/null
+++ b/addon/ng2/blueprints/route/index.js
@@ -0,0 +1,16 @@
+var stringUtils = require('ember-cli/lib/utilities/string');
+
+module.exports = {
+ description: ''
+
+ //locals: function(options) {
+ // // Return custom template variables here.
+ // return {
+ //
+ // };
+ //}
+
+ // afterInstall: function(options) {
+ // // Perform extra work here.
+ // }
+};
diff --git a/tests/e2e/e2e_workflow.spec.js b/tests/e2e/e2e_workflow.spec.js
index 6c8e4341bad0..00a81ca039d2 100644
--- a/tests/e2e/e2e_workflow.spec.js
+++ b/tests/e2e/e2e_workflow.spec.js
@@ -131,6 +131,40 @@ describe('Basic end-to-end Workflow', function () {
});
});
+ it('Perform `ng test`', function(done) {
+ this.timeout(30000);
+
+ return ng([
+ 'test'
+ ]).then(function(err) {
+ // TODO when `ng test` will be implemented
+ //expect(err).to.be.equal(1);
+ done();
+ });
+ });
+
+ it('Can create a test route using `ng generate route test-route`', function() {
+ return ng([
+ 'generate',
+ 'route',
+ 'test-route'
+ ]).then(function() {
+ var routeDir = path.join(process.cwd(), 'src', 'app', 'test-route');
+ expect(fs.existsSync(routeDir));
+ expect(fs.existsSync(path.join(routeDir, 'test-pipe-detail.component.css')));
+ expect(fs.existsSync(path.join(routeDir, 'test-pipe-detail.component.html')));
+ expect(fs.existsSync(path.join(routeDir, 'test-pipe-detail.component.spec.ts')));
+ expect(fs.existsSync(path.join(routeDir, 'test-pipe-detail.component.ts')));
+ expect(fs.existsSync(path.join(routeDir, 'test-pipe-list.component.css')));
+ expect(fs.existsSync(path.join(routeDir, 'test-pipe-list.component.html')));
+ expect(fs.existsSync(path.join(routeDir, 'test-pipe-list.component.spec.ts')));
+ expect(fs.existsSync(path.join(routeDir, 'test-pipe-list.component.ts')));
+ expect(fs.existsSync(path.join(routeDir, 'test-pipe-root.component.ts')));
+ expect(fs.existsSync(path.join(routeDir, 'test-pipe-root.service.spec.ts')));
+ expect(fs.existsSync(path.join(routeDir, 'test-pipe-root.service.ts')));
+ });
+ });
+
it('Perform `ng test`', function(done) {
this.timeout(300000);
@@ -140,7 +174,7 @@ describe('Basic end-to-end Workflow', function () {
// TODO when `ng test` will be implemented
//expect(err).to.be.equal(1);
// Clean `tmp` folder
-
+
process.chdir(path.resolve(root, '..'));
sh.rm('-rf', './tmp'); // tmp.teardown takes too long