UNPKG

@babel/helper-transform-fixture-test-runner

Version:

Transform test runner for @babel/helper-fixtures module

378 lines (375 loc) 13.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = _default; exports.runCodeInTestContext = runCodeInTestContext; var babel = require("@babel/core"); var _helperFixtures = require("@babel/helper-fixtures"); var _codeFrame = require("@babel/code-frame"); var helpers = require("./helpers"); var _assert = require("assert"); var _fs = require("fs"); var _path = require("path"); var _vm = require("vm"); var _quickLru = require("quick-lru"); var _url = require("url"); var _module = require("module"); var _helperCheckDuplicateNodes = require("@babel/helper-check-duplicate-nodes"); function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } { if (!_assert.rejects) { _assert.rejects = _asyncToGenerator(function* (block, validateError) { try { yield typeof block === "function" ? block() : block; return Promise.reject(new Error("Promise not rejected")); } catch (error) { if (typeof validateError === "function" && !validateError(error)) { return Promise.reject(new Error("Promise rejected with invalid error")); } } }); } } const EXTERNAL_HELPERS_VERSION = "7.100.0"; const cachedScripts = new _quickLru({ maxSize: 10 }); const contextModuleCache = new WeakMap(); const sharedTestContext = createContext(); function transformWithoutConfigFile(code, opts) { return babel.transformSync(code, Object.assign({ configFile: false, babelrc: false }, opts)); } function transformAsyncWithoutConfigFile(code, opts) { return babel.transformAsync(code, Object.assign({ configFile: false, babelrc: false }, opts)); } function createContext() { const context = _vm.createContext(Object.assign({}, helpers, { process: process, transform: transformWithoutConfigFile, transformAsync: transformAsyncWithoutConfigFile, setTimeout: setTimeout, setImmediate: setImmediate, expect })); context.global = context; const moduleCache = Object.create(null); contextModuleCache.set(context, moduleCache); runCacheableScriptInTestContext(_path.join(_path.dirname(__filename), "babel-helpers-in-memory.js"), babel.buildExternalHelpers, context, moduleCache); return context; } function runCacheableScriptInTestContext(filename, srcFn, context, moduleCache) { let cached = cachedScripts.get(filename); if (!cached) { const code = `(function (exports, require, module, __filename, __dirname) {\n${srcFn()}\n});`; cached = { code, cachedData: undefined }; cachedScripts.set(filename, cached); } let script; { script = new _vm.Script(cached.code, { filename, displayErrors: true, lineOffset: -1, cachedData: cached.cachedData, produceCachedData: true }); if (script.cachedDataProduced) { cached.cachedData = script.cachedData; } } const module = { id: filename, exports: {} }; moduleCache[filename] = module; const req = id => runModuleInTestContext(id, filename, context, moduleCache); const dirname = _path.dirname(filename); script.runInContext(context).call(module.exports, module.exports, req, module, filename, dirname); return module; } function runModuleInTestContext(id, relativeFilename, context, moduleCache) { const filename = (((v, w) => (v = v.split("."), w = w.split("."), +v[0] > +w[0] || v[0] == w[0] && +v[1] >= +w[1]))(process.versions.node, "8.9") ? require.resolve : (r, { paths: [b] }, M = require("module")) => { let f = M._findPath(r, M._nodeModulePaths(b).concat(b)); if (f) return f; f = new Error(`Cannot resolve module '${r}'`); f.code = "MODULE_NOT_FOUND"; throw f; })(id, { paths: [_path.dirname(relativeFilename)] }); if (filename === id) return require(id); if (moduleCache[filename]) return moduleCache[filename].exports; return runCacheableScriptInTestContext(filename, () => _fs.readFileSync(filename, "utf8"), context, moduleCache).exports; } function runCodeInTestContext(code, opts, context = sharedTestContext) { const filename = opts.filename; const dirname = _path.dirname(filename); const moduleCache = contextModuleCache.get(context); const req = id => runModuleInTestContext(id, filename, context, moduleCache); const module = { id: filename, exports: {} }; const oldCwd = process.cwd(); try { if (opts.filename) process.chdir(_path.dirname(opts.filename)); const src = `(function(exports, require, module, __filename, __dirname, opts) {\n${code}\n});`; return _vm.runInContext(src, context, { filename, displayErrors: true, lineOffset: -1 })(module.exports, req, module, filename, dirname, opts); } finally { process.chdir(oldCwd); } } function maybeMockConsole(_x3, _x4) { return _maybeMockConsole.apply(this, arguments); } function _maybeMockConsole() { _maybeMockConsole = _asyncToGenerator(function* (validateLogs, run) { const actualLogs = { stdout: "", stderr: "" }; if (!validateLogs) return { result: yield run(), actualLogs }; const spy1 = jest.spyOn(console, "log").mockImplementation(msg => { actualLogs.stdout += `${msg}\n`; }); const spy2 = jest.spyOn(console, "warn").mockImplementation(msg => { actualLogs.stderr += `${msg}\n`; }); try { return { result: yield run(), actualLogs }; } finally { spy1.mockRestore(); spy2.mockRestore(); } }); return _maybeMockConsole.apply(this, arguments); } function run(_x5) { return _run.apply(this, arguments); } function _run() { _run = _asyncToGenerator(function* (task) { const { actual, expect: expected, exec, options: opts, doNotSetSourceType, optionsDir, validateLogs, ignoreOutput, stdout, stderr } = task; function getOpts(self) { const newOpts = Object.assign({ ast: true, cwd: _path.dirname(self.loc), filename: self.loc, filenameRelative: self.filename, sourceFileName: self.filename }, doNotSetSourceType ? {} : { sourceType: "script" }, { babelrc: false, configFile: false, inputSourceMap: task.inputSourceMap || undefined }, opts); return (0, _helperFixtures.resolveOptionPluginOrPreset)(newOpts, optionsDir); } let execCode = exec.code; let result; let resultExec; if (execCode) { const context = createContext(); const execOpts = getOpts(exec); ({ result } = yield maybeMockConsole(validateLogs, () => babel.transformAsync(execCode, execOpts))); (0, _helperCheckDuplicateNodes.default)(result.ast); execCode = result.code; try { resultExec = runCodeInTestContext(execCode, execOpts, context); } catch (err) { err.message = `${exec.loc}: ${err.message}\n` + (0, _codeFrame.codeFrameColumns)(execCode, {}); throw err; } } const inputCode = actual.code; const expectedCode = expected.code; if (!execCode || inputCode) { let actualLogs; ({ result, actualLogs } = yield maybeMockConsole(validateLogs, () => babel.transformAsync(inputCode, getOpts(actual)))); const outputCode = normalizeOutput(result.code); (0, _helperCheckDuplicateNodes.default)(result.ast); if (!ignoreOutput) { if (!expected.code && outputCode && !opts.throws && _fs.statSync(_path.dirname(expected.loc)).isDirectory() && !process.env.CI) { const expectedFile = expected.loc.replace(/\.m?js$/, result.sourceType === "module" ? ".mjs" : ".js"); console.log(`New test file created: ${expectedFile}`); _fs.writeFileSync(expectedFile, `${outputCode}\n`); if (expected.loc !== expectedFile) { try { _fs.unlinkSync(expected.loc); } catch (e) {} } } else { validateFile(outputCode, expected.loc, expectedCode); if (inputCode) { expect(expected.loc).toMatch(result.sourceType === "module" ? /\.mjs$/ : /\.js$/); } } } if (validateLogs) { const normalizationOpts = { normalizePathSeparator: true, normalizePresetEnvDebug: task.taskDir.includes("babel-preset-env") }; validateFile(normalizeOutput(actualLogs.stdout, normalizationOpts), stdout.loc, stdout.code); validateFile(normalizeOutput(actualLogs.stderr, normalizationOpts), stderr.loc, stderr.code); } } if (opts.sourceMaps === true) { try { expect(result.map).toEqual(task.sourceMap); } catch (e) { if (!process.env.OVERWRITE && task.sourceMap) throw e; console.log(`Updated test file: ${task.sourceMapFile.loc}`); _fs.writeFileSync(task.sourceMapFile.loc, JSON.stringify(result.map, null, 2)); } } if (execCode && resultExec) { return resultExec; } }); return _run.apply(this, arguments); } function validateFile(actualCode, expectedLoc, expectedCode) { try { expect(actualCode).toEqualFile({ filename: expectedLoc, code: expectedCode }); } catch (e) { if (!process.env.OVERWRITE) throw e; console.log(`Updated test file: ${expectedLoc}`); _fs.writeFileSync(expectedLoc, `${actualCode}\n`); } } function escapeRegExp(string) { return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&"); } function normalizeOutput(code, { normalizePathSeparator = false, normalizePresetEnvDebug = false } = {}) { const projectRoot = _path.resolve(_path.dirname(__filename), "../../../"); const cwdSymbol = "<CWD>"; let result = code.trim().replace(new RegExp(escapeRegExp(projectRoot), "g"), cwdSymbol); if (process.platform === "win32") { result = result.replace(new RegExp(escapeRegExp(projectRoot.replace(/\\/g, "/")), "g"), cwdSymbol).replace(new RegExp(escapeRegExp(projectRoot.replace(/\\/g, "\\\\")), "g"), cwdSymbol); if (normalizePathSeparator) { result = result.replace(/<CWD>[\w\\/.-]+/g, path => path.replace(/\\\\?/g, "/")); } } { if (normalizePresetEnvDebug) { result = result.replace(/(\s+)proposal-/gm, "$1transform-"); } if (parseInt(process.versions.node, 10) <= 8) { result = result.replace(/<CWD>\/node_modules\/@babel\/runtime-corejs3/g, "<CWD>/packages/babel-runtime-corejs3"); } } return result; } expect.extend({ toEqualFile(actual, { filename, code }) { if (this.isNot) { throw new Error(".toEqualFile does not support negation"); } const pass = actual === code; return { pass, message: () => { const diffString = this.utils.diff(code, actual, { expand: false }); return `Expected ${filename} to match transform output.\n` + `To autogenerate a passing version of this file, delete the file and re-run the tests.\n\n` + `Diff:\n\n${diffString}`; } }; } }); function _default(fixturesLoc, name, suiteOpts = {}, taskOpts = {}, dynamicOpts) { const suites = (0, _helperFixtures.default)(fixturesLoc); for (const testSuite of suites) { var _suiteOpts$ignoreSuit; if ((_suiteOpts$ignoreSuit = suiteOpts.ignoreSuites) != null && _suiteOpts$ignoreSuit.includes(testSuite.title)) continue; describe(name + "/" + testSuite.title, function () { for (const task of testSuite.tests) { var _suiteOpts$ignoreTask, _suiteOpts$ignoreTask2; if ((_suiteOpts$ignoreTask = suiteOpts.ignoreTasks) != null && _suiteOpts$ignoreTask.includes(task.title) || (_suiteOpts$ignoreTask2 = suiteOpts.ignoreTasks) != null && _suiteOpts$ignoreTask2.includes(testSuite.title + "/" + task.title)) { continue; } const testFn = task.disabled ? it.skip : it; testFn(task.title, _asyncToGenerator(function* () { const runTask = () => run(task); if ("sourceMap" in task.options) { throw new Error("`sourceMap` option is deprecated. Use `sourceMaps` instead."); } if ("sourceMaps" in task.options === false) { task.options.sourceMaps = !!task.sourceMap; } Object.assign(task.options, taskOpts); if (dynamicOpts) dynamicOpts(task.options, task); if (task.externalHelpers) { var _task$options, _task$options$plugins; ((_task$options$plugins = (_task$options = task.options).plugins) != null ? _task$options$plugins : _task$options.plugins = []).push(["external-helpers", { helperVersion: EXTERNAL_HELPERS_VERSION }]); } const throwMsg = task.options.throws; if (throwMsg) { delete task.options.throws; yield _assert.rejects(runTask, function (err) { _assert.ok(throwMsg === true || err.message.includes(throwMsg), ` Expected Error: ${throwMsg} Actual Error: ${err.message}`); return true; }); } else { return runTask(); } })); } }); } } //# sourceMappingURL=index.js.map