Store refactor: make it easier to test stores (#9290)
* refactor: convert RoomViewStore from flux Store to standard EventEmitter Parts of a series of experimental changes to improve the design of stores. * Use a gen5 store for RoomViewStore for now due to lock handling * Revert "Use a gen5 store for RoomViewStore for now due to lock handling" This reverts commit 1076af071d997d87b8ae0b0dcddfd1ae428665af. * Add untilEmission and tweak untilDispatch; use it in RoomViewStore * Add more RVS tests; remove custom room ID listener code and use EventEmitter * Better comments * Null guard `dis` as tests mock out `defaultDispatcher` * Additional tests
This commit is contained in:
parent
1f1a18f914
commit
06c4ba32cd
11 changed files with 289 additions and 129 deletions
|
@ -24,18 +24,104 @@ import { DispatcherAction } from "../../src/dispatcher/actions";
|
|||
|
||||
export const emitPromise = (e: EventEmitter, k: string | symbol) => new Promise(r => e.once(k, r));
|
||||
|
||||
export function untilDispatch(waitForAction: DispatcherAction): Promise<ActionPayload> {
|
||||
let dispatchHandle: string;
|
||||
return new Promise<ActionPayload>(resolve => {
|
||||
dispatchHandle = defaultDispatcher.register(payload => {
|
||||
if (payload.action === waitForAction) {
|
||||
defaultDispatcher.unregister(dispatchHandle);
|
||||
resolve(payload);
|
||||
/**
|
||||
* Waits for a certain payload to be dispatched.
|
||||
* @param waitForAction The action string to wait for or the callback which is invoked for every dispatch. If this returns true, stops waiting.
|
||||
* @param timeout The max time to wait before giving up and stop waiting. If 0, no timeout.
|
||||
* @param dispatcher The dispatcher to listen on.
|
||||
* @returns A promise which resolves when the callback returns true. Resolves with the payload that made it stop waiting.
|
||||
* Rejects when the timeout is reached.
|
||||
*/
|
||||
export function untilDispatch(
|
||||
waitForAction: DispatcherAction | ((payload: ActionPayload) => boolean), dispatcher=defaultDispatcher, timeout=1000,
|
||||
): Promise<ActionPayload> {
|
||||
const callerLine = new Error().stack.toString().split("\n")[2];
|
||||
if (typeof waitForAction === "string") {
|
||||
const action = waitForAction;
|
||||
waitForAction = (payload) => {
|
||||
return payload.action === action;
|
||||
};
|
||||
}
|
||||
const callback = waitForAction as ((payload: ActionPayload) => boolean);
|
||||
return new Promise((resolve, reject) => {
|
||||
let fulfilled = false;
|
||||
let timeoutId;
|
||||
// set a timeout handler if needed
|
||||
if (timeout > 0) {
|
||||
timeoutId = setTimeout(() => {
|
||||
if (!fulfilled) {
|
||||
reject(new Error(`untilDispatch: timed out at ${callerLine}`));
|
||||
fulfilled = true;
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
// listen for dispatches
|
||||
const token = dispatcher.register((p: ActionPayload) => {
|
||||
const finishWaiting = callback(p);
|
||||
if (finishWaiting || fulfilled) { // wait until we're told or we timeout
|
||||
// if we haven't timed out, resolve now with the payload.
|
||||
if (!fulfilled) {
|
||||
resolve(p);
|
||||
fulfilled = true;
|
||||
}
|
||||
// cleanup
|
||||
dispatcher.unregister(token);
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a certain event to be emitted.
|
||||
* @param emitter The EventEmitter to listen on.
|
||||
* @param eventName The event string to wait for.
|
||||
* @param check Optional function which is invoked when the event fires. If this returns true, stops waiting.
|
||||
* @param timeout The max time to wait before giving up and stop waiting. If 0, no timeout.
|
||||
* @returns A promise which resolves when the callback returns true or when the event is emitted if
|
||||
* no callback is provided. Rejects when the timeout is reached.
|
||||
*/
|
||||
export function untilEmission(
|
||||
emitter: EventEmitter, eventName: string, check: ((...args: any[]) => boolean)=undefined, timeout=1000,
|
||||
): Promise<void> {
|
||||
const callerLine = new Error().stack.toString().split("\n")[2];
|
||||
return new Promise((resolve, reject) => {
|
||||
let fulfilled = false;
|
||||
let timeoutId;
|
||||
// set a timeout handler if needed
|
||||
if (timeout > 0) {
|
||||
timeoutId = setTimeout(() => {
|
||||
if (!fulfilled) {
|
||||
reject(new Error(`untilEmission: timed out at ${callerLine}`));
|
||||
fulfilled = true;
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
const callback = (...args: any[]) => {
|
||||
// if they supplied a check function, call it now. Bail if it returns false.
|
||||
if (check) {
|
||||
if (!check(...args)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// we didn't time out, resolve. Otherwise, we already rejected so don't resolve now.
|
||||
if (!fulfilled) {
|
||||
resolve();
|
||||
fulfilled = true;
|
||||
}
|
||||
// cleanup
|
||||
emitter.off(eventName, callback);
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
};
|
||||
// listen for emissions
|
||||
emitter.on(eventName, callback);
|
||||
});
|
||||
}
|
||||
|
||||
export const findByAttr = (attr: string) => (component: ReactWrapper, value: string) =>
|
||||
component.find(`[${attr}="${value}"]`);
|
||||
export const findByTestId = findByAttr('data-test-id');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue