202 lines
8.9 KiB
TypeScript
202 lines
8.9 KiB
TypeScript
import { expect, test } from "../../fixtures/test-base";
|
|
import {
|
|
assertLocatorWithinViewportX,
|
|
assertNoDocumentHorizontalOverflow,
|
|
assertNoElementHorizontalOverflow,
|
|
} from "../../helpers/layout-assertions";
|
|
import { useRegularMode } from "../../helpers/regular-mode";
|
|
import { MobileKanbanPage } from "../../pages/mobile-kanban-page";
|
|
|
|
useRegularMode();
|
|
|
|
test.describe("Create task dependency selector on mobile", () => {
|
|
test("keeps the selector contained and supports touch selection and help", async ({
|
|
testPage,
|
|
apiClient,
|
|
seedData,
|
|
}) => {
|
|
const predecessor = await apiClient.createTask(
|
|
seedData.workspaceId,
|
|
"Mobile dependency predecessor with a long title",
|
|
{
|
|
workflow_id: seedData.workflowId,
|
|
workflow_step_id: seedData.startStepId,
|
|
repository_ids: [seedData.repositoryId],
|
|
},
|
|
);
|
|
await apiClient.mockGitHubAssociateTaskPR({
|
|
workspace_id: seedData.workspaceId,
|
|
task_id: predecessor.id,
|
|
owner: "kandev-e2e",
|
|
repo: "dependency-fixtures",
|
|
pr_number: 188,
|
|
pr_url: "https://github.test/kandev-e2e/dependency-fixtures/pull/188",
|
|
pr_title: "Dependency fixture PR",
|
|
head_branch: "feature/dependency-fixture",
|
|
base_branch: "main",
|
|
author_login: "e2e",
|
|
state: "open",
|
|
});
|
|
|
|
const mobile = new MobileKanbanPage(testPage);
|
|
await mobile.goto();
|
|
await mobile.mobileFab.tap();
|
|
|
|
const dialog = testPage.getByTestId("create-task-dialog");
|
|
await expect(dialog).toBeVisible();
|
|
const advancedTrigger = dialog.getByTestId("task-create-advanced-settings-trigger");
|
|
const advancedTriggerBox = await advancedTrigger.boundingBox();
|
|
expect(advancedTriggerBox).not.toBeNull();
|
|
expect(advancedTriggerBox!.height).toBeGreaterThanOrEqual(44);
|
|
await assertLocatorWithinViewportX(advancedTrigger, "mobile advanced settings trigger");
|
|
await expect(dialog.getByTestId("task-create-dependencies-trigger")).toHaveCount(0);
|
|
|
|
await advancedTrigger.tap();
|
|
await expect(advancedTrigger).toHaveAttribute("aria-expanded", "true");
|
|
const settingLabel = dialog.getByTestId("task-create-dependency-setting-label");
|
|
await expect(settingLabel).toContainText("Depends on");
|
|
const settingRow = dialog.getByTestId("task-create-dependency-setting-row");
|
|
const settingRowBox = await settingRow.boundingBox();
|
|
const settingLabelBox = await settingLabel.boundingBox();
|
|
const selectorContainer = dialog.getByTestId("task-create-dependency-selector-container");
|
|
const selectorContainerBox = await selectorContainer.boundingBox();
|
|
expect(settingRowBox).not.toBeNull();
|
|
expect(settingLabelBox).not.toBeNull();
|
|
expect(selectorContainerBox).not.toBeNull();
|
|
expect(selectorContainerBox!.x).toBeGreaterThan(settingLabelBox!.x + settingLabelBox!.width);
|
|
expect(Math.abs(selectorContainerBox!.y - settingLabelBox!.y)).toBeLessThanOrEqual(4);
|
|
const settingInfo = dialog.getByTestId("task-create-dependency-setting-info");
|
|
const settingInfoBox = await settingInfo.boundingBox();
|
|
expect(settingInfoBox).not.toBeNull();
|
|
expect(settingInfoBox!.height).toBeGreaterThanOrEqual(44);
|
|
expect(settingInfoBox!.width).toBeGreaterThanOrEqual(44);
|
|
await settingInfo.tap();
|
|
await expect(testPage.locator('[data-slot="tooltip-content"]:visible').last()).toContainText(
|
|
"This task waits until every selected task completes successfully.",
|
|
);
|
|
const trigger = dialog.getByTestId("task-create-dependencies-trigger");
|
|
const triggerBox = await trigger.boundingBox();
|
|
expect(triggerBox).not.toBeNull();
|
|
expect(triggerBox!.height).toBeGreaterThanOrEqual(44);
|
|
await assertLocatorWithinViewportX(trigger, "mobile dependency trigger");
|
|
await assertNoElementHorizontalOverflow(trigger, "mobile dependency trigger text");
|
|
|
|
await trigger.tap();
|
|
const picker = dialog.getByTestId("task-create-dependencies-popover");
|
|
await expect(picker).toBeVisible();
|
|
await assertLocatorWithinViewportX(picker, "mobile dependency picker");
|
|
await assertNoElementHorizontalOverflow(picker, "mobile dependency picker");
|
|
await assertNoDocumentHorizontalOverflow(testPage, "mobile dependency picker");
|
|
|
|
const info = picker.getByTestId("task-create-dependency-info");
|
|
const infoBox = await info.boundingBox();
|
|
expect(infoBox).not.toBeNull();
|
|
expect(infoBox!.height).toBeGreaterThanOrEqual(44);
|
|
expect(infoBox!.width).toBeGreaterThanOrEqual(44);
|
|
await info.tap();
|
|
await expect(testPage.locator('[data-slot="tooltip-content"]:visible').last()).toContainText(
|
|
"This task waits until every selected task completes successfully.",
|
|
);
|
|
|
|
const option = picker.getByTestId(`task-create-dependency-option-${predecessor.id}`);
|
|
await expect(option).toBeVisible();
|
|
await expect(option.getByTestId("task-create-dependency-task-icon")).toBeVisible();
|
|
const search = picker.getByPlaceholder("Search tasks or #PR/MR number...");
|
|
await search.fill("188");
|
|
await expect(option).toBeVisible();
|
|
await expect(option.locator('[aria-label="Linked pull or merge request #188"]')).toHaveText(
|
|
"#188",
|
|
);
|
|
await option.tap();
|
|
await expect(trigger).toContainText("#188 · Mobile dependency predecessor");
|
|
await assertNoDocumentHorizontalOverflow(testPage, "mobile dependency selection");
|
|
});
|
|
|
|
test("edits predecessors from the same touch-friendly dialog", async ({
|
|
testPage,
|
|
apiClient,
|
|
seedData,
|
|
prCapture,
|
|
}) => {
|
|
const taskOptions = {
|
|
workflow_id: seedData.workflowId,
|
|
workflow_step_id: seedData.startStepId,
|
|
repository_ids: [seedData.repositoryId],
|
|
};
|
|
const predecessor = await apiClient.createTask(
|
|
seedData.workspaceId,
|
|
"Mobile edit dependency Alpha",
|
|
taskOptions,
|
|
);
|
|
const replacement = await apiClient.createTask(
|
|
seedData.workspaceId,
|
|
"Mobile edit dependency Beta",
|
|
taskOptions,
|
|
);
|
|
const target = await apiClient.createTask(
|
|
seedData.workspaceId,
|
|
"Mobile edit dependency target",
|
|
{
|
|
...taskOptions,
|
|
description: "Mobile edit prompt",
|
|
blocked_by: [predecessor.id],
|
|
},
|
|
);
|
|
await apiClient.updateTaskState(target.id, "IN_PROGRESS");
|
|
|
|
const mobile = new MobileKanbanPage(testPage);
|
|
await mobile.goto();
|
|
await mobile.taskCard(target.id).getByRole("button", { name: "More options" }).tap();
|
|
await testPage
|
|
.locator('[data-slot="dropdown-menu-content"]:visible')
|
|
.getByRole("menuitem", { name: "Edit", exact: true })
|
|
.tap();
|
|
|
|
const dialog = testPage.getByTestId("create-task-dialog");
|
|
const editDependencies = dialog.getByTestId("task-edit-dependencies");
|
|
await expect(editDependencies).toBeVisible();
|
|
const trigger = dialog.getByTestId("task-create-dependencies-trigger");
|
|
await expect(trigger).toBeVisible({ timeout: 30_000 });
|
|
const triggerBox = await trigger.boundingBox();
|
|
expect(triggerBox).not.toBeNull();
|
|
expect(triggerBox!.height).toBeGreaterThanOrEqual(44);
|
|
await assertLocatorWithinViewportX(trigger, "mobile edit dependency trigger");
|
|
await assertNoElementHorizontalOverflow(trigger, "mobile edit dependency trigger");
|
|
await expect(trigger).toContainText("Mobile edit dependency Alpha");
|
|
|
|
await trigger.tap();
|
|
const picker = dialog.getByTestId("task-create-dependencies-popover");
|
|
await expect(picker).toBeVisible();
|
|
await assertLocatorWithinViewportX(picker, "mobile edit dependency picker");
|
|
await assertNoElementHorizontalOverflow(picker, "mobile edit dependency picker");
|
|
await assertNoDocumentHorizontalOverflow(testPage, "mobile edit dependency picker");
|
|
const info = picker.getByTestId("task-create-dependency-info");
|
|
const infoBox = await info.boundingBox();
|
|
expect(infoBox).not.toBeNull();
|
|
expect(infoBox!.height).toBeGreaterThanOrEqual(44);
|
|
expect(infoBox!.width).toBeGreaterThanOrEqual(44);
|
|
|
|
const search = picker.getByPlaceholder("Search tasks...");
|
|
await search.fill("Mobile edit dependency Beta");
|
|
const option = picker.getByTestId(`task-create-dependency-option-${replacement.id}`);
|
|
await expect(option).toBeVisible();
|
|
const optionBox = await option.boundingBox();
|
|
expect(optionBox).not.toBeNull();
|
|
expect(optionBox!.height).toBeGreaterThanOrEqual(44);
|
|
await prCapture.screenshot("mobile-task-dependency-picker", {
|
|
caption: "Mobile task edit dialog predecessor picker",
|
|
});
|
|
await option.tap();
|
|
await expect(trigger).toContainText("2 dependencies");
|
|
await testPage.keyboard.press("Escape");
|
|
await dialog.getByRole("button", { name: "Update", exact: true }).tap();
|
|
await expect(dialog).toHaveCount(0);
|
|
await expect
|
|
.poll(async () =>
|
|
(await apiClient.getTaskDependencies(target.id)).depends_on?.map((ref) => ref.id),
|
|
)
|
|
.toEqual(expect.arrayContaining([predecessor.id, replacement.id]));
|
|
await assertNoDocumentHorizontalOverflow(testPage, "mobile edit dependency saved");
|
|
});
|
|
});
|