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

Commit a130486

Browse files
committed
docs(forms): fix css, form validation e2e, other tweaks
1 parent c995de0 commit a130486

File tree

6 files changed

+174
-61
lines changed

6 files changed

+174
-61
lines changed
Lines changed: 159 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,180 @@
11
/// <reference path="../_protractor/e2e.d.ts" />
22
'use strict'; // necessary for node!
3-
describeIf(browser.appIsTs || browser.appIsJs, 'Forms Tests', function () {
43

5-
beforeEach(function () {
4+
// THESE TESTS ARE INCOMPLETE
5+
describeIf(browser.appIsTs || browser.appIsJs, 'Form Validation Tests', function () {
6+
7+
beforeAll(function () {
68
browser.get('');
79
});
810

9-
it('should display correct title', function () {
10-
expect(element.all(by.css('h1')).get(0).getText()).toEqual('Hero Form');
11+
describe('Hero Form 1', () => {
12+
beforeAll(() => {
13+
getPage('hero-form-template1');
14+
});
15+
16+
tests();
1117
});
1218

19+
describe('Hero Form 2', () => {
20+
beforeAll(() => {
21+
getPage('hero-form-template2');
22+
});
1323

14-
it('should not display message before submit', function () {
15-
let ele = element(by.css('h2'));
16-
expect(ele.isDisplayed()).toBe(false);
24+
tests();
25+
bobTests();
1726
});
1827

19-
it('should hide form after submit', function () {
20-
let ele = element.all(by.css('h1')).get(0);
21-
expect(ele.isDisplayed()).toBe(true);
22-
let b = element.all(by.css('button[type=submit]')).get(0);
23-
b.click().then(function() {
24-
expect(ele.isDisplayed()).toBe(false);
28+
describe('Hero Form 3 (Reactive)', () => {
29+
beforeAll(() => {
30+
getPage('hero-form-reactive3');
31+
makeNameTooLong();
2532
});
33+
34+
tests();
35+
bobTests();
2636
});
37+
});
2738

28-
it('should display message after submit', function () {
29-
let b = element.all(by.css('button[type=submit]')).get(0);
30-
b.click().then(function() {
31-
expect(element(by.css('h2')).getText()).toContain('You submitted the following');
32-
});
39+
//////////
40+
41+
const testName = 'Test Name';
42+
43+
let page: {
44+
section: protractor.ElementFinder,
45+
form: protractor.ElementFinder,
46+
title: protractor.ElementFinder,
47+
nameInput: protractor.ElementFinder,
48+
alterEgoInput: protractor.ElementFinder,
49+
powerSelect: protractor.ElementFinder,
50+
errorMessages: protractor.ElementArrayFinder,
51+
heroFormButtons: protractor.ElementArrayFinder,
52+
heroSubmitted: protractor.ElementFinder
53+
};
54+
55+
function getPage(sectionTag: string) {
56+
let section = element(by.css(sectionTag));
57+
let buttons = section.all(by.css('button'));
58+
59+
page = {
60+
section: section,
61+
form: section.element(by.css('form')),
62+
title: section.element(by.css('h1')),
63+
nameInput: section.element(by.css('#name')),
64+
alterEgoInput: section.element(by.css('#alterEgo')),
65+
powerSelect: section.element(by.css('#power')),
66+
errorMessages: section.all(by.css('div.alert')),
67+
heroFormButtons: buttons,
68+
heroSubmitted: section.element(by.css('hero-submitted > div'))
69+
};
70+
}
71+
72+
function tests() {
73+
it('should display correct title', function () {
74+
expect(page.title.getText()).toContain('Hero Form');
75+
});
76+
77+
it('should not display submitted message before submit', function () {
78+
expect(page.heroSubmitted.isElementPresent(by.css('h2'))).toBe(false);
79+
});
80+
81+
it('should have form buttons', function () {
82+
expect(page.heroFormButtons.count()).toEqual(2);
83+
});
84+
85+
it('should have error at start', function () {
86+
expectFormIsInvalid();
87+
});
88+
89+
// it('showForm', function () {
90+
// page.form.getInnerHtml().then(html => console.log(html));
91+
// });
92+
93+
it('should have disabled submit button', function () {
94+
expect(page.heroFormButtons.get(0).isEnabled()).toBe(false);
95+
});
96+
97+
it('resetting name to valid name should clear errors', function () {
98+
const ele = page.nameInput;
99+
expect(ele.isPresent()).toBe(true, 'nameInput should exist');
100+
ele.clear();
101+
ele.sendKeys(testName);
102+
expectFormIsValid();
103+
});
104+
105+
it('should produce "required" error after clearing name', function () {
106+
page.nameInput.clear();
107+
// page.alterEgoInput.click(); // to blur ... didn't work
108+
page.nameInput.sendKeys('x', protractor.Key.BACK_SPACE); // ugh!
109+
expect(page.form.getAttribute('class')).toMatch('ng-invalid');
110+
expect(page.errorMessages.get(0).getText()).toContain('required');
111+
});
112+
113+
it('should produce "at least 4 characters" error when name="x"', function () {
114+
page.nameInput.clear();
115+
page.nameInput.sendKeys('x'); // too short
116+
expectFormIsInvalid();
117+
expect(page.errorMessages.get(0).getText()).toContain('at least 4 characters');
118+
});
119+
120+
it('resetting name to valid name again should clear errors', function () {
121+
page.nameInput.sendKeys(testName);
122+
expectFormIsValid();
123+
});
124+
125+
it('should have enabled submit button', function () {
126+
const submitBtn = page.heroFormButtons.get(0);
127+
expect(submitBtn.isEnabled()).toBe(true);
33128
});
34129

35130
it('should hide form after submit', function () {
36-
let alterEgoEle = element.all(by.css('input[ngcontrol=alterEgo]')).get(0);
37-
expect(alterEgoEle.isDisplayed()).toBe(true);
38-
let submitButtonEle = element.all(by.css('button[type=submit]')).get(0);
39-
submitButtonEle.click().then(function() {
40-
expect(alterEgoEle.isDisplayed()).toBe(false);
41-
});
131+
page.heroFormButtons.get(0).click();
132+
expect(page.title.isDisplayed()).toBe(false);
42133
});
43134

44-
it('should reflect submitted data after submit', function () {
45-
let test = 'testing 1 2 3';
46-
let newValue: string;
47-
let alterEgoEle = element.all(by.css('input[ngcontrol=alterEgo]')).get(0);
48-
alterEgoEle.getAttribute('value').then(function(value) {
49-
// alterEgoEle.sendKeys(test);
50-
sendKeys(alterEgoEle, test);
51-
newValue = value + test;
52-
expect(alterEgoEle.getAttribute('value')).toEqual(newValue);
53-
}).then(function() {
54-
let b = element.all(by.css('button[type=submit]')).get(0);
55-
return b.click();
56-
}).then(function() {
57-
let alterEgoTextEle = element(by.cssContainingText('div', 'Alter Ego'));
58-
expect(alterEgoTextEle.isPresent()).toBe(true, 'cannot locate "Alter Ego" label');
59-
let divEle = element(by.cssContainingText('div', newValue));
60-
expect(divEle.isPresent()).toBe(true, 'cannot locate div with this text: ' + newValue);
61-
});
135+
it('submitted form should be displayed', function () {
136+
expect(page.heroSubmitted.isElementPresent(by.css('h2'))).toBe(true);
62137
});
63-
});
64138

139+
it('submitted form should have new hero name', function () {
140+
expect(page.heroSubmitted.getText()).toContain(testName);
141+
});
142+
143+
it('clicking edit button should reveal form again', function () {
144+
const editBtn = page.heroSubmitted.element(by.css('button'));
145+
editBtn.click();
146+
expect(page.heroSubmitted.isElementPresent(by.css('h2')))
147+
.toBe(false, 'submitted hidden again');
148+
expect(page.title.isDisplayed()).toBe(true, 'can see form title');
149+
});
150+
}
151+
152+
function expectFormIsValid() {
153+
expect(page.form.getAttribute('class')).toMatch('ng-valid');
154+
}
155+
156+
function expectFormIsInvalid() {
157+
expect(page.form.getAttribute('class')).toMatch('ng-invalid');
158+
}
159+
160+
function bobTests() {
161+
const emsg = 'Someone named "Bob" cannot be a hero.';
162+
163+
it('should produce "no bob" error after setting name to "Bobby"', function () {
164+
page.nameInput.clear();
165+
page.nameInput.sendKeys('Bobby');
166+
expectFormIsInvalid();
167+
expect(page.errorMessages.get(0).getText()).toBe(emsg);
168+
});
169+
170+
it('should be ok again with valid name', function () {
171+
page.nameInput.clear();
172+
page.nameInput.sendKeys(testName);
173+
expectFormIsValid();
174+
});
175+
}
176+
177+
function makeNameTooLong() {
178+
// make the first name invalid
179+
page.nameInput.sendKeys('ThisHeroNameHasWayWayTooManyLetters');
180+
}

public/docs/_examples/cb-form-validation/ts/app/reactive/hero-form-reactive.component.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ export class HeroFormReactiveComponent implements OnInit {
3838
addHero() {
3939
this.hero = new Hero(42, '', '');
4040
this.buildForm();
41-
this.onValueChanged();
4241
// #enddocregion add-hero
4342
// #enddocregion class
4443

@@ -74,17 +73,19 @@ export class HeroFormReactiveComponent implements OnInit {
7473

7574
this.heroForm.valueChanges
7675
.subscribe(data => this.onValueChanged(data));
76+
77+
this.onValueChanged(); // (re)set validation messages now
7778
}
7879

7980
// #enddocregion form-builder
8081

8182
onValueChanged(data?: any) {
82-
const controls = this.heroForm ? this.heroForm.controls : {};
83+
if (!this.heroForm) { return; }
8384

8485
for (const field in this.formErrors) {
8586
// clear previous error message (if any)
8687
this.formErrors[field] = '';
87-
const control = controls[field];
88+
const control = this.heroForm.get(field);
8889

8990
if (control && control.dirty && !control.valid) {
9091
const messages = this.validationMessages[field];

public/docs/_examples/cb-form-validation/ts/app/template/hero-form-template2.component.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,13 @@ export class HeroFormTemplate2Component implements AfterViewChecked {
6060

6161
// #docregion handler
6262
onValueChanged(data?: any) {
63-
const controls = this.heroForm ? this.heroForm.controls : {};
63+
if (!this.heroForm) { return; }
64+
const form = this.heroForm.form;
6465

6566
for (const field in this.formErrors) {
6667
// clear previous error message (if any)
6768
this.formErrors[field] = '';
68-
const control = controls[field];
69+
const control = form.get(field);
6970

7071
if (control && control.dirty && !control.valid) {
7172
const messages = this.validationMessages[field];
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
.ng-valid[required] {
1+
.ng-valid[required], .ng-valid.required {
22
border-left: 5px solid #42A948; /* green */
33
}
44

5-
.ng-invalid {
5+
.ng-invalid:not(form) {
66
border-left: 5px solid #a94442; /* red */
77
}
8-
9-
.ng-valid.required {
10-
border-left: 5px solid #42A948; /* green */
11-
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/* #docregion */
2-
.ng-valid[required] {
2+
.ng-valid[required], .ng-valid.required {
33
border-left: 5px solid #42A948; /* green */
44
}
55

6-
.ng-invalid {
6+
.ng-invalid:not(form) {
77
border-left: 5px solid #a94442; /* red */
88
}
9-
/* #enddocregion */
9+
/* #enddocregion */

public/docs/ts/latest/cookbook/form-validation.jade

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,9 @@ a#reactive
327327
A real app would retrieve the hero asynchronously from a data service, a task best performed in the `ngOnInit` hook.
328328
:marked
329329
- the `buildForm` method uses the `FormBuilder` (`fb`) to declare the form control model.
330-
Then it attaches the same `onValueChanged` handler to the form.
330+
Then it attaches the same `onValueChanged` handler (there's a one line difference)
331+
to the form's `valueChanged` event and calls it immediately
332+
to set error messages for the new control model.
331333

332334
:marked
333335
#### _FormBuilder_ declaration
@@ -371,9 +373,6 @@ a#reactive
371373
Then it calls `buildForm` again which replaces the previous `heroForm` control model with a new one.
372374
The `<form>` tag's `[formGroup]` binding refreshes the page with the new control model.
373375

374-
Finally, it calls the `onValueChanged` handler to clear previous error messages and reset them
375-
to reflect Angular's validation of the new `hero` object.
376-
377376
Here's the complete reactive component file, compared to the two template-driven component files.
378377
+makeTabs(
379378
`cb-form-validation/ts/app/reactive/hero-form-reactive.component.ts,

0 commit comments

Comments
 (0)