Files
freeCodeCamp/client/src/templates/Challenges/redux/execute-challenge-saga.js

154 lines
3.6 KiB
JavaScript
Raw Normal View History

2018-12-10 08:22:32 +03:00
import {
put,
select,
call,
takeLatest,
takeEvery,
2019-01-09 03:36:45 +03:00
fork,
getContext
2018-12-10 08:22:32 +03:00
} from 'redux-saga/effects';
import { delay, channel } from 'redux-saga';
2018-11-26 02:17:38 +03:00
import {
2019-02-08 17:33:05 +03:00
challengeDataSelector,
2018-11-26 02:17:38 +03:00
challengeTestsSelector,
initConsole,
updateConsole,
initLogs,
updateLogs,
logsToConsole,
2019-02-08 17:33:05 +03:00
updateTests,
isBuildEnabledSelector,
disableBuildOnError
2018-11-26 02:17:38 +03:00
} from './';
import {
buildChallenge,
getTestRunner,
challengeHasPreview,
updatePreview
} from '../utils/build';
2018-11-26 02:17:38 +03:00
export function* executeChallengeSaga() {
2019-02-08 17:33:05 +03:00
const isBuildEnabled = yield select(isBuildEnabledSelector);
if (!isBuildEnabled) {
return;
}
2018-12-27 13:34:55 +03:00
const consoleProxy = yield channel();
try {
yield put(initLogs());
yield put(initConsole('// running tests'));
// reset tests to initial state
const tests = (yield select(challengeTestsSelector)).map(
({ text, testString }) => ({ text, testString })
);
yield put(updateTests(tests));
2018-12-27 13:34:55 +03:00
yield fork(logToConsole, consoleProxy);
const proxyLogger = args => consoleProxy.put(args);
const challengeData = yield select(challengeDataSelector);
const buildData = yield buildChallengeData(challengeData);
2019-02-08 17:33:05 +03:00
const document = yield getContext('document');
const testRunner = yield call(
getTestRunner,
buildData,
proxyLogger,
document
);
const testResults = yield executeTests(testRunner, tests);
yield put(updateTests(testResults));
yield put(updateConsole('// tests completed'));
yield put(logsToConsole('// console output'));
} catch (e) {
yield put(updateConsole(e));
2018-12-27 13:34:55 +03:00
} finally {
consoleProxy.close();
2018-11-26 02:17:38 +03:00
}
}
function* logToConsole(channel) {
yield takeEvery(channel, function*(args) {
yield put(updateLogs(args));
});
}
function* buildChallengeData(challengeData) {
2018-12-27 13:34:55 +03:00
try {
2019-02-08 17:33:05 +03:00
return yield call(buildChallenge, challengeData);
} catch (e) {
yield put(disableBuildOnError(e));
// eslint-disable-next-line no-throw-literal
throw 'Build failed';
2018-12-27 13:34:55 +03:00
}
2018-11-26 02:17:38 +03:00
}
function* executeTests(testRunner, tests, testTimeout = 5000) {
const testResults = [];
for (const { text, testString } of tests) {
const newTest = { text, testString };
try {
2018-12-10 17:29:58 +03:00
const { pass, err } = yield call(testRunner, testString, testTimeout);
if (pass) {
newTest.pass = true;
} else {
throw err;
}
} catch (err) {
newTest.message = text;
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;
}
yield put(updateConsole(newTest.message));
} finally {
testResults.push(newTest);
}
}
return testResults;
}
function* previewChallengeSaga() {
yield delay(700);
2019-02-08 17:33:05 +03:00
const isBuildEnabled = yield select(isBuildEnabledSelector);
if (!isBuildEnabled) {
return;
}
const challengeData = yield select(challengeDataSelector);
if (!challengeHasPreview(challengeData)) {
return;
}
2019-02-08 17:33:05 +03:00
2018-12-10 17:29:58 +03:00
try {
2019-02-08 17:33:05 +03:00
yield put(initConsole(''));
const ctx = yield buildChallengeData(challengeData);
2019-01-09 03:36:45 +03:00
const document = yield getContext('document');
yield call(updatePreview, ctx, document);
2018-12-10 17:29:58 +03:00
} catch (err) {
console.error(err);
}
}
2018-11-26 02:17:38 +03:00
export function createExecuteChallengeSaga(types) {
return [
takeLatest(types.executeChallenge, executeChallengeSaga),
takeLatest(
[
types.updateFile,
types.previewMounted,
types.challengeMounted,
types.resetChallenge
],
previewChallengeSaga
)
];
2018-11-26 02:17:38 +03:00
}