From 6d7744646deb500ef388f11f3078bf6e170be848 Mon Sep 17 00:00:00 2001 From: Valeriy S Date: Mon, 10 Dec 2018 09:19:47 +0300 Subject: [PATCH] feat(client): extract common tests execution logic --- .../redux/execute-challenge-saga.js | 156 ++++++++---------- .../src/templates/Challenges/utils/frame.js | 4 +- 2 files changed, 75 insertions(+), 85 deletions(-) diff --git a/client/src/templates/Challenges/redux/execute-challenge-saga.js b/client/src/templates/Challenges/redux/execute-challenge-saga.js index 9b1c023202..d3ab38b39a 100644 --- a/client/src/templates/Challenges/redux/execute-challenge-saga.js +++ b/client/src/templates/Challenges/redux/execute-challenge-saga.js @@ -4,10 +4,9 @@ import { call, takeLatest, takeEvery, - race, fork } from 'redux-saga/effects'; -import { delay, channel } from 'redux-saga'; +import { channel } from 'redux-saga'; import { challengeMetaSelector, @@ -48,18 +47,17 @@ function* ExecuteChallengeSaga() { yield put(initLogs()); yield put(initConsole('// running tests')); - const tests = yield select(challengeTestsSelector); let testResults; switch (challengeType) { case js: case bonfire: - testResults = yield ExecuteJSChallengeSaga(tests); + testResults = yield ExecuteJSChallengeSaga(); break; case backend: // yield ExecuteBackendChallengeSaga(); break; default: - testResults = yield ExecuteDOMChallengeSaga(tests); + testResults = yield ExecuteDOMChallengeSaga(); } yield put(updateTests(testResults)); @@ -70,16 +68,81 @@ function* ExecuteChallengeSaga() { } } -function* ExecuteJSChallengeSaga(tests) { - const testResults = []; +function* ExecuteJSChallengeSaga() { const files = yield select(challengeFilesSelector); const { code, solution } = yield call(buildJSFromFiles, files); + const testResults = yield call(executeTests, { + testRunner: testWorker, + code, + solution + }); + return testResults; +} + +function createTestFrame(state, ctx, proxyLogger) { + return new Promise(resolve => { + const frameTest = createTestFramer(document, state, resolve, proxyLogger); + frameTest(ctx); + }).then(() => console.log('Frame ready')); +} + +function* logToConsole(channel) { + yield takeEvery(channel, function*(args) { + yield put(updateLogs(args)); + }); +} + +function* ExecuteDOMChallengeSaga() { + const state = yield select(); + const ctx = yield call(buildFromFiles, state); + const consoleProxy = yield channel(); + yield fork(logToConsole, consoleProxy); + + yield call(createTestFrame, state, ctx, consoleProxy); + + const testResults = yield call(executeTests, { + testRunner: { + execute({ script }, testTimeout) { + return Promise.race([ + runTestInTestFrame(document, script), + new Promise((_, reject) => + setTimeout(() => reject('timeout'), testTimeout) + ) + ]); + }, + killWorker() {} + } + }); + + consoleProxy.close(); + return testResults; +} + +function* updateMainSaga() { + try { + const { html, modern } = challengeTypes; + const { challengeType } = yield select(challengeMetaSelector); + if (challengeType !== html && challengeType !== modern) { + return; + } + const state = yield select(); + const frameMain = yield call(createMainFramer, document, state); + const ctx = yield call(buildFromFiles, state); + yield call(frameMain, ctx); + } catch (err) { + console.error(err); + } +} + +function* executeTests({ testRunner, code = '', solution = '' }) { + const tests = yield select(challengeTestsSelector); + const testResults = []; for (const { text, testString } of tests) { const newTest = { text, testString }; try { - const { pass, err, logs } = yield call( - testWorker.execute, + const { pass, err, logs = [] } = yield call( + testRunner.execute, { script: solution + '\n' + testString, code }, testTimeout ); @@ -104,85 +167,12 @@ function* ExecuteJSChallengeSaga(tests) { yield put(updateConsole(newTest.message)); } finally { testResults.push(newTest); - yield call(testWorker.killWorker); + yield call(testRunner.killWorker); } } return testResults; } -function createTestFrame(state, ctx, proxyLogger) { - return new Promise(resolve => { - const frameTest = createTestFramer(document, state, resolve, proxyLogger); - frameTest(ctx); - }).then(() => console.log('Frame ready')); -} - -function* logToConsole(channel) { - yield takeEvery(channel, function*(args) { - yield put(updateLogs(args)); - }); -} - -function* ExecuteDOMChallengeSaga(tests) { - const testResults = []; - const state = yield select(); - const ctx = yield call(buildFromFiles, state); - const consoleProxy = yield channel(); - yield fork(logToConsole, consoleProxy); - - yield call(createTestFrame, state, ctx, consoleProxy); - - for (const { text, testString } of tests) { - const newTest = { text, testString }; - try { - const [{ pass, err }, timeout] = yield race([ - call(runTestInTestFrame, document, testString), - delay(testTimeout, 'timeout') - ]); - if (timeout) { - throw timeout; - } - if (pass) { - newTest.pass = true; - } else { - throw err; - } - } catch (err) { - newTest.message = text.replace(/(.*?)<\/code>/g, '$1'); - if (err === 'timeout') { - newTest.err = 'Test timed out'; - newTest.message = `${newTest.message} (${newTest.err})`; - } else { - const { message, stack } = err; - newTest.err = message + '\n' + stack; - newTest.stack = stack; - } - console.error(err); - yield put(updateConsole(newTest.message)); - } finally { - testResults.push(newTest); - } - } - consoleProxy.close(); - return testResults; -} - -function* updateMainSaga() { - try { - const { html, modern } = challengeTypes; - const { challengeType } = yield select(challengeMetaSelector); - if (challengeType !== html && challengeType !== modern) { - return; - } - const state = yield select(); - const frameMain = yield call(createMainFramer, document, state); - const ctx = yield call(buildFromFiles, state); - yield call(frameMain, ctx); - } catch (err) { - console.error(err); - } -} - export function createExecuteChallengeSaga(types) { return [ takeLatest(types.executeChallenge, ExecuteChallengeSaga), diff --git a/client/src/templates/Challenges/utils/frame.js b/client/src/templates/Challenges/utils/frame.js index 80edd0e338..c0aa72d0d7 100644 --- a/client/src/templates/Challenges/utils/frame.js +++ b/client/src/templates/Challenges/utils/frame.js @@ -33,12 +33,12 @@ const createHeader = (id = mainId) => ` `; -export const runTestInTestFrame = async function(document, tests) { +export const runTestInTestFrame = async function(document, test) { const { contentDocument: frame } = document.getElementById(testId); // Enable Stateless Functional Component. Otherwise, enzyme-adapter-react-16 // does not work correctly. setConfig({ pureSFC: true }); - const result = await frame.__runTest(tests); + const result = await frame.__runTest(test); setConfig({ pureSFC: false }); return result; };