Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: third_party/pkg/angular/test/directive/ng_model_spec.dart

Issue 257423008: Update all Angular libs (run update_all.sh). (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 library ng_model_spec; 1 library ng_model_spec;
2 2
3 import '../_specs.dart'; 3 import '../_specs.dart';
4 import 'dart:html' as dom; 4 import 'dart:html' as dom;
5 5
6 //-----------------------------------------------------------------------------
7 // Utility functions
8
9 /* This function simulates typing the given text into the input field. The
10 * text will be added wherever the insertion point happens to be. This method
11 * has as side-effect to set the focus on the input (without setting the
12 * focus, the text dispatch may not work).
13 */
14 void simulateTypingText(InputElement input, String text) {
15 input..focus()..dispatchEvent(new TextEvent('textInput', data: text));
16 }
17
18 bool simulateTypingTextWithConfirmation(InputElement input, String text,
19 { bool shouldWorkForChrome : true }) {
20 bool result;
21 String val = input.value;
22 try {
23 simulateTypingText(input, text);
24 result = input.value == val + text;
25 } catch (e) {
26 result = false;
27 }
28 if (!result && shouldWorkForChrome) expect(isBrowser('Chrome')).toBeFalsy();
29 return result;
30 }
31
32 bool isBrowser(String pattern) => dom.window.navigator.userAgent.indexOf(pattern ) > 0;
33
34 //-----------------------------------------------------------------------------
35
6 void main() { 36 void main() {
7 describe('ng-model', () { 37 describe('ng-model', () {
8 TestBed _; 38 TestBed _;
9 39
10 beforeEach(module((Module module) { 40 beforeEachModule((Module module) {
11 module..type(ControllerWithNoLove); 41 module
12 })); 42 ..type(ControllerWithNoLove)
43 ..type(MyCustomInputValidator)
44 ..type(CountingValidator);
45 });
13 46
14 beforeEach(inject((TestBed tb) => _ = tb)); 47 beforeEach((TestBed tb) => _ = tb);
15 48
16 describe('type="text" like', () { 49 describe('type="text" like', () {
17 it('should update input value from model', inject(() { 50 it('should update input value from model', () {
18 _.compile('<input type="text" ng-model="model">'); 51 _.compile('<input type="text" ng-model="model">');
19 _.rootScope.apply(); 52 _.rootScope.apply();
20 53
21 expect((_.rootElement as dom.InputElement).value).toEqual(''); 54 expect((_.rootElement as dom.InputElement).value).toEqual('');
22 55
23 _.rootScope.apply('model = "misko"'); 56 _.rootScope.apply('model = "misko"');
24 expect((_.rootElement as dom.InputElement).value).toEqual('misko'); 57 expect((_.rootElement as dom.InputElement).value).toEqual('misko');
25 })); 58 });
26 59
27 it('should render null as the empty string', inject(() { 60 it('should render null as the empty string', () {
28 _.compile('<input type="text" ng-model="model">'); 61 _.compile('<input type="text" ng-model="model">');
29 _.rootScope.apply(); 62 _.rootScope.apply();
30 63
31 expect((_.rootElement as dom.InputElement).value).toEqual(''); 64 expect((_.rootElement as dom.InputElement).value).toEqual('');
32 65
33 _.rootScope.apply('model = null'); 66 _.rootScope.apply('model = null');
34 expect((_.rootElement as dom.InputElement).value).toEqual(''); 67 expect((_.rootElement as dom.InputElement).value).toEqual('');
35 })); 68 });
36 69
37 it('should update model from the input value', inject(() { 70 it('should update model from the input value', () {
38 _.compile('<input type="text" ng-model="model" probe="p">'); 71 _.compile('<input type="text" ng-model="model" probe="p">');
39 Probe probe = _.rootScope.context['p']; 72 Probe probe = _.rootScope.context['p'];
40 var ngModel = probe.directive(NgModel); 73 var ngModel = probe.directive(NgModel);
41 InputElement inputElement = probe.element; 74 InputElement inputElement = probe.element;
42 75
43 inputElement.value = 'abc'; 76 inputElement.value = 'abc';
44 _.triggerEvent(inputElement, 'change'); 77 _.triggerEvent(inputElement, 'change');
45 expect(_.rootScope.context['model']).toEqual('abc'); 78 expect(_.rootScope.context['model']).toEqual('abc');
46 79
47 inputElement.value = 'def'; 80 inputElement.value = 'def';
48 var input = probe.directive(InputTextLikeDirective); 81 var input = probe.directive(InputTextLike);
49 input.processValue(); 82 input.processValue();
50 expect(_.rootScope.context['model']).toEqual('def'); 83 expect(_.rootScope.context['model']).toEqual('def');
51 })); 84 });
52 85
53 it('should update model from the input value for type=number', inject(() { 86 it('should write to input only if the value is different',
87 (Injector i, Animate animate) {
88
89 var scope = _.rootScope;
90 var element = new dom.InputElement();
91 var ngElement = new NgElement(element, scope, animate);
92
93 NodeAttrs nodeAttrs = new NodeAttrs(new DivElement());
94 nodeAttrs['ng-model'] = 'model';
95 var model = new NgModel(scope, ngElement, i.createChild([new Module()]),
96 nodeAttrs, new Animate());
97 dom.querySelector('body').append(element);
98 var input = new InputTextLike(element, model, scope);
99
100 element
101 ..value = 'abc'
102 ..selectionStart = 1
103 ..selectionEnd = 2;
104
105 scope.apply(() {
106 scope.context['model'] = 'abc';
107 });
108
109 expect(element.value).toEqual('abc');
110 // No update. selectionStart/End is unchanged.
111 expect(element.selectionStart).toEqual(1);
112 expect(element.selectionEnd).toEqual(2);
113
114 scope.apply(() {
115 scope.context['model'] = 'xyz';
116 });
117
118 // Value updated. selectionStart/End changed.
119 expect(element.value).toEqual('xyz');
120 expect(element.selectionStart).toEqual(3);
121 expect(element.selectionEnd).toEqual(3);
122 });
123
124 it('should only render the input value upon the next digest', (Scope scope ) {
125 _.compile('<input type="text" ng-model="model" probe="p">');
126 Probe probe = _.rootScope.context['p'];
127 var ngModel = probe.directive(NgModel);
128 InputElement inputElement = probe.element;
129
130 ngModel.render('xyz');
131 scope.context['model'] = 'xyz';
132
133 expect(inputElement.value).not.toEqual('xyz');
134
135 scope.apply();
136
137 expect(inputElement.value).toEqual('xyz');
138 });
139 });
140
141 describe('type="number" or type="range"', () {
142
143 it('should update model from the input value for type=number', () {
54 _.compile('<input type="number" ng-model="model" probe="p">'); 144 _.compile('<input type="number" ng-model="model" probe="p">');
55 Probe probe = _.rootScope.context['p']; 145 Probe probe = _.rootScope.context['p'];
56 var ngModel = probe.directive(NgModel); 146 var ngModel = probe.directive(NgModel);
57 InputElement inputElement = probe.element; 147 InputElement inputElement = probe.element;
58 148
59 inputElement.value = '12'; 149 inputElement.value = '12';
60 _.triggerEvent(inputElement, 'change'); 150 _.triggerEvent(inputElement, 'change');
61 expect(_.rootScope.context['model']).toEqual(12); 151 expect(_.rootScope.context['model']).toEqual(12);
62 152
63 inputElement.value = '14'; 153 inputElement.value = '14';
64 var input = probe.directive(InputNumberLikeDirective); 154 var input = probe.directive(InputNumberLike);
65 input.processValue(); 155 input.processValue();
66 expect(_.rootScope.context['model']).toEqual(14); 156 expect(_.rootScope.context['model']).toEqual(14);
67 })); 157 });
68 158
69 it('should update input type=number to blank when model is null', inject(( ) { 159 it('should update input type=number to blank when model is null', () {
70 _.compile('<input type="number" ng-model="model" probe="p">'); 160 _.compile('<input type="number" ng-model="model" probe="p">');
71 Probe probe = _.rootScope.context['p']; 161 Probe probe = _.rootScope.context['p'];
72 var ngModel = probe.directive(NgModel); 162 var ngModel = probe.directive(NgModel);
73 InputElement inputElement = probe.element; 163 InputElement inputElement = probe.element;
74 164
75 inputElement.value = '12'; 165 inputElement.value = '12';
76 _.triggerEvent(inputElement, 'change'); 166 _.triggerEvent(inputElement, 'change');
77 expect(_.rootScope.context['model']).toEqual(12); 167 expect(_.rootScope.context['model']).toEqual(12);
78 168
79 _.rootScope.context['model'] = null; 169 _.rootScope.context['model'] = null;
80 _.rootScope.apply(); 170 _.rootScope.apply();
81 expect(inputElement.value).toEqual(''); 171 expect(inputElement.value).toEqual('');
82 })); 172 });
83 173
84 it('should write to input only if value is different', inject((Injector i, AstParser parser) { 174 it('should be invalid when the input value results in a NaN value', () {
85 var scope = _.rootScope; 175 _.compile('<input type="number" ng-model="model" probe="p">');
86 var element = new dom.InputElement(); 176 Probe probe = _.rootScope.context['p'];
87 NodeAttrs nodeAttrs = new NodeAttrs(new DivElement()); 177 var ngModel = probe.directive(NgModel);
88 nodeAttrs['ng-model'] = 'model'; 178 InputElement inputElement = probe.element;
89 var model = new NgModel(scope, element, i.createChild([new Module()]), n ew NgNullForm(), parser, nodeAttrs);
90 dom.querySelector('body').append(element);
91 var input = new InputTextLikeDirective(element, model, scope);
92 179
93 element 180 inputElement.value = 'aa';
94 ..value = 'abc' 181 _.triggerEvent(inputElement, 'change');
95 ..selectionStart = 1 182 expect(_.rootScope.context['model'].isNaN).toBe(true);
96 ..selectionEnd = 2; 183 expect(ngModel.valid).toBe(false);
184 });
97 185
98 model.render('abc'); 186 it('should leave input unchanged when text does not represent a valid numb er', (Injector i) {
99
100 expect(element.value).toEqual('abc');
101 // No update. selectionStart/End is unchanged.
102 expect(element.selectionStart).toEqual(1);
103 expect(element.selectionEnd).toEqual(2);
104
105 model.render('xyz');
106
107 // Value updated. selectionStart/End changed.
108 expect(element.value).toEqual('xyz');
109 expect(element.selectionStart).toEqual(3);
110 expect(element.selectionEnd).toEqual(3);
111 }));
112 });
113
114 /* This function simulates typing the given text into the input
115 * field. The text will be added wherever the insertion point
116 * happens to be. This method has as side-effect to set the
117 * focus on the input (without setting the focus the text
118 * dispatch may not work).
119 */
120 void simulateTypingText(InputElement input, String text) {
121 input..focus()..dispatchEvent(new TextEvent('textInput', data: text));
122 }
123
124 describe('type="number" like', () {
125
126 it('should leave input unchanged when text does not represent a valid numb er', inject((Injector i) {
127 var modelFieldName = 'modelForNumFromInvalid1'; 187 var modelFieldName = 'modelForNumFromInvalid1';
128 var element = _.compile('<input type="number" ng-model="$modelFieldName" >'); 188 var element = _.compile('<input type="number" ng-model="$modelFieldName" >');
129 dom.querySelector('body').append(element); 189 dom.querySelector('body').append(element);
130 190
191 if (!simulateTypingTextWithConfirmation(element, '1')) return; // skip t est.
192 element.value = ''; // reset input
193
131 // This test will progressively enter the text '1e1' 194 // This test will progressively enter the text '1e1'
132 // '1' is a valid number. 195 // '1' is a valid number.
133 // '1e' is not a valid number. 196 // '1e' is not a valid number.
134 // '1e1' is again a valid number (with an exponent) 197 // '1e1' is again a valid number (with an exponent)
135 198
136 simulateTypingText(element, '1'); 199 simulateTypingText(element, '1');
137 _.triggerEvent(element, 'change'); 200 _.triggerEvent(element, 'change');
138 expect(element.value).toEqual('1'); 201 expect(element.value).toEqual('1');
139 expect(_.rootScope.context[modelFieldName]).toEqual(1); 202 expect(_.rootScope.context[modelFieldName]).toEqual(1);
140 203
141 simulateTypingText(element, 'e'); 204 simulateTypingText(element, 'e');
142 // Because the text is not a valid number, the element value is empty. 205 // Because the text is not a valid number, the element value is empty.
143 expect(element.value).toEqual(''); 206 expect(element.value).toEqual('');
144 // When the input is invalid, the model is [double.NAN]: 207 // When the input is invalid, the model is [double.NAN]:
145 _.triggerEvent(element, 'change'); 208 _.triggerEvent(element, 'change');
146 expect(_.rootScope.context[modelFieldName].isNaN).toBeTruthy(); 209 expect(_.rootScope.context[modelFieldName].isNaN).toBeTruthy();
147 210
148 simulateTypingText(element, '1'); 211 simulateTypingText(element, '1');
149 _.triggerEvent(element, 'change'); 212 _.triggerEvent(element, 'change');
150 expect(element.value).toEqual('1e1'); 213 expect(element.value).toEqual('1e1');
151 expect(_.rootScope.context[modelFieldName]).toEqual(10); 214 expect(_.rootScope.context[modelFieldName]).toEqual(10);
152 })); 215 });
153 216
154 it('should not reformat user input to equivalent numeric representation', inject((Injector i) { 217 it('should not reformat user input to equivalent numeric representation', (Injector i) {
155 var modelFieldName = 'modelForNumFromInvalid2'; 218 var modelFieldName = 'modelForNumFromInvalid2';
156 var element = _.compile('<input type="number" ng-model="$modelFieldName" >'); 219 var element = _.compile('<input type="number" ng-model="$modelFieldName" >');
157 dom.querySelector('body').append(element); 220 dom.querySelector('body').append(element);
158 221
222 if (!simulateTypingTextWithConfirmation(element, '1')) return; // skip t est.
223 element.value = ''; // reset input
224
159 simulateTypingText(element, '1e-1'); 225 simulateTypingText(element, '1e-1');
160 expect(element.value).toEqual('1e-1'); 226 expect(element.value).toEqual('1e-1');
161 expect(_.rootScope.context[modelFieldName]).toEqual(0.1); 227 expect(_.rootScope.context[modelFieldName]).toEqual(0.1);
162 })); 228 });
163 229
164 it('should update input value from model', inject(() { 230 it('should update input value from model', () {
165 _.compile('<input type="number" ng-model="model">'); 231 _.compile('<input type="number" ng-model="model">');
166 _.rootScope.apply(); 232 _.rootScope.apply();
167 233
168 _.rootScope.apply('model = 42'); 234 _.rootScope.apply('model = 42');
169 expect((_.rootElement as dom.InputElement).value).toEqual('42'); 235 expect((_.rootElement as dom.InputElement).value).toEqual('42');
170 })); 236 });
171 237
172 it('should update input value from model for range inputs', inject(() { 238 it('should update input value from model for range inputs', () {
173 _.compile('<input type="range" ng-model="model">'); 239 _.compile('<input type="range" ng-model="model">');
174 _.rootScope.apply(); 240 _.rootScope.apply();
175 241
176 _.rootScope.apply('model = 42'); 242 _.rootScope.apply('model = 42');
177 expect((_.rootElement as dom.InputElement).value).toEqual('42'); 243 expect((_.rootElement as dom.InputElement).value).toEqual('42');
178 })); 244 });
179 245
180 it('should update model from the input value', inject(() { 246 it('should update model from the input value', () {
181 _.compile('<input type="number" ng-model="model" probe="p">'); 247 _.compile('<input type="number" ng-model="model" probe="p">');
182 Probe probe = _.rootScope.context['p']; 248 Probe probe = _.rootScope.context['p'];
183 var ngModel = probe.directive(NgModel); 249 var ngModel = probe.directive(NgModel);
184 InputElement inputElement = probe.element; 250 InputElement inputElement = probe.element;
185 251
186 inputElement.value = '42'; 252 inputElement.value = '42';
187 _.triggerEvent(inputElement, 'change'); 253 _.triggerEvent(inputElement, 'change');
188 expect(_.rootScope.context['model']).toEqual(42); 254 expect(_.rootScope.context['model']).toEqual(42);
189 255
190 inputElement.value = '43'; 256 inputElement.value = '43';
191 var input = probe.directive(InputNumberLikeDirective); 257 var input = probe.directive(InputNumberLike);
192 input.processValue(); 258 input.processValue();
193 expect(_.rootScope.context['model']).toEqual(43); 259 expect(_.rootScope.context['model']).toEqual(43);
194 })); 260 });
195 261
196 it('should update model to NaN from a blank input value', inject(() { 262 it('should update model to NaN from a blank input value', () {
197 _.compile('<input type="number" ng-model="model" probe="p">'); 263 _.compile('<input type="number" ng-model="model" probe="p">');
198 Probe probe = _.rootScope.context['p']; 264 Probe probe = _.rootScope.context['p'];
199 var ngModel = probe.directive(NgModel); 265 var ngModel = probe.directive(NgModel);
200 InputElement inputElement = probe.element; 266 InputElement inputElement = probe.element;
201 267
202 inputElement.value = ''; 268 inputElement.value = '';
203 _.triggerEvent(inputElement, 'change'); 269 _.triggerEvent(inputElement, 'change');
204 expect(_.rootScope.context['model'].isNaN).toBeTruthy(); 270 expect(_.rootScope.context['model'].isNaN).toBeTruthy();
205 })); 271 });
206 272
207 it('should update model from the input value for range inputs', inject(() { 273 it('should update model from the input value for range inputs', () {
208 _.compile('<input type="range" ng-model="model" probe="p">'); 274 _.compile('<input type="range" ng-model="model" probe="p">');
209 Probe probe = _.rootScope.context['p']; 275 Probe probe = _.rootScope.context['p'];
210 var ngModel = probe.directive(NgModel); 276 var ngModel = probe.directive(NgModel);
211 InputElement inputElement = probe.element; 277 InputElement inputElement = probe.element;
212 278
213 inputElement.value = '42'; 279 inputElement.value = '42';
214 _.triggerEvent(inputElement, 'change'); 280 _.triggerEvent(inputElement, 'change');
215 expect(_.rootScope.context['model']).toEqual(42); 281 expect(_.rootScope.context['model']).toEqual(42);
216 282
217 inputElement.value = '43'; 283 inputElement.value = '43';
218 var input = probe.directive(InputNumberLikeDirective); 284 var input = probe.directive(InputNumberLike);
219 input.processValue(); 285 input.processValue();
220 expect(_.rootScope.context['model']).toEqual(43); 286 expect(_.rootScope.context['model']).toEqual(43);
221 })); 287 });
222 288
223 it('should update model to a native default value from a blank range input value', inject(() { 289 it('should update model to a native default value from a blank range input value', () {
224 _.compile('<input type="range" ng-model="model" probe="p">'); 290 _.compile('<input type="range" ng-model="model" probe="p">');
225 Probe probe = _.rootScope.context['p']; 291 Probe probe = _.rootScope.context['p'];
226 var ngModel = probe.directive(NgModel); 292 var ngModel = probe.directive(NgModel);
227 InputElement inputElement = probe.element; 293 InputElement inputElement = probe.element;
228 294
229 inputElement.value = ''; 295 inputElement.value = '';
230 _.triggerEvent(inputElement, 'change'); 296 _.triggerEvent(inputElement, 'change');
231 expect(_.rootScope.context['model']).toBeDefined(); 297 expect(_.rootScope.context['model']).toBeDefined();
232 })); 298 });
233 299
234 it('should render null as blank', inject(() { 300 it('should render null as blank', () {
235 _.compile('<input type="number" ng-model="model">'); 301 _.compile('<input type="number" ng-model="model">');
236 _.rootScope.apply(); 302 _.rootScope.apply();
237 303
238 _.rootScope.apply('model = null'); 304 _.rootScope.apply('model = null');
239 expect((_.rootElement as dom.InputElement).value).toEqual(''); 305 expect((_.rootElement as dom.InputElement).value).toEqual('');
240 })); 306 });
307
308 it('should only render the input value upon the next digest', (Scope scope ) {
309 _.compile('<input type="number" ng-model="model" probe="p">');
310 Probe probe = _.rootScope.context['p'];
311 var ngModel = probe.directive(NgModel);
312 InputElement inputElement = probe.element;
313
314 ngModel.render(123);
315 scope.context['model'] = 123;
316
317 expect(inputElement.value).not.toEqual('123');
318
319 scope.apply();
320
321 expect(inputElement.value).toEqual('123');
322 });
241 323
242 }); 324 });
243 325
244 describe('type="password"', () { 326 describe('type="password"', () {
245 it('should update input value from model', inject(() { 327 it('should update input value from model', () {
246 _.compile('<input type="password" ng-model="model">'); 328 _.compile('<input type="password" ng-model="model">');
247 _.rootScope.apply(); 329 _.rootScope.apply();
248 330
249 expect((_.rootElement as dom.InputElement).value).toEqual(''); 331 expect((_.rootElement as dom.InputElement).value).toEqual('');
250 332
251 _.rootScope.apply('model = "misko"'); 333 _.rootScope.apply('model = "misko"');
252 expect((_.rootElement as dom.InputElement).value).toEqual('misko'); 334 expect((_.rootElement as dom.InputElement).value).toEqual('misko');
253 })); 335 });
254 336
255 it('should render null as the empty string', inject(() { 337 it('should render null as the empty string', () {
256 _.compile('<input type="password" ng-model="model">'); 338 _.compile('<input type="password" ng-model="model">');
257 _.rootScope.apply(); 339 _.rootScope.apply();
258 340
259 expect((_.rootElement as dom.InputElement).value).toEqual(''); 341 expect((_.rootElement as dom.InputElement).value).toEqual('');
260 342
261 _.rootScope.apply('model = null'); 343 _.rootScope.apply('model = null');
262 expect((_.rootElement as dom.InputElement).value).toEqual(''); 344 expect((_.rootElement as dom.InputElement).value).toEqual('');
263 })); 345 });
264 346
265 it('should update model from the input value', inject(() { 347 it('should update model from the input value', () {
266 _.compile('<input type="password" ng-model="model" probe="p">'); 348 _.compile('<input type="password" ng-model="model" probe="p">');
267 Probe probe = _.rootScope.context['p']; 349 Probe probe = _.rootScope.context['p'];
268 var ngModel = probe.directive(NgModel); 350 var ngModel = probe.directive(NgModel);
269 InputElement inputElement = probe.element; 351 InputElement inputElement = probe.element;
270 352
271 inputElement.value = 'abc'; 353 inputElement.value = 'abc';
272 _.triggerEvent(inputElement, 'change'); 354 _.triggerEvent(inputElement, 'change');
273 expect(_.rootScope.context['model']).toEqual('abc'); 355 expect(_.rootScope.context['model']).toEqual('abc');
274 356
275 inputElement.value = 'def'; 357 inputElement.value = 'def';
276 var input = probe.directive(InputTextLikeDirective); 358 var input = probe.directive(InputTextLike);
277 input.processValue(); 359 input.processValue();
278 expect(_.rootScope.context['model']).toEqual('def'); 360 expect(_.rootScope.context['model']).toEqual('def');
279 361
280 })); 362 });
281 363
282 it('should write to input only if value is different', inject((Injector i, AstParser parser) { 364 it('should write to input only if value is different',
365 (Injector i, Animate animate) {
366
283 var scope = _.rootScope; 367 var scope = _.rootScope;
284 var element = new dom.InputElement(); 368 var element = new dom.InputElement();
369 var ngElement = new NgElement(element, scope, animate);
370
285 NodeAttrs nodeAttrs = new NodeAttrs(new DivElement()); 371 NodeAttrs nodeAttrs = new NodeAttrs(new DivElement());
286 nodeAttrs['ng-model'] = 'model'; 372 nodeAttrs['ng-model'] = 'model';
287 var model = new NgModel(scope, element, i.createChild([new Module()]), n ew NgNullForm(), parser, nodeAttrs); 373 var model = new NgModel(scope, ngElement, i.createChild([new Module()]),
374 nodeAttrs, new Animate());
288 dom.querySelector('body').append(element); 375 dom.querySelector('body').append(element);
289 var input = new InputTextLikeDirective(element, model, scope); 376 var input = new InputTextLike(element, model, scope);
290 377
291 element 378 element
292 ..value = 'abc' 379 ..value = 'abc'
293 ..selectionStart = 1 380 ..selectionStart = 1
294 ..selectionEnd = 2; 381 ..selectionEnd = 2;
295 382
296 model.render('abc'); 383 scope.apply(() {
384 scope.context['model'] = 'abc';
385 });
297 386
298 expect(element.value).toEqual('abc'); 387 expect(element.value).toEqual('abc');
299 expect(element.selectionStart).toEqual(1); 388 expect(element.selectionStart).toEqual(1);
300 expect(element.selectionEnd).toEqual(2); 389 expect(element.selectionEnd).toEqual(2);
301 390
302 model.render('xyz'); 391 scope.apply(() {
392 scope.context['model'] = 'xyz';
393 });
303 394
304 expect(element.value).toEqual('xyz'); 395 expect(element.value).toEqual('xyz');
305 expect(element.selectionStart).toEqual(3); 396 expect(element.selectionStart).toEqual(3);
306 expect(element.selectionEnd).toEqual(3); 397 expect(element.selectionEnd).toEqual(3);
307 })); 398 });
399
400 it('should only render the input value upon the next digest', (Scope scope ) {
401 _.compile('<input type="password" ng-model="model" probe="p">');
402 Probe probe = _.rootScope.context['p'];
403 var ngModel = probe.directive(NgModel);
404 InputElement inputElement = probe.element;
405
406 ngModel.render('xyz');
407 scope.context['model'] = 'xyz';
408
409 expect(inputElement.value).not.toEqual('xyz');
410
411 scope.apply();
412
413 expect(inputElement.value).toEqual('xyz');
414 });
308 }); 415 });
309 416
310 describe('type="search"', () { 417 describe('type="search"', () {
311 it('should update input value from model', inject(() { 418 it('should update input value from model', () {
312 _.compile('<input type="search" ng-model="model">'); 419 _.compile('<input type="search" ng-model="model">');
313 _.rootScope.apply(); 420 _.rootScope.apply();
314 421
315 expect((_.rootElement as dom.InputElement).value).toEqual(''); 422 expect((_.rootElement as dom.InputElement).value).toEqual('');
316 423
317 _.rootScope.apply('model = "misko"'); 424 _.rootScope.apply('model = "misko"');
318 expect((_.rootElement as dom.InputElement).value).toEqual('misko'); 425 expect((_.rootElement as dom.InputElement).value).toEqual('misko');
319 })); 426 });
320 427
321 it('should render null as the empty string', inject(() { 428 it('should render null as the empty string', () {
322 _.compile('<input type="search" ng-model="model">'); 429 _.compile('<input type="search" ng-model="model">');
323 _.rootScope.apply(); 430 _.rootScope.apply();
324 431
325 expect((_.rootElement as dom.InputElement).value).toEqual(''); 432 expect((_.rootElement as dom.InputElement).value).toEqual('');
326 433
327 _.rootScope.apply('model = null'); 434 _.rootScope.apply('model = null');
328 expect((_.rootElement as dom.InputElement).value).toEqual(''); 435 expect((_.rootElement as dom.InputElement).value).toEqual('');
329 })); 436 });
330 437
331 it('should update model from the input value', inject(() { 438 it('should update model from the input value', () {
332 _.compile('<input type="search" ng-model="model" probe="p">'); 439 _.compile('<input type="search" ng-model="model" probe="p">');
333 Probe probe = _.rootScope.context['p']; 440 Probe probe = _.rootScope.context['p'];
334 var ngModel = probe.directive(NgModel); 441 var ngModel = probe.directive(NgModel);
335 InputElement inputElement = probe.element; 442 InputElement inputElement = probe.element;
336 443
337 inputElement.value = 'abc'; 444 inputElement.value = 'abc';
338 _.triggerEvent(inputElement, 'change'); 445 _.triggerEvent(inputElement, 'change');
339 expect(_.rootScope.context['model']).toEqual('abc'); 446 expect(_.rootScope.context['model']).toEqual('abc');
340 447
341 inputElement.value = 'def'; 448 inputElement.value = 'def';
342 var input = probe.directive(InputTextLikeDirective); 449 var input = probe.directive(InputTextLike);
343 input.processValue(); 450 input.processValue();
344 expect(_.rootScope.context['model']).toEqual('def'); 451 expect(_.rootScope.context['model']).toEqual('def');
345 })); 452 });
346 453
347 it('should write to input only if value is different', inject((Injector i, AstParser parser) { 454 it('should write to input only if value is different',
455 (Injector i, Animate animate) {
456
348 var scope = _.rootScope; 457 var scope = _.rootScope;
349 var element = new dom.InputElement(); 458 var element = new dom.InputElement();
459 var ngElement = new NgElement(element, scope, animate);
460
350 NodeAttrs nodeAttrs = new NodeAttrs(new DivElement()); 461 NodeAttrs nodeAttrs = new NodeAttrs(new DivElement());
351 nodeAttrs['ng-model'] = 'model'; 462 nodeAttrs['ng-model'] = 'model';
352 var model = new NgModel(scope, element, i.createChild([new Module()]), n ew NgNullForm(), parser, nodeAttrs); 463 var model = new NgModel(scope, ngElement, i.createChild([new Module()]),
464 nodeAttrs, new Animate());
353 dom.querySelector('body').append(element); 465 dom.querySelector('body').append(element);
354 var input = new InputTextLikeDirective(element, model, scope); 466 var input = new InputTextLike(element, model, scope);
355 467
356 element 468 element
357 ..value = 'abc' 469 ..value = 'abc'
358 ..selectionStart = 1 470 ..selectionStart = 1
359 ..selectionEnd = 2; 471 ..selectionEnd = 2;
360 472
361 model.render('abc'); 473 scope.apply(() {
474 scope.context['model'] = 'abc';
475 });
362 476
363 expect(element.value).toEqual('abc'); 477 expect(element.value).toEqual('abc');
364 // No update. selectionStart/End is unchanged. 478 // No update. selectionStart/End is unchanged.
365 expect(element.selectionStart).toEqual(1); 479 expect(element.selectionStart).toEqual(1);
366 expect(element.selectionEnd).toEqual(2); 480 expect(element.selectionEnd).toEqual(2);
367 481
368 model.render('xyz'); 482 scope.apply(() {
483 scope.context['model'] = 'xyz';
484 });
369 485
370 // Value updated. selectionStart/End changed. 486 // Value updated. selectionStart/End changed.
371 expect(element.value).toEqual('xyz'); 487 expect(element.value).toEqual('xyz');
372 expect(element.selectionStart).toEqual(3); 488 expect(element.selectionStart).toEqual(3);
373 expect(element.selectionEnd).toEqual(3); 489 expect(element.selectionEnd).toEqual(3);
374 })); 490 });
491
492 it('should only render the input value upon the next digest', (Scope scope ) {
493 _.compile('<input type="search" ng-model="model" probe="p">');
494 Probe probe = _.rootScope.context['p'];
495 var ngModel = probe.directive(NgModel);
496 InputElement inputElement = probe.element;
497
498 ngModel.render('xyz');
499 scope.context['model'] = 'xyz';
500
501 expect(inputElement.value).not.toEqual('xyz');
502
503 scope.apply();
504
505 expect(inputElement.value).toEqual('xyz');
506 });
375 }); 507 });
376 508
377 describe('no type attribute', () { 509 describe('no type attribute', () {
378 it('should be set "text" as default value for "type" attribute', inject(() { 510 it('should be set "text" as default value for "type" attribute', () {
379 _.compile('<input ng-model="model">'); 511 _.compile('<input ng-model="model">');
380 _.rootScope.apply(); 512 _.rootScope.apply();
381 expect((_.rootElement as dom.InputElement).attributes['type']).toEqual(' text'); 513 expect((_.rootElement as dom.InputElement).attributes['type']).toEqual(' text');
382 })); 514 });
383 515
384 it('should update input value from model', inject(() { 516 it('should update input value from model', () {
385 _.compile('<input ng-model="model">'); 517 _.compile('<input ng-model="model">');
386 _.rootScope.apply(); 518 _.rootScope.apply();
387 519
388 expect((_.rootElement as dom.InputElement).value).toEqual(''); 520 expect((_.rootElement as dom.InputElement).value).toEqual('');
389 521
390 _.rootScope.apply('model = "misko"'); 522 _.rootScope.apply('model = "misko"');
391 expect((_.rootElement as dom.InputElement).value).toEqual('misko'); 523 expect((_.rootElement as dom.InputElement).value).toEqual('misko');
392 })); 524 });
393 525
394 it('should render null as the empty string', inject(() { 526 it('should render null as the empty string', () {
395 _.compile('<input ng-model="model">'); 527 _.compile('<input ng-model="model">');
396 _.rootScope.apply(); 528 _.rootScope.apply();
397 529
398 expect((_.rootElement as dom.InputElement).value).toEqual(''); 530 expect((_.rootElement as dom.InputElement).value).toEqual('');
399 531
400 _.rootScope.apply('model = null'); 532 _.rootScope.apply('model = null');
401 expect((_.rootElement as dom.InputElement).value).toEqual(''); 533 expect((_.rootElement as dom.InputElement).value).toEqual('');
402 })); 534 });
403 535
404 it('should update model from the input value', inject(() { 536 it('should update model from the input value', () {
405 _.compile('<input ng-model="model" probe="p">'); 537 _.compile('<input ng-model="model" probe="p">');
406 Probe probe = _.rootScope.context['p']; 538 Probe probe = _.rootScope.context['p'];
407 var ngModel = probe.directive(NgModel); 539 var ngModel = probe.directive(NgModel);
408 InputElement inputElement = probe.element; 540 InputElement inputElement = probe.element;
409 541
410 inputElement.value = 'abc'; 542 inputElement.value = 'abc';
411 _.triggerEvent(inputElement, 'change'); 543 _.triggerEvent(inputElement, 'change');
412 expect(_.rootScope.context['model']).toEqual('abc'); 544 expect(_.rootScope.context['model']).toEqual('abc');
413 545
414 inputElement.value = 'def'; 546 inputElement.value = 'def';
415 var input = probe.directive(InputTextLikeDirective); 547 var input = probe.directive(InputTextLike);
416 input.processValue(); 548 input.processValue();
417 expect(_.rootScope.context['model']).toEqual('def'); 549 expect(_.rootScope.context['model']).toEqual('def');
418 })); 550 });
419 551
420 it('should write to input only if value is different', inject((Injector i, AstParser parser) { 552 it('should write to input only if value is different',
553 (Injector i, Animate animate) {
554
421 var scope = _.rootScope; 555 var scope = _.rootScope;
422 var element = new dom.InputElement(); 556 var element = new dom.InputElement();
557 var ngElement = new NgElement(element, scope, animate);
558
423 NodeAttrs nodeAttrs = new NodeAttrs(new DivElement()); 559 NodeAttrs nodeAttrs = new NodeAttrs(new DivElement());
424 nodeAttrs['ng-model'] = 'model'; 560 nodeAttrs['ng-model'] = 'model';
425 var model = new NgModel(scope, element, i.createChild([new Module()]), n ew NgNullForm(), parser, nodeAttrs); 561 var model = new NgModel(scope, ngElement, i.createChild([new Module()]),
562 nodeAttrs, new Animate());
426 dom.querySelector('body').append(element); 563 dom.querySelector('body').append(element);
427 var input = new InputTextLikeDirective(element, model, scope); 564 var input = new InputTextLike(element, model, scope);
428 565
429 element 566 element
430 ..value = 'abc' 567 ..value = 'abc'
431 ..selectionStart = 1 568 ..selectionStart = 1
432 ..selectionEnd = 2; 569 ..selectionEnd = 2;
433 570
434 model.render('abc'); 571 scope.apply(() {
572 scope.context['model'] = 'abc';
573 });
435 574
436 expect(element.value).toEqual('abc'); 575 expect(element.value).toEqual('abc');
437 expect(element.selectionStart).toEqual(1); 576 expect(element.selectionStart).toEqual(1);
438 expect(element.selectionEnd).toEqual(2); 577 expect(element.selectionEnd).toEqual(2);
439 578
440 model.render('xyz'); 579 scope.apply(() {
580 scope.context['model'] = 'xyz';
581 });
441 582
442 expect(element.value).toEqual('xyz'); 583 expect(element.value).toEqual('xyz');
443 expect(element.selectionStart).toEqual(3); 584 expect(element.selectionStart).toEqual(3);
444 expect(element.selectionEnd).toEqual(3); 585 expect(element.selectionEnd).toEqual(3);
445 })); 586 });
587
588 it('should only render the input value upon the next digest', (Scope scope ) {
589 _.compile('<input ng-model="model" probe="p">');
590 Probe probe = _.rootScope.context['p'];
591 var ngModel = probe.directive(NgModel);
592 InputElement inputElement = probe.element;
593
594 ngModel.render('xyz');
595 scope.context['model'] = 'xyz';
596
597 expect(inputElement.value).not.toEqual('xyz');
598
599 scope.apply();
600
601 expect(inputElement.value).toEqual('xyz');
602 });
446 }); 603 });
447 604
448 describe('type="checkbox"', () { 605 describe('type="checkbox"', () {
449 it('should update input value from model', inject((Scope scope) { 606 it('should update input value from model', (Scope scope) {
450 var element = _.compile('<input type="checkbox" ng-model="model">'); 607 var element = _.compile('<input type="checkbox" ng-model="model">');
451 608
452 scope.apply(() { 609 scope.apply(() {
453 scope.context['model'] = true; 610 scope.context['model'] = true;
454 }); 611 });
455 expect(element.checked).toBe(true); 612 expect(element.checked).toBe(true);
456 613
457 scope.apply(() { 614 scope.apply(() {
458 scope.context['model'] = false; 615 scope.context['model'] = false;
459 }); 616 });
460 expect(element.checked).toBe(false); 617 expect(element.checked).toBe(false);
461 })); 618 });
462 619
463 it('should render as dirty when checked', inject((Scope scope) { 620 it('should render as dirty when checked', (Scope scope) {
464 var element = _.compile('<input type="text" ng-model="my_model" probe="i " />'); 621 var element = _.compile('<input type="text" ng-model="my_model" probe="i " />');
465 Probe probe = _.rootScope.context['i']; 622 Probe probe = _.rootScope.context['i'];
466 var model = probe.directive(NgModel); 623 var model = probe.directive(NgModel);
467 624
468 expect(model.pristine).toEqual(true); 625 expect(model.pristine).toEqual(true);
469 expect(model.dirty).toEqual(false); 626 expect(model.dirty).toEqual(false);
470 627
471 _.triggerEvent(element, 'change'); 628 _.triggerEvent(element, 'change');
472 629
473 expect(model.pristine).toEqual(false); 630 expect(model.pristine).toEqual(false);
474 expect(model.dirty).toEqual(true); 631 expect(model.dirty).toEqual(true);
475 })); 632 });
476 633
477 634 it('should update input value from model using ng-true-value/false', (Scop e scope) {
478 it('should update input value from model using ng-true-value/false', injec t((Scope scope) {
479 var element = _.compile('<input type="checkbox" ng-model="model" ng-true -value="1" ng-false-value="0">'); 635 var element = _.compile('<input type="checkbox" ng-model="model" ng-true -value="1" ng-false-value="0">');
480 636
481 scope.apply(() { 637 scope.apply(() {
482 scope.context['model'] = 1; 638 scope.context['model'] = 1;
483 }); 639 });
484 expect(element.checked).toBe(true); 640 expect(element.checked).toBe(true);
485 641
486 scope.apply(() { 642 scope.apply(() {
487 scope.context['model'] = 0; 643 scope.context['model'] = 0;
488 }); 644 });
489 expect(element.checked).toBe(false); 645 expect(element.checked).toBe(false);
490 646
491 element.checked = true; 647 element.checked = true;
492 _.triggerEvent(element, 'change'); 648 _.triggerEvent(element, 'change');
493 expect(scope.context['model']).toBe(1); 649 expect(scope.context['model']).toBe(1);
494 650
495 element.checked = false; 651 element.checked = false;
496 _.triggerEvent(element, 'change'); 652 _.triggerEvent(element, 'change');
497 expect(scope.context['model']).toBe(0); 653 expect(scope.context['model']).toBe(0);
498 })); 654 });
499 655
500 656 it('should allow non boolean values like null, 0, 1', (Scope scope) {
501 it('should allow non boolean values like null, 0, 1', inject((Scope scope) {
502 var element = _.compile('<input type="checkbox" ng-model="model">'); 657 var element = _.compile('<input type="checkbox" ng-model="model">');
503 658
504 scope.apply(() { 659 scope.apply(() {
505 scope.context['model'] = 0; 660 scope.context['model'] = 0;
506 }); 661 });
507 expect(element.checked).toBe(false); 662 expect(element.checked).toBe(false);
508 663
509 scope.apply(() { 664 scope.apply(() {
510 scope.context['model'] = 1; 665 scope.context['model'] = 1;
511 }); 666 });
512 expect(element.checked).toBe(true); 667 expect(element.checked).toBe(true);
513 668
514 scope.apply(() { 669 scope.apply(() {
515 scope.context['model'] = null; 670 scope.context['model'] = null;
516 }); 671 });
517 expect(element.checked).toBe(false); 672 expect(element.checked).toBe(false);
518 })); 673 });
519 674
520 675 it('should update model from the input value', (Scope scope) {
521 it('should update model from the input value', inject((Scope scope) {
522 var element = _.compile('<input type="checkbox" ng-model="model">'); 676 var element = _.compile('<input type="checkbox" ng-model="model">');
523 677
524 element.checked = true; 678 element.checked = true;
525 _.triggerEvent(element, 'change'); 679 _.triggerEvent(element, 'change');
526 expect(scope.context['model']).toBe(true); 680 expect(scope.context['model']).toBe(true);
527 681
528 element.checked = false; 682 element.checked = false;
529 _.triggerEvent(element, 'change'); 683 _.triggerEvent(element, 'change');
530 expect(scope.context['model']).toBe(false); 684 expect(scope.context['model']).toBe(false);
531 })); 685 });
686
687 it('should update model from the input using ng-true-value/false', (Scope scope) {
688 var element = _.compile('<input type="checkbox" ng-model="model" '
689 'ng-true-value="yes" ng-false-value="no">');
690 scope.apply(() {
691 scope.context['yes'] = 'yes sir!';
692 scope.context['no'] = 'no, sorry';
693 });
694
695 element.checked = true;
696 _.triggerEvent(element, 'change');
697 expect(scope.context['model']).toEqual('yes sir!');
698
699 element.checked = false;
700 _.triggerEvent(element, 'change');
701 expect(scope.context['model']).toEqual('no, sorry');
702 });
703
704 it('should only render the input value upon the next digest', (Scope scope ) {
705 _.compile('<input type="checkbox" ng-model="model" probe="p">');
706 Probe probe = _.rootScope.context['p'];
707 var ngModel = probe.directive(NgModel);
708 InputElement inputElement = probe.element;
709
710 ngModel.render('xyz');
711 scope.context['model'] = true;
712
713 expect(inputElement.checked).toBe(false);
714
715 scope.apply();
716
717 expect(inputElement.checked).toBe(true);
718 });
532 }); 719 });
533 720
534 describe('textarea', () { 721 describe('textarea', () {
535 it('should update textarea value from model', inject(() { 722 it('should update textarea value from model', () {
536 _.compile('<textarea ng-model="model">'); 723 _.compile('<textarea ng-model="model">');
537 _.rootScope.apply(); 724 _.rootScope.apply();
538 725
539 expect((_.rootElement as dom.TextAreaElement).value).toEqual(''); 726 expect((_.rootElement as dom.TextAreaElement).value).toEqual('');
540 727
541 _.rootScope.apply('model = "misko"'); 728 _.rootScope.apply('model = "misko"');
542 expect((_.rootElement as dom.TextAreaElement).value).toEqual('misko'); 729 expect((_.rootElement as dom.TextAreaElement).value).toEqual('misko');
543 })); 730 });
544 731
545 it('should render null as the empty string', inject(() { 732 it('should render null as the empty string', () {
546 _.compile('<textarea ng-model="model">'); 733 _.compile('<textarea ng-model="model">');
547 _.rootScope.apply(); 734 _.rootScope.apply();
548 735
549 expect((_.rootElement as dom.TextAreaElement).value).toEqual(''); 736 expect((_.rootElement as dom.TextAreaElement).value).toEqual('');
550 737
551 _.rootScope.apply('model = null'); 738 _.rootScope.apply('model = null');
552 expect((_.rootElement as dom.TextAreaElement).value).toEqual(''); 739 expect((_.rootElement as dom.TextAreaElement).value).toEqual('');
553 })); 740 });
554 741
555 it('should update model from the input value', inject(() { 742 it('should update model from the input value', () {
556 _.compile('<textarea ng-model="model" probe="p">'); 743 _.compile('<textarea ng-model="model" probe="p">');
557 Probe probe = _.rootScope.context['p']; 744 Probe probe = _.rootScope.context['p'];
558 var ngModel = probe.directive(NgModel); 745 var ngModel = probe.directive(NgModel);
559 TextAreaElement element = probe.element; 746 TextAreaElement element = probe.element;
560 747
561 element.value = 'abc'; 748 element.value = 'abc';
562 _.triggerEvent(element, 'change'); 749 _.triggerEvent(element, 'change');
563 expect(_.rootScope.context['model']).toEqual('abc'); 750 expect(_.rootScope.context['model']).toEqual('abc');
564 751
565 element.value = 'def'; 752 element.value = 'def';
566 var textarea = probe.directive(InputTextLikeDirective); 753 var textarea = probe.directive(InputTextLike);
567 textarea.processValue(); 754 textarea.processValue();
568 expect(_.rootScope.context['model']).toEqual('def'); 755 expect(_.rootScope.context['model']).toEqual('def');
569 756
570 })); 757 });
571 758
572 // NOTE(deboer): This test passes on Dartium, but fails in the content_she ll. 759 // NOTE(deboer): This test passes on Dartium, but fails in the content_she ll.
573 // The Dart team is looking into this bug. 760 // The Dart team is looking into this bug.
574 xit('should write to input only if value is different', inject((Injector i , AstParser parser) { 761 xit('should write to input only if value is different',
762 (Injector i, Animate animate) {
763
575 var scope = _.rootScope; 764 var scope = _.rootScope;
576 var element = new dom.TextAreaElement(); 765 var element = new dom.TextAreaElement();
766 var ngElement = new NgElement(element, scope, animate);
767
577 NodeAttrs nodeAttrs = new NodeAttrs(new DivElement()); 768 NodeAttrs nodeAttrs = new NodeAttrs(new DivElement());
578 nodeAttrs['ng-model'] = 'model'; 769 nodeAttrs['ng-model'] = 'model';
579 var model = new NgModel(scope, element, i.createChild([new Module()]), n ew NgNullForm(), parser, nodeAttrs); 770 var model = new NgModel(scope, ngElement, i.createChild([new Module()]),
771 nodeAttrs, new Animate());
580 dom.querySelector('body').append(element); 772 dom.querySelector('body').append(element);
581 var input = new InputTextLikeDirective(element, model, scope); 773 var input = new InputTextLike(element, model, scope);
582 774
583 element 775 element
584 ..value = 'abc' 776 ..value = 'abc'
585 ..selectionStart = 1 777 ..selectionStart = 1
586 ..selectionEnd = 2; 778 ..selectionEnd = 2;
587 779
588 model.render('abc'); 780 model.render('abc');
589 781
590 expect(element.value).toEqual('abc'); 782 expect(element.value).toEqual('abc');
591 expect(element.selectionStart).toEqual(1); 783 expect(element.selectionStart).toEqual(1);
592 expect(element.selectionEnd).toEqual(2); 784 expect(element.selectionEnd).toEqual(2);
593 785
594 model.render('xyz'); 786 model.render('xyz');
595 787
596 // Setting the value on a textarea doesn't update the selection the way it 788 // Setting the value on a textarea doesn't update the selection the way it
597 // does on input elements. This stays unchanged. 789 // does on input elements. This stays unchanged.
598 expect(element.value).toEqual('xyz'); 790 expect(element.value).toEqual('xyz');
599 expect(element.selectionStart).toEqual(0); 791 expect(element.selectionStart).toEqual(0);
600 expect(element.selectionEnd).toEqual(0); 792 expect(element.selectionEnd).toEqual(0);
601 })); 793 });
794
795 it('should only render the input value upon the next digest', (Scope scope ) {
796 _.compile('<textarea ng-model="model" probe="p"></textarea>');
797 Probe probe = _.rootScope.context['p'];
798 var ngModel = probe.directive(NgModel);
799 TextAreaElement inputElement = probe.element;
800
801 ngModel.render('xyz');
802 scope.context['model'] = 'xyz';
803
804 expect(inputElement.value).not.toEqual('xyz');
805
806 scope.apply();
807
808 expect(inputElement.value).toEqual('xyz');
809 });
602 }); 810 });
603 811
604 describe('type="radio"', () { 812 describe('type="radio"', () {
605 it('should update input value from model', inject(() { 813 it('should update input value from model', () {
606 _.compile('<input type="radio" name="color" value="red" ng-model="color" probe="r">' + 814 _.compile('<input type="radio" name="color" value="red" ng-model="color" probe="r">' +
607 '<input type="radio" name="color" value="green" ng-model="colo r" probe="g">' + 815 '<input type="radio" name="color" value="green" ng-model="colo r" probe="g">' +
608 '<input type="radio" name="color" value="blue" ng-model="color " probe="b">'); 816 '<input type="radio" name="color" value="blue" ng-model="color " probe="b">');
609 _.rootScope.apply(); 817 _.rootScope.apply();
610 818
611 RadioButtonInputElement redBtn = _.rootScope.context['r'].element; 819 RadioButtonInputElement redBtn = _.rootScope.context['r'].element;
612 RadioButtonInputElement greenBtn = _.rootScope.context['g'].element; 820 RadioButtonInputElement greenBtn = _.rootScope.context['g'].element;
613 RadioButtonInputElement blueBtn = _.rootScope.context['b'].element; 821 RadioButtonInputElement blueBtn = _.rootScope.context['b'].element;
614 822
615 expect(redBtn.checked).toBe(false); 823 expect(redBtn.checked).toBe(false);
(...skipping 20 matching lines...) Expand all
636 expect(_.rootScope.context['color']).toEqual('red'); 844 expect(_.rootScope.context['color']).toEqual('red');
637 expect(redBtn.checked).toBe(true); 845 expect(redBtn.checked).toBe(true);
638 expect(greenBtn.checked).toBe(false); 846 expect(greenBtn.checked).toBe(false);
639 expect(blueBtn.checked).toBe(false); 847 expect(blueBtn.checked).toBe(false);
640 848
641 _.triggerEvent(greenBtn, 'click'); 849 _.triggerEvent(greenBtn, 'click');
642 expect(_.rootScope.context['color']).toEqual('green'); 850 expect(_.rootScope.context['color']).toEqual('green');
643 expect(redBtn.checked).toBe(false); 851 expect(redBtn.checked).toBe(false);
644 expect(greenBtn.checked).toBe(true); 852 expect(greenBtn.checked).toBe(true);
645 expect(blueBtn.checked).toBe(false); 853 expect(blueBtn.checked).toBe(false);
646 })); 854 });
647 855
648 it('should support ng-value', () { 856 it('should support ng-value', () {
649 _.compile('<input type="radio" name="color" ng-value="red" ng-model="col or" probe="r">' + 857 _.compile('<input type="radio" name="color" ng-value="red" ng-model="col or" probe="r">' +
650 '<input type="radio" name="color" ng-value="green" ng-model="c olor" probe="g">' + 858 '<input type="radio" name="color" ng-value="green" ng-model="c olor" probe="g">' +
651 '<input type="radio" name="color" ng-value="blue" ng-model="co lor" probe="b">'); 859 '<input type="radio" name="color" ng-value="blue" ng-model="co lor" probe="b">');
652 860
653 var red = {'name': 'RED'}; 861 var red = {'name': 'RED'};
654 var green = {'name': 'GREEN'}; 862 var green = {'name': 'GREEN'};
655 var blue = {'name': 'BLUE'}; 863 var blue = {'name': 'BLUE'};
656 _.rootScope.context 864 _.rootScope.context
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
692 expect(greenBtn.checked).toBe(false); 900 expect(greenBtn.checked).toBe(false);
693 expect(blueBtn.checked).toBe(false); 901 expect(blueBtn.checked).toBe(false);
694 902
695 _.triggerEvent(greenBtn, 'click'); 903 _.triggerEvent(greenBtn, 'click');
696 expect(_.rootScope.context['color']).toEqual(green); 904 expect(_.rootScope.context['color']).toEqual(green);
697 expect(redBtn.checked).toBe(false); 905 expect(redBtn.checked).toBe(false);
698 expect(greenBtn.checked).toBe(true); 906 expect(greenBtn.checked).toBe(true);
699 expect(blueBtn.checked).toBe(false); 907 expect(blueBtn.checked).toBe(false);
700 }); 908 });
701 909
702 it('should render as dirty when checked', inject((Scope scope) { 910 it('should render as dirty when checked', (Scope scope) {
703 var element = _.compile( 911 var element = _.compile(
704 '<div>' + 912 '<div>' +
705 ' <input type="radio" id="on" ng-model="my_model" probe="i" value="on " />' + 913 ' <input type="radio" id="on" ng-model="my_model" probe="i" value="on " />' +
706 ' <input type="radio" id="off" ng-model="my_model" probe="j" value="o ff" />' + 914 ' <input type="radio" id="off" ng-model="my_model" probe="j" value="o ff" />' +
707 '</div>' 915 '</div>'
708 ); 916 );
709 Probe probe = _.rootScope.context['i']; 917 Probe probe = _.rootScope.context['i'];
710 918
711 var model = probe.directive(NgModel); 919 var model = probe.directive(NgModel);
712 920
713 var input1 = element.querySelector("#on"); 921 var input1 = element.querySelector("#on");
714 var input2 = element.querySelector("#off"); 922 var input2 = element.querySelector("#off");
715 923
924 scope.apply();
925
716 expect(model.pristine).toEqual(true); 926 expect(model.pristine).toEqual(true);
717 expect(model.dirty).toEqual(false); 927 expect(model.dirty).toEqual(false);
718 928
719 expect(input1.classes.contains("ng-dirty")).toBe(false); 929 expect(input1.classes.contains("ng-dirty")).toBe(false);
720 expect(input2.classes.contains("ng-dirty")).toBe(false); 930 expect(input2.classes.contains("ng-dirty")).toBe(false);
721 expect(input1.classes.contains("ng-pristine")).toBe(true); 931 expect(input1.classes.contains("ng-pristine")).toBe(true);
722 expect(input1.classes.contains("ng-pristine")).toBe(true); 932 expect(input1.classes.contains("ng-pristine")).toBe(true);
723 933
724 input1.checked = true; 934 input1.checked = true;
725 _.triggerEvent(input1, 'click'); 935 _.triggerEvent(input1, 'click');
936 scope.apply();
726 937
727 expect(model.pristine).toEqual(false); 938 expect(model.pristine).toEqual(false);
728 expect(model.dirty).toEqual(true); 939 expect(model.dirty).toEqual(true);
729 940
730 input1.checked = false; 941 input1.checked = false;
731 input2.checked = true; 942 input2.checked = true;
732 _.triggerEvent(input2, 'click'); 943 _.triggerEvent(input2, 'click');
944 scope.apply();
733 945
734 expect(input1.classes.contains("ng-dirty")).toBe(true); 946 expect(input1.classes.contains("ng-dirty")).toBe(true);
735 expect(input2.classes.contains("ng-dirty")).toBe(true); 947 expect(input2.classes.contains("ng-dirty")).toBe(true);
736 expect(input1.classes.contains("ng-pristine")).toBe(false); 948 expect(input1.classes.contains("ng-pristine")).toBe(false);
737 expect(input1.classes.contains("ng-pristine")).toBe(false); 949 expect(input1.classes.contains("ng-pristine")).toBe(false);
738 })); 950 });
951
952 it('should only render the input value upon the next digest', (Scope scope ) {
953 var element = _.compile(
954 '<div>' +
955 ' <input type="radio" id="on" ng-model="model" probe="i" value="on" / >' +
956 ' <input type="radio" id="off" ng-model="model" probe="j" value="off" />' +
957 '</div>'
958 );
959
960 Probe probe1 = _.rootScope.context['i'];
961 var ngModel1 = probe1.directive(NgModel);
962 InputElement inputElement1 = probe1.element;
963
964 Probe probe2 = _.rootScope.context['j'];
965 var ngModel2 = probe2.directive(NgModel);
966 InputElement inputElement2 = probe2.element;
967
968 ngModel1.render('on');
969 scope.context['model'] = 'on';
970
971 expect(inputElement1.checked).toBe(false);
972 expect(inputElement2.checked).toBe(false);
973
974 scope.apply();
975
976 expect(inputElement1.checked).toBe(true);
977 expect(inputElement2.checked).toBe(false);
978 });
739 }); 979 });
740 980
741 describe('type="search"', () { 981 describe('type="search"', () {
742 it('should update input value from model', inject(() { 982 it('should update input value from model', () {
743 _.compile('<input type="search" ng-model="model">'); 983 _.compile('<input type="search" ng-model="model">');
744 _.rootScope.apply(); 984 _.rootScope.apply();
745 985
746 expect((_.rootElement as dom.InputElement).value).toEqual(''); 986 expect((_.rootElement as dom.InputElement).value).toEqual('');
747 987
748 _.rootScope.apply('model = "matias"'); 988 _.rootScope.apply('model = "matias"');
749 expect((_.rootElement as dom.InputElement).value).toEqual('matias'); 989 expect((_.rootElement as dom.InputElement).value).toEqual('matias');
750 })); 990 });
751 991
752 it('should render null as the empty string', inject(() { 992 it('should render null as the empty string', () {
753 _.compile('<input type="search" ng-model="model">'); 993 _.compile('<input type="search" ng-model="model">');
754 _.rootScope.apply(); 994 _.rootScope.apply();
755 995
756 expect((_.rootElement as dom.InputElement).value).toEqual(''); 996 expect((_.rootElement as dom.InputElement).value).toEqual('');
757 997
758 _.rootScope.apply('model = null'); 998 _.rootScope.apply('model = null');
759 expect((_.rootElement as dom.InputElement).value).toEqual(''); 999 expect((_.rootElement as dom.InputElement).value).toEqual('');
760 })); 1000 });
761 1001
762 it('should update model from the input value', inject(() { 1002 it('should update model from the input value', () {
763 _.compile('<input type="search" ng-model="model" probe="p">'); 1003 _.compile('<input type="search" ng-model="model" probe="p">');
764 Probe probe = _.rootScope.context['p']; 1004 Probe probe = _.rootScope.context['p'];
765 var ngModel = probe.directive(NgModel); 1005 var ngModel = probe.directive(NgModel);
766 InputElement inputElement = probe.element; 1006 InputElement inputElement = probe.element;
767 1007
768 inputElement.value = 'xzy'; 1008 inputElement.value = 'xzy';
769 _.triggerEvent(inputElement, 'change'); 1009 _.triggerEvent(inputElement, 'change');
770 expect(_.rootScope.context['model']).toEqual('xzy'); 1010 expect(_.rootScope.context['model']).toEqual('xzy');
771 1011
772 inputElement.value = '123'; 1012 inputElement.value = '123';
773 var input = probe.directive(InputTextLikeDirective); 1013 var input = probe.directive(InputTextLike);
774 input.processValue(); 1014 input.processValue();
775 expect(_.rootScope.context['model']).toEqual('123'); 1015 expect(_.rootScope.context['model']).toEqual('123');
776 })); 1016 });
1017
1018 it('should only render the input value upon the next digest', (Scope scope ) {
1019 _.compile('<input type="search" ng-model="model" probe="p">');
1020 Probe probe = _.rootScope.context['p'];
1021 var ngModel = probe.directive(NgModel);
1022 InputElement inputElement = probe.element;
1023
1024 ngModel.render('xyz');
1025 scope.context['model'] = 'xyz';
1026
1027 expect(inputElement.value).not.toEqual('xyz');
1028
1029 scope.apply();
1030
1031 expect(inputElement.value).toEqual('xyz');
1032 });
777 }); 1033 });
778 1034
779 describe('contenteditable', () { 1035 describe('contenteditable', () {
780 it('should update content from model', inject(() { 1036 it('should update content from model', () {
781 _.compile('<p contenteditable ng-model="model">'); 1037 _.compile('<p contenteditable ng-model="model">');
782 _.rootScope.apply(); 1038 _.rootScope.apply();
783 1039
784 expect(_.rootElement.text).toEqual(''); 1040 expect(_.rootElement.text).toEqual('');
785 1041
786 _.rootScope.apply('model = "misko"'); 1042 _.rootScope.apply('model = "misko"');
787 expect(_.rootElement.text).toEqual('misko'); 1043 expect(_.rootElement.text).toEqual('misko');
788 })); 1044 });
789 1045
790 it('should update model from the input value', inject(() { 1046 it('should update model from the input value', () {
791 _.compile('<p contenteditable ng-model="model">'); 1047 _.compile('<p contenteditable ng-model="model">');
792 Element element = _.rootElement; 1048 Element element = _.rootElement;
793 1049
794 element.innerHtml = 'abc'; 1050 element.innerHtml = 'abc';
795 _.triggerEvent(element, 'change'); 1051 _.triggerEvent(element, 'change');
796 expect(_.rootScope.context['model']).toEqual('abc'); 1052 expect(_.rootScope.context['model']).toEqual('abc');
797 1053
798 element.innerHtml = 'def'; 1054 element.innerHtml = 'def';
799 var input = ngInjector(element).get(ContentEditableDirective); 1055 var input = ngInjector(element).get(ContentEditable);
800 input.processValue(); 1056 input.processValue();
801 expect(_.rootScope.context['model']).toEqual('def'); 1057 expect(_.rootScope.context['model']).toEqual('def');
802 })); 1058 });
1059
1060 it('should only render the input value upon the next digest', (Scope scope ) {
1061 _.compile('<div contenteditable ng-model="model" probe="p"></div>');
1062 Probe probe = _.rootScope.context['p'];
1063 var ngModel = probe.directive(NgModel);
1064 Element element = probe.element;
1065
1066 ngModel.render('xyz');
1067 scope.context['model'] = 'xyz';
1068
1069 expect(element.innerHtml).not.toEqual('xyz');
1070
1071 scope.apply();
1072
1073 expect(element.innerHtml).toEqual('xyz');
1074 });
803 }); 1075 });
804 1076
805 describe('pristine / dirty', () { 1077 describe('pristine / dirty', () {
806 it('should be set to pristine by default', inject((Scope scope) { 1078 it('should be set to pristine by default', (Scope scope) {
807 _.compile('<input type="text" ng-model="my_model" probe="i" />'); 1079 _.compile('<input type="text" ng-model="my_model" probe="i" />');
808 Probe probe = _.rootScope.context['i']; 1080 Probe probe = _.rootScope.context['i'];
809 var model = probe.directive(NgModel); 1081 var model = probe.directive(NgModel);
810 1082
811 expect(model.pristine).toEqual(true); 1083 expect(model.pristine).toEqual(true);
812 expect(model.dirty).toEqual(false); 1084 expect(model.dirty).toEqual(false);
813 })); 1085 });
814 1086
815 it('should add and remove the correct CSS classes when set to dirty and to pristine', inject((Scope scope) { 1087 it('should add and remove the correct CSS classes when set to dirty and to pristine', (Scope scope) {
816 _.compile('<input type="text" ng-model="my_model" probe="i" />'); 1088 _.compile('<input type="text" ng-model="my_model" probe="i" />');
817 Probe probe = _.rootScope.context['i']; 1089 Probe probe = _.rootScope.context['i'];
818 NgModel model = probe.directive(NgModel); 1090 NgModel model = probe.directive(NgModel);
819 InputElement element = probe.element; 1091 InputElement element = probe.element;
820 1092
821 model.dirty = true; 1093 model.addInfo('ng-dirty');
1094 scope.apply();
1095
822 expect(model.pristine).toEqual(false); 1096 expect(model.pristine).toEqual(false);
823 expect(model.dirty).toEqual(true); 1097 expect(model.dirty).toEqual(true);
824 expect(element.classes.contains('ng-pristine')).toBe(false); 1098 expect(element).not.toHaveClass('ng-pristine');
825 expect(element.classes.contains('ng-dirty')).toBe(true); 1099 expect(element).toHaveClass('ng-dirty');
826 1100
827 model.pristine = true; 1101 model.removeInfo('ng-dirty');
1102 scope.apply();
1103
828 expect(model.pristine).toEqual(true); 1104 expect(model.pristine).toEqual(true);
829 expect(model.dirty).toEqual(false); 1105 expect(model.dirty).toEqual(false);
830 expect(element.classes.contains('ng-pristine')).toBe(true); 1106 expect(element).toHaveClass('ng-pristine');
831 expect(element.classes.contains('ng-dirty')).toBe(false); 1107 expect(element).not.toHaveClass('ng-dirty');
832 })); 1108 });
833 1109
834 it('should render the parent form/fieldset as dirty but not the other mode ls', inject((Scope scope) { 1110 // TODO(matias): figure out why the 2nd apply is optional
1111 it('should render the parent form/fieldset as dirty but not the other mode ls',
1112 (Scope scope) {
1113
835 _.compile('<form name="myForm">' + 1114 _.compile('<form name="myForm">' +
836 ' <fieldset name="myFieldset">' + 1115 ' <fieldset name="myFieldset">' +
837 ' <input type="text" ng-model="my_model1" probe="myModel1" />' + 1116 ' <input type="text" ng-model="my_model1" probe="myModel1" />' +
838 ' <input type="text" ng-model="my_model2" probe="myModel2" />' + 1117 ' <input type="text" ng-model="my_model2" probe="myModel2" />' +
839 ' </fieldset>' + 1118 ' </fieldset>' +
840 '</form>'); 1119 '</form>');
841 1120
1121 var formElement = _.rootScope.context['myForm'].element.node;
1122 var fieldsetElement = _.rootScope.context['myFieldset'].element.node;
842 var inputElement1 = _.rootScope.context['myModel1'].element; 1123 var inputElement1 = _.rootScope.context['myModel1'].element;
843 var inputElement2 = _.rootScope.context['myModel2'].element; 1124 var inputElement2 = _.rootScope.context['myModel2'].element;
844 var formElement = _.rootScope.context['myForm'].element;
845 var fieldsetElement = _.rootScope.context['myFieldset'].element;
846 1125
847 expect(formElement.classes.contains('ng-pristine')).toBe(true); 1126 scope.apply();
848 expect(formElement.classes.contains('ng-dirty')).toBe(false);
849 1127
850 expect(fieldsetElement.classes.contains('ng-pristine')).toBe(true); 1128 expect(formElement).toHaveClass('ng-pristine');
851 expect(fieldsetElement.classes.contains('ng-dirty')).toBe(false); 1129 expect(formElement).not.toHaveClass('ng-dirty');
852 1130
853 expect(inputElement1.classes.contains('ng-pristine')).toBe(true); 1131 expect(fieldsetElement).toHaveClass('ng-pristine');
854 expect(inputElement1.classes.contains('ng-dirty')).toBe(false); 1132 expect(fieldsetElement).not.toHaveClass('ng-dirty');
855 1133
856 expect(inputElement2.classes.contains('ng-pristine')).toBe(true); 1134 expect(inputElement1).toHaveClass('ng-pristine');
857 expect(inputElement2.classes.contains('ng-dirty')).toBe(false); 1135 expect(inputElement1).not.toHaveClass('ng-dirty');
1136
1137 expect(inputElement2).toHaveClass('ng-pristine');
1138 expect(inputElement2).not.toHaveClass('ng-dirty');
858 1139
859 inputElement1.value = '...hi...'; 1140 inputElement1.value = '...hi...';
860 _.triggerEvent(inputElement1, 'change'); 1141 _.triggerEvent(inputElement1, 'change');
1142 scope.apply();
861 1143
862 expect(formElement.classes.contains('ng-pristine')).toBe(false); 1144 expect(formElement).not.toHaveClass('ng-pristine');
863 expect(formElement.classes.contains('ng-dirty')).toBe(true); 1145 expect(formElement).toHaveClass('ng-dirty');
864 1146
865 expect(fieldsetElement.classes.contains('ng-pristine')).toBe(false); 1147 expect(fieldsetElement).not.toHaveClass('ng-pristine');
866 expect(fieldsetElement.classes.contains('ng-dirty')).toBe(true); 1148 expect(fieldsetElement).toHaveClass('ng-dirty');
867 1149
868 expect(inputElement1.classes.contains('ng-pristine')).toBe(false); 1150 expect(inputElement1).not.toHaveClass('ng-pristine');
869 expect(inputElement1.classes.contains('ng-dirty')).toBe(true); 1151 expect(inputElement1).toHaveClass('ng-dirty');
870 1152
871 expect(inputElement2.classes.contains('ng-pristine')).toBe(true); 1153 expect(inputElement2).toHaveClass('ng-pristine');
872 expect(inputElement2.classes.contains('ng-dirty')).toBe(false); 1154 expect(inputElement2).not.toHaveClass('ng-dirty');
873 })); 1155 });
874 }); 1156 });
875 1157
876 describe('validation', () { 1158 describe('validation', () {
877 it('should happen automatically when the scope changes', inject((Scope sco pe) { 1159 it('should happen automatically when the scope changes', (Scope scope) {
878 _.compile('<input type="text" ng-model="model" probe="i" required>'); 1160 _.compile('<input type="text" ng-model="model" probe="i" required>');
879 _.rootScope.apply(); 1161 _.rootScope.apply();
880 1162
881 Probe probe = _.rootScope.context['i']; 1163 Probe probe = _.rootScope.context['i'];
882 var model = probe.directive(NgModel); 1164 var model = probe.directive(NgModel);
883 1165
884 expect(model.invalid).toBe(true); 1166 expect(model.invalid).toBe(true);
885 expect(model.valid).toBe(false); 1167 expect(model.valid).toBe(false);
886 1168
887 _.rootScope.apply('model = "viljami"'); 1169 _.rootScope.apply('model = "viljami"');
888 1170
889 expect(model.invalid).toBe(false); 1171 expect(model.invalid).toBe(false);
890 expect(model.valid).toBe(true); 1172 expect(model.valid).toBe(true);
891 })); 1173 });
892 1174
893 it('should happen automatically upon user input via the onInput event', in ject(() { 1175 it('should happen automatically upon user input via the onInput event', () {
894 _.compile('<input type="text" ng-model="model" probe="i" required>'); 1176 _.compile('<input type="text" ng-model="model" probe="i" required>');
1177 _.rootScope.apply();
895 1178
896 Probe probe = _.rootScope.context['i']; 1179 Probe probe = _.rootScope.context['i'];
897 var model = probe.directive(NgModel); 1180 var model = probe.directive(NgModel);
898 InputElement inputElement = model.element; 1181 InputElement inputElement = model.element.node;
899 1182
900 expect(model.invalid).toBe(true); 1183 expect(model.invalid).toBe(true);
901 expect(model.valid).toBe(false); 1184 expect(model.valid).toBe(false);
902 1185
903 inputElement.value = 'some value'; 1186 inputElement.value = 'some value';
904 _.triggerEvent(inputElement, 'input'); 1187 _.triggerEvent(inputElement, 'input');
905 1188
906 expect(model.invalid).toBe(false); 1189 expect(model.invalid).toBe(false);
907 expect(model.valid).toBe(true); 1190 expect(model.valid).toBe(true);
908 })); 1191 });
909 }); 1192 });
910 1193
911 describe('valid / invalid', () { 1194 describe('valid / invalid', () {
912 it('should add and remove the correct flags when set to valid and to inval id', inject((Scope scope) { 1195 it('should add and remove the correct flags when set to valid and to inval id', (Scope scope) {
913 _.compile('<input type="text" ng-model="my_model" probe="i" />'); 1196 _.compile('<input type="text" ng-model="my_model" probe="i" />');
914 Probe probe = _.rootScope.context['i']; 1197 Probe probe = _.rootScope.context['i'];
915 var model = probe.directive(NgModel); 1198 var model = probe.directive(NgModel);
916 InputElement element = probe.element; 1199 InputElement element = probe.element;
917 1200
918 model.invalid = true; 1201 model.addError('ng-required');
1202 model.validate();
1203 scope.apply();
1204
919 expect(model.valid).toEqual(false); 1205 expect(model.valid).toEqual(false);
920 expect(model.invalid).toEqual(true); 1206 expect(model.invalid).toEqual(true);
921 expect(element.classes.contains('ng-valid')).toBe(false); 1207 //expect(element).not.toHaveClass('ng-valid');
922 expect(element.classes.contains('ng-invalid')).toBe(true); 1208 expect(element).toHaveClass('ng-invalid');
923 1209
924 model.valid = true; 1210 model.removeError('ng-required');
1211 model.validate();
1212 scope.apply();
1213
925 expect(model.valid).toEqual(true); 1214 expect(model.valid).toEqual(true);
926 expect(model.invalid).toEqual(false); 1215 expect(model.invalid).toEqual(false);
927 expect(element.classes.contains('ng-invalid')).toBe(false); 1216 expect(element).not.toHaveClass('ng-invalid');
928 expect(element.classes.contains('ng-valid')).toBe(true); 1217 // expect(element).toHaveClass('ng-valid');
929 })); 1218 });
930 1219
931 it('should set the validity with respect to all existing validations when setValidity() is used', inject((Scope scope) { 1220 it('should set the validity with respect to all existing validations when setValidity() is used', (Scope scope) {
932 _.compile('<input type="text" ng-model="my_model" probe="i" />'); 1221 _.compile('<input type="text" ng-model="my_model" probe="i" />');
933 Probe probe = _.rootScope.context['i']; 1222 Probe probe = _.rootScope.context['i'];
934 var model = probe.directive(NgModel); 1223 var model = probe.directive(NgModel);
935 1224
936 model.setValidity("required", false); 1225 model.addError("required");
937 expect(model.valid).toEqual(false); 1226 expect(model.valid).toEqual(false);
938 expect(model.invalid).toEqual(true); 1227 expect(model.invalid).toEqual(true);
939 1228
940 model.setValidity("format", false); 1229 model.addError("format");
941 expect(model.valid).toEqual(false); 1230 expect(model.valid).toEqual(false);
942 expect(model.invalid).toEqual(true); 1231 expect(model.invalid).toEqual(true);
943 1232
944 model.setValidity("format", true); 1233 model.removeError("format");
945 expect(model.valid).toEqual(false); 1234 expect(model.valid).toEqual(false);
946 expect(model.invalid).toEqual(true); 1235 expect(model.invalid).toEqual(true);
947 1236
948 model.setValidity("required", true); 1237 model.removeError("required");
949 expect(model.valid).toEqual(true); 1238 expect(model.valid).toEqual(true);
950 expect(model.invalid).toEqual(false); 1239 expect(model.invalid).toEqual(false);
951 })); 1240 });
952 1241
953 it('should register each error only once when invalid', inject((Scope scop e) { 1242 it('should register each error only once when invalid', (Scope scope) {
954 _.compile('<input type="text" ng-model="my_model" probe="i" />'); 1243 _.compile('<input type="text" ng-model="my_model" probe="i" />');
955 Probe probe = _.rootScope.context['i']; 1244 Probe probe = _.rootScope.context['i'];
956 var model = probe.directive(NgModel); 1245 var model = probe.directive(NgModel);
957 1246
958 model.setValidity("distinct-error", false); 1247 model.addError("distinct-error");
959 expect(model.valid).toEqual(false); 1248 expect(model.valid).toEqual(false);
960 expect(model.invalid).toEqual(true); 1249 expect(model.invalid).toEqual(true);
961 1250
962 model.setValidity("distinct-error", false); 1251 model.addError("distinct-error");
963 expect(model.valid).toEqual(false); 1252 expect(model.valid).toEqual(false);
964 expect(model.invalid).toEqual(true); 1253 expect(model.invalid).toEqual(true);
965 1254
966 model.setValidity("distinct-error", true); 1255 model.removeError("distinct-error");
967 expect(model.valid).toEqual(true); 1256 expect(model.valid).toEqual(true);
968 expect(model.invalid).toEqual(false); 1257 expect(model.invalid).toEqual(false);
969 })); 1258 });
1259 });
1260
1261 describe('error handling', () {
1262 it('should return true or false depending on if an error exists on a form' ,
1263 (Scope scope, TestBed _) {
1264
1265 _.compile('<input type="text" ng-model="input" name="input" probe="i" /> ');
1266 scope.apply();
1267
1268 Probe p = scope.context['i'];
1269 NgModel model = p.directive(NgModel);
1270
1271 expect(model.hasErrorState('big-failure')).toBe(false);
1272
1273 model.addError("big-failure");
1274
1275 expect(model.hasErrorState('big-failure')).toBe(true);
1276
1277 model.removeError("big-failure");
1278
1279 expect(model.hasErrorState('big-failure')).toBe(false);
1280 });
970 }); 1281 });
971 1282
972 describe('text-like events', () { 1283 describe('text-like events', () {
973 it('should update the binding on the "input" event', inject(() { 1284 it('should update the binding on the "input" event', () {
974 _.compile('<input type="text" ng-model="model" probe="p">'); 1285 _.compile('<input type="text" ng-model="model" probe="p">');
975 Probe probe = _.rootScope.context['p']; 1286 Probe probe = _.rootScope.context['p'];
976 InputElement inputElement = probe.element; 1287 InputElement inputElement = probe.element;
977 1288
978 inputElement.value = 'waaaah'; 1289 inputElement.value = 'waaaah';
979 1290
980 expect(_.rootScope.context['model']).not.toEqual('waaaah'); 1291 expect(_.rootScope.context['model']).not.toEqual('waaaah');
981 1292
982 _.triggerEvent(inputElement, 'input'); 1293 _.triggerEvent(inputElement, 'input');
983 1294
984 expect(_.rootScope.context['model']).toEqual('waaaah'); 1295 expect(_.rootScope.context['model']).toEqual('waaaah');
985 })); 1296 });
986 }); 1297 });
987 1298
988 describe('error messages', () { 1299 describe('error messages', () {
989 it('should produce a useful error for bad ng-model expressions', () { 1300 it('should produce a useful error for bad ng-model expressions', () {
990 expect(async(() { 1301 expect(async(() {
991 _.compile('<div no-love><textarea ng-model=ctrl.love probe="loveProbe" ></textarea></div'); 1302 _.compile('<div no-love><textarea ng-model=ctrl.love probe="loveProbe" ></textarea></div');
992 Probe probe = _.rootScope.context['loveProbe']; 1303 Probe probe = _.rootScope.context['loveProbe'];
993 TextAreaElement inputElement = probe.element; 1304 TextAreaElement inputElement = probe.element;
994 1305
995 inputElement.value = 'xzy'; 1306 inputElement.value = 'xzy';
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1036 _.triggerEvent(input, 'blur'); 1347 _.triggerEvent(input, 'blur');
1037 1348
1038 expect(model.touched).toBe(true); 1349 expect(model.touched).toBe(true);
1039 expect(model.untouched).toBe(false); 1350 expect(model.untouched).toBe(false);
1040 1351
1041 model.reset(); 1352 model.reset();
1042 1353
1043 expect(model.touched).toBe(false); 1354 expect(model.touched).toBe(false);
1044 expect(model.untouched).toBe(true); 1355 expect(model.untouched).toBe(true);
1045 }); 1356 });
1357
1358 describe('validators', () {
1359 it('should display the valid and invalid CSS classes on the element for ea ch validation',
1360 (TestBed _, Scope scope) {
1361
1362 var input = _.compile('<input type="email" ng-model="myModel" />');
1363
1364 scope.apply(() {
1365 scope.context['myModel'] = 'value';
1366 });
1367
1368 expect(input).toHaveClass('ng-email-invalid');
1369 expect(input).not.toHaveClass('ng-email-valid');
1370
1371 scope.apply(() {
1372 scope.context['myModel'] = 'value@email.com';
1373 });
1374
1375 expect(input).toHaveClass('ng-email-valid');
1376 expect(input).not.toHaveClass('ng-email-invalid');
1377 });
1378
1379 it('should display the valid and invalid CSS classes on the element for cu stom validations',
1380 (TestBed _, Scope scope) {
1381
1382 var input = _.compile('<input type="text" ng-model="myModel" custom-inpu t-validation />');
1383
1384 scope.apply();
1385
1386 expect(input).toHaveClass('custom-invalid');
1387 expect(input).not.toHaveClass('custom-valid');
1388
1389 scope.apply(() {
1390 scope.context['myModel'] = 'yes';
1391 });
1392
1393 expect(input).toHaveClass('custom-valid');
1394 expect(input).not.toHaveClass('custom-invalid');
1395 });
1396
1397 it('should only validate twice during compilation and once upon scope dige st',
1398 (TestBed _, Scope scope) {
1399
1400 scope.context['required'] = true;
1401 _.compile('<input type="text" '
1402 'ng-model="model" '
1403 'ng-required="required" '
1404 'ng-pattern="pattern" '
1405 'counting-validator '
1406 'probe="i">');
1407
1408 scope.context['pattern'] = '^[aeiou]+\$';
1409 scope.context['required'] = true;
1410
1411 scope.apply();
1412
1413 var model = scope.context['i'].directive(NgModel);
1414 var counter = model.validators.firstWhere((validator) => validator.name == 'counting');
1415
1416 // TODO(#881): There is a bug in ngModel where the validators are valida ted too often.
1417 // Should be 2. One for ngModel and one for all the other ones
1418 // Currently, this count is 2 on Chrome and 3 on Firefox.
1419 expect(counter.count == 2 || counter.count == 3).toBe(true);
1420 expect(model.invalid).toBe(true);
1421
1422 counter.count = 0;
1423 scope.context['pattern'] = '';
1424 scope.context['required'] = false;
1425 scope.apply();
1426
1427 expect(counter.count).toBe(1);
1428 });
1429
1430 it('should only validate twice regardless of attribute order', (TestBed _, Scope scope) {
1431 scope.context['required'] = true;
1432 _.compile('<input type="text" '
1433 'ng-required="required" '
1434 'ng-pattern="pattern" '
1435 'counting-validator '
1436 'ng-model="model" '
1437 'probe="i">');
1438
1439 scope.context['pattern'] = '^[aeiou]+\$';
1440 scope.context['required'] = true;
1441
1442 scope.apply();
1443
1444 var model = scope.context['i'].directive(NgModel);
1445
1446 var counter = model.validators.firstWhere((validator) => validator.name == 'counting');
1447
1448 // TODO(#881): There is a bug in ngModel where the validators are valida ted too often.
1449 // Should be 2. One for ngModel and one for all the other ones
1450 // Currently, this count is 3 on Chrome and 1 on Firefox.
1451 expect(counter.count == 2 || counter.count == 3).toBe(true);
1452 });
1453 });
1454
1455 describe('converters', () {
1456 it('should parse the model value according to the given parser', (Scope sc ope) {
1457 _.compile('<input type="text" ng-model="model" probe="i">');
1458 scope.apply();
1459
1460 var probe = scope.context['i'];
1461 var input = probe.element;
1462 var model = probe.directive(NgModel);
1463 model.converter = new LowercaseValueParser();
1464
1465 input.value = 'HELLO';
1466 _.triggerEvent(input, 'change');
1467 _.rootScope.apply();
1468
1469 expect(model.viewValue).toEqual('HELLO');
1470 expect(model.modelValue).toEqual('hello');
1471 });
1472
1473 it('should format the model value according to the given formatter', (Scop e scope) {
1474 _.compile('<input type="text" ng-model="model" probe="i">');
1475 scope.apply();
1476
1477 var probe = scope.context['i'];
1478 var input = probe.element;
1479 var model = probe.directive(NgModel);
1480 model.converter = new UppercaseValueFormatter();
1481
1482 scope.apply(() {
1483 scope.context['model'] = 'greetings';
1484 });
1485
1486 expect(model.viewValue).toEqual('GREETINGS');
1487 expect(model.modelValue).toEqual('greetings');
1488 });
1489
1490 it('should retain the current input value if the parser fails', (Scope sco pe) {
1491 _.compile('<form name="myForm">' +
1492 ' <input type="text" ng-model="model1" name="myModel1" probe=" i">' +
1493 ' <input type="text" ng-model="model2" name="myModel2" probe=" j">' +
1494 '</form>');
1495 scope.apply();
1496
1497 var probe1 = scope.context['i'];
1498 var input1 = probe1.element;
1499 var model1 = probe1.directive(NgModel);
1500
1501 var probe2 = scope.context['j'];
1502 var input2 = probe2.element;
1503 var model2 = probe2.directive(NgModel);
1504
1505 model1.converter = new FailedValueParser();
1506
1507 input1.value = '123';
1508 _.triggerEvent(input1, 'change');
1509 _.rootScope.apply();
1510
1511 expect(model1.viewValue).toEqual('123');
1512 expect(input1.value).toEqual('123');
1513 expect(model1.modelValue).toEqual(null);
1514
1515 expect(model2.viewValue).toEqual(null);
1516 expect(input2.value).toEqual('');
1517 expect(model2.modelValue).toEqual(null);
1518 });
1519
1520 it('should reformat the viewValue when the formatter is changed', (Scope s cope) {
1521 _.compile('<input type="text" ng-model="model" probe="i">');
1522 scope.apply();
1523
1524 var probe = scope.context['i'];
1525 var input = probe.element;
1526 var model = probe.directive(NgModel);
1527 model.converter = new LowercaseValueParser();
1528
1529 input.value = 'HI THERE';
1530 _.triggerEvent(input, 'change');
1531 _.rootScope.apply();
1532
1533 expect(model.viewValue).toEqual('HI THERE');
1534 expect(model.modelValue).toEqual('hi there');
1535
1536 model.converter = new VowelValueParser();
1537
1538 expect(model.viewValue).toEqual('iee');
1539 expect(model.modelValue).toEqual('hi there');
1540 });
1541 });
1046 }); 1542 });
1047 } 1543 }
1048 1544
1049 @NgController( 1545 @Controller(
1050 selector: '[no-love]', 1546 selector: '[no-love]',
1051 publishAs: 'ctrl') 1547 publishAs: 'ctrl')
1052 class ControllerWithNoLove { 1548 class ControllerWithNoLove {
1053 var apathy = null; 1549 var apathy = null;
1054 } 1550 }
1551
1552 class LowercaseValueParser implements NgModelConverter {
1553 final name = 'lowercase';
1554 format(value) => value;
1555 parse(value) {
1556 return value != null ? value.toLowerCase() : null;
1557 }
1558 }
1559
1560 class UppercaseValueFormatter implements NgModelConverter {
1561 final name = 'uppercase';
1562 parse(value) => value;
1563 format(value) {
1564 return value != null ? value.toUpperCase() : null;
1565 }
1566 }
1567
1568 class FailedValueParser implements NgModelConverter {
1569 final name = 'failed';
1570 format(value) => value;
1571 parse(value) {
1572 throw new Exception();
1573 }
1574 }
1575
1576 class VowelValueParser implements NgModelConverter {
1577 final name = 'vowel';
1578 parse(value) => value;
1579 format(value) {
1580 if(value != null) {
1581 var exp = new RegExp("[^aeiouAEIOU]");
1582 value = value.replaceAll(exp, "");
1583 }
1584 return value;
1585 }
1586 }
1587
1588 @Decorator(
1589 selector: '[custom-input-validation]')
1590 class MyCustomInputValidator extends NgValidator {
1591 MyCustomInputValidator(NgModel ngModel) {
1592 ngModel.addValidator(this);
1593 }
1594
1595 final String name = 'custom';
1596
1597 bool isValid(name) {
1598 return name != null && name == 'yes';
1599 }
1600 }
1601
1602 @Decorator(
1603 selector: '[counting-validator]')
1604 class CountingValidator extends NgValidator {
1605
1606 final String name = 'counting';
1607 int count = 0;
1608
1609 CountingValidator(NgModel ngModel) {
1610 ngModel.addValidator(this);
1611 }
1612
1613 bool isValid(String modelValue) {
1614 count++;
1615 return true;
1616 }
1617 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698