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