Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 4b8f385

Browse files
committed
test(ngOptions): fix flaky test on Firefox 54+
1 parent 56ac2a7 commit 4b8f385

File tree

1 file changed

+51
-6
lines changed

1 file changed

+51
-6
lines changed

test/ng/directive/ngOptionsSpec.js

+51-6
Original file line numberDiff line numberDiff line change
@@ -2923,6 +2923,9 @@ describe('ngOptions', function() {
29232923

29242924

29252925
it('should not re-set the `selected` property if it already has the correct value', function() {
2926+
var optionProto;
2927+
var originalSelectedDescriptor;
2928+
29262929
scope.values = [{name: 'A'}, {name: 'B'}];
29272930
createMultiSelect();
29282931

@@ -2932,14 +2935,51 @@ describe('ngOptions', function() {
29322935

29332936
// Set up spies
29342937
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);
29412941
});
29422942

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+
29432983
// Select `optionA`
29442984
scope.$apply('selected = [values[0]]');
29452985

@@ -2999,6 +3039,11 @@ describe('ngOptions', function() {
29993039
expect(options[1].selected).toBe(false);
30003040
optionsSetSelected[0].calls.reset();
30013041
optionsSetSelected[1].calls.reset();
3042+
3043+
// Restore `originalSelectedDescriptor`
3044+
if (originalSelectedDescriptor) {
3045+
Object.defineProperty(optionProto, 'selected', originalSelectedDescriptor);
3046+
}
30023047
});
30033048

30043049
if (window.MutationObserver) {

0 commit comments

Comments
 (0)