From ffe407822f2a482c4fae19cad697430dbff6b375 Mon Sep 17 00:00:00 2001 From: Alistair Brown Date: Sun, 17 May 2020 11:43:09 +0100 Subject: [PATCH 1/4] Add stack trace to code recursively scheduling timers Use stack information for jobs and timers to provide more context when an infinite loop error is thrown. Fixes #230 Add flag for approaching infinite loop Use a flag when approaching infinite loop detection which will enable an error object to be attached to the job, which can then be used for generating the stack output. Update for PR feedback - Safely update stack property using `defineProperty`, wrapped in try/catch - Tests for more clock functions to confirm stack trace provided - Stack trace slice automatically calculated based on clock function position Remove unnecessary durations and use globals - Use global setInterval and setImmediate to match setTimeout use - Remove unnecessary duration parameter from setImmediate and setImmediate add use of prettier Firefox and Chrome tests pass after RegExp fix Chrome shows stack messages differently, and uses the words: 'at' and 'Object', Firefox does not - that has created two problems: first, the stack trace messages that are shown to the user are a bit different (Firefox always has at least one more line at the start of the stack list - usually internal functions that are not supposed to appear). Second, the RegExp tried to match parts of the strings that only exist in Chrome, failing the Firefox tests) prettier used --- src/fake-timers-src.js | 100 ++++++++++-- test/fake-timers-test.js | 342 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 431 insertions(+), 11 deletions(-) diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js index e64a8300..138879ce 100644 --- a/src/fake-timers-src.js +++ b/src/fake-timers-src.js @@ -152,6 +152,18 @@ function withGlobal(_global) { return isFinite(num); } + var isNearInfiniteLimit = false; + + function checkIsNearInfiniteLimit(clock, i) { + if (clock.loopLimit && i === clock.loopLimit - 1) { + isNearInfiniteLimit = true; + } + } + + function resetIsNearInfiniteLimit() { + isNearInfiniteLimit = false; + } + /** * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into * number of milliseconds. This is used to support human-readable strings passed @@ -235,6 +247,64 @@ function withGlobal(_global) { return timer && timer.callAt >= from && timer.callAt <= to; } + function getInfiniteLoopError(clock, job) { + var infiniteLoopError = new Error( + "Aborting after running " + + clock.loopLimit + + " timers, assuming an infinite loop!" + ); + var computedTargetPattern = /target\.*[<|(|[].*?[>|\]|)]\s*/; + var clockMethodPattern = new RegExp( + String(Object.keys(clock).join("|")) + ); + + var matchedLineIndex = -1; + job.error.stack.split("\n").some(function (line, i) { + // If we've matched a computed target line (e.g. setTimeout) then we + // don't need to look any further. Return true to stop iterating. + var matchedComputedTarget = line.match(computedTargetPattern); + if (matchedComputedTarget) { + matchedLineIndex = i; + return true; + } + + // If we've matched a clock method line, then there may still be + // others further down the trace. Return false to keep iterating. + var matchedClockMethod = line.match(clockMethodPattern); + if (matchedClockMethod) { + matchedLineIndex = i; + return false; + } + + // If we haven't matched anything on this line, but we matched + // previously and set the matched line index, then we can stop. + // If we haven't matched previously, then we should keep iterating. + return matchedLineIndex >= 0; + }); + + var stack = + infiniteLoopError + + "\n" + + (job.type || "Microtask") + + " - " + + (job.func.name || "anonymous") + + "\n" + + job.error.stack + .split("\n") + .slice(matchedLineIndex + 1) + .join("\n"); + + try { + Object.defineProperty(infiniteLoopError, "stack", { + value: stack, + }); + } catch (e) { + // noop + } + + return infiniteLoopError; + } + /** * @param {Date} target * @param {Date} source @@ -358,12 +428,13 @@ function withGlobal(_global) { for (var i = 0; i < clock.jobs.length; i++) { var job = clock.jobs[i]; job.func.apply(null, job.args); + + checkIsNearInfiniteLimit(clock, i); if (clock.loopLimit && i > clock.loopLimit) { - throw new Error( - `Aborting after running ${clock.loopLimit} timers, assuming an infinite loop!` - ); + throw getInfiniteLoopError(clock, job); } } + resetIsNearInfiniteLimit(); clock.jobs = []; } @@ -388,6 +459,10 @@ function withGlobal(_global) { } } + if (isNearInfiniteLimit) { + timer.error = new Error(); + } + timer.type = timer.immediate ? "Immediate" : "Timeout"; if (timer.hasOwnProperty("delay")) { @@ -946,6 +1021,7 @@ function withGlobal(_global) { return enqueueJob(clock, { func: func, args: Array.prototype.slice.call(arguments, 1), + error: isNearInfiniteLimit ? new Error() : null, }); }; @@ -1253,20 +1329,22 @@ function withGlobal(_global) { runJobs(clock); for (i = 0; i < clock.loopLimit; i++) { if (!clock.timers) { + resetIsNearInfiniteLimit(); return clock.now; } numTimers = Object.keys(clock.timers).length; if (numTimers === 0) { + resetIsNearInfiniteLimit(); return clock.now; } clock.next(); + checkIsNearInfiniteLimit(clock, i); } - throw new Error( - `Aborting after running ${clock.loopLimit} timers, assuming an infinite loop!` - ); + var excessJob = firstTimer(clock); + throw getInfiniteLoopError(clock, excessJob); }; clock.runToFrame = function runToFrame() { @@ -1286,6 +1364,7 @@ function withGlobal(_global) { var numTimers; if (i < clock.loopLimit) { if (!clock.timers) { + resetIsNearInfiniteLimit(); resolve(clock.now); return; } @@ -1293,6 +1372,7 @@ function withGlobal(_global) { numTimers = Object.keys(clock.timers) .length; if (numTimers === 0) { + resetIsNearInfiniteLimit(); resolve(clock.now); return; } @@ -1302,14 +1382,12 @@ function withGlobal(_global) { i++; doRun(); + checkIsNearInfiniteLimit(clock, i); return; } - reject( - new Error( - `Aborting after running ${clock.loopLimit} timers, assuming an infinite loop!` - ) - ); + var excessJob = firstTimer(clock); + reject(getInfiniteLoopError(clock, excessJob)); } catch (e) { reject(e); } diff --git a/test/fake-timers-test.js b/test/fake-timers-test.js index 8465eb5d..7498ab9f 100644 --- a/test/fake-timers-test.js +++ b/test/fake-timers-test.js @@ -4744,3 +4744,345 @@ describe("#347 - Support util.promisify once installed", function () { }); }); }); + +describe("loop limit stack trace", function () { + var test, + expectedMessage = + "Aborting after running 5 timers, assuming an infinite loop!"; + + beforeEach(function () { + test = this; + this.clock = FakeTimers.install({ loopLimit: 5 }); + }); + + afterEach(function () { + this.clock.uninstall(); + }); + + describe("queueMicrotask", function () { + beforeEach(function () { + function recursiveQueueMicroTask() { + test.clock.queueMicrotask(recursiveQueueMicroTask); + } + recursiveQueueMicroTask(); + }); + + it("provides a stack trace for running microtasks", function () { + var caughtError = false; + + try { + test.clock.runMicrotasks(); + } catch (err) { + caughtError = true; + assert.equals(err.message, expectedMessage); + assert.equals( + new RegExp( + "Error: " + + expectedMessage + + "\\s+Microtask - recursiveQueueMicroTask\\s+(at )*recursiveQueueMicroTask" + ).test(err.stack), + true + ); + } + assert.equals(caughtError, true); + }); + }); + + describe("nextTick", function () { + beforeEach(function () { + function recursiveQueueMicroTask() { + test.clock.nextTick(recursiveQueueMicroTask); + } + recursiveQueueMicroTask(); + }); + + it("provides a stack trace for running microtasks", function () { + var caughtError = false; + + try { + test.clock.runMicrotasks(); + } catch (err) { + caughtError = true; + assert.equals(err.message, expectedMessage); + assert.equals( + new RegExp( + "Error: " + + expectedMessage + + "\\s+Microtask - recursiveQueueMicroTask\\s+(at )*recursiveQueueMicroTask" + ).test(err.stack), + true + ); + } + assert.equals(caughtError, true); + }); + }); + + describe("setTimeout", function () { + beforeEach(function () { + function recursiveCreateTimer() { + setTimeout(function recursiveCreateTimerTimeout() { + recursiveCreateTimer(); + }, 10); + } + recursiveCreateTimer(); + }); + + it("provides a stack trace for running all async", function () { + var catchSpy = sinon.spy(); + + return test.clock + .runAllAsync() + .catch(catchSpy) + .then(function () { + assert(catchSpy.calledOnce); + var err = catchSpy.firstCall.args[0]; + assert.equals(err.message, expectedMessage); + assert.equals( + new RegExp( + "Error: " + + expectedMessage + + "\\s+Timeout - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + ).test(err.stack), + true + ); + }); + }); + + it("provides a stack trace for running all sync", function () { + var caughtError = false; + + try { + test.clock.runAll(); + } catch (err) { + caughtError = true; + assert.equals(err.message, expectedMessage); + assert.equals( + new RegExp( + "Error: " + + expectedMessage + + "\\s+Timeout - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + ).test(err.stack), + true + ); + } + assert.equals(caughtError, true); + }); + }); + + describe("requestIdleCallback", function () { + beforeEach(function () { + function recursiveCreateTimer() { + test.clock.requestIdleCallback( + function recursiveCreateTimerTimeout() { + recursiveCreateTimer(); + }, + 10 + ); + } + recursiveCreateTimer(); + }); + + it("provides a stack trace for running all async", function () { + var catchSpy = sinon.spy(); + + return test.clock + .runAllAsync() + .catch(catchSpy) + .then(function () { + assert(catchSpy.calledOnce); + var err = catchSpy.firstCall.args[0]; + assert.equals(err.message, expectedMessage); + assert.equals( + new RegExp( + "Error: " + + expectedMessage + + "\\s+Timeout - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + ).test(err.stack), + true + ); + }); + }); + + it("provides a stack trace for running all sync", function () { + var caughtError = false; + + try { + test.clock.runAll(); + } catch (err) { + caughtError = true; + assert.equals(err.message, expectedMessage); + assert.equals( + new RegExp( + "Error: " + + expectedMessage + + "\\s+Timeout - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + ).test(err.stack), + true + ); + } + assert.equals(caughtError, true); + }); + }); + + describe("setInterval", function () { + beforeEach(function () { + function recursiveCreateTimer() { + setInterval(function recursiveCreateTimerTimeout() { + recursiveCreateTimer(); + }, 10); + } + recursiveCreateTimer(); + }); + + it("provides a stack trace for running all async", function () { + var catchSpy = sinon.spy(); + + return test.clock + .runAllAsync() + .catch(catchSpy) + .then(function () { + assert(catchSpy.calledOnce); + var err = catchSpy.firstCall.args[0]; + assert.equals(err.message, expectedMessage); + assert.equals( + new RegExp( + "Error: " + + expectedMessage + + "\\s+Interval - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + ).test(err.stack), + true + ); + }); + }); + + it("provides a stack trace for running all sync", function () { + var caughtError = false; + + try { + test.clock.runAll(); + } catch (err) { + caughtError = true; + assert.equals(err.message, expectedMessage); + assert.equals( + new RegExp( + "Error: " + + expectedMessage + + "\\s+Interval - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + ).test(err.stack), + true + ); + } + assert.equals(caughtError, true); + }); + }); + + describe("setImmediate", function () { + beforeEach(function () { + if (!setImmediatePresent) { + this.skip(); + } + + function recursiveCreateTimer() { + setImmediate(function recursiveCreateTimerTimeout() { + recursiveCreateTimer(); + }); + } + recursiveCreateTimer(); + }); + + it("provides a stack trace for running all async", function () { + var catchSpy = sinon.spy(); + + return test.clock + .runAllAsync() + .catch(catchSpy) + .then(function () { + assert(catchSpy.calledOnce); + var err = catchSpy.firstCall.args[0]; + assert.equals(err.message, expectedMessage); + assert.equals( + new RegExp( + "Error: " + + expectedMessage + + "\\s+Immediate - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + ).test(err.stack), + true + ); + }); + }); + + it("provides a stack trace for running all sync", function () { + var caughtError = false; + + try { + test.clock.runAll(); + } catch (err) { + caughtError = true; + assert.equals(err.message, expectedMessage); + assert.equals( + new RegExp( + "Error: " + + expectedMessage + + "\\s+Immediate - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + ).test(err.stack), + true + ); + } + assert.equals(caughtError, true); + }); + }); + + describe("requestAnimationFrame", function () { + beforeEach(function () { + function recursiveCreateTimer() { + test.clock.requestAnimationFrame( + function recursiveCreateTimerTimeout() { + recursiveCreateTimer(); + } + ); + } + recursiveCreateTimer(); + }); + + it("provides a stack trace for running all async", function () { + var catchSpy = sinon.spy(); + + return test.clock + .runAllAsync() + .catch(catchSpy) + .then(function () { + assert(catchSpy.calledOnce); + var err = catchSpy.firstCall.args[0]; + assert.equals(err.message, expectedMessage); + assert.equals( + new RegExp( + "Error: " + + expectedMessage + + "\\s+AnimationFrame - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + ).test(err.stack), + true + ); + }); + }); + + it("provides a stack trace for running all sync", function () { + var caughtError = false; + + try { + test.clock.runAll(); + } catch (err) { + caughtError = true; + assert.equals(err.message, expectedMessage); + assert.equals( + new RegExp( + "Error: " + + expectedMessage + + "\\s+AnimationFrame - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + ).test(err.stack), + true + ); + } + assert.equals(caughtError, true); + }); + }); +}); From bfcd38b31c4f295d616f86cd174cee729218ee4c Mon Sep 17 00:00:00 2001 From: itay Date: Sat, 5 Jun 2021 12:35:28 +0300 Subject: [PATCH 2/4] small RegExp fix for node env + linting --- src/fake-timers-src.js | 39 ++++++++++++++++++-------------- test/fake-timers-test.js | 48 ++++++++++------------------------------ 2 files changed, 34 insertions(+), 53 deletions(-) diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js index 138879ce..4cd64b58 100644 --- a/src/fake-timers-src.js +++ b/src/fake-timers-src.js @@ -249,14 +249,24 @@ function withGlobal(_global) { function getInfiniteLoopError(clock, job) { var infiniteLoopError = new Error( - "Aborting after running " + - clock.loopLimit + - " timers, assuming an infinite loop!" + `Aborting after running ${clock.loopLimit} timers, assuming an infinite loop!` ); + + var clockMethodPattern; + + // pattern never matched in Firefox and Node var computedTargetPattern = /target\.*[<|(|[].*?[>|\]|)]\s*/; - var clockMethodPattern = new RegExp( - String(Object.keys(clock).join("|")) - ); + + if (addTimerReturnsObject) { + // node.js environment + clockMethodPattern = new RegExp( + `\\s+at (Object\\.)?(?:${Object.keys(clock).join("|")})\\s+` + ); + } else { + clockMethodPattern = new RegExp( + String(Object.keys(clock).join("|")) + ); + } var matchedLineIndex = -1; job.error.stack.split("\n").some(function (line, i) { @@ -282,17 +292,12 @@ function withGlobal(_global) { return matchedLineIndex >= 0; }); - var stack = - infiniteLoopError + - "\n" + - (job.type || "Microtask") + - " - " + - (job.func.name || "anonymous") + - "\n" + - job.error.stack - .split("\n") - .slice(matchedLineIndex + 1) - .join("\n"); + var stack = `${infiniteLoopError}\n${job.type || "Microtask"} - ${ + job.func.name || "anonymous" + }\n${job.error.stack + .split("\n") + .slice(matchedLineIndex + 1) + .join("\n")}`; try { Object.defineProperty(infiniteLoopError, "stack", { diff --git a/test/fake-timers-test.js b/test/fake-timers-test.js index 7498ab9f..710fde91 100644 --- a/test/fake-timers-test.js +++ b/test/fake-timers-test.js @@ -4777,9 +4777,7 @@ describe("loop limit stack trace", function () { assert.equals(err.message, expectedMessage); assert.equals( new RegExp( - "Error: " + - expectedMessage + - "\\s+Microtask - recursiveQueueMicroTask\\s+(at )*recursiveQueueMicroTask" + `Error: ${expectedMessage}\\s+Microtask - recursiveQueueMicroTask\\s+(at )*recursiveQueueMicroTask` ).test(err.stack), true ); @@ -4806,9 +4804,7 @@ describe("loop limit stack trace", function () { assert.equals(err.message, expectedMessage); assert.equals( new RegExp( - "Error: " + - expectedMessage + - "\\s+Microtask - recursiveQueueMicroTask\\s+(at )*recursiveQueueMicroTask" + `Error: ${expectedMessage}\\s+Microtask - recursiveQueueMicroTask\\s+(at )*recursiveQueueMicroTask` ).test(err.stack), true ); @@ -4839,9 +4835,7 @@ describe("loop limit stack trace", function () { assert.equals(err.message, expectedMessage); assert.equals( new RegExp( - "Error: " + - expectedMessage + - "\\s+Timeout - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + `Error: ${expectedMessage}\\s+Timeout - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer` ).test(err.stack), true ); @@ -4858,9 +4852,7 @@ describe("loop limit stack trace", function () { assert.equals(err.message, expectedMessage); assert.equals( new RegExp( - "Error: " + - expectedMessage + - "\\s+Timeout - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + `Error: ${expectedMessage}\\s+Timeout - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer` ).test(err.stack), true ); @@ -4894,9 +4886,7 @@ describe("loop limit stack trace", function () { assert.equals(err.message, expectedMessage); assert.equals( new RegExp( - "Error: " + - expectedMessage + - "\\s+Timeout - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + `Error: ${expectedMessage}\\s+Timeout - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer` ).test(err.stack), true ); @@ -4913,9 +4903,7 @@ describe("loop limit stack trace", function () { assert.equals(err.message, expectedMessage); assert.equals( new RegExp( - "Error: " + - expectedMessage + - "\\s+Timeout - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + `Error: ${expectedMessage}\\s+Timeout - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer` ).test(err.stack), true ); @@ -4946,9 +4934,7 @@ describe("loop limit stack trace", function () { assert.equals(err.message, expectedMessage); assert.equals( new RegExp( - "Error: " + - expectedMessage + - "\\s+Interval - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + `Error: ${expectedMessage}\\s+Interval - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer` ).test(err.stack), true ); @@ -4965,9 +4951,7 @@ describe("loop limit stack trace", function () { assert.equals(err.message, expectedMessage); assert.equals( new RegExp( - "Error: " + - expectedMessage + - "\\s+Interval - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + `Error: ${expectedMessage}\\s+Interval - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer` ).test(err.stack), true ); @@ -5002,9 +4986,7 @@ describe("loop limit stack trace", function () { assert.equals(err.message, expectedMessage); assert.equals( new RegExp( - "Error: " + - expectedMessage + - "\\s+Immediate - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + `Error: ${expectedMessage}\\s+Immediate - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer` ).test(err.stack), true ); @@ -5021,9 +5003,7 @@ describe("loop limit stack trace", function () { assert.equals(err.message, expectedMessage); assert.equals( new RegExp( - "Error: " + - expectedMessage + - "\\s+Immediate - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + `Error: ${expectedMessage}\\s+Immediate - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer` ).test(err.stack), true ); @@ -5056,9 +5036,7 @@ describe("loop limit stack trace", function () { assert.equals(err.message, expectedMessage); assert.equals( new RegExp( - "Error: " + - expectedMessage + - "\\s+AnimationFrame - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + `Error: ${expectedMessage}\\s+AnimationFrame - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer` ).test(err.stack), true ); @@ -5075,9 +5053,7 @@ describe("loop limit stack trace", function () { assert.equals(err.message, expectedMessage); assert.equals( new RegExp( - "Error: " + - expectedMessage + - "\\s+AnimationFrame - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer" + `Error: ${expectedMessage}\\s+AnimationFrame - recursiveCreateTimerTimeout\\s+(at )*recursiveCreateTimer` ).test(err.stack), true ); From 6bab5f3f9c2309541aa66984bc549bcef0163d1b Mon Sep 17 00:00:00 2001 From: itay Date: Sat, 5 Jun 2021 17:05:12 +0300 Subject: [PATCH 3/4] improving test-coverage statistics --- src/fake-timers-src.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js index 4cd64b58..ffad2883 100644 --- a/src/fake-timers-src.js +++ b/src/fake-timers-src.js @@ -252,30 +252,30 @@ function withGlobal(_global) { `Aborting after running ${clock.loopLimit} timers, assuming an infinite loop!` ); - var clockMethodPattern; - - // pattern never matched in Firefox and Node + // pattern never matched in Node var computedTargetPattern = /target\.*[<|(|[].*?[>|\]|)]\s*/; + var clockMethodPattern = new RegExp( + String(Object.keys(clock).join("|")) + ); if (addTimerReturnsObject) { // node.js environment clockMethodPattern = new RegExp( `\\s+at (Object\\.)?(?:${Object.keys(clock).join("|")})\\s+` ); - } else { - clockMethodPattern = new RegExp( - String(Object.keys(clock).join("|")) - ); } var matchedLineIndex = -1; job.error.stack.split("\n").some(function (line, i) { - // If we've matched a computed target line (e.g. setTimeout) then we - // don't need to look any further. Return true to stop iterating. - var matchedComputedTarget = line.match(computedTargetPattern); - if (matchedComputedTarget) { - matchedLineIndex = i; - return true; + if (!addTimerReturnsObject) { + // when not in node + // If we've matched a computed target line (e.g. setTimeout) then we + // don't need to look any further. Return true to stop iterating. + var matchedComputedTarget = line.match(computedTargetPattern); + if (matchedComputedTarget) { + matchedLineIndex = i; + return true; + } } // If we've matched a clock method line, then there may still be From ee3e90167b7b8a4ac0b1d1ece8da9cee8315f14a Mon Sep 17 00:00:00 2001 From: itay Date: Mon, 7 Jun 2021 00:39:04 +0300 Subject: [PATCH 4/4] at attempt to ignore certain condition in nyc coverage --- src/fake-timers-src.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js index ffad2883..3121316e 100644 --- a/src/fake-timers-src.js +++ b/src/fake-timers-src.js @@ -267,15 +267,13 @@ function withGlobal(_global) { var matchedLineIndex = -1; job.error.stack.split("\n").some(function (line, i) { - if (!addTimerReturnsObject) { - // when not in node - // If we've matched a computed target line (e.g. setTimeout) then we - // don't need to look any further. Return true to stop iterating. - var matchedComputedTarget = line.match(computedTargetPattern); - if (matchedComputedTarget) { - matchedLineIndex = i; - return true; - } + // If we've matched a computed target line (e.g. setTimeout) then we + // don't need to look any further. Return true to stop iterating. + var matchedComputedTarget = line.match(computedTargetPattern); + /* istanbul ignore if */ + if (matchedComputedTarget) { + matchedLineIndex = i; + return true; } // If we've matched a clock method line, then there may still be