Front end of the Slack clone application.

TestScheduler.js 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import { Observable } from '../Observable';
  2. import { Notification } from '../Notification';
  3. import { ColdObservable } from './ColdObservable';
  4. import { HotObservable } from './HotObservable';
  5. import { SubscriptionLog } from './SubscriptionLog';
  6. import { VirtualTimeScheduler, VirtualAction } from '../scheduler/VirtualTimeScheduler';
  7. const defaultMaxFrame = 750;
  8. export class TestScheduler extends VirtualTimeScheduler {
  9. constructor(assertDeepEqual) {
  10. super(VirtualAction, defaultMaxFrame);
  11. this.assertDeepEqual = assertDeepEqual;
  12. this.hotObservables = [];
  13. this.coldObservables = [];
  14. this.flushTests = [];
  15. }
  16. createTime(marbles) {
  17. const indexOf = marbles.indexOf('|');
  18. if (indexOf === -1) {
  19. throw new Error('marble diagram for time should have a completion marker "|"');
  20. }
  21. return indexOf * TestScheduler.frameTimeFactor;
  22. }
  23. createColdObservable(marbles, values, error) {
  24. if (marbles.indexOf('^') !== -1) {
  25. throw new Error('cold observable cannot have subscription offset "^"');
  26. }
  27. if (marbles.indexOf('!') !== -1) {
  28. throw new Error('cold observable cannot have unsubscription marker "!"');
  29. }
  30. const messages = TestScheduler.parseMarbles(marbles, values, error);
  31. const cold = new ColdObservable(messages, this);
  32. this.coldObservables.push(cold);
  33. return cold;
  34. }
  35. createHotObservable(marbles, values, error) {
  36. if (marbles.indexOf('!') !== -1) {
  37. throw new Error('hot observable cannot have unsubscription marker "!"');
  38. }
  39. const messages = TestScheduler.parseMarbles(marbles, values, error);
  40. const subject = new HotObservable(messages, this);
  41. this.hotObservables.push(subject);
  42. return subject;
  43. }
  44. materializeInnerObservable(observable, outerFrame) {
  45. const messages = [];
  46. observable.subscribe((value) => {
  47. messages.push({ frame: this.frame - outerFrame, notification: Notification.createNext(value) });
  48. }, (err) => {
  49. messages.push({ frame: this.frame - outerFrame, notification: Notification.createError(err) });
  50. }, () => {
  51. messages.push({ frame: this.frame - outerFrame, notification: Notification.createComplete() });
  52. });
  53. return messages;
  54. }
  55. expectObservable(observable, unsubscriptionMarbles = null) {
  56. const actual = [];
  57. const flushTest = { actual, ready: false };
  58. const unsubscriptionFrame = TestScheduler
  59. .parseMarblesAsSubscriptions(unsubscriptionMarbles).unsubscribedFrame;
  60. let subscription;
  61. this.schedule(() => {
  62. subscription = observable.subscribe(x => {
  63. let value = x;
  64. // Support Observable-of-Observables
  65. if (x instanceof Observable) {
  66. value = this.materializeInnerObservable(value, this.frame);
  67. }
  68. actual.push({ frame: this.frame, notification: Notification.createNext(value) });
  69. }, (err) => {
  70. actual.push({ frame: this.frame, notification: Notification.createError(err) });
  71. }, () => {
  72. actual.push({ frame: this.frame, notification: Notification.createComplete() });
  73. });
  74. }, 0);
  75. if (unsubscriptionFrame !== Number.POSITIVE_INFINITY) {
  76. this.schedule(() => subscription.unsubscribe(), unsubscriptionFrame);
  77. }
  78. this.flushTests.push(flushTest);
  79. return {
  80. toBe(marbles, values, errorValue) {
  81. flushTest.ready = true;
  82. flushTest.expected = TestScheduler.parseMarbles(marbles, values, errorValue, true);
  83. }
  84. };
  85. }
  86. expectSubscriptions(actualSubscriptionLogs) {
  87. const flushTest = { actual: actualSubscriptionLogs, ready: false };
  88. this.flushTests.push(flushTest);
  89. return {
  90. toBe(marbles) {
  91. const marblesArray = (typeof marbles === 'string') ? [marbles] : marbles;
  92. flushTest.ready = true;
  93. flushTest.expected = marblesArray.map(marbles => TestScheduler.parseMarblesAsSubscriptions(marbles));
  94. }
  95. };
  96. }
  97. flush() {
  98. const hotObservables = this.hotObservables;
  99. while (hotObservables.length > 0) {
  100. hotObservables.shift().setup();
  101. }
  102. super.flush();
  103. const readyFlushTests = this.flushTests.filter(test => test.ready);
  104. while (readyFlushTests.length > 0) {
  105. const test = readyFlushTests.shift();
  106. this.assertDeepEqual(test.actual, test.expected);
  107. }
  108. }
  109. static parseMarblesAsSubscriptions(marbles) {
  110. if (typeof marbles !== 'string') {
  111. return new SubscriptionLog(Number.POSITIVE_INFINITY);
  112. }
  113. const len = marbles.length;
  114. let groupStart = -1;
  115. let subscriptionFrame = Number.POSITIVE_INFINITY;
  116. let unsubscriptionFrame = Number.POSITIVE_INFINITY;
  117. for (let i = 0; i < len; i++) {
  118. const frame = i * this.frameTimeFactor;
  119. const c = marbles[i];
  120. switch (c) {
  121. case '-':
  122. case ' ':
  123. break;
  124. case '(':
  125. groupStart = frame;
  126. break;
  127. case ')':
  128. groupStart = -1;
  129. break;
  130. case '^':
  131. if (subscriptionFrame !== Number.POSITIVE_INFINITY) {
  132. throw new Error('found a second subscription point \'^\' in a ' +
  133. 'subscription marble diagram. There can only be one.');
  134. }
  135. subscriptionFrame = groupStart > -1 ? groupStart : frame;
  136. break;
  137. case '!':
  138. if (unsubscriptionFrame !== Number.POSITIVE_INFINITY) {
  139. throw new Error('found a second subscription point \'^\' in a ' +
  140. 'subscription marble diagram. There can only be one.');
  141. }
  142. unsubscriptionFrame = groupStart > -1 ? groupStart : frame;
  143. break;
  144. default:
  145. throw new Error('there can only be \'^\' and \'!\' markers in a ' +
  146. 'subscription marble diagram. Found instead \'' + c + '\'.');
  147. }
  148. }
  149. if (unsubscriptionFrame < 0) {
  150. return new SubscriptionLog(subscriptionFrame);
  151. }
  152. else {
  153. return new SubscriptionLog(subscriptionFrame, unsubscriptionFrame);
  154. }
  155. }
  156. static parseMarbles(marbles, values, errorValue, materializeInnerObservables = false) {
  157. if (marbles.indexOf('!') !== -1) {
  158. throw new Error('conventional marble diagrams cannot have the ' +
  159. 'unsubscription marker "!"');
  160. }
  161. const len = marbles.length;
  162. const testMessages = [];
  163. const subIndex = marbles.indexOf('^');
  164. const frameOffset = subIndex === -1 ? 0 : (subIndex * -this.frameTimeFactor);
  165. const getValue = typeof values !== 'object' ?
  166. (x) => x :
  167. (x) => {
  168. // Support Observable-of-Observables
  169. if (materializeInnerObservables && values[x] instanceof ColdObservable) {
  170. return values[x].messages;
  171. }
  172. return values[x];
  173. };
  174. let groupStart = -1;
  175. for (let i = 0; i < len; i++) {
  176. const frame = i * this.frameTimeFactor + frameOffset;
  177. let notification;
  178. const c = marbles[i];
  179. switch (c) {
  180. case '-':
  181. case ' ':
  182. break;
  183. case '(':
  184. groupStart = frame;
  185. break;
  186. case ')':
  187. groupStart = -1;
  188. break;
  189. case '|':
  190. notification = Notification.createComplete();
  191. break;
  192. case '^':
  193. break;
  194. case '#':
  195. notification = Notification.createError(errorValue || 'error');
  196. break;
  197. default:
  198. notification = Notification.createNext(getValue(c));
  199. break;
  200. }
  201. if (notification) {
  202. testMessages.push({ frame: groupStart > -1 ? groupStart : frame, notification });
  203. }
  204. }
  205. return testMessages;
  206. }
  207. }
  208. //# sourceMappingURL=TestScheduler.js.map