Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 227 additions & 0 deletions test/flags.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import { expect, it, describe, vi, beforeEach, afterEach } from "vitest";

const flagEnvKeys = [
"CI",
"NODE_ENV",
"MODE",
"TEST",
"DEBUG",
"MINIMAL",
"NO_COLOR",
"FORCE_COLOR",
"TERM",
"GITHUB_ACTIONS",
"VERCEL",
"VERCEL_ENV",
"NOW_BUILDER",
"GITLAB_CI",
"TRAVIS",
"CIRCLECI",
"BUILDKITE",
"APPVEYOR",
"CODESANDBOX_SSE",
"CODESANDBOX_HOST",
];
Comment on lines +3 to +24
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Isolate the full provider env surface in test setup.

detectProvider() scans many more env names than the subset cleared here. If these tests run on an unlisted CI provider, the leaked env will make the "no CI" cases fail and can also flip isMinimal / isColorSupported. Please either neutralize every provider key from src/providers.ts or replace process.env with an isolated test env for this suite.

Also applies to: 32-36

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/flags.test.ts` around lines 3 - 24, The test only clears a subset of env
keys so detectProvider (in src/providers.ts) can still see other
provider-related variables and flip behavior for isMinimal/isColorSupported;
update the test setup in test/flags.test.ts to either 1) neutralize every
provider env name exported/used by detectProvider by importing the provider key
list from src/providers.ts and clearing them from process.env before each test,
or 2) replace process.env entirely for the suite with an isolated copy (e.g.,
save original = process.env, set process.env = {} or a controlled object, then
restore original after tests) so detectProvider, isMinimal and isColorSupported
see a deterministic environment. Ensure you reference detectProvider and the
provider keys from src/providers.ts so new provider env names are automatically
handled.


async function importFlags() {
vi.resetModules();
return import("../src/flags.ts");
}

describe("flags", () => {
beforeEach(() => {
for (const key of flagEnvKeys) {
vi.stubEnv(key, "");
}
});

afterEach(() => {
vi.unstubAllEnvs();
});

describe("isCI", () => {
it("is true when CI env var is set", async () => {
vi.stubEnv("CI", "true");
const { isCI } = await importFlags();
expect(isCI).toBe(true);
});

it("is true when a CI provider is detected", async () => {
vi.stubEnv("GITHUB_ACTIONS", "true");
const { isCI } = await importFlags();
expect(isCI).toBe(true);
});

it("is false when only a non-CI provider is detected", async () => {
vi.stubEnv("VERCEL", "1");
const { isCI } = await importFlags();
expect(isCI).toBe(false);
});

it("is false when no CI env or provider is set", async () => {
const { isCI } = await importFlags();
expect(isCI).toBe(false);
});
});

describe("isTest", () => {
it("is true when NODE_ENV is test", async () => {
vi.stubEnv("NODE_ENV", "test");
const { isTest } = await importFlags();
expect(isTest).toBe(true);
});

it("is true when TEST env var is set", async () => {
vi.stubEnv("TEST", "1");
const { isTest } = await importFlags();
expect(isTest).toBe(true);
});

it("is false when neither NODE_ENV=test nor TEST is set", async () => {
const { isTest } = await importFlags();
expect(isTest).toBe(false);
});
});

describe("isDevelopment", () => {
it("is true when NODE_ENV is development", async () => {
vi.stubEnv("NODE_ENV", "development");
const { isDevelopment } = await importFlags();
expect(isDevelopment).toBe(true);
});

it("is true when NODE_ENV is dev", async () => {
vi.stubEnv("NODE_ENV", "dev");
const { isDevelopment } = await importFlags();
expect(isDevelopment).toBe(true);
});

it("is true when MODE is development", async () => {
vi.stubEnv("MODE", "development");
const { isDevelopment } = await importFlags();
expect(isDevelopment).toBe(true);
});

it("is false when no development env is set", async () => {
const { isDevelopment } = await importFlags();
expect(isDevelopment).toBe(false);
});
});

describe("isProduction", () => {
it("is true when NODE_ENV is production", async () => {
vi.stubEnv("NODE_ENV", "production");
const { isProduction } = await importFlags();
expect(isProduction).toBe(true);
});

it("is true when MODE is production", async () => {
vi.stubEnv("MODE", "production");
const { isProduction } = await importFlags();
expect(isProduction).toBe(true);
});

it("is false when no production env is set", async () => {
const { isProduction } = await importFlags();
expect(isProduction).toBe(false);
});
});

describe("isDebug", () => {
it("is true when DEBUG is set", async () => {
vi.stubEnv("DEBUG", "true");
const { isDebug } = await importFlags();
expect(isDebug).toBe(true);
});

it("is true for any truthy DEBUG value", async () => {
vi.stubEnv("DEBUG", "*");
const { isDebug } = await importFlags();
expect(isDebug).toBe(true);
});

it("is false when DEBUG is not set", async () => {
const { isDebug } = await importFlags();
expect(isDebug).toBe(false);
});
});

describe("isMinimal", () => {
it("is true when MINIMAL env is set", async () => {
vi.stubEnv("MINIMAL", "1");
const { isMinimal } = await importFlags();
expect(isMinimal).toBe(true);
});

it("is true when running in CI", async () => {
vi.stubEnv("CI", "true");
const { isMinimal } = await importFlags();
expect(isMinimal).toBe(true);
});

it("is true when NODE_ENV is test", async () => {
vi.stubEnv("NODE_ENV", "test");
const { isMinimal } = await importFlags();
expect(isMinimal).toBe(true);
});
});

describe("isColorSupported", () => {
it("is false when NO_COLOR is set", async () => {
vi.stubEnv("NO_COLOR", "1");
vi.stubEnv("FORCE_COLOR", "1");
const { isColorSupported } = await importFlags();
expect(isColorSupported).toBe(false);
});

it("is true when FORCE_COLOR is set", async () => {
vi.stubEnv("FORCE_COLOR", "1");
const { isColorSupported } = await importFlags();
expect(isColorSupported).toBe(true);
});

it("is true in CI", async () => {
vi.stubEnv("CI", "true");
const { isColorSupported } = await importFlags();
expect(isColorSupported).toBe(true);
});
});

describe("nodeVersion", () => {
it("returns a semver string", async () => {
const { nodeVersion } = await importFlags();
expect(nodeVersion).toMatch(/^\d+\.\d+\.\d+/);
});

it("strips the v prefix", async () => {
const { nodeVersion } = await importFlags();
expect(nodeVersion).not.toMatch(/^v/);
});
});

describe("nodeMajorVersion", () => {
it("returns a number", async () => {
const { nodeMajorVersion } = await importFlags();
expect(nodeMajorVersion).toBeTypeOf("number");
});

it("matches the major part of nodeVersion", async () => {
const { nodeVersion, nodeMajorVersion } = await importFlags();
expect(nodeMajorVersion).toBe(Number(nodeVersion!.split(".")[0]));
});
});

describe("platform flags", () => {
it("platform is a non-empty string", async () => {
const { platform } = await importFlags();
expect(platform).toBeTypeOf("string");
expect(platform.length).toBeGreaterThan(0);
});

it("at most one OS flag is true", async () => {
const { isWindows, isLinux, isMacOS } = await importFlags();
const trueCount = [isWindows, isLinux, isMacOS].filter(Boolean).length;
expect(trueCount).toBeLessThanOrEqual(1);
});
});
});