import readdir from "fs-readdir-recursive"; import * as helper from "@babel/helper-fixtures"; import rimraf from "rimraf"; import child from "child_process"; import path from "path"; import fs from "fs"; import { fileURLToPath } from "url"; import { createRequire } from "module"; const require = createRequire(import.meta.url); const dirname = path.dirname(fileURLToPath(import.meta.url)); const fixtureLoc = path.join(dirname, "fixtures", "cli"); const tmpLoc = path.join(dirname, "tmp"); const binLoc = path.join(dirname, "../lib/babel-node"); const fileFilter = function (x) { return x !== ".DS_Store"; }; const outputFileSync = function (filePath, data) { fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, data); }; const readDir = function (loc, filter) { const files = {}; if (fs.existsSync(loc)) { readdir(loc, filter).forEach(function (filename) { files[filename] = helper.readFile(path.join(loc, filename)); }); } return files; }; const saveInFiles = function (files) { Object.keys(files).forEach(function (filename) { const content = files[filename]; outputFileSync(path.join(tmpLoc, filename), content); }); }; const assertTest = function (stdout, stderr, ipcMessage, opts) { const expectStderr = opts.stderr.trim(); stderr = stderr.trim(); if (opts.stderr) { if (opts.stderrContains) { expect(stderr.includes(expectStderr)).toBeTruthy(); } else { expect(stderr).toBe(expectStderr); } } else if (stderr) { throw new Error("stderr:\n" + stderr + "\n\nstdout:\n" + stdout); } const expectStdout = opts.stdout.trim(); stdout = stdout.trim(); stdout = stdout.replace(/\\/g, "/"); if (opts.stdout) { if (opts.stdoutContains) { expect(stdout.includes(expectStdout)).toBeTruthy(); } else { expect(stdout).toBe(expectStdout); } } else if (stdout) { throw new Error("stdout:\n" + stdout); } if (opts.ipc) { expect(ipcMessage).toEqual(opts.ipcMessage); } if (opts.outFiles) { // For some reaons, on GH actions always appears a file called "null" ¯\_(ツ)_/¯ const actualFiles = readDir(tmpLoc, name => name !== "null"); Object.keys(actualFiles).forEach(function (filename) { if (!Object.prototype.hasOwnProperty.call(opts.inFiles, filename)) { const expected = opts.outFiles[filename]; const actual = actualFiles[filename]; if (expected == null) { throw new Error("Unexpected generated file: " + filename); } if (expected) { expect(actual).toBe(expected); } } }); Object.keys(opts.outFiles).forEach(function (filename) { expect(actualFiles).toHaveProperty(filename); }); } }; const buildTest = function (testName, opts) { return function (callback) { saveInFiles(opts.inFiles); const args = [binLoc].concat(opts.args); const spawnOpts = { cwd: tmpLoc, env: { BABEL_DISABLE_CACHE: true } }; if (opts.ipc) { spawnOpts.stdio = ["pipe", "pipe", "pipe", "ipc"]; } const spawn = child.spawn(process.execPath, args, spawnOpts); let stderr = ""; let stdout = ""; let ipcMessage; spawn.stderr.on("data", function (chunk) { stderr += chunk; }); spawn.stdout.on("data", function (chunk) { stdout += chunk; }); if (opts.ipc) { spawn.on("message", function (message) { ipcMessage = message; }); } spawn.on("close", function () { let err; try { assertTest(stdout, stderr, ipcMessage, opts); } catch (e) { err = e; err.message = args.map(arg => `"${arg}"`).join(" ") + ": " + err.message; } callback(err); }); if (opts.stdin) { spawn.stdin.write(opts.stdin); spawn.stdin.end(); } }; }; describe("bin/babel-node", function () { beforeEach(() => { if (fs.existsSync(tmpLoc)) { rimraf.sync(tmpLoc); } fs.mkdirSync(tmpLoc); }); fs.readdirSync(fixtureLoc).forEach(function (testName) { if (testName[0] === ".") return; const testLoc = path.join(fixtureLoc, testName); const opts = { args: [], }; const optionsLoc = path.join(testLoc, "options.json"); if (fs.existsSync(optionsLoc)) Object.assign(opts, require(optionsLoc)); ["stdout", "stdin", "stderr"].forEach(function (key) { const loc = path.join(testLoc, key + ".txt"); if (fs.existsSync(loc)) { opts[key] = helper.readFile(loc); } else { opts[key] = opts[key] || ""; } }); opts.outFiles = readDir(path.join(testLoc, "out-files"), fileFilter); opts.inFiles = readDir(path.join(testLoc, "in-files"), fileFilter); const babelrcLoc = path.join(testLoc, ".babelrc"); if (fs.existsSync(babelrcLoc)) { // copy .babelrc file to tmp directory opts.inFiles[".babelrc"] = helper.readFile(babelrcLoc); } else { // Place an empty .babelrc in each test so tests won't unexpectedly get to repo-level config. opts.inFiles[".babelrc"] = "{}"; } if (!opts.inFiles["package.json"]) { opts.inFiles["package.json"] = `{ "type": "commonjs" }`; } // eslint-disable-next-line jest/valid-title it(testName, buildTest(testName, opts), 20000); }); });