Files
freeCodeCamp/tools/challenge-parser/parser/plugins/add-solution.test.js
2026-04-25 11:42:44 +05:30

128 lines
3.9 KiB
JavaScript

import path from 'path';
import { describe, beforeAll, beforeEach, it, expect } from 'vitest';
import { isObject } from 'lodash';
import parseFixture from '../__fixtures__/parse-fixture';
import addSolution from './add-solution';
describe('add solution plugin', () => {
let mockAST, multiSolnsAST, editableSolutionAST;
beforeAll(async () => {
editableSolutionAST = await parseFixture('with-erm-in-solution.md');
mockAST = await parseFixture('simple.md');
multiSolnsAST = await parseFixture('with-multiple-solns.md');
});
const plugin = addSolution();
let file = { data: {} };
beforeEach(() => {
file = { data: {} };
});
it('returns a function', () => {
expect(typeof plugin).toEqual('function');
});
it('adds a `solutions` property to `file.data`', () => {
plugin(mockAST, file);
expect('solutions' in file.data).toBe(true);
});
it('ensures that the `solutions` property is an array', () => {
plugin(mockAST, file);
expect(Array.isArray(file.data.solutions)).toBe(true);
});
it('each entry in the `solutions` array is an object', () => {
plugin(mockAST, file);
expect(file.data.solutions.every(el => isObject(el))).toBe(true);
});
it('adds solution objects to the challengeFiles array following a schema', () => {
expect.assertions(9);
plugin(mockAST, file);
const {
data: { solutions }
} = file;
const testObject = solutions[0].find(solution => solution.ext === 'js');
expect(Object.keys(testObject).length).toEqual(4);
expect(testObject).toHaveProperty('ext');
expect(typeof testObject['ext']).toBe('string');
expect(testObject).toHaveProperty('name');
expect(typeof testObject['name']).toBe('string');
expect(testObject).toHaveProperty('contents');
expect(typeof testObject['contents']).toBe('string');
expect(testObject).toHaveProperty('id');
expect(typeof testObject['id']).toBe('string');
});
it('adds multiple solutions if they exist', () => {
expect.assertions(5);
plugin(multiSolnsAST, file);
const {
data: { solutions }
} = file;
expect(solutions.length).toBe(3);
expect(solutions[0].find(solution => solution.ext === 'js').contents).toBe(
"var x = 'y';"
);
expect(solutions[1].find(solution => solution.ext === 'html').contents)
.toBe(`<html>
<body>
solution number two
</body>
</html>`);
expect(solutions[1].find(solution => solution.ext === 'css').contents)
.toBe(`body {
background: white;
}`);
expect(solutions[2].find(solution => solution.ext === 'js').contents).toBe(
"var x = 'y3';"
);
});
it('should reject solutions with editable region markers', () => {
expect.assertions(1);
expect(() => plugin(editableSolutionAST, file)).toThrow(
'--fcc-editable-region-- should only appear in the --seed-contents--\n' +
'section, not in --solutions--'
);
});
it('should have an output to match the snapshot', () => {
plugin(mockAST, file);
expect(file.data).toMatchSnapshot();
});
it('should throw if a workshop non-last step has solutions', async () => {
expect.assertions(1);
const workshopNonLastAST = await parseFixture('with-multiple-solns.md');
const workshopFile = {
data: {},
path: path.join(
__dirname,
'../__fixtures__/workshop-test-steps/step-1.md'
)
};
expect(() => plugin(workshopNonLastAST, workshopFile)).toThrow(
'has solutions but is not the last step'
);
});
it('should allow solutions in non-last steps for upcoming workshop blocks', async () => {
expect.assertions(1);
const workshopNonLastAST = await parseFixture('with-multiple-solns.md');
const upcomingWorkshopFile = {
data: {},
path: path.join(
__dirname,
'../__fixtures__/workshop-upcoming-test-steps/step-1.md'
)
};
expect(() =>
plugin(workshopNonLastAST, upcomingWorkshopFile)
).not.toThrow();
});
});