diff --git a/client/utils/buildChallenges.js b/client/utils/buildChallenges.js index 90ab93665a..a23c9aea1c 100644 --- a/client/utils/buildChallenges.js +++ b/client/utils/buildChallenges.js @@ -5,18 +5,13 @@ const { createChallenge, getChallengesDirForLang } = require('../../curriculum/getChallenges'); -const { dasherize, nameify } = require('../../utils/slugs'); const { locale } = require('../config/env.json'); -const { blockNameify } = require('../../utils/block-nameify'); - -const arrToString = arr => - Array.isArray(arr) ? arr.join('\n') : _.toString(arr); exports.localeChallengesRootDir = getChallengesDirForLang(locale); exports.replaceChallengeNode = locale => { return async function replaceChallengeNode(fullFilePath) { - return prepareChallenge(await createChallenge(fullFilePath, null, locale)); + return await createChallenge(fullFilePath, null, locale); }; }; @@ -32,29 +27,7 @@ exports.buildChallenges = async function buildChallenges() { const builtChallenges = blocks .filter(block => !block.isPrivate) - .map(({ challenges }) => challenges.map(prepareChallenge)) + .map(({ challenges }) => challenges) .reduce((accu, current) => accu.concat(current), []); return builtChallenges; }; - -function prepareChallenge(challenge) { - challenge.name = nameify(challenge.title); - if (challenge.files) { - challenge.files = _.reduce( - challenge.files, - (map, file) => { - map[file.key] = { - ...file, - head: arrToString(file.head), - contents: arrToString(file.contents), - tail: arrToString(file.tail) - }; - return map; - }, - {} - ); - } - challenge.block = dasherize(challenge.block); - challenge.superBlock = blockNameify(challenge.superBlock); - return challenge; -} diff --git a/curriculum/getChallenges.js b/curriculum/getChallenges.js index abeaafce89..d563445834 100644 --- a/curriculum/getChallenges.js +++ b/curriculum/getChallenges.js @@ -1,5 +1,5 @@ const path = require('path'); -const { findIndex } = require('lodash'); +const { findIndex, reduce, toString } = require('lodash'); const readDirP = require('readdirp-walk'); const { parseMarkdown } = require('../tools/challenge-md-parser'); const fs = require('fs'); @@ -11,8 +11,10 @@ const { /* eslint-enable max-len*/ const { COMMENT_TRANSLATIONS } = require('./comment-dictionary'); -const { dasherize } = require('../utils/slugs'); const { isAuditedCert } = require('../utils/is-audited'); +const { dasherize, nameify } = require('../utils/slugs'); +const { createPoly } = require('../utils/polyvinyl'); +const { blockNameify } = require('../utils/block-nameify'); const challengesDir = path.resolve(__dirname, './challenges'); const metaDir = path.resolve(challengesDir, '_meta'); @@ -180,6 +182,43 @@ Trying to parse ${fullPath}`); challenge.template = template; challenge.time = time; + 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, + (map, file) => { + map[file.key] = { + ...file, + head: arrToString(file.head), + contents: arrToString(file.contents), + tail: arrToString(file.tail) + }; + return map; + }, + {} + ); + // TODO: This should be something that can be folded into the above reduce + challenge.files = Object.keys(challenge.files) + .filter(key => challenge.files[key]) + .map(key => challenge.files[key]) + .reduce( + (files, file) => ({ + ...files, + [file.key]: { + ...createPoly(file), + seed: file.contents.slice(0) + } + }), + {} + ); + } + challenge.block = dasherize(challenge.block); + challenge.superBlock = blockNameify(challenge.superBlock); return challenge; } @@ -255,3 +294,7 @@ function getBlockNameFromFullPath(fullFilePath) { const [, block] = fullFilePath.split(path.sep).reverse(); return block; } + +function arrToString(arr) { + return Array.isArray(arr) ? arr.join('\n') : toString(arr); +} diff --git a/curriculum/test/test-challenges.js b/curriculum/test/test-challenges.js index 3f4658301a..f8ab80c7ea 100644 --- a/curriculum/test/test-challenges.js +++ b/curriculum/test/test-challenges.js @@ -42,6 +42,7 @@ const { } = require('../../client/utils/challengeTypes'); const { dasherize } = require('../../utils/slugs'); +const { sortFiles } = require('../../utils/sort-files'); const { testedLang } = require('../utils'); @@ -50,7 +51,6 @@ const { buildJSChallenge } = require('../../client/src/templates/Challenges/utils/build'); -const { createPoly } = require('../../utils/polyvinyl'); const { sortChallenges } = require('./utils/sort-challenges'); const testEvaluator = require('../../client/config/test-evaluator').filename; @@ -319,7 +319,6 @@ function populateTestsForLang({ lang, challenges, meta }) { }); }); - let { files = [] } = challenge; if (challengeType === challengeTypes.backend) { it('Check tests is not implemented.'); return; @@ -334,7 +333,6 @@ function populateTestsForLang({ lang, challenges, meta }) { // TODO: create more sophisticated validation now we allow for more // than one seed/solution file. - files = files.map(createPoly); it('Test suite must fail on the initial contents', async function() { this.timeout(5000 * tests.length + 1000); // suppress errors in the console. @@ -344,7 +342,7 @@ function populateTestsForLang({ lang, challenges, meta }) { let testRunner; try { testRunner = await createTestRunner( - { ...challenge, files }, + challenge, '', buildChallenge ); @@ -390,7 +388,7 @@ function populateTestsForLang({ lang, challenges, meta }) { it(`Solution ${index + 1} must pass the tests`, async function() { this.timeout(5000 * tests.length + 1000); const testRunner = await createTestRunner( - { ...challenge, files }, + challenge, solution, buildChallenge ); @@ -418,14 +416,10 @@ async function createTestRunner( solution, buildChallenge ) { - // 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. + // fallback for single solution + const sortedFiles = sortFiles(files); if (solution) { - files[0].contents = solution; + files[sortedFiles[0].key].contents = solution; } const { build, sources, loadEnzyme } = await buildChallenge({