UNPKG

course-renderer

Version:

Manages CA School Courses file system storage and HTML conversion

328 lines (256 loc) 11.9 kB
'use strict'; const assert = require('assert'); const fs = require('fs-extra'); const cheerio = require('cheerio') const yaml = require('js-yaml') const md = require('markdown-it') const vinyl = require('vinyl') const md2html = require('../dist/tasks/md2html') const sanitizer = require('../dist/tasks/path-sanitizer') const generator = require('../dist/tasks/answer-generator') const summaryParser = require('../dist/tasks/summary-parser') const caquestion = require('../dist/markdown/caquestion') const aceEditor = require('../dist/tasks/ace-editor') const annotation = require('../dist/markdown/annotation') describe('rendering markdown files', function() { var fixtures = {}, rendered = {}, ch = {}, parser before(function() { var files = { option: 'option.md', fence: 'fence.md', singleChar: 'singleCharOption.md' } parser = md().use(caquestion) for ( var key in files ) { fixtures[key] = fs.readFileSync(__dirname + '/fixtures/' + files[key]).toString() rendered[key] = parser.render(fixtures[key]) ch[key] = cheerio.load(rendered[key]) } }) describe('Converting markdown files to html', function() { it('should render html', callback => { const stream = md2html({ "sourcePath": "./test" }); stream.on('data', file => { assert(/<[a-z][\s\S]*>/i.test(file.contents.toString())); assert(/\.html$/.test(file.path)) }); stream.on('end', callback); stream.write(new vinyl({ path: './test/fixture.q.md', contents: fs.readFileSync('./test/fixture.q.md') })); stream.write(new vinyl({ path: './test/fixture.md', contents: fs.readFileSync('./test/fixture.md') })); stream.end(); }) }) describe('post rendering hooks', function() { const ch = cheerio.load(fs.readFileSync(__dirname + '/post-rendering-fixture.html', 'utf8')) it('path sanitizer hook', function() { const source = "/path/to/my/source" const repo = "myTestRepo" sanitizer.pathSanitize(ch, repo, source) ch('.ca-verify-button').each((idx, ele) => { assert.equal(source, ch(ele).data('question-source')) assert.equal(repo, ch(ele).data('questionnaire-repo')) }) }) it('answer-generator hook', function() { generator.answerGenerator(ch, __dirname + '/..', 'test', ( ) => { ch('.ca-verify-button').each((idx, ele) => { assert.notEqual(ch(ele).attr('data-question-id'), '') assert.equal(answer[ch(ele).data('question-id')], answersMap[idx]) }) }) }) }) describe('ca-question markdown-it plugin', function() { describe('ca-question annotation parser', function() { it('parses annotation markups with MS type', function() { var annotationText = '/// type=MS, answer=[1,2,3]' var parsedAnnotation = annotation.parseAnnotation(annotationText, 1) assert.equal(parsedAnnotation.type, 'MS') assert.equal(parsedAnnotation.answer, '1,2,3') annotationText = '/// type=MS' assert.throws( () => { parsedAnnotation = annotation.parseAnnotation(annotationText, 1) }, Error ) annotationText = '/// type=MS, answer=[1,2,3], readonly=true' assert.throws( () => { parsedAnnotation = annotation.parseAnnotation(annotationText, 1) }, Error ) }) it ('parsed annotation markups with SS type', function() { var annotationText = '/// type=SS, answer=[2]' var parsedAnnotation = annotation.parseAnnotation(annotationText, 1) assert.equal(parsedAnnotation.type, 'SS') assert.equal(parsedAnnotation.answer, '2') annotationText = '/// type=SS' assert.throws( () => { parsedAnnotation = annotation.parseAnnotation(annotationText, 1) }, Error ) annotationText = '/// type=SS, answer=[2], readonly=true' assert.throws( () => { parsedAnnotation = annotation.parseAnnotation(annotationText, 1) }, Error ) }) it ('parsed annotation markups with TI type', function() { var annotationText = '/// type=TI, answer=[test]' var parsedAnnotation = annotation.parseAnnotation(annotationText, 1) assert.equal(parsedAnnotation.type, 'TI') assert.equal(parsedAnnotation.answer, 'test') annotationText = '/// type=TI' assert.throws( () => { parsedAnnotation = annotation.parseAnnotation(annotationText, 1) }, Error ) annotationText = '/// type=TI, answer=[test], readonly=true' assert.throws( () => { parsedAnnotation = annotation.parseAnnotation(annotationText, 1) }, Error ) }) it ('parsed annotation markups with CR type', function() { var annotationText = '/// type=CR, answer=[/path/to/my/proof/file]' var parsedAnnotation = annotation.parseAnnotation(annotationText, 1) assert.equal(parsedAnnotation.type, 'CR') assert.equal(parsedAnnotation.answer, '/path/to/my/proof/file') annotationText = '/// type=CR' assert.throws( () => { parsedAnnotation = annotation.parseAnnotation(annotationText, 1) }, Error ) annotationText = '/// type=CR, answer=[/path/to/my/proof/file], readonly=true' assert.throws( () => { parsedAnnotation = annotation.parseAnnotation(annotationText, 1) }, Error ) }) it ('parsed annotation markups with REPL type', function() { var annotationText = '/// type=REPL' var parsedAnnotation = annotation.parseAnnotation(annotationText, 1) assert.equal(parsedAnnotation.type, 'REPL') annotationText = '/// type=REPL, answer=[hello]' assert.throws( () => { parsedAnnotation = annotation.parseAnnotation(annotationText, 1) }, Error ) var annotationText = '/// type=REPL, readonly=true' var parsedAnnotation = annotation.parseAnnotation(annotationText, 1) assert.equal(parsedAnnotation.type, 'REPL') assert.equal(parsedAnnotation.readonly, 'true') }) it ("It won't accept any other type of question", function() { var annotationText = '/// type=CQ, answer=[test answer]' assert.throws( () => { parsedAnnotation = annotation.parsedAnnotation(annotationText, 1) }, Error ) }) }) describe('ca-question option parser', function() { it('Can be used to check if a string contains an option marker', function() { const labels = { radio: ['SS', 'MS', 'TI', 'CR'], checkbox:['option 1', 'option 2', 'option 3', 'option 4', 'option 5'] } ch.option('input[type="checkbox"]').each( (idx, ele) => { assert.equal(ch.option(ele).val(), idx + 1) }) ch.option('.checkbox').find('label').each( (idx, ele) => { assert.equal(ch.option(ele).text().replace(/\s+/, ''), labels.checkbox[idx]) }) ch.option('.checkbox').each( (idx, ele) => { assert.equal(true, ch.option(ele).hasClass('ca-checkbox') ) }) ch.option('input[type="radio"]').each( (idx, ele) => { assert.equal(ch.option(ele).val(), idx + 1) }) ch.option('.radio').find('label').each( (idx, ele ) => { assert.equal(ch.option(ele).text().replace(/\s+/, ''), labels.radio[idx]) }) ch.option('.radio').each( (idx, ele ) => { assert.equal(true, ch.option(ele).hasClass('ca-radio')) }) }) it('Can render single char options', function() { const label = ['9', '2', '4', '3', '5'] ch.singleChar('input[type="radio"]').each( (idx, ele) => { assert.equal(ch.singleChar(ele).val(), idx + 1) }) ch.singleChar('.radio').find('label').each( (idx, ele) => { assert.equal(ch.singleChar(ele).text().replace(/\s+/, ''), label[idx]) }) ch.singleChar('.radio').each( (idx, ele) => { assert.equal(true, ch.singleChar(ele).hasClass('ca-radio')) }) }) }) describe('ca-question fence parser', function() { it('An annotation with CR type that is followed by a fence markup will be rendered by the cafence', function() { aceEditor.aceEditor(ch.fence) assert.equal( true, ch.fence('pre').hasClass('ace-editor') ) assert.equal( true, ch.fence('code').hasClass('language-php') ) assert.equal('$var = 1;\n$var++\n', ch.fence('code').text()) }) }) }) describe('SummaryParser', function() { var content; before(function() { content = fs.readFileSync(__dirname + '/fixtures/SUMMARY.md', 'utf8') }) it('parses a CA Academy TOC file', function() { const stream = summaryParser() let summaryOutput = '' stream.on('data', (chunk) => { summaryOutput.concat(chunk.contents.toString()) }) stream.on('end', () => { const parsed = JSON.parse(summaryOutput) assert.equal(true, Array.isArray(parsed)) assert.equal(4, parsed.length) assert.equal('Data Types', parsed[0].text) assert.equal('content/DataTypes.html', parsed[0].url) assert.equal(true, Array.isArray(parsed[0].children)) assert.equal(4, parsed[0].children.length) assert.equal('Strings', parsed[0].children[0].text) assert.equal('Integers', parsed[0].children[2].text) }) stream.write(new vinyl({ path: '/test/fixtures/SUMMARY.md', contents: fs.readFileSync('./test/fixtures/SUMMARY.md') })) }) }) after(function() { fs.removeSync('answer.yml') fs.removeSync( __dirname + '/answer.yml') }) })