fix: keep test progress visible in agent runs (#131448)

This commit is contained in:
Peter Steinberger
2026-08-27 20:07:42 -07:00
committed by GitHub
parent 8a6089687a
commit e638097631
5 changed files with 107 additions and 2 deletions
+98
View File
@@ -0,0 +1,98 @@
import { describe, expect, it } from "vitest";
import { spawnNodeEvalSync } from "../src/test-utils/node-process.js";
import { DEFAULT_VITEST_TEST_TIMEOUT_MS } from "./vitest/vitest.timeouts.ts";
const reporterConfigs = [
"vitest.config.ts",
"test/vitest/vitest.cli-process.config.ts",
"test/vitest/vitest.ui.config.ts",
"test/vitest/vitest.ui-e2e.config.ts",
"test/vitest/vitest.e2e.config.ts",
"ui/vitest.config.ts",
"ui/vitest.node.config.ts",
];
type ReporterEntry = [string, Record<string, unknown>];
type ReporterResolution = {
defaults: Array<{ config: string; reporters: ReporterEntry[]; cli: ReporterEntry[] }>;
custom: ReporterEntry[];
customCli: ReporterEntry[];
injectedPty: ReporterEntry[];
};
describe("Vitest reporter contracts", () => {
it.each(["false", "true"])(
"reports completed agent tests and preserves overrides with GITHUB_ACTIONS=%s",
(githubActions) => {
// Resolve in a fresh process: shared config and std-env capture their environment on import.
// This starts no test workers and leaves the enclosing Vitest module/cache ownership alone.
const result = spawnNodeEvalSync(
`
import path from "node:path";
import { parseCLI, resolveConfig } from "vitest/node";
import { sharedVitestConfig } from "./test/vitest/vitest.shared.config.ts";
import { createTuiPtyVitestConfig } from "./test/vitest/vitest.tui-pty.config.ts";
const defaults = [];
for (const config of ${JSON.stringify(reporterConfigs)}) {
const root = config.startsWith("ui/") ? path.resolve("ui") : process.cwd();
const options = { root, config: path.resolve(config) };
const normal = await resolveConfig(options);
const cli = parseCLI(["vitest", "--reporter=json"]).options;
const override = await resolveConfig({ ...cli, ...options });
defaults.push({ config, reporters: normal.vitestConfig.reporters, cli: override.vitestConfig.reporters });
}
const customConfig = {
...sharedVitestConfig,
test: {
...sharedVitestConfig.test,
reporters: [["json", { outputFile: "custom-report.json" }]],
},
};
const custom = await resolveConfig({ config: false }, customConfig);
const customCli = await resolveConfig({
...parseCLI(["vitest", "--reporter=json", "--reporter=json"]).options,
config: false,
}, customConfig);
const injectedPty = await resolveConfig({ config: false }, createTuiPtyVitestConfig({
GITHUB_ACTIONS: process.env.GITHUB_ACTIONS === "true" ? "false" : "true",
}));
console.log("REPORTER_RESOLUTION " + JSON.stringify({
defaults,
custom: custom.vitestConfig.reporters,
customCli: customCli.vitestConfig.reporters,
injectedPty: injectedPty.vitestConfig.reporters,
}));
`,
{
imports: ["tsx"],
env: { ...process.env, AI_AGENT: "vitest-reporter-test", GITHUB_ACTIONS: githubActions },
timeout: DEFAULT_VITEST_TEST_TIMEOUT_MS,
},
);
expect(result.error, result.stderr).toBeUndefined();
expect(result.signal, result.stderr).toBeNull();
expect(result.status, result.stderr).toBe(0);
const report = result.stdout
.split("\n")
.find((line) => line.startsWith("REPORTER_RESOLUTION "));
expect(report, result.stdout).toBeDefined();
const resolved = JSON.parse(
report!.slice("REPORTER_RESOLUTION ".length),
) as ReporterResolution;
const expected = githubActions === "true" ? ["verbose", "github-actions"] : ["verbose"];
for (const { config, reporters, cli } of resolved.defaults) {
expect(
reporters.map(([name]) => name),
config,
).toEqual(expected);
expect(cli, `${config} CLI override`).toEqual([["json", {}]]);
}
expect(resolved.defaults).toHaveLength(reporterConfigs.length);
expect(resolved.custom).toEqual([["json", { outputFile: "custom-report.json" }]]);
expect(resolved.customCli).toEqual(resolved.custom);
expect(resolved.injectedPty.map(([name]) => name)).toEqual(
githubActions === "true" ? ["verbose"] : ["verbose", "github-actions"],
);
},
);
});
-1
View File
@@ -36,7 +36,6 @@ export function createE2EVitestConfig(env: Record<string, string | undefined> =
test: {
...baseTest,
maxWorkers: e2eWorkers,
reporters: ["verbose"],
silent: !verboseE2E,
globalSetup: [resolveRepoRootPath("test/vitest/vitest.e2e.global-setup.ts")],
setupFiles: [
+2
View File
@@ -469,6 +469,8 @@ export const sharedVitestConfig = {
},
test: {
dir: repoRoot,
// Emit completed cases even under agent detection so healthy runs feed the output watchdog.
reporters: ["verbose", ...(process.env.GITHUB_ACTIONS === "true" ? ["github-actions"] : [])],
testTimeout: DEFAULT_VITEST_TEST_TIMEOUT_MS,
// 180s on every platform: GitHub-hosted 4-core fallback runners (Blacksmith
// outage breaker) push e2e beforeAll hooks past 120s; Windows always needed it.
+2
View File
@@ -10,6 +10,7 @@ import {
jsdomOptimizedDeps,
nonIsolatedRunnerPath,
resolveDefaultVitestPool,
sharedVitestConfig,
} from "../test/vitest/vitest.shared.config.ts";
import { uiIsolatedTestFiles } from "../test/vitest/vitest.ui-isolated-paths.mjs";
import { controlUiLocaleModulesPlugin } from "./config/control-ui-locales.ts";
@@ -152,6 +153,7 @@ export default defineConfig({
},
test: {
...sharedUiTestConfig,
reporters: sharedVitestConfig.test.reporters,
projects: [
defineProject({
plugins: [controlUiLocaleModulesPlugin()],
+5 -1
View File
@@ -1,12 +1,16 @@
// Control UI config module wires vitest behavior.
import { defineConfig } from "vitest/config";
import { resolveDefaultVitestPool } from "../test/vitest/vitest.shared.config.ts";
import {
resolveDefaultVitestPool,
sharedVitestConfig,
} from "../test/vitest/vitest.shared.config.ts";
import { controlUiLocaleModulesPlugin } from "./config/control-ui-locales.ts";
// Node-only tests for pure logic (no Playwright/browser dependency).
export default defineConfig({
plugins: [controlUiLocaleModulesPlugin()],
test: {
reporters: sharedVitestConfig.test.reporters,
isolate: false,
pool: resolveDefaultVitestPool(),
testTimeout: 120_000,