import type { ElementHandle, Page } from "@playwright/test"; export type DraftConfirmSubmissionMode = "card_button_then_text_message" | "text_message"; async function pickBottomComposerOrNull(page: Page): Promise | null> { const candidates = await page.locator('textarea, [contenteditable="true"]').elementHandles(); let bestHandle: ElementHandle | null = null; let bestBottom = -1; for (const candidate of candidates) { const asHtml = candidate as ElementHandle; const box = await asHtml.boundingBox(); if (!box || box.width < 60 || box.height < 16) { continue; } const visible = await asHtml.isVisible(); if (!visible) { continue; } const bottom = box.y + box.height; if (bottom > bestBottom) { bestBottom = bottom; bestHandle = asHtml; } } return bestHandle; } async function pickBottomComposer(page: Page): Promise> { const bestHandle = await pickBottomComposerOrNull(page); if (!bestHandle) { throw new Error("No visible Feishu message composer was found."); } return bestHandle; } export async function sendChatMessage(page: Page, message: string, sendShortcut: string): Promise { const composer = await pickBottomComposer(page); const isTextArea = await composer.evaluate((node) => node.tagName.toLowerCase() === "textarea"); await composer.click({ force: true }); const selectAllShortcut = process.platform === "darwin" ? "Meta+A" : "Control+A"; await page.keyboard.press(selectAllShortcut).catch(() => undefined); await page.keyboard.press("Backspace").catch(() => undefined); if (isTextArea) { await composer.fill(message); } else { await page.keyboard.type(message, { delay: 18 }); } await page.keyboard.press(sendShortcut); } async function pickBottomVisibleConfirmButton(page: Page): Promise | null> { const candidates = await page.getByRole("button", { name: "确认", exact: true }).elementHandles(); let bestHandle: ElementHandle | null = null; let bestBottom = -1; for (const candidate of candidates) { const asHtml = candidate as ElementHandle; const box = await asHtml.boundingBox(); if (!box || box.width < 32 || box.height < 20) { continue; } const visible = await asHtml.isVisible(); if (!visible) { continue; } const bottom = box.y + box.height; if (bottom > bestBottom) { bestBottom = bottom; bestHandle = asHtml; } } return bestHandle; } export async function confirmDraftSubmission( page: Page, { sendShortcut, workItemId, }: { sendShortcut: string; workItemId: number; }, ): Promise { const confirmMessage = `work_item #${workItemId} 确认`; const confirmButton = await pickBottomVisibleConfirmButton(page); if (confirmButton) { await confirmButton.click({ force: true }); await page.waitForTimeout(1_200); await sendChatMessage(page, confirmMessage, sendShortcut); return "card_button_then_text_message"; } await sendChatMessage(page, confirmMessage, sendShortcut); return "text_message"; }