feat(client): extract common tests execution logic
This commit is contained in:
@ -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),
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user