feat: allow next challenge's seed to be a solution (#39145)

* feat: allow next challenge's seed to be a solution
This commit is contained in:
Oliver Eyton-Williams 2020-06-30 06:33:28 +02:00 committed by Mrugesh Mohapatra
parent 4f1be63055
commit fd7a8c0d5e
3 changed files with 272 additions and 3 deletions

View File

@ -23,7 +23,7 @@ const {
const { assert, AssertionError } = require('chai');
const Mocha = require('mocha');
const { flatten } = require('lodash');
const { flatten, isEmpty } = require('lodash');
const jsdom = require('jsdom');
@ -51,6 +51,7 @@ const {
} = 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;
@ -226,7 +227,9 @@ async function getChallenges(lang) {
return [...challengeArray, ...flatten(challengesForBlock)];
}, [])
);
return challenges;
// This matches the order Gatsby uses (via a GraphQL query). Ideally both
// should be sourced and sorted using a single query, but we're not there yet.
return sortChallenges(challenges);
}
function validateBlock(challenge) {
@ -245,7 +248,7 @@ function populateTestsForLang({ lang, challenges, meta }) {
describe(`Check challenges (${lang})`, function() {
this.timeout(5000);
challenges.forEach(challenge => {
challenges.forEach((challenge, id) => {
const dashedBlockName = dasherize(challenge.block);
describe(challenge.block || 'No block', function() {
describe(challenge.title || 'No title', function() {
@ -367,6 +370,15 @@ function populateTestsForLang({ lang, challenges, meta }) {
});
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];
}
}
const noSolution = new RegExp('// solution required');
solutions = solutions.filter(
solution => !!solution && !noSolution.test(solution)

View File

@ -0,0 +1,5 @@
const { sortBy } = require('lodash');
exports.sortChallenges = arr => {
return sortBy(arr, ['superOrder', 'order', 'challengeOrder']);
};

View File

@ -0,0 +1,252 @@
/* global expect */
const { sortChallenges } = require('./sort-challenges');
const challenges = [
{
name: 'HTML - project 1 - step 1',
superOrder: 1,
order: 1,
challengeOrder: 1
},
{
name: 'HTML - project 1 - step 2',
superOrder: 1,
order: 1,
challengeOrder: 2
},
{
name: 'HTML - project 1 - step 3',
superOrder: 1,
order: 1,
challengeOrder: 3
},
{
name: 'HTML - project 1 - step 4',
superOrder: 1,
order: 1,
challengeOrder: 4
},
{
name: 'HTML - project 2 - step 1',
superOrder: 1,
order: 2,
challengeOrder: 1
},
{
name: 'HTML - project 2 - step 2',
superOrder: 1,
order: 2,
challengeOrder: 2
},
{
name: 'HTML - project 2 - step 3',
superOrder: 1,
order: 2,
challengeOrder: 3
},
{
name: 'HTML - project 2 - step 4',
superOrder: 1,
order: 2,
challengeOrder: 4
},
{
name: 'HTML - project 3 - step 1',
superOrder: 1,
order: 3,
challengeOrder: 1
},
{
name: 'HTML - project 3 - step 2',
superOrder: 1,
order: 3,
challengeOrder: 2
},
{
name: 'HTML - project 3 - step 3',
superOrder: 1,
order: 3,
challengeOrder: 3
},
{
name: 'HTML - project 3 - step 4',
superOrder: 1,
order: 3,
challengeOrder: 4
},
{
name: 'CSS - project 1 - step 1',
superOrder: 2,
order: 1,
challengeOrder: 1
},
{
name: 'CSS - project 1 - step 2',
superOrder: 2,
order: 1,
challengeOrder: 2
},
{
name: 'CSS - project 1 - step 3',
superOrder: 2,
order: 1,
challengeOrder: 3
},
{
name: 'CSS - project 1 - step 4',
superOrder: 2,
order: 1,
challengeOrder: 4
},
{
name: 'CSS - project 2 - step 1',
superOrder: 2,
order: 2,
challengeOrder: 1
},
{
name: 'CSS - project 2 - step 2',
superOrder: 2,
order: 2,
challengeOrder: 2
},
{
name: 'CSS - project 2 - step 3',
superOrder: 2,
order: 2,
challengeOrder: 3
},
{
name: 'CSS - project 2 - step 4',
superOrder: 2,
order: 2,
challengeOrder: 4
},
{
name: 'CSS - project 3 - step 1',
superOrder: 2,
order: 3,
challengeOrder: 1
},
{
name: 'CSS - project 3 - step 2',
superOrder: 2,
order: 3,
challengeOrder: 2
},
{
name: 'CSS - project 3 - step 3',
superOrder: 2,
order: 3,
challengeOrder: 3
},
{
name: 'CSS - project 3 - step 4',
superOrder: 2,
order: 3,
challengeOrder: 4
},
{
name: 'JS - project 1 - step 1',
superOrder: 3,
order: 1,
challengeOrder: 1
},
{
name: 'JS - project 1 - step 2',
superOrder: 3,
order: 1,
challengeOrder: 2
},
{
name: 'JS - project 1 - step 3',
superOrder: 3,
order: 1,
challengeOrder: 3
},
{
name: 'JS - project 1 - step 4',
superOrder: 3,
order: 1,
challengeOrder: 4
},
{
name: 'JS - project 2 - step 1',
superOrder: 3,
order: 2,
challengeOrder: 1
},
{
name: 'JS - project 2 - step 2',
superOrder: 3,
order: 2,
challengeOrder: 2
},
{
name: 'JS - project 2 - step 3',
superOrder: 3,
order: 2,
challengeOrder: 3
},
{
name: 'JS - project 2 - step 4',
superOrder: 3,
order: 2,
challengeOrder: 4
},
{
name: 'JS - project 3 - step 1',
superOrder: 3,
order: 3,
challengeOrder: 1
},
{
name: 'JS - project 3 - step 2',
superOrder: 3,
order: 3,
challengeOrder: 2
},
{
name: 'JS - project 3 - step 3',
superOrder: 3,
order: 3,
challengeOrder: 3
},
{
name: 'JS - project 3 - step 4',
superOrder: 3,
order: 3,
challengeOrder: 4
}
];
describe('sortChallenges', () => {
it('sorts challenges by superblock, block and challenge order', () => {
const copyOfChallenges = [...challenges];
shuffle(copyOfChallenges);
const actualChallenges = sortChallenges(copyOfChallenges);
expect(actualChallenges).toEqual(challenges);
});
it('does not change the original array', () => {
const copyOfChallenges = [...challenges];
copyOfChallenges[0] = {
name: 'JS - project 3 - step 4',
superOrder: 3,
order: 3,
challengeOrder: 4
};
const actualChallenges = sortChallenges(copyOfChallenges);
expect(actualChallenges[0]).not.toEqual(copyOfChallenges[0]);
});
});
// Use the FisherYates shuffle algorithm to shuffle array
const shuffle = arr => {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
};