@@ -2923,6 +2923,9 @@ describe('ngOptions', function() {
2923
2923
2924
2924
2925
2925
it ( 'should not re-set the `selected` property if it already has the correct value' , function ( ) {
2926
+ var optionProto ;
2927
+ var originalSelectedDescriptor ;
2928
+
2926
2929
scope . values = [ { name : 'A' } , { name : 'B' } ] ;
2927
2930
createMultiSelect ( ) ;
2928
2931
@@ -2932,14 +2935,51 @@ describe('ngOptions', function() {
2932
2935
2933
2936
// Set up spies
2934
2937
forEach ( options , function ( option , i ) {
2935
- optionsSetSelected [ i ] = jasmine . createSpy ( 'optionSetSelected' + i ) ;
2936
- _selected [ i ] = option . selected ;
2937
- Object . defineProperty ( option , 'selected' , {
2938
- get : function ( ) { return _selected [ i ] ; } ,
2939
- set : optionsSetSelected [ i ] . and . callFake ( function ( value ) { _selected [ i ] = value ; } )
2940
- } ) ;
2938
+ var setSelected = function ( value ) { _selected [ i ] = value ; } ;
2939
+ optionsSetSelected [ i ] = jasmine . createSpy ( 'optionSetSelected' + i ) . and . callFake ( setSelected ) ;
2940
+ setSelected ( option . selected ) ;
2941
2941
} ) ;
2942
2942
2943
+ if ( ! isFunction ( Object . getOwnPropertyDescriptor ) ) {
2944
+ forEach ( options , function ( option , i ) {
2945
+ Object . defineProperty ( option , 'selected' , {
2946
+ get : function ( ) { return _selected [ i ] ; } ,
2947
+ set : optionsSetSelected [ i ]
2948
+ } ) ;
2949
+ } ) ;
2950
+ } else {
2951
+ // Support: Firefox 54+
2952
+ // We cannot use the above (simpler) method for all browsers, because of Firefox 54+, which
2953
+ // is very flaky when the getter/setter property is defined on the element itself and not
2954
+ // the prototype. (Possibly the result of some (buggy?) optimization.)
2955
+ optionProto = Object . getPrototypeOf ( options [ 0 ] ) ;
2956
+ originalSelectedDescriptor = Object . getOwnPropertyDescriptor ( optionProto , 'selected' ) ;
2957
+
2958
+ var getSelected = function ( index ) { return _selected [ index ] ; } ;
2959
+ var setSelected = function ( index , value ) { optionsSetSelected [ index ] ( value ) ; } ;
2960
+ var getSelectedOriginal = function ( option ) {
2961
+ return originalSelectedDescriptor . get . call ( option ) ;
2962
+ } ;
2963
+ var setSelectedOriginal = function ( option , value ) {
2964
+ originalSelectedDescriptor . set . call ( option , value ) ;
2965
+ } ;
2966
+ var getIndexAndCall = function ( option , foundFn , notFoundFn , value ) {
2967
+ for ( var i = 0 , ii = options . length ; i < ii ; ++ i ) {
2968
+ if ( options [ i ] === option ) return foundFn ( i , value ) ;
2969
+ }
2970
+ return notFoundFn ( option , value ) ;
2971
+ } ;
2972
+
2973
+ Object . defineProperty ( optionProto , 'selected' , {
2974
+ get : function ( ) {
2975
+ return getIndexAndCall ( this , getSelected , getSelectedOriginal ) ;
2976
+ } ,
2977
+ set : function ( value ) {
2978
+ return getIndexAndCall ( this , setSelected , setSelectedOriginal , value ) ;
2979
+ }
2980
+ } ) ;
2981
+ }
2982
+
2943
2983
// Select `optionA`
2944
2984
scope . $apply ( 'selected = [values[0]]' ) ;
2945
2985
@@ -2999,6 +3039,11 @@ describe('ngOptions', function() {
2999
3039
expect ( options [ 1 ] . selected ) . toBe ( false ) ;
3000
3040
optionsSetSelected [ 0 ] . calls . reset ( ) ;
3001
3041
optionsSetSelected [ 1 ] . calls . reset ( ) ;
3042
+
3043
+ // Restore `originalSelectedDescriptor`
3044
+ if ( originalSelectedDescriptor ) {
3045
+ Object . defineProperty ( optionProto , 'selected' , originalSelectedDescriptor ) ;
3046
+ }
3002
3047
} ) ;
3003
3048
3004
3049
if ( window . MutationObserver ) {
0 commit comments