vitest-marbles
Version:
Marble testing helpers library for RxJs and Jest
518 lines (475 loc) • 19.3 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("rxjs"), require("rxjs/testing"));
else if(typeof define === 'function' && define.amd)
define("VitestMarbles", ["rxjs", "rxjs/testing"], factory);
else if(typeof exports === 'object')
exports["VitestMarbles"] = factory(require("rxjs"), require("rxjs/testing"));
else
root["VitestMarbles"] = factory(root["rxjs"], root["rxjs/testing"]);
})((typeof self !== 'undefined' ? self : this), (__WEBPACK_EXTERNAL_MODULE_rxjs__, __WEBPACK_EXTERNAL_MODULE_rxjs_testing__) => {
return /******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "rxjs":
/*!***********************!*\
!*** external "rxjs" ***!
\***********************/
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE_rxjs__;
/***/ }),
/***/ "rxjs/testing":
/*!*******************************!*\
!*** external "rxjs/testing" ***!
\*******************************/
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_MODULE_rxjs_testing__;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/*!*******************************!*\
!*** ./index.ts + 10 modules ***!
\*******************************/
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
"Scheduler": () => (/* reexport */ Scheduler),
"cold": () => (/* binding */ cold),
"hot": () => (/* binding */ hot),
"time": () => (/* binding */ time)
});
// EXTERNAL MODULE: external "rxjs"
var external_rxjs_ = __webpack_require__("rxjs");
// EXTERNAL MODULE: external "rxjs/testing"
var testing_ = __webpack_require__("rxjs/testing");
;// CONCATENATED MODULE: ./src/marbles-glossary.ts
var MarblesGlossary;
(function (MarblesGlossary) {
MarblesGlossary["Completion"] = "|";
MarblesGlossary["Error"] = "#";
MarblesGlossary["TimeFrame"] = "-";
MarblesGlossary["Subscription"] = "^";
MarblesGlossary["Unsubscription"] = "!";
MarblesGlossary["GroupStart"] = "(";
MarblesGlossary["GroupEnd"] = ")";
})(MarblesGlossary || (MarblesGlossary = {}));
;// CONCATENATED MODULE: ./src/notification-event.ts
var NotificationEvent = /** @class */ (function () {
function NotificationEvent(start) {
this.start = start;
this.marbles = '';
}
Object.defineProperty(NotificationEvent.prototype, "end", {
get: function () {
return this.start + this.marbles.length;
},
enumerable: false,
configurable: true
});
return NotificationEvent;
}());
;// CONCATENATED MODULE: ./src/notification-kind.ts
var ValueLiteral = {};
var NotificationKindChars = {
N: ValueLiteral,
C: MarblesGlossary.Completion,
E: MarblesGlossary.Error,
};
;// CONCATENATED MODULE: ./src/marblizer.ts
var frameStep = 10;
var Marblizer = /** @class */ (function () {
function Marblizer() {
}
Marblizer.marblize = function (messages) {
var emissions = Marblizer.getNotificationEvents(messages);
var marbles = '';
for (var i = 0, prevEndFrame = 0; i < emissions.length; prevEndFrame = emissions[i].end, i++) {
marbles = "".concat(marbles).concat(MarblesGlossary.TimeFrame.repeat(emissions[i].start - prevEndFrame) +
emissions[i].marbles);
}
return marbles;
};
Marblizer.marblizeSubscriptions = function (logs) {
var _this = this;
return logs.map(function (log) {
return _this.marblizeLogEntry(log.subscribedFrame / frameStep, MarblesGlossary.Subscription) +
_this.marblizeLogEntry((log.unsubscribedFrame - log.subscribedFrame) / frameStep - 1, MarblesGlossary.Unsubscription);
});
};
Marblizer.marblizeLogEntry = function (logPoint, symbol) {
if (logPoint !== Infinity) {
return MarblesGlossary.TimeFrame.repeat(logPoint) + symbol;
}
else {
return '';
}
};
Marblizer.getNotificationEvents = function (messages) {
var framesToEmissions = messages.reduce(function (result, message) {
if (!result[message.frame]) {
result[message.frame] = new NotificationEvent(message.frame / frameStep);
}
var event = result[message.frame];
event.marbles += Marblizer.extractMarble(message);
return result;
}, {});
var events = Object.keys(framesToEmissions).map(function (frame) { return framesToEmissions[frame]; });
Marblizer.encloseGroupEvents(events);
return events;
};
Marblizer.extractMarble = function (message) {
var marble = NotificationKindChars[message.notification.kind];
if (marble === ValueLiteral)
marble = message.notification.value;
return marble;
};
Marblizer.encloseGroupEvents = function (events) {
events.forEach(function (event) {
if (event.marbles.length > 1) {
event.marbles = "".concat(MarblesGlossary.GroupStart).concat(event.marbles).concat(MarblesGlossary.GroupEnd);
}
});
};
return Marblizer;
}());
;// CONCATENATED MODULE: ./src/vitest/custom-matchers.ts
function canMarblize() {
var messages = [];
for (var _i = 0; _i < arguments.length; _i++) {
messages[_i] = arguments[_i];
}
return messages.every(function (message) { return message.filter(function (_a) {
var kind = _a.notification.kind;
return kind === 'N';
}).every(isCharacter); });
}
function isCharacter(_a) {
var notification = _a.notification;
var value = notification.value;
return ((typeof value === 'string' && value.length === 1) || (value !== undefined && JSON.stringify(value).length === 1));
}
var customTestMatchers = {
toBeNotifications: function (actual, expected) {
var _this = this;
var actualMarble = actual;
var expectedMarble = expected;
if (canMarblize(actual, expected)) {
actualMarble = Marblizer.marblize(actual);
expectedMarble = Marblizer.marblize(expected);
}
var pass = this.equals(actualMarble, expectedMarble);
var message = pass
? function () {
return _this.utils.matcherHint('.not.toBeNotifications') +
'\n\n' +
"Expected notifications to not be:\n" +
" ".concat(_this.utils.printExpected(expectedMarble), "\n") +
"But got:\n" +
" ".concat(_this.utils.printReceived(actualMarble));
}
: function () {
var diffString = _this.utils.diff(expectedMarble, actualMarble, {
expand: true,
});
return (_this.utils.matcherHint('.toBeNotifications') +
'\n\n' +
"Expected notifications to be:\n" +
" ".concat(_this.utils.printExpected(expectedMarble), "\n") +
"But got:\n" +
" ".concat(_this.utils.printReceived(actualMarble)) +
(diffString ? "\n\nDifference:\n\n".concat(diffString) : ''));
};
return { message: message, pass: pass };
},
toBeSubscriptions: function (actual, expected) {
var _this = this;
var actualMarbleArray = Marblizer.marblizeSubscriptions(actual);
var expectedMarbleArray = Marblizer.marblizeSubscriptions(expected);
var pass = subscriptionsPass(actualMarbleArray, expectedMarbleArray);
var message = pass
? function () {
return _this.utils.matcherHint('.not.toHaveSubscriptions') +
'\n\n' +
"Expected observable to not have the following subscription points:\n" +
" ".concat(_this.utils.printExpected(expectedMarbleArray), "\n") +
"But got:\n" +
" ".concat(_this.utils.printReceived(actualMarbleArray));
}
: function () {
var diffString = _this.utils.diff(expectedMarbleArray, actualMarbleArray, {
expand: true,
});
return (_this.utils.matcherHint('.toHaveSubscriptions') +
'\n\n' +
"Expected observable to have the following subscription points:\n" +
" ".concat(_this.utils.printExpected(expectedMarbleArray), "\n") +
"But got:\n" +
" ".concat(_this.utils.printReceived(actualMarbleArray)) +
(diffString ? "\n\nDifference:\n\n".concat(diffString) : ''));
};
return { message: message, pass: pass };
},
toHaveEmptySubscriptions: function (actual) {
var _this = this;
var pass = !(actual && actual.length > 0);
var marbles;
if (actual && actual.length > 0) {
marbles = Marblizer.marblizeSubscriptions(actual);
}
var message = pass
? function () {
return _this.utils.matcherHint('.not.toHaveNoSubscriptions') +
'\n\n' +
"Expected observable to have at least one subscription point, but got nothing" +
_this.utils.printReceived('');
}
: function () {
return _this.utils.matcherHint('.toHaveNoSubscriptions') +
'\n\n' +
"Expected observable to have no subscription points\n" +
"But got:\n" +
" ".concat(_this.utils.printReceived(marbles), "\n\n");
};
return { message: message, pass: pass };
},
};
function subscriptionsPass(actualMarbleArray, expectedMarbleArray) {
if (actualMarbleArray.length !== expectedMarbleArray.length) {
return false;
}
var pass = true;
for (var _i = 0, actualMarbleArray_1 = actualMarbleArray; _i < actualMarbleArray_1.length; _i++) {
var actualMarble = actualMarbleArray_1[_i];
if (!expectedMarbleArray.includes(actualMarble)) {
pass = false;
break;
}
}
return pass;
}
expect.extend(customTestMatchers);
;// CONCATENATED MODULE: ./src/rxjs/assert-deep-equal.ts
function expectedIsSubscriptionLogArray(actual, expected) {
return ((actual.length === 0 && expected.length === 0) ||
(expected.length !== 0 && expected[0].subscribedFrame !== undefined));
}
function actualIsSubscriptionsAndExpectedIsEmpty(actual, expected) {
return expected.length === 0 && actual.length !== 0 && actual[0].subscribedFrame !== undefined;
}
function assertDeepEqual(actual, expected) {
if (!expected)
return;
if (actualIsSubscriptionsAndExpectedIsEmpty(actual, expected)) {
expect(actual).toHaveEmptySubscriptions();
}
else if (expectedIsSubscriptionLogArray(actual, expected)) {
expect(actual).toBeSubscriptions(expected);
}
else {
expect(actual).toBeNotifications(expected);
}
}
;// CONCATENATED MODULE: ./src/rxjs/scheduler.ts
var Scheduler = /** @class */ (function () {
function Scheduler() {
}
Scheduler.init = function () {
Scheduler.instance = new testing_.TestScheduler(assertDeepEqual);
};
Scheduler.get = function () {
if (Scheduler.instance) {
return Scheduler.instance;
}
throw new Error('Scheduler is not initialized');
};
Scheduler.reset = function () {
Scheduler.instance = null;
};
Scheduler.materializeInnerObservable = function (observable, outerFrame) {
var scheduler = Scheduler.get();
// @ts-ignore
return scheduler.materializeInnerObservable(observable, outerFrame);
};
return Scheduler;
}());
;// CONCATENATED MODULE: ./src/rxjs/cold-observable.ts
var __extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var ColdObservable = /** @class */ (function (_super) {
__extends(ColdObservable, _super);
function ColdObservable(marbles, values, error) {
var _this = _super.call(this) || this;
_this.marbles = marbles;
_this.values = values;
_this.error = error;
_this.source = Scheduler.get().createColdObservable(marbles, values, error);
return _this;
}
ColdObservable.prototype.getSubscriptions = function () {
return this.source.subscriptions;
};
return ColdObservable;
}(external_rxjs_.Observable));
;// CONCATENATED MODULE: ./src/rxjs/hot-observable.ts
var hot_observable_extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var HotObservable = /** @class */ (function (_super) {
hot_observable_extends(HotObservable, _super);
function HotObservable(marbles, values, error) {
var _this = _super.call(this) || this;
_this.marbles = marbles;
_this.values = values;
_this.error = error;
_this.source = Scheduler.get().createHotObservable(marbles, values, error);
return _this;
}
HotObservable.prototype.getSubscriptions = function () {
return this.source.subscriptions;
};
return HotObservable;
}(external_rxjs_.Observable));
;// CONCATENATED MODULE: ./src/rxjs/strip-alignment-chars.ts
function stripAlignmentChars(marbles) {
return marbles.replace(/^[ ]+/, '');
}
;// CONCATENATED MODULE: ./index.ts
function hot(marbles, values, error) {
return new HotObservable(stripAlignmentChars(marbles), values, error);
}
function cold(marbles, values, error) {
return new ColdObservable(stripAlignmentChars(marbles), values, error);
}
function time(marbles) {
return Scheduler.get().createTime(stripAlignmentChars(marbles));
}
var dummyResult = {
message: function () { return ''; },
pass: true
};
expect.extend({
toHaveSubscriptions: function (actual, marbles) {
var sanitizedMarbles = Array.isArray(marbles) ? marbles.map(stripAlignmentChars) : stripAlignmentChars(marbles);
Scheduler.get().expectSubscriptions(actual.getSubscriptions()).toBe(sanitizedMarbles);
return dummyResult;
},
toHaveNoSubscriptions: function (actual) {
Scheduler.get().expectSubscriptions(actual.getSubscriptions()).toBe([]);
return dummyResult;
},
toBeObservable: function (actual, expected) {
Scheduler.get().expectObservable(actual).toBe(expected.marbles, expected.values, expected.error);
return dummyResult;
},
toBeMarble: function (actual, marbles) {
Scheduler.get().expectObservable(actual).toBe(stripAlignmentChars(marbles));
return dummyResult;
},
toSatisfyOnFlush: function (actual, func) {
Scheduler.get().expectObservable(actual);
// tslint:disable:no-string-literal
var flushTests = Scheduler.get()['flushTests'];
flushTests[flushTests.length - 1].ready = true;
onFlush.push(func);
return dummyResult;
}
});
var onFlush = [];
beforeEach(function () { Scheduler.init(); onFlush = []; });
afterEach(function () {
Scheduler.get().flush();
while (onFlush.length > 0) {
// @ts-ignore
onFlush.shift()();
}
Scheduler.reset();
});
})();
/******/ return __webpack_exports__;
/******/ })()
;
});
//# sourceMappingURL=vitest-marbles.js.map