UNPKG

mocha-tape-deck

Version:

Create, manage, and replay HTTP requests and responses for fast, deterministic tests.

163 lines 5.64 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const nock = require("nock"); const mocha = require("mocha"); const path = require("path"); const rimraf = require("rimraf"); const sanitize = require("sanitize-filename"); function TestTapeDeck(cassettePath, title, fn) { return new MochaTapeDeck(cassettePath, title, fn); } exports.TestTapeDeck = TestTapeDeck; class TapeDeck { constructor(cassettePath) { this.cassettePath = cassettePath; } createTest(title, fn) { return new MochaTapeDeck(this.cassettePath, title, fn); } removeAllCassettes() { return new Promise((res, rej) => { rimraf(this.cassettePath, (err) => { if (err) { rej(err); } else { res(); } }); }); } } exports.TapeDeck = TapeDeck; class MochaTapeDeck extends mocha.Test { constructor(cassettePath, title, fn) { super(title, fn); this.cassettePath = cassettePath; this.fnPrefix = () => { }; this.fnSuffix = () => { }; this.actionSpecified = false; } recordCassette(cassetteFileName) { this.actionSpecified = true; if (process.env.NO_CASSETTE_MOCKING) { return this; } if (!this.fn) { return this; } let cassetteFilePath; this.fnPrefix = () => { if (!fs.existsSync(this.cassettePath)) { fs.mkdirSync(this.cassettePath); } if (fs.existsSync(this.getCassetteFilePath(cassetteFileName))) { cassetteFilePath = cassetteFileName ? path.join(this.cassettePath, cassetteFileName) : this.getCassetteFilePath(); fs.unlinkSync(cassetteFilePath); } nock.recorder.rec(({ dont_print: true, use_separator: false, output_objects: true })); }; this.fnSuffix = () => { const res = nock.recorder.play(); fs.writeFileSync(this.getCassetteFilePath(cassetteFileName), JSON.stringify(res, null, 2)); }; return this; } playCassette(cassetteFileName) { this.actionSpecified = true; if (process.env.NO_CASSETTE_MOCKING) { return this; } this.fnPrefix = () => { const cassettePath = this.getCassetteFilePath(cassetteFileName); nock.load(cassettePath); if (!nock.isActive()) { nock.activate(); } }; this.fnSuffix = () => { }; return this; } selectCassetteAction(fn, cassettePath) { return fn() === 'record' ? this.recordCassette() : this.playCassette(cassettePath); } register(suite, options = { failIfNoCassette: false }) { const originalFn = this.fn; this.fn = (done) => { try { if (!this.actionSpecified) { if (this.cassetteExists(this.getCassetteFilePath())) { this.playCassette(); } else { if (options.failIfNoCassette) { throw new Error('Expected cassette file for mocha tape-deck player does not exist'); } this.recordCassette(); } } this.fnPrefix(); let testExecutedPromise; let doneWrapper; const donePromise = new Promise((res) => { doneWrapper = res; }); const returnVal = originalFn(done ? doneWrapper : undefined); // sanity check for promise case if (returnVal && returnVal.then) { testExecutedPromise = returnVal; } else { //test was synchronous testExecutedPromise = Promise.resolve(); } testExecutedPromise .then(() => { if (done) { return donePromise .then((res) => { done(res); }); } }) .then(() => this.fnSuffix()) .then(this.resetNock.bind(this)) .catch(() => { this.resetNock.bind(this); }); // if we return with a done fn defined, we get the error Resolution method is overspecified. if (!done) { return testExecutedPromise; } } catch (e) { // catches timeout errors. Mocha magic handles the rest. NOTE, this is incredibly hard to test for this.resetNock(); } }; suite.addTest(this); } resetNock() { nock.recorder.clear(); nock.cleanAll(); nock.restore(); } cassetteExists(filePath) { return fs.existsSync(filePath); } getCassetteFilePath(filename) { return path.join(this.cassettePath, filename || this.getCassetteName()); } getCassetteName(filename) { // remove all spaces and /, replace them with _ and - respectively return sanitize(filename || this.fullTitle()) + '.cassette'; } } exports.MochaTapeDeck = MochaTapeDeck; //# sourceMappingURL=index.js.map