diff --git a/client/src/templates/Challenges/redux/execute-challenge-saga.js b/client/src/templates/Challenges/redux/execute-challenge-saga.js index 8b8136adc0..d68a77ca3b 100644 --- a/client/src/templates/Challenges/redux/execute-challenge-saga.js +++ b/client/src/templates/Challenges/redux/execute-challenge-saga.js @@ -1,4 +1,5 @@ import { + all, delay, put, select, @@ -124,20 +125,30 @@ function* previewChallengeSaga() { if (!isBuildEnabled) { return; } - const challengeData = yield select(challengeDataSelector); + + const consoleProxy = yield channel(); try { - yield put(initConsole('')); + yield put(initLogs()); + yield fork(logToConsole, consoleProxy); + const proxyLogger = args => consoleProxy.put(args); + const challengeData = yield select(challengeDataSelector); + // try to build even if there's no preview so build errors will be reported. - const ctx = yield buildChallengeData(challengeData); + const buildData = yield buildChallengeData(challengeData); // then only continue if there is a preview. if (!challengeHasPreview(challengeData)) { return; } const document = yield getContext('document'); - yield call(updatePreview, ctx, document); + yield call(updatePreview, buildData, document, proxyLogger); + // We don't want to see the default console, so we initialise and output in + // one call. + yield all([put(initConsole('')), put(logsToConsole('// console output'))]); } catch (err) { console.error(err); + } finally { + consoleProxy.close(); } } diff --git a/client/src/templates/Challenges/utils/build.js b/client/src/templates/Challenges/utils/build.js index a33f03823e..07d5689df4 100644 --- a/client/src/templates/Challenges/utils/build.js +++ b/client/src/templates/Challenges/utils/build.js @@ -159,10 +159,13 @@ export function buildBackendChallenge({ url }) { }; } -export function updatePreview(buildData, document) { +export async function updatePreview(buildData, document, proxyLogger) { const { challengeType } = buildData; + if (challengeType === challengeTypes.html) { - createMainFramer(document)(buildData); + await new Promise(resolve => + createMainFramer(document, resolve, proxyLogger)(buildData) + ); } else { throw new Error(`Cannot show preview for challenge type ${challengeType}`); } diff --git a/client/src/templates/Challenges/utils/frame.js b/client/src/templates/Challenges/utils/frame.js index fe052b4bf7..63349e78d8 100644 --- a/client/src/templates/Challenges/utils/frame.js +++ b/client/src/templates/Challenges/utils/frame.js @@ -91,13 +91,7 @@ const buildProxyConsole = proxyLogger => ctx => { }; const initTestFrame = frameReady => ctx => { - const contentLoaded = new Promise(resolve => { - if (ctx.document.readyState === 'loading') { - ctx.document.addEventListener('DOMContentLoaded', resolve); - } else { - resolve(); - } - }); + const contentLoaded = new Promise(resolve => waitForFrame(resolve)(ctx)); contentLoaded.then(async () => { const { sources, loadEnzyme } = ctx; // default for classic challenges @@ -111,6 +105,15 @@ const initTestFrame = frameReady => ctx => { return ctx; }; +const waitForFrame = frameReady => ctx => { + if (ctx.document.readyState === 'loading') { + ctx.document.addEventListener('DOMContentLoaded', frameReady); + } else { + frameReady(); + } + return ctx; +}; + function writeToFrame(content, frame) { frame.open(); frame.write(content); @@ -123,18 +126,17 @@ const writeContentToFrame = ctx => { return ctx; }; -export const createMainFramer = document => - flow( - createFrame(document, mainId), - mountFrame(document), - writeContentToFrame - ); +export const createMainFramer = (document, frameReady, proxyConsole) => + createFramer(document, frameReady, proxyConsole, mainId, waitForFrame); export const createTestFramer = (document, frameReady, proxyConsole) => + createFramer(document, frameReady, proxyConsole, testId, initTestFrame); + +const createFramer = (document, frameReady, proxyConsole, id, init) => flow( - createFrame(document, testId), + createFrame(document, id), mountFrame(document), writeContentToFrame, buildProxyConsole(proxyConsole), - initTestFrame(frameReady) + init(frameReady) );