import { execSync } from "node:child_process"; import fs from "node:fs"; import { test, expect } from "../../fixtures/test-base"; import { makeGitEnv } from "../../helpers/git-helper"; test.describe("Create task from GitHub PR list", () => { // Cold-start backend boots can race on the first test in a worker. test.describe.configure({ retries: 1 }); test("starting a task from a PR pre-fills the matching repo and the PR head branch", async ({ testPage, apiClient, seedData, backend, }) => { test.setTimeout(60_000); // seedData is worker-scoped, so the workspace can accumulate repos across // test reruns. Use a unique owner/repo per invocation so matchRepo lands // on exactly the one we just created. const suffix = `${process.pid}-${Date.now()}`; const ownerSlug = `owner-${suffix}`; const repoSlug = `repo-${suffix}`; // Create a real local git repo so the branch chip can enumerate branches. // Both `main` and the PR head branch must exist locally — the chip lists // local branches, not the GitHub mock's branches. const repoDir = `${backend.tmpDir}/repos/pr-list-${suffix}`; fs.mkdirSync(repoDir, { recursive: true }); const gitEnv = makeGitEnv(backend.tmpDir); execSync("git init -b main", { cwd: repoDir, env: gitEnv }); execSync('git commit --allow-empty -m "init"', { cwd: repoDir, env: gitEnv }); execSync("git checkout -b feature/from-pr-list", { cwd: repoDir, env: gitEnv }); execSync('git commit --allow-empty -m "feature"', { cwd: repoDir, env: gitEnv }); execSync("git checkout main", { cwd: repoDir, env: gitEnv }); // Pre-seed a GitHub-backed repository that matches the mock PR's owner/repo. // The launcher's matchRepo() looks up workspace repos by provider_owner/name. await apiClient.createRepository(seedData.workspaceId, repoDir, "main", { name: `${ownerSlug}/${repoSlug}`, provider: "github", provider_owner: ownerSlug, provider_name: repoSlug, }); await apiClient.mockGitHubReset(); await apiClient.mockGitHubSetUser("test-user"); await apiClient.mockGitHubAddBranches(ownerSlug, repoSlug, [ { name: "main" }, { name: "feature/from-pr-list" }, ]); await apiClient.mockGitHubAddPRs([ { number: 77, title: "PR from GitHub page", state: "open", head_branch: "feature/from-pr-list", base_branch: "main", author_login: "test-user", repo_owner: ownerSlug, repo_name: repoSlug, }, ]); await testPage.goto("/github"); // Wait for the seeded PR row to render. The "Review requested" default // preset matches because the mock user is the requested reviewer fallback // for the seeded PR. We scope by data-pr-number to avoid catching any // leftover PRs cached by previous tests. const prRow = testPage.locator('[data-testid="pr-row"][data-pr-number="77"]'); await expect(prRow).toBeVisible({ timeout: 15_000 }); // Open the task launcher menu for this PR and choose the "Review" preset. await prRow.getByTestId("pr-start-task-trigger").click(); await testPage.locator('[data-testid="pr-start-task-preset"][data-preset-id="review"]').click(); // The create-task dialog should open with the matching workspace repo // selected and the PR head branch pre-filled in the branch chip. const dialog = testPage.getByTestId("create-task-dialog"); await expect(dialog).toBeVisible(); await expect(dialog.getByTestId("repo-chip-trigger").first()).toContainText( `${ownerSlug}/${repoSlug}`, ); await expect(dialog.getByTestId("branch-chip-trigger").first()).toContainText( "feature/from-pr-list", { timeout: 10_000 }, ); // The dialog title should be derived from the preset + PR title. await expect(testPage.getByTestId("task-title-input")).toHaveValue( /Review: PR from GitHub page/, ); }); });