diff --git a/tests/e2e/repo/jobs.spec.ts b/tests/e2e/repo/jobs.spec.ts new file mode 100644 index 000000000..d729226fb --- /dev/null +++ b/tests/e2e/repo/jobs.spec.ts @@ -0,0 +1,80 @@ +import type { RadiclePeer } from "@tests/support/peerManager.js"; + +import { expect, test } from "@tests/support/fixtures.js"; +import { createRepo } from "@tests/support/repo.js"; +import { radJobBinary } from "@tests/support/support.js"; + +async function recordRun( + peer: RadiclePeer, + repoFolder: string, + head: string, + log: string, + outcome: "succeeded" | "failed" | "started", +) { + const job = (args: string[]) => + peer.spawn(radJobBinary, ["--no-sync", ...args], { cwd: repoFolder }); + const { stdout: runId } = await job(["run", head, log]); + if (outcome !== "started") { + await job([outcome, head, runId.trim()]); + } +} + +test("CI runs are summarised and link to their logs", async ({ + page, + peer, +}) => { + const { rid, repoFolder } = await createRepo(peer, { name: "ci" }); + const { stdout: head } = await peer.git(["rev-parse", "HEAD"], { + cwd: repoFolder, + }); + await peer.spawn(radJobBinary, ["--no-sync", "new", head.trim()], { + cwd: repoFolder, + }); + await recordRun( + peer, + repoFolder, + head.trim(), + "https://ci.example.com/runs/passing", + "succeeded", + ); + await recordRun( + peer, + repoFolder, + head.trim(), + "https://github.com/o/r/actions/runs/12345/job/1", + "failed", + ); + + await page.goto(`/repos/${rid}/home`); + const summary = page.getByRole("button", { name: /1 passed ยท 1 failed/ }); + await expect(summary).toBeVisible(); + + await summary.click(); + await expect(page.getByRole("link", { name: /run 12345/ })).toHaveAttribute( + "href", + "https://github.com/o/r/actions/runs/12345/job/1", + ); + await expect( + page.locator('a[href="https://ci.example.com/runs/passing"]'), + ).toBeVisible(); +}); + +test("a running job is shown as running", async ({ page, peer }) => { + const { rid, repoFolder } = await createRepo(peer, { name: "ci" }); + const { stdout: head } = await peer.git(["rev-parse", "HEAD"], { + cwd: repoFolder, + }); + await peer.spawn(radJobBinary, ["--no-sync", "new", head.trim()], { + cwd: repoFolder, + }); + await recordRun( + peer, + repoFolder, + head.trim(), + "https://ci.example.com/runs/slow", + "started", + ); + + await page.goto(`/repos/${rid}/home`); + await expect(page.getByRole("button", { name: /1 running/ })).toBeVisible(); +}); diff --git a/tests/support/globalSetup.ts b/tests/support/globalSetup.ts index b7595d74d..9c9c6dcd2 100644 --- a/tests/support/globalSetup.ts +++ b/tests/support/globalSetup.ts @@ -50,6 +50,8 @@ export default async function globalSetup(): Promise<() => void> { "--manifest-path", "./crates/test-http-api/Cargo.toml", ]); + // The job COB CLI, at the version of radicle-job the app is built with. + await execa("cargo", ["build", "-p", "radicle-job", "--bin", "rad-job"]); // Keeps test keys out of the developer's ssh-agent. delete process.env.SSH_AUTH_SOCK; diff --git a/tests/support/support.ts b/tests/support/support.ts index b951f0a31..980b863d0 100644 --- a/tests/support/support.ts +++ b/tests/support/support.ts @@ -31,6 +31,14 @@ export const testHttpApiBinary = Path.resolve( "debug", "test-http-api", ); + +export const radJobBinary = Path.resolve( + supportDir, + "../..", + process.env.CARGO_TARGET_DIR ?? "target", + "debug", + "rad-job", +); const workspacePaths = [Path.join(tmpDir, "peers"), Path.join(tmpDir, "repos")]; export const heartwoodRelease = (