xref: /expo/apps/test-suite/tests/helpers.js (revision caa57586)
1import asyncRetry from 'async-retry';
2import { isMatch } from 'lodash';
3import React from 'react';
4import { Alert } from 'react-native';
5
6export const waitFor = (millis) => new Promise((resolve) => setTimeout(resolve, millis));
7
8export const alertAndWaitForResponse = async (message) => {
9  return new Promise((resolve) => Alert.alert(message, null, [{ text: 'OK', onPress: resolve }]));
10};
11
12export const retryForStatus = (object, status) =>
13  asyncRetry(
14    async (bail, retriesCount) => {
15      const readStatus = await object.getStatusAsync();
16      if (isMatch(readStatus, status)) {
17        return true;
18      } else {
19        const stringifiedStatus = JSON.stringify(status);
20        const desiredError = `The A/V instance has not entered desired state (${stringifiedStatus}) after ${retriesCount} retries.`;
21        const lastKnownError = `Last known state: ${JSON.stringify(readStatus)}.`;
22        throw new Error(`${desiredError} ${lastKnownError}`);
23      }
24    },
25    { retries: 5, minTimeout: 100 }
26  );
27
28export const mountAndWaitFor = (child, propName = 'ref', setPortalChild) =>
29  new Promise((resolve) => {
30    // `ref` prop is set directly in the child, not in the `props` object.
31    // https://github.com/facebook/react/issues/8873#issuecomment-275423780
32    const previousPropFunc = propName === 'ref' ? child.ref : child.props[propName];
33    const newPropFunc = (val) => {
34      previousPropFunc && previousPropFunc(val);
35      resolve(val);
36    };
37    const clonedChild = React.cloneElement(child, { [propName]: newPropFunc });
38    setPortalChild(clonedChild);
39  });
40
41export class TimeoutError extends Error {
42  constructor(...args) {
43    super(...args);
44    this.name = 'TimeoutError';
45  }
46}
47
48export const mountAndWaitForWithTimeout = (child, propName = 'ref', setPortalChild, timeout) =>
49  Promise.race([
50    mountAndWaitFor(child, propName, setPortalChild),
51    new Promise((resolve, reject) => {
52      setTimeout(() => {
53        reject(new TimeoutError(`mountAndWaitFor did not resolve after ${timeout} ms.`));
54      }, timeout);
55    }),
56  ]);
57
58export default {
59  waitFor,
60  TimeoutError,
61  retryForStatus,
62  mountAndWaitFor,
63  mountAndWaitForWithTimeout,
64};
65