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';
|
2018-12-11 07:33:05 +03:00
|
|
|
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 './';
|
|
|
|
|
2019-02-13 15:47:00 +03:00
|
|
|
import {
|
|
|
|
buildChallenge,
|
|
|
|
getTestRunner,
|
|
|
|
challengeHasPreview,
|
|
|
|
updatePreview
|
|
|
|
} from '../utils/build';
|
2018-11-26 02:17:38 +03:00
|
|
|
|
2019-01-09 03:35:31 +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();
|
2019-05-26 17:00:12 -04:00
|
|
|
|
2018-12-05 12:37:48 +03:00
|
|
|
try {
|
|
|
|
yield put(initLogs());
|
|
|
|
yield put(initConsole('// running tests'));
|
2019-05-26 17:00:12 -04:00
|
|
|
// 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);
|
2019-01-09 03:35:31 +03:00
|
|
|
const proxyLogger = args => consoleProxy.put(args);
|
2018-12-05 12:37:48 +03:00
|
|
|
|
2019-02-13 15:47:00 +03:00
|
|
|
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
|
|
|
|
);
|
2019-05-26 17:00:12 -04:00
|
|
|
const testResults = yield executeTests(testRunner, tests);
|
2018-12-05 12:37:48 +03:00
|
|
|
|
|
|
|
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
|
|
|
}
|
2018-12-04 17:23:15 +03:00
|
|
|
}
|
|
|
|
|
2018-12-10 15:06:39 +03:00
|
|
|
function* logToConsole(channel) {
|
|
|
|
yield takeEvery(channel, function*(args) {
|
|
|
|
yield put(updateLogs(args));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-02-13 15:47:00 +03:00
|
|
|
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));
|
2019-02-13 15:47:00 +03:00
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2019-05-26 17:00:12 -04:00
|
|
|
function* executeTests(testRunner, tests, testTimeout = 5000) {
|
2018-12-10 09:19:47 +03:00
|
|
|
const testResults = [];
|
2018-12-10 01:46:26 +03:00
|
|
|
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);
|
2018-12-10 01:46:26 +03:00
|
|
|
if (pass) {
|
|
|
|
newTest.pass = true;
|
|
|
|
} else {
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
} catch (err) {
|
2019-06-09 01:58:10 +02:00
|
|
|
newTest.message = text;
|
2018-12-10 01:46:26 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-02-13 15:47:00 +03:00
|
|
|
function* previewChallengeSaga() {
|
|
|
|
yield delay(700);
|
|
|
|
|
2019-02-08 17:33:05 +03:00
|
|
|
const isBuildEnabled = yield select(isBuildEnabledSelector);
|
|
|
|
if (!isBuildEnabled) {
|
|
|
|
return;
|
|
|
|
}
|
2019-02-13 15:47:00 +03:00
|
|
|
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(''));
|
2019-02-13 15:47:00 +03:00
|
|
|
const ctx = yield buildChallengeData(challengeData);
|
2019-01-09 03:36:45 +03:00
|
|
|
const document = yield getContext('document');
|
2019-02-13 15:47:00 +03:00
|
|
|
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) {
|
2018-12-10 01:46:26 +03:00
|
|
|
return [
|
2019-01-09 03:35:31 +03:00
|
|
|
takeLatest(types.executeChallenge, executeChallengeSaga),
|
2018-12-10 01:46:26 +03:00
|
|
|
takeLatest(
|
|
|
|
[
|
|
|
|
types.updateFile,
|
|
|
|
types.previewMounted,
|
|
|
|
types.challengeMounted,
|
|
|
|
types.resetChallenge
|
|
|
|
],
|
2019-02-13 15:47:00 +03:00
|
|
|
previewChallengeSaga
|
2018-12-10 01:46:26 +03:00
|
|
|
)
|
|
|
|
];
|
2018-11-26 02:17:38 +03:00
|
|
|
}
|