chore: import upstream snapshot with attribution
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Coverage for the init scaffolder's environment probe.
|
||||
*
|
||||
* The probe reads three external sources: git config, git remote, and
|
||||
* package.json. Tests focus on the parts we control directly — repo
|
||||
* URL normalisation and package.json field extraction. The git-config
|
||||
* and git-remote subprocess calls are tested indirectly via `init`
|
||||
* integration tests; mocking child_process gets brittle fast and the
|
||||
* subprocess wrapper is a thin layer that doesn't have much to fail.
|
||||
*/
|
||||
|
||||
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
import { probeEnvironment } from "../src/init/environment.js";
|
||||
|
||||
describe("probeEnvironment — package.json extraction", () => {
|
||||
let dir: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
dir = await mkdtemp(join(tmpdir(), "emdash-env-test-"));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await rm(dir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("returns all-undefined for a directory with no package.json", async () => {
|
||||
const env = await probeEnvironment(dir);
|
||||
expect(env.license).toBeUndefined();
|
||||
expect(env.description).toBeUndefined();
|
||||
// Note: authorName / authorEmail may be set from the user's
|
||||
// global git config; we don't assert on them here. The package
|
||||
// extraction is the focus.
|
||||
});
|
||||
|
||||
it("reads license, description, and repository from package.json", async () => {
|
||||
await writeFile(
|
||||
join(dir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "test",
|
||||
license: "Apache-2.0",
|
||||
description: "A test plugin",
|
||||
repository: "https://github.com/example/test",
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
const env = await probeEnvironment(dir);
|
||||
expect(env.license).toBe("Apache-2.0");
|
||||
expect(env.description).toBe("A test plugin");
|
||||
expect(env.repo).toBe("https://github.com/example/test");
|
||||
});
|
||||
|
||||
it("normalizes git@github.com SSH URLs to https", async () => {
|
||||
await writeFile(
|
||||
join(dir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "test",
|
||||
repository: { type: "git", url: "git+ssh://git@github.com:example/test.git" },
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
// SSH URL with the `ssh://` scheme prefix isn't the shape our
|
||||
// normaliser handles — the user-facing common case is
|
||||
// `git@host:path`, not `ssh://git@host/path`. The probe
|
||||
// returns undefined; the prompt's fallback chain handles it.
|
||||
const env = await probeEnvironment(dir);
|
||||
expect(env.repo).toBeUndefined();
|
||||
});
|
||||
|
||||
it("strips the .git suffix and the git+ prefix from package.json#repository.url", async () => {
|
||||
await writeFile(
|
||||
join(dir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "test",
|
||||
repository: { type: "git", url: "git+https://github.com/example/test.git" },
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
const env = await probeEnvironment(dir);
|
||||
expect(env.repo).toBe("https://github.com/example/test");
|
||||
});
|
||||
|
||||
it("treats a malformed package.json as missing", async () => {
|
||||
await writeFile(join(dir, "package.json"), "{ not valid json", "utf8");
|
||||
const env = await probeEnvironment(dir);
|
||||
expect(env.license).toBeUndefined();
|
||||
expect(env.description).toBeUndefined();
|
||||
expect(env.repo).toBeUndefined();
|
||||
});
|
||||
|
||||
it("treats a non-object package.json as missing", async () => {
|
||||
// Valid JSON but not an object — pathological but possible.
|
||||
await writeFile(join(dir, "package.json"), "[]", "utf8");
|
||||
const env = await probeEnvironment(dir);
|
||||
expect(env.license).toBeUndefined();
|
||||
expect(env.description).toBeUndefined();
|
||||
});
|
||||
|
||||
it("ignores oversized package.json (defence against weird files)", async () => {
|
||||
// Build a 100 KiB file. The cap is 64 KiB; the probe should
|
||||
// skip rather than buffer the whole thing.
|
||||
const fluff = " ".repeat(100 * 1024);
|
||||
await writeFile(
|
||||
join(dir, "package.json"),
|
||||
`{ "license": "MIT", "_fluff": "${fluff}" }`,
|
||||
"utf8",
|
||||
);
|
||||
const env = await probeEnvironment(dir);
|
||||
expect(env.license).toBeUndefined();
|
||||
});
|
||||
|
||||
it("treats empty-string license / description as undefined", async () => {
|
||||
await writeFile(
|
||||
join(dir, "package.json"),
|
||||
JSON.stringify({ name: "test", license: "", description: " " }),
|
||||
"utf8",
|
||||
);
|
||||
const env = await probeEnvironment(dir);
|
||||
expect(env.license).toBeUndefined();
|
||||
expect(env.description).toBeUndefined();
|
||||
});
|
||||
|
||||
it("trims whitespace from license / description values", async () => {
|
||||
await writeFile(
|
||||
join(dir, "package.json"),
|
||||
JSON.stringify({ name: "test", license: " MIT ", description: " hello " }),
|
||||
"utf8",
|
||||
);
|
||||
const env = await probeEnvironment(dir);
|
||||
expect(env.license).toBe("MIT");
|
||||
expect(env.description).toBe("hello");
|
||||
});
|
||||
|
||||
it("accepts repository as a bare string", async () => {
|
||||
await writeFile(
|
||||
join(dir, "package.json"),
|
||||
JSON.stringify({ name: "test", repository: "https://github.com/example/test.git" }),
|
||||
"utf8",
|
||||
);
|
||||
const env = await probeEnvironment(dir);
|
||||
expect(env.repo).toBe("https://github.com/example/test");
|
||||
});
|
||||
|
||||
it("returns undefined for unrecognised repository shapes", async () => {
|
||||
await writeFile(
|
||||
join(dir, "package.json"),
|
||||
JSON.stringify({ name: "test", repository: { type: "git" /* no url */ } }),
|
||||
"utf8",
|
||||
);
|
||||
const env = await probeEnvironment(dir);
|
||||
expect(env.repo).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user