diff --git a/.changeset/nervous-donkeys-hammer.md b/.changeset/nervous-donkeys-hammer.md new file mode 100644 index 0000000..1450803 --- /dev/null +++ b/.changeset/nervous-donkeys-hammer.md @@ -0,0 +1,5 @@ +--- +"@fixture-kit/core": patch +--- + +Keep relative symlinks inside the copy in `Fixture.fromDirectory`. `fs.cp` rewrites relative symlink targets to absolute paths unless `verbatimSymlinks` is set, so a link in the source directory pointed back at the source after the copy. diff --git a/src/fixture.spec.ts b/src/fixture.spec.ts index cb21977..2614606 100644 --- a/src/fixture.spec.ts +++ b/src/fixture.spec.ts @@ -3,6 +3,7 @@ import { Fixture } from "./fixture.js"; import path from "node:path"; import { fileURLToPath } from "node:url"; import fs from "node:fs/promises"; +import os from "node:os"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const testFixture = (name: string) => path.join(__dirname, "..", "fixtures", name); @@ -34,6 +35,29 @@ describe("Fixture", () => { await fs.readFile(path.join(testFixture("basic"), "src/index.ts"), "utf-8"), ); }); + + // Windows needs an elevated process to create symlinks, so this runs on POSIX only. + it.skipIf(process.platform === "win32")( + "should keep a relative symlink pointing inside the copy", + async () => { + const source = await fs.mkdtemp(path.join(os.tmpdir(), "fixture-kit-source-")); + + try { + await fs.writeFile(path.join(source, "target.txt"), "original"); + await fs.symlink("target.txt", path.join(source, "link.txt")); + + await using fixture = await Fixture.fromDirectory(source); + await fs.writeFile(path.join(fixture.root, "link.txt"), "written through the link"); + + expect(await fs.readFile(path.join(fixture.root, "target.txt"), "utf-8")).toBe( + "written through the link", + ); + expect(await fs.readFile(path.join(source, "target.txt"), "utf-8")).toBe("original"); + } finally { + await fs.rm(source, { recursive: true, force: true }); + } + }, + ); }); describe("create", () => { diff --git a/src/fixture.ts b/src/fixture.ts index 3a45ecb..0f76ca5 100644 --- a/src/fixture.ts +++ b/src/fixture.ts @@ -32,7 +32,7 @@ export class Fixture implements AsyncDisposable { throw new Error(`source must be a directory: ${sourcePath}`); } - await fs.cp(sourcePath, fixture.root, { recursive: true }); + await fs.cp(sourcePath, fixture.root, { recursive: true, verbatimSymlinks: true }); return fixture; } catch (error) {