TestScheduler.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. "use strict";
  2. var __extends = (this && this.__extends) || (function () {
  3. var extendStatics = function (d, b) {
  4. extendStatics = Object.setPrototypeOf ||
  5. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  6. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  7. return extendStatics(d, b);
  8. }
  9. return function (d, b) {
  10. extendStatics(d, b);
  11. function __() { this.constructor = d; }
  12. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  13. };
  14. })();
  15. Object.defineProperty(exports, "__esModule", { value: true });
  16. var Observable_1 = require("../Observable");
  17. var Notification_1 = require("../Notification");
  18. var ColdObservable_1 = require("./ColdObservable");
  19. var HotObservable_1 = require("./HotObservable");
  20. var SubscriptionLog_1 = require("./SubscriptionLog");
  21. var VirtualTimeScheduler_1 = require("../scheduler/VirtualTimeScheduler");
  22. var AsyncScheduler_1 = require("../scheduler/AsyncScheduler");
  23. var defaultMaxFrame = 750;
  24. var TestScheduler = (function (_super) {
  25. __extends(TestScheduler, _super);
  26. function TestScheduler(assertDeepEqual) {
  27. var _this = _super.call(this, VirtualTimeScheduler_1.VirtualAction, defaultMaxFrame) || this;
  28. _this.assertDeepEqual = assertDeepEqual;
  29. _this.hotObservables = [];
  30. _this.coldObservables = [];
  31. _this.flushTests = [];
  32. _this.runMode = false;
  33. return _this;
  34. }
  35. TestScheduler.prototype.createTime = function (marbles) {
  36. var indexOf = marbles.indexOf('|');
  37. if (indexOf === -1) {
  38. throw new Error('marble diagram for time should have a completion marker "|"');
  39. }
  40. return indexOf * TestScheduler.frameTimeFactor;
  41. };
  42. TestScheduler.prototype.createColdObservable = function (marbles, values, error) {
  43. if (marbles.indexOf('^') !== -1) {
  44. throw new Error('cold observable cannot have subscription offset "^"');
  45. }
  46. if (marbles.indexOf('!') !== -1) {
  47. throw new Error('cold observable cannot have unsubscription marker "!"');
  48. }
  49. var messages = TestScheduler.parseMarbles(marbles, values, error, undefined, this.runMode);
  50. var cold = new ColdObservable_1.ColdObservable(messages, this);
  51. this.coldObservables.push(cold);
  52. return cold;
  53. };
  54. TestScheduler.prototype.createHotObservable = function (marbles, values, error) {
  55. if (marbles.indexOf('!') !== -1) {
  56. throw new Error('hot observable cannot have unsubscription marker "!"');
  57. }
  58. var messages = TestScheduler.parseMarbles(marbles, values, error, undefined, this.runMode);
  59. var subject = new HotObservable_1.HotObservable(messages, this);
  60. this.hotObservables.push(subject);
  61. return subject;
  62. };
  63. TestScheduler.prototype.materializeInnerObservable = function (observable, outerFrame) {
  64. var _this = this;
  65. var messages = [];
  66. observable.subscribe(function (value) {
  67. messages.push({ frame: _this.frame - outerFrame, notification: Notification_1.Notification.createNext(value) });
  68. }, function (err) {
  69. messages.push({ frame: _this.frame - outerFrame, notification: Notification_1.Notification.createError(err) });
  70. }, function () {
  71. messages.push({ frame: _this.frame - outerFrame, notification: Notification_1.Notification.createComplete() });
  72. });
  73. return messages;
  74. };
  75. TestScheduler.prototype.expectObservable = function (observable, subscriptionMarbles) {
  76. var _this = this;
  77. if (subscriptionMarbles === void 0) { subscriptionMarbles = null; }
  78. var actual = [];
  79. var flushTest = { actual: actual, ready: false };
  80. var subscriptionParsed = TestScheduler.parseMarblesAsSubscriptions(subscriptionMarbles, this.runMode);
  81. var subscriptionFrame = subscriptionParsed.subscribedFrame === Number.POSITIVE_INFINITY ?
  82. 0 : subscriptionParsed.subscribedFrame;
  83. var unsubscriptionFrame = subscriptionParsed.unsubscribedFrame;
  84. var subscription;
  85. this.schedule(function () {
  86. subscription = observable.subscribe(function (x) {
  87. var value = x;
  88. if (x instanceof Observable_1.Observable) {
  89. value = _this.materializeInnerObservable(value, _this.frame);
  90. }
  91. actual.push({ frame: _this.frame, notification: Notification_1.Notification.createNext(value) });
  92. }, function (err) {
  93. actual.push({ frame: _this.frame, notification: Notification_1.Notification.createError(err) });
  94. }, function () {
  95. actual.push({ frame: _this.frame, notification: Notification_1.Notification.createComplete() });
  96. });
  97. }, subscriptionFrame);
  98. if (unsubscriptionFrame !== Number.POSITIVE_INFINITY) {
  99. this.schedule(function () { return subscription.unsubscribe(); }, unsubscriptionFrame);
  100. }
  101. this.flushTests.push(flushTest);
  102. var runMode = this.runMode;
  103. return {
  104. toBe: function (marbles, values, errorValue) {
  105. flushTest.ready = true;
  106. flushTest.expected = TestScheduler.parseMarbles(marbles, values, errorValue, true, runMode);
  107. }
  108. };
  109. };
  110. TestScheduler.prototype.expectSubscriptions = function (actualSubscriptionLogs) {
  111. var flushTest = { actual: actualSubscriptionLogs, ready: false };
  112. this.flushTests.push(flushTest);
  113. var runMode = this.runMode;
  114. return {
  115. toBe: function (marbles) {
  116. var marblesArray = (typeof marbles === 'string') ? [marbles] : marbles;
  117. flushTest.ready = true;
  118. flushTest.expected = marblesArray.map(function (marbles) {
  119. return TestScheduler.parseMarblesAsSubscriptions(marbles, runMode);
  120. });
  121. }
  122. };
  123. };
  124. TestScheduler.prototype.flush = function () {
  125. var _this = this;
  126. var hotObservables = this.hotObservables;
  127. while (hotObservables.length > 0) {
  128. hotObservables.shift().setup();
  129. }
  130. _super.prototype.flush.call(this);
  131. this.flushTests = this.flushTests.filter(function (test) {
  132. if (test.ready) {
  133. _this.assertDeepEqual(test.actual, test.expected);
  134. return false;
  135. }
  136. return true;
  137. });
  138. };
  139. TestScheduler.parseMarblesAsSubscriptions = function (marbles, runMode) {
  140. var _this = this;
  141. if (runMode === void 0) { runMode = false; }
  142. if (typeof marbles !== 'string') {
  143. return new SubscriptionLog_1.SubscriptionLog(Number.POSITIVE_INFINITY);
  144. }
  145. var len = marbles.length;
  146. var groupStart = -1;
  147. var subscriptionFrame = Number.POSITIVE_INFINITY;
  148. var unsubscriptionFrame = Number.POSITIVE_INFINITY;
  149. var frame = 0;
  150. var _loop_1 = function (i) {
  151. var nextFrame = frame;
  152. var advanceFrameBy = function (count) {
  153. nextFrame += count * _this.frameTimeFactor;
  154. };
  155. var c = marbles[i];
  156. switch (c) {
  157. case ' ':
  158. if (!runMode) {
  159. advanceFrameBy(1);
  160. }
  161. break;
  162. case '-':
  163. advanceFrameBy(1);
  164. break;
  165. case '(':
  166. groupStart = frame;
  167. advanceFrameBy(1);
  168. break;
  169. case ')':
  170. groupStart = -1;
  171. advanceFrameBy(1);
  172. break;
  173. case '^':
  174. if (subscriptionFrame !== Number.POSITIVE_INFINITY) {
  175. throw new Error('found a second subscription point \'^\' in a ' +
  176. 'subscription marble diagram. There can only be one.');
  177. }
  178. subscriptionFrame = groupStart > -1 ? groupStart : frame;
  179. advanceFrameBy(1);
  180. break;
  181. case '!':
  182. if (unsubscriptionFrame !== Number.POSITIVE_INFINITY) {
  183. throw new Error('found a second subscription point \'^\' in a ' +
  184. 'subscription marble diagram. There can only be one.');
  185. }
  186. unsubscriptionFrame = groupStart > -1 ? groupStart : frame;
  187. break;
  188. default:
  189. if (runMode && c.match(/^[0-9]$/)) {
  190. if (i === 0 || marbles[i - 1] === ' ') {
  191. var buffer = marbles.slice(i);
  192. var match = buffer.match(/^([0-9]+(?:\.[0-9]+)?)(ms|s|m) /);
  193. if (match) {
  194. i += match[0].length - 1;
  195. var duration = parseFloat(match[1]);
  196. var unit = match[2];
  197. var durationInMs = void 0;
  198. switch (unit) {
  199. case 'ms':
  200. durationInMs = duration;
  201. break;
  202. case 's':
  203. durationInMs = duration * 1000;
  204. break;
  205. case 'm':
  206. durationInMs = duration * 1000 * 60;
  207. break;
  208. default:
  209. break;
  210. }
  211. advanceFrameBy(durationInMs / this_1.frameTimeFactor);
  212. break;
  213. }
  214. }
  215. }
  216. throw new Error('there can only be \'^\' and \'!\' markers in a ' +
  217. 'subscription marble diagram. Found instead \'' + c + '\'.');
  218. }
  219. frame = nextFrame;
  220. out_i_1 = i;
  221. };
  222. var this_1 = this, out_i_1;
  223. for (var i = 0; i < len; i++) {
  224. _loop_1(i);
  225. i = out_i_1;
  226. }
  227. if (unsubscriptionFrame < 0) {
  228. return new SubscriptionLog_1.SubscriptionLog(subscriptionFrame);
  229. }
  230. else {
  231. return new SubscriptionLog_1.SubscriptionLog(subscriptionFrame, unsubscriptionFrame);
  232. }
  233. };
  234. TestScheduler.parseMarbles = function (marbles, values, errorValue, materializeInnerObservables, runMode) {
  235. var _this = this;
  236. if (materializeInnerObservables === void 0) { materializeInnerObservables = false; }
  237. if (runMode === void 0) { runMode = false; }
  238. if (marbles.indexOf('!') !== -1) {
  239. throw new Error('conventional marble diagrams cannot have the ' +
  240. 'unsubscription marker "!"');
  241. }
  242. var len = marbles.length;
  243. var testMessages = [];
  244. var subIndex = runMode ? marbles.replace(/^[ ]+/, '').indexOf('^') : marbles.indexOf('^');
  245. var frame = subIndex === -1 ? 0 : (subIndex * -this.frameTimeFactor);
  246. var getValue = typeof values !== 'object' ?
  247. function (x) { return x; } :
  248. function (x) {
  249. if (materializeInnerObservables && values[x] instanceof ColdObservable_1.ColdObservable) {
  250. return values[x].messages;
  251. }
  252. return values[x];
  253. };
  254. var groupStart = -1;
  255. var _loop_2 = function (i) {
  256. var nextFrame = frame;
  257. var advanceFrameBy = function (count) {
  258. nextFrame += count * _this.frameTimeFactor;
  259. };
  260. var notification = void 0;
  261. var c = marbles[i];
  262. switch (c) {
  263. case ' ':
  264. if (!runMode) {
  265. advanceFrameBy(1);
  266. }
  267. break;
  268. case '-':
  269. advanceFrameBy(1);
  270. break;
  271. case '(':
  272. groupStart = frame;
  273. advanceFrameBy(1);
  274. break;
  275. case ')':
  276. groupStart = -1;
  277. advanceFrameBy(1);
  278. break;
  279. case '|':
  280. notification = Notification_1.Notification.createComplete();
  281. advanceFrameBy(1);
  282. break;
  283. case '^':
  284. advanceFrameBy(1);
  285. break;
  286. case '#':
  287. notification = Notification_1.Notification.createError(errorValue || 'error');
  288. advanceFrameBy(1);
  289. break;
  290. default:
  291. if (runMode && c.match(/^[0-9]$/)) {
  292. if (i === 0 || marbles[i - 1] === ' ') {
  293. var buffer = marbles.slice(i);
  294. var match = buffer.match(/^([0-9]+(?:\.[0-9]+)?)(ms|s|m) /);
  295. if (match) {
  296. i += match[0].length - 1;
  297. var duration = parseFloat(match[1]);
  298. var unit = match[2];
  299. var durationInMs = void 0;
  300. switch (unit) {
  301. case 'ms':
  302. durationInMs = duration;
  303. break;
  304. case 's':
  305. durationInMs = duration * 1000;
  306. break;
  307. case 'm':
  308. durationInMs = duration * 1000 * 60;
  309. break;
  310. default:
  311. break;
  312. }
  313. advanceFrameBy(durationInMs / this_2.frameTimeFactor);
  314. break;
  315. }
  316. }
  317. }
  318. notification = Notification_1.Notification.createNext(getValue(c));
  319. advanceFrameBy(1);
  320. break;
  321. }
  322. if (notification) {
  323. testMessages.push({ frame: groupStart > -1 ? groupStart : frame, notification: notification });
  324. }
  325. frame = nextFrame;
  326. out_i_2 = i;
  327. };
  328. var this_2 = this, out_i_2;
  329. for (var i = 0; i < len; i++) {
  330. _loop_2(i);
  331. i = out_i_2;
  332. }
  333. return testMessages;
  334. };
  335. TestScheduler.prototype.run = function (callback) {
  336. var prevFrameTimeFactor = TestScheduler.frameTimeFactor;
  337. var prevMaxFrames = this.maxFrames;
  338. TestScheduler.frameTimeFactor = 1;
  339. this.maxFrames = Number.POSITIVE_INFINITY;
  340. this.runMode = true;
  341. AsyncScheduler_1.AsyncScheduler.delegate = this;
  342. var helpers = {
  343. cold: this.createColdObservable.bind(this),
  344. hot: this.createHotObservable.bind(this),
  345. flush: this.flush.bind(this),
  346. expectObservable: this.expectObservable.bind(this),
  347. expectSubscriptions: this.expectSubscriptions.bind(this),
  348. };
  349. try {
  350. var ret = callback(helpers);
  351. this.flush();
  352. return ret;
  353. }
  354. finally {
  355. TestScheduler.frameTimeFactor = prevFrameTimeFactor;
  356. this.maxFrames = prevMaxFrames;
  357. this.runMode = false;
  358. AsyncScheduler_1.AsyncScheduler.delegate = undefined;
  359. }
  360. };
  361. return TestScheduler;
  362. }(VirtualTimeScheduler_1.VirtualTimeScheduler));
  363. exports.TestScheduler = TestScheduler;
  364. //# sourceMappingURL=TestScheduler.js.map