|
| 1 | +import { expect, type Page } from '@playwright/test' |
| 2 | +import { generateId } from '@sim/utils/id' |
| 3 | +import { adminEmail, signInAsAdmin } from '@/platform/checks/live/session' |
| 4 | +import { withLiveDatabase } from '@/platform/checks/live/support/database' |
| 5 | +import { test } from '@/platform/checks/test' |
| 6 | + |
| 7 | +const WORKSPACE_ID = 'platform-live-workspace-main' |
| 8 | + |
| 9 | +/** |
| 10 | + * Seeds a workflow with no blocks in the main workspace, owned by the administrator, opens its |
| 11 | + * editor and deletes it afterwards. An empty workflow shows the canvas command list once the |
| 12 | + * canvas is ready to edit, which is what each check waits for. |
| 13 | + */ |
| 14 | +async function withEmptyWorkflow(page: Page, work: () => Promise<void>) { |
| 15 | + const workflowId = `platform-live-workflow-${generateId()}` |
| 16 | + await withLiveDatabase( |
| 17 | + (sql) => sql`insert into workflow |
| 18 | + (id, user_id, workspace_id, name, last_synced, created_at, updated_at) |
| 19 | + values (${workflowId}, (select id from "user" where email = ${adminEmail()}), ${WORKSPACE_ID}, |
| 20 | + 'Block search check', now(), now(), now())` |
| 21 | + ) |
| 22 | + try { |
| 23 | + await signInAsAdmin(page, `/workspace/${WORKSPACE_ID}/workflows/${workflowId}`) |
| 24 | + await expect(page.getByRole('button', { name: /^Search Blocks/ })).toBeVisible() |
| 25 | + await work() |
| 26 | + } finally { |
| 27 | + await withLiveDatabase((sql) => sql`delete from workflow where id = ${workflowId}`) |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +const workflowPanel = (page: Page) => page.getByRole('complementary', { name: 'Workflow panel' }) |
| 32 | + |
| 33 | +/** Opens the right panel's Variables tab, so a check starts with the Toolbar's pane hidden. */ |
| 34 | +async function leaveToolbarTab(page: Page) { |
| 35 | + const variables = workflowPanel(page).getByRole('tab', { name: 'Variables', exact: true }) |
| 36 | + await variables.click() |
| 37 | + await expect(variables).toHaveAttribute('aria-selected', 'true') |
| 38 | + return variables |
| 39 | +} |
| 40 | + |
| 41 | +/** The Toolbar tab is selected and its block search holds focus. */ |
| 42 | +async function expectToolbarSearchFocused(page: Page) { |
| 43 | + const panel = workflowPanel(page) |
| 44 | + await expect(panel.getByRole('tab', { name: 'Toolbar', exact: true })).toHaveAttribute( |
| 45 | + 'aria-selected', |
| 46 | + 'true' |
| 47 | + ) |
| 48 | + await expect(panel.getByRole('searchbox', { name: 'Search toolbar' })).toBeFocused() |
| 49 | +} |
| 50 | + |
| 51 | +test('Add Block in the canvas menu opens the Toolbar tab with its search focused', async ({ |
| 52 | + page, |
| 53 | +}) => { |
| 54 | + await withEmptyWorkflow(page, async () => { |
| 55 | + const variables = await leaveToolbarTab(page) |
| 56 | + // A corner of the pane, clear of the command list in its centre. |
| 57 | + await page.getByRole('application').click({ button: 'right', position: { x: 48, y: 48 } }) |
| 58 | + const menu = page.getByRole('menu', { name: 'Canvas actions' }) |
| 59 | + await expect(menu).toBeVisible() |
| 60 | + await expect(variables).toHaveAttribute('aria-selected', 'true') |
| 61 | + await menu.getByRole('menuitem', { name: /^Add Block/ }).click() |
| 62 | + await expectToolbarSearchFocused(page) |
| 63 | + // The menu returns focus as it unmounts, so focus must still be in the search once it has gone. |
| 64 | + await expect(menu).toBeHidden() |
| 65 | + await expectToolbarSearchFocused(page) |
| 66 | + }) |
| 67 | +}) |
| 68 | + |
| 69 | +test('Search Blocks on an empty canvas opens the Toolbar tab with its search focused', async ({ |
| 70 | + page, |
| 71 | +}) => { |
| 72 | + await withEmptyWorkflow(page, async () => { |
| 73 | + await leaveToolbarTab(page) |
| 74 | + await page.getByRole('button', { name: /^Search Blocks/ }).click() |
| 75 | + await expectToolbarSearchFocused(page) |
| 76 | + }) |
| 77 | +}) |
0 commit comments