diff --git a/.codeclimate.yml b/.codeclimate.yml deleted file mode 100644 index 468ed1e4..00000000 --- a/.codeclimate.yml +++ /dev/null @@ -1,17 +0,0 @@ -languages: - JavaScript: true -ratings: - paths: - - "src/**/*.js" -exclude_paths: -- "spec/**/*.js" -- "lib/**" -- "dist/*" -- "grunt/**" -- "images/*" -- "**/*.md" -- "**/*.yml" -- "**/*.json" -- "**/*.scss" -- "**/*.erb" -- "*.sh" diff --git a/README.md b/README.md index 5ea77347..265625c0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ [](http://jasmine.github.io) [![Build Status](https://travis-ci.org/jasmine/jasmine.svg?branch=master)](https://travis-ci.org/jasmine/jasmine) -[![Code Climate](https://codeclimate.com/github/jasmine/jasmine.svg)](https://codeclimate.com/github/jasmine/jasmine) # A JavaScript Testing Framework diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index a7182895..6d0f4c95 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -140,14 +140,20 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { * Set this to a lower value to speed up pretty printing if you have large objects. * @name jasmine.MAX_PRETTY_PRINT_DEPTH */ - j$.MAX_PRETTY_PRINT_DEPTH = 40; + 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. * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH */ - j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100; + j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50; + /** + * Maximum number of charasters to display when pretty printing objects. + * Characters past this number will be ellipised. + * @name jasmine.MAX_PRETTY_PRINT_CHARS + */ + j$.MAX_PRETTY_PRINT_CHARS = 1000; /** * Default number of milliseconds Jasmine will wait for an asynchronous spec to complete. * @name jasmine.DEFAULT_TIMEOUT_INTERVAL @@ -4177,6 +4183,10 @@ getJasmineRequireObj().pp = function(j$) { } else { this.emitScalar(value.toString()); } + } catch (e) { + if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { + throw e; + } } finally { this.ppNestLevel_--; } @@ -4213,6 +4223,7 @@ getJasmineRequireObj().pp = function(j$) { function StringPrettyPrinter() { PrettyPrinter.call(this); + this.length = 0; this.stringParts = []; } @@ -4368,9 +4379,31 @@ getJasmineRequireObj().pp = function(j$) { }; StringPrettyPrinter.prototype.append = function(value) { - this.stringParts.push(value); + var result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); + this.length += result.value.length; + this.stringParts.push(result.value); + + if (result.truncated) { + throw new MaxCharsReachedError(); + } }; + function truncate(s, maxlen) { + if (s.length <= maxlen) { + return { value: s, truncated: false }; + } + + s = s.substring(0, maxlen - 4) + ' ...'; + return { value: s, truncated: true }; + } + + function MaxCharsReachedError() { + this.message = 'Exceeded ' + j$.MAX_PRETTY_PRINT_CHARS + + ' characters while pretty-printing a value'; + } + + MaxCharsReachedError.prototype = new Error(); + function keys(obj, isArray) { var allKeys = Object.keys ? Object.keys(obj) : (function(o) { @@ -4534,7 +4567,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { var maybeThenable = queueableFn.fn.call(self.userContext); if (maybeThenable && j$.isFunction_(maybeThenable.then)) { - maybeThenable.then(next, next.fail); + maybeThenable.then(next, onPromiseRejection); completedSynchronously = false; return { completedSynchronously: false }; } @@ -4556,6 +4589,11 @@ getJasmineRequireObj().QueueRunner = function(j$) { errored = true; } + function onPromiseRejection(e) { + onException(e); + next(); + } + function handleException(e, queueableFn) { onException(e); if (!self.catchException(e)) { diff --git a/spec/core/PrettyPrintSpec.js b/spec/core/PrettyPrintSpec.js index 931c2dba..cfdeeb18 100644 --- a/spec/core/PrettyPrintSpec.js +++ b/spec/core/PrettyPrintSpec.js @@ -131,6 +131,41 @@ describe("jasmineUnderTest.pp", function () { } }); + function withMaxChars(maxChars, fn) { + var originalMaxChars = jasmineUnderTest.MAX_PRETTY_PRINT_CHARS; + + try { + jasmineUnderTest.MAX_PRETTY_PRINT_CHARS = maxChars; + fn(); + } finally { + jasmineUnderTest.MAX_PRETTY_PRINT_CHARS = originalMaxChars; + } + } + + it("should truncate outputs that are too long", function() { + var big = [ + { a: 1, b: "a long string" }, + {} + ]; + + withMaxChars(34, function() { + expect(jasmineUnderTest.pp(big)).toEqual("[ Object({ a: 1, b: 'a long st ..."); + }); + }); + + it("should not serialize more objects after hitting MAX_PRETTY_PRINT_CHARS", function() { + var a = { jasmineToString: function() { return 'object a'; } }, + b = { jasmineToString: function() { return 'object b'; } }, + c = { jasmineToString: jasmine.createSpy('c jasmineToString').and.returnValue('') }, + d = { jasmineToString: jasmine.createSpy('d jasmineToString').and.returnValue('') }; + + withMaxChars(30, function() { + jasmineUnderTest.pp([{a: a, b: b, c: c}, d]); + expect(c.jasmineToString).not.toHaveBeenCalled(); + expect(d.jasmineToString).not.toHaveBeenCalled(); + }); + }); + it("should print 'null' as the constructor of an object with its own constructor property", function() { expect(jasmineUnderTest.pp({constructor: function() {}})).toContain("null({"); expect(jasmineUnderTest.pp({constructor: 'foo'})).toContain("null({"); diff --git a/spec/core/QueueRunnerSpec.js b/spec/core/QueueRunnerSpec.js index a237135b..3059b50b 100644 --- a/spec/core/QueueRunnerSpec.js +++ b/spec/core/QueueRunnerSpec.js @@ -375,27 +375,30 @@ describe("QueueRunner", function() { expect(onComplete).toHaveBeenCalled(); }); - it("fails the function when the promise is rejected", function() { + it("handles a rejected promise like an unhandled exception", function() { var promise = new StubPromise(), queueableFn1 = { fn: function() { - setTimeout(function() { promise.rejectHandler('foo'); }, 100); + setTimeout(function() { + promise.rejectHandler('foo') + }, 100); return promise; } }, queueableFn2 = { fn: jasmine.createSpy('fn2') }, failFn = jasmine.createSpy('fail'), + onExceptionCallback = jasmine.createSpy('on exception callback'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn1, queueableFn2], - fail: failFn + onException: onExceptionCallback }); queueRunner.execute(); - expect(failFn).not.toHaveBeenCalled(); + expect(onExceptionCallback).not.toHaveBeenCalled(); expect(queueableFn2.fn).not.toHaveBeenCalled(); jasmine.clock().tick(100); - expect(failFn).toHaveBeenCalledWith('foo'); + expect(onExceptionCallback).toHaveBeenCalledWith('foo'); expect(queueableFn2.fn).toHaveBeenCalled(); }); }); diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index d1a78de2..fc721d7d 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -1530,6 +1530,7 @@ describe("Env integration", function() { reporter.jasmineDone.and.callFake(function() { var specStatus = reporter.specDone.calls.argsFor(0)[0]; + expect(specStatus.status).toBe('pending'); expect(specStatus.pendingReason).toBe('with a message'); done(); @@ -1544,6 +1545,45 @@ describe("Env integration", function() { env.execute(); }); + it('should report pending spec messages from promise-returning functions', function(done) { + function StubPromise(fn) { + try { + fn(); + } catch (e) { + this.exception = e; + } + } + + StubPromise.prototype.then = function(resolve, reject) { + reject(this.exception); + }; + + var env = new jasmineUnderTest.Env(), + reporter = jasmine.createSpyObj('fakeReporter', [ + 'specDone', + 'jasmineDone' + ]); + + reporter.jasmineDone.and.callFake(function() { + var specStatus = reporter.specDone.calls.argsFor(0)[0]; + + expect(specStatus.status).toBe('pending'); + expect(specStatus.pendingReason).toBe('with a message'); + + done(); + }); + + env.addReporter(reporter); + + env.it('will be pending', function() { + return new StubPromise(function() { + env.pending('with a message'); + }); + }); + + env.execute(); + }); + it('should report using fallback reporter', function(done) { var env = new jasmineUnderTest.Env(), reporter = jasmine.createSpyObj('fakeReporter', [ diff --git a/src/core/PrettyPrinter.js b/src/core/PrettyPrinter.js index 3d70f1a4..71b6434a 100644 --- a/src/core/PrettyPrinter.js +++ b/src/core/PrettyPrinter.js @@ -59,6 +59,10 @@ getJasmineRequireObj().pp = function(j$) { } else { this.emitScalar(value.toString()); } + } catch (e) { + if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { + throw e; + } } finally { this.ppNestLevel_--; } @@ -95,6 +99,7 @@ getJasmineRequireObj().pp = function(j$) { function StringPrettyPrinter() { PrettyPrinter.call(this); + this.length = 0; this.stringParts = []; } @@ -250,9 +255,31 @@ getJasmineRequireObj().pp = function(j$) { }; StringPrettyPrinter.prototype.append = function(value) { - this.stringParts.push(value); + var result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); + this.length += result.value.length; + this.stringParts.push(result.value); + + if (result.truncated) { + throw new MaxCharsReachedError(); + } }; + function truncate(s, maxlen) { + if (s.length <= maxlen) { + return { value: s, truncated: false }; + } + + s = s.substring(0, maxlen - 4) + ' ...'; + return { value: s, truncated: true }; + } + + function MaxCharsReachedError() { + this.message = 'Exceeded ' + j$.MAX_PRETTY_PRINT_CHARS + + ' characters while pretty-printing a value'; + } + + MaxCharsReachedError.prototype = new Error(); + function keys(obj, isArray) { var allKeys = Object.keys ? Object.keys(obj) : (function(o) { diff --git a/src/core/QueueRunner.js b/src/core/QueueRunner.js index 8af19fa1..f422b49d 100644 --- a/src/core/QueueRunner.js +++ b/src/core/QueueRunner.js @@ -125,7 +125,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { var maybeThenable = queueableFn.fn.call(self.userContext); if (maybeThenable && j$.isFunction_(maybeThenable.then)) { - maybeThenable.then(next, next.fail); + maybeThenable.then(next, onPromiseRejection); completedSynchronously = false; return { completedSynchronously: false }; } @@ -147,6 +147,11 @@ getJasmineRequireObj().QueueRunner = function(j$) { errored = true; } + function onPromiseRejection(e) { + onException(e); + next(); + } + function handleException(e, queueableFn) { onException(e); if (!self.catchException(e)) { diff --git a/src/core/base.js b/src/core/base.js index ea09cb34..da39f762 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -8,14 +8,20 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { * Set this to a lower value to speed up pretty printing if you have large objects. * @name jasmine.MAX_PRETTY_PRINT_DEPTH */ - j$.MAX_PRETTY_PRINT_DEPTH = 40; + 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. * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH */ - j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100; + j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50; + /** + * Maximum number of charasters to display when pretty printing objects. + * Characters past this number will be ellipised. + * @name jasmine.MAX_PRETTY_PRINT_CHARS + */ + j$.MAX_PRETTY_PRINT_CHARS = 1000; /** * Default number of milliseconds Jasmine will wait for an asynchronous spec to complete. * @name jasmine.DEFAULT_TIMEOUT_INTERVAL