UNPKG

@backstage/backend-test-utils

Version:

Test helpers library for Backstage backends

174 lines (168 loc) • 5.62 kB
'use strict'; var os = require('os'); var backendPluginApi = require('@backstage/backend-plugin-api'); var fs = require('fs-extra'); var textextensions = require('textextensions'); var path = require('path'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } var os__default = /*#__PURE__*/_interopDefaultCompat(os); var fs__default = /*#__PURE__*/_interopDefaultCompat(fs); var textextensions__default = /*#__PURE__*/_interopDefaultCompat(textextensions); const tmpdirMarker = Symbol("os-tmpdir-mock"); class MockDirectoryImpl { #root; constructor(root) { this.#root = root; } get path() { return this.#root; } resolve(...paths) { return path.resolve(this.#root, ...paths); } setContent(root) { this.remove(); return this.addContent(root); } addContent(root) { const entries = this.#transformInput(root); for (const entry of entries) { const fullPath = path.resolve(this.#root, entry.path); if (!backendPluginApi.isChildPath(this.#root, fullPath)) { throw new Error( `Provided path must resolve to a child path of the mock directory, got '${fullPath}'` ); } if (entry.type === "dir") { fs__default.default.ensureDirSync(fullPath); } else if (entry.type === "file") { fs__default.default.ensureDirSync(path.dirname(fullPath)); fs__default.default.writeFileSync(fullPath, entry.content); } else if (entry.type === "callback") { fs__default.default.ensureDirSync(path.dirname(fullPath)); entry.callback({ path: fullPath, symlink(target) { fs__default.default.symlinkSync(target, fullPath); } }); } } } content(options) { const shouldReadAsText = (typeof options?.shouldReadAsText === "boolean" ? () => options?.shouldReadAsText : options?.shouldReadAsText) ?? ((path$1) => textextensions__default.default.includes(path.extname(path$1).slice(1))); const root = path.resolve(this.#root, options?.path ?? ""); if (!backendPluginApi.isChildPath(this.#root, root)) { throw new Error( `Provided path must resolve to a child path of the mock directory, got '${root}'` ); } function read(path$1) { if (!fs__default.default.pathExistsSync(path$1)) { return void 0; } const entries = fs__default.default.readdirSync(path$1, { withFileTypes: true }); return Object.fromEntries( entries.map((entry) => { const fullPath = path.resolve(path$1, entry.name); if (entry.isDirectory()) { return [entry.name, read(fullPath)]; } const content = fs__default.default.readFileSync(fullPath); const relativePosixPath = path.relative(root, fullPath).split(path.win32.sep).join(path.posix.sep); if (shouldReadAsText(relativePosixPath, content)) { return [entry.name, content.toString("utf8")]; } return [entry.name, content]; }) ); } return read(root); } clear = () => { this.setContent({}); }; remove = () => { fs__default.default.rmSync(this.#root, { recursive: true, force: true, maxRetries: 10 }); }; #transformInput(input) { const entries = []; function traverse(node, path) { if (typeof node === "string") { entries.push({ type: "file", path, content: Buffer.from(node, "utf8") }); } else if (node instanceof Buffer) { entries.push({ type: "file", path, content: node }); } else if (typeof node === "function") { entries.push({ type: "callback", path, callback: node }); } else { entries.push({ type: "dir", path }); for (const [name, child] of Object.entries(node)) { traverse(child, path ? `${path}/${name}` : name); } } } traverse(input, ""); return entries; } } const cleanupCallbacks = new Array(); let registered = false; function registerTestHooks() { if (typeof afterAll !== "function") { return; } if (registered) { return; } registered = true; afterAll(async () => { for (const callback of cleanupCallbacks) { try { callback(); } catch (error) { console.error( `Failed to clean up mock directory after tests, ${error}` ); } } cleanupCallbacks.length = 0; }); } registerTestHooks(); function createMockDirectory(options) { const tmpDir = process.env.RUNNER_TEMP || os__default.default.tmpdir(); const root = fs__default.default.mkdtempSync(path.join(tmpDir, "backstage-tmp-test-dir-")); const mocker = new MockDirectoryImpl(root); const origTmpdir = options?.mockOsTmpDir ? os__default.default.tmpdir : void 0; if (origTmpdir) { if (Object.hasOwn(origTmpdir, tmpdirMarker)) { throw new Error( "Cannot mock os.tmpdir() when it has already been mocked" ); } const mock = Object.assign(() => mocker.path, { [tmpdirMarker]: true }); os__default.default.tmpdir = mock; } const needsCleanup = !process.env.CI; if (needsCleanup) { process.on("beforeExit", mocker.remove); } if (needsCleanup) { cleanupCallbacks.push(() => mocker.remove()); } if (origTmpdir) { afterAll(() => { os__default.default.tmpdir = origTmpdir; }); } if (options?.content) { mocker.setContent(options.content); } return mocker; } exports.createMockDirectory = createMockDirectory; //# sourceMappingURL=MockDirectory.cjs.js.map