mirror of https://github.com/openclaw/openclaw.git
refactor: route direct extension test targets
This commit is contained in:
parent
d0d5b34b44
commit
1bee69f79b
|
|
@ -25,6 +25,7 @@ Most days:
|
|||
- Full gate (expected before push): `pnpm build && pnpm check && pnpm test`
|
||||
- Faster local full-suite run on a roomy machine: `pnpm test:max`
|
||||
- Direct Vitest watch loop (modern projects config): `pnpm test:watch`
|
||||
- Direct file targeting now routes extension/channel paths too: `pnpm test -- extensions/discord/src/monitor/message-handler.preflight.test.ts`
|
||||
|
||||
When you touch tests or want extra confidence:
|
||||
|
||||
|
|
@ -57,7 +58,8 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
|
|||
- Should be fast and stable
|
||||
- Projects note:
|
||||
- `pnpm test` and `pnpm test:watch` both use the same native Vitest `projects` config now.
|
||||
- The tiny script wrapper only strips pnpm's passthrough separator; scheduling stays native Vitest.
|
||||
- The tiny script wrapper still keeps scheduling native, but it now reroutes direct `extensions/...` and channel-surface test paths onto the matching Vitest lane automatically.
|
||||
- If you target mixed suites in one command, the wrapper runs those lanes sequentially under the same local heavy-check lock.
|
||||
- Embedded runner note:
|
||||
- When you change message-tool discovery inputs or compaction runtime context,
|
||||
keep both levels of coverage.
|
||||
|
|
|
|||
|
|
@ -121,6 +121,50 @@ function isBoundThreadBotSystemMessage(params: {
|
|||
return DISCORD_BOUND_THREAD_SYSTEM_PREFIXES.some((prefix) => text.startsWith(prefix));
|
||||
}
|
||||
|
||||
function resolveDiscordMentionState(params: {
|
||||
authorIsBot: boolean;
|
||||
botId?: string;
|
||||
hasAnyMention: boolean;
|
||||
isDirectMessage: boolean;
|
||||
isExplicitlyMentioned: boolean;
|
||||
mentionRegexes: RegExp[];
|
||||
mentionText: string;
|
||||
mentionedEveryone: boolean;
|
||||
referencedAuthorId?: string;
|
||||
senderIsPluralKit: boolean;
|
||||
transcript?: string;
|
||||
}): { implicitMention: boolean; wasMentioned: boolean } {
|
||||
if (params.isDirectMessage) {
|
||||
return {
|
||||
implicitMention: false,
|
||||
wasMentioned: false,
|
||||
};
|
||||
}
|
||||
|
||||
const everyoneMentioned =
|
||||
params.mentionedEveryone && (!params.authorIsBot || params.senderIsPluralKit);
|
||||
const wasMentioned =
|
||||
everyoneMentioned ||
|
||||
matchesMentionWithExplicit({
|
||||
text: params.mentionText,
|
||||
mentionRegexes: params.mentionRegexes,
|
||||
explicit: {
|
||||
hasAnyMention: params.hasAnyMention,
|
||||
isExplicitlyMentioned: params.isExplicitlyMentioned,
|
||||
canResolveExplicit: Boolean(params.botId),
|
||||
},
|
||||
transcript: params.transcript,
|
||||
});
|
||||
const implicitMention = Boolean(
|
||||
params.botId && params.referencedAuthorId && params.referencedAuthorId === params.botId,
|
||||
);
|
||||
|
||||
return {
|
||||
implicitMention,
|
||||
wasMentioned,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolvePreflightMentionRequirement(params: {
|
||||
shouldRequireMention: boolean;
|
||||
bypassMentionRequirement: boolean;
|
||||
|
|
@ -751,25 +795,19 @@ export async function preflightDiscordMessage(
|
|||
}
|
||||
|
||||
const mentionText = hasTypedText ? baseText : "";
|
||||
const wasMentioned =
|
||||
!isDirectMessage &&
|
||||
(((!author.bot || sender.isPluralKit) && Boolean(message.mentionedEveryone)) ||
|
||||
matchesMentionWithExplicit({
|
||||
text: mentionText,
|
||||
mentionRegexes,
|
||||
explicit: {
|
||||
hasAnyMention,
|
||||
isExplicitlyMentioned: explicitlyMentioned,
|
||||
canResolveExplicit: Boolean(botId),
|
||||
},
|
||||
transcript: preflightTranscript,
|
||||
}));
|
||||
const implicitMention = Boolean(
|
||||
!isDirectMessage &&
|
||||
botId &&
|
||||
message.referencedMessage?.author?.id &&
|
||||
message.referencedMessage.author.id === botId,
|
||||
);
|
||||
const { implicitMention, wasMentioned } = resolveDiscordMentionState({
|
||||
authorIsBot: Boolean(author.bot),
|
||||
botId,
|
||||
hasAnyMention,
|
||||
isDirectMessage,
|
||||
isExplicitlyMentioned: explicitlyMentioned,
|
||||
mentionRegexes,
|
||||
mentionText,
|
||||
mentionedEveryone: Boolean(message.mentionedEveryone),
|
||||
referencedAuthorId: message.referencedMessage?.author?.id,
|
||||
senderIsPluralKit: sender.isPluralKit,
|
||||
transcript: preflightTranscript,
|
||||
});
|
||||
if (shouldLogVerbose()) {
|
||||
logVerbose(
|
||||
`discord: inbound id=${message.id} guild=${params.data.guild_id ?? "dm"} channel=${messageChannelId} mention=${wasMentioned ? "yes" : "no"} type=${isDirectMessage ? "dm" : isGroupDm ? "group-dm" : "guild"} content=${messageText ? "yes" : "no"}`,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import fs from "node:fs";
|
||||
import { acquireLocalHeavyCheckLockSync } from "./lib/local-heavy-check-runtime.mjs";
|
||||
import { spawnPnpmRunner } from "./pnpm-runner.mjs";
|
||||
import { buildVitestArgs } from "./test-projects.test-support.mjs";
|
||||
import { createVitestRunSpecs, writeVitestIncludeFile } from "./test-projects.test-support.mjs";
|
||||
|
||||
const vitestArgs = buildVitestArgs(process.argv.slice(2));
|
||||
const releaseLock = acquireLocalHeavyCheckLockSync({
|
||||
cwd: process.cwd(),
|
||||
env: process.env,
|
||||
|
|
@ -18,21 +18,62 @@ const releaseLockOnce = () => {
|
|||
releaseLock();
|
||||
};
|
||||
|
||||
const child = spawnPnpmRunner({
|
||||
pnpmArgs: vitestArgs,
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
child.on("exit", (code, signal) => {
|
||||
releaseLockOnce();
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
function cleanupVitestRunSpec(spec) {
|
||||
if (!spec.includeFilePath) {
|
||||
return;
|
||||
}
|
||||
process.exit(code ?? 1);
|
||||
});
|
||||
try {
|
||||
fs.rmSync(spec.includeFilePath, { force: true });
|
||||
} catch {
|
||||
// Best-effort cleanup for temp include lists.
|
||||
}
|
||||
}
|
||||
|
||||
child.on("error", (error) => {
|
||||
function runVitestSpec(spec) {
|
||||
if (spec.includeFilePath && spec.includePatterns) {
|
||||
writeVitestIncludeFile(spec.includeFilePath, spec.includePatterns);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawnPnpmRunner({
|
||||
pnpmArgs: spec.pnpmArgs,
|
||||
env: spec.env,
|
||||
});
|
||||
|
||||
child.on("exit", (code, signal) => {
|
||||
cleanupVitestRunSpec(spec);
|
||||
resolve({ code: code ?? 1, signal });
|
||||
});
|
||||
|
||||
child.on("error", (error) => {
|
||||
cleanupVitestRunSpec(spec);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const runSpecs = createVitestRunSpecs(process.argv.slice(2), {
|
||||
baseEnv: process.env,
|
||||
cwd: process.cwd(),
|
||||
});
|
||||
|
||||
for (const spec of runSpecs) {
|
||||
const result = await runVitestSpec(spec);
|
||||
if (result.signal) {
|
||||
releaseLockOnce();
|
||||
process.kill(process.pid, result.signal);
|
||||
return;
|
||||
}
|
||||
if (result.code !== 0) {
|
||||
releaseLockOnce();
|
||||
process.exit(result.code);
|
||||
}
|
||||
}
|
||||
|
||||
releaseLockOnce();
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
releaseLockOnce();
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
export type VitestRunPlan = {
|
||||
config: string;
|
||||
forwardedArgs: string[];
|
||||
includePatterns: string[] | null;
|
||||
watchMode: boolean;
|
||||
};
|
||||
|
||||
export type VitestRunSpec = {
|
||||
config: string;
|
||||
env: Record<string, string | undefined>;
|
||||
includeFilePath: string | null;
|
||||
includePatterns: string[] | null;
|
||||
pnpmArgs: string[];
|
||||
watchMode: boolean;
|
||||
};
|
||||
|
||||
export function parseTestProjectsArgs(
|
||||
args: string[],
|
||||
cwd?: string,
|
||||
): {
|
||||
forwardedArgs: string[];
|
||||
targetArgs: string[];
|
||||
watchMode: boolean;
|
||||
};
|
||||
|
||||
export function buildVitestRunPlans(args: string[], cwd?: string): VitestRunPlan[];
|
||||
|
||||
export function createVitestRunSpecs(
|
||||
args: string[],
|
||||
params?: {
|
||||
baseEnv?: Record<string, string | undefined>;
|
||||
cwd?: string;
|
||||
tempDir?: string;
|
||||
},
|
||||
): VitestRunSpec[];
|
||||
|
||||
export function writeVitestIncludeFile(filePath: string, includePatterns: string[]): void;
|
||||
|
||||
export function buildVitestArgs(args: string[], cwd?: string): string[];
|
||||
|
|
@ -1,5 +1,77 @@
|
|||
export function parseTestProjectsArgs(args) {
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { isChannelSurfaceTestFile } from "../vitest.channel-paths.mjs";
|
||||
|
||||
const DEFAULT_VITEST_CONFIG = "vitest.config.ts";
|
||||
const CHANNEL_VITEST_CONFIG = "vitest.channels.config.ts";
|
||||
const EXTENSIONS_VITEST_CONFIG = "vitest.extensions.config.ts";
|
||||
const INCLUDE_FILE_ENV_KEY = "OPENCLAW_VITEST_INCLUDE_FILE";
|
||||
|
||||
function normalizePathPattern(value) {
|
||||
return value.replaceAll("\\", "/");
|
||||
}
|
||||
|
||||
function isExistingPathTarget(arg, cwd) {
|
||||
return fs.existsSync(path.resolve(cwd, arg));
|
||||
}
|
||||
|
||||
function isGlobTarget(arg) {
|
||||
return /[*?[\]{}]/u.test(arg);
|
||||
}
|
||||
|
||||
function isFileLikeTarget(arg) {
|
||||
return /\.(?:test|spec)\.[cm]?[jt]sx?$/u.test(arg);
|
||||
}
|
||||
|
||||
function isPathLikeTargetArg(arg, cwd) {
|
||||
if (!arg || arg === "--" || arg.startsWith("-")) {
|
||||
return false;
|
||||
}
|
||||
return isExistingPathTarget(arg, cwd) || isGlobTarget(arg) || isFileLikeTarget(arg);
|
||||
}
|
||||
|
||||
function toRepoRelativeTarget(arg, cwd) {
|
||||
if (isGlobTarget(arg)) {
|
||||
return normalizePathPattern(arg.replace(/^\.\//u, ""));
|
||||
}
|
||||
const absolute = path.resolve(cwd, arg);
|
||||
return normalizePathPattern(path.relative(cwd, absolute));
|
||||
}
|
||||
|
||||
function toScopedIncludePattern(arg, cwd) {
|
||||
const relative = toRepoRelativeTarget(arg, cwd);
|
||||
if (isGlobTarget(relative) || isFileLikeTarget(relative)) {
|
||||
return relative;
|
||||
}
|
||||
return `${relative.replace(/\/+$/u, "")}/**/*.test.ts`;
|
||||
}
|
||||
|
||||
function classifyTarget(arg, cwd) {
|
||||
const relative = toRepoRelativeTarget(arg, cwd);
|
||||
if (relative.startsWith("extensions/")) {
|
||||
return isChannelSurfaceTestFile(relative) ? "channel" : "extension";
|
||||
}
|
||||
if (isChannelSurfaceTestFile(relative)) {
|
||||
return "channel";
|
||||
}
|
||||
return "default";
|
||||
}
|
||||
|
||||
function createVitestArgs(params) {
|
||||
return [
|
||||
"exec",
|
||||
"vitest",
|
||||
...(params.watchMode ? [] : ["run"]),
|
||||
"--config",
|
||||
params.config,
|
||||
...params.forwardedArgs,
|
||||
];
|
||||
}
|
||||
|
||||
export function parseTestProjectsArgs(args, cwd = process.cwd()) {
|
||||
const forwardedArgs = [];
|
||||
const targetArgs = [];
|
||||
let watchMode = false;
|
||||
|
||||
for (const arg of args) {
|
||||
|
|
@ -10,20 +82,109 @@ export function parseTestProjectsArgs(args) {
|
|||
watchMode = true;
|
||||
continue;
|
||||
}
|
||||
if (isPathLikeTargetArg(arg, cwd)) {
|
||||
targetArgs.push(arg);
|
||||
}
|
||||
forwardedArgs.push(arg);
|
||||
}
|
||||
|
||||
return { forwardedArgs, watchMode };
|
||||
return { forwardedArgs, targetArgs, watchMode };
|
||||
}
|
||||
|
||||
export function buildVitestArgs(args) {
|
||||
const { forwardedArgs, watchMode } = parseTestProjectsArgs(args);
|
||||
return [
|
||||
"exec",
|
||||
"vitest",
|
||||
...(watchMode ? [] : ["run"]),
|
||||
"--config",
|
||||
"vitest.config.ts",
|
||||
...forwardedArgs,
|
||||
];
|
||||
export function buildVitestRunPlans(args, cwd = process.cwd()) {
|
||||
const { forwardedArgs, targetArgs, watchMode } = parseTestProjectsArgs(args, cwd);
|
||||
if (targetArgs.length === 0) {
|
||||
return [
|
||||
{
|
||||
config: DEFAULT_VITEST_CONFIG,
|
||||
forwardedArgs,
|
||||
includePatterns: null,
|
||||
watchMode,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const groupedTargets = new Map();
|
||||
for (const targetArg of targetArgs) {
|
||||
const kind = classifyTarget(targetArg, cwd);
|
||||
const current = groupedTargets.get(kind) ?? [];
|
||||
current.push(targetArg);
|
||||
groupedTargets.set(kind, current);
|
||||
}
|
||||
|
||||
if (watchMode && groupedTargets.size > 1) {
|
||||
throw new Error(
|
||||
"watch mode with mixed test suites is not supported; target one suite at a time or use a dedicated suite command",
|
||||
);
|
||||
}
|
||||
|
||||
const nonTargetArgs = forwardedArgs.filter((arg) => !targetArgs.includes(arg));
|
||||
const orderedKinds = ["default", "channel", "extension"];
|
||||
const plans = [];
|
||||
for (const kind of orderedKinds) {
|
||||
const grouped = groupedTargets.get(kind);
|
||||
if (!grouped || grouped.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const config =
|
||||
kind === "channel"
|
||||
? CHANNEL_VITEST_CONFIG
|
||||
: kind === "extension"
|
||||
? EXTENSIONS_VITEST_CONFIG
|
||||
: DEFAULT_VITEST_CONFIG;
|
||||
const includePatterns =
|
||||
kind === "default"
|
||||
? null
|
||||
: grouped.map((targetArg) => toScopedIncludePattern(targetArg, cwd));
|
||||
const scopedTargetArgs = kind === "default" ? grouped : [];
|
||||
plans.push({
|
||||
config,
|
||||
forwardedArgs: [...nonTargetArgs, ...scopedTargetArgs],
|
||||
includePatterns,
|
||||
watchMode,
|
||||
});
|
||||
}
|
||||
return plans;
|
||||
}
|
||||
|
||||
export function createVitestRunSpecs(args, params = {}) {
|
||||
const cwd = params.cwd ?? process.cwd();
|
||||
const plans = buildVitestRunPlans(args, cwd);
|
||||
return plans.map((plan, index) => {
|
||||
const includeFilePath = plan.includePatterns
|
||||
? path.join(
|
||||
params.tempDir ?? os.tmpdir(),
|
||||
`openclaw-vitest-include-${process.pid}-${Date.now()}-${index}.json`,
|
||||
)
|
||||
: null;
|
||||
return {
|
||||
config: plan.config,
|
||||
env: includeFilePath
|
||||
? {
|
||||
...(params.baseEnv ?? process.env),
|
||||
[INCLUDE_FILE_ENV_KEY]: includeFilePath,
|
||||
}
|
||||
: (params.baseEnv ?? process.env),
|
||||
includeFilePath,
|
||||
includePatterns: plan.includePatterns,
|
||||
pnpmArgs: createVitestArgs(plan),
|
||||
watchMode: plan.watchMode,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function writeVitestIncludeFile(filePath, includePatterns) {
|
||||
fs.writeFileSync(filePath, `${JSON.stringify(includePatterns, null, 2)}\n`);
|
||||
}
|
||||
|
||||
export function buildVitestArgs(args, cwd = process.cwd()) {
|
||||
const [plan] = buildVitestRunPlans(args, cwd);
|
||||
if (!plan) {
|
||||
return createVitestArgs({
|
||||
config: DEFAULT_VITEST_CONFIG,
|
||||
forwardedArgs: [],
|
||||
watchMode: false,
|
||||
});
|
||||
}
|
||||
return createVitestArgs(plan);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,146 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const { buildVitestArgs, buildVitestRunPlans, createVitestRunSpecs, parseTestProjectsArgs } =
|
||||
(await import("../../scripts/test-projects.test-support.mjs")) as unknown as {
|
||||
buildVitestArgs: (args: string[], cwd?: string) => string[];
|
||||
buildVitestRunPlans: (
|
||||
args: string[],
|
||||
cwd?: string,
|
||||
) => Array<{
|
||||
config: string;
|
||||
forwardedArgs: string[];
|
||||
includePatterns: string[] | null;
|
||||
watchMode: boolean;
|
||||
}>;
|
||||
createVitestRunSpecs: (
|
||||
args: string[],
|
||||
params?: {
|
||||
baseEnv?: NodeJS.ProcessEnv;
|
||||
cwd?: string;
|
||||
tempDir?: string;
|
||||
},
|
||||
) => Array<{
|
||||
config: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
includeFilePath: string | null;
|
||||
includePatterns: string[] | null;
|
||||
pnpmArgs: string[];
|
||||
watchMode: boolean;
|
||||
}>;
|
||||
parseTestProjectsArgs: (
|
||||
args: string[],
|
||||
cwd?: string,
|
||||
) => {
|
||||
forwardedArgs: string[];
|
||||
targetArgs: string[];
|
||||
watchMode: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
describe("test-projects args", () => {
|
||||
it("drops a pnpm passthrough separator while preserving targeted filters", () => {
|
||||
expect(parseTestProjectsArgs(["--", "src/foo.test.ts", "-t", "target"])).toEqual({
|
||||
forwardedArgs: ["src/foo.test.ts", "-t", "target"],
|
||||
targetArgs: ["src/foo.test.ts"],
|
||||
watchMode: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps watch mode explicit without leaking the sentinel to Vitest", () => {
|
||||
expect(buildVitestArgs(["--watch", "--", "src/foo.test.ts"])).toEqual([
|
||||
"exec",
|
||||
"vitest",
|
||||
"--config",
|
||||
"vitest.config.ts",
|
||||
"src/foo.test.ts",
|
||||
]);
|
||||
});
|
||||
|
||||
it("uses run mode by default", () => {
|
||||
expect(buildVitestArgs(["src/foo.test.ts"])).toEqual([
|
||||
"exec",
|
||||
"vitest",
|
||||
"run",
|
||||
"--config",
|
||||
"vitest.config.ts",
|
||||
"src/foo.test.ts",
|
||||
]);
|
||||
});
|
||||
|
||||
it("routes direct channel extension file targets to the channels config", () => {
|
||||
expect(
|
||||
buildVitestRunPlans(["extensions/discord/src/monitor/message-handler.preflight.test.ts"]),
|
||||
).toEqual([
|
||||
{
|
||||
config: "vitest.channels.config.ts",
|
||||
forwardedArgs: [],
|
||||
includePatterns: ["extensions/discord/src/monitor/message-handler.preflight.test.ts"],
|
||||
watchMode: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("routes direct provider extension file targets to the extensions config", () => {
|
||||
expect(buildVitestRunPlans(["extensions/firecrawl/index.test.ts"])).toEqual([
|
||||
{
|
||||
config: "vitest.extensions.config.ts",
|
||||
forwardedArgs: [],
|
||||
includePatterns: ["extensions/firecrawl/index.test.ts"],
|
||||
watchMode: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("splits mixed core and extension targets into separate vitest runs", () => {
|
||||
expect(
|
||||
buildVitestRunPlans([
|
||||
"src/config/config-misc.test.ts",
|
||||
"extensions/discord/src/monitor/message-handler.preflight.test.ts",
|
||||
"-t",
|
||||
"mention",
|
||||
]),
|
||||
).toEqual([
|
||||
{
|
||||
config: "vitest.config.ts",
|
||||
forwardedArgs: ["-t", "mention", "src/config/config-misc.test.ts"],
|
||||
includePatterns: null,
|
||||
watchMode: false,
|
||||
},
|
||||
{
|
||||
config: "vitest.channels.config.ts",
|
||||
forwardedArgs: ["-t", "mention"],
|
||||
includePatterns: ["extensions/discord/src/monitor/message-handler.preflight.test.ts"],
|
||||
watchMode: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("writes scoped include files for routed extension runs", () => {
|
||||
const [spec] = createVitestRunSpecs([
|
||||
"extensions/discord/src/monitor/message-handler.preflight.test.ts",
|
||||
]);
|
||||
|
||||
expect(spec?.pnpmArgs).toEqual([
|
||||
"exec",
|
||||
"vitest",
|
||||
"run",
|
||||
"--config",
|
||||
"vitest.channels.config.ts",
|
||||
]);
|
||||
expect(spec?.includePatterns).toEqual([
|
||||
"extensions/discord/src/monitor/message-handler.preflight.test.ts",
|
||||
]);
|
||||
expect(spec?.includeFilePath).toContain("openclaw-vitest-include-");
|
||||
expect(spec?.env.OPENCLAW_VITEST_INCLUDE_FILE).toBe(spec?.includeFilePath);
|
||||
});
|
||||
|
||||
it("rejects watch mode when a command spans multiple suites", () => {
|
||||
expect(() =>
|
||||
buildVitestRunPlans([
|
||||
"--watch",
|
||||
"src/config/config-misc.test.ts",
|
||||
"extensions/discord/src/monitor/message-handler.preflight.test.ts",
|
||||
]),
|
||||
).toThrow("watch mode with mixed test suites is not supported");
|
||||
});
|
||||
});
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildVitestArgs,
|
||||
parseTestProjectsArgs,
|
||||
} from "../../scripts/test-projects.test-support.mjs";
|
||||
|
||||
describe("test-projects args", () => {
|
||||
it("drops a pnpm passthrough separator while preserving targeted filters", () => {
|
||||
expect(parseTestProjectsArgs(["--", "src/foo.test.ts", "-t", "target"])).toEqual({
|
||||
forwardedArgs: ["src/foo.test.ts", "-t", "target"],
|
||||
watchMode: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps watch mode explicit without leaking the sentinel to Vitest", () => {
|
||||
expect(buildVitestArgs(["--watch", "--", "src/foo.test.ts"])).toEqual([
|
||||
"exec",
|
||||
"vitest",
|
||||
"--config",
|
||||
"vitest.config.ts",
|
||||
"src/foo.test.ts",
|
||||
]);
|
||||
});
|
||||
|
||||
it("uses run mode by default", () => {
|
||||
expect(buildVitestArgs(["src/foo.test.ts"])).toEqual([
|
||||
"exec",
|
||||
"vitest",
|
||||
"run",
|
||||
"--config",
|
||||
"vitest.config.ts",
|
||||
"src/foo.test.ts",
|
||||
]);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue