Skip to content

Commit

Permalink
Prohibit faking of faked timers (#426)
Browse files Browse the repository at this point in the history
* Disallow faking of faked timers

* Use a property on the global context when fake is installed

* Lint

* Correct `isFake` property placement

* Fix broken test

* typo

Co-authored-by: Carl-Erik Kopseng <carlerik@gmail.com>
  • Loading branch information
cjbarth and fatso83 committed Apr 13, 2022
1 parent f8a09c9 commit 7046e72
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 1 deletion.
9 changes: 9 additions & 0 deletions src/fake-timers-src.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ function withGlobal(_global) {
target.parse = source.parse;
target.UTC = source.UTC;
target.prototype.toUTCString = source.prototype.toUTCString;
target.isFake = true;

return target;
}
Expand Down Expand Up @@ -1635,6 +1636,14 @@ function withGlobal(_global) {
);
}

if (_global.Date.isFake === true) {
// Timers are already faked; this is a problem.
// Make the user reset timers before continuing.
throw new TypeError(
"Can't install fake timers twice on the same global object."
);
}

// eslint-disable-next-line no-param-reassign
config = typeof config !== "undefined" ? config : {};
config.shouldAdvanceTime = config.shouldAdvanceTime || false;
Expand Down
79 changes: 78 additions & 1 deletion test/fake-timers-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,81 @@ const utilPromisifyAvailable = promisePresent && utilPromisify;
const timeoutResult = global.setTimeout(NOOP, 0);
const addTimerReturnsObject = typeof timeoutResult === "object";

describe("issue #2449: permanent loss of native functions", function () {
it("should not fake faked timers", function () {
const currentTime = new Date().getTime();
const date1 = new Date("2015-09-25");
const date2 = new Date("2015-09-26");
let clock = FakeTimers.install({ now: date1 });
assert.same(clock.now, date1.getTime());
assert.same(new Date().getTime(), 1443139200000);
assert.exception(function () {
FakeTimers.install({ now: date2 });
});
clock.uninstall();
clock = FakeTimers.install({ now: date2 });
assert.same(clock.now, date2.getTime());
clock.uninstall();
assert.greater(new Date().getTime(), currentTime, true);
});

it("should not fake faked timers on a custom target", function () {
const setTimeoutFake = sinon.fake();
const context = {
Date: Date,
setTimeout: setTimeoutFake,
clearTimeout: sinon.fake(),
};
let clock = FakeTimers.withGlobal(context).install();
assert.exception(function () {
clock = FakeTimers.withGlobal(context).install();
});
clock.uninstall();

// After uninstaling we should be able to install without issue
clock = FakeTimers.withGlobal(context).install();
clock.uninstall();
});

it("should not allow a fake on a custom target if the global is faked and the context inherited from the global", function () {
const globalClock = FakeTimers.install();
assert.equals(new Date().getTime(), 0);
const setTimeoutFake = sinon.fake();
const context = {
Date: Date,
setTimeout: setTimeoutFake,
clearTimeout: sinon.fake(),
};
assert.equals(new context.Date().getTime(), 0);
assert.exception(function () {
FakeTimers.withGlobal(context).install();
});

globalClock.uninstall();
refute.equals(new Date().getTime(), 0);
});

it("should allow a fake on the global if a fake on a customer target is already defined", function () {
const setTimeoutFake = sinon.fake();
const context = {
Date: Date,
setTimeout: setTimeoutFake,
clearTimeout: sinon.fake(),
};
const clock = FakeTimers.withGlobal(context).install();
assert.equals(new context.Date().getTime(), 0);
refute.equals(new Date().getTime(), 0);
const globalClock = FakeTimers.install();
assert.equals(new Date().getTime(), 0);

globalClock.uninstall();
refute.equals(new Date().getTime(), 0);
assert.equals(new context.Date().getTime(), 0);
clock.uninstall();
refute.equals(new Date().getTime(), 0);
refute.equals(new context.Date().getTime(), 0);
});
});
describe("issue #59", function () {
it("should install and uninstall the clock on a custom target", function () {
const setTimeoutFake = sinon.fake();
Expand Down Expand Up @@ -5139,11 +5214,13 @@ describe("loop limit stack trace", function () {
});

describe("setImmediate", function () {
beforeEach(function () {
before(function () {
if (!setImmediatePresent) {
this.skip();
}
});

beforeEach(function () {
function recursiveCreateTimer() {
setImmediate(function recursiveCreateTimerTimeout() {
recursiveCreateTimer();
Expand Down

0 comments on commit 7046e72

Please sign in to comment.