@rxjs-stuff/marbles
Version:
A set of plugins that provide a natural feeling integration with Mocha and Chai for RxJS "marbles" testing.
129 lines • 6.72 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.afterEach = exports.beforeEach = exports.assertDeepEqual = exports.chaiMarbles = void 0;
const chai_1 = require("chai");
const rxjs_1 = require("rxjs");
const marbles_1 = require("@rxjs-stuff/marbles");
const chaiMarbles = (chai, utils) => {
function getContext() {
const context = utils.flag(exports.chaiMarbles, 'currentTest');
const marbles = context ? utils.flag(context, 'marblesHelpers') : undefined;
return [marbles, context];
}
utils.addMethod(chai.Assertion.prototype, 'subscribedWith', function (...subscriptionMarbles) {
const obj = utils.flag(this, 'object');
const [marbles] = getContext();
if (rxjs_1.isObservable(obj)) {
marbles.expectSubscriptions(marbles_1.wrapLogSubscriptions(marbles.scheduler, obj).subscriptions).toBe(subscriptionMarbles);
}
});
utils.addMethod(chai.Assertion.prototype, 'verifyError', function (callbackFn) {
utils.flag(this, 'verifyError', callbackFn);
});
utils.addMethod(chai.Assertion.prototype, 'marbleValues', function (values) {
utils.flag(this, 'marbleValues', values);
});
utils.addChainableMethod(chai.Assertion.prototype, 'subscription', function (subscriptionMarbles) {
utils.flag(this, 'subscriptionMarbles', subscriptionMarbles);
});
utils.addChainableMethod(chai.Assertion.prototype, 'originalContext', function (context) {
utils.flag(this, 'originalContext', context);
});
function observableErrorsAccessor(context) {
return () => {
const originalContext = utils.flag(context, 'originalContext');
const originalContextObservableErrors = originalContext ? utils.flag(originalContext, 'observableErrors') : undefined;
if (originalContextObservableErrors) {
return originalContextObservableErrors;
}
const observableErrors = utils.flag(context, 'observableErrors');
if (observableErrors) {
return observableErrors;
}
const observableErrorsInit = [];
utils.flag(context, 'observableErrors', observableErrorsInit);
return observableErrorsInit;
};
}
utils.overwriteMethod(chai.Assertion.prototype, 'equal', function (_super) {
return function equal(expected) {
var _a;
const obj = utils.flag(this, 'object');
const [marbles] = getContext();
if (marbles && rxjs_1.isObservable(obj) && typeof expected === 'string') {
marbles_1.wrapLogSubscriptions(marbles.scheduler, obj);
const subscription = utils.flag(this, 'subscriptionMarbles');
const marbleValues = utils.flag(this, 'marbleValues');
const getObservableErrors = observableErrorsAccessor(this);
const observableErrors = getObservableErrors();
const errorHandledObservable = obj.pipe(marbles_1.collectErrors(observableErrors));
marbles.expectObservable(errorHandledObservable, subscription, this).toBe(expected, marbleValues);
// gross hacky stuff to make the stack trace line up correctly
const stackObj = {
name: 'REPLACE_ME',
};
Error.captureStackTrace(stackObj, utils.flag(this, 'ssfi'));
utils.flag(this, 'marblesStackObj', stackObj);
return;
}
const originalContext = utils.flag(this, 'originalContext');
if (originalContext) {
const verifyError = (_a = utils.flag(originalContext, 'verifyError')) !== null && _a !== void 0 ? _a : utils.flag(this, 'verifyError');
try {
utils.flag(this, 'message', utils.flag(originalContext, 'message'));
const result = _super.call(this, expected);
if (verifyError) {
throw Object.assign(new Error(''), { _isMissingVerifyError: true });
}
return result;
}
catch (err) {
// even more gross hacky stuff to make the stack trace line up correctly
const stackObj = utils.flag(originalContext, 'marblesStackObj');
const [, ...stackLines] = stackObj.stack.split('\n');
const [firstLine] = err.stack.split('\n');
const realStack = marbles_1.cleanStack(firstLine, ...stackLines);
// show stack traces for errors collected from the test subject observable
const getObservableErrors = observableErrorsAccessor(this);
const observableErrors = getObservableErrors();
const observableErrorsStacks = observableErrors.reduce((result, error) => {
if (result.length === 0) {
result.push('\nThe observable completed with error(s):\n');
}
result.push(marbles_1.cleanStack(error.stack || error.toString()));
return result;
}, []);
const fullStack = [realStack, ...observableErrorsStacks].join('\n\n');
Object.assign(err, {
stack: fullStack,
});
if (verifyError && !err._isMissingVerifyError) {
verifyError(err);
return true;
}
throw err;
}
}
return _super.call(this, expected);
};
});
Object.assign(chai.Assertion.prototype, { __chai_marbles_wuz_here: true });
};
exports.chaiMarbles = chaiMarbles;
const assertDeepEqual = (actual, expected, context) => {
// FIXME: sometimes the diff doesn't show - is it with certain deeply nested objects?
// Or specific to Uuid because it has certain stringifying implementations to fail the matcher, but don't
// show a difference for inspection?
chai_1.expect(actual).with.originalContext(context).to.deep.equal(expected);
};
exports.assertDeepEqual = assertDeepEqual;
const beforeEach = (helpers, context) => {
chai_1.util.flag(exports.chaiMarbles, 'currentTest', context);
chai_1.util.flag(context, 'marblesHelpers', helpers);
};
exports.beforeEach = beforeEach;
const afterEach = () => {
chai_1.util.flag(exports.chaiMarbles, 'currentTest', undefined);
};
exports.afterEach = afterEach;
//# sourceMappingURL=chai-marbles.js.map