@samitouri / QOS-React / commits / c0fe8d6f69

Adds ReactScheduler red->green unit test for bug fixed in #12834 (#12861)

* Scheduler red->green unit test for bug * fix lint issue * ran prettier

Royi Hagigi committed May 18, 2018 at 15:05 UTC c0fe8d6f6942f5cbc93c09825e803ba8cf950522
1 file changed +51 -11
packages/react-scheduler/src/__tests__/ReactScheduler-test.js
+51 -11
@@ -12,6 +12,20 @@
12 let ReactScheduler;
13
14 describe('ReactScheduler', () => {
15 + let postMessageCallback;
16 + let postMessageEvents = [];
17 +
18 + function drainPostMessageQueue() {
19 + if (postMessageCallback) {
20 + while (postMessageEvents.length) {
21 + postMessageCallback(postMessageEvents.shift());
22 + }
23 + }
24 + }
25 + function advanceAll() {
26 + jest.runAllTimers();
27 + drainPostMessageQueue();
28 + }
29 beforeEach(() => {
30 // TODO pull this into helper method, reduce repetition.
31 // mock the browser APIs which are used in react-scheduler:
@@ -23,7 +37,8 @@ describe('ReactScheduler', () => {
37 });
38 };
39 const originalAddEventListener = global.addEventListener;
26 - let postMessageCallback;
40 + postMessageCallback = null;
41 + postMessageEvents = [];
42 global.addEventListener = function(eventName, callback, useCapture) {
43 if (eventName === 'message') {
44 postMessageCallback = callback;
@@ -33,9 +48,7 @@ describe('ReactScheduler', () => {
48 };
49 global.postMessage = function(messageKey, targetOrigin) {
50 const postMessageEvent = {source: window, data: messageKey};
36 - if (postMessageCallback) {
37 - postMessageCallback(postMessageEvent);
38 - }
51 + postMessageEvents.push(postMessageEvent);
52 };
53 jest.resetModules();
54 ReactScheduler = require('react-scheduler');
@@ -46,7 +59,7 @@ describe('ReactScheduler', () => {
59 const {scheduleWork} = ReactScheduler;
60 const cb = jest.fn();
61 scheduleWork(cb);
49 - jest.runAllTimers();
62 + advanceAll();
63 expect(cb.mock.calls.length).toBe(1);
64 // should not have timed out and should include a timeRemaining method
65 expect(cb.mock.calls[0][0].didTimeout).toBe(false);
@@ -66,7 +79,7 @@ describe('ReactScheduler', () => {
79 scheduleWork(callbackB);
80 expect(callbackLog).toEqual([]);
81 // after a delay, calls as many callbacks as it has time for
69 - jest.runAllTimers();
82 + advanceAll();
83 expect(callbackLog).toEqual(['A', 'B']);
84 // callbackA should not have timed out and should include a timeRemaining method
85 expect(callbackA.mock.calls[0][0].didTimeout).toBe(false);
@@ -80,6 +93,33 @@ describe('ReactScheduler', () => {
93 );
94 });
95
96 + it("accepts callbacks betweeen animationFrame and postMessage and doesn't stall", () => {
97 + const {scheduleWork} = ReactScheduler;
98 + const callbackLog = [];
99 + const callbackA = jest.fn(() => callbackLog.push('A'));
100 + const callbackB = jest.fn(() => callbackLog.push('B'));
101 + const callbackC = jest.fn(() => callbackLog.push('C'));
102 + scheduleWork(callbackA);
103 + // initially waits to call the callback
104 + expect(callbackLog).toEqual([]);
105 + jest.runAllTimers();
106 + // this should schedule work *after* the requestAnimationFrame but before the message handler
107 + scheduleWork(callbackB);
108 + expect(callbackLog).toEqual([]);
109 + // now it should drain the message queue and do all scheduled work
110 + drainPostMessageQueue();
111 + expect(callbackLog).toEqual(['A', 'B']);
112 +
113 + // advances timers, now with an empty queue of work (to ensure they don't deadlock)
114 + advanceAll();
115 +
116 + // see if more work can be done now.
117 + scheduleWork(callbackC);
118 + expect(callbackLog).toEqual(['A', 'B']);
119 + advanceAll();
120 + expect(callbackLog).toEqual(['A', 'B', 'C']);
121 + });
122 +
123 it(
124 'schedules callbacks in correct order and' +
125 'keeps calling them if there is time',
@@ -105,7 +145,7 @@ describe('ReactScheduler', () => {
145 expect(callbackLog).toEqual([]);
146 // after a delay, calls the scheduled callbacks,
147 // and also calls new callbacks scheduled by current callbacks
108 - jest.runAllTimers();
148 + advanceAll();
149 expect(callbackLog).toEqual(['A', 'B', 'C']);
150 },
151 );
@@ -141,7 +181,7 @@ describe('ReactScheduler', () => {
181 // initially waits to call the callback
182 expect(callbackLog).toEqual([]);
183 // while flushing callbacks, calls as many as it has time for
144 - jest.runAllTimers();
184 + advanceAll();
185 expect(callbackLog).toEqual(['A', 'B', 'C', 'D', 'E', 'F']);
186 });
187
@@ -164,7 +204,7 @@ describe('ReactScheduler', () => {
204 scheduleWork(callbackB);
205 expect(callbackLog).toEqual([]);
206 // after a delay, calls the latest callback passed
167 - jest.runAllTimers();
207 + advanceAll();
208 expect(callbackLog).toEqual(['A0', 'B', 'A1']);
209 });
210 });
@@ -177,7 +217,7 @@ describe('ReactScheduler', () => {
217 const callbackId = scheduleWork(cb);
218 expect(cb.mock.calls.length).toBe(0);
219 cancelScheduledWork(callbackId);
180 - jest.runAllTimers();
220 + advanceAll();
221 expect(cb.mock.calls.length).toBe(0);
222 });
223
@@ -195,7 +235,7 @@ describe('ReactScheduler', () => {
235 callbackBId = scheduleWork(callbackB);
236 // Initially doesn't call anything
237 expect(callbackLog).toEqual([]);
198 - jest.runAllTimers();
238 + advanceAll();
239 // B should not get called because A cancelled B
240 expect(callbackLog).toEqual(['A']);
241 expect(callbackB.mock.calls.length).toBe(0);