From b312ed49400b6fc1d027fb2b2507738920627e21 Mon Sep 17 00:00:00 2001 From: Joe Pea Date: Tue, 21 Sep 2021 21:39:21 -0700 Subject: [PATCH 1/7] update base.js docs, mention setting DEFAULT_TIMEOUT_INTERVAL to a high number while debugging This info is useful because if someone does not set the number to a high enough value while stepping through test code, before or after hooks may be triggered mid-test while the user is debugging which will be confusing. --- src/core/base.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/core/base.js b/src/core/base.js index 89cd2b6e..8089383f 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -5,7 +5,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { /** * Maximum object depth the pretty printer will print to. - * Set this to a lower value to speed up pretty printing if you have large objects. + * Set this to a lower value to speed up pretty printing if you have large objects. The default value is 8. * @name jasmine.MAX_PRETTY_PRINT_DEPTH * @since 1.3.0 */ @@ -13,20 +13,23 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { /** * Maximum number of array elements to display when pretty printing objects. * This will also limit the number of keys and values displayed for an object. - * Elements past this number will be ellipised. + * Elements past this number will be ellipised. The default value is 50. * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH * @since 2.7.0 */ j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50; /** * Maximum number of characters to display when pretty printing objects. - * Characters past this number will be ellipised. + * Characters past this number will be ellipised. The default value is 1000. * @name jasmine.MAX_PRETTY_PRINT_CHARS * @since 2.9.0 */ j$.MAX_PRETTY_PRINT_CHARS = 1000; /** * Default number of milliseconds Jasmine will wait for an asynchronous spec to complete. + * While debugging tests, you may want to set this to a large + * number no bigger than 2147483647 so that `before` or `after` hooks do not run in the middle of an + * async test that you are stepping through in a debugger. The default value is 5000. * @name jasmine.DEFAULT_TIMEOUT_INTERVAL * @since 1.3.0 */ From 4c043717a955ff82e0a9a2ef906715574d0078ca Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 25 Sep 2021 12:11:20 -0700 Subject: [PATCH 2/7] Revert "Dogfood the new jasmine-npm completion interface" This works, but until the -npm 3.10.0 is out, it creates a chicken and egg problem where each of core 3.10.0 and -npm 3.10.0 needs to be released after the other. This reverts commit 1c9382c9907eff62cfa95aceef180fbf7d0c8b86. --- Gruntfile.js | 14 ++++---------- package.json | 2 +- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index e50595cd..14077d78 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -40,17 +40,11 @@ module.exports = function(grunt) { jasmine = new Jasmine({jasmineCore: jasmineCore}); jasmine.loadConfigFile('./spec/support/jasmine.json'); + jasmine.onComplete(function(passed) { + done(passed); + }); - jasmine.exitOnCompletion = false; - jasmine.execute().then( - result => { - done(result.overallStatus === 'passed'); - }, - err => { - console.error(err); - exit(1); - } - ); + jasmine.execute(); } ); diff --git a/package.json b/package.json index 093c77fd..84dd9372 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "grunt-contrib-concat": "^1.0.1", "grunt-css-url-embed": "^1.11.1", "grunt-sass": "^3.0.2", - "jasmine": "github:jasmine/jasmine-npm#main", + "jasmine": "^3.4.0", "jasmine-browser-runner": "github:jasmine/jasmine-browser#main", "jsdom": "^15.0.0", "load-grunt-tasks": "^4.0.0", From 7fc3408051d96924405c6aedfa5540da027f39e9 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 25 Sep 2021 13:29:44 -0700 Subject: [PATCH 3/7] Expanded jsdocs for jasmine.DEFAULT_TIMEOUT_INTERVAL --- lib/jasmine-core/jasmine.js | 24 +++++++++++++++++------- src/core/base.js | 24 +++++++++++++++++------- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index a27a6a0f..2c9826e8 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -174,32 +174,42 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { /** * Maximum object depth the pretty printer will print to. - * Set this to a lower value to speed up pretty printing if you have large objects. The default value is 8. + * Set this to a lower value to speed up pretty printing if you have large objects. * @name jasmine.MAX_PRETTY_PRINT_DEPTH + * @default 8 * @since 1.3.0 */ j$.MAX_PRETTY_PRINT_DEPTH = 8; /** * Maximum number of array elements to display when pretty printing objects. * This will also limit the number of keys and values displayed for an object. - * Elements past this number will be ellipised. The default value is 50. + * Elements past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH + * @default 50 * @since 2.7.0 */ j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50; /** * Maximum number of characters to display when pretty printing objects. - * Characters past this number will be ellipised. The default value is 1000. + * Characters past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_CHARS + * @default 100 * @since 2.9.0 */ j$.MAX_PRETTY_PRINT_CHARS = 1000; /** - * Default number of milliseconds Jasmine will wait for an asynchronous spec to complete. - * While debugging tests, you may want to set this to a large - * number no bigger than 2147483647 so that `before` or `after` hooks do not run in the middle of an - * async test that you are stepping through in a debugger. The default value is 5000. + * Default number of milliseconds Jasmine will wait for an asynchronous spec, + * before, or after function to complete. This can be overridden on a case by + * case basis by passing a time limit as the third argument to {@link it}, + * {@link beforeEach}, {@link afterEach}, {@link beforeAll}, or + * {@link afterAll}. The value must be no greater than the largest number of + * milliseconds supported by setTimeout, which is usually 2147483647. + * + * While debugging tests, you may want to set this to a large number (or pass + * a large number to one of the functions mentioned above) so that Jasmine + * does not move on to after functions or the next spec while you're debugging. * @name jasmine.DEFAULT_TIMEOUT_INTERVAL + * @default 5000 * @since 1.3.0 */ j$.DEFAULT_TIMEOUT_INTERVAL = 5000; diff --git a/src/core/base.js b/src/core/base.js index 28608aed..4b0ff1cd 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -5,32 +5,42 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { /** * Maximum object depth the pretty printer will print to. - * Set this to a lower value to speed up pretty printing if you have large objects. The default value is 8. + * Set this to a lower value to speed up pretty printing if you have large objects. * @name jasmine.MAX_PRETTY_PRINT_DEPTH + * @default 8 * @since 1.3.0 */ j$.MAX_PRETTY_PRINT_DEPTH = 8; /** * Maximum number of array elements to display when pretty printing objects. * This will also limit the number of keys and values displayed for an object. - * Elements past this number will be ellipised. The default value is 50. + * Elements past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH + * @default 50 * @since 2.7.0 */ j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50; /** * Maximum number of characters to display when pretty printing objects. - * Characters past this number will be ellipised. The default value is 1000. + * Characters past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_CHARS + * @default 100 * @since 2.9.0 */ j$.MAX_PRETTY_PRINT_CHARS = 1000; /** - * Default number of milliseconds Jasmine will wait for an asynchronous spec to complete. - * While debugging tests, you may want to set this to a large - * number no bigger than 2147483647 so that `before` or `after` hooks do not run in the middle of an - * async test that you are stepping through in a debugger. The default value is 5000. + * Default number of milliseconds Jasmine will wait for an asynchronous spec, + * before, or after function to complete. This can be overridden on a case by + * case basis by passing a time limit as the third argument to {@link it}, + * {@link beforeEach}, {@link afterEach}, {@link beforeAll}, or + * {@link afterAll}. The value must be no greater than the largest number of + * milliseconds supported by setTimeout, which is usually 2147483647. + * + * While debugging tests, you may want to set this to a large number (or pass + * a large number to one of the functions mentioned above) so that Jasmine + * does not move on to after functions or the next spec while you're debugging. * @name jasmine.DEFAULT_TIMEOUT_INTERVAL + * @default 5000 * @since 1.3.0 */ j$.DEFAULT_TIMEOUT_INTERVAL = 5000; From c3fb3e985ac8af537d1df89574ff54312964f681 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 25 Sep 2021 15:43:31 -0700 Subject: [PATCH 4/7] Reject timeout values that are too large for setTimeout See #1930 --- lib/jasmine-core/jasmine.js | 55 ++++++++++++++++++++++++++++++++++++- spec/core/EnvSpec.js | 36 ++++++++++++++++++++++++ spec/core/baseSpec.js | 45 ++++++++++++++++++++++++++++++ src/core/Env.js | 29 +++++++++++++++++++ src/core/base.js | 11 +++++++- src/core/util.js | 15 ++++++++++ 6 files changed, 189 insertions(+), 2 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 2c9826e8..ab9bb00e 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -212,7 +212,16 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { * @default 5000 * @since 1.3.0 */ - j$.DEFAULT_TIMEOUT_INTERVAL = 5000; + var DEFAULT_TIMEOUT_INTERVAL = 5000; + Object.defineProperty(j$, 'DEFAULT_TIMEOUT_INTERVAL', { + get: function() { + return DEFAULT_TIMEOUT_INTERVAL; + }, + set: function(newValue) { + j$.util.validateTimeout(newValue, 'jasmine.DEFAULT_TIMEOUT_INTERVAL'); + DEFAULT_TIMEOUT_INTERVAL = newValue; + } + }); j$.getGlobal = function() { return jasmineGlobal; @@ -726,6 +735,21 @@ getJasmineRequireObj().util = function(j$) { } }; + util.validateTimeout = function(timeout, msgPrefix) { + // Timeouts are implemented with setTimeout, which only supports a limited + // range of values. The limit is unspecified, as is the behavior when it's + // exceeded. But on all currently supported JS runtimes, setTimeout calls + // the callback immediately when the timeout is greater than 2147483647 + // (the maximum value of a signed 32 bit integer). + var max = 2147483647; + + if (timeout > max) { + throw new Error( + (msgPrefix || 'Timeout value') + ' cannot be greater than ' + max + ); + } + }; + return util; }; @@ -2286,6 +2310,11 @@ getJasmineRequireObj().Env = function(j$) { if (arguments.length > 1 && typeof fn !== 'undefined') { ensureIsFunctionOrAsync(fn, 'it'); } + + if (timeout) { + j$.util.validateTimeout(timeout); + } + var spec = specFactory(description, fn, currentDeclarationSuite, timeout); if (currentDeclarationSuite.markedPending) { spec.pend(); @@ -2309,6 +2338,10 @@ getJasmineRequireObj().Env = function(j$) { this.fit = function(description, fn, timeout) { ensureIsNotNested('fit'); ensureIsFunctionOrAsync(fn, 'fit'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } var spec = specFactory(description, fn, currentDeclarationSuite, timeout); currentDeclarationSuite.addChild(spec); focusedRunnables.push(spec.id); @@ -2373,6 +2406,11 @@ getJasmineRequireObj().Env = function(j$) { this.beforeEach = function(beforeEachFunction, timeout) { ensureIsNotNested('beforeEach'); ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + currentDeclarationSuite.beforeEach({ fn: beforeEachFunction, timeout: timeout || 0 @@ -2382,6 +2420,11 @@ getJasmineRequireObj().Env = function(j$) { this.beforeAll = function(beforeAllFunction, timeout) { ensureIsNotNested('beforeAll'); ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + currentDeclarationSuite.beforeAll({ fn: beforeAllFunction, timeout: timeout || 0 @@ -2391,6 +2434,11 @@ getJasmineRequireObj().Env = function(j$) { this.afterEach = function(afterEachFunction, timeout) { ensureIsNotNested('afterEach'); ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + afterEachFunction.isCleanup = true; currentDeclarationSuite.afterEach({ fn: afterEachFunction, @@ -2401,6 +2449,11 @@ getJasmineRequireObj().Env = function(j$) { this.afterAll = function(afterAllFunction, timeout) { ensureIsNotNested('afterAll'); ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + currentDeclarationSuite.afterAll({ fn: afterAllFunction, timeout: timeout || 0 diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index 2134dd17..566be5eb 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -256,6 +256,12 @@ describe('Env', function() { env.it('async', jasmine.getEnv().makeAsyncAwaitFunction()); }).not.toThrow(); }); + + it('throws an error when the timeout value is too large for setTimeout', function() { + expect(function() { + env.it('huge timeout', function() {}, 2147483648); + }).toThrowError('Timeout value cannot be greater than 2147483647'); + }); }); describe('#xit', function() { @@ -298,6 +304,12 @@ describe('Env', function() { /fit expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/ ); }); + + it('throws an error when the timeout value is too large for setTimeout', function() { + expect(function() { + env.fit('huge timeout', function() {}, 2147483648); + }).toThrowError('Timeout value cannot be greater than 2147483647'); + }); }); describe('#beforeEach', function() { @@ -315,6 +327,12 @@ describe('Env', function() { env.beforeEach(jasmine.getEnv().makeAsyncAwaitFunction()); }).not.toThrow(); }); + + it('throws an error when the timeout value is too large for setTimeout', function() { + expect(function() { + env.beforeEach(function() {}, 2147483648); + }).toThrowError('Timeout value cannot be greater than 2147483647'); + }); }); describe('#beforeAll', function() { @@ -332,6 +350,12 @@ describe('Env', function() { env.beforeAll(jasmine.getEnv().makeAsyncAwaitFunction()); }).not.toThrow(); }); + + it('throws an error when the timeout value is too large for setTimeout', function() { + expect(function() { + env.beforeAll(function() {}, 2147483648); + }).toThrowError('Timeout value cannot be greater than 2147483647'); + }); }); describe('#afterEach', function() { @@ -349,6 +373,12 @@ describe('Env', function() { env.afterEach(jasmine.getEnv().makeAsyncAwaitFunction()); }).not.toThrow(); }); + + it('throws an error when the timeout value is too large for setTimeout', function() { + expect(function() { + env.afterEach(function() {}, 2147483648); + }).toThrowError('Timeout value cannot be greater than 2147483647'); + }); }); describe('#afterAll', function() { @@ -366,6 +396,12 @@ describe('Env', function() { env.afterAll(jasmine.getEnv().makeAsyncAwaitFunction()); }).not.toThrow(); }); + + it('throws an error when the timeout value is too large for setTimeout', function() { + expect(function() { + env.afterAll(function() {}, 2147483648); + }).toThrowError('Timeout value cannot be greater than 2147483647'); + }); }); describe('when not constructed with suppressLoadErrors: true', function() { diff --git a/spec/core/baseSpec.js b/spec/core/baseSpec.js index dd5dd225..0c6ef23a 100644 --- a/spec/core/baseSpec.js +++ b/spec/core/baseSpec.js @@ -141,4 +141,49 @@ describe('base helpers', function() { ); }); }); + + describe('DEFAULT_TIMEOUT_INTERVAL setter', function() { + var max = 2147483647; + + beforeEach(function() { + this.initialValue = jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL; + }); + + afterEach(function() { + jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = this.initialValue; + }); + + it('accepts only values <= ' + max, function() { + expect(function() { + jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = max + 1; + }).toThrowError( + 'jasmine.DEFAULT_TIMEOUT_INTERVAL cannot be greater than ' + max + ); + + jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = max; + expect(jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL).toEqual(max); + }); + + it('is consistent with setTimeout in this environment', function(done) { + var f1 = jasmine.createSpy('setTimeout callback for ' + max), + f2 = jasmine.createSpy('setTimeout callback for ' + (max + 1)), + id; + + // Suppress printing of TimeoutOverflowWarning in node + spyOn(console, 'error'); + + id = setTimeout(f1, max); + setTimeout(function() { + clearTimeout(id); + expect(f1).not.toHaveBeenCalled(); + + id = setTimeout(f2, max + 1); + setTimeout(function() { + clearTimeout(id); + expect(f2).toHaveBeenCalled(); + done(); + }); + }); + }); + }); }); diff --git a/src/core/Env.js b/src/core/Env.js index a5d19f3d..895579c1 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -1245,6 +1245,11 @@ getJasmineRequireObj().Env = function(j$) { if (arguments.length > 1 && typeof fn !== 'undefined') { ensureIsFunctionOrAsync(fn, 'it'); } + + if (timeout) { + j$.util.validateTimeout(timeout); + } + var spec = specFactory(description, fn, currentDeclarationSuite, timeout); if (currentDeclarationSuite.markedPending) { spec.pend(); @@ -1268,6 +1273,10 @@ getJasmineRequireObj().Env = function(j$) { this.fit = function(description, fn, timeout) { ensureIsNotNested('fit'); ensureIsFunctionOrAsync(fn, 'fit'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } var spec = specFactory(description, fn, currentDeclarationSuite, timeout); currentDeclarationSuite.addChild(spec); focusedRunnables.push(spec.id); @@ -1332,6 +1341,11 @@ getJasmineRequireObj().Env = function(j$) { this.beforeEach = function(beforeEachFunction, timeout) { ensureIsNotNested('beforeEach'); ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + currentDeclarationSuite.beforeEach({ fn: beforeEachFunction, timeout: timeout || 0 @@ -1341,6 +1355,11 @@ getJasmineRequireObj().Env = function(j$) { this.beforeAll = function(beforeAllFunction, timeout) { ensureIsNotNested('beforeAll'); ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + currentDeclarationSuite.beforeAll({ fn: beforeAllFunction, timeout: timeout || 0 @@ -1350,6 +1369,11 @@ getJasmineRequireObj().Env = function(j$) { this.afterEach = function(afterEachFunction, timeout) { ensureIsNotNested('afterEach'); ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + afterEachFunction.isCleanup = true; currentDeclarationSuite.afterEach({ fn: afterEachFunction, @@ -1360,6 +1384,11 @@ getJasmineRequireObj().Env = function(j$) { this.afterAll = function(afterAllFunction, timeout) { ensureIsNotNested('afterAll'); ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + currentDeclarationSuite.afterAll({ fn: afterAllFunction, timeout: timeout || 0 diff --git a/src/core/base.js b/src/core/base.js index 4b0ff1cd..ee5c0cad 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -43,7 +43,16 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { * @default 5000 * @since 1.3.0 */ - j$.DEFAULT_TIMEOUT_INTERVAL = 5000; + var DEFAULT_TIMEOUT_INTERVAL = 5000; + Object.defineProperty(j$, 'DEFAULT_TIMEOUT_INTERVAL', { + get: function() { + return DEFAULT_TIMEOUT_INTERVAL; + }, + set: function(newValue) { + j$.util.validateTimeout(newValue, 'jasmine.DEFAULT_TIMEOUT_INTERVAL'); + DEFAULT_TIMEOUT_INTERVAL = newValue; + } + }); j$.getGlobal = function() { return jasmineGlobal; diff --git a/src/core/util.js b/src/core/util.js index 64aedef1..c45e38fe 100644 --- a/src/core/util.js +++ b/src/core/util.js @@ -142,5 +142,20 @@ getJasmineRequireObj().util = function(j$) { } }; + util.validateTimeout = function(timeout, msgPrefix) { + // Timeouts are implemented with setTimeout, which only supports a limited + // range of values. The limit is unspecified, as is the behavior when it's + // exceeded. But on all currently supported JS runtimes, setTimeout calls + // the callback immediately when the timeout is greater than 2147483647 + // (the maximum value of a signed 32 bit integer). + var max = 2147483647; + + if (timeout > max) { + throw new Error( + (msgPrefix || 'Timeout value') + ' cannot be greater than ' + max + ); + } + }; + return util; }; From ef981bb794a5c3ec84864c710d7aa633011ae76d Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 25 Sep 2021 15:50:34 -0700 Subject: [PATCH 5/7] Run the browser-flakes build on the debugging branch --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7c06d379..1498ecb2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -192,7 +192,7 @@ workflows: filters: branches: only: - - main + - browser-flakes jobs: - build: executor: node14 From 7a289f1de7d3da0b7d35639e2994a4562b3b4bf8 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 25 Sep 2021 16:07:33 -0700 Subject: [PATCH 6/7] Added the ability to associate trace information with failing specs This is meant to aid in debugging failures, particularly intermittent failures, in cases where interactive debugging or console.log aren't suitable. --- lib/jasmine-core/jasmine-html.js | 44 +++++++++++++ lib/jasmine-core/jasmine.css | 13 ++++ lib/jasmine-core/jasmine.js | 51 ++++++++++++++- spec/core/SpecSpec.js | 109 ++++++++++++++++++++++++++++++- spec/core/baseSpec.js | 8 +++ spec/core/integration/EnvSpec.js | 83 +++++++++++++++++++++++ spec/html/HtmlReporterSpec.js | 35 +++++++++- src/core/Env.js | 10 +++ src/core/Spec.js | 25 ++++++- src/core/base.js | 16 +++++ src/html/HtmlReporter.js | 44 +++++++++++++ src/html/_HTMLReporter.scss | 16 +++++ 12 files changed, 447 insertions(+), 7 deletions(-) diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index 8399eda4..9c24ba17 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -423,9 +423,53 @@ jasmineRequire.HtmlReporter = function(j$) { ); } + if (result.trace) { + messages.appendChild(traceTable(result.trace)); + } + return failure; } + function traceTable(trace) { + var tbody = createDom('tbody'); + + trace.forEach(function(entry) { + tbody.appendChild( + createDom( + 'tr', + {}, + createDom('td', {}, entry.timestamp.toString()), + createDom('td', {}, entry.message) + ) + ); + }); + + return createDom( + 'div', + { className: 'jasmine-trace' }, + createDom( + 'div', + { className: 'jasmine-trace-header' }, + 'Trace information' + ), + createDom( + 'table', + {}, + createDom( + 'thead', + {}, + createDom( + 'tr', + {}, + createDom('th', {}, 'Time (ms)'), + createDom('th', {}, 'Message') + ) + ), + tbody + ) + ); + } + function summaryList(resultsTree, domParent) { var specListNode; for (var i = 0; i < resultsTree.children.length; i++) { diff --git a/lib/jasmine-core/jasmine.css b/lib/jasmine-core/jasmine.css index dba3ca98..87e5f002 100644 --- a/lib/jasmine-core/jasmine.css +++ b/lib/jasmine-core/jasmine.css @@ -268,4 +268,17 @@ body { border: 1px solid #ddd; background: white; white-space: pre; +} +.jasmine_html-reporter .jasmine-trace { + margin: 5px 0 0 0; + padding: 5px; + color: #666; + border: 1px solid #ddd; + background: white; +} +.jasmine_html-reporter .jasmine-trace table { + border-spacing: 0; +} +.jasmine_html-reporter .jasmine-trace table, .jasmine_html-reporter .jasmine-trace th, .jasmine_html-reporter .jasmine-trace td { + border: 1px solid #ddd; } \ No newline at end of file diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index ab9bb00e..f6778e23 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -589,6 +589,22 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { putativeSpy.calls instanceof j$.CallTracker ); }; + + /** + * Logs a message for use in debugging. If the spec fails, trace messages + * will be included in the {@link SpecResult|result} passed to the + * reporter's specDone method. + * + * This method should be called only when a spec (including any associated + * beforeEach or afterEach functions) is running. + * @function + * @name jasmine.trace + * @since 3.10.0 + * @param {String} msg - The message to log + */ + j$.trace = function(msg) { + j$.getEnv().trace(msg); + }; }; getJasmineRequireObj().util = function(j$) { @@ -823,8 +839,9 @@ getJasmineRequireObj().Spec = function(j$) { * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} + * @property {TraceEntry[]|null} trace - Trace messages, if any, that were logged using {@link Env#trace} during a failing spec. * @since 2.0.0 -x */ + */ this.result = { id: this.id, description: this.description, @@ -834,7 +851,8 @@ x */ deprecationWarnings: [], pendingReason: '', duration: null, - properties: null + properties: null, + trace: null }; } @@ -879,6 +897,11 @@ x */ self.queueableFn.fn = null; self.result.status = self.status(excluded, failSpecWithNoExp); self.result.duration = self.timer.elapsed(); + + if (self.result.status !== 'failed') { + self.result.trace = null; + } + self.resultCallback(self.result, done); } }; @@ -990,6 +1013,20 @@ x */ ); }; + Spec.prototype.trace = function(msg) { + if (!this.result.trace) { + this.result.trace = []; + } + + /** + * @typedef TraceEntry + * @property {String} message - The message that was passed to {@link Env#trace}. + * @property {number} timestamp - The time when the entry was added, in + * milliseconds from the spec's start time + */ + this.result.trace.push({ message: msg, timestamp: this.timer.elapsed() }); + }; + var extractCustomPendingMessage = function(e) { var fullMessage = e.toString(), boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), @@ -2383,6 +2420,16 @@ getJasmineRequireObj().Env = function(j$) { currentSuite().setSuiteProperty(key, value); }; + this.trace = function(msg) { + var maybeSpec = currentRunnable(); + + if (!maybeSpec || !maybeSpec.trace) { + throw new Error("'trace' was called when there was no current spec"); + } + + maybeSpec.trace(msg); + }; + this.expect = function(actual) { if (!currentRunnable()) { throw new Error( diff --git a/spec/core/SpecSpec.js b/spec/core/SpecSpec.js index fa530049..9f250e9f 100644 --- a/spec/core/SpecSpec.js +++ b/spec/core/SpecSpec.js @@ -228,7 +228,8 @@ describe('Spec', function() { deprecationWarnings: [], pendingReason: '', duration: jasmine.any(Number), - properties: null + properties: null, + trace: null }, 'things' ); @@ -516,4 +517,110 @@ describe('Spec', function() { args.cleanupFns[0].fn(); expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([]); }); + + describe('#trace', function() { + it('adds the messages to the result', function() { + var timer = jasmine.createSpyObj('timer', ['start', 'elapsed']), + spec = new jasmineUnderTest.Spec({ + queueableFn: { + fn: function() {} + }, + queueRunnerFactory: function() {}, + timer: timer + }), + t1 = 123, + t2 = 456; + + spec.execute(); + expect(spec.result.trace).toBeNull(); + timer.elapsed.and.returnValue(t1); + spec.trace('msg 1'); + expect(spec.result.trace).toEqual([{ message: 'msg 1', timestamp: t1 }]); + timer.elapsed.and.returnValue(t2); + spec.trace('msg 2'); + expect(spec.result.trace).toEqual([ + { message: 'msg 1', timestamp: t1 }, + { message: 'msg 2', timestamp: t2 } + ]); + }); + + describe('When the spec passes', function() { + it('omits the messages from the reported result', function() { + var resultCallback = jasmine.createSpy('resultCallback'), + spec = new jasmineUnderTest.Spec({ + queueableFn: { + fn: function() {} + }, + resultCallback: resultCallback, + queueRunnerFactory: function(config) { + spec.trace('msg'); + config.cleanupFns.forEach(function(fn) { + fn.fn(); + }); + config.onComplete(false); + } + }); + + spec.execute(function() {}); + expect(resultCallback).toHaveBeenCalledWith( + jasmine.objectContaining({ trace: null }), + undefined + ); + }); + + it('removes the messages to save memory', function() { + var resultCallback = jasmine.createSpy('resultCallback'), + spec = new jasmineUnderTest.Spec({ + queueableFn: { + fn: function() {} + }, + resultCallback: resultCallback, + queueRunnerFactory: function(config) { + spec.trace('msg'); + config.cleanupFns.forEach(function(fn) { + fn.fn(); + }); + config.onComplete(false); + } + }); + + spec.execute(function() {}); + expect(resultCallback).toHaveBeenCalled(); + expect(spec.result.trace).toBeNull(); + }); + }); + + describe('When the spec fails', function() { + it('includes the messages in the reported result', function() { + var resultCallback = jasmine.createSpy('resultCallback'), + timer = jasmine.createSpyObj('timer', ['start', 'elapsed']), + spec = new jasmineUnderTest.Spec({ + queueableFn: { + fn: function() {} + }, + resultCallback: resultCallback, + queueRunnerFactory: function(config) { + spec.trace('msg'); + spec.onException(new Error('nope')); + config.cleanupFns.forEach(function(fn) { + fn.fn(); + }); + config.onComplete(true); + }, + timer: timer + }), + timestamp = 12345; + + timer.elapsed.and.returnValue(timestamp); + + spec.execute(function() {}); + expect(resultCallback).toHaveBeenCalledWith( + jasmine.objectContaining({ + trace: [{ message: 'msg', timestamp: timestamp }] + }), + undefined + ); + }); + }); + }); }); diff --git a/spec/core/baseSpec.js b/spec/core/baseSpec.js index 0c6ef23a..a35edff7 100644 --- a/spec/core/baseSpec.js +++ b/spec/core/baseSpec.js @@ -186,4 +186,12 @@ describe('base helpers', function() { }); }); }); + + describe('trace', function() { + it("forwards to the current env's trace function", function() { + spyOn(jasmineUnderTest.getEnv(), 'trace'); + jasmineUnderTest.trace('a message'); + expect(jasmineUnderTest.getEnv().trace).toHaveBeenCalledWith('a message'); + }); + }); }); diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index b257d085..bf70f9dc 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -3234,4 +3234,87 @@ describe('Env integration', function() { }); }); }); + + it('sends traces to the reporter when the spec fails', function(done) { + var reporter = jasmine.createSpyObj('reporter', ['specDone']), + startTime, + endTime; + + env.addReporter(reporter); + env.configure({ random: false }); + + env.it('fails', function() { + startTime = new Date().getTime(); + env.trace('message 1'); + env.trace('message 2'); + env.expect(1).toBe(2); + endTime = new Date().getTime(); + }); + + env.it('passes', function() { + env.trace('message that should not be reported'); + }); + + env.execute(null, function() { + function numberInRange(min, max) { + return { + asymmetricMatch: function(compareTo) { + return compareTo >= min && compareTo <= max; + }, + jasmineToString: function(pp) { + return ''; + } + }; + } + + var duration; + + expect(reporter.specDone).toHaveBeenCalledTimes(2); + duration = reporter.specDone.calls.argsFor(0)[0].duration; + expect(reporter.specDone.calls.argsFor(0)[0]).toEqual( + jasmine.objectContaining({ + trace: [ + { + timestamp: numberInRange(0, duration), + message: 'message 1' + }, + { + timestamp: numberInRange(0, duration), + message: 'message 2' + } + ] + }) + ); + expect(reporter.specDone.calls.argsFor(1)[0].trace).toBeFalsy(); + done(); + }); + }); + + it('reports an error when trace is used when a spec is not running', function(done) { + var reporter = jasmine.createSpyObj('reporter', ['suiteDone']); + + env.describe('a suite', function() { + env.beforeAll(function() { + env.trace('a message'); + }); + + env.it('a spec', function() {}); + }); + + env.addReporter(reporter); + env.execute(null, function() { + expect(reporter.suiteDone).toHaveBeenCalledWith( + jasmine.objectContaining({ + failedExpectations: [ + jasmine.objectContaining({ + message: jasmine.stringContaining( + "'trace' was called when there was no current spec" + ) + }) + ] + }) + ); + done(); + }); + }); }); diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index 5fef9a21..13dce454 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -1339,6 +1339,23 @@ describe('HtmlReporter', function() { } ] }; + var failingSpecResultWithTrace = { + id: 567, + status: 'failed', + description: 'a failing spec', + fullName: 'a suite inner suite a failing spec', + passedExpectations: [], + failedExpectations: [ + { + message: 'a failure message', + stack: 'a stack trace' + } + ], + trace: [ + { timestamp: 123, message: 'msg 1' }, + { timestamp: 456, message: 'msg 1' } + ] + }; var passingSuiteResult = { id: 1, @@ -1356,18 +1373,20 @@ describe('HtmlReporter', function() { reporter.suiteDone(passingSuiteResult); reporter.suiteDone(failingSuiteResult); reporter.suiteDone(passingSuiteResult); + reporter.specStarted(failingSpecResultWithTrace); + reporter.specDone(failingSpecResultWithTrace); reporter.jasmineDone({}); }); it('reports the specs counts', function() { var alertBar = container.querySelector('.jasmine-alert .jasmine-bar'); - expect(alertBar.innerHTML).toMatch(/2 specs, 2 failure/); + expect(alertBar.innerHTML).toMatch(/3 specs, 3 failures/); }); it('reports failure messages and stack traces', function() { var specFailures = container.querySelector('.jasmine-failures'); - expect(specFailures.childNodes.length).toEqual(2); + expect(specFailures.childNodes.length).toEqual(3); var specFailure = specFailures.childNodes[0]; expect(specFailure.getAttribute('class')).toMatch(/jasmine-failed/); @@ -1408,6 +1427,18 @@ describe('HtmlReporter', function() { expect(suiteStackTrace.innerHTML).toEqual('a stack trace'); }); + it('reports traces when present', function() { + var specFailure = container.querySelectorAll( + '.jasmine-spec-detail.jasmine-failed' + )[2], + trace = specFailure.querySelector('.jasmine-trace table'), + rows; + + expect(trace).toBeTruthy(); + rows = trace.querySelectorAll('tbody tr'); + expect(rows.length).toEqual(2); + }); + it('provides links to focus on a failure and each containing suite', function() { var description = container.querySelector( '.jasmine-failures .jasmine-description' diff --git a/src/core/Env.js b/src/core/Env.js index 895579c1..3cd79c5f 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -1318,6 +1318,16 @@ getJasmineRequireObj().Env = function(j$) { currentSuite().setSuiteProperty(key, value); }; + this.trace = function(msg) { + var maybeSpec = currentRunnable(); + + if (!maybeSpec || !maybeSpec.trace) { + throw new Error("'trace' was called when there was no current spec"); + } + + maybeSpec.trace(msg); + }; + this.expect = function(actual) { if (!currentRunnable()) { throw new Error( diff --git a/src/core/Spec.js b/src/core/Spec.js index 2691ce04..cd23a238 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -68,8 +68,9 @@ getJasmineRequireObj().Spec = function(j$) { * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} + * @property {TraceEntry[]|null} trace - Trace messages, if any, that were logged using {@link Env#trace} during a failing spec. * @since 2.0.0 -x */ + */ this.result = { id: this.id, description: this.description, @@ -79,7 +80,8 @@ x */ deprecationWarnings: [], pendingReason: '', duration: null, - properties: null + properties: null, + trace: null }; } @@ -124,6 +126,11 @@ x */ self.queueableFn.fn = null; self.result.status = self.status(excluded, failSpecWithNoExp); self.result.duration = self.timer.elapsed(); + + if (self.result.status !== 'failed') { + self.result.trace = null; + } + self.resultCallback(self.result, done); } }; @@ -235,6 +242,20 @@ x */ ); }; + Spec.prototype.trace = function(msg) { + if (!this.result.trace) { + this.result.trace = []; + } + + /** + * @typedef TraceEntry + * @property {String} message - The message that was passed to {@link Env#trace}. + * @property {number} timestamp - The time when the entry was added, in + * milliseconds from the spec's start time + */ + this.result.trace.push({ message: msg, timestamp: this.timer.elapsed() }); + }; + var extractCustomPendingMessage = function(e) { var fullMessage = e.toString(), boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), diff --git a/src/core/base.js b/src/core/base.js index ee5c0cad..912e52f2 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -420,4 +420,20 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { putativeSpy.calls instanceof j$.CallTracker ); }; + + /** + * Logs a message for use in debugging. If the spec fails, trace messages + * will be included in the {@link SpecResult|result} passed to the + * reporter's specDone method. + * + * This method should be called only when a spec (including any associated + * beforeEach or afterEach functions) is running. + * @function + * @name jasmine.trace + * @since 3.10.0 + * @param {String} msg - The message to log + */ + j$.trace = function(msg) { + j$.getEnv().trace(msg); + }; }; diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index dc91c4dc..80b9d03e 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -392,9 +392,53 @@ jasmineRequire.HtmlReporter = function(j$) { ); } + if (result.trace) { + messages.appendChild(traceTable(result.trace)); + } + return failure; } + function traceTable(trace) { + var tbody = createDom('tbody'); + + trace.forEach(function(entry) { + tbody.appendChild( + createDom( + 'tr', + {}, + createDom('td', {}, entry.timestamp.toString()), + createDom('td', {}, entry.message) + ) + ); + }); + + return createDom( + 'div', + { className: 'jasmine-trace' }, + createDom( + 'div', + { className: 'jasmine-trace-header' }, + 'Trace information' + ), + createDom( + 'table', + {}, + createDom( + 'thead', + {}, + createDom( + 'tr', + {}, + createDom('th', {}, 'Time (ms)'), + createDom('th', {}, 'Message') + ) + ), + tbody + ) + ); + } + function summaryList(resultsTree, domParent) { var specListNode; for (var i = 0; i < resultsTree.children.length; i++) { diff --git a/src/html/_HTMLReporter.scss b/src/html/_HTMLReporter.scss index 2ee9c9be..7ab2d515 100644 --- a/src/html/_HTMLReporter.scss +++ b/src/html/_HTMLReporter.scss @@ -388,4 +388,20 @@ body { background: white; white-space: pre; } + + .jasmine-trace { + margin: 5px 0 0 0; + padding: 5px; + color: $light-text-color; + border: 1px solid #ddd; + background: white; + + table { + border-spacing: 0; + } + + table, th, td { + border: 1px solid #ddd; + } + } } From fdad8849df475b95cfc280e91086f0a73b18187a Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 2 Oct 2021 09:45:15 -0700 Subject: [PATCH 7/7] Revert "Added the ability to associate trace information with failing specs" Pushing this back to 4.0 in hopes of increasing the chance that third party reporters will notice it and add support. This reverts commit 7a289f1de7d3da0b7d35639e2994a4562b3b4bf8. --- lib/jasmine-core/jasmine-html.js | 44 ------------- lib/jasmine-core/jasmine.css | 13 ---- lib/jasmine-core/jasmine.js | 51 +-------------- spec/core/SpecSpec.js | 109 +------------------------------ spec/core/baseSpec.js | 8 --- spec/core/integration/EnvSpec.js | 83 ----------------------- spec/html/HtmlReporterSpec.js | 35 +--------- src/core/Env.js | 10 --- src/core/Spec.js | 25 +------ src/core/base.js | 16 ----- src/html/HtmlReporter.js | 44 ------------- src/html/_HTMLReporter.scss | 16 ----- 12 files changed, 7 insertions(+), 447 deletions(-) diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index 9c24ba17..8399eda4 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -423,53 +423,9 @@ jasmineRequire.HtmlReporter = function(j$) { ); } - if (result.trace) { - messages.appendChild(traceTable(result.trace)); - } - return failure; } - function traceTable(trace) { - var tbody = createDom('tbody'); - - trace.forEach(function(entry) { - tbody.appendChild( - createDom( - 'tr', - {}, - createDom('td', {}, entry.timestamp.toString()), - createDom('td', {}, entry.message) - ) - ); - }); - - return createDom( - 'div', - { className: 'jasmine-trace' }, - createDom( - 'div', - { className: 'jasmine-trace-header' }, - 'Trace information' - ), - createDom( - 'table', - {}, - createDom( - 'thead', - {}, - createDom( - 'tr', - {}, - createDom('th', {}, 'Time (ms)'), - createDom('th', {}, 'Message') - ) - ), - tbody - ) - ); - } - function summaryList(resultsTree, domParent) { var specListNode; for (var i = 0; i < resultsTree.children.length; i++) { diff --git a/lib/jasmine-core/jasmine.css b/lib/jasmine-core/jasmine.css index 87e5f002..dba3ca98 100644 --- a/lib/jasmine-core/jasmine.css +++ b/lib/jasmine-core/jasmine.css @@ -268,17 +268,4 @@ body { border: 1px solid #ddd; background: white; white-space: pre; -} -.jasmine_html-reporter .jasmine-trace { - margin: 5px 0 0 0; - padding: 5px; - color: #666; - border: 1px solid #ddd; - background: white; -} -.jasmine_html-reporter .jasmine-trace table { - border-spacing: 0; -} -.jasmine_html-reporter .jasmine-trace table, .jasmine_html-reporter .jasmine-trace th, .jasmine_html-reporter .jasmine-trace td { - border: 1px solid #ddd; } \ No newline at end of file diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index f6778e23..ab9bb00e 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -589,22 +589,6 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { putativeSpy.calls instanceof j$.CallTracker ); }; - - /** - * Logs a message for use in debugging. If the spec fails, trace messages - * will be included in the {@link SpecResult|result} passed to the - * reporter's specDone method. - * - * This method should be called only when a spec (including any associated - * beforeEach or afterEach functions) is running. - * @function - * @name jasmine.trace - * @since 3.10.0 - * @param {String} msg - The message to log - */ - j$.trace = function(msg) { - j$.getEnv().trace(msg); - }; }; getJasmineRequireObj().util = function(j$) { @@ -839,9 +823,8 @@ getJasmineRequireObj().Spec = function(j$) { * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} - * @property {TraceEntry[]|null} trace - Trace messages, if any, that were logged using {@link Env#trace} during a failing spec. * @since 2.0.0 - */ +x */ this.result = { id: this.id, description: this.description, @@ -851,8 +834,7 @@ getJasmineRequireObj().Spec = function(j$) { deprecationWarnings: [], pendingReason: '', duration: null, - properties: null, - trace: null + properties: null }; } @@ -897,11 +879,6 @@ getJasmineRequireObj().Spec = function(j$) { self.queueableFn.fn = null; self.result.status = self.status(excluded, failSpecWithNoExp); self.result.duration = self.timer.elapsed(); - - if (self.result.status !== 'failed') { - self.result.trace = null; - } - self.resultCallback(self.result, done); } }; @@ -1013,20 +990,6 @@ getJasmineRequireObj().Spec = function(j$) { ); }; - Spec.prototype.trace = function(msg) { - if (!this.result.trace) { - this.result.trace = []; - } - - /** - * @typedef TraceEntry - * @property {String} message - The message that was passed to {@link Env#trace}. - * @property {number} timestamp - The time when the entry was added, in - * milliseconds from the spec's start time - */ - this.result.trace.push({ message: msg, timestamp: this.timer.elapsed() }); - }; - var extractCustomPendingMessage = function(e) { var fullMessage = e.toString(), boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), @@ -2420,16 +2383,6 @@ getJasmineRequireObj().Env = function(j$) { currentSuite().setSuiteProperty(key, value); }; - this.trace = function(msg) { - var maybeSpec = currentRunnable(); - - if (!maybeSpec || !maybeSpec.trace) { - throw new Error("'trace' was called when there was no current spec"); - } - - maybeSpec.trace(msg); - }; - this.expect = function(actual) { if (!currentRunnable()) { throw new Error( diff --git a/spec/core/SpecSpec.js b/spec/core/SpecSpec.js index 9f250e9f..fa530049 100644 --- a/spec/core/SpecSpec.js +++ b/spec/core/SpecSpec.js @@ -228,8 +228,7 @@ describe('Spec', function() { deprecationWarnings: [], pendingReason: '', duration: jasmine.any(Number), - properties: null, - trace: null + properties: null }, 'things' ); @@ -517,110 +516,4 @@ describe('Spec', function() { args.cleanupFns[0].fn(); expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([]); }); - - describe('#trace', function() { - it('adds the messages to the result', function() { - var timer = jasmine.createSpyObj('timer', ['start', 'elapsed']), - spec = new jasmineUnderTest.Spec({ - queueableFn: { - fn: function() {} - }, - queueRunnerFactory: function() {}, - timer: timer - }), - t1 = 123, - t2 = 456; - - spec.execute(); - expect(spec.result.trace).toBeNull(); - timer.elapsed.and.returnValue(t1); - spec.trace('msg 1'); - expect(spec.result.trace).toEqual([{ message: 'msg 1', timestamp: t1 }]); - timer.elapsed.and.returnValue(t2); - spec.trace('msg 2'); - expect(spec.result.trace).toEqual([ - { message: 'msg 1', timestamp: t1 }, - { message: 'msg 2', timestamp: t2 } - ]); - }); - - describe('When the spec passes', function() { - it('omits the messages from the reported result', function() { - var resultCallback = jasmine.createSpy('resultCallback'), - spec = new jasmineUnderTest.Spec({ - queueableFn: { - fn: function() {} - }, - resultCallback: resultCallback, - queueRunnerFactory: function(config) { - spec.trace('msg'); - config.cleanupFns.forEach(function(fn) { - fn.fn(); - }); - config.onComplete(false); - } - }); - - spec.execute(function() {}); - expect(resultCallback).toHaveBeenCalledWith( - jasmine.objectContaining({ trace: null }), - undefined - ); - }); - - it('removes the messages to save memory', function() { - var resultCallback = jasmine.createSpy('resultCallback'), - spec = new jasmineUnderTest.Spec({ - queueableFn: { - fn: function() {} - }, - resultCallback: resultCallback, - queueRunnerFactory: function(config) { - spec.trace('msg'); - config.cleanupFns.forEach(function(fn) { - fn.fn(); - }); - config.onComplete(false); - } - }); - - spec.execute(function() {}); - expect(resultCallback).toHaveBeenCalled(); - expect(spec.result.trace).toBeNull(); - }); - }); - - describe('When the spec fails', function() { - it('includes the messages in the reported result', function() { - var resultCallback = jasmine.createSpy('resultCallback'), - timer = jasmine.createSpyObj('timer', ['start', 'elapsed']), - spec = new jasmineUnderTest.Spec({ - queueableFn: { - fn: function() {} - }, - resultCallback: resultCallback, - queueRunnerFactory: function(config) { - spec.trace('msg'); - spec.onException(new Error('nope')); - config.cleanupFns.forEach(function(fn) { - fn.fn(); - }); - config.onComplete(true); - }, - timer: timer - }), - timestamp = 12345; - - timer.elapsed.and.returnValue(timestamp); - - spec.execute(function() {}); - expect(resultCallback).toHaveBeenCalledWith( - jasmine.objectContaining({ - trace: [{ message: 'msg', timestamp: timestamp }] - }), - undefined - ); - }); - }); - }); }); diff --git a/spec/core/baseSpec.js b/spec/core/baseSpec.js index a35edff7..0c6ef23a 100644 --- a/spec/core/baseSpec.js +++ b/spec/core/baseSpec.js @@ -186,12 +186,4 @@ describe('base helpers', function() { }); }); }); - - describe('trace', function() { - it("forwards to the current env's trace function", function() { - spyOn(jasmineUnderTest.getEnv(), 'trace'); - jasmineUnderTest.trace('a message'); - expect(jasmineUnderTest.getEnv().trace).toHaveBeenCalledWith('a message'); - }); - }); }); diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index bf70f9dc..b257d085 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -3234,87 +3234,4 @@ describe('Env integration', function() { }); }); }); - - it('sends traces to the reporter when the spec fails', function(done) { - var reporter = jasmine.createSpyObj('reporter', ['specDone']), - startTime, - endTime; - - env.addReporter(reporter); - env.configure({ random: false }); - - env.it('fails', function() { - startTime = new Date().getTime(); - env.trace('message 1'); - env.trace('message 2'); - env.expect(1).toBe(2); - endTime = new Date().getTime(); - }); - - env.it('passes', function() { - env.trace('message that should not be reported'); - }); - - env.execute(null, function() { - function numberInRange(min, max) { - return { - asymmetricMatch: function(compareTo) { - return compareTo >= min && compareTo <= max; - }, - jasmineToString: function(pp) { - return ''; - } - }; - } - - var duration; - - expect(reporter.specDone).toHaveBeenCalledTimes(2); - duration = reporter.specDone.calls.argsFor(0)[0].duration; - expect(reporter.specDone.calls.argsFor(0)[0]).toEqual( - jasmine.objectContaining({ - trace: [ - { - timestamp: numberInRange(0, duration), - message: 'message 1' - }, - { - timestamp: numberInRange(0, duration), - message: 'message 2' - } - ] - }) - ); - expect(reporter.specDone.calls.argsFor(1)[0].trace).toBeFalsy(); - done(); - }); - }); - - it('reports an error when trace is used when a spec is not running', function(done) { - var reporter = jasmine.createSpyObj('reporter', ['suiteDone']); - - env.describe('a suite', function() { - env.beforeAll(function() { - env.trace('a message'); - }); - - env.it('a spec', function() {}); - }); - - env.addReporter(reporter); - env.execute(null, function() { - expect(reporter.suiteDone).toHaveBeenCalledWith( - jasmine.objectContaining({ - failedExpectations: [ - jasmine.objectContaining({ - message: jasmine.stringContaining( - "'trace' was called when there was no current spec" - ) - }) - ] - }) - ); - done(); - }); - }); }); diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index 13dce454..5fef9a21 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -1339,23 +1339,6 @@ describe('HtmlReporter', function() { } ] }; - var failingSpecResultWithTrace = { - id: 567, - status: 'failed', - description: 'a failing spec', - fullName: 'a suite inner suite a failing spec', - passedExpectations: [], - failedExpectations: [ - { - message: 'a failure message', - stack: 'a stack trace' - } - ], - trace: [ - { timestamp: 123, message: 'msg 1' }, - { timestamp: 456, message: 'msg 1' } - ] - }; var passingSuiteResult = { id: 1, @@ -1373,20 +1356,18 @@ describe('HtmlReporter', function() { reporter.suiteDone(passingSuiteResult); reporter.suiteDone(failingSuiteResult); reporter.suiteDone(passingSuiteResult); - reporter.specStarted(failingSpecResultWithTrace); - reporter.specDone(failingSpecResultWithTrace); reporter.jasmineDone({}); }); it('reports the specs counts', function() { var alertBar = container.querySelector('.jasmine-alert .jasmine-bar'); - expect(alertBar.innerHTML).toMatch(/3 specs, 3 failures/); + expect(alertBar.innerHTML).toMatch(/2 specs, 2 failure/); }); it('reports failure messages and stack traces', function() { var specFailures = container.querySelector('.jasmine-failures'); - expect(specFailures.childNodes.length).toEqual(3); + expect(specFailures.childNodes.length).toEqual(2); var specFailure = specFailures.childNodes[0]; expect(specFailure.getAttribute('class')).toMatch(/jasmine-failed/); @@ -1427,18 +1408,6 @@ describe('HtmlReporter', function() { expect(suiteStackTrace.innerHTML).toEqual('a stack trace'); }); - it('reports traces when present', function() { - var specFailure = container.querySelectorAll( - '.jasmine-spec-detail.jasmine-failed' - )[2], - trace = specFailure.querySelector('.jasmine-trace table'), - rows; - - expect(trace).toBeTruthy(); - rows = trace.querySelectorAll('tbody tr'); - expect(rows.length).toEqual(2); - }); - it('provides links to focus on a failure and each containing suite', function() { var description = container.querySelector( '.jasmine-failures .jasmine-description' diff --git a/src/core/Env.js b/src/core/Env.js index 3cd79c5f..895579c1 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -1318,16 +1318,6 @@ getJasmineRequireObj().Env = function(j$) { currentSuite().setSuiteProperty(key, value); }; - this.trace = function(msg) { - var maybeSpec = currentRunnable(); - - if (!maybeSpec || !maybeSpec.trace) { - throw new Error("'trace' was called when there was no current spec"); - } - - maybeSpec.trace(msg); - }; - this.expect = function(actual) { if (!currentRunnable()) { throw new Error( diff --git a/src/core/Spec.js b/src/core/Spec.js index cd23a238..2691ce04 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -68,9 +68,8 @@ getJasmineRequireObj().Spec = function(j$) { * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} - * @property {TraceEntry[]|null} trace - Trace messages, if any, that were logged using {@link Env#trace} during a failing spec. * @since 2.0.0 - */ +x */ this.result = { id: this.id, description: this.description, @@ -80,8 +79,7 @@ getJasmineRequireObj().Spec = function(j$) { deprecationWarnings: [], pendingReason: '', duration: null, - properties: null, - trace: null + properties: null }; } @@ -126,11 +124,6 @@ getJasmineRequireObj().Spec = function(j$) { self.queueableFn.fn = null; self.result.status = self.status(excluded, failSpecWithNoExp); self.result.duration = self.timer.elapsed(); - - if (self.result.status !== 'failed') { - self.result.trace = null; - } - self.resultCallback(self.result, done); } }; @@ -242,20 +235,6 @@ getJasmineRequireObj().Spec = function(j$) { ); }; - Spec.prototype.trace = function(msg) { - if (!this.result.trace) { - this.result.trace = []; - } - - /** - * @typedef TraceEntry - * @property {String} message - The message that was passed to {@link Env#trace}. - * @property {number} timestamp - The time when the entry was added, in - * milliseconds from the spec's start time - */ - this.result.trace.push({ message: msg, timestamp: this.timer.elapsed() }); - }; - var extractCustomPendingMessage = function(e) { var fullMessage = e.toString(), boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), diff --git a/src/core/base.js b/src/core/base.js index 912e52f2..ee5c0cad 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -420,20 +420,4 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { putativeSpy.calls instanceof j$.CallTracker ); }; - - /** - * Logs a message for use in debugging. If the spec fails, trace messages - * will be included in the {@link SpecResult|result} passed to the - * reporter's specDone method. - * - * This method should be called only when a spec (including any associated - * beforeEach or afterEach functions) is running. - * @function - * @name jasmine.trace - * @since 3.10.0 - * @param {String} msg - The message to log - */ - j$.trace = function(msg) { - j$.getEnv().trace(msg); - }; }; diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index 80b9d03e..dc91c4dc 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -392,53 +392,9 @@ jasmineRequire.HtmlReporter = function(j$) { ); } - if (result.trace) { - messages.appendChild(traceTable(result.trace)); - } - return failure; } - function traceTable(trace) { - var tbody = createDom('tbody'); - - trace.forEach(function(entry) { - tbody.appendChild( - createDom( - 'tr', - {}, - createDom('td', {}, entry.timestamp.toString()), - createDom('td', {}, entry.message) - ) - ); - }); - - return createDom( - 'div', - { className: 'jasmine-trace' }, - createDom( - 'div', - { className: 'jasmine-trace-header' }, - 'Trace information' - ), - createDom( - 'table', - {}, - createDom( - 'thead', - {}, - createDom( - 'tr', - {}, - createDom('th', {}, 'Time (ms)'), - createDom('th', {}, 'Message') - ) - ), - tbody - ) - ); - } - function summaryList(resultsTree, domParent) { var specListNode; for (var i = 0; i < resultsTree.children.length; i++) { diff --git a/src/html/_HTMLReporter.scss b/src/html/_HTMLReporter.scss index 7ab2d515..2ee9c9be 100644 --- a/src/html/_HTMLReporter.scss +++ b/src/html/_HTMLReporter.scss @@ -388,20 +388,4 @@ body { background: white; white-space: pre; } - - .jasmine-trace { - margin: 5px 0 0 0; - padding: 5px; - color: $light-text-color; - border: 1px solid #ddd; - background: white; - - table { - border-spacing: 0; - } - - table, th, td { - border: 1px solid #ddd; - } - } }