|
1 | 1 | 'use strict';
|
2 | 2 |
|
3 | 3 | describe('select', function() {
|
4 |
| - var scope, formElement, element, $compile, ngModelCtrl, selectCtrl, renderSpy; |
| 4 | + var scope, formElement, element, $compile, ngModelCtrl, selectCtrl, renderSpy, optionAttributesList = []; |
5 | 5 |
|
6 | 6 | function compile(html) {
|
7 | 7 | formElement = jqLite('<form name="form">' + html + '</form>');
|
@@ -55,6 +55,18 @@ describe('select', function() {
|
55 | 55 | '</option>'
|
56 | 56 | };
|
57 | 57 | });
|
| 58 | + |
| 59 | + $compileProvider.directive('exposeAttributes', function() { |
| 60 | + return { |
| 61 | + require: '^^select', |
| 62 | + link: { |
| 63 | + pre: function(scope, element, attrs, ctrl) { |
| 64 | + optionAttributesList.push(attrs); |
| 65 | + } |
| 66 | + } |
| 67 | + }; |
| 68 | + }); |
| 69 | + |
58 | 70 | }));
|
59 | 71 |
|
60 | 72 | beforeEach(inject(function($rootScope, _$compile_) {
|
@@ -1224,5 +1236,202 @@ describe('select', function() {
|
1224 | 1236 | }).toThrowMinErr('ng','badname', 'hasOwnProperty is not a valid "option value" name');
|
1225 | 1237 | });
|
1226 | 1238 |
|
| 1239 | + describe('with ngValue (and non-primitive values)', function() { |
| 1240 | + |
| 1241 | + they('should set the option attribute and select it for value $prop', [ |
| 1242 | + 'string', |
| 1243 | + undefined, |
| 1244 | + 1, |
| 1245 | + true, |
| 1246 | + null, |
| 1247 | + {prop: 'value'}, |
| 1248 | + ['a'], |
| 1249 | + NaN |
| 1250 | + ], function(prop) { |
| 1251 | + scope.option1 = prop; |
| 1252 | + scope.selected = 'NOMATCH'; |
| 1253 | + |
| 1254 | + compile('<select ng-model="selected">' + |
| 1255 | + '<option ng-value="option1">{{option1}}</option>' + |
| 1256 | + '</select>'); |
| 1257 | + |
| 1258 | + scope.$digest(); |
| 1259 | + expect(element.find('option').eq(0).val()).toBe('? string:NOMATCH ?'); |
| 1260 | + |
| 1261 | + scope.selected = prop; |
| 1262 | + scope.$digest(); |
| 1263 | + |
| 1264 | + expect(element.find('option').eq(0).val()).toBe(hashKey(prop)); |
| 1265 | + |
| 1266 | + // Reset |
| 1267 | + scope.selected = false; |
| 1268 | + scope.$digest(); |
| 1269 | + |
| 1270 | + expect(element.find('option').eq(0).val()).toBe('? boolean:false ?'); |
| 1271 | + |
| 1272 | + browserTrigger(element.find('option').eq(0)); |
| 1273 | + if (typeof prop === 'number' && isNaN(prop)) { |
| 1274 | + expect(scope.selected).toBeNaN(); |
| 1275 | + } else { |
| 1276 | + expect(scope.selected).toBe(prop); |
| 1277 | + } |
| 1278 | + }); |
| 1279 | + |
| 1280 | + |
| 1281 | + they('should update the option attribute and select it for value $prop', [ |
| 1282 | + 'string', |
| 1283 | + undefined, |
| 1284 | + 1, |
| 1285 | + true, |
| 1286 | + null, |
| 1287 | + {prop: 'value'}, |
| 1288 | + ['a'], |
| 1289 | + NaN |
| 1290 | + ], function(prop) { |
| 1291 | + scope.option = prop; |
| 1292 | + scope.selected = 'NOMATCH'; |
| 1293 | + |
| 1294 | + compile('<select ng-model="selected">' + |
| 1295 | + '<option ng-value="option">{{option}}</option>' + |
| 1296 | + '</select>'); |
| 1297 | + |
| 1298 | + var selectController = element.controller('select'); |
| 1299 | + spyOn(selectController, 'removeOption').and.callThrough(); |
| 1300 | + |
| 1301 | + scope.$digest(); |
| 1302 | + expect(selectController.removeOption).not.toHaveBeenCalled(); |
| 1303 | + expect(element.find('option').eq(0).val()).toBe('? string:NOMATCH ?'); |
| 1304 | + |
| 1305 | + scope.selected = prop; |
| 1306 | + scope.$digest(); |
| 1307 | + |
| 1308 | + expect(element.find('option').eq(0).val()).toBe(hashKey(prop)); |
| 1309 | + |
| 1310 | + scope.option = 'UPDATEDVALUE'; |
| 1311 | + scope.$digest(); |
| 1312 | + |
| 1313 | + expect(selectController.removeOption.calls.count()).toBe(1); |
| 1314 | + |
| 1315 | + // Updating the option value currently does not update the select model |
| 1316 | + if (typeof prop === 'number' && isNaN(prop)) { |
| 1317 | + expect(selectController.removeOption.calls.argsFor(0)[0]).toBeNaN(); |
| 1318 | + expect(scope.selected).toBeNaN(); |
| 1319 | + } else { |
| 1320 | + expect(selectController.removeOption.calls.argsFor(0)[0]).toBe(prop); |
| 1321 | + expect(scope.selected).toBe(prop); |
| 1322 | + } |
| 1323 | + |
| 1324 | + expect(element.find('option').eq(0).prop('selected')).toBe(true); |
| 1325 | + expect(element.find('option').eq(1).val()).toBe('string:UPDATEDVALUE'); |
| 1326 | + |
| 1327 | + scope.selected = 'UPDATEDVALUE'; |
| 1328 | + scope.$digest(); |
| 1329 | + |
| 1330 | + // expect(element.find('option').eq(0).prop('selected')).toBe(true); not selected in Chrome? |
| 1331 | + expect(element.find('option').eq(0).val()).toBe('string:UPDATEDVALUE'); |
| 1332 | + }); |
| 1333 | + |
| 1334 | + it('should interact with custom attribute $observe and $set calls', function() { |
| 1335 | + var log = [], optionAttr; |
| 1336 | + |
| 1337 | + compile('<select ng-model="selected">' + |
| 1338 | + '<option expose-attributes ng-value="option">{{option}}</option>' + |
| 1339 | + '</select>'); |
| 1340 | + |
| 1341 | + optionAttr = optionAttributesList[0]; |
| 1342 | + optionAttr.$observe('value', function(newVal) { |
| 1343 | + log.push(newVal); |
| 1344 | + }); |
| 1345 | + |
| 1346 | + scope.option = 'init'; |
| 1347 | + scope.$digest(); |
| 1348 | + |
| 1349 | + expect(log[0]).toBe('init'); |
| 1350 | + expect(element.find('option').eq(1).val()).toBe('string:init'); |
| 1351 | + |
| 1352 | + optionAttr.$set('value', 'update'); |
| 1353 | + expect(log[1]).toBe('update'); |
| 1354 | + expect(element.find('option').eq(1).val()).toBe('string:update'); |
| 1355 | + |
| 1356 | + }); |
| 1357 | + |
| 1358 | + describe('and select[multiple]', function() { |
| 1359 | + |
| 1360 | + it('should allow multiple selection', function() { |
| 1361 | + scope.options = { |
| 1362 | + a: 'string', |
| 1363 | + b: undefined, |
| 1364 | + c: 1, |
| 1365 | + d: true, |
| 1366 | + e: null, |
| 1367 | + f: {prop: 'value'}, |
| 1368 | + g: ['a'], |
| 1369 | + h: NaN |
| 1370 | + }; |
| 1371 | + scope.selected = []; |
| 1372 | + |
| 1373 | + compile('<select multiple ng-model="selected">' + |
| 1374 | + '<option ng-value="options.a">{{options.a}}</option>' + |
| 1375 | + '<option ng-value="options.b">{{options.b}}</option>' + |
| 1376 | + '<option ng-value="options.c">{{options.c}}</option>' + |
| 1377 | + '<option ng-value="options.d">{{options.d}}</option>' + |
| 1378 | + '<option ng-value="options.e">{{options.e}}</option>' + |
| 1379 | + '<option ng-value="options.f">{{options.f}}</option>' + |
| 1380 | + '<option ng-value="options.g">{{options.g}}</option>' + |
| 1381 | + '<option ng-value="options.h">{{options.h}}</option>' + |
| 1382 | + '</select>'); |
| 1383 | + |
| 1384 | + scope.$digest(); |
| 1385 | + expect(element).toEqualSelect( |
| 1386 | + 'string:string', |
| 1387 | + 'undefined:undefined', |
| 1388 | + 'number:1', |
| 1389 | + 'boolean:true', |
| 1390 | + 'object:null', |
| 1391 | + 'object:4', |
| 1392 | + 'object:5', |
| 1393 | + 'number:NaN' |
| 1394 | + ); |
| 1395 | + |
| 1396 | + scope.selected = ['string', 1]; |
| 1397 | + scope.$digest(); |
| 1398 | + |
| 1399 | + expect(element.find('option').eq(0).prop('selected')).toBe(true); |
| 1400 | + expect(element.find('option').eq(2).prop('selected')).toBe(true); |
| 1401 | + |
| 1402 | + browserTrigger(element.find('option').eq(1)); |
| 1403 | + expect(scope.selected).toEqual([undefined]); |
| 1404 | + |
| 1405 | + //reset |
| 1406 | + scope.selected = []; |
| 1407 | + scope.$digest(); |
| 1408 | + |
| 1409 | + forEach(element.find('option'), function(option) { |
| 1410 | + // browserTrigger can't produce click + ctrl, so set selection manually |
| 1411 | + jqLite(option).prop('selected', true); |
| 1412 | + }); |
| 1413 | + |
| 1414 | + browserTrigger(element, 'change'); |
| 1415 | + |
| 1416 | + var arrayVal = ['a']; |
| 1417 | + arrayVal.$$hashKey = 'object:5'; |
| 1418 | + |
| 1419 | + expect(scope.selected).toEqual([ |
| 1420 | + 'string', |
| 1421 | + undefined, |
| 1422 | + 1, |
| 1423 | + true, |
| 1424 | + null, |
| 1425 | + {prop: 'value', $$hashKey: 'object:4'}, |
| 1426 | + arrayVal, |
| 1427 | + NaN |
| 1428 | + ]); |
| 1429 | + }); |
| 1430 | + |
| 1431 | + }); |
| 1432 | + |
| 1433 | + |
| 1434 | + }); |
| 1435 | + |
1227 | 1436 | });
|
1228 | 1437 | });
|
0 commit comments