fix: unify build of challenges

This commit is contained in:
Valeriy S
2018-12-29 11:34:03 +03:00
committed by Stuart Taylor
parent 2297881f91
commit 2168b61151
4 changed files with 64 additions and 78 deletions

View File

@@ -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) {

View File

@@ -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 })}`;
}

View File

@@ -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);

View File

@@ -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 }
};
}