Files
kandev/apps/web/e2e/tests/task/create-task-url-reopen-no-branches.spec.ts
T

128 lines
5.4 KiB
TypeScript

import { test, expect } from "../../fixtures/test-base";
import { KanbanPage } from "../../pages/kanban-page";
// Regression test for the user-reported bug:
//
// 1. Open the create-task dialog, switch to GitHub URL mode, enter a URL.
// 2. Submit the task - the URL flow registers the repo in the workspace as
// a remote (provider-backed) repository, without a local clone.
// 3. Reopen the dialog - the repo now appears in the workspace dropdown.
// Pre-fix: picking it showed "no branches" with the
// "No branches available for this repository." tooltip, because the
// backend tried to list branches from an empty local_path and the
// frontend cached the failure as a successful (empty) load.
//
// Fix: branch listing for a provider-backed repo now goes through the
// GitHub API (mocked in e2e) instead of the local clone. The local clone is
// only consulted when source_type=local. This test deliberately uses a fake
// owner/repo so the orchestrator's background `gh repo clone` fails - that
// keeps local_path empty, proving the remote-first path is what's serving
// the branches.
test.describe("Create-task URL flow - branches after reopen", () => {
test.describe.configure({ retries: 1 });
test("repo added via GitHub URL still lists branches when re-picked from the workspace dropdown", async ({
testPage,
apiClient,
seedData,
}) => {
test.setTimeout(60_000);
// Fake owner/repo so the background clone in the orchestrator fails with
// 404 and local_path stays empty. The bug only repros when local listing
// can't help - that's the path the fix has to keep working.
const owner = "kandev-e2e-no-such-owner";
const repo = "kandev-e2e-no-such-repo";
const repoFullName = `${owner}/${repo}`;
const taskTitle = "URL repo reopen bug";
await apiClient.mockGitHubAddBranches(owner, repo, [
{ name: "main" },
{ name: "develop" },
{ name: "feature/test" },
]);
const kanban = new KanbanPage(testPage);
await kanban.goto();
// ── First open: enter URL, pick worktree executor, submit task ──
await kanban.createTaskButton.first().click();
const dialog = testPage.getByTestId("create-task-dialog");
await expect(dialog).toBeVisible();
// Switch to Remote tab and paste via the chip popover (URL input moved
// inside the chip in Task 5/8).
await testPage.getByTestId("source-mode-remote").click();
await testPage.getByTestId("remote-repo-chip-trigger").first().click();
const pasteInput = testPage.getByTestId("remote-paste-url-input");
await pasteInput.fill(`https://github.com/${repoFullName}`);
await pasteInput.press("Enter");
await testPage.getByTestId("task-title-input").fill(taskTitle);
await testPage.getByTestId("task-description-input").fill("/e2e:simple-message");
await selectWorktreeExecutor(testPage);
const startBtn = testPage.getByTestId("submit-start-agent");
await expect(startBtn).toBeEnabled({ timeout: 15_000 });
await startBtn.click();
await expect(dialog).not.toBeVisible({ timeout: 10_000 });
// Wait for the workspace to surface the URL-added repo so the second
// dialog open sees it in the dropdown.
await expect
.poll(
async () => {
const res = await apiClient.rawRequest(
"GET",
`/api/v1/workspaces/${seedData.workspaceId}/repositories`,
);
const body = (await res.json()) as { repositories?: Array<{ name: string }> };
return (body.repositories ?? []).some((r) => r.name === repoFullName);
},
{ timeout: 15_000, intervals: [200, 500, 1000] },
)
.toBe(true);
// ── Second open: pick the same repo from the workspace dropdown ──
await kanban.createTaskButton.first().click();
await expect(dialog).toBeVisible();
await selectWorktreeExecutor(testPage);
await testPage.getByTestId("repo-chip-trigger").first().click();
await testPage
.getByRole("option", { name: new RegExp(`^${repoFullName}\\b`) })
.first()
.click();
// The branch chip should become enabled once the remote-branch fetch
// resolves. Pre-fix this never happened: the listing returned an error
// and the chip stayed disabled with the "no branches" tooltip.
const branchChip = testPage.getByTestId("branch-chip-trigger").first();
await expect(branchChip).toBeEnabled({ timeout: 10_000 });
// Open the dropdown and verify every mocked branch is present. Substring
// name matching (string, not regex) tolerates the "remote" badge that the
// pill renders alongside the branch name in the option's accessible name.
await branchChip.click();
for (const name of ["main", "develop", "feature/test"]) {
await expect(testPage.getByRole("option", { name })).toBeVisible({ timeout: 5_000 });
}
});
});
// Pin the executor on every dialog open. This test runs in a shard alongside
// create-task-branch-selector specs that leave local-executor profiles
// behind, and a stale local default trips the local-executor's
// currentLocalBranch autoselect race. Worktree is the cleanest neutral
// choice and exercises the same remote-branch-listing path.
async function selectWorktreeExecutor(page: import("@playwright/test").Page): Promise<void> {
const selector = page.getByTestId("executor-profile-selector");
await selector.click();
await page
.getByRole("option", { name: /Worktree/i })
.first()
.click();
}