From d3a3cf1ff3871500755d584c5adbb6a2ffec3320 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Mon, 18 Dec 2017 09:43:25 -0800 Subject: [PATCH 1/6] Collapsed StringPrettyPrinter into PrettyPrinter We never really took advantage of the potential extensibility that the split provided, and the boundary between the two was getting muddied over time. --- lib/jasmine-core/jasmine.js | 43 +++++++++++++------------------------ src/core/PrettyPrinter.js | 43 +++++++++++++------------------------ 2 files changed, 30 insertions(+), 56 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 260bb289..6b06fd18 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -3961,6 +3961,8 @@ getJasmineRequireObj().pp = function(j$) { function PrettyPrinter() { this.ppNestLevel_ = 0; this.seen = []; + this.length = 0; + this.stringParts = []; } function hasCustomToString(value) { @@ -4044,31 +4046,15 @@ getJasmineRequireObj().pp = function(j$) { return objKeys.length > length; }; - PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_; - PrettyPrinter.prototype.emitSet = j$.unimplementedMethod_; - PrettyPrinter.prototype.emitMap = j$.unimplementedMethod_; - PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_; - PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_; - PrettyPrinter.prototype.emitString = j$.unimplementedMethod_; - - function StringPrettyPrinter() { - PrettyPrinter.call(this); - - this.length = 0; - this.stringParts = []; - } - - j$.util.inherit(StringPrettyPrinter, PrettyPrinter); - - StringPrettyPrinter.prototype.emitScalar = function(value) { + PrettyPrinter.prototype.emitScalar = function(value) { this.append(value); }; - StringPrettyPrinter.prototype.emitString = function(value) { + PrettyPrinter.prototype.emitString = function(value) { this.append('\'' + value + '\''); }; - StringPrettyPrinter.prototype.emitArray = function(array) { + PrettyPrinter.prototype.emitArray = function(array) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Array'); return; @@ -4102,7 +4088,7 @@ getJasmineRequireObj().pp = function(j$) { this.append(' ]'); }; - StringPrettyPrinter.prototype.emitSet = function(set) { + PrettyPrinter.prototype.emitSet = function(set) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Set'); return; @@ -4122,7 +4108,7 @@ getJasmineRequireObj().pp = function(j$) { this.append(' )'); }; - StringPrettyPrinter.prototype.emitMap = function(map) { + PrettyPrinter.prototype.emitMap = function(map) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Map'); return; @@ -4142,7 +4128,7 @@ getJasmineRequireObj().pp = function(j$) { this.append(' )'); }; - StringPrettyPrinter.prototype.emitObject = function(obj) { + PrettyPrinter.prototype.emitObject = function(obj) { var ctor = obj.constructor, constructorName; @@ -4175,7 +4161,7 @@ getJasmineRequireObj().pp = function(j$) { this.append(' })'); }; - StringPrettyPrinter.prototype.emitTypedArray = function(arr) { + PrettyPrinter.prototype.emitTypedArray = function(arr) { var constructorName = j$.fnNameFor(arr.constructor), limitedArray = Array.prototype.slice.call(arr, 0, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH), itemsString = Array.prototype.join.call(limitedArray, ', '); @@ -4187,7 +4173,7 @@ getJasmineRequireObj().pp = function(j$) { this.append(constructorName + ' [ ' + itemsString + ' ]'); }; - StringPrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) { + PrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) { this.append(property); this.append(': '); if (isGetter) { @@ -4197,7 +4183,7 @@ getJasmineRequireObj().pp = function(j$) { } }; - StringPrettyPrinter.prototype.append = function(value) { + PrettyPrinter.prototype.append = function(value) { var result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); this.length += result.value.length; this.stringParts.push(result.value); @@ -4207,6 +4193,7 @@ getJasmineRequireObj().pp = function(j$) { } }; + function truncate(s, maxlen) { if (s.length <= maxlen) { return { value: s, truncated: false }; @@ -4253,9 +4240,9 @@ getJasmineRequireObj().pp = function(j$) { return extraKeys; } return function(value) { - var stringPrettyPrinter = new StringPrettyPrinter(); - stringPrettyPrinter.format(value); - return stringPrettyPrinter.stringParts.join(''); + var prettyPrinter = new PrettyPrinter(); + prettyPrinter.format(value); + return prettyPrinter.stringParts.join(''); }; }; diff --git a/src/core/PrettyPrinter.js b/src/core/PrettyPrinter.js index 0685fa24..88746102 100644 --- a/src/core/PrettyPrinter.js +++ b/src/core/PrettyPrinter.js @@ -3,6 +3,8 @@ getJasmineRequireObj().pp = function(j$) { function PrettyPrinter() { this.ppNestLevel_ = 0; this.seen = []; + this.length = 0; + this.stringParts = []; } function hasCustomToString(value) { @@ -86,31 +88,15 @@ getJasmineRequireObj().pp = function(j$) { return objKeys.length > length; }; - PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_; - PrettyPrinter.prototype.emitSet = j$.unimplementedMethod_; - PrettyPrinter.prototype.emitMap = j$.unimplementedMethod_; - PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_; - PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_; - PrettyPrinter.prototype.emitString = j$.unimplementedMethod_; - - function StringPrettyPrinter() { - PrettyPrinter.call(this); - - this.length = 0; - this.stringParts = []; - } - - j$.util.inherit(StringPrettyPrinter, PrettyPrinter); - - StringPrettyPrinter.prototype.emitScalar = function(value) { + PrettyPrinter.prototype.emitScalar = function(value) { this.append(value); }; - StringPrettyPrinter.prototype.emitString = function(value) { + PrettyPrinter.prototype.emitString = function(value) { this.append('\'' + value + '\''); }; - StringPrettyPrinter.prototype.emitArray = function(array) { + PrettyPrinter.prototype.emitArray = function(array) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Array'); return; @@ -144,7 +130,7 @@ getJasmineRequireObj().pp = function(j$) { this.append(' ]'); }; - StringPrettyPrinter.prototype.emitSet = function(set) { + PrettyPrinter.prototype.emitSet = function(set) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Set'); return; @@ -164,7 +150,7 @@ getJasmineRequireObj().pp = function(j$) { this.append(' )'); }; - StringPrettyPrinter.prototype.emitMap = function(map) { + PrettyPrinter.prototype.emitMap = function(map) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Map'); return; @@ -184,7 +170,7 @@ getJasmineRequireObj().pp = function(j$) { this.append(' )'); }; - StringPrettyPrinter.prototype.emitObject = function(obj) { + PrettyPrinter.prototype.emitObject = function(obj) { var ctor = obj.constructor, constructorName; @@ -217,7 +203,7 @@ getJasmineRequireObj().pp = function(j$) { this.append(' })'); }; - StringPrettyPrinter.prototype.emitTypedArray = function(arr) { + PrettyPrinter.prototype.emitTypedArray = function(arr) { var constructorName = j$.fnNameFor(arr.constructor), limitedArray = Array.prototype.slice.call(arr, 0, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH), itemsString = Array.prototype.join.call(limitedArray, ', '); @@ -229,7 +215,7 @@ getJasmineRequireObj().pp = function(j$) { this.append(constructorName + ' [ ' + itemsString + ' ]'); }; - StringPrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) { + PrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) { this.append(property); this.append(': '); if (isGetter) { @@ -239,7 +225,7 @@ getJasmineRequireObj().pp = function(j$) { } }; - StringPrettyPrinter.prototype.append = function(value) { + PrettyPrinter.prototype.append = function(value) { var result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); this.length += result.value.length; this.stringParts.push(result.value); @@ -249,6 +235,7 @@ getJasmineRequireObj().pp = function(j$) { } }; + function truncate(s, maxlen) { if (s.length <= maxlen) { return { value: s, truncated: false }; @@ -295,8 +282,8 @@ getJasmineRequireObj().pp = function(j$) { return extraKeys; } return function(value) { - var stringPrettyPrinter = new StringPrettyPrinter(); - stringPrettyPrinter.format(value); - return stringPrettyPrinter.stringParts.join(''); + var prettyPrinter = new PrettyPrinter(); + prettyPrinter.format(value); + return prettyPrinter.stringParts.join(''); }; }; From b6cc34d9e94de82df94db506f75af6146b410855 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Mon, 18 Dec 2017 16:34:37 -0800 Subject: [PATCH 2/6] Correctly remove spies of window.onerror on IE --- lib/jasmine-core/jasmine.js | 3 ++- spec/core/SpyRegistrySpec.js | 18 +++++++++++++++++ spec/html/SpyRegistryHtmlSpec.js | 34 ++++++++++++++++++++++++++++++++ src/core/SpyRegistry.js | 3 ++- 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 spec/html/SpyRegistryHtmlSpec.js diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 6b06fd18..e0778d74 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -4809,6 +4809,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) { function SpyRegistry(options) { options = options || {}; + var global = options.global || j$.getGlobal(); var currentSpies = options.currentSpies || function() { return []; }; this.allowRespy = function(allow){ @@ -4852,7 +4853,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) { spiedMethod = j$.createSpy(methodName, originalMethod), restoreStrategy; - if (Object.prototype.hasOwnProperty.call(obj, methodName)) { + if (Object.prototype.hasOwnProperty.call(obj, methodName) || (obj === global && methodName === 'onerror')) { restoreStrategy = function() { obj[methodName] = originalMethod; }; diff --git a/spec/core/SpyRegistrySpec.js b/spec/core/SpyRegistrySpec.js index 4f09c932..1d01a3c4 100644 --- a/spec/core/SpyRegistrySpec.js +++ b/spec/core/SpyRegistrySpec.js @@ -287,6 +287,24 @@ describe("SpyRegistry", function() { expect(jasmineUnderTest.isSpy(subject.spiedFunc)).toBe(false); }); + + it("restores window.onerror by overwriting, not deleting", function() { + function FakeWindow() { + } + FakeWindow.prototype.onerror = function() {}; + + var spies = [], + global = new FakeWindow(), + spyRegistry = new jasmineUnderTest.SpyRegistry({ + currentSpies: function() { return spies; }, + global: global + }); + + spyRegistry.spyOn(global, 'onerror'); + spyRegistry.clearSpies(); + expect(global.onerror).toBe(FakeWindow.prototype.onerror); + expect(global.hasOwnProperty('onerror')).toBe(true); + }); }); describe('spying on properties', function() { diff --git a/spec/html/SpyRegistryHtmlSpec.js b/spec/html/SpyRegistryHtmlSpec.js new file mode 100644 index 00000000..fd9a81fb --- /dev/null +++ b/spec/html/SpyRegistryHtmlSpec.js @@ -0,0 +1,34 @@ +describe('Spy Registry browser-specific behavior', function() { + it('can spy on and unspy window.onerror', function() { + requireWriteableOnerror(); + + var spies = [], + spyRegistry = new jasmineUnderTest.SpyRegistry({ + currentSpies: function() { return spies; }, + global: window + }), + originalHandler = window.onerror; + + try { + spyRegistry.spyOn(window, 'onerror'); + spyRegistry.clearSpies(); + expect(window.onerror).toBe(originalHandler); + } finally { + window.onerror = originalHandler; + } + }); + + function requireWriteableOnerror() { + var descriptor; + + try { + descriptor = Object.getOwnPropertyDescriptor(window, 'onerror'); + } catch(e) { + // IE 8 doesn't support `definePropery` on non-DOM nodes + } + + if (descriptor && !(descriptor.writable || descriptor.set)) { + pending('Browser declares window.onerror to be readonly'); + } + } +}); diff --git a/src/core/SpyRegistry.js b/src/core/SpyRegistry.js index d8d4cf7f..74a1db6d 100644 --- a/src/core/SpyRegistry.js +++ b/src/core/SpyRegistry.js @@ -4,6 +4,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) { function SpyRegistry(options) { options = options || {}; + var global = options.global || j$.getGlobal(); var currentSpies = options.currentSpies || function() { return []; }; this.allowRespy = function(allow){ @@ -47,7 +48,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) { spiedMethod = j$.createSpy(methodName, originalMethod), restoreStrategy; - if (Object.prototype.hasOwnProperty.call(obj, methodName)) { + if (Object.prototype.hasOwnProperty.call(obj, methodName) || (obj === global && methodName === 'onerror')) { restoreStrategy = function() { obj[methodName] = originalMethod; }; From 65b4499dec680508e3e63dd8d921404d94c68b85 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Wed, 20 Dec 2017 12:04:43 -0800 Subject: [PATCH 3/6] In Jasmine's own tests, don't leak between jasmine and jasmineUnderTest --- lib/jasmine-core/jasmine.js | 2 +- spec/support/jasmine.yml | 1 + src/core/requireCore.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index e0778d74..ec4876c7 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -34,7 +34,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) { if (typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]') { jasmineGlobal = window; } - jasmineRequire = jasmineGlobal.jasmineRequire = jasmineGlobal.jasmineRequire || {}; + jasmineRequire = jasmineGlobal.jasmineRequire = {}; } function getJasmineRequire() { diff --git a/spec/support/jasmine.yml b/spec/support/jasmine.yml index eb6a784d..545a2cb9 100644 --- a/spec/support/jasmine.yml +++ b/spec/support/jasmine.yml @@ -3,6 +3,7 @@ src_dir: - 'src' src_files: + - 'core/requireCore.js' - 'core/base.js' - 'core/util.js' #end of known dependencies diff --git a/src/core/requireCore.js b/src/core/requireCore.js index 44e868e3..159e75a8 100644 --- a/src/core/requireCore.js +++ b/src/core/requireCore.js @@ -12,7 +12,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) { if (typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]') { jasmineGlobal = window; } - jasmineRequire = jasmineGlobal.jasmineRequire = jasmineGlobal.jasmineRequire || {}; + jasmineRequire = jasmineGlobal.jasmineRequire = {}; } function getJasmineRequire() { From 62b815c485fbe2b62e6ba67e827cbf9144da393c Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 21 Dec 2017 10:51:07 -0800 Subject: [PATCH 4/6] Use timeout objects when in node Fixes: https://github.com/jasmine/jasmine/issues/1469 --- spec/core/ClockSpec.js | 61 +++++++++++++++++++++++++++++++++--------- src/core/Clock.js | 37 +++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 15 deletions(-) diff --git a/spec/core/ClockSpec.js b/spec/core/ClockSpec.js index 6b3ed1d0..bcce2fa1 100644 --- a/spec/core/ClockSpec.js +++ b/spec/core/ClockSpec.js @@ -1,5 +1,7 @@ describe("Clock", function() { + var NODE_JS = typeof process !== 'undefined' && process.versions && typeof process.versions.node === 'string'; + it("does not replace setTimeout until it is installed", function() { var fakeSetTimeout = jasmine.createSpy("global setTimeout"), fakeGlobal = { setTimeout: fakeSetTimeout }, @@ -294,13 +296,19 @@ describe("Clock", function() { fakeGlobal = { setTimeout: fakeSetTimeout }, delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); + clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate), + timeout = new clock.FakeTimeout(); clock.install(); clock.setTimeout(delayedFn, 0, 'a', 'b'); expect(fakeSetTimeout).not.toHaveBeenCalled(); - expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(delayedFn, 0, ['a', 'b']); + + if (!NODE_JS) { + expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(delayedFn, 0, ['a', 'b']); + } else { + expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(delayedFn, 0, ['a', 'b'], false, timeout); + } }); it("returns an id for the delayed function", function() { @@ -312,12 +320,16 @@ describe("Clock", function() { delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate), - timeoutId; + timeout; clock.install(); - timeoutId = clock.setTimeout(delayedFn, 0); + timeout = clock.setTimeout(delayedFn, 0); - expect(timeoutId).toEqual(123); + if (!NODE_JS) { + expect(timeout).toEqual(123); + } else { + expect(timeout.constructor.name).toEqual('FakeTimeout'); + } }); it("clears the scheduled function with the scheduler", function() { @@ -342,13 +354,19 @@ describe("Clock", function() { fakeGlobal = { setInterval: fakeSetInterval }, delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); + clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate), + timeout = new clock.FakeTimeout; clock.install(); clock.setInterval(delayedFn, 0, 'a', 'b'); expect(fakeSetInterval).not.toHaveBeenCalled(); - expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(delayedFn, 0, ['a', 'b'], true); + + if (!NODE_JS) { + expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(delayedFn, 0, ['a', 'b'], true); + } else { + expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(delayedFn, 0, ['a', 'b'], true, timeout); + } }); it("returns an id for the delayed function", function() { @@ -360,12 +378,16 @@ describe("Clock", function() { delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate), - intervalId; + interval; clock.install(); - intervalId = clock.setInterval(delayedFn, 0); + interval = clock.setInterval(delayedFn, 0); - expect(intervalId).toEqual(123); + if (!NODE_JS) { + expect(interval).toEqual(123); + } else { + expect(interval.constructor.name).toEqual('FakeTimeout'); + } }); it("clears the scheduled function with the scheduler", function() { @@ -401,7 +423,8 @@ describe("Clock", function() { setInterval: fakeSetInterval }, mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); + clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate), + timeout = new clock.FakeTimeout(); fakeSetTimeout.apply = null; fakeSetInterval.apply = null; @@ -409,13 +432,25 @@ describe("Clock", function() { clock.install(); clock.setTimeout(fn, 0); - expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, []); + + if (!NODE_JS) { + expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, []); + } else { + expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, [], false, timeout); + } + expect(function() { clock.setTimeout(fn, 0, 'extra'); }).toThrow(); clock.setInterval(fn, 0); - expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, [], true); + + if (!NODE_JS) { + expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, [], true); + } else { + expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, [], true, timeout); + } + expect(function() { clock.setInterval(fn, 0, 'extra'); }).toThrow(); diff --git a/src/core/Clock.js b/src/core/Clock.js index 52fafcfe..21829c87 100644 --- a/src/core/Clock.js +++ b/src/core/Clock.js @@ -1,4 +1,7 @@ getJasmineRequireObj().Clock = function() { + + var NODE_JS = typeof process !== 'undefined' && process.versions && typeof process.versions.node === 'string'; + /** * _Note:_ Do not construct this directly, Jasmine will make one during booting. You can get the current clock with {@link jasmine.clock}. * @class Clock @@ -22,6 +25,7 @@ getJasmineRequireObj().Clock = function() { delayedFunctionScheduler, timer; + self.FakeTimeout = FakeTimeout; /** * Install the mock clock over the built-in methods. @@ -145,7 +149,15 @@ getJasmineRequireObj().Clock = function() { } function setTimeout(fn, delay) { - return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2)); + if (!NODE_JS) { + return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2)); + } + + var timeout = new FakeTimeout(); + + delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2), false, timeout); + + return timeout; } function clearTimeout(id) { @@ -153,7 +165,15 @@ getJasmineRequireObj().Clock = function() { } function setInterval(fn, interval) { - return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true); + if (!NODE_JS) { + return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true); + } + + var timeout = new FakeTimeout(); + + delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true, timeout); + + return timeout; } function clearInterval(id) { @@ -165,5 +185,18 @@ getJasmineRequireObj().Clock = function() { } } + /** + * Mocks Node.js Timeout class + */ + function FakeTimeout() {} + + FakeTimeout.prototype.ref = function () { + return this; + }; + + FakeTimeout.prototype.unref = function () { + return this; + }; + return Clock; }; From b439e2fb37c68f81f8a9ba50b2e4f41599088081 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 23 Dec 2017 11:09:15 -0800 Subject: [PATCH 5/6] Extracted results state management out of the HTML reporter [#153891435] --- lib/jasmine-core/jasmine-html.js | 80 ++++++++++++++++++++------------ src/html/HtmlReporter.js | 80 ++++++++++++++++++++------------ 2 files changed, 102 insertions(+), 58 deletions(-) diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index a33e6c17..3d241f59 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -34,6 +34,45 @@ jasmineRequire.HtmlReporter = function(j$) { elapsed: function() { return 0; } }; + function ResultsStateBuilder() { + this.topResults = new j$.ResultsNode({}, '', null); + this.currentParent = this.topResults; + this.specsExecuted = 0; + this.failureCount = 0; + this.pendingSpecCount = 0; + } + + ResultsStateBuilder.prototype.suiteStarted = function(result) { + this.currentParent.addChild(result, 'suite'); + this.currentParent = this.currentParent.last(); + }; + + ResultsStateBuilder.prototype.suiteDone = function(result) { + if (this.currentParent !== this.topResults) { + this.currentParent = this.currentParent.parent; + } + }; + + ResultsStateBuilder.prototype.specStarted = function(result) { + this.currentParent.addChild(result, 'spec'); + }; + + ResultsStateBuilder.prototype.specDone = function(result) { + if (result.status !== 'disabled') { + this.specsExecuted++; + } + + if (result.status === 'failed') { + this.failureCount++; + } + + if (result.status == 'pending') { + this.pendingSpecCount++; + } + }; + + + function HtmlReporter(options) { var env = options.env || {}, getContainer = options.getContainer, @@ -46,9 +85,6 @@ jasmineRequire.HtmlReporter = function(j$) { filterSpecs = options.filterSpecs, timer = options.timer || noopTimer, results = [], - specsExecuted = 0, - failureCount = 0, - pendingSpecCount = 0, htmlReporterMain, symbols, failedSuites = []; @@ -77,12 +113,10 @@ jasmineRequire.HtmlReporter = function(j$) { var summary = createDom('div', {className: 'jasmine-summary'}); - var topResults = new j$.ResultsNode({}, '', null), - currentParent = topResults; + var stateBuilder = new ResultsStateBuilder(); this.suiteStarted = function(result) { - currentParent.addChild(result, 'suite'); - currentParent = currentParent.last(); + stateBuilder.suiteStarted(result); }; this.suiteDone = function(result) { @@ -90,27 +124,21 @@ jasmineRequire.HtmlReporter = function(j$) { failedSuites.push(result); } - if (currentParent == topResults) { - return; - } - - currentParent = currentParent.parent; + stateBuilder.suiteDone(result); }; this.specStarted = function(result) { - currentParent.addChild(result, 'spec'); + stateBuilder.specStarted(result); }; var failures = []; this.specDone = function(result) { + stateBuilder.specDone(result); + if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') { console.error('Spec \'' + result.fullName + '\' has no expectations.'); } - if (result.status != 'disabled') { - specsExecuted++; - } - if (!symbols){ symbols = find('.jasmine-symbol-summary'); } @@ -123,8 +151,6 @@ jasmineRequire.HtmlReporter = function(j$) { )); if (result.status == 'failed') { - failureCount++; - var failure = createDom('div', {className: 'jasmine-spec-detail jasmine-failed'}, createDom('div', {className: 'jasmine-description'}, @@ -142,10 +168,6 @@ jasmineRequire.HtmlReporter = function(j$) { failures.push(failure); } - - if (result.status == 'pending') { - pendingSpecCount++; - } }; this.jasmineDone = function(doneResult) { @@ -208,8 +230,8 @@ jasmineRequire.HtmlReporter = function(j$) { } }; - if (specsExecuted < totalSpecsDefined) { - var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; + if (stateBuilder.specsExecuted < totalSpecsDefined) { + var skippedMessage = 'Ran ' + stateBuilder.specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; var skippedLink = addToExistingQueryString('spec', ''); alert.appendChild( createDom('span', {className: 'jasmine-bar jasmine-skipped'}, @@ -221,9 +243,9 @@ jasmineRequire.HtmlReporter = function(j$) { var statusBarClassName = 'jasmine-bar '; if (totalSpecsDefined > 0) { - statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount); - if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); } - statusBarClassName += (failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed'; + statusBarMessage += pluralize('spec', stateBuilder.specsExecuted) + ', ' + pluralize('failure', stateBuilder.failureCount); + if (stateBuilder.pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', stateBuilder.pendingSpecCount); } + statusBarClassName += (stateBuilder.failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed'; } else { statusBarClassName += 'jasmine-skipped'; statusBarMessage += 'No specs found'; @@ -258,7 +280,7 @@ jasmineRequire.HtmlReporter = function(j$) { var results = find('.jasmine-results'); results.appendChild(summary); - summaryList(topResults, summary); + summaryList(stateBuilder.topResults, summary); function summaryList(resultsTree, domParent) { var specListNode; diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index 02a9e1ce..27baf7da 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -5,6 +5,45 @@ jasmineRequire.HtmlReporter = function(j$) { elapsed: function() { return 0; } }; + function ResultsStateBuilder() { + this.topResults = new j$.ResultsNode({}, '', null); + this.currentParent = this.topResults; + this.specsExecuted = 0; + this.failureCount = 0; + this.pendingSpecCount = 0; + } + + ResultsStateBuilder.prototype.suiteStarted = function(result) { + this.currentParent.addChild(result, 'suite'); + this.currentParent = this.currentParent.last(); + }; + + ResultsStateBuilder.prototype.suiteDone = function(result) { + if (this.currentParent !== this.topResults) { + this.currentParent = this.currentParent.parent; + } + }; + + ResultsStateBuilder.prototype.specStarted = function(result) { + this.currentParent.addChild(result, 'spec'); + }; + + ResultsStateBuilder.prototype.specDone = function(result) { + if (result.status !== 'disabled') { + this.specsExecuted++; + } + + if (result.status === 'failed') { + this.failureCount++; + } + + if (result.status == 'pending') { + this.pendingSpecCount++; + } + }; + + + function HtmlReporter(options) { var env = options.env || {}, getContainer = options.getContainer, @@ -17,9 +56,6 @@ jasmineRequire.HtmlReporter = function(j$) { filterSpecs = options.filterSpecs, timer = options.timer || noopTimer, results = [], - specsExecuted = 0, - failureCount = 0, - pendingSpecCount = 0, htmlReporterMain, symbols, failedSuites = []; @@ -48,12 +84,10 @@ jasmineRequire.HtmlReporter = function(j$) { var summary = createDom('div', {className: 'jasmine-summary'}); - var topResults = new j$.ResultsNode({}, '', null), - currentParent = topResults; + var stateBuilder = new ResultsStateBuilder(); this.suiteStarted = function(result) { - currentParent.addChild(result, 'suite'); - currentParent = currentParent.last(); + stateBuilder.suiteStarted(result); }; this.suiteDone = function(result) { @@ -61,27 +95,21 @@ jasmineRequire.HtmlReporter = function(j$) { failedSuites.push(result); } - if (currentParent == topResults) { - return; - } - - currentParent = currentParent.parent; + stateBuilder.suiteDone(result); }; this.specStarted = function(result) { - currentParent.addChild(result, 'spec'); + stateBuilder.specStarted(result); }; var failures = []; this.specDone = function(result) { + stateBuilder.specDone(result); + if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') { console.error('Spec \'' + result.fullName + '\' has no expectations.'); } - if (result.status != 'disabled') { - specsExecuted++; - } - if (!symbols){ symbols = find('.jasmine-symbol-summary'); } @@ -94,8 +122,6 @@ jasmineRequire.HtmlReporter = function(j$) { )); if (result.status == 'failed') { - failureCount++; - var failure = createDom('div', {className: 'jasmine-spec-detail jasmine-failed'}, createDom('div', {className: 'jasmine-description'}, @@ -113,10 +139,6 @@ jasmineRequire.HtmlReporter = function(j$) { failures.push(failure); } - - if (result.status == 'pending') { - pendingSpecCount++; - } }; this.jasmineDone = function(doneResult) { @@ -179,8 +201,8 @@ jasmineRequire.HtmlReporter = function(j$) { } }; - if (specsExecuted < totalSpecsDefined) { - var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; + if (stateBuilder.specsExecuted < totalSpecsDefined) { + var skippedMessage = 'Ran ' + stateBuilder.specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; var skippedLink = addToExistingQueryString('spec', ''); alert.appendChild( createDom('span', {className: 'jasmine-bar jasmine-skipped'}, @@ -192,9 +214,9 @@ jasmineRequire.HtmlReporter = function(j$) { var statusBarClassName = 'jasmine-bar '; if (totalSpecsDefined > 0) { - statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount); - if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); } - statusBarClassName += (failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed'; + statusBarMessage += pluralize('spec', stateBuilder.specsExecuted) + ', ' + pluralize('failure', stateBuilder.failureCount); + if (stateBuilder.pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', stateBuilder.pendingSpecCount); } + statusBarClassName += (stateBuilder.failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed'; } else { statusBarClassName += 'jasmine-skipped'; statusBarMessage += 'No specs found'; @@ -229,7 +251,7 @@ jasmineRequire.HtmlReporter = function(j$) { var results = find('.jasmine-results'); results.appendChild(summary); - summaryList(topResults, summary); + summaryList(stateBuilder.topResults, summary); function summaryList(resultsTree, domParent) { var specListNode; From b943f6b736f034de3c79ee3db3261dc4328fcea0 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 23 Dec 2017 11:14:37 -0800 Subject: [PATCH 6/6] Fixed SPEC HAS NO EXPECTATIONS warning in HTML reporter [Finishes #153891435] --- lib/jasmine-core/jasmine-html.js | 3 ++- spec/html/HtmlReporterSpec.js | 4 ++-- src/html/HtmlReporter.js | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index 3d241f59..2ce54d1b 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -54,10 +54,11 @@ jasmineRequire.HtmlReporter = function(j$) { }; ResultsStateBuilder.prototype.specStarted = function(result) { - this.currentParent.addChild(result, 'spec'); }; ResultsStateBuilder.prototype.specDone = function(result) { + this.currentParent.addChild(result, 'spec'); + if (result.status !== 'disabled') { this.specsExecuted++; } diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index 7047ea56..59138c8a 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -209,7 +209,7 @@ describe("New HtmlReporter", function() { }); describe("when Jasmine is done", function() { - it("adds EMPTY to the link title of specs that have no expectations", function() { + it("adds a warning to the link title of specs that have no expectations", function() { if (!window.console) { window.console = { error: function(){} }; } @@ -228,7 +228,7 @@ describe("New HtmlReporter", function() { reporter.initialize(); reporter.jasmineStarted({}); reporter.suiteStarted({id: 1}); - reporter.specStarted({id: 1, status: 'passed', passedExpectations: [], failedExpectations: []}); + reporter.specStarted({id: 1, passedExpectations: [], failedExpectations: []}); reporter.specDone({ id: 1, status: 'passed', diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index 27baf7da..6ce6a41b 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -25,10 +25,11 @@ jasmineRequire.HtmlReporter = function(j$) { }; ResultsStateBuilder.prototype.specStarted = function(result) { - this.currentParent.addChild(result, 'spec'); }; ResultsStateBuilder.prototype.specDone = function(result) { + this.currentParent.addChild(result, 'spec'); + if (result.status !== 'disabled') { this.specsExecuted++; }