"use strict";

var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.renderHook = renderHook;
exports.testHook = testHook;
Object.defineProperty(exports, "act", {
  enumerable: true,
  get: function get() {
    return _reactTestRenderer.act;
  }
});

var _inheritsLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/inheritsLoose"));

var _react = _interopRequireWildcard(require("react"));

var _reactTestRenderer = require("react-test-renderer");

function TestHook(_ref) {
  var callback = _ref.callback,
      hookProps = _ref.hookProps,
      children = _ref.children;
  children(callback(hookProps));
  return null;
}

var ErrorBoundary =
/*#__PURE__*/
function (_React$Component) {
  (0, _inheritsLoose2["default"])(ErrorBoundary, _React$Component);

  function ErrorBoundary(props) {
    var _this;

    _this = _React$Component.call(this, props) || this;
    _this.state = {
      hasError: false
    };
    return _this;
  }

  ErrorBoundary.getDerivedStateFromError = function getDerivedStateFromError() {
    return {
      hasError: true
    };
  };

  var _proto = ErrorBoundary.prototype;

  _proto.componentDidCatch = function componentDidCatch(error) {
    this.props.onError(error);
  };

  _proto.componentDidUpdate = function componentDidUpdate(prevProps) {
    if (this.props != prevProps && this.state.hasError) {
      this.setState({
        hasError: false
      });
    }
  };

  _proto.render = function render() {
    return !this.state.hasError && this.props.children;
  };

  return ErrorBoundary;
}(_react["default"].Component);

function Fallback() {
  return null;
}

function resultContainer() {
  var value = null;
  var error = null;
  var resolvers = [];
  var result = {
    get current() {
      if (error) {
        throw error;
      }

      return value;
    },

    get error() {
      return error;
    }

  };

  var updateResult = function updateResult(val, err) {
    value = val;
    error = err;
    resolvers.splice(0, resolvers.length).forEach(function (resolve) {
      return resolve();
    });
  };

  return {
    result: result,
    addResolver: function addResolver(resolver) {
      resolvers.push(resolver);
    },
    setValue: function setValue(val) {
      return updateResult(val);
    },
    setError: function setError(err) {
      return updateResult(undefined, err);
    }
  };
}

function renderHook(callback, _temp) {
  var _ref2 = _temp === void 0 ? {} : _temp,
      initialProps = _ref2.initialProps,
      wrapper = _ref2.wrapper;

  var _resultContainer = resultContainer(),
      result = _resultContainer.result,
      setValue = _resultContainer.setValue,
      setError = _resultContainer.setError,
      addResolver = _resultContainer.addResolver;

  var hookProps = {
    current: initialProps
  };

  var wrapUiIfNeeded = function wrapUiIfNeeded(innerElement) {
    return wrapper ? _react["default"].createElement(wrapper, null, innerElement) : innerElement;
  };

  var toRender = function toRender() {
    return wrapUiIfNeeded(_react["default"].createElement(ErrorBoundary, {
      onError: setError
    }, _react["default"].createElement(_react.Suspense, {
      fallback: _react["default"].createElement(Fallback, null)
    }, _react["default"].createElement(TestHook, {
      callback: callback,
      hookProps: hookProps.current
    }, setValue))));
  };

  var testRenderer;
  (0, _reactTestRenderer.act)(function () {
    testRenderer = (0, _reactTestRenderer.create)(toRender());
  });
  var _testRenderer = testRenderer,
      _unmount = _testRenderer.unmount,
      update = _testRenderer.update;
  return {
    result: result,
    waitForNextUpdate: function waitForNextUpdate() {
      return new Promise(function (resolve) {
        return addResolver(resolve);
      });
    },
    rerender: function rerender(newProps) {
      if (newProps === void 0) {
        newProps = hookProps.current;
      }

      hookProps.current = newProps;
      (0, _reactTestRenderer.act)(function () {
        update(toRender());
      });
    },
    unmount: function unmount() {
      (0, _reactTestRenderer.act)(function () {
        _unmount();
      });
    }
  };
}

function testHook() {
  console.warn('`testHook` has been deprecated and will be removed in a future release.  Please use `renderHook` instead.');
  return renderHook.apply(void 0, arguments);
}