-
-
Notifications
You must be signed in to change notification settings - Fork 2k
/
Copy pathindex.js
117 lines (101 loc) · 2.8 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { options } from 'preact';
/**
* Setup a rerender function that will drain the queue of pending renders
* @returns {() => void}
*/
export function setupRerender() {
options.__test__previousDebounce = options.debounceRendering;
options.debounceRendering = cb => (options.__test__drainQueue = cb);
return () => options.__test__drainQueue && options.__test__drainQueue();
}
const isThenable = value => value != null && typeof value.then == 'function';
/** Depth of nested calls to `act`. */
let actDepth = 0;
/**
* Run a test function, and flush all effects and rerenders after invoking it.
*
* Returns a Promise which resolves "immediately" if the callback is
* synchronous or when the callback's result resolves if it is asynchronous.
*
* @param {() => void|Promise<void>} cb The function under test. This may be sync or async.
* @return {Promise<void>}
*/
export function act(cb) {
if (++actDepth > 1) {
// If calls to `act` are nested, a flush happens only when the
// outermost call returns. In the inner call, we just execute the
// callback and return since the infrastructure for flushing has already
// been set up.
//
// If an exception occurs, the outermost `act` will handle cleanup.
const result = cb();
if (isThenable(result)) {
return result.then(() => {
--actDepth;
});
}
--actDepth;
return Promise.resolve();
}
const previousRequestAnimationFrame = options.requestAnimationFrame;
const rerender = setupRerender();
/** @type {() => void} */
let flush, toFlush;
// Override requestAnimationFrame so we can flush pending hooks.
options.requestAnimationFrame = fc => (flush = fc);
const finish = () => {
try {
rerender();
while (flush) {
toFlush = flush;
flush = null;
toFlush();
rerender();
}
teardown();
} catch (e) {
if (!err) {
err = e;
}
}
options.requestAnimationFrame = previousRequestAnimationFrame;
--actDepth;
};
let err;
let result;
try {
result = cb();
} catch (e) {
err = e;
}
if (isThenable(result)) {
return result.then(finish, err => {
finish();
throw err;
});
}
// nb. If the callback is synchronous, effects must be flushed before
// `act` returns, so that the caller does not have to await the result,
// even though React recommends this.
finish();
if (err) {
throw err;
}
return Promise.resolve();
}
/**
* Teardown test environment and reset preact's internal state
*/
export function teardown() {
if (options.__test__drainQueue) {
// Flush any pending updates leftover by test
options.__test__drainQueue();
delete options.__test__drainQueue;
}
if (typeof options.__test__previousDebounce != 'undefined') {
options.debounceRendering = options.__test__previousDebounce;
delete options.__test__previousDebounce;
} else {
options.debounceRendering = undefined;
}
}