Files
freeCodeCamp/curriculum/utils.test.ts
2025-09-19 23:10:06 +05:30

338 lines
9.9 KiB
TypeScript

import path from 'path';
import { config } from 'dotenv';
import { describe, it, expect } from 'vitest';
import { SuperBlocks } from '../shared-dist/config/curriculum';
import {
closestFilters,
closestMatch,
createSuperOrder,
filterByBlock,
filterByChallengeId,
filterBySuperblock,
getSuperOrder
} from './utils';
config({ path: path.resolve(__dirname, '../.env') });
const mockSuperBlocks = [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.FoundationalCSharp,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler,
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.TheOdinProject,
SuperBlocks.FullStackDeveloper
];
const fullSuperOrder = {
[SuperBlocks.RespWebDesignNew]: 0,
[SuperBlocks.JsAlgoDataStructNew]: 1,
[SuperBlocks.FrontEndDevLibs]: 2,
[SuperBlocks.DataVis]: 3,
[SuperBlocks.RelationalDb]: 4,
[SuperBlocks.BackEndDevApis]: 5,
[SuperBlocks.QualityAssurance]: 6,
[SuperBlocks.SciCompPy]: 7,
[SuperBlocks.DataAnalysisPy]: 8,
[SuperBlocks.InfoSec]: 9,
[SuperBlocks.MachineLearningPy]: 10,
[SuperBlocks.CollegeAlgebraPy]: 11,
[SuperBlocks.FoundationalCSharp]: 12,
[SuperBlocks.CodingInterviewPrep]: 13,
[SuperBlocks.ProjectEuler]: 14,
[SuperBlocks.RespWebDesign]: 15,
[SuperBlocks.JsAlgoDataStruct]: 16,
[SuperBlocks.TheOdinProject]: 17,
[SuperBlocks.FullStackDeveloper]: 18
};
describe('createSuperOrder', () => {
const superOrder = createSuperOrder(mockSuperBlocks);
it('should create the correct object given an array of SuperBlocks', () => {
expect(superOrder).toStrictEqual(fullSuperOrder);
});
it('throws when not given an array of SuperBlocks', () => {
expect(() => createSuperOrder()).toThrow();
expect(() => createSuperOrder(null)).toThrow();
expect(() => createSuperOrder('')).toThrow();
});
});
describe('getSuperOrder', () => {
it('returns a number for valid curriculum', () => {
expect(typeof getSuperOrder('responsive-web-design')).toBe('number');
});
it('returns undefined for unknown curriculum', () => {
expect(getSuperOrder()).toBeUndefined();
expect(getSuperOrder(null)).toBeUndefined();
expect(getSuperOrder('')).toBeUndefined();
expect(getSuperOrder('respansive-wib-desoin')).toBeUndefined();
expect(getSuperOrder('certifications')).toBeUndefined();
});
it('returns numbers for all current curriculum', () => {
const superBlocks = Object.values(SuperBlocks);
const superOrderValues = superBlocks.map(sb => getSuperOrder(sb, true));
const definedValues = superOrderValues.filter(v => typeof v === 'number');
expect(definedValues.length).toBe(superBlocks.length);
});
it('returns unique numbers for all current curriculum', () => {
const superBlocks = Object.values(SuperBlocks);
const superOrderValues = superBlocks.map(sb => getSuperOrder(sb, true));
const uniqueValues = Array.from(new Set(superOrderValues));
expect(uniqueValues.length).toBe(superBlocks.length);
});
});
describe('filter utils', () => {
describe('filterByChallengeId', () => {
it('returns the same superblocks if no challengeId is provided', () => {
const superblocks = [
{
name: 'superblock-1',
blocks: [{ dashedName: 'block-1', challengeOrder: [{ id: '1' }] }]
},
{
name: 'superblock-2',
blocks: [{ dashedName: 'block-2', challengeOrder: [{ id: '2' }] }]
}
];
expect(filterByChallengeId(superblocks)).toEqual(superblocks);
});
it('ignores blocks without the specified challengeId', () => {
const superblocks = [
{
name: 'superblock-1',
blocks: [
{ dashedName: 'block-1', challengeOrder: [{ id: '1' }] },
{ dashedName: 'block-2', challengeOrder: [{ id: '2' }] }
]
}
];
const filtered = filterByChallengeId(superblocks, { challengeId: '2' });
expect(filtered).toEqual([
{
name: 'superblock-1',
blocks: [{ dashedName: 'block-2', challengeOrder: [{ id: '2' }] }]
}
]);
});
it('returns only the specified challenge and its solution challenge', () => {
const superblocks = [
{
name: 'superblock-1',
blocks: [
{
dashedName: 'block-1',
challengeOrder: [{ id: '1' }, { id: '2' }, { id: '3' }]
},
{ dashedName: 'block-2', challengeOrder: [{ id: '4' }] }
]
}
];
const filtered = filterByChallengeId(superblocks, { challengeId: '1' });
expect(filtered).toEqual([
{
name: 'superblock-1',
blocks: [
{
dashedName: 'block-1',
challengeOrder: [{ id: '1' }, { id: '2' }]
}
]
}
]);
});
it('returns only superblocks containing the specified challenge', () => {
const superblocks = [
{
name: 'superblock-1',
blocks: [
{ dashedName: 'block-1', challengeOrder: [{ id: '1' }] },
{ dashedName: 'block-2', challengeOrder: [{ id: '2' }] }
]
},
{
name: 'superblock-2',
blocks: [{ dashedName: 'block-3', challengeOrder: [{ id: '3' }] }]
}
];
const filtered = filterByChallengeId(superblocks, { challengeId: '2' });
expect(filtered).toEqual([
{
name: 'superblock-1',
blocks: [{ dashedName: 'block-2', challengeOrder: [{ id: '2' }] }]
}
]);
});
});
describe('filterByBlock', () => {
it('returns the same superblocks if no block is provided', () => {
const superblocks = [
{
name: 'superblock-1',
blocks: [{ dashedName: 'block-1' }, { dashedName: 'block-2' }]
}
];
expect(filterByBlock(superblocks)).toEqual(superblocks);
});
it('returns only the specified block', () => {
const superblocks = [
{
name: 'superblock-1',
blocks: [{ dashedName: 'block-1' }, { dashedName: 'block-2' }]
}
];
const filtered = filterByBlock(superblocks, { block: 'block-1' });
expect(filtered).toEqual([
{
name: 'superblock-1',
blocks: [{ dashedName: 'block-1' }]
}
]);
});
it('returns an empty array if no blocks match the specified block', () => {
const superblocks = [
{
name: 'superblock-1',
blocks: [{ dashedName: 'block-1' }, { dashedName: 'block-2' }]
}
];
const filtered = filterByBlock(superblocks, {
block: 'nonexistent-block'
});
expect(filtered).toEqual([]);
});
});
describe('filterBySuperblock', () => {
it('returns the same superblocks if no superBlock is provided', () => {
const superblocks = [
{
name: 'superblock-1',
blocks: [{ dashedName: 'block-1' }, { dashedName: 'block-2' }]
}
];
expect(filterBySuperblock(superblocks)).toEqual(superblocks);
});
it('returns only the specified superblock', () => {
const superblocks = [
{
name: 'superblock-1',
blocks: [{ dashedName: 'block-1' }, { dashedName: 'block-2' }]
},
{
name: 'superblock-2',
blocks: [{ dashedName: 'block-3' }]
}
];
const filtered = filterBySuperblock(superblocks, {
superBlock: 'superblock-1'
});
expect(filtered).toEqual([
{
name: 'superblock-1',
blocks: [{ dashedName: 'block-1' }, { dashedName: 'block-2' }]
}
]);
});
});
describe('closestMatch', () => {
it('returns the closest matching element', () => {
const items = [
'responsive-web-design',
'javascript-algorithms-and-data-structures',
'front-end-development-libraries',
'data-visualization'
];
const input = 'responsiv web design';
const closest = 'responsive-web-design';
expect(closestMatch(input, items)).toBe(closest);
});
it('ignores case when finding the closest match', () => {
const items = [
'responsive-web-design',
'ReSPonSivE-WeB-DeSiGne',
'javascript-algorithms-and-data-structures',
'front-end-development-libraries',
'data-visualization'
];
const input = 'ReSPonSiv WeB DeSiGn';
const closest = 'responsive-web-design';
expect(closestMatch(input, items)).toBe(closest);
});
});
describe('closestFilters', () => {
it('returns the closest matching superblock filter', () => {
const superblocks = [
{
name: 'responsive-web-design',
blocks: [
{ dashedName: 'basic-html-and-html5' },
{ dashedName: 'css-flexbox' }
]
},
{
name: 'javascript-algorithms-and-data-structures',
blocks: [{ dashedName: 'basic-javascript' }, { dashedName: 'es6' }]
}
];
expect(
closestFilters({ superBlock: 'responsiv web design' }, superblocks)
).toEqual({ superBlock: 'responsive-web-design' });
});
it('returns the closest matching block filter', () => {
const superblocks = [
{
name: 'responsive-web-design',
blocks: [
{ dashedName: 'basic-html-and-html5' },
{ dashedName: 'css-flexbox' }
]
},
{
name: 'javascript-algorithms-and-data-structures',
blocks: [{ dashedName: 'basic-javascript' }, { dashedName: 'es6' }]
}
];
expect(closestFilters({ block: 'basic-javascr' }, superblocks)).toEqual({
block: 'basic-javascript'
});
});
});
});