| 110 | export function createWorkspaceUI(page: Page, context: DemoProjectConfig): WorkspaceUI { |
| 111 | const projects = { |
| 112 | async openFirstWorkspace(): Promise<void> { |
| 113 | const navigation = page.getByRole("navigation", { name: "Projects" }); |
| 114 | await expect(navigation).toBeVisible(); |
| 115 | |
| 116 | const projectItems = navigation.locator('[role="button"][aria-controls]'); |
| 117 | const projectItem = projectItems.first(); |
| 118 | await expect(projectItem).toBeVisible(); |
| 119 | |
| 120 | const workspaceListId = await projectItem.getAttribute("aria-controls"); |
| 121 | if (!workspaceListId) { |
| 122 | throw new Error("Project item is missing aria-controls attribute"); |
| 123 | } |
| 124 | |
| 125 | const workspaceItems = page.locator(`#${workspaceListId} > div[role="button"]`); |
| 126 | const workspaceItem = workspaceItems.first(); |
| 127 | const isVisible = await workspaceItem.isVisible().catch(() => false); |
| 128 | if (!isVisible) { |
| 129 | // Click the expand/collapse button within the project item |
| 130 | const expandButton = projectItem.getByRole("button", { name: /expand project/i }); |
| 131 | await expandButton.click(); |
| 132 | await workspaceItem.waitFor({ state: "visible" }); |
| 133 | } |
| 134 | |
| 135 | await workspaceItem.click(); |
| 136 | |
| 137 | // Startup can land on a project page or a restored workspace. After clicking a |
| 138 | // workspace we need to confirm the navigation actually landed on the demo workspace |
| 139 | // (not just any transcript). |
| 140 | const expectedProjectName = path.basename(context.projectPath); |
| 141 | await expect(page.getByTestId("workspace-menu-bar")).toContainText(expectedProjectName, { |
| 142 | timeout: 20_000, |
| 143 | }); |
| 144 | |
| 145 | await chat.waitForTranscript(); |
| 146 | }, |
| 147 | }; |
| 148 | |
| 149 | const chat = { |