yhtml5-test
Version:
A test framework for front-end projects
155 lines (141 loc) • 4.54 kB
JavaScript
/**
* 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);
}