UNPKG

yhtml5-test

Version:

A test framework for front-end projects

155 lines (141 loc) 4.54 kB
/** * Copyright (c) 2015-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import React from 'react'; import ReactDOM from 'react-dom'; import CompileErrorContainer from './containers/CompileErrorContainer'; import RuntimeErrorContainer from './containers/RuntimeErrorContainer'; import { listenToRuntimeErrors } from './listenToRuntimeErrors'; import { iframeStyle, overlayStyle } from './styles'; import { applyStyles } from './utils/dom/css'; var iframe = null; var isLoadingIframe = false; var renderedElement = null; var currentBuildError = null; var currentRuntimeErrorRecords = []; var currentRuntimeErrorOptions = null; var stopListeningToRuntimeErrors = null; export function reportBuildError(error) { currentBuildError = error; update(); } export function dismissBuildError() { currentBuildError = null; update(); } export function startReportingRuntimeErrors(options) { if (stopListeningToRuntimeErrors !== null) { throw new Error('Already listening'); } currentRuntimeErrorOptions = options; listenToRuntimeErrors(function (errorRecord) { try { if (typeof options.onError === 'function') { options.onError.call(null); } } finally { handleRuntimeError(errorRecord); } }, options.filename); } function handleRuntimeError(errorRecord) { if (currentRuntimeErrorRecords.some(function (_ref) { var error = _ref.error; return error === errorRecord.error; })) { // Deduplicate identical errors. // This fixes https://github.com/facebookincubator/create-react-app/issues/3011. return; } currentRuntimeErrorRecords = currentRuntimeErrorRecords.concat([errorRecord]); update(); } function dismissRuntimeErrors() { currentRuntimeErrorRecords = []; update(); } export function stopReportingRuntimeErrors() { if (stopListeningToRuntimeErrors === null) { throw new Error('Not currently listening'); } currentRuntimeErrorOptions = null; try { stopListeningToRuntimeErrors(); } finally { stopListeningToRuntimeErrors = null; } } function update() { renderedElement = render(); // Loading iframe can be either sync or async depending on the browser. if (isLoadingIframe) { // Iframe is loading. // First render will happen soon--don't need to do anything. return; } if (iframe) { // Iframe has already loaded. // Just update it. updateIframeContent(); return; } // We need to schedule the first render. isLoadingIframe = true; var loadingIframe = window.document.createElement('iframe'); applyStyles(loadingIframe, iframeStyle); loadingIframe.onload = function () { var iframeDocument = loadingIframe.contentDocument; if (iframeDocument != null && iframeDocument.body != null) { iframeDocument.body.style.margin = '0'; // Keep popup within body boundaries for iOS Safari iframeDocument.body.style['max-width'] = '100vw'; var iframeRoot = iframeDocument.createElement('div'); applyStyles(iframeRoot, overlayStyle); iframeDocument.body.appendChild(iframeRoot); // Ready! Now we can update the UI. iframe = loadingIframe; isLoadingIframe = false; updateIframeContent(); } }; var appDocument = window.document; appDocument.body.appendChild(loadingIframe); } function render() { if (currentBuildError) { return React.createElement(CompileErrorContainer, { error: currentBuildError }); } if (currentRuntimeErrorRecords.length > 0) { if (!currentRuntimeErrorOptions) { throw new Error('Expected options to be injected.'); } return React.createElement(RuntimeErrorContainer, { errorRecords: currentRuntimeErrorRecords, close: dismissRuntimeErrors, launchEditorEndpoint: currentRuntimeErrorOptions.launchEditorEndpoint }); } return null; } function updateIframeContent() { if (iframe === null) { throw new Error('Iframe has not been created yet.'); } var iframeBody = iframe.contentDocument.body; if (!iframeBody) { throw new Error('Expected iframe to have a body.'); } var iframeRoot = iframeBody.firstChild; if (renderedElement === null) { // Destroy iframe and force it to be recreated on next error window.document.body.removeChild(iframe); ReactDOM.unmountComponentAtNode(iframeRoot); iframe = null; return; } // Update the overlay ReactDOM.render(renderedElement, iframeRoot); }