feat: handle multi-file solutions
This commit is contained in:
committed by
Mrugesh Mohapatra
parent
54630cbfca
commit
301212e194
@ -185,12 +185,10 @@ Trying to parse ${fullPath}`);
|
||||
return prepareChallenge(challenge);
|
||||
}
|
||||
|
||||
// gets the challenge ready for sourcing into Gatsby
|
||||
function prepareChallenge(challenge) {
|
||||
challenge.name = nameify(challenge.title);
|
||||
if (challenge.files) {
|
||||
challenge.files = reduce(
|
||||
challenge.files,
|
||||
// TODO: tests and more descriptive name.
|
||||
function filesToObject(files) {
|
||||
return reduce(
|
||||
files,
|
||||
(map, file) => {
|
||||
map[file.key] = {
|
||||
...file,
|
||||
@ -202,7 +200,15 @@ function prepareChallenge(challenge) {
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
// gets the challenge ready for sourcing into Gatsby
|
||||
function prepareChallenge(challenge) {
|
||||
challenge.name = nameify(challenge.title);
|
||||
if (challenge.files) {
|
||||
challenge.files = filesToObject(challenge.files);
|
||||
// TODO: This should be something that can be folded into the above reduce
|
||||
// EDIT: maybe not, now that we're doing the same for solutionFiles.
|
||||
challenge.files = Object.keys(challenge.files)
|
||||
.filter(key => challenge.files[key])
|
||||
.map(key => challenge.files[key])
|
||||
@ -217,6 +223,10 @@ function prepareChallenge(challenge) {
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
if (challenge.solutionFiles) {
|
||||
challenge.solutionFiles = filesToObject(challenge.solutionFiles);
|
||||
}
|
||||
challenge.block = dasherize(challenge.block);
|
||||
challenge.superBlock = blockNameify(challenge.superBlock);
|
||||
return challenge;
|
||||
|
@ -73,7 +73,12 @@ function getSchemaForLang(lang) {
|
||||
})
|
||||
),
|
||||
solutions: Joi.array().items(Joi.string().optional()),
|
||||
solutionFiles: Joi.array().items(fileJoi),
|
||||
solutionFiles: Joi.object().keys({
|
||||
indexcss: fileJoi,
|
||||
indexhtml: fileJoi,
|
||||
indexjs: fileJoi,
|
||||
indexjsx: fileJoi
|
||||
}),
|
||||
superBlock: Joi.string(),
|
||||
superOrder: Joi.number(),
|
||||
suborder: Joi.number(),
|
||||
|
@ -23,7 +23,7 @@ const {
|
||||
|
||||
const { assert, AssertionError } = require('chai');
|
||||
const Mocha = require('mocha');
|
||||
const { flatten, isEmpty } = require('lodash');
|
||||
const { flatten, isEmpty, cloneDeep } = require('lodash');
|
||||
|
||||
const jsdom = require('jsdom');
|
||||
|
||||
@ -330,9 +330,6 @@ function populateTestsForLang({ lang, challenges, meta }) {
|
||||
? buildJSChallenge
|
||||
: buildDOMChallenge;
|
||||
|
||||
// TODO: create more sophisticated validation now we allow for more
|
||||
// than one seed/solution file.
|
||||
|
||||
it('Test suite must fail on the initial contents', async function() {
|
||||
this.timeout(5000 * tests.length + 1000);
|
||||
// suppress errors in the console.
|
||||
@ -363,22 +360,31 @@ function populateTestsForLang({ lang, challenges, meta }) {
|
||||
assert(fails, 'Test suit does not fail on the initial contents');
|
||||
});
|
||||
|
||||
let { solutions = [] } = challenge;
|
||||
// if there are no solutions in the challenge, it's assumed the next
|
||||
// challenge's seed will be a solution to the current challenge.
|
||||
// This is expected to happen in the project based curriculum.
|
||||
if (isEmpty(solutions)) {
|
||||
const nextChallenge = challenges[id + 1];
|
||||
if (nextChallenge) {
|
||||
solutions = [nextChallenge.files[0].contents];
|
||||
}
|
||||
}
|
||||
let { solutions = [], solutionFiles = {} } = challenge;
|
||||
|
||||
const noSolution = new RegExp('// solution required');
|
||||
solutions = solutions.filter(
|
||||
solution => !!solution && !noSolution.test(solution)
|
||||
);
|
||||
|
||||
if (solutions.length === 0) {
|
||||
// if there are no solutions in the challenge, it's assumed the next
|
||||
// challenge's seed will be a solution to the current challenge.
|
||||
// This is expected to happen in the project based curriculum.
|
||||
|
||||
if (isEmpty(solutions)) {
|
||||
if (!isEmpty(solutionFiles)) {
|
||||
solutions = [solutionFiles];
|
||||
// TODO: there needs to be a way of telling that a challenge uses
|
||||
// multiple files when it doesn't have anything in solutionFiles!
|
||||
} else {
|
||||
const nextChallenge = challenges[id + 1];
|
||||
if (nextChallenge) {
|
||||
solutions = [nextChallenge.files[0].contents];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (solutions.length === 0 && isEmpty(solutionFiles)) {
|
||||
it('Check tests. No solutions');
|
||||
return;
|
||||
}
|
||||
@ -404,22 +410,24 @@ function populateTestsForLang({ lang, challenges, meta }) {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: solutions will need to be multi-file, too, with a fallback when there
|
||||
// is only one file.
|
||||
// we cannot simply use the solution instead of files, because the are not
|
||||
// just the seed(s), they contain the head and tail code. The best approach
|
||||
// is probably to separate out the head and tail from the files. Then the
|
||||
// files can be entirely replaced by the solution.
|
||||
async function createTestRunner(challenge, solution, buildChallenge) {
|
||||
const { required = [], template } = challenge;
|
||||
// we should avoid modifying challenge, as it gets reused:
|
||||
const files = cloneDeep(challenge.files);
|
||||
|
||||
async function createTestRunner(
|
||||
{ required = [], template, files },
|
||||
solution,
|
||||
buildChallenge
|
||||
) {
|
||||
// TODO: there must be a better way of handling both single and multi-file
|
||||
// solutions
|
||||
if (typeof solution === 'object' && !isEmpty(solution)) {
|
||||
Object.keys(solution).forEach(key => {
|
||||
files[key].contents = solution[key].contents;
|
||||
});
|
||||
} else if (solution) {
|
||||
// fallback for single solution
|
||||
const sortedFiles = sortFiles(files);
|
||||
if (solution) {
|
||||
|
||||
files[sortedFiles[0].key].contents = solution;
|
||||
} else {
|
||||
throw Error('Tried to create test runner without a solution.');
|
||||
}
|
||||
|
||||
const { build, sources, loadEnzyme } = await buildChallenge({
|
||||
|
Reference in New Issue
Block a user