Files
kandev/apps/web/e2e/tests/gitlab/gitlab-watches.spec.ts
João Salavisa 7326796df2 feat(i18n): localize Settings → Integrations → GitLab (#2160)
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 19:45:46 +02:00

161 lines
6.5 KiB
TypeScript

import { test, expect } from "../../fixtures/test-base";
import { GITLAB_PROJECT, seedGitLabReview } from "../../helpers/gitlab";
import {
assertLocatorWithinViewportX,
assertNoDocumentHorizontalOverflow,
} from "../../helpers/layout-assertions";
import { GitLabSettingsPage } from "../../pages/gitlab-settings-page";
import { KanbanPage } from "../../pages/kanban-page";
test.describe("GitLab watch controls", () => {
test("review watch dispatches exactly once and supports pause plus reset", async ({
testPage,
apiClient,
seedData,
}) => {
test.setTimeout(120_000);
await apiClient.configureGitLab(seedData.workspaceId);
const watch = await apiClient.createGitLabReviewWatch({
workspace_id: seedData.workspaceId,
workflow_id: seedData.workflowId,
workflow_step_id: seedData.startStepId,
agent_profile_id: seedData.agentProfileId,
executor_profile_id: seedData.worktreeExecutorProfileId,
projects: [{ path: GITLAB_PROJECT }],
});
await expect
.poll(async () => {
const response = await apiClient.rawRequest(
"GET",
`/api/v1/gitlab/watches/review?workspace_id=${encodeURIComponent(seedData.workspaceId)}`,
);
const body = (await response.json()) as {
watches: Array<{ id: string; last_polled_at?: string }>;
};
return body.watches.find((item) => item.id === watch.id)?.last_polled_at ?? null;
})
.not.toBeNull();
await seedGitLabReview(apiClient, seedData.workspaceId, 91, "Watch exact-once MR");
const settings = new GitLabSettingsPage(testPage);
await settings.goto(seedData.workspaceId);
const card = settings.reviewWatches;
const table = card.getByTestId("gitlab-watch-desktop-table");
await expect(table.getByText(GITLAB_PROJECT, { exact: true })).toBeVisible();
await assertLocatorWithinViewportX(card, "desktop review watches");
await assertNoDocumentHorizontalOverflow(testPage, "desktop review watch settings");
await table.getByRole("button", { name: "Check now" }).click();
await expect(testPage.getByText(/Found 1 matching merge request/)).toBeVisible();
const taskTitle = `[${GITLAB_PROJECT}!91] Watch exact-once MR`;
const kanban = new KanbanPage(testPage);
await kanban.goto();
await expect(kanban.taskCardByTitle(taskTitle)).toHaveCount(1, {
timeout: 20_000,
});
await settings.goto(seedData.workspaceId);
await table.getByRole("button", { name: "Check now" }).click();
await expect(
testPage.getByText("No new merge requests matched", { exact: true }),
).toBeVisible();
await kanban.goto();
await expect(kanban.taskCardByTitle(taskTitle)).toHaveCount(1);
await settings.goto(seedData.workspaceId);
await table.getByRole("button", { name: "Pause watch" }).click();
const save = testPage
.getByTestId("settings-floating-save")
.getByRole("button", { name: /save changes/i });
await expect(save).toBeEnabled();
await save.click();
await expect(table.getByText("Paused", { exact: true })).toBeVisible();
await expect(table.getByRole("button", { name: "Check now" })).toBeDisabled();
await table.getByRole("button", { name: "Enable watch" }).click();
await save.click();
await table.getByRole("button", { name: "Reset watch" }).click();
const reset = testPage.getByTestId("reset-watch-dialog");
await expect(reset).toBeVisible();
await expect(testPage.getByTestId("reset-watch-dialog-description")).toContainText(
/delete 1 task/i,
);
await testPage.getByTestId("reset-watch-dialog-confirm").click();
// Singular form: the toast is an i18next `_one`/`_other` plural, not "task(s)".
await expect(testPage.getByText(/Review watch reset; 1 task deleted/)).toBeVisible();
await kanban.goto();
await expect(kanban.taskCardByTitle(taskTitle)).toHaveCount(1, {
timeout: 20_000,
});
});
test("issue watch creates one visible task for a repeated match", async ({
testPage,
apiClient,
seedData,
}) => {
await apiClient.configureGitLab(seedData.workspaceId);
const now = new Date().toISOString();
const watch = await apiClient.createGitLabIssueWatch({
workspace_id: seedData.workspaceId,
workflow_id: seedData.workflowId,
workflow_step_id: seedData.startStepId,
agent_profile_id: seedData.agentProfileId,
executor_profile_id: seedData.worktreeExecutorProfileId,
projects: [{ path: GITLAB_PROJECT }],
});
await expect
.poll(async () => {
const response = await apiClient.rawRequest(
"GET",
`/api/v1/gitlab/watches/issue?workspace_id=${encodeURIComponent(seedData.workspaceId)}`,
);
const body = (await response.json()) as {
watches: Array<{ id: string; last_polled_at?: string }>;
};
return body.watches.find((item) => item.id === watch.id)?.last_polled_at ?? null;
})
.not.toBeNull();
await apiClient.mockGitLabAddIssues(seedData.workspaceId, GITLAB_PROJECT, [
{
id: 10_092,
iid: 92,
project_id: 101,
title: "Watch exact-once issue",
body: "Issue body",
url: `https://gitlab.com/${GITLAB_PROJECT}/-/issues/92`,
web_url: `https://gitlab.com/${GITLAB_PROJECT}/-/issues/92`,
state: "opened",
author_username: "reporter",
project_namespace: "platform",
project_path: GITLAB_PROJECT,
labels: ["bug"],
assignees: [],
created_at: now,
updated_at: now,
},
]);
const settings = new GitLabSettingsPage(testPage);
await settings.goto(seedData.workspaceId);
const card = settings.issueWatches;
const table = card.getByTestId("gitlab-watch-desktop-table");
await assertLocatorWithinViewportX(card, "desktop issue watches");
await table.getByRole("button", { name: "Check now" }).click();
await expect(testPage.getByText(/Found 1 matching issue/)).toBeVisible();
const taskTitle = `[${GITLAB_PROJECT}#92] Watch exact-once issue`;
const kanban = new KanbanPage(testPage);
await kanban.goto();
await expect(kanban.taskCardByTitle(taskTitle)).toHaveCount(1, {
timeout: 20_000,
});
await settings.goto(seedData.workspaceId);
await table.getByRole("button", { name: "Check now" }).click();
await expect(testPage.getByText("No new issues matched", { exact: true })).toBeVisible();
await kanban.goto();
await expect(kanban.taskCardByTitle(taskTitle)).toHaveCount(1);
await assertNoDocumentHorizontalOverflow(testPage, "desktop issue watch settings");
});
});