fix(external curricula): use strings for superblock stages (#60813)

This commit is contained in:
Huyen Nguyen
2025-06-11 14:51:23 +07:00
committed by GitHub
parent b9ee08ac58
commit 66bc604bd7
2 changed files with 53 additions and 18 deletions

View File

@@ -20,7 +20,8 @@ import {
type GeneratedBlockBasedCurriculumProps,
type GeneratedChapterBasedCurriculumProps,
type ChapterBasedCurriculumIntros,
orderedSuperBlockInfo
orderedSuperBlockInfo,
OrderedSuperBlocks
} from './build-external-curricula-data-v2';
const VERSION = 'v2';
@@ -55,16 +56,29 @@ describe('external curriculum data build', () => {
});
test('the available-superblocks file should have the correct structure', async () => {
const filteredSuperBlockStages: string[] = Object.keys(SuperBlockStage)
.filter(key => isNaN(Number(key))) // Filter out numeric keys to get only the names
.filter(name => name !== 'Upcoming' && name !== 'Next') // Filter out 'Upcoming' and 'Next'
.map(name => name.toLowerCase());
const validateAvailableSuperBlocks = availableSuperBlocksValidator();
const availableSuperblocks: unknown = JSON.parse(
const availableSuperblocks = JSON.parse(
await fs.promises.readFile(
`${clientStaticPath}/curriculum-data/${VERSION}/available-superblocks.json`,
'utf-8'
)
);
) as { superblocks: OrderedSuperBlocks };
const result = validateAvailableSuperBlocks(availableSuperblocks);
expect(Object.keys(availableSuperblocks.superblocks)).toHaveLength(
filteredSuperBlockStages.length
);
expect(Object.keys(availableSuperblocks.superblocks)).toEqual(
expect.arrayContaining(filteredSuperBlockStages)
);
if (result.error) {
throw Error(
`file: available-superblocks.json
@@ -263,23 +277,35 @@ ${result.error.message}`);
});
test('All public SuperBlocks should be present in the SuperBlock object', () => {
const stages = Object.keys(orderedSuperBlockInfo).map(
key => Number(key) as SuperBlockStage
);
// Create a mapping from string to shared/config SuperBlockStage enum value
// so we can look up the enum value by string.
const superBlockStageStringMap: Record<string, SuperBlockStage> = {
core: SuperBlockStage.Core,
english: SuperBlockStage.English,
professional: SuperBlockStage.Professional,
extra: SuperBlockStage.Extra,
legacy: SuperBlockStage.Legacy,
upcoming: SuperBlockStage.Upcoming,
next: SuperBlockStage.Next
};
expect(stages).not.toContain(SuperBlockStage.Next);
expect(stages).not.toContain(SuperBlockStage.Upcoming);
const stages = Object.keys(orderedSuperBlockInfo);
expect(stages).not.toContain('next');
expect(stages).not.toContain('upcoming');
for (const stage of stages) {
const superBlockDashedNames = orderedSuperBlockInfo[stage].map(
superBlock => superBlock.dashedName
);
const stageValueInNum = superBlockStageStringMap[stage];
expect(superBlockDashedNames).toEqual(
expect.arrayContaining(superBlockStages[stage])
expect.arrayContaining(superBlockStages[stageValueInNum])
);
expect(superBlockDashedNames).toHaveLength(
superBlockStages[stage].length
superBlockStages[stageValueInNum].length
);
}
});

View File

@@ -2,10 +2,7 @@ import { mkdirSync, writeFileSync, readFileSync } from 'fs';
import { resolve, dirname } from 'path';
import { submitTypes } from '../../../shared/config/challenge-types';
import { type ChallengeNode } from '../../../client/src/redux/prop-types';
import {
SuperBlocks,
SuperBlockStage
} from '../../../shared/config/curriculum';
import { SuperBlocks } from '../../../shared/config/curriculum';
import fullStackSuperBlockStructure from '../../../curriculum/superblock-structure/full-stack.json';
import type { Chapter } from '../../../shared/config/chapters';
@@ -85,6 +82,21 @@ interface GeneratedBlock {
meta: Record<string, unknown>;
}
// This enum is based on the `SuperBlockStage` enum in shared/config,
// but with string value instead of number.
enum SuperBlockStage {
Core = 'core',
English = 'english',
Professional = 'professional',
Extra = 'extra',
Legacy = 'legacy'
}
export type OrderedSuperBlocks = Record<
string,
Array<{ dashedName: SuperBlocks; public: boolean; title: string }>
>;
const ver = 'v2';
const staticFolderPath = resolve(__dirname, '../../../client/static');
@@ -97,10 +109,7 @@ const intros = JSON.parse(
readFileSync(blockIntroPath, 'utf-8')
) as CurriculumIntros;
export const orderedSuperBlockInfo: Record<
string,
Array<{ dashedName: SuperBlocks; public: boolean; title: string }>
> = {
export const orderedSuperBlockInfo: OrderedSuperBlocks = {
[SuperBlockStage.Core]: [
{
dashedName: SuperBlocks.FullStackDeveloper,