feat: add block description to mobile curriculum (#45370)
* feat: add block description to mobile curriculum * feat: add superblock names
This commit is contained in:
@ -177,10 +177,15 @@ async function buildBlocks({ basename: blockName }, curriculum, superBlock) {
|
|||||||
__dirname,
|
__dirname,
|
||||||
`./challenges/_meta/${blockName}/meta.json`
|
`./challenges/_meta/${blockName}/meta.json`
|
||||||
);
|
);
|
||||||
let blockMeta;
|
|
||||||
try {
|
if (fs.existsSync(metaPath)) {
|
||||||
blockMeta = require(metaPath);
|
// try to read the file, if the meta path does not exist it should be a certification.
|
||||||
|
// As they do not have meta files.
|
||||||
|
|
||||||
|
const blockMeta = JSON.parse(fs.readFileSync(metaPath));
|
||||||
|
|
||||||
const { isUpcomingChange } = blockMeta;
|
const { isUpcomingChange } = blockMeta;
|
||||||
|
|
||||||
if (typeof isUpcomingChange !== 'boolean') {
|
if (typeof isUpcomingChange !== 'boolean') {
|
||||||
throw Error(
|
throw Error(
|
||||||
`meta file at ${metaPath} is missing 'isUpcomingChange', it must be 'true' or 'false'`
|
`meta file at ${metaPath} is missing 'isUpcomingChange', it must be 'true' or 'false'`
|
||||||
@ -192,10 +197,7 @@ async function buildBlocks({ basename: blockName }, curriculum, superBlock) {
|
|||||||
const blockInfo = { meta: blockMeta, challenges: [] };
|
const blockInfo = { meta: blockMeta, challenges: [] };
|
||||||
curriculum[superBlock].blocks[blockName] = blockInfo;
|
curriculum[superBlock].blocks[blockName] = blockInfo;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} else {
|
||||||
if (e.code !== 'MODULE_NOT_FOUND') {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
curriculum['certifications'].blocks[blockName] = { challenges: [] };
|
curriculum['certifications'].blocks[blockName] = { challenges: [] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,10 @@ const path = require('path');
|
|||||||
|
|
||||||
exports.buildMobileCurriculum = function buildMobileCurriculum(json) {
|
exports.buildMobileCurriculum = function buildMobileCurriculum(json) {
|
||||||
const mobileStaticPath = path.resolve(__dirname, '../../../client/static');
|
const mobileStaticPath = path.resolve(__dirname, '../../../client/static');
|
||||||
|
const blockIntroPath = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../../../client/i18n/locales/english/intro.json'
|
||||||
|
);
|
||||||
|
|
||||||
fs.mkdirSync(`${mobileStaticPath}/mobile`, { recursive: true });
|
fs.mkdirSync(`${mobileStaticPath}/mobile`, { recursive: true });
|
||||||
writeAndParseCurriculumJson(json);
|
writeAndParseCurriculumJson(json);
|
||||||
@ -12,7 +16,14 @@ exports.buildMobileCurriculum = function buildMobileCurriculum(json) {
|
|||||||
key => key !== 'certifications'
|
key => key !== 'certifications'
|
||||||
);
|
);
|
||||||
|
|
||||||
writeToFile('availableSuperblocks', { superblocks: superBlockKeys });
|
writeToFile('availableSuperblocks', {
|
||||||
|
// removing "/" as it will create an extra sub-path when accessed via an endpoint
|
||||||
|
|
||||||
|
superblocks: [
|
||||||
|
superBlockKeys.map(key => key.replace(/\//, '-')),
|
||||||
|
getSuperBlockNames(superBlockKeys)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
for (let i = 0; i < superBlockKeys.length; i++) {
|
for (let i = 0; i < superBlockKeys.length; i++) {
|
||||||
const superBlock = {};
|
const superBlock = {};
|
||||||
@ -25,16 +36,32 @@ exports.buildMobileCurriculum = function buildMobileCurriculum(json) {
|
|||||||
|
|
||||||
for (let j = 0; j < blockNames.length; j++) {
|
for (let j = 0; j < blockNames.length; j++) {
|
||||||
superBlock[superBlockKeys[i]]['blocks'][blockNames[j]] = {};
|
superBlock[superBlockKeys[i]]['blocks'][blockNames[j]] = {};
|
||||||
|
|
||||||
|
superBlock[superBlockKeys[i]]['blocks'][blockNames[j]]['desc'] =
|
||||||
|
getBlockDescription(superBlockKeys[i], blockNames[j]);
|
||||||
|
|
||||||
superBlock[superBlockKeys[i]]['blocks'][blockNames[j]]['challenges'] =
|
superBlock[superBlockKeys[i]]['blocks'][blockNames[j]]['challenges'] =
|
||||||
curriculum[superBlockKeys[i]]['blocks'][blockNames[j]]['meta'];
|
curriculum[superBlockKeys[i]]['blocks'][blockNames[j]]['meta'];
|
||||||
}
|
}
|
||||||
|
|
||||||
writeToFile(superBlockKeys[i], superBlock);
|
writeToFile(superBlockKeys[i].replace(/\//, '-'), superBlock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeToFile(filename, json) {
|
function getBlockDescription(superBlockKey, blockKey) {
|
||||||
const fullPath = `${mobileStaticPath}/mobile/${filename}.json`;
|
const intros = JSON.parse(fs.readFileSync(blockIntroPath));
|
||||||
|
|
||||||
|
return intros[superBlockKey]['blocks'][blockKey]['intro'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSuperBlockNames(superBlockKeys) {
|
||||||
|
const superBlocks = JSON.parse(fs.readFileSync(blockIntroPath));
|
||||||
|
|
||||||
|
return superBlockKeys.map(key => superBlocks[key].title);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeToFile(fileName, json) {
|
||||||
|
const fullPath = `${mobileStaticPath}/mobile/${fileName}.json`;
|
||||||
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
||||||
fs.writeFileSync(fullPath, JSON.stringify(json, null, 2));
|
fs.writeFileSync(fullPath, JSON.stringify(json, null, 2));
|
||||||
}
|
}
|
||||||
|
53
tools/scripts/build/mobile-curriculum.test.js
Normal file
53
tools/scripts/build/mobile-curriculum.test.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const { AssertionError } = require('chai');
|
||||||
|
const { getChallengesForLang } = require('../../../curriculum/getChallenges');
|
||||||
|
const { buildMobileCurriculum } = require('./build-mobile-curriculum');
|
||||||
|
const { mobileSchemaValidator } = require('./mobileSchema');
|
||||||
|
|
||||||
|
describe('mobile curriculum build', () => {
|
||||||
|
const mobileStaticPath = path.resolve(__dirname, '../../../client/static');
|
||||||
|
const blockIntroPath = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../../../client/i18n/locales/english/intro.json'
|
||||||
|
);
|
||||||
|
|
||||||
|
const validateMobileSuperBlock = mobileSchemaValidator();
|
||||||
|
|
||||||
|
let curriculum;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
curriculum = await getChallengesForLang('english');
|
||||||
|
await buildMobileCurriculum(curriculum);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
test('the mobile curriculum should have a static folder with multiple files', () => {
|
||||||
|
expect(fs.existsSync(`${mobileStaticPath}/mobile`)).toBe(true);
|
||||||
|
|
||||||
|
expect(fs.readdirSync(`${mobileStaticPath}/mobile`).length).toBeGreaterThan(
|
||||||
|
0
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the mobile curriculum should have access to the intro.json file', () => {
|
||||||
|
expect(fs.existsSync(blockIntroPath)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the files generated should have the correct schema', () => {
|
||||||
|
const fileArray = fs.readdirSync(`${mobileStaticPath}/mobile`);
|
||||||
|
|
||||||
|
fileArray
|
||||||
|
.filter(fileInArray => fileInArray !== 'availableSuperblocks.json')
|
||||||
|
.forEach(fileInArray => {
|
||||||
|
const fileContent = fs.readFileSync(
|
||||||
|
`${mobileStaticPath}/mobile/${fileInArray}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = validateMobileSuperBlock(JSON.parse(fileContent));
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
throw new AssertionError(result.error, `file: ${fileInArray}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
32
tools/scripts/build/mobileSchema.js
Normal file
32
tools/scripts/build/mobileSchema.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
const Joi = require('joi');
|
||||||
|
|
||||||
|
const blockSchema = Joi.object({}).keys({
|
||||||
|
desc: Joi.array().min(1),
|
||||||
|
challenges: Joi.object({}).keys({
|
||||||
|
name: Joi.string(),
|
||||||
|
isUpcomingChange: Joi.bool(),
|
||||||
|
usesMultifileEditor: Joi.bool().optional(),
|
||||||
|
hasEditableBoundaries: Joi.bool().optional(),
|
||||||
|
isBeta: Joi.bool().optional(),
|
||||||
|
dashedName: Joi.string(),
|
||||||
|
order: Joi.number(),
|
||||||
|
time: Joi.string().allow(''),
|
||||||
|
template: Joi.string().allow(''),
|
||||||
|
required: Joi.array(),
|
||||||
|
superBlock: Joi.string(),
|
||||||
|
challengeOrder: Joi.array().items(Joi.array().min(1))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const subSchema = Joi.object({}).keys({
|
||||||
|
blocks: Joi.object({}).pattern(Joi.string(), Joi.object().concat(blockSchema))
|
||||||
|
});
|
||||||
|
|
||||||
|
const schema = Joi.object({}).pattern(
|
||||||
|
Joi.string(),
|
||||||
|
Joi.object().concat(subSchema)
|
||||||
|
);
|
||||||
|
|
||||||
|
exports.mobileSchemaValidator = () => {
|
||||||
|
return superblock => schema.validate(superblock);
|
||||||
|
};
|
Reference in New Issue
Block a user