fix: remove state dependency from frame creation

This commit is contained in:
Valeriy
2019-01-09 03:35:31 +03:00
committed by Stuart Taylor
parent 66f14c8329
commit b59c7cfed0
2 changed files with 45 additions and 61 deletions

View File

@ -34,10 +34,7 @@ import {
runTestInTestFrame runTestInTestFrame
} from '../utils/frame.js'; } from '../utils/frame.js';
const testWorker = createWorker('test-evaluator'); export function* executeChallengeSaga() {
const testTimeout = 5000;
function* ExecuteChallengeSaga() {
const consoleProxy = yield channel(); const consoleProxy = yield channel();
try { try {
const { js, bonfire, backend } = challengeTypes; const { js, bonfire, backend } = challengeTypes;
@ -46,6 +43,7 @@ function* ExecuteChallengeSaga() {
yield put(initLogs()); yield put(initLogs());
yield put(initConsole('// running tests')); yield put(initConsole('// running tests'));
yield fork(logToConsole, consoleProxy); yield fork(logToConsole, consoleProxy);
const proxyLogger = args => consoleProxy.put(args);
const state = yield select(); const state = yield select();
@ -53,13 +51,13 @@ function* ExecuteChallengeSaga() {
switch (challengeType) { switch (challengeType) {
case js: case js:
case bonfire: case bonfire:
testResults = yield ExecuteJSChallengeSaga(state, consoleProxy); testResults = yield executeJSChallengeSaga(state, proxyLogger);
break; break;
case backend: case backend:
testResults = yield ExecuteBackendChallengeSaga(state, consoleProxy); testResults = yield executeBackendChallengeSaga(state, proxyLogger);
break; break;
default: default:
testResults = yield ExecuteDOMChallengeSaga(state, consoleProxy); testResults = yield executeDOMChallengeSaga(state, proxyLogger);
} }
yield put(updateTests(testResults)); yield put(updateTests(testResults));
@ -78,72 +76,59 @@ function* logToConsole(channel) {
}); });
} }
function* ExecuteJSChallengeSaga(state, proxyLogger) { function* executeJSChallengeSaga(state, proxyLogger) {
const { build, sources } = yield call(buildJSChallenge, state); const { build, sources } = yield call(buildJSChallenge, state);
const code = sources && 'index' in sources ? sources['index'] : ''; const code = sources && 'index' in sources ? sources['index'] : '';
const log = args => proxyLogger.put(args); const testWorker = createWorker('test-evaluator');
testWorker.on('LOG', log); testWorker.on('LOG', proxyLogger);
try { try {
return yield call(executeTests, (testString, testTimeout) => return yield call(executeTests, async(testString, testTimeout) => {
testWorker try {
.execute( return await testWorker.execute(
{ script: build + '\n' + testString, code, sources }, { script: build + '\n' + testString, code, sources },
testTimeout testTimeout
) );
.then(result => { } finally {
testWorker.killWorker(); testWorker.killWorker();
return result; }
}) });
);
} finally { } finally {
testWorker.remove('LOG', log); testWorker.remove('LOG', proxyLogger);
} }
} }
function createTestFrame(state, ctx, proxyLogger) { function createTestFrame(ctx, proxyLogger) {
return new Promise(resolve => { return new Promise(resolve =>
const frameTest = createTestFramer(document, state, resolve, proxyLogger); createTestFramer(document, resolve, proxyLogger)(ctx)
frameTest(ctx); );
});
} }
function* ExecuteDOMChallengeSaga(state, proxyLogger) { function* executeDOMChallengeSaga(state, proxyLogger) {
const ctx = yield call(buildDOMChallenge, state); const ctx = yield call(buildDOMChallenge, state);
yield call(createTestFrame, ctx, proxyLogger);
yield call(createTestFrame, state, ctx, proxyLogger);
// wait for a code execution on a "ready" event in jQuery challenges // wait for a code execution on a "ready" event in jQuery challenges
yield delay(100); yield delay(100);
return yield call(executeTests, (testString, testTimeout) => return yield call(executeTests, (testString, testTimeout) =>
Promise.race([ runTestInTestFrame(document, testString, testTimeout)
runTestInTestFrame(document, testString),
new Promise((_, reject) =>
setTimeout(() => reject('timeout'), testTimeout)
)
])
); );
} }
// TODO: use a web worker // TODO: use a web worker
function* ExecuteBackendChallengeSaga(state, proxyLogger) { function* executeBackendChallengeSaga(state, proxyLogger) {
const ctx = yield call(buildBackendChallenge, state); const ctx = yield call(buildBackendChallenge, state);
yield call(createTestFrame, ctx, proxyLogger);
yield call(createTestFrame, state, ctx, proxyLogger);
return yield call(executeTests, (testString, testTimeout) => return yield call(executeTests, (testString, testTimeout) =>
Promise.race([ runTestInTestFrame(document, testString, testTimeout)
runTestInTestFrame(document, testString),
new Promise((_, reject) =>
setTimeout(() => reject('timeout'), testTimeout)
)
])
); );
} }
function* executeTests(testRunner) { function* executeTests(testRunner) {
const tests = yield select(challengeTestsSelector); const tests = yield select(challengeTestsSelector);
const testTimeout = 5000;
const testResults = []; const testResults = [];
for (const { text, testString } of tests) { for (const { text, testString } of tests) {
const newTest = { text, testString }; const newTest = { text, testString };
@ -180,8 +165,8 @@ function* updateMainSaga() {
return; return;
} }
const state = yield select(); const state = yield select();
const frameMain = yield call(createMainFramer, document, state);
const ctx = yield call(buildDOMChallenge, state); const ctx = yield call(buildDOMChallenge, state);
const frameMain = yield call(createMainFramer, document);
yield call(frameMain, ctx); yield call(frameMain, ctx);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
@ -190,7 +175,7 @@ function* updateMainSaga() {
export function createExecuteChallengeSaga(types) { export function createExecuteChallengeSaga(types) {
return [ return [
takeLatest(types.executeChallenge, ExecuteChallengeSaga), takeLatest(types.executeChallenge, executeChallengeSaga),
takeLatest( takeLatest(
[ [
types.updateFile, types.updateFile,

View File

@ -3,8 +3,6 @@ import { configure, shallow, mount } from 'enzyme';
import Adapter16 from 'enzyme-adapter-react-16'; import Adapter16 from 'enzyme-adapter-react-16';
import { setConfig } from 'react-hot-loader'; import { setConfig } from 'react-hot-loader';
import { isJSEnabledSelector } from '../redux';
// we use two different frames to make them all essentially pure functions // we use two different frames to make them all essentially pure functions
// main iframe is responsible rendering the preview and is where we proxy the // main iframe is responsible rendering the preview and is where we proxy the
// console.log // console.log
@ -33,23 +31,24 @@ const createHeader = (id = mainId) => `
</script> </script>
`; `;
export const runTestInTestFrame = async function(document, test) { export const runTestInTestFrame = async function(document, test, timeout) {
const { contentDocument: frame } = document.getElementById(testId); const { contentDocument: frame } = document.getElementById(testId);
// Enable Stateless Functional Component. Otherwise, enzyme-adapter-react-16 // Enable Stateless Functional Component. Otherwise, enzyme-adapter-react-16
// does not work correctly. // does not work correctly.
setConfig({ pureSFC: true }); setConfig({ pureSFC: true });
const result = await frame.__runTest(test); try {
setConfig({ pureSFC: false }); return await Promise.race([
return result; new Promise((_, reject) => setTimeout(() => reject('timeout'), timeout)),
frame.__runTest(test)
]);
} finally {
setConfig({ pureSFC: false });
}
}; };
const createFrame = (document, state, id) => ctx => { const createFrame = (document, id) => ctx => {
const isJSEnabled = isJSEnabledSelector(state);
const frame = document.createElement('iframe'); const frame = document.createElement('iframe');
frame.id = id; frame.id = id;
if (!isJSEnabled) {
frame.sandbox = 'allow-same-origin';
}
return { return {
...ctx, ...ctx,
element: frame element: frame
@ -77,7 +76,7 @@ const mountFrame = document => ({ element, ...rest }) => {
const buildProxyConsole = proxyLogger => ctx => { const buildProxyConsole = proxyLogger => ctx => {
const oldLog = ctx.window.console.log.bind(ctx.window.console); const oldLog = ctx.window.console.log.bind(ctx.window.console);
ctx.window.console.log = function proxyConsole(...args) { ctx.window.console.log = function proxyConsole(...args) {
proxyLogger.put(args); proxyLogger(args);
return oldLog(...args); return oldLog(...args);
}; };
return ctx; return ctx;
@ -111,16 +110,16 @@ const writeContentToFrame = ctx => {
return ctx; return ctx;
}; };
export const createMainFramer = (document, state) => export const createMainFramer = document =>
flow( flow(
createFrame(document, state, mainId), createFrame(document, mainId),
mountFrame(document), mountFrame(document),
writeContentToFrame writeContentToFrame
); );
export const createTestFramer = (document, state, frameReady, proxyConsole) => export const createTestFramer = (document, frameReady, proxyConsole) =>
flow( flow(
createFrame(document, state, testId), createFrame(document, testId),
mountFrame(document), mountFrame(document),
writeTestDepsToDocument(frameReady), writeTestDepsToDocument(frameReady),
buildProxyConsole(proxyConsole), buildProxyConsole(proxyConsole),