UNPKG

unexpected-express

Version:

Extend the unexpected assertion library with support for testing Express middleware

133 lines (117 loc) 4.4 kB
const messy = require('messy'); const resolveExpectedResponseProperties = require('./resolveExpectedResponseProperties'); const UnexpectedExpressMocker = require('./UnexpectedExpressMocker'); module.exports = { name: 'unexpected-express', version: require('../package.json').version, installInto(expect) { expect = expect.child(); const topLevelExpect = expect; expect.use(require('unexpected-messy')); expect.addType({ name: 'IncomingMessage', base: 'object', identify(obj) { return ( obj && obj.constructor && obj.constructor.name === 'IncomingMessage' ); }, inspect(obj, depth, output) { output.text(obj.constructor.name, 'jsFunctionName'); }, }); expect.exportAssertion( [ '<function> to yield exchange satisfying <any>', // Please prefer this one because it does use 'to satisfy' semantics '<function> to yield exchange <any>', ], (expect, subject, value) => { if (!subject.handle || !subject.set) { expect.subjectOutput = function () { this.text('express middleware'); }; } else { expect.subjectOutput = function () { this.text('express app'); }; } const missingProperties = Object.keys(value).filter( (key) => key !== 'request' && key !== 'response' ); if (missingProperties.length > 0) { throw new Error(`Property "${missingProperties[0]}" does not exist`); } const { expectedResponseProperties, expectedMetadata } = resolveExpectedResponseProperties(value); const mocker = new UnexpectedExpressMocker(subject); const options = { ...value, expectedErrorPassedToNext: expectedMetadata.errorPassedToNext, }; return mocker.mock(options).then((context) => { const { errorPassedToNext } = context.metadata; // rethrow error that was passed to next() and captured as handled if (errorPassedToNext && errorPassedToNext.UnexpectedExpressError) { const { name, data } = errorPassedToNext; if (['UnknownRouteError', 'SilentRouteError'].includes(name)) { throw errorPassedToNext.data.error; // original error } else { context = { ...context }; context.metadata = { ...context.metadata }; context.metadata.errorPassedToNext = data.error; } } const promiseByKey = { httpExchange: expect.promise(() => expect(context.httpExchange, 'to satisfy', { response: expectedResponseProperties, }) ), metadata: {}, }; for (const key of Object.keys(expectedMetadata)) { promiseByKey.metadata[key] = expect.promise(() => topLevelExpect( context.metadata[key], 'to satisfy', expectedMetadata[key] ) ); } return expect.promise.settle(promiseByKey).then((promises) => { if (promises.some((promise) => promise.isRejected())) { expect.fail({ diff(output) { if (promiseByKey.httpExchange.isRejected()) { output.append( promiseByKey.httpExchange.reason().getDiff(output) ); } else { output.appendInspected(context.httpExchange); } Object.keys(promiseByKey.metadata).forEach((key) => { if (promiseByKey.metadata[key].isRejected()) { output.nl().annotationBlock(function () { this.text(key) .text(':') .sp() .append( promiseByKey.metadata[key] .reason() .getErrorMessage(output) ); }); } }); return output; }, }); } return context; }); }); } ); }, }; module.exports.messy = messy;