Prevent mock clock timing fns from being spied on

Fixes #826
This commit is contained in:
Steve Gravrock
2025-09-25 20:56:19 -07:00
parent 979e4a5d0f
commit 190a13ed96
5 changed files with 73 additions and 0 deletions

View File

@@ -2912,6 +2912,8 @@ getJasmineRequireObj().Clock = function() {
process.versions &&
typeof process.versions.node === 'string';
const IsMockClockTimingFn = Symbol('IsMockClockTimingFn');
/**
* @class Clock
* @since 1.3.0
@@ -3090,6 +3092,10 @@ callbacks to execute _before_ running the next one.
advanceUntilModeChanges();
};
setTimeout[IsMockClockTimingFn] = true;
clearTimeout[IsMockClockTimingFn] = true;
setInterval[IsMockClockTimingFn] = true;
clearInterval[IsMockClockTimingFn] = true;
return this;
// Advances the Clock's time until the mode changes.
@@ -3242,6 +3248,7 @@ callbacks to execute _before_ running the next one.
return this;
};
Clock.IsMockClockTimingFn = IsMockClockTimingFn;
return Clock;
};
@@ -10046,6 +10053,12 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
throw new Error(getErrorMsg(methodName + '() method does not exist'));
}
// Spying on mock clock timing fns would prevent the real ones from being
// restored.
if (obj[methodName] && obj[methodName][j$.Clock.IsMockClockTimingFn]) {
throw new Error("Mock clock timing functions can't be spied on");
}
if (obj[methodName] && j$.isSpy(obj[methodName])) {
if (this.respy) {
return obj[methodName];

View File

@@ -408,6 +408,41 @@ describe('Clock', function() {
expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled();
});
it('identifies its timing functions', function() {
const fakeSetTimeout = jasmine.createSpy('global setTimeout');
const fakeGlobal = { setTimeout: fakeSetTimeout };
const delayedFunctionScheduler = jasmine.createSpyObj(
'delayedFunctionScheduler',
['scheduleFunction']
);
const mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
};
const clock = new jasmineUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
);
clock.install();
expect(
fakeGlobal.setTimeout[jasmineUnderTest.Clock.IsMockClockTimingFn]
).toEqual(true);
expect(
fakeGlobal.clearTimeout[jasmineUnderTest.Clock.IsMockClockTimingFn]
).toEqual(true);
expect(
fakeGlobal.setInterval[jasmineUnderTest.Clock.IsMockClockTimingFn]
).toEqual(true);
expect(
fakeGlobal.clearInterval[jasmineUnderTest.Clock.IsMockClockTimingFn]
).toEqual(true);
});
describe('setTimeout', function() {
it('schedules the delayed function with the fake timer', function() {
const fakeSetTimeout = jasmine.createSpy('setTimeout'),

View File

@@ -135,6 +135,18 @@ describe('SpyRegistry', function() {
target.spiedFunc();
expect(originalFunctionWasCalled).toBe(false);
});
it('throws if the method is a mock clock method', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: createSpy
});
const target = { spiedFunc: function() {} };
target.spiedFunc[jasmineUnderTest.Clock.IsMockClockTimingFn] = true;
expect(function() {
spyRegistry.spyOn(target, 'spiedFunc');
}).toThrowError("Mock clock timing functions can't be spied on");
});
});
describe('#spyOnProperty', function() {

View File

@@ -5,6 +5,8 @@ getJasmineRequireObj().Clock = function() {
process.versions &&
typeof process.versions.node === 'string';
const IsMockClockTimingFn = Symbol('IsMockClockTimingFn');
/**
* @class Clock
* @since 1.3.0
@@ -183,6 +185,10 @@ callbacks to execute _before_ running the next one.
advanceUntilModeChanges();
};
setTimeout[IsMockClockTimingFn] = true;
clearTimeout[IsMockClockTimingFn] = true;
setInterval[IsMockClockTimingFn] = true;
clearInterval[IsMockClockTimingFn] = true;
return this;
// Advances the Clock's time until the mode changes.
@@ -335,5 +341,6 @@ callbacks to execute _before_ running the next one.
return this;
};
Clock.IsMockClockTimingFn = IsMockClockTimingFn;
return Clock;
};

View File

@@ -41,6 +41,12 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
throw new Error(getErrorMsg(methodName + '() method does not exist'));
}
// Spying on mock clock timing fns would prevent the real ones from being
// restored.
if (obj[methodName] && obj[methodName][j$.Clock.IsMockClockTimingFn]) {
throw new Error("Mock clock timing functions can't be spied on");
}
if (obj[methodName] && j$.isSpy(obj[methodName])) {
if (this.respy) {
return obj[methodName];