111 lines
3.2 KiB
TypeScript
111 lines
3.2 KiB
TypeScript
import { expect } from "@playwright/test";
|
|
import type { Page } from "@playwright/test";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { execSync } from "node:child_process";
|
|
import type { ApiClient } from "./api-client";
|
|
import { KanbanPage } from "../pages/kanban-page";
|
|
import { SessionPage } from "../pages/session-page";
|
|
|
|
export class GitHelper {
|
|
constructor(
|
|
private repoDir: string,
|
|
private env: NodeJS.ProcessEnv,
|
|
) {}
|
|
|
|
exec(cmd: string): string {
|
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
try {
|
|
return execSync(cmd, { cwd: this.repoDir, env: this.env, encoding: "utf8" });
|
|
} catch (err) {
|
|
const msg = (err as Error).message ?? "";
|
|
if (msg.includes("index.lock") && attempt < 2) {
|
|
execSync("sleep 0.3");
|
|
continue;
|
|
}
|
|
throw err;
|
|
}
|
|
}
|
|
throw new Error(`git exec failed after 3 attempts: ${cmd}`);
|
|
}
|
|
|
|
createFile(name: string, content: string | Buffer) {
|
|
const filePath = path.join(this.repoDir, name);
|
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
fs.writeFileSync(filePath, content);
|
|
}
|
|
|
|
modifyFile(name: string, content: string | Buffer) {
|
|
this.createFile(name, content);
|
|
}
|
|
|
|
deleteFile(name: string) {
|
|
const filePath = path.join(this.repoDir, name);
|
|
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
}
|
|
|
|
stageFile(name: string) {
|
|
this.exec(`git add "${name}"`);
|
|
}
|
|
|
|
stageAll() {
|
|
this.exec("git add -A");
|
|
}
|
|
|
|
commit(message: string): string {
|
|
this.exec(`git commit -m "${message}"`);
|
|
return this.exec("git rev-parse HEAD").trim();
|
|
}
|
|
|
|
getCurrentSha(): string {
|
|
return this.exec("git rev-parse HEAD").trim();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Strip GIT_CONFIG_* environment variables that can inject global git hooks
|
|
* into fresh git repos, breaking E2E test setup.
|
|
*/
|
|
function stripGitConfigOverrides(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
const clean: NodeJS.ProcessEnv = {};
|
|
for (const [key, value] of Object.entries(env)) {
|
|
if (/^GIT_CONFIG_(KEY|VALUE|COUNT|PARAMETERS|GLOBAL|SYSTEM)/i.test(key)) continue;
|
|
clean[key] = value;
|
|
}
|
|
return clean;
|
|
}
|
|
|
|
export function makeGitEnv(tmpDir: string): NodeJS.ProcessEnv {
|
|
return {
|
|
...stripGitConfigOverrides(process.env),
|
|
HOME: tmpDir,
|
|
GIT_AUTHOR_NAME: "E2E Test",
|
|
GIT_AUTHOR_EMAIL: "e2e@test.local",
|
|
GIT_COMMITTER_NAME: "E2E Test",
|
|
GIT_COMMITTER_EMAIL: "e2e@test.local",
|
|
};
|
|
}
|
|
|
|
export async function openTaskSession(page: Page, title: string): Promise<SessionPage> {
|
|
const kanban = new KanbanPage(page);
|
|
await kanban.goto();
|
|
const card = kanban.taskCardByTitle(title);
|
|
await expect(card).toBeVisible({ timeout: 15_000 });
|
|
await card.click();
|
|
await expect(page).toHaveURL(/\/t\//, { timeout: 15_000 });
|
|
const session = new SessionPage(page);
|
|
await session.waitForLoad();
|
|
return session;
|
|
}
|
|
|
|
export async function createStandardProfile(apiClient: ApiClient, name: string) {
|
|
const { agents } = await apiClient.listAgents();
|
|
const agentId = agents[0]?.id;
|
|
if (!agentId) throw new Error("No agent available");
|
|
return apiClient.createAgentProfile(agentId, name, {
|
|
model: "mock-fast",
|
|
auto_approve: true,
|
|
cli_passthrough: false,
|
|
});
|
|
}
|