feat(client): extract common tests execution logic

This commit is contained in:
Valeriy S
2018-12-10 09:19:47 +03:00
committed by Stuart Taylor
parent 0775766702
commit 6d7744646d
2 changed files with 75 additions and 85 deletions

View File

@ -4,10 +4,9 @@ import {
call, call,
takeLatest, takeLatest,
takeEvery, takeEvery,
race,
fork fork
} from 'redux-saga/effects'; } from 'redux-saga/effects';
import { delay, channel } from 'redux-saga'; import { channel } from 'redux-saga';
import { import {
challengeMetaSelector, challengeMetaSelector,
@ -48,18 +47,17 @@ function* ExecuteChallengeSaga() {
yield put(initLogs()); yield put(initLogs());
yield put(initConsole('// running tests')); yield put(initConsole('// running tests'));
const tests = yield select(challengeTestsSelector);
let testResults; let testResults;
switch (challengeType) { switch (challengeType) {
case js: case js:
case bonfire: case bonfire:
testResults = yield ExecuteJSChallengeSaga(tests); testResults = yield ExecuteJSChallengeSaga();
break; break;
case backend: case backend:
// yield ExecuteBackendChallengeSaga(); // yield ExecuteBackendChallengeSaga();
break; break;
default: default:
testResults = yield ExecuteDOMChallengeSaga(tests); testResults = yield ExecuteDOMChallengeSaga();
} }
yield put(updateTests(testResults)); yield put(updateTests(testResults));
@ -70,16 +68,81 @@ function* ExecuteChallengeSaga() {
} }
} }
function* ExecuteJSChallengeSaga(tests) { function* ExecuteJSChallengeSaga() {
const testResults = [];
const files = yield select(challengeFilesSelector); const files = yield select(challengeFilesSelector);
const { code, solution } = yield call(buildJSFromFiles, files); 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) { for (const { text, testString } of tests) {
const newTest = { text, testString }; const newTest = { text, testString };
try { try {
const { pass, err, logs } = yield call( const { pass, err, logs = [] } = yield call(
testWorker.execute, testRunner.execute,
{ script: solution + '\n' + testString, code }, { script: solution + '\n' + testString, code },
testTimeout testTimeout
); );
@ -104,85 +167,12 @@ function* ExecuteJSChallengeSaga(tests) {
yield put(updateConsole(newTest.message)); yield put(updateConsole(newTest.message));
} finally { } finally {
testResults.push(newTest); testResults.push(newTest);
yield call(testWorker.killWorker); yield call(testRunner.killWorker);
} }
} }
return testResults; 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>(.*?)<\/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) { export function createExecuteChallengeSaga(types) {
return [ return [
takeLatest(types.executeChallenge, ExecuteChallengeSaga), takeLatest(types.executeChallenge, ExecuteChallengeSaga),

View File

@ -33,12 +33,12 @@ const createHeader = (id = mainId) => `
</script> </script>
`; `;
export const runTestInTestFrame = async function(document, tests) { export const runTestInTestFrame = async function(document, test) {
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(tests); const result = await frame.__runTest(test);
setConfig({ pureSFC: false }); setConfig({ pureSFC: false });
return result; return result;
}; };