Skip to content
This repository was archived by the owner on Dec 4, 2017. It is now read-only.

docs(hierarchical-di): Correct avoidance example (#3086) and more #3091

Merged
merged 2 commits into from
Jan 13, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 90 additions & 40 deletions public/docs/_examples/hierarchical-dependency-injection/e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,106 @@
import { browser, element, by } from 'protractor';
'use strict'; // necessary for es6 output in node

describe('Hierarchical dependency injection', function () {
import { browser, by, element } from 'protractor';

beforeEach(function () {
describe('Hierarchical dependency injection', () => {

beforeAll(() => {
browser.get('');
});

it('should open with a card view', function () {
expect(element.all(by.cssContainingText('button', 'edit')).get(0).isDisplayed()).toBe(true,
'edit button should be displayed');
});
describe('Heroes Scenario', () => {
let page = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting usage of the Page Objects pattern. There really is no reason to have it separately in smaller tests.

Copy link
Contributor Author

@wardbell wardbell Jan 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reason was that I was gathering together the queries I wanted to do in tests. Wasn't sure how many tests I would write but I was pretty sure the affordances should be involved.

Did it make the tests hard to read?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at all, I think we should have more page objects like this in our e2e tests overall.

heroName: '',
heroText: '',

it('should have multiple heroes listed', function () {
expect(element.all(by.css('heroes-list li')).count()).toBeGreaterThan(1);
});
// queries
heroEl: element.all(by.css('heroes-list li')).get(0), // first hero
heroCardEl: element(by.css('heroes-list hero-card')), // first hero card
cardNameInputEl: element.all(by.css('heroes-list hero-card input')).get(0),
cancelButtonEl: element(by.cssContainingText('heroes-list hero-card button', 'Cancel')),
closeButtonEl: element(by.cssContainingText('heroes-list hero-card button', 'Close')),
saveButtonEl: element(by.cssContainingText('heroes-list hero-card button', 'Save'))
};

it('should change to editor view after selection', function () {
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');
it('should list multiple heroes', () => {
expect(element.all(by.css('heroes-list li')).count()).toBeGreaterThan(1);
});

it('should show no hero cards at the start', () => {
expect(element.all(by.css('heroes-list li hero-card')).count()).toBe(0);
});

it('should open first hero in hero-card view after click', () => {
page.heroEl.getText()
.then(val => {
// console.log('Selected hero text: ' + val);
page.heroText = val;
page.heroName = val.substring(0, val.indexOf('()') - 1);
})
.then(() => page.heroEl.click())
.then(() => {
expect(page.heroCardEl.isDisplayed()).toBe(true);
});
});

it('hero card should have first hero\'s name', () => {
// Not `page.cardNameInputEl.getAttribute('value')` although later that is essential
expect(page.cardNameInputEl.getText()).toEqual(page.heroName);
});

it('should be able to cancel change', () => {
page.cardNameInputEl.sendKeys('foo')
.then(() => {
expect(page.cardNameInputEl.getAttribute('value')).toContain('foo', 'input name should have foo');
expect(page.heroEl.getText()).toEqual(page.heroText, 'list text should be unchanged');
return page.cancelButtonEl.click();
})
.then(() => {
expect(page.cardNameInputEl.getAttribute('value')).not.toContain('foo', 'input name should not have foo');
expect(page.heroEl.getText()).toEqual(page.heroText, 'list text should be unchanged');
});
});

it('should be able to save change', () => {
page.cardNameInputEl.sendKeys('bar')
.then(() => {
expect(page.cardNameInputEl.getAttribute('value')).toContain('bar', 'input name should have bar');
expect(page.heroEl.getText()).toEqual(page.heroText, 'list text should be unchanged');
return page.saveButtonEl.click();
})
.then(() => {
expect(page.cardNameInputEl.getAttribute('value')).toContain('bar', 'input name should still have bar');
expect(page.heroEl.getText()).toContain('bar', 'list text should have changed to include bar');
});
});

it('should be able to close card', () => {
page.saveButtonEl.click()
.then(() => {
expect(element.all(by.css('heroes-list li hero-card')).count()).toBe(0);
});
});
});

it('should be able to save editor change', function () {
testEdit(true);
});

it('should be able to cancel editor change', function () {
testEdit(false);
describe('Villains Scenario', () => {
it('should list multiple villains', () => {
expect(element.all(by.css('villains-list li')).count()).toBeGreaterThan(1);
});
});

function testEdit(shouldSave: boolean) {
// select 2nd ele
let heroEle = element.all(by.css('heroes-list li')).get(1);
// get the 2nd span which is the name of the hero
let heroNameEle = heroEle.all(by.css('hero-card span')).get(1);
let editButtonEle = heroEle.element(by.cssContainingText('button', 'edit'));
editButtonEle.click().then(function() {
let inputEle = heroEle.element(by.css('hero-editor input'));
return inputEle.sendKeys('foo');
}).then(function() {
let buttonName = shouldSave ? 'save' : 'cancel';
let buttonEle = heroEle.element(by.cssContainingText('button', buttonName));
return buttonEle.click();
}).then(function() {
if (shouldSave) {
expect(heroNameEle.getText()).toContain('foo');
} else {
expect(heroNameEle.getText()).not.toContain('foo');
}
});
}
describe('Cars Scenario', () => {

it('A-component should use expected services', () => {
expect(element(by.css('a-car')).getText()).toContain('C1-E1-T1');
});

it('B-component should use expected services', () => {
expect(element(by.css('b-car')).getText()).toContain('C2-E2-T1');
});

it('C-component should use expected services', () => {
expect(element(by.css('c-car')).getText()).toContain('C3-E2-T1');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Component } from '@angular/core';

@Component({
selector: 'my-app',
template: `
<label><input type="checkbox" [checked]="showHeroes" (change)="showHeroes=!showHeroes">Heroes</label>
<label><input type="checkbox" [checked]="showVillains" (change)="showVillains=!showVillains">Villains</label>
<label><input type="checkbox" [checked]="showCars" (change)="showCars=!showCars">Cars</label>

<h1>Hierarchical Dependency Injection</h1>

<heroes-list *ngIf="showHeroes"></heroes-list>
<villains-list *ngIf="showVillains"></villains-list>
<my-cars *ngIf="showCars"></my-cars>
`
})
export class AppComponent {
showCars = true;
showHeroes = true;
showVillains = true;
}
Original file line number Diff line number Diff line change
@@ -1,43 +1,33 @@
// #docregion
import { NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { FormsModule } from '@angular/forms';

import { HeroesListComponent } from './heroes-list.component';
import { HeroEditorComponent } from './hero-editor.component';
import { HeroCardComponent } from './hero-card.component';
import { HeroesService } from './heroes.service';
import { AppComponent } from './app.component';
import { HeroCardComponent } from './hero-card.component';
import { HeroesListComponent } from './heroes-list.component';
import { HeroesService } from './heroes.service';
import { VillainsListComponent } from './villains-list.component';

import { carComponents, carServices } from './car.components';

@NgModule({
imports: [
BrowserModule,
FormsModule
],
providers: [ HeroesService ],
providers: [
carServices,
HeroesService
],
declarations: [
AppComponent,
carComponents,
HeroesListComponent,
HeroCardComponent,
HeroEditorComponent
VillainsListComponent
],
bootstrap: [ HeroesListComponent ]
bootstrap: [ AppComponent ]
})
export class AppModule { }

/* Documentation artifact below
// #docregion bad-alternative
// Don't do this!
@NgModule({
imports: [
BrowserModule,
FormsModule
],
providers: [ HeroesService, RestoreService ],
declarations: [ HeroesListComponent ],
bootstrap: [
HeroesListComponent,
HeroCardComponent,
HeroEditorComponent
]
})
// #enddocregion bad-alternative
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Component } from '@angular/core';

import {
CarService, CarService2, CarService3,
EngineService, EngineService2, TiresService
} from './car.services';

////////// CCarComponent ////////////
@Component({
selector: 'c-car',
template: `<div>C: {{description}}</div>`,
providers: [
{ provide: CarService, useClass: CarService3 }
]
})
export class CCarComponent {
description: string;
constructor(carService: CarService) {
this.description = `${carService.getCar().description} (${carService.name})`;
}
}

////////// BCarComponent ////////////
@Component({
selector: 'b-car',
template: `
<div>B: {{description}}</div>
<c-car></c-car>
`,
providers: [
{ provide: CarService, useClass: CarService2 },
{ provide: EngineService, useClass: EngineService2 }
]
})
export class BCarComponent {
description: string;
constructor(carService: CarService) {
this.description = `${carService.getCar().description} (${carService.name})`;
}
}

////////// ACarComponent ////////////
@Component({
selector: 'a-car',
template: `
<div>A: {{description}}</div>
<b-car></b-car>`
})
export class ACarComponent {
description: string;
constructor(carService: CarService) {
this.description = `${carService.getCar().description} (${carService.name})`;
}
}
////////// CarsComponent ////////////
@Component({
selector: 'my-cars',
template: `
<h3>Cars</h3>
<a-car></a-car>`
})
export class CarsComponent { }

////////////////

export const carComponents = [
CarsComponent,
ACarComponent, BCarComponent, CCarComponent
];

// generic car-related services
export const carServices = [
CarService, EngineService, TiresService
];
Loading