Files
wehub-resource-sync dde272c4b8
i18n - Build Validation / Validate i18n Builds (24) (push) Has been cancelled
CI - Node.js / Lint (24) (push) Has been cancelled
CI - Node.js / Build (24) (push) Has been cancelled
CI - Node.js / Test (24) (push) Has been cancelled
CI - Node.js / Test - Upcoming Changes (24) (push) Has been cancelled
CI - Node.js / Test - i18n (italian, 24) (push) Has been cancelled
CI - Node.js / Test - i18n (portuguese, 24) (push) Has been cancelled
CD - Docker - GHCR Images / Build and Push Images (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 11:55:53 +08:00

121 lines
3.5 KiB
TypeScript

import { dirname } from 'path';
import { fileURLToPath } from 'url';
import { describe, test, expect } from 'vitest';
import {
availableLangs,
LangNames,
LangCodes
} from '@freecodecamp/shared/config/i18n';
import {
SuperBlocks,
superBlockStages,
SuperBlockStage
} from '@freecodecamp/shared/config/curriculum';
import { getCurriculum } from '../tools/get-curriculum';
import intro from './locales/english/intro.json';
interface Intro {
[key: string]: {
title: string;
summary?: string[];
intro: string[];
blocks: {
[block: string]: {
title: string;
intro: string[];
};
};
};
}
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
describe('Locale tests:', () => {
availableLangs.client.forEach(lang => {
describe(`-- ${lang} --`, () => {
test(`has an entry in the langDisplayNames enum`, () => {
expect(
Object.keys(LangNames)
.map(langCode => langCode.toLowerCase())
.includes(lang)
).toBe(true);
});
test(`has an entry in the langCodes enum`, () => {
expect(
Object.keys(LangCodes)
.map(langCode => langCode.toLowerCase())
.includes(lang)
).toBe(true);
});
});
});
});
describe('Intro file structure tests:', () => {
const typedIntro = intro as unknown as Intro;
const superblocks = Object.values(SuperBlocks);
const catalogSuperBlocks = superBlockStages[SuperBlockStage.Catalog];
for (const superBlock of superblocks) {
test(`superBlock ${superBlock} has required properties`, () => {
expect(typeof typedIntro[superBlock].title).toBe('string');
// catalog superblocks should have a summary
expect(
!catalogSuperBlocks.includes(superBlock) ||
Array.isArray(typedIntro[superBlock].summary)
).toBe(true);
expect(typedIntro[superBlock].intro).toBeInstanceOf(Array);
expect(typedIntro[superBlock].blocks).toBeInstanceOf(Object);
const blocks = Object.keys(typedIntro[superBlock].blocks);
blocks.forEach(block => {
expect(typeof typedIntro[superBlock].blocks[block].title).toBe(
'string'
);
expect(typedIntro[superBlock].blocks[block].intro).toBeInstanceOf(
Array
);
});
});
}
});
type SuperBlockInfo = {
blocks: Record<string, unknown>;
};
describe('Curriculum validation', () => {
const curriculum = getCurriculum() as Record<string, SuperBlockInfo>;
// certifications are not superblocks, they're just mixed in with them.
const superblocks = Object.entries(curriculum).filter(
([key]) => key !== 'certifications'
);
// It's important that we check that each block in the curriculum has a title
// in the intro, rather than the other way around, because the intro must
// include upcoming changes. The curriculum only does if SHOW_UPCOMING_CHANGES
// is true.
superblocks.forEach(superblock => {
const [name, superBlockInfo] = superblock;
const blockObject = superBlockInfo.blocks;
describe(`${name}`, () => {
test('should have titles for each block in intro.json', () => {
const blocks = Object.keys(blockObject);
blocks.forEach(block => {
const blockFromIntro = (intro as unknown as Intro)[superblock[0]]
.blocks[block];
expect(
blockFromIntro.title,
`block ${block} needs a non-empty title`
).toBeTruthy();
});
});
});
});
});