fix: index.css/js to styles/script (#44356)

* fix: replace index with script/styles as needed

* fix: remove redundant fileKey

It's overwritten by createPoly, so the parser does not need to create it

* fix: curriculum test suite

* Update client/src/templates/Challenges/classic/MultifileEditor.js

Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>

Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>
This commit is contained in:
Oliver Eyton-Williams
2021-12-03 21:32:29 +01:00
committed by GitHub
parent 7a5e805769
commit f613a1e5fd
14 changed files with 115 additions and 157 deletions

View File

@ -297,7 +297,7 @@ export type CompletedChallenge = {
}; };
export type Ext = 'js' | 'html' | 'css' | 'jsx'; export type Ext = 'js' | 'html' | 'css' | 'jsx';
export type FileKey = 'indexjs' | 'indexhtml' | 'indexcss'; export type FileKey = 'scriptjs' | 'indexhtml' | 'stylescss';
export type ChallengeMeta = { export type ChallengeMeta = {
block: string; block: string;

View File

@ -40,14 +40,13 @@ const propTypes = {
saveEditorContent: PropTypes.func.isRequired, saveEditorContent: PropTypes.func.isRequired,
setEditorFocusability: PropTypes.func, setEditorFocusability: PropTypes.func,
theme: PropTypes.string, theme: PropTypes.string,
// TODO: is this used?
title: PropTypes.string, title: PropTypes.string,
updateFile: PropTypes.func.isRequired, updateFile: PropTypes.func.isRequired,
usesMultifileEditor: PropTypes.bool, usesMultifileEditor: PropTypes.bool,
visibleEditors: PropTypes.shape({ visibleEditors: PropTypes.shape({
indexjs: PropTypes.bool, scriptjs: PropTypes.bool,
indexjsx: PropTypes.bool, indexjsx: PropTypes.bool,
indexcss: PropTypes.bool, stylescss: PropTypes.bool,
indexhtml: PropTypes.bool indexhtml: PropTypes.bool
}) })
}; };
@ -83,7 +82,7 @@ const MultifileEditor = props => {
theme, theme,
resizeProps, resizeProps,
title, title,
visibleEditors: { indexcss, indexhtml, indexjs, indexjsx }, visibleEditors: { stylescss, indexhtml, scriptjs, indexjsx },
usesMultifileEditor usesMultifileEditor
} = props; } = props;
const editorTheme = theme === 'night' ? 'vs-dark-custom' : 'vs-custom'; const editorTheme = theme === 'night' ? 'vs-dark-custom' : 'vs-custom';
@ -105,8 +104,8 @@ const MultifileEditor = props => {
if (indexjsx) editorKeys.push('indexjsx'); if (indexjsx) editorKeys.push('indexjsx');
if (indexhtml) editorKeys.push('indexhtml'); if (indexhtml) editorKeys.push('indexhtml');
if (indexcss) editorKeys.push('indexcss'); if (stylescss) editorKeys.push('stylescss');
if (indexjs) editorKeys.push('indexjs'); if (scriptjs) editorKeys.push('scriptjs');
const editorAndSplitterKeys = editorKeys.reduce((acc, key) => { const editorAndSplitterKeys = editorKeys.reduce((acc, key) => {
if (acc.length === 0) { if (acc.length === 0) {

View File

@ -227,11 +227,11 @@ const transformIncludes = async function (fileP) {
div.querySelector('script[src="./script.js"]'); div.querySelector('script[src="./script.js"]');
const importedFiles = []; const importedFiles = [];
if (link) { if (link) {
importedFiles.push('index.css'); importedFiles.push('styles.css');
link.remove(); link.remove();
} }
if (script) { if (script) {
importedFiles.push('index.js'); importedFiles.push('script.js');
script.remove(); script.remove();
} }

View File

@ -51,17 +51,10 @@ const composeFunctions = (...fns) =>
fns.map(applyFunction).reduce((f, g) => x => f(x).then(g)); fns.map(applyFunction).reduce((f, g) => x => f(x).then(g));
function buildSourceMap(challengeFiles) { function buildSourceMap(challengeFiles) {
// TODO: concatenating the source/contents is a quick hack for multi-file // TODO: rename sources.index to sources.contents.
// editing. It is used because all the files (js, html and css) end up with
// the same name 'index'. This made the last file the only file to appear in
// sources.
// A better solution is to store and handle them separately. Perhaps never
// setting the name to 'index'. Use 'contents' instead?
// TODO: is file.source ever defined?
const source = challengeFiles.reduce( const source = challengeFiles.reduce(
(sources, challengeFile) => { (sources, challengeFile) => {
sources[challengeFile.name] += sources.index += challengeFile.source || challengeFile.contents;
challengeFile.source || challengeFile.contents;
sources.editableContents += challengeFile.editableContents || ''; sources.editableContents += challengeFile.editableContents || '';
return sources; return sources;
}, },

View File

@ -459,7 +459,9 @@ ${inspect(commentMap)}
challengeFile.editableContents = getLines( challengeFile.editableContents = getLines(
challengeFile.contents, challengeFile.contents,
challenge.challengeFiles.find( challenge.challengeFiles.find(
x => x.fileKey === challengeFile.fileKey x =>
x.ext === challengeFile.ext &&
x.name === challengeFile.name
).editableRegionBoundaries ).editableRegionBoundaries
); );
}); });
@ -523,7 +525,7 @@ async function createTestRunner(
const challengeFiles = cloneDeep(challenge.challengeFiles); const challengeFiles = cloneDeep(challenge.challengeFiles);
solutionFiles.forEach(solutionFile => { solutionFiles.forEach(solutionFile => {
const challengeFile = challengeFiles.find( const challengeFile = challengeFiles.find(
x => x.fileKey === solutionFile.fileKey x => x.ext === solutionFile.ext && x.name === solutionFile.name
); );
challengeFile.contents = solutionFile.contents; challengeFile.contents = solutionFile.contents;
challengeFile.editableContents = solutionFile.editableContents; challengeFile.editableContents = solutionFile.editableContents;

View File

@ -60,7 +60,6 @@ Object {
</html>", </html>",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "html", "ext": "html",
"fileKey": "indexhtml",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "index",
@ -72,10 +71,9 @@ Object {
}", }",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "css", "ext": "css",
"fileKey": "indexcss",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "styles",
"tail": "", "tail": "",
}, },
Object { Object {
@ -86,10 +84,9 @@ for (let index = 0; index < array.length; index++) {
}", }",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "js", "ext": "js",
"fileKey": "indexjs",
"head": "", "head": "",
"id": "custom-name", "id": "custom-name",
"name": "index", "name": "script",
"tail": "", "tail": "",
}, },
], ],
@ -129,7 +126,6 @@ Object {
</html>", </html>",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "html", "ext": "html",
"fileKey": "indexhtml",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "index",
@ -141,20 +137,18 @@ Object {
}", }",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "css", "ext": "css",
"fileKey": "indexcss",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "styles",
"tail": "", "tail": "",
}, },
Object { Object {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "js", "ext": "js",
"fileKey": "indexjs",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "script",
"tail": "", "tail": "",
}, },
], ],
@ -222,7 +216,6 @@ Object {
23, 23,
], ],
"ext": "html", "ext": "html",
"fileKey": "indexhtml",
"head": "", "head": "",
"id": "html-key", "id": "html-key",
"name": "index", "name": "index",
@ -246,20 +239,18 @@ a {
9, 9,
], ],
"ext": "css", "ext": "css",
"fileKey": "indexcss",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "styles",
"tail": "", "tail": "",
}, },
Object { Object {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "js", "ext": "js",
"fileKey": "indexjs",
"head": " // this runs before the user's code is evaluated.", "head": " // this runs before the user's code is evaluated.",
"id": "final-key", "id": "final-key",
"name": "index", "name": "script",
"tail": "", "tail": "",
}, },
], ],
@ -307,7 +298,6 @@ a {
</body> </body>
</html>", </html>",
"ext": "html", "ext": "html",
"fileKey": "indexhtml",
"head": "", "head": "",
"id": "html-key", "id": "html-key",
"name": "index", "name": "index",
@ -327,19 +317,17 @@ a {
color: green; color: green;
}", }",
"ext": "css", "ext": "css",
"fileKey": "indexcss",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "styles",
"tail": "", "tail": "",
}, },
Object { Object {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"ext": "js", "ext": "js",
"fileKey": "indexjs",
"head": "", "head": "",
"id": "final-key", "id": "final-key",
"name": "index", "name": "script",
"tail": "", "tail": "",
}, },
], ],
@ -393,7 +381,6 @@ Object {
</html>", </html>",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "html", "ext": "html",
"fileKey": "indexhtml",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "index",
@ -405,20 +392,18 @@ Object {
}", }",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "css", "ext": "css",
"fileKey": "indexcss",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "styles",
"tail": "", "tail": "",
}, },
Object { Object {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "js", "ext": "js",
"fileKey": "indexjs",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "script",
"tail": "", "tail": "",
}, },
], ],
@ -440,7 +425,6 @@ Object {
</body> </body>
</html>", </html>",
"ext": "html", "ext": "html",
"fileKey": "indexhtml",
"head": "", "head": "",
"id": "html-key", "id": "html-key",
"name": "index", "name": "index",
@ -451,20 +435,18 @@ Object {
background: white; background: white;
}", }",
"ext": "css", "ext": "css",
"fileKey": "indexcss",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "styles",
"tail": "", "tail": "",
}, },
Object { Object {
"contents": "var x = 'y'; "contents": "var x = 'y';
\`\`", \`\`",
"ext": "js", "ext": "js",
"fileKey": "indexjs",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "script",
"tail": "", "tail": "",
}, },
], ],
@ -499,7 +481,6 @@ Object {
</html>", </html>",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "html", "ext": "html",
"fileKey": "indexhtml",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "index",
@ -511,20 +492,18 @@ Object {
}", }",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "css", "ext": "css",
"fileKey": "indexcss",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "styles",
"tail": "", "tail": "",
}, },
Object { Object {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "js", "ext": "js",
"fileKey": "indexjs",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "script",
"tail": "", "tail": "",
}, },
], ],
@ -565,7 +544,6 @@ Object {
</html>", </html>",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "html", "ext": "html",
"fileKey": "indexhtml",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "index",
@ -577,20 +555,18 @@ Object {
}", }",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "css", "ext": "css",
"fileKey": "indexcss",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "styles",
"tail": "", "tail": "",
}, },
Object { Object {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "js", "ext": "js",
"fileKey": "indexjs",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "script",
"tail": "", "tail": "",
}, },
], ],
@ -626,7 +602,6 @@ Object {
</body> </body>
</html>", </html>",
"ext": "html", "ext": "html",
"fileKey": "indexhtml",
"head": "", "head": "",
"id": "html-key", "id": "html-key",
"name": "index", "name": "index",
@ -637,20 +612,18 @@ Object {
background: white; background: white;
}", }",
"ext": "css", "ext": "css",
"fileKey": "indexcss",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "styles",
"tail": "", "tail": "",
}, },
Object { Object {
"contents": "var x = 'y'; "contents": "var x = 'y';
\`\`", \`\`",
"ext": "js", "ext": "js",
"fileKey": "indexjs",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "script",
"tail": "", "tail": "",
}, },
], ],

View File

@ -10,7 +10,6 @@ Object {
</html>", </html>",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "html", "ext": "html",
"fileKey": "indexhtml",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "index",
@ -22,20 +21,18 @@ Object {
}", }",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "css", "ext": "css",
"fileKey": "indexcss",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "styles",
"tail": "", "tail": "",
}, },
Object { Object {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"editableRegionBoundaries": Array [], "editableRegionBoundaries": Array [],
"ext": "js", "ext": "js",
"fileKey": "indexjs",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "script",
"tail": "", "tail": "",
}, },
], ],

View File

@ -10,7 +10,6 @@ Object {
</body> </body>
</html>", </html>",
"ext": "html", "ext": "html",
"fileKey": "indexhtml",
"head": "", "head": "",
"id": "html-key", "id": "html-key",
"name": "index", "name": "index",
@ -21,20 +20,18 @@ Object {
background: white; background: white;
}", }",
"ext": "css", "ext": "css",
"fileKey": "indexcss",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "styles",
"tail": "", "tail": "",
}, },
Object { Object {
"contents": "var x = 'y'; "contents": "var x = 'y';
\`\`", \`\`",
"ext": "js", "ext": "js",
"fileKey": "indexjs",
"head": "", "head": "",
"id": "", "id": "",
"name": "index", "name": "script",
"tail": "", "tail": "",
}, },
], ],

View File

@ -2,7 +2,6 @@ const { isEmpty } = require('lodash');
const { root } = require('mdast-builder'); const { root } = require('mdast-builder');
const visitChildren = require('unist-util-visit-children'); const visitChildren = require('unist-util-visit-children');
const getAllBetween = require('./utils/between-headings'); const getAllBetween = require('./utils/between-headings');
// const visit = require('unist-util-visit');
const { getFileVisitor } = require('./utils/get-file-visitor'); const { getFileVisitor } = require('./utils/get-file-visitor');
const editableRegionMarker = '--fcc-editable-region--'; const editableRegionMarker = '--fcc-editable-region--';

View File

@ -42,15 +42,13 @@ describe('add-seed plugin', () => {
}); });
it('adds test objects to the challengeFiles array following a schema', () => { it('adds test objects to the challengeFiles array following a schema', () => {
expect.assertions(17); expect.assertions(15);
plugin(simpleAST, file); plugin(simpleAST, file);
const { const {
data: { challengeFiles } data: { challengeFiles }
} = file; } = file;
const testObject = challengeFiles.find(x => x.fileKey === 'indexjs'); const testObject = challengeFiles.find(x => x.ext === 'js');
expect(Object.keys(testObject).length).toEqual(8); expect(Object.keys(testObject).length).toEqual(7);
expect(testObject).toHaveProperty('fileKey');
expect(typeof testObject['fileKey']).toBe('string');
expect(testObject).toHaveProperty('ext'); expect(testObject).toHaveProperty('ext');
expect(typeof testObject['ext']).toBe('string'); expect(typeof testObject['ext']).toBe('string');
expect(testObject).toHaveProperty('name'); expect(testObject).toHaveProperty('name');
@ -73,16 +71,16 @@ describe('add-seed plugin', () => {
const { const {
data: { challengeFiles } data: { challengeFiles }
} = file; } = file;
const indexjs = challengeFiles.find(x => x.fileKey === 'indexjs'); const scriptjs = challengeFiles.find(x => x.ext === 'js');
const indexhtml = challengeFiles.find(x => x.fileKey === 'indexhtml'); const indexhtml = challengeFiles.find(x => x.ext === 'html');
const indexcss = challengeFiles.find(x => x.fileKey === 'indexcss'); const stylescss = challengeFiles.find(x => x.ext === 'css');
expect(indexjs.contents).toBe(`var x = 'y';`); expect(scriptjs.contents).toBe(`var x = 'y';`);
expect(indexhtml.contents).toBe(`<html> expect(indexhtml.contents).toBe(`<html>
<body> <body>
</body> </body>
</html>`); </html>`);
expect(indexcss.contents).toBe(`body { expect(stylescss.contents).toBe(`body {
background: green; background: green;
}`); }`);
}); });
@ -93,10 +91,10 @@ describe('add-seed plugin', () => {
const { const {
data: { challengeFiles } data: { challengeFiles }
} = file; } = file;
const indexcss = challengeFiles.find(x => x.fileKey === 'indexcss'); const stylescss = challengeFiles.find(x => x.ext === 'css');
expect(indexcss.contents).not.toMatch('--fcc-editable-region--'); expect(stylescss.contents).not.toMatch('--fcc-editable-region--');
expect(indexcss.editableRegionBoundaries).toEqual([1, 4]); expect(stylescss.editableRegionBoundaries).toEqual([1, 4]);
}); });
// TODO: can we reuse 'name'? It's always 'index', I think, which suggests // TODO: can we reuse 'name'? It's always 'index', I think, which suggests
@ -107,13 +105,13 @@ describe('add-seed plugin', () => {
const { const {
data: { challengeFiles } data: { challengeFiles }
} = file; } = file;
const indexjs = challengeFiles.find(x => x.fileKey === 'indexjs'); const scriptjs = challengeFiles.find(x => x.ext === 'js');
const indexhtml = challengeFiles.find(x => x.fileKey === 'indexhtml'); const indexhtml = challengeFiles.find(x => x.ext === 'html');
const indexcss = challengeFiles.find(x => x.fileKey === 'indexcss'); const stylescss = challengeFiles.find(x => x.ext === 'css');
expect(indexhtml.id).toBe(''); expect(indexhtml.id).toBe('');
expect(indexcss.id).toBe('key-for-css'); expect(stylescss.id).toBe('key-for-css');
expect(indexjs.id).toBe('key-for-js'); expect(scriptjs.id).toBe('key-for-js');
}); });
it('throws if an id is anywhere except directly before a code node', () => { it('throws if an id is anywhere except directly before a code node', () => {
@ -140,13 +138,13 @@ describe('add-seed plugin', () => {
const { const {
data: { challengeFiles } data: { challengeFiles }
} = file; } = file;
const indexjs = challengeFiles.find(x => x.fileKey === 'indexjs'); const scriptjs = challengeFiles.find(x => x.ext === 'js');
const indexhtml = challengeFiles.find(x => x.fileKey === 'indexhtml'); const indexhtml = challengeFiles.find(x => x.ext === 'html');
const indexcss = challengeFiles.find(x => x.fileKey === 'indexcss'); const stylescss = challengeFiles.find(x => x.ext === 'css');
expect(indexjs.head).toBe(''); expect(scriptjs.head).toBe('');
expect(indexhtml.head).toBe(`<!-- comment -->`); expect(indexhtml.head).toBe(`<!-- comment -->`);
expect(indexcss.head).toBe(`body { expect(stylescss.head).toBe(`body {
etc: '' etc: ''
}`); }`);
}); });
@ -157,15 +155,15 @@ describe('add-seed plugin', () => {
const { const {
data: { challengeFiles } data: { challengeFiles }
} = file; } = file;
const indexjs = challengeFiles.find(x => x.fileKey === 'indexjs'); const scriptjs = challengeFiles.find(x => x.ext === 'js');
const indexhtml = challengeFiles.find(x => x.fileKey === 'indexhtml'); const indexhtml = challengeFiles.find(x => x.ext === 'html');
const indexcss = challengeFiles.find(x => x.fileKey === 'indexcss'); const stylescss = challengeFiles.find(x => x.ext === 'css');
expect(indexjs.tail).toBe(`function teardown(params) { expect(scriptjs.tail).toBe(`function teardown(params) {
// after // after
}`); }`);
expect(indexhtml.tail).toBe(''); expect(indexhtml.tail).toBe('');
expect(indexcss.tail).toBe(`body { expect(stylescss.tail).toBe(`body {
background: blue; background: blue;
}`); }`);
}); });
@ -194,16 +192,16 @@ describe('add-seed plugin', () => {
const { const {
data: { challengeFiles } data: { challengeFiles }
} = file; } = file;
const indexjs = challengeFiles.find(x => x.fileKey === 'indexjs'); const scriptjs = challengeFiles.find(x => x.ext === 'js');
const indexhtml = challengeFiles.find(x => x.fileKey === 'indexhtml'); const indexhtml = challengeFiles.find(x => x.ext === 'html');
const indexcss = challengeFiles.find(x => x.fileKey === 'indexcss'); const stylescss = challengeFiles.find(x => x.ext === 'css');
expect(indexjs.head).toBe(''); expect(scriptjs.head).toBe('');
expect(indexjs.tail).toBe('function teardown(params) {\n // after\n}'); expect(scriptjs.tail).toBe('function teardown(params) {\n // after\n}');
expect(indexhtml.head).toBe(''); expect(indexhtml.head).toBe('');
expect(indexhtml.tail).toBe(''); expect(indexhtml.tail).toBe('');
expect(indexcss.head).toBe(''); expect(stylescss.head).toBe('');
expect(indexcss.tail).toBe('body {\n background: blue;\n}'); expect(stylescss.tail).toBe('body {\n background: blue;\n}');
}); });
it('quietly ignores empty after sections', () => { it('quietly ignores empty after sections', () => {
@ -212,16 +210,16 @@ describe('add-seed plugin', () => {
const { const {
data: { challengeFiles } data: { challengeFiles }
} = file; } = file;
const indexjs = challengeFiles.find(x => x.fileKey === 'indexjs'); const scriptjs = challengeFiles.find(x => x.ext === 'js');
const indexhtml = challengeFiles.find(x => x.fileKey === 'indexhtml'); const indexhtml = challengeFiles.find(x => x.ext === 'html');
const indexcss = challengeFiles.find(x => x.fileKey === 'indexcss'); const stylescss = challengeFiles.find(x => x.ext === 'css');
expect(indexjs.head).toBe(''); expect(scriptjs.head).toBe('');
expect(indexjs.tail).toBe(''); expect(scriptjs.tail).toBe('');
expect(indexhtml.head).toBe('<!-- comment -->'); expect(indexhtml.head).toBe('<!-- comment -->');
expect(indexhtml.tail).toBe(''); expect(indexhtml.tail).toBe('');
expect(indexcss.head).toBe("body {\n etc: ''\n}"); expect(stylescss.head).toBe("body {\n etc: ''\n}");
expect(indexcss.tail).toBe(''); expect(stylescss.tail).toBe('');
}); });
it('throws an error (with line number) if 2 markers appear on 1 line', () => { it('throws an error (with line number) if 2 markers appear on 1 line', () => {
@ -239,12 +237,12 @@ describe('add-seed plugin', () => {
}); });
it('handles jsx', () => { it('handles jsx', () => {
expect.assertions(4); expect.assertions(3);
plugin(jsxSeedAST, file); plugin(jsxSeedAST, file);
const { const {
data: { challengeFiles } data: { challengeFiles }
} = file; } = file;
const indexjsx = challengeFiles.find(x => x.fileKey === 'indexjsx'); const indexjsx = challengeFiles.find(x => x.ext === 'jsx');
expect(indexjsx.head).toBe(`function setup() {}`); expect(indexjsx.head).toBe(`function setup() {}`);
expect(indexjsx.tail).toBe(`function teardown(params) { expect(indexjsx.tail).toBe(`function teardown(params) {
@ -256,7 +254,6 @@ describe('add-seed plugin', () => {
const Button = () => { const Button = () => {
return <button> {/* another comment! */} text </button>; return <button> {/* another comment! */} text </button>;
};`); };`);
expect(indexjsx.fileKey).toBe(`indexjsx`);
}); });
it('combines all the code of a specific language into a single file', () => { it('combines all the code of a specific language into a single file', () => {

View File

@ -34,17 +34,13 @@ describe('add solution plugin', () => {
}); });
it('adds solution objects to the challengeFiles array following a schema', () => { it('adds solution objects to the challengeFiles array following a schema', () => {
expect.assertions(15); expect.assertions(13);
plugin(mockAST, file); plugin(mockAST, file);
const { const {
data: { solutions } data: { solutions }
} = file; } = file;
const testObject = solutions[0].find( const testObject = solutions[0].find(solution => solution.ext === 'js');
solution => solution.fileKey === 'indexjs' expect(Object.keys(testObject).length).toEqual(6);
);
expect(Object.keys(testObject).length).toEqual(7);
expect(testObject).toHaveProperty('fileKey');
expect(typeof testObject['fileKey']).toBe('string');
expect(testObject).toHaveProperty('ext'); expect(testObject).toHaveProperty('ext');
expect(typeof testObject['ext']).toBe('string'); expect(typeof testObject['ext']).toBe('string');
expect(testObject).toHaveProperty('name'); expect(testObject).toHaveProperty('name');
@ -66,24 +62,22 @@ describe('add solution plugin', () => {
data: { solutions } data: { solutions }
} = file; } = file;
expect(solutions.length).toBe(3); expect(solutions.length).toBe(3);
expect( expect(solutions[0].find(solution => solution.ext === 'js').contents).toBe(
solutions[0].find(solution => solution.fileKey === 'indexjs').contents "var x = 'y';"
).toBe("var x = 'y';"); );
expect( expect(solutions[1].find(solution => solution.ext === 'html').contents)
solutions[1].find(solution => solution.fileKey === 'indexhtml').contents .toBe(`<html>
).toBe(`<html>
<body> <body>
solution number two solution number two
</body> </body>
</html>`); </html>`);
expect( expect(solutions[1].find(solution => solution.ext === 'css').contents)
solutions[1].find(solution => solution.fileKey === 'indexcss').contents .toBe(`body {
).toBe(`body {
background: white; background: white;
}`); }`);
expect( expect(solutions[2].find(solution => solution.ext === 'js').contents).toBe(
solutions[2].find(solution => solution.fileKey === 'indexjs').contents "var x = 'y3';"
).toBe("var x = 'y3';"); );
}); });
it('should reject solutions with editable region markers', () => { it('should reject solutions with editable region markers', () => {

View File

@ -12,9 +12,8 @@ const supportedLanguages = ['js', 'css', 'html', 'jsx', 'py'];
function defaultFile(lang, id) { function defaultFile(lang, id) {
return { return {
fileKey: `index${lang}`,
ext: lang, ext: lang,
name: 'index', name: getFilenames(lang),
contents: '', contents: '',
head: '', head: '',
tail: '', tail: '',
@ -22,6 +21,14 @@ function defaultFile(lang, id) {
}; };
} }
function getFilenames(lang) {
const langToFilename = {
js: 'script',
css: 'styles'
};
return langToFilename[lang] ?? 'index';
}
function getFileVisitor(seeds, seedKey, validate) { function getFileVisitor(seeds, seedKey, validate) {
return (node, index, parent) => { return (node, index, parent) => {
if (is(node, 'root')) return; if (is(node, 'root')) return;
@ -43,21 +50,21 @@ function codeToData(node, seeds, seedKey, validate) {
Please use one of js, css, html, jsx or py Please use one of js, css, html, jsx or py
`); `);
const fileKey = `index${lang}`; const fileId = `index${lang}`;
const id = seeds[fileKey] ? seeds[fileKey].id : ''; const id = seeds[fileId] ? seeds[fileId].id : '';
// the contents will be missing if there is an id preceding this code // the contents will be missing if there is an id preceding this code
// block. // block.
if (!seeds[fileKey]) { if (!seeds[fileId]) {
seeds[fileKey] = defaultFile(lang, id); seeds[fileId] = defaultFile(lang, id);
} }
if (isEmpty(node.value) && seedKey !== 'contents') { if (isEmpty(node.value) && seedKey !== 'contents') {
const section = keyToSection[seedKey]; const section = keyToSection[seedKey];
throw Error(`Empty code block in --${section}-- section`); throw Error(`Empty code block in --${section}-- section`);
} }
seeds[fileKey][seedKey] = isEmpty(seeds[fileKey][seedKey]) seeds[fileId][seedKey] = isEmpty(seeds[fileId][seedKey])
? node.value ? node.value
: seeds[fileKey][seedKey] + '\n' + node.value; : seeds[fileId][seedKey] + '\n' + node.value;
} }
function idToData(node, index, parent, seeds) { function idToData(node, index, parent, seeds) {

View File

@ -4,10 +4,10 @@ exports.challengeFiles = [
error: null, error: null,
ext: 'css', ext: 'css',
head: '', head: '',
history: ['index.css'], history: ['styles.css'],
fileKey: 'indexcss', fileKey: 'stylescss',
name: 'index', name: 'styles',
path: 'index.css', path: 'styles.css',
seed: 'some css', seed: 'some css',
tail: '' tail: ''
}, },
@ -28,10 +28,10 @@ exports.challengeFiles = [
error: null, error: null,
ext: 'js', ext: 'js',
head: '', head: '',
history: ['index.js'], history: ['script.js'],
fileKey: 'indexjs', fileKey: 'scriptjs',
name: 'index', name: 'script',
path: 'index.js', path: 'script.js',
seed: 'some js', seed: 'some js',
tail: '' tail: ''
}, },

View File

@ -17,7 +17,7 @@ describe('sort-files', () => {
it('should sort the objects into html, css, jsx, js order', () => { it('should sort the objects into html, css, jsx, js order', () => {
const sorted = sortChallengeFiles(challengeFiles); const sorted = sortChallengeFiles(challengeFiles);
const sortedKeys = sorted.map(({ fileKey }) => fileKey); const sortedKeys = sorted.map(({ fileKey }) => fileKey);
const expected = ['indexhtml', 'indexcss', 'indexjsx', 'indexjs']; const expected = ['indexhtml', 'stylescss', 'indexjsx', 'scriptjs'];
expect(sortedKeys).toStrictEqual(expected); expect(sortedKeys).toStrictEqual(expected);
}); });
}); });