'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _reactDom = require('react-dom');

var _reactDom2 = _interopRequireDefault(_reactDom);

var _propTypes = require('prop-types');

var _propTypes2 = _interopRequireDefault(_propTypes);

var _stringifyObject = require('stringify-object');

var _stringifyObject2 = _interopRequireDefault(_stringifyObject);

var _lodash = require('lodash');

var _lodash2 = _interopRequireDefault(_lodash);

var _style = require('./style');

var _style2 = _interopRequireDefault(_style);

var _helpers = require('../../helpers');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* eslint no-use-before-define: 0 */

var TestRunner = function (_Component) {
  _inherits(TestRunner, _Component);

  function TestRunner(props) {
    _classCallCheck(this, TestRunner);

    var _this = _possibleConstructorReturn(this, (TestRunner.__proto__ || Object.getPrototypeOf(TestRunner)).call(this, props));

    _this.getIframeDoc = _this.getIframeDoc.bind(_this);
    _this.runTests = _this.runTests.bind(_this);
    _this.handleStartClick = _this.handleStartClick.bind(_this);
    _this.handleInfiniteLoop = _this.handleInfiniteLoop.bind(_this);
    _this.state = { consoleLogs: [], autoRunEnabled: props.autoRun, hasRun: false };
    return _this;
  }

  _createClass(TestRunner, [{
    key: 'componentWillReceiveProps',
    value: function componentWillReceiveProps(nextProps) {
      var _this2 = this;

      if (this.props.codeToTest !== nextProps.codeToTest && this.state.autoRunEnabled) {
        (0, _helpers.limitEval)(nextProps.codeToTest, function (success) {
          if (success) {
            _this2.setState({ consoleLogs: [] });
            _this2.iframe.onload = _this2.runTests.bind(_this2, nextProps);
            // Need to clear Mocha tests
            _this2.iframe.contentWindow.location.reload();
          } else {
            _this2.handleInfiniteLoop();
          }
        }, nextProps.infiniteLoopTimeout);
      }
    }
  }, {
    key: 'componentWillUnmount',
    value: function componentWillUnmount() {
      _reactDom2.default.unmountComponentAtNode(this.iframe);
    }
  }, {
    key: 'handleInfiniteLoop',
    value: function handleInfiniteLoop() {
      this.props.onError({ message: 'Your Code is taking too long to run. Maybe you wrote an infinite loop.' });
      this.setState({ autoRunEnabled: false });
    }
  }, {
    key: 'toggleTestRunner',
    value: function toggleTestRunner(callback) {
      this.setState(function (_ref) {
        var wasEnabled = _ref.autoRunEnabled;

        return { autoRunEnabled: !wasEnabled, hasRun: true };
      }, callback);
    }
  }, {
    key: 'getIframeDoc',
    value: function getIframeDoc() {
      if (!this.iframe) return;
      var iFrame = this.iframe;
      var iFrameDoc = iFrame.contentDocument;
      if (!iFrameDoc && iFrame.contentWindow) {
        iFrameDoc = iFrame.contentWindow.document;
      }
      return iFrameDoc;
    }
  }, {
    key: 'runTests',
    value: function runTests(_ref2) {
      var _this3 = this;

      var codeToTest = _ref2.codeToTest,
          testSpec = _ref2.testSpec;
      var testHandlerUrl = this.props.testHandlerUrl;
      // Set up test results handler

      this.iframe.contentWindow.testHandler = this.props.onTestResults;
      // Set up console scrapper
      var nativeLog = this.iframe.contentWindow.console.log;
      var nativeErrorLog = this.iframe.contentWindow.console.error;

      this.iframe.contentWindow.console.log = function () {
        for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
          args[_key] = arguments[_key];
        }

        var log = args.reduce(function (log, arg) {
          return log + '\n' + (0, _stringifyObject2.default)(arg);
        }, '');
        _this3.setState({
          consoleLogs: _this3.state.consoleLogs.concat(log)
        }, function () {
          _this3.props.onConsoleLog(_this3.state.consoleLogs);
        });
        nativeLog.apply(_this3.iframe.contentWindow.console, args);
      };

      var doc = this.getIframeDoc();
      // Remove previous <body> elements
      while (doc.body.firstChild) {
        doc.body.removeChild(doc.body.firstChild);
      }
      var scripts = [
      // { type: 'script', content: codeToTest, url: 'https://cdnjs.cloudflare.com/ajax/libs/mocha/3.4.2/mocha.js' },
      // { type: 'script', content: codeToTest, url: 'https://cdnjs.cloudflare.com/ajax/libs/chai/4.0.1/chai.min.js' },
      // { type: 'script', content: 'mocha.setup("bdd")', url: '' },
      { type: 'script', content: codeToTest, url: '' }, { type: 'script', content: testSpec, url: '' },
      // Append script to handle Mocha results
      { type: 'script', url: testHandlerUrl }];
      var mochaDiv = document.createElement('div');
      mochaDiv.id = 'mocha';
      doc.body.appendChild(mochaDiv);
      scripts.forEach(function (el) {
        var domElement = document.createElement(el.type);
        if (el.type === 'script' && el.url.length) {
          domElement.src = el.url;
        } else {
          domElement.innerHTML = el.content;
        }
        doc.body.appendChild(domElement);
      });
    }
  }, {
    key: 'handleStartClick',
    value: function handleStartClick() {
      var _this4 = this;

      this.toggleTestRunner(function () {
        if (_this4.state.autoRunEnabled) {
          (0, _helpers.limitEval)(_this4.props.codeToTest, function (success) {
            if (success) {
              _this4.setState({ consoleLogs: [] });
              _this4.iframe.onload = _this4.runTests.bind(_this4, _this4.props);
              // Need to clear Mocha tests
              _this4.iframe.contentWindow.location.reload();
            } else {
              _this4.handleInfiniteLoop();
            }
          }, _this4.props.infiniteLoopTimeout);
        }
      });
    }
  }, {
    key: 'render',
    value: function render() {
      var _this5 = this;

      var _state = this.state,
          autoRunEnabled = _state.autoRunEnabled,
          hasRun = _state.hasRun;
      var testRunnerContainer = _style2.default.testRunnerContainer,
          testRunnerInstructions = _style2.default.testRunnerInstructions,
          testRunnerOverlay = _style2.default.testRunnerOverlay;

      return _react2.default.createElement(
        'div',
        { className: 'test-runner-container', style: testRunnerContainer },
        _react2.default.createElement(
          'button',
          { onClick: this.handleStartClick, style: testRunnerOverlay },
          autoRunEnabled ? 'Stop' : 'Start'
        ),
        _react2.default.createElement('iframe', {
          title: 'test-runner',
          src: this.props.iFrameUrl,
          ref: function ref(iframe) {
            _this5.iframe = iframe;
          },
          style: _style2.default.iframe,
          frameBorder: '0',
          id: 'iframe'
        }),
        (0, _helpers.renderIf)(!autoRunEnabled && !hasRun)(_react2.default.createElement(
          'div',
          { className: 'text-center', style: testRunnerInstructions },
          'Click "Start" to run the tests.'
        ))
      );
    }
  }]);

  return TestRunner;
}(_react.Component);

TestRunner.propTypes = {
  autoRun: _propTypes2.default.bool.isRequired,
  testSpec: _propTypes2.default.string.isRequired,
  codeToTest: _propTypes2.default.string,
  onTestResults: _propTypes2.default.func,
  iFrameUrl: _propTypes2.default.string.isRequired,
  testHandlerUrl: _propTypes2.default.string.isRequired,
  onError: _propTypes2.default.func,
  infiniteLoopTimeout: _propTypes2.default.number
};

TestRunner.defaultProps = {
  scriptUrl: '',
  codeToTest: '',
  onTestResults: function onTestResults(passCount, failCount, testDuration) {},
  testHandlerUrl: 'get_results.js',
  onError: function onError(err) {
    var message = _lodash2.default.get(err, 'message') ? err.message : 'Unknown';
    console.error('There was an error running your tests: ', message);
  }
};

exports.default = TestRunner;