@backstage/backend-test-utils
Version:
Test helpers library for Backstage backends
174 lines (168 loc) • 5.62 kB
JavaScript
;
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