fix: unify build of challenges
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import chai from 'chai';
|
||||
import '@babel/polyfill';
|
||||
import __toString from 'lodash/toString';
|
||||
|
||||
const oldLog = self.console.log.bind(self.console);
|
||||
self.console.log = function proxyConsole(...args) {
|
||||
@@ -8,17 +9,17 @@ self.console.log = function proxyConsole(...args) {
|
||||
};
|
||||
|
||||
onmessage = async e => {
|
||||
const { script: __test, code } = e.data;
|
||||
/* eslint-disable no-unused-vars */
|
||||
const { code = '' } = e.data;
|
||||
const assert = chai.assert;
|
||||
// Fake Deep Equal dependency
|
||||
const DeepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
||||
/* eslint-enable no-unused-vars */
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
const testResult = eval(__test);
|
||||
const testResult = eval(e.data.script);
|
||||
if (typeof testResult === 'function') {
|
||||
await testResult(() => code);
|
||||
await testResult(fileName => __toString(e.data.sources[fileName]));
|
||||
}
|
||||
self.postMessage({ pass: true });
|
||||
} catch (err) {
|
||||
|
@@ -58,16 +58,11 @@ export const cssToHtml = cond([
|
||||
|
||||
// FileStream::concatHtml(
|
||||
// required: [ ...Object ],
|
||||
// template: String
|
||||
// ) => Observable[{ build: String, sources: Dictionary }]
|
||||
export function concatHtml(required, template, files) {
|
||||
// template: String,
|
||||
// files: [ polyVinyl ]
|
||||
// ) => String
|
||||
export function concatHtml(required, template, files = []) {
|
||||
const createBody = template ? _template(template) : defaultTemplate;
|
||||
const sourceMap = Promise.all(files).then(files =>
|
||||
files.reduce((sources, file) => {
|
||||
sources[file.name] = file.source || file.contents;
|
||||
return sources;
|
||||
}, {})
|
||||
);
|
||||
|
||||
const head = required
|
||||
.map(({ link, src }) => {
|
||||
@@ -84,29 +79,12 @@ A required file can not have both a src and a link: src = ${src}, link = ${link}
|
||||
}
|
||||
return '';
|
||||
})
|
||||
.reduce((head, required) => [...head, required], [])
|
||||
.reduce((head, element, index, thisArray) => {
|
||||
if (index + 1 === thisArray.length) {
|
||||
return `<head>${head.concat(element)}</head>`;
|
||||
}
|
||||
return head.concat(element);
|
||||
}, '');
|
||||
.reduce((head, element) => head.concat(element), '');
|
||||
|
||||
const body = Promise.all(files).then(files =>
|
||||
files
|
||||
.reduce((body, file) => [...body, file.contents + htmlCatch], [])
|
||||
.map(source => createBody({ source }))
|
||||
const source = files.reduce(
|
||||
(source, file) => source.concat(file.contents, htmlCatch),
|
||||
''
|
||||
);
|
||||
|
||||
const frameRunner =
|
||||
'<script src="/js/frame-runner.js" type="text/javascript"></script>';
|
||||
|
||||
return (
|
||||
Promise.all([head, body, frameRunner, sourceMap]).then(
|
||||
([head, body, frameRunner, sourceMap]) => ({
|
||||
build: head + frameRunner + body,
|
||||
sources: sourceMap
|
||||
})
|
||||
)
|
||||
);
|
||||
return `<head>${head}</head>${createBody({ source })}`;
|
||||
}
|
||||
|
@@ -16,13 +16,12 @@ import {
|
||||
initLogs,
|
||||
updateLogs,
|
||||
logsToConsole,
|
||||
updateTests,
|
||||
challengeFilesSelector
|
||||
updateTests
|
||||
} from './';
|
||||
|
||||
import {
|
||||
buildJSFromFiles,
|
||||
buildHtmlFromFiles,
|
||||
buildJSChallenge,
|
||||
buildDOMChallenge,
|
||||
buildBackendChallenge
|
||||
} from '../utils/build';
|
||||
|
||||
@@ -48,17 +47,19 @@ function* ExecuteChallengeSaga() {
|
||||
yield put(initConsole('// running tests'));
|
||||
yield fork(logToConsole, consoleProxy);
|
||||
|
||||
const state = yield select();
|
||||
|
||||
let testResults;
|
||||
switch (challengeType) {
|
||||
case js:
|
||||
case bonfire:
|
||||
testResults = yield ExecuteJSChallengeSaga(consoleProxy);
|
||||
testResults = yield ExecuteJSChallengeSaga(state, consoleProxy);
|
||||
break;
|
||||
case backend:
|
||||
testResults = yield ExecuteBackendChallengeSaga(consoleProxy);
|
||||
testResults = yield ExecuteBackendChallengeSaga(state, consoleProxy);
|
||||
break;
|
||||
default:
|
||||
testResults = yield ExecuteDOMChallengeSaga(consoleProxy);
|
||||
testResults = yield ExecuteDOMChallengeSaga(state, consoleProxy);
|
||||
}
|
||||
|
||||
yield put(updateTests(testResults));
|
||||
@@ -77,9 +78,9 @@ function* logToConsole(channel) {
|
||||
});
|
||||
}
|
||||
|
||||
function* ExecuteJSChallengeSaga(proxyLogger) {
|
||||
const files = yield select(challengeFilesSelector);
|
||||
const { code, solution } = yield call(buildJSFromFiles, files);
|
||||
function* ExecuteJSChallengeSaga(state, proxyLogger) {
|
||||
const { build, sources } = yield call(buildJSChallenge, state);
|
||||
const code = sources && 'index' in sources ? sources['index'] : '';
|
||||
|
||||
const log = args => proxyLogger.put(args);
|
||||
testWorker.on('LOG', log);
|
||||
@@ -87,7 +88,10 @@ function* ExecuteJSChallengeSaga(proxyLogger) {
|
||||
try {
|
||||
return yield call(executeTests, (testString, testTimeout) =>
|
||||
testWorker
|
||||
.execute({ script: solution + '\n' + testString, code }, testTimeout)
|
||||
.execute(
|
||||
{ script: build + '\n' + testString, code, sources },
|
||||
testTimeout
|
||||
)
|
||||
.then(result => {
|
||||
testWorker.killWorker();
|
||||
return result;
|
||||
@@ -105,9 +109,8 @@ function createTestFrame(state, ctx, proxyLogger) {
|
||||
});
|
||||
}
|
||||
|
||||
function* ExecuteDOMChallengeSaga(proxyLogger) {
|
||||
const state = yield select();
|
||||
const ctx = yield call(buildHtmlFromFiles, state);
|
||||
function* ExecuteDOMChallengeSaga(state, proxyLogger) {
|
||||
const ctx = yield call(buildDOMChallenge, state);
|
||||
|
||||
yield call(createTestFrame, state, ctx, proxyLogger);
|
||||
// wait for a code execution on a "ready" event in jQuery challenges
|
||||
@@ -124,8 +127,7 @@ function* ExecuteDOMChallengeSaga(proxyLogger) {
|
||||
}
|
||||
|
||||
// TODO: use a web worker
|
||||
function* ExecuteBackendChallengeSaga(proxyLogger) {
|
||||
const state = yield select();
|
||||
function* ExecuteBackendChallengeSaga(state, proxyLogger) {
|
||||
const ctx = yield call(buildBackendChallenge, state);
|
||||
|
||||
yield call(createTestFrame, state, ctx, proxyLogger);
|
||||
@@ -179,7 +181,7 @@ function* updateMainSaga() {
|
||||
}
|
||||
const state = yield select();
|
||||
const frameMain = yield call(createMainFramer, document, state);
|
||||
const ctx = yield call(buildHtmlFromFiles, state);
|
||||
const ctx = yield call(buildDOMChallenge, state);
|
||||
yield call(frameMain, ctx);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
@@ -11,8 +11,11 @@ import { transformers, testJS$JSX } from '../rechallenge/transformers';
|
||||
import { cssToHtml, jsToHtml, concatHtml } from '../rechallenge/builders.js';
|
||||
import { isPromise } from './polyvinyl';
|
||||
|
||||
const frameRunner =
|
||||
"<script src='/js/frame-runner.js' type='text/javascript'></script>";
|
||||
const frameRunner = [
|
||||
{
|
||||
src: '/js/frame-runner.js'
|
||||
}
|
||||
];
|
||||
|
||||
const globalRequires = [
|
||||
{
|
||||
@@ -63,19 +66,30 @@ const pipeLine = flow(
|
||||
applyFunctions(toHtml)
|
||||
);
|
||||
|
||||
export function buildHtmlFromFiles(state) {
|
||||
const files = challengeFilesSelector(state);
|
||||
const { required = [], template } = challengeMetaSelector(state);
|
||||
const finalRequires = [...globalRequires, ...required];
|
||||
const requiredFiles = Object.keys(files)
|
||||
.map(key => files[key])
|
||||
.filter(filterJSIfDisabled(state))
|
||||
.filter(Boolean);
|
||||
const finalFiles = requiredFiles.map(pipeLine);
|
||||
return concatHtml(finalRequires, template, finalFiles);
|
||||
function buildSourceMap(files) {
|
||||
return files.reduce((sources, file) => {
|
||||
sources[file.name] = file.source || file.contents;
|
||||
return sources;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function buildJSFromFiles(files) {
|
||||
export function buildDOMChallenge(state) {
|
||||
const files = challengeFilesSelector(state);
|
||||
const { required = [], template } = challengeMetaSelector(state);
|
||||
const finalRequires = [...globalRequires, ...required, ...frameRunner];
|
||||
const finalFiles = Object.keys(files)
|
||||
.map(key => files[key])
|
||||
.filter(filterJSIfDisabled(state))
|
||||
.filter(Boolean)
|
||||
.map(pipeLine);
|
||||
return Promise.all(finalFiles).then(files => ({
|
||||
build: concatHtml(finalRequires, template, files),
|
||||
sources: buildSourceMap(files)
|
||||
}));
|
||||
}
|
||||
|
||||
export function buildJSChallenge(state) {
|
||||
const files = challengeFilesSelector(state);
|
||||
const pipeLine = flow(
|
||||
applyFunctions(throwers),
|
||||
applyFunctions(transformers)
|
||||
@@ -83,14 +97,8 @@ export function buildJSFromFiles(files) {
|
||||
const finalFiles = Object.keys(files)
|
||||
.map(key => files[key])
|
||||
.map(pipeLine);
|
||||
const sourceMap = Promise.all(finalFiles).then(files =>
|
||||
files.reduce((sources, file) => {
|
||||
sources[file.name] = file.source || file.contents;
|
||||
return sources;
|
||||
}, {})
|
||||
);
|
||||
const body = Promise.all(finalFiles).then(files =>
|
||||
files
|
||||
return Promise.all(finalFiles).then(files => ({
|
||||
build: files
|
||||
.reduce(
|
||||
(body, file) => [
|
||||
...body,
|
||||
@@ -98,11 +106,8 @@ export function buildJSFromFiles(files) {
|
||||
],
|
||||
[]
|
||||
)
|
||||
.join('/n')
|
||||
);
|
||||
return Promise.all([body, sourceMap]).then(([body, sources]) => ({
|
||||
solution: body,
|
||||
code: sources && 'index' in sources ? sources['index'] : ''
|
||||
.join('/n'),
|
||||
sources: buildSourceMap(files)
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -111,7 +116,7 @@ export function buildBackendChallenge(state) {
|
||||
solution: { value: url }
|
||||
} = backendFormValuesSelector(state);
|
||||
return {
|
||||
build: frameRunner,
|
||||
build: concatHtml(frameRunner, ''),
|
||||
sources: { url }
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user