├── .gitignore ├── README.md ├── package.json ├── protractor.conf.js ├── src ├── app │ ├── app.component.ts │ ├── bootstrap.ts │ ├── characters │ │ ├── character.service.ts │ │ ├── character.ts │ │ └── characters.component.ts │ ├── dashboard │ │ └── dashboard.component.ts │ └── silly-calculator │ │ └── calculator.component.ts ├── index.html ├── styles.css └── system.config.ts ├── test └── e2e │ ├── calculator.spec.ts │ ├── characters.spec.ts │ ├── dashboard.spec.ts │ └── pageObjects │ ├── calculator.pageObject.ts │ └── dashboard.pageObject.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # node & jspm 2 | node_modules 3 | jspm_packages 4 | 5 | # generated 6 | dev 7 | *.log 8 | 9 | # type definitions 10 | typings 11 | 12 | # IntelliJ 13 | .idea 14 | 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Angular 2 Go with Protractor ! 2 | 3 | Simple starter example that gets you going with Angular 2 and Protractor in minutes, based on the 4 | [angular2-go seed](https://github.com/johnpapa/angular2-go) by John Papa. 5 | 6 | ## Quick note about dependencies 7 | 8 | This seed uses a Protractor version >= v3.0.0. Starting with v3.0.0 Protractor no longer supports node versions < v4.2 9 | (see [changelog](https://github.com/angular/protractor/blob/master/CHANGELOG.md#300)), so make sure you're using a 10 | compatible node version. 11 | 12 | ## Run the application 13 | 14 | 1. Run `npm install` 15 | 16 | 1. Run the TypeScript compiler and watch for changes `npm run dev` 17 | 18 | 1. Open 2nd terminal and launch the app in the browser `npm start` 19 | 20 | ## Run the e2e tests 21 | 22 | 1. Ensure the application is running (`npm start`) 23 | 24 | 1. Open a third terminal and run the tests `npm run e2e` 25 | 26 | The tests are run directly against Chrome, so no need to fire up a Selenium Standalone Server instance. Check 27 | protractor.conf.js for comments about ng2-specific configuration. 28 | 29 | Keep in mind that currently not all locator strategies are supported for ng2 apps. Things like by.model() or by.binding() 30 | are not working just yet. Check the Protractor [changelog](https://github.com/angular/protractor/blob/master/CHANGELOG.md) 31 | for new releases 32 | 33 | ##Notes 34 | 35 | This sample intentionally uses precise versions of Angular 2 and SystemJS so new versions do not break it. I will update these as Angular 2 moves out of Alpha. 36 | 37 | This uses the Path Routing Strategy (HTML5 Mode in Angular 1). This is ideal, however since this demo strives for a simple server using live-server, if you refresh the browser when on a deep link (a named route), you will get a 404. Simply go back to the root /. 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular2-go", 3 | "version": "0.0.1", 4 | "repository": "https://github.com/johnpapa/angular2-go", 5 | "scripts": { 6 | "copy": "cpy 'src/**/*.html' 'src/**/*.css' dev/src", 7 | "tsc": "tsc --outDir dev -w", 8 | "dev": "npm run copy && npm run tsc", 9 | "start": "live-server --open=dev/src/#/dashboard", 10 | "e2e": "./node_modules/protractor/bin/webdriver-manager update && protractor protractor.conf.js" 11 | }, 12 | "engines": { 13 | "npm": ">=2.14.x", 14 | "node": ">=4.2.x" 15 | }, 16 | "dependencies": { 17 | "angular2": "2.0.0-alpha.45", 18 | "systemjs": "0.19.5" 19 | }, 20 | "devDependencies": { 21 | "cpy": "^3.4.1", 22 | "live-server": "^0.8.1", 23 | "protractor": "^3.0.0", 24 | "typescript": "^1.6.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | baseUrl: 'http://localhost:8080/dev/src/', 3 | specs: ['dev/test/e2e/**/*.spec.js'], 4 | directConnect: true, 5 | exclude: [], 6 | multiCapabilities: [{ 7 | browserName: 'chrome' 8 | }], 9 | allScriptsTimeout: 110000, 10 | getPageTimeout: 100000, 11 | framework: 'jasmine2', 12 | jasmineNodeOpts: { 13 | isVerbose: false, 14 | showColors: true, 15 | includeStackTrace: false, 16 | defaultTimeoutInterval: 400000 17 | }, 18 | 19 | /** 20 | * ng2 related configuration 21 | * 22 | * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching 23 | * `rootEl` 24 | * 25 | */ 26 | useAllAngular2AppRoots: true 27 | }; -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'angular2/angular2'; 2 | import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router'; 3 | 4 | import {DashboardComponent} from './dashboard/dashboard.component'; 5 | import {CharactersComponent} from './characters/characters.component'; 6 | import {CalculatorComponent} from './silly-calculator/calculator.component'; 7 | 8 | @Component({ 9 | selector: 'my-app', 10 | template: ` 11 | Dashboard | 12 | Characters | 13 | Silly calculator 14 | 15 | `, 16 | directives: [ROUTER_DIRECTIVES] 17 | }) 18 | @RouteConfig([ 19 | { path: '/dashboard', as: 'Dashboard', component: DashboardComponent }, 20 | { path: '/characters', as: 'Characters', component: CharactersComponent }, 21 | { path: '/calculator', as: 'Calculator', component: CalculatorComponent } 22 | ]) 23 | export class AppComponent { } 24 | -------------------------------------------------------------------------------- /src/app/bootstrap.ts: -------------------------------------------------------------------------------- 1 | import {provide, bootstrap} from 'angular2/angular2'; 2 | import {ROUTER_PROVIDERS, HashLocationStrategy, LocationStrategy} from 'angular2/router'; 3 | 4 | import {AppComponent} from './app.component'; 5 | import {CharacterService} from './characters/character.service'; 6 | 7 | bootstrap(AppComponent, [ 8 | ROUTER_PROVIDERS, 9 | CharacterService, 10 | provide(LocationStrategy, {useClass: HashLocationStrategy}) 11 | ]); 12 | -------------------------------------------------------------------------------- /src/app/characters/character.service.ts: -------------------------------------------------------------------------------- 1 | import {Character} from './character'; 2 | 3 | export class CharacterService { 4 | getCharacters() { return Promise.resolve(CHARACTERS); } 5 | 6 | getCharacter(id: number) { 7 | return Promise.resolve(CHARACTERS) 8 | .then((characters) => { return characters.filter((c) => { 9 | return c.id === id; 10 | })[0]}); 11 | } 12 | } 13 | 14 | var CHARACTERS : Character[] = [ 15 | { 16 | "id": 11, 17 | "name": "Aragorn" 18 | }, 19 | { 20 | "id": 12, 21 | "name": "Meriadoc Brandybuck" 22 | }, 23 | { 24 | "id": 13, 25 | "name": "Pippin Took" 26 | }, 27 | { 28 | "id": 14, 29 | "name": "Frodo Baggins" 30 | }, 31 | { 32 | "id": 15, 33 | "name": "Samwise Gamgee" 34 | }, 35 | { 36 | "id": 16, 37 | "name": "Gandalf" 38 | }, 39 | { 40 | "id": 17, 41 | "name": "Boromir" 42 | }, 43 | { 44 | "id": 18, 45 | "name": "Gimli" 46 | }, 47 | { 48 | "id": 19, 49 | "name": "Legolas" 50 | }, 51 | { 52 | "id": 20, 53 | "name": "Elrond" 54 | } 55 | ]; 56 | -------------------------------------------------------------------------------- /src/app/characters/character.ts: -------------------------------------------------------------------------------- 1 | export interface Character { 2 | id: number; 3 | name: string; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/characters/characters.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, NgFor, NgIf} from 'angular2/angular2'; 2 | import {Router} from 'angular2/router'; 3 | 4 | import {CharacterService} from './character.service'; 5 | import {Character} from './character'; 6 | 7 | @Component({ 8 | selector: 'my-characters', 9 | template: ` 10 |

Select a Character

11 | 16 |

17 | {{currentCharacter.name | uppercase}} is my character 18 |

19 | `, 20 | directives: [NgFor, NgIf], 21 | styles: [` 22 | .characters {list-style-type: none; margin-left: 1em; padding: 0; width: 14em;} 23 | .characters li { cursor: pointer; } 24 | .characters li:hover {color: #369; background-color: #EEE; } 25 | `] 26 | }) 27 | export class CharactersComponent { 28 | private _characters: Character[]; 29 | public currentCharacter: Character; 30 | 31 | constructor(private _characterService: CharacterService) { } 32 | 33 | get characters() { 34 | return this._characters || this.getCharacters() 35 | } 36 | 37 | onSelect(character: Character) { this.currentCharacter = character; } 38 | 39 | ///////////////// 40 | 41 | private getCharacters() { 42 | this._characters = []; 43 | 44 | this._characterService.getCharacters() 45 | .then(characters => this._characters = characters); 46 | 47 | return this._characters; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/app/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, FORM_DIRECTIVES} from 'angular2/angular2'; 2 | 3 | @Component({ 4 | selector: 'my-dashboard', 5 | template: ` 6 |

Dashboard

7 |
Yo {{name}}
8 | 9 | 10 |

{{message}}

11 | `, 12 | directives: [FORM_DIRECTIVES] 13 | }) 14 | export class DashboardComponent { 15 | public name = 'John'; 16 | public message = ''; 17 | 18 | sayHello() { 19 | this.message = 'Yo ' + this.name + '!'; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/app/silly-calculator/calculator.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, FORM_DIRECTIVES} from 'angular2/angular2'; 2 | 3 | @Component({ 4 | selector: 'my-calculator', 5 | template: ` 6 |

The silly calculator

7 | 8 | + 9 | 10 | = 11 | {{add(firstOperand, secondOperand)}} 12 | `, 13 | directives: [FORM_DIRECTIVES] 14 | }) 15 | export class CalculatorComponent { 16 | 17 | add(a: any, b: any) { 18 | if(a && b) { 19 | return (parseInt(a) + parseInt(b)); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Loading... 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | h2 { color: #444; font-weight: lighter; } 2 | body { margin: 2em; } 3 | body, input[text], button { color: #888; font-family: Cambria, Georgia; } 4 | button {padding: 0.2em; font-size: 14px} -------------------------------------------------------------------------------- /src/system.config.ts: -------------------------------------------------------------------------------- 1 | System.config({ 2 | packages: {'app': {defaultExtension: 'js'}} 3 | }); 4 | -------------------------------------------------------------------------------- /test/e2e/calculator.spec.ts: -------------------------------------------------------------------------------- 1 | import {CalculatorPageObject} from './pageObjects/calculator.pageObject'; 2 | 3 | describe('Calculator Page', function() { 4 | 5 | var calculator = new CalculatorPageObject(); 6 | 7 | beforeEach(function() { 8 | browser.get('#/calculator'); 9 | }); 10 | 11 | it('should add positive numbers', function() { 12 | calculator.add(1, 2); 13 | expect(calculator.result.getText()).toEqual('3'); 14 | 15 | calculator.add(2, 2); 16 | expect(calculator.result.getText()).toEqual('4'); 17 | }); 18 | 19 | it('should add negative numbers', function() { 20 | calculator.add(-1, 4); 21 | expect(calculator.result.getText()).toEqual('3'); 22 | 23 | calculator.add('2', '-2'); 24 | expect(calculator.result.getText()).toEqual('0'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/e2e/characters.spec.ts: -------------------------------------------------------------------------------- 1 | // TODO - go for it ;) -------------------------------------------------------------------------------- /test/e2e/dashboard.spec.ts: -------------------------------------------------------------------------------- 1 | import {DashboardPageObject} from './pageObjects/dashboard.pageObject'; 2 | 3 | describe('Dashboard page', function() { 4 | 5 | var dashboard: DashboardPageObject = new DashboardPageObject(); 6 | 7 | beforeEach(function() { 8 | browser.get('#/dashboard'); 9 | }); 10 | 11 | it('should say hello', function() { 12 | dashboard.sayHi('yo'); 13 | expect(dashboard.greeting.getText()).toEqual('Yo yo'); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /test/e2e/pageObjects/calculator.pageObject.ts: -------------------------------------------------------------------------------- 1 | export class CalculatorPageObject { 2 | public firstOperand = element.all(by.tagName('input')).get(0); 3 | public secondOperand = element.all(by.tagName('input')).get(1); 4 | public result = element(by.css('.result')); 5 | 6 | ///// 7 | 8 | add(a: any, b: any) { 9 | // this doesn't work in ts 10 | // this.firstOperand.clear().sendKeys(); 11 | 12 | this.firstOperand.clear(); 13 | this.firstOperand.sendKeys(a); 14 | 15 | this.secondOperand.clear(); 16 | this.secondOperand.sendKeys(b); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/e2e/pageObjects/dashboard.pageObject.ts: -------------------------------------------------------------------------------- 1 | export class DashboardPageObject { 2 | public name = element(by.tagName('input')); 3 | public hiButton = element(by.tagName('button')); 4 | public greeting = element(by.tagName('div')); 5 | 6 | ///// 7 | 8 | sayHi(name: string) { 9 | this.name.clear(); 10 | this.name.sendKeys(name); 11 | this.hiButton.click(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "commonjs", 5 | "sourceMap": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "removeComments": false, 9 | "noImplicitAny": true 10 | }, 11 | "exclude": [ 12 | "node_modules", 13 | "test/e2e/pageObjects" 14 | ] 15 | } --------------------------------------------------------------------------------