import * as babel from "babel-core"; import { buildExternalHelpers } from "babel-core"; import getFixtures from "babel-helper-fixtures"; import sourceMap from "source-map"; import codeFrame from "babel-code-frame"; import defaults from "lodash/defaults"; import includes from "lodash/includes"; import * as helpers from "./helpers"; import extend from "lodash/extend"; import merge from "lodash/merge"; import assert from "assert"; import chai from "chai"; import "babel-polyfill"; import fs from "fs"; import path from "path"; const babelHelpers = eval(buildExternalHelpers(null, "var")); function wrapPackagesArray(type, names, optionsDir) { return (names || []).map(function (val) { if (typeof val === "string") val = [val]; // relative path (outside of monorepo) if (val[0][0] === ".") { if (!optionsDir) { throw new Error("Please provide an options.json in test dir when using a " + "relative plugin path."); } val[0] = path.resolve(optionsDir, val[0]); } // check node_modules/babel-x-y else { val[0] = __dirname + "/../../babel-" + type + "-" + val[0]; } return val; }); } function run(task) { const actual = task.actual; const expect = task.expect; const exec = task.exec; const opts = task.options; const optionsDir = task.optionsDir; function getOpts(self) { const newOpts = merge({ filename: self.loc, }, opts); newOpts.plugins = wrapPackagesArray("plugin", newOpts.plugins, optionsDir); newOpts.presets = wrapPackagesArray("preset", newOpts.presets, optionsDir).map(function (val) { if (val.length > 2) { throw new Error("Unexpected extra options " + JSON.stringify(val.slice(2)) + " passed to preset."); } return val; }); return newOpts; } let execCode = exec.code; let result; let resultExec; if (execCode) { const execOpts = getOpts(exec); const execDirName = path.dirname(exec.loc); result = babel.transform(execCode, execOpts); execCode = result.code; try { resultExec = runExec(execOpts, execCode, execDirName); } catch (err) { err.message = exec.loc + ": " + err.message; err.message += codeFrame(execCode); throw err; } } let actualCode = actual.code; const expectCode = expect.code; if (!execCode || actualCode) { result = babel.transform(actualCode, getOpts(actual)); if ( !expect.code && result.code && !opts.throws && fs.statSync(path.dirname(expect.loc)).isDirectory() && !process.env.CI ) { console.log(`New test file created: ${expect.loc}`); fs.writeFileSync(expect.loc, result.code); } else { actualCode = result.code.trim(); chai.expect(actualCode).to.be.equal(expectCode, actual.loc + " !== " + expect.loc); } } if (task.sourceMap) { chai.expect(result.map).to.deep.equal(task.sourceMap); } if (task.sourceMappings) { const consumer = new sourceMap.SourceMapConsumer(result.map); task.sourceMappings.forEach(function (mapping) { const actual = mapping.original; const expect = consumer.originalPositionFor(mapping.generated); chai.expect({ line: expect.line, column: expect.column }).to.deep.equal(actual); }); } if (execCode && resultExec) { return resultExec; } } function runExec(opts, execCode, execDirname) { const sandbox = { ...helpers, babelHelpers, assert: chai.assert, transform: babel.transform, opts, exports: {}, require(id) { return require(id[0] === "." ? path.resolve(execDirname, id) : id); } }; const fn = new Function(...Object.keys(sandbox), execCode); return fn.apply(null, Object.values(sandbox)); } export default function ( fixturesLoc: string, name: string, suiteOpts = {}, taskOpts = {}, dynamicOpts?: Function, ) { const suites = getFixtures(fixturesLoc); for (const testSuite of suites) { if (includes(suiteOpts.ignoreSuites, testSuite.title)) continue; describe(name + "/" + testSuite.title, function () { for (const task of testSuite.tests) { if (includes(suiteOpts.ignoreTasks, task.title) || includes(suiteOpts.ignoreTasks, testSuite.title + "/" + task.title)) continue; it(task.title, !task.disabled && function () { function runTask() { run(task); } defaults(task.options, { filenameRelative: task.expect.filename, sourceFileName: task.actual.filename, sourceMapTarget: task.expect.filename, suppressDeprecationMessages: true, babelrc: false, sourceMap: !!(task.sourceMappings || task.sourceMap), }); extend(task.options, taskOpts); if (dynamicOpts) dynamicOpts(task.options, task); const throwMsg = task.options.throws; if (throwMsg) { // internal api doesn't have this option but it's best not to pollute // the options object with useless options delete task.options.throws; assert.throws(runTask, function (err) { return throwMsg === true || err.message.indexOf(throwMsg) >= 0; }); } else { if (task.exec.code) { const result = run(task); if (result && typeof result.then === "function") { return result; } } else { runTask(); } } }); } }); } }