From d41d10b7a8ce2590e38857362930354b648bf26f Mon Sep 17 00:00:00 2001 From: JP Bochi Date: Thu, 3 Dec 2020 14:00:23 +0100 Subject: [PATCH] Wraps assert functions, updating stack trace of generated errors --- lib/test.js | 32 +++++++++++++++++++++++++++----- test/supertest.js | 22 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/lib/test.js b/lib/test.js index 0dbb3e17..df89b6c8 100644 --- a/lib/test.js +++ b/lib/test.js @@ -64,6 +64,28 @@ Test.prototype.serverAddress = function(app, path) { return protocol + '://127.0.0.1:' + port + path; }; +/** + * Wraps an assert function into another. + * The wrapper function edit the stack trace of any assertion error, prepending a more useful stack to it. + * + * @param {Function} assertFn + * @returns {Function} wrapped assert function + */ + +function wrapAssertFn(assertFn) { + var savedStack = new Error().stack.split('\n').slice(3); + + return function(res) { + var badStack; + var err = assertFn(res); + if (err && err.stack) { + badStack = err.stack.replace(err.message, '').split('\n').slice(1); + err.stack = [err.message, savedStack, '----', badStack].flat().join('\n'); + } + return err; + }; +} + /** * Expectations: * @@ -83,7 +105,7 @@ Test.prototype.serverAddress = function(app, path) { Test.prototype.expect = function(a, b, c) { // callback if (typeof a === 'function') { - this._asserts.push(a); + this._asserts.push(wrapAssertFn(a)); return this; } if (typeof b === 'function') this.end(b); @@ -91,22 +113,22 @@ Test.prototype.expect = function(a, b, c) { // status if (typeof a === 'number') { - this._asserts.push(this._assertStatus.bind(this, a)); + this._asserts.push(wrapAssertFn(this._assertStatus.bind(this, a))); // body if (typeof b !== 'function' && arguments.length > 1) { - this._asserts.push(this._assertBody.bind(this, b)); + this._asserts.push(wrapAssertFn(this._assertBody.bind(this, b))); } return this; } // header field if (typeof b === 'string' || typeof b === 'number' || b instanceof RegExp) { - this._asserts.push(this._assertHeader.bind(this, { name: '' + a, value: b })); + this._asserts.push(wrapAssertFn(this._assertHeader.bind(this, { name: '' + a, value: b }))); return this; } // body - this._asserts.push(this._assertBody.bind(this, a)); + this._asserts.push(wrapAssertFn(this._assertBody.bind(this, a))); return this; }; diff --git a/test/supertest.js b/test/supertest.js index 215f2be2..b929063b 100644 --- a/test/supertest.js +++ b/test/supertest.js @@ -359,6 +359,7 @@ describe('request(app)', function () { .expect(404) .end(function (err, res) { err.message.should.equal('expected 404 "Not Found", got 200 "OK"'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -419,6 +420,7 @@ describe('request(app)', function () { .expect(200, '') .end(function (err, res) { err.message.should.equal('expected \'\' response body, got \'foo\''); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -440,6 +442,7 @@ describe('request(app)', function () { .expect('hey') .end(function (err, res) { err.message.should.equal('expected \'hey\' response body, got \'{"foo":"bar"}\''); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -459,6 +462,7 @@ describe('request(app)', function () { .expect('hey') .end(function (err, res) { err.message.should.equal('expected 200 "OK", got 500 "Internal Server Error"'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -491,6 +495,7 @@ describe('request(app)', function () { .expect({ foo: 'baz' }) .end(function (err, res) { err.message.should.equal('expected { foo: \'baz\' } response body, got { foo: \'bar\' }'); + err.stack.should.match(/test\/supertest.js:/); request(app) .get('/') @@ -522,6 +527,7 @@ describe('request(app)', function () { .expect({ stringValue: 'foo', numberValue: 3, nestedObject: { innerString: 5 } }) .end(function (err, res) { err.message.should.equal('expected {\n stringValue: \'foo\',\n numberValue: 3,\n nestedObject: { innerString: 5 }\n} response body, got {\n stringValue: \'foo\',\n numberValue: 3,\n nestedObject: { innerString: \'5\' }\n}'); // eslint-disable-line max-len + err.stack.should.match(/test\/supertest.js:/); request(app) .get('/') @@ -542,6 +548,7 @@ describe('request(app)', function () { .expect(/^bar/) .end(function (err, res) { err.message.should.equal('expected body \'foobar\' to match /^bar/'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -560,6 +567,7 @@ describe('request(app)', function () { .expect('hey tj') .end(function (err, res) { err.message.should.equal("expected 'hey' response body, got 'hey tj'"); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -592,6 +600,7 @@ describe('request(app)', function () { .expect('Content-Foo', 'bar') .end(function (err, res) { err.message.should.equal('expected "Content-Foo" header field'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -609,6 +618,7 @@ describe('request(app)', function () { .end(function (err, res) { err.message.should.equal('expected "Content-Type" of "text/html", ' + 'got "application/json; charset=utf-8"'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -640,6 +650,7 @@ describe('request(app)', function () { .end(function (err) { err.message.should.equal('expected "Content-Type" matching /^application/, ' + 'got "text/html; charset=utf-8"'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -656,6 +667,7 @@ describe('request(app)', function () { .expect('Content-Length', 4) .end(function (err) { err.message.should.equal('expected "Content-Length" of "4", got "3"'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -682,6 +694,7 @@ describe('request(app)', function () { }) .end(function (err) { err.message.should.equal('failed'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -707,6 +720,7 @@ describe('request(app)', function () { }) .end(function (err) { err.message.should.equal('some descriptive error'); + err.stack.should.match(/test\/supertest.js:/); (err instanceof Error).should.be.true; done(); }); @@ -747,6 +761,7 @@ describe('request(app)', function () { .expect('Content-Type', /json/) .end(function (err) { err.message.should.match(/Content-Type/); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -790,6 +805,7 @@ describe('request(app)', function () { .end(function (err) { err.message.should.equal('expected "Content-Type" matching /bloop/, ' + 'got "text/html; charset=utf-8"'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -808,6 +824,7 @@ describe('request(app)', function () { .end(function (err) { err.message.should.equal('expected "Content-Type" matching /bloop/, ' + 'got "text/html; charset=utf-8"'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -826,6 +843,7 @@ describe('request(app)', function () { .end(function (err) { err.message.should.equal('expected "Content-Type" matching /bloop/, ' + 'got "text/html; charset=utf-8"'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -984,6 +1002,7 @@ describe('assert ordering by call order', function () { .end(function (err, res) { err.message.should.equal('expected \'hey\' response body, ' + 'got \'{"message":"something went wrong"}\''); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -1003,6 +1022,7 @@ describe('assert ordering by call order', function () { .expect('hey') .end(function (err, res) { err.message.should.equal('expected 200 "OK", got 500 "Internal Server Error"'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -1023,6 +1043,7 @@ describe('assert ordering by call order', function () { .end(function (err, res) { err.message.should.equal('expected "content-type" matching /html/, ' + 'got "application/json; charset=utf-8"'); + err.stack.should.match(/test\/supertest.js:/); done(); }); }); @@ -1195,6 +1216,7 @@ describe('request.get(url).query(vals) works as expected', function () { .end(function (err, res) { err.should.be.an.instanceof(Error); err.message.should.match(/Nock: Disallowed net connect/); + err.stack.should.match(/test\/supertest.js:/); done(); });