goal-bar.e2e.ts 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. // Keyless assembled-browser coverage for the goal bar over the shipped Web
  2. // bundles and the fixture Connection RPC. The command creates a real projected
  3. // goal in the fixture session; the golden pins the active strip, while the
  4. // clear gesture proves the acknowledged tombstone leaves neither stale chrome
  5. // nor a duplicate-mutation error.
  6. import { fileURLToPath } from 'node:url'
  7. import { join } from 'node:path'
  8. import type { Browser, Page } from 'playwright'
  9. import { chromium } from 'playwright'
  10. import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
  11. import {
  12. assertFixtureInventory, captureStableAria, compareOrRefreshGolden,
  13. launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold,
  14. } from './scaffold.ts'
  15. import { newEnglishPage, saveFailureShot } from './support.ts'
  16. const SNAPSHOT_DIR = fileURLToPath(new URL('./expected/goal-bar', import.meta.url))
  17. const ACTIVE_EXPECTED = join(SNAPSHOT_DIR, 'active.expected.md')
  18. const INACTIVE_EXPECTED = join(SNAPSHOT_DIR, 'inactive.expected.md')
  19. const OVERLAY = fileURLToPath(new URL('./goal-bar.overlay.yml', import.meta.url))
  20. const MODE = webSnapshotMode()
  21. describe('web e2e: goal bar clear convergence', () => {
  22. let scaffold: WebScaffold
  23. let browser: Browser
  24. let page: Page
  25. let tripwire: ReturnType<typeof watchConsole>
  26. beforeAll(async () => {
  27. scaffold = await launchWebScaffold({ extraOverlayPath: OVERLAY, welcomeNoticePending: true })
  28. browser = await chromium.launch()
  29. page = await newEnglishPage(browser)
  30. tripwire = watchConsole(page)
  31. const login = await page.context().request.get(scaffold.authenticatedUrl, { maxRedirects: 0 })
  32. expect(login.status()).toBe(303)
  33. await page.goto(`${scaffold.baseUrl}?fixture`, { waitUntil: 'load' })
  34. await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
  35. }, 120_000)
  36. afterAll(async () => {
  37. await browser?.close()
  38. await scaffold?.close()
  39. })
  40. it('renders one active goal and clears it without exposing a stale error', async () => {
  41. onTestFailed(() => saveFailureShot(page, 'web-e2e-goal-bar-clear'))
  42. // Startup reuses the fixture workspace's blank session, keeping this
  43. // command independent of alpha's running replay and pending question.
  44. const input = page.locator('[data-composer-input][data-placeholder="Describe what you want to build, / commands, @ files or sessions"]')
  45. await input.waitFor({ timeout: 10_000 })
  46. await input.fill('/goal guard rapid clear clicks')
  47. await input.press('Enter')
  48. const bar = page.locator('[data-goal-bar]')
  49. await bar.waitFor({ timeout: 10_000 })
  50. await expect.poll(() => bar.getByRole('button', { name: 'Pause goal' }).count(), {
  51. timeout: 10_000,
  52. }).toBe(1)
  53. const snapshot = await captureStableAria(page, '[data-goal-bar]', scaffold.workspaceCwd)
  54. await compareOrRefreshGolden(ACTIVE_EXPECTED, snapshot, MODE)
  55. await page.evaluate(() => {
  56. (globalThis as unknown as { __fxTiming?: { disarmOnlyGoal(): void } }).__fxTiming?.disarmOnlyGoal()
  57. })
  58. await expect.poll(() => bar.getByRole('button', { name: 'Resume goal' }).count(), {
  59. timeout: 10_000,
  60. }).toBe(1)
  61. const inactive = await captureStableAria(page, '[data-goal-bar]', scaffold.workspaceCwd)
  62. await compareOrRefreshGolden(INACTIVE_EXPECTED, inactive, MODE)
  63. const clear = bar.getByRole('button', { name: 'Clear goal' })
  64. await clear.evaluate((button) => {
  65. const control = button as HTMLButtonElement
  66. control.click()
  67. control.click()
  68. })
  69. await expect.poll(() => page.locator('[data-goal-bar]').count(), { timeout: 10_000 }).toBe(0)
  70. expect(await page.getByText(/no current goal/iu).count()).toBe(0)
  71. expect(tripwire.pageErrors).toEqual([])
  72. expect(tripwire.warnings).toEqual([])
  73. }, 60_000)
  74. it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => {
  75. await assertFixtureInventory(SNAPSHOT_DIR, ['active.expected.md', 'inactive.expected.md'])
  76. })
  77. })