Files
kandev/apps/web/e2e/tests/task/plugin-repository-task-create.spec.ts

182 lines
7.1 KiB
TypeScript

import type { Page } from "@playwright/test";
import { expect, test } from "../../fixtures/test-base";
import { installFixturePlugin, PLUGIN_ID } from "../../helpers/plugin-fixture";
import type { ApiClient } from "../../helpers/api-client";
import { KanbanPage } from "../../pages/kanban-page";
const FIXTURE_PROVIDER = "fixture-source-control";
const FIXTURE_REPOSITORY_ID = "fixture-repository";
const FIXTURE_REPOSITORY_URL = "https://bitbucket.example.test/projects/TEAM/repos/fixture";
const FIXTURE_BRANCH = "feature/provider-contract";
type RepositorySummary = {
id: string;
provider?: string;
provider_repo_id?: string;
};
async function fixtureRepositories(apiClient: ApiClient, workspaceId: string) {
const response = await apiClient.rawRequest(
"GET",
`/api/v1/workspaces/${workspaceId}/repositories`,
);
const bodyText = await response.text();
expect(response.ok, bodyText).toBe(true);
const body = JSON.parse(bodyText) as { repositories?: RepositorySummary[] };
return (body.repositories ?? []).filter(
(repository) =>
repository.provider === FIXTURE_PROVIDER &&
repository.provider_repo_id === FIXTURE_REPOSITORY_ID,
);
}
async function removeFixtureRepositories(apiClient: ApiClient, workspaceId: string): Promise<void> {
for (const repository of await fixtureRepositories(apiClient, workspaceId)) {
const response = await apiClient.rawRequest("DELETE", `/api/v1/repositories/${repository.id}`);
if (!response.ok && response.status !== 404) {
throw new Error(
`fixture repository cleanup failed (${response.status}): ${await response.text()}`,
);
}
}
}
async function selectFixtureRepository(page: Page): Promise<void> {
await page.getByTestId("source-mode-remote").click();
const repositoryTrigger = page.getByTestId("remote-repo-chip-trigger");
await expect(repositoryTrigger).toHaveCount(1);
await repositoryTrigger.click();
const repositoryOption = page
.getByTestId("remote-repo-option")
.filter({ hasText: "TEAM/fixture" });
await expect(repositoryOption).toHaveCount(1);
await expect(repositoryOption).toBeVisible({ timeout: 15_000 });
await repositoryOption.click();
await expect(repositoryTrigger).toContainText("TEAM/fixture");
}
async function selectFixtureBranch(page: Page): Promise<void> {
const branch = page.getByTestId("remote-branch-chip-trigger");
await expect(branch).toHaveCount(1);
await expect(branch).toBeEnabled({ timeout: 15_000 });
await branch.click();
const option = page.getByRole("option", { name: FIXTURE_BRANCH, exact: false });
await expect(option).toHaveCount(1);
await expect(option).toBeVisible({ timeout: 15_000 });
await option.click();
await expect(branch).toContainText(FIXTURE_BRANCH);
}
async function fillTaskForm(page: Page, title: string): Promise<void> {
await page.getByTestId("task-title-input").fill(title);
await page
.getByTestId("task-description-input")
.fill("Create this task from a plugin repository.");
}
async function createWithoutAgent(page: Page): Promise<Response> {
const responsePromise = page.waitForResponse(
(response) =>
response.url().endsWith("/api/v1/tasks") && response.request().method() === "POST",
);
await page.getByTestId("submit-start-agent-chevron").click();
await page.getByTestId("submit-create-without-agent").click();
return responsePromise;
}
test.describe("first-use plugin repository task creation", () => {
test.afterEach(async ({ apiClient, seedData }) => {
await apiClient.rawRequest("DELETE", `/api/plugins/${PLUGIN_ID}`).catch(() => undefined);
await removeFixtureRepositories(apiClient, seedData.workspaceId).catch(() => undefined);
});
test("creates and persists the authoritative repository on desktop", async ({
testPage,
apiClient,
seedData,
}) => {
test.setTimeout(120_000);
await removeFixtureRepositories(apiClient, seedData.workspaceId);
expect(await fixtureRepositories(apiClient, seedData.workspaceId)).toHaveLength(0);
await installFixturePlugin(testPage);
const kanban = new KanbanPage(testPage);
await kanban.goto();
await kanban.createTaskButton.first().click();
const dialog = testPage.getByTestId("create-task-dialog");
await expect(dialog).toBeVisible();
await selectFixtureRepository(testPage);
await selectFixtureBranch(testPage);
await fillTaskForm(testPage, "First-use plugin repository task");
const response = await createWithoutAgent(testPage);
const responseBody = await response.text();
expect(response.status(), responseBody).toBe(200);
const created = JSON.parse(responseBody) as { id: string };
expect(created.id).toBeTruthy();
await expect(dialog).toBeHidden({ timeout: 15_000 });
const task = await apiClient.getTask(created.id);
expect(task.repositories).toHaveLength(1);
expect(task.repositories?.[0]).toMatchObject({
base_branch: "main",
checkout_branch: FIXTURE_BRANCH,
});
const repositories = await fixtureRepositories(apiClient, seedData.workspaceId);
expect(repositories).toHaveLength(1);
const repositoryResponse = await apiClient.rawRequest(
"GET",
`/api/v1/repositories/${repositories[0]!.id}`,
);
const repositoryBody = await repositoryResponse.text();
expect(repositoryResponse.ok, repositoryBody).toBe(true);
expect(JSON.parse(repositoryBody)).toMatchObject({
provider: FIXTURE_PROVIDER,
provider_repo_id: FIXTURE_REPOSITORY_ID,
provider_owner: "TEAM",
provider_name: "fixture",
remote_url: "https://bitbucket.example.test/scm/TEAM/fixture.git",
default_branch: "main",
});
});
test("keeps the form open when the provider becomes inactive", async ({
testPage,
apiClient,
seedData,
}) => {
test.setTimeout(120_000);
await removeFixtureRepositories(apiClient, seedData.workspaceId);
expect(await fixtureRepositories(apiClient, seedData.workspaceId)).toHaveLength(0);
await installFixturePlugin(testPage);
const kanban = new KanbanPage(testPage);
await kanban.goto();
await kanban.createTaskButton.first().click();
const dialog = testPage.getByTestId("create-task-dialog");
await expect(dialog).toBeVisible();
await selectFixtureRepository(testPage);
await selectFixtureBranch(testPage);
const title = "Inactive plugin repository task";
await fillTaskForm(testPage, title);
const disable = await apiClient.rawRequest("POST", `/api/plugins/${PLUGIN_ID}/disable`);
expect(disable.ok, await disable.text()).toBe(true);
const response = await createWithoutAgent(testPage);
expect(response.status()).toBe(503);
await expect(dialog).toBeVisible();
await expect(
testPage.getByText(
"The repository provider is unavailable. Check the connection and try again.",
{
exact: true,
},
),
).toBeVisible();
const tasks = await apiClient.listTasks(seedData.workspaceId);
expect(tasks.tasks.some((task) => task.title === title)).toBe(false);
expect(await fixtureRepositories(apiClient, seedData.workspaceId)).toHaveLength(0);
expect(testPage.url()).not.toContain(FIXTURE_REPOSITORY_URL);
});
});