mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
refactor(external curricula): simplify get intro logic (#59707)
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs, { readFileSync } from 'fs';
|
||||||
|
|
||||||
import readdirp from 'readdirp';
|
import readdirp from 'readdirp';
|
||||||
|
|
||||||
@@ -8,9 +8,20 @@ import {
|
|||||||
superblockSchemaValidator,
|
superblockSchemaValidator,
|
||||||
availableSuperBlocksValidator
|
availableSuperBlocksValidator
|
||||||
} from './external-data-schema';
|
} from './external-data-schema';
|
||||||
import { orderedSuperBlockInfo } from './build-external-curricula-data';
|
import {
|
||||||
|
type Curriculum,
|
||||||
|
type CurriculumIntros,
|
||||||
|
type GeneratedCurriculumProps,
|
||||||
|
orderedSuperBlockInfo
|
||||||
|
} from './build-external-curricula-data';
|
||||||
|
|
||||||
const VERSION = 'v1';
|
const VERSION = 'v1';
|
||||||
|
const intros = JSON.parse(
|
||||||
|
readFileSync(
|
||||||
|
path.resolve(__dirname, '../../../client/i18n/locales/english/intro.json'),
|
||||||
|
'utf-8'
|
||||||
|
)
|
||||||
|
) as CurriculumIntros;
|
||||||
|
|
||||||
describe('external curriculum data build', () => {
|
describe('external curriculum data build', () => {
|
||||||
const clientStaticPath = path.resolve(__dirname, '../../../client/static');
|
const clientStaticPath = path.resolve(__dirname, '../../../client/static');
|
||||||
@@ -52,28 +63,69 @@ ${result.error.message}`
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the files generated should have the correct schema', async () => {
|
test('the super block files generated should have the correct schema', async () => {
|
||||||
const fileArray = (
|
const fileArray = (
|
||||||
await readdirp.promise(`${clientStaticPath}/curriculum-data/${VERSION}`, {
|
await readdirp.promise(`${clientStaticPath}/curriculum-data/${VERSION}`, {
|
||||||
directoryFilter: ['!challenges']
|
directoryFilter: ['!challenges'],
|
||||||
|
fileFilter: entry => {
|
||||||
|
// The directory contains super block files and other curriculum-related files.
|
||||||
|
// We're only interested in super block ones.
|
||||||
|
const superBlocks = Object.values(SuperBlocks);
|
||||||
|
return superBlocks.includes(entry.basename);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
).map(file => file.path);
|
).map(file => file.path);
|
||||||
|
|
||||||
fileArray
|
fileArray.forEach(fileInArray => {
|
||||||
.filter(fileInArray => fileInArray !== 'available-superblocks.json')
|
const fileContent = fs.readFileSync(
|
||||||
.forEach(fileInArray => {
|
`${clientStaticPath}/curriculum-data/${VERSION}/${fileInArray}`,
|
||||||
const fileContent = fs.readFileSync(
|
'utf-8'
|
||||||
`${clientStaticPath}/curriculum-data/${VERSION}/${fileInArray}`,
|
);
|
||||||
'utf-8'
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = validateSuperBlock(JSON.parse(fileContent));
|
const result = validateSuperBlock(JSON.parse(fileContent));
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
throw Error(`file: ${fileInArray}
|
throw Error(`file: ${fileInArray}
|
||||||
${result.error.message}`);
|
${result.error.message}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('super blocks and blocks should have the correct data', async () => {
|
||||||
|
const superBlockFiles = (
|
||||||
|
await readdirp.promise(`${clientStaticPath}/curriculum-data/${VERSION}`, {
|
||||||
|
directoryFilter: ['!challenges'],
|
||||||
|
fileFilter: entry => {
|
||||||
|
// The directory contains super block files and other curriculum-related files.
|
||||||
|
// We're only interested in super block ones.
|
||||||
|
const superBlocks = Object.values(SuperBlocks);
|
||||||
|
return superBlocks.includes(entry.basename);
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
).map(file => file.path);
|
||||||
|
|
||||||
|
superBlockFiles.forEach(file => {
|
||||||
|
const fileContentJson = fs.readFileSync(
|
||||||
|
`${clientStaticPath}/curriculum-data/${VERSION}/${file}`,
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
|
||||||
|
const fileContent = JSON.parse(
|
||||||
|
fileContentJson
|
||||||
|
) as Curriculum<GeneratedCurriculumProps>;
|
||||||
|
|
||||||
|
const superBlock = Object.keys(fileContent)[0] as SuperBlocks;
|
||||||
|
|
||||||
|
// Randomly pick a block to check its data.
|
||||||
|
const blocks = Object.keys(fileContent[superBlock].blocks);
|
||||||
|
const randomBlockIndex = Math.floor(Math.random() * blocks.length);
|
||||||
|
const randomBlock = blocks[randomBlockIndex];
|
||||||
|
|
||||||
|
expect(fileContent[superBlock].intro).toEqual(intros[superBlock].intro);
|
||||||
|
expect(fileContent[superBlock].blocks[randomBlock].desc).toEqual(
|
||||||
|
intros[superBlock].blocks[randomBlock].intro
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('All public SuperBlocks should be present in the SuperBlock object', () => {
|
test('All public SuperBlocks should be present in the SuperBlock object', () => {
|
||||||
|
|||||||
@@ -4,24 +4,26 @@ import { submitTypes } from '../../../shared/config/challenge-types';
|
|||||||
import { type ChallengeNode } from '../../../client/src/redux/prop-types';
|
import { type ChallengeNode } from '../../../client/src/redux/prop-types';
|
||||||
import { SuperBlocks } from '../../../shared/config/curriculum';
|
import { SuperBlocks } from '../../../shared/config/curriculum';
|
||||||
|
|
||||||
type Intro = { [keyValue in SuperBlocks]: IntroProps };
|
export type CurriculumIntros = {
|
||||||
|
[keyValue in SuperBlocks]: {
|
||||||
|
title: string;
|
||||||
|
intro: string[];
|
||||||
|
blocks: Record<string, { title: string; intro: string[] }>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export type Curriculum<T> = {
|
export type Curriculum<T> = {
|
||||||
[keyValue in SuperBlocks]: T extends CurriculumProps
|
[keyValue in SuperBlocks]: T extends CurriculumProps
|
||||||
? CurriculumProps
|
? CurriculumProps
|
||||||
: GeneratedCurriculumProps;
|
: GeneratedCurriculumProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IntroProps extends CurriculumProps {
|
|
||||||
title: string;
|
|
||||||
intro: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CurriculumProps {
|
export interface CurriculumProps {
|
||||||
intro: string[];
|
intro: string[];
|
||||||
blocks: Record<string, Block<ChallengeNode['challenge'][]>>;
|
blocks: Record<string, Block<ChallengeNode['challenge'][]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GeneratedCurriculumProps {
|
export interface GeneratedCurriculumProps {
|
||||||
intro: string[];
|
intro: string[];
|
||||||
blocks: Record<string, Block<Record<string, unknown>>>;
|
blocks: Record<string, Block<Record<string, unknown>>>;
|
||||||
}
|
}
|
||||||
@@ -71,6 +73,9 @@ export function buildExtCurriculumData(
|
|||||||
__dirname,
|
__dirname,
|
||||||
'../../../client/i18n/locales/english/intro.json'
|
'../../../client/i18n/locales/english/intro.json'
|
||||||
);
|
);
|
||||||
|
const intros = JSON.parse(
|
||||||
|
readFileSync(blockIntroPath, 'utf-8')
|
||||||
|
) as CurriculumIntros;
|
||||||
|
|
||||||
mkdirSync(dataPath, { recursive: true });
|
mkdirSync(dataPath, { recursive: true });
|
||||||
|
|
||||||
@@ -85,7 +90,7 @@ export function buildExtCurriculumData(
|
|||||||
writeToFile('available-superblocks', {
|
writeToFile('available-superblocks', {
|
||||||
superblocks: orderedSuperBlockInfo.map(x => ({
|
superblocks: orderedSuperBlockInfo.map(x => ({
|
||||||
...x,
|
...x,
|
||||||
title: getSuperBlockTitle(x.dashedName)
|
title: intros[x.dashedName].title
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -96,7 +101,7 @@ export function buildExtCurriculumData(
|
|||||||
if (blockNames.length === 0) continue;
|
if (blockNames.length === 0) continue;
|
||||||
|
|
||||||
superBlock[superBlockKey] = <GeneratedCurriculumProps>{};
|
superBlock[superBlockKey] = <GeneratedCurriculumProps>{};
|
||||||
superBlock[superBlockKey].intro = getSuperBlockDescription(superBlockKey);
|
superBlock[superBlockKey].intro = intros[superBlockKey]['intro'];
|
||||||
superBlock[superBlockKey].blocks = {};
|
superBlock[superBlockKey].blocks = {};
|
||||||
|
|
||||||
for (const blockName of blockNames) {
|
for (const blockName of blockNames) {
|
||||||
@@ -105,7 +110,7 @@ export function buildExtCurriculumData(
|
|||||||
>{};
|
>{};
|
||||||
|
|
||||||
superBlock[superBlockKey]['blocks'][blockName]['desc'] =
|
superBlock[superBlockKey]['blocks'][blockName]['desc'] =
|
||||||
getBlockDescription(superBlockKey, blockName);
|
intros[superBlockKey]['blocks'][blockName]['intro'];
|
||||||
|
|
||||||
superBlock[superBlockKey]['blocks'][blockName]['challenges'] =
|
superBlock[superBlockKey]['blocks'][blockName]['challenges'] =
|
||||||
curriculum[superBlockKey]['blocks'][blockName]['meta'];
|
curriculum[superBlockKey]['blocks'][blockName]['meta'];
|
||||||
@@ -131,30 +136,6 @@ export function buildExtCurriculumData(
|
|||||||
writeFileSync(filePath, JSON.stringify(data, null, 2));
|
writeFileSync(filePath, JSON.stringify(data, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBlockDescription(
|
|
||||||
superBlockKeys: SuperBlocks,
|
|
||||||
blockKey: string
|
|
||||||
): string[] {
|
|
||||||
const intros = JSON.parse(readFileSync(blockIntroPath, 'utf-8')) as Intro;
|
|
||||||
|
|
||||||
return intros[superBlockKeys]['blocks'][blockKey]['intro'];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSuperBlockDescription(superBlockKey: SuperBlocks): string[] {
|
|
||||||
const superBlockIntro = JSON.parse(
|
|
||||||
readFileSync(blockIntroPath, 'utf-8')
|
|
||||||
) as Intro;
|
|
||||||
return superBlockIntro[superBlockKey]['intro'];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSuperBlockTitle(superBlock: SuperBlocks): string {
|
|
||||||
const superBlocks = JSON.parse(
|
|
||||||
readFileSync(blockIntroPath, 'utf-8')
|
|
||||||
) as Intro;
|
|
||||||
|
|
||||||
return superBlocks[superBlock].title;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSubmitTypes() {
|
function getSubmitTypes() {
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
`${dataPath}/submit-types.json`,
|
`${dataPath}/submit-types.json`,
|
||||||
|
|||||||
Reference in New Issue
Block a user