UNPKG

unexpected

Version:
219 lines (187 loc) 8.31 kB
/* global global */ var basename = require('path').basename; var debug = require('debug')('metalsmith-unexpected-markdown'); var dirname = require('path').dirname; var extname = require('path').extname; var resolve = require('path').resolve; var marked = require('marked'); var fs = require('fs'); var extend = require('../lib/utils').extend; var vm = require('vm'); var unexpected = require('../lib/').clone(); unexpected.installPlugin(require('magicpen-prism')); var lightExpect = unexpected.clone() .installPlugin(require('./magicpen-github-syntax-theme')); var darkExpect = unexpected.clone() .installPlugin(require('./magicpen-dark-syntax-theme')); var styleRegex = /style=".*?"/; module.exports = function plugin(options) { options = options || {}; var keys = options.keys || []; function parseFlags(flagsString) { var flags = {}; flagsString.split(/,/).forEach(function (flagString) { var m = /(\w+):(\w+)/.exec(flagString); flags[m[1]] = m[2] === 'true'; }); return flags; } return function(files, metalsmith, done){ var exampleTests = {}; Object.keys(files).sort().forEach(function(file){ debug('checking file: %s', file); if (!markdown(file)) return; var data = files[file]; var dir = dirname(file); var html = basename(file, extname(file)) + '.html'; if ('.' !== dir) html = dir + '/' + html; var exampleExpect = (files[file].theme === 'dark' ? darkExpect : lightExpect).clone(); var lastError = ''; var oldGlobal = extend({}, global); global.expect = exampleExpect; var tests = exampleTests[file] = []; options.renderer = new marked.Renderer(); options.renderer.code = function(code, lang, escaped) { var m = /^(\w+)#(\w+:\w+(,\w+:\w+)*)/.exec(lang); var flags = { evaluate: true }; if (m) { lang = m[1]; extend(flags, parseFlags(m[2])); } if (lang === 'js') { lang = 'javascript'; } switch (lang) { case 'javascript': if (flags.evaluate) { tests.push({ code: code }); try { vm.runInThisContext(code); lastError = '<div class="output"></div>'; } catch (e) { var errorMessage = e._isUnexpected ? e.output : exampleExpect.output.clone().error(e.message); lastError = errorMessage.toString('html') .replace(styleRegex, 'class="output"'); } } break; case 'output': tests[tests.length - 1].output = code; return lastError; } return exampleExpect.output.clone().code(code, 'javascript').toString('html') .replace(styleRegex, 'class="code ' + this.options.langPrefix + 'javascript"'); }; debug('converting file: %s', file); var str = marked(data.contents.toString(), options); Object.keys(global).forEach(function (key) { if (!(key in oldGlobal)) { delete global[key]; } }); extend(global, oldGlobal); data.contents = new Buffer(str); keys.forEach(function(key) { data[key] = marked(data[key], options); }); delete files[file]; files[html] = data; }); var pen = unexpected.output.clone(); pen.indentationWidth = 4; pen.addStyle('escapedString', function (content) { this.text(JSON.stringify(content).replace(/^"|"$/g, '')); }); pen.text('/*global describe, it, beforeEach*/').nl(); pen.text('// THIS FILE IS AUTOGENERATED! DO NOT CHANGE IT MANUALLY.').nl(); pen.text('// THIS FILE IS AUTOGENERATED! DO NOT CHANGE IT MANUALLY.').nl(); pen.text('// It is built based on the examples in the documentation folder').nl(); pen.text('// when the documentation site gets build by running "make site-build".').nl(); pen.text('var expect = require("../").clone();').nl(2); pen.text('expect.addAssertion("to have message", function (expect, subject, value) {').nl(); pen.indentLines(); pen.i().block(function () { this.text('var message;').nl(); this.text('if (subject._isUnexpected) {').nl(); this.text(' message = subject.output.toString();').nl(); this.text('} else if (subject && Object.prototype.toString.call(subject) === "[object Error]") {').nl(); this.text(' message = subject.message;').nl(); this.text('} else {').nl(); this.text(' message = String(subject);').nl(); this.text('}').nl(); this.text('this.errorMode = "bubble";').nl(); this.text('expect(message, "to equal", value);'); }).nl(); pen.outdentLines(); pen.text('});').nl(2); pen.text('describe("documentation tests", function () {').nl(); pen.indentLines(); pen.i().text('beforeEach(function () {').nl(); pen.indentLines(); pen.i().text('expect = expect.clone();').nl(); pen.outdentLines(); pen.i().text('});').nl(2); Object.keys(exampleTests).sort().forEach(function (file, index) { var tests = exampleTests[file]; if (tests.length === 0) { return; } if (index > 0) { pen.nl(); } pen.i().text('it("').text(file).text(' contains correct examples", function () {').nl(); pen.indentLines(); tests.forEach(function (test, index) { if (index > 0) { pen.nl(); } if (test.output) { pen.i().text('try {').nl(); pen.indentLines(); pen.i().block('text', test.code).nl(); pen.i().text('expect.fail(function (output) {').nl(); pen.indentLines(); pen.i().text('output.error("expected:").nl();').nl(); test.code.split(/\n/).forEach(function (line, index) { pen.i().text('output.code("').escapedString(line).text('").nl();').nl(); }); pen.i().text('output.error("to throw");').nl(); pen.outdentLines(); pen.i().text('});').nl(); pen.outdentLines(); pen.i().text('} catch (e) {').nl(); pen.indentLines(); pen.i().text('expect(e, "to have message",').nl(); pen.indentLines(); var lines = test.output.split(/\n/); lines.forEach(function (line, index) { pen.i().text('"').escapedString(line); if (index < lines.length - 1) { pen.text('\\n" +').nl(); } else { pen.text('"'); } }); pen.nl(); pen.outdentLines(); pen.i().text(');').nl(); pen.outdentLines(); pen.i().text('}'); } else { pen.i().block('text', test.code); } pen.nl(); }); pen.outdentLines(); pen.i().text('});').nl(); }); pen.outdentLines(); pen.text('});').nl(); fs.writeFile(resolve(__dirname, '..', 'test', 'documentation.spec.js'), pen.toString(), done); }; }; function markdown(file){ return /\.md|\.markdown/.test(extname(file)); }