| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 |
- // Regression drive for the unified hero composer (0729-0357-hero-unify):
- // cold start with zero workspaces -> create a workspace -> type. Asserts the
- // composer textarea is the SAME DOM node across the disabled->live flip (a
- // remount drops the __heroMark marker property) — the session-maybe
- // composer.bar contract.
- //
- // Prereqs: `pnpm run build`, then a fresh server against empty state:
- // rm -rf .storages && DSH_HOME=$(mktemp -d) node --experimental-transform-types \
- // --import ./scripts/tspath-loader.ts apps/cli/src/bin.ts web --port 44285 \
- // --workspace-root $(mktemp -d)
- // Run: node scripts/hero-composer-dom-continuity.mjs
- // (BASE_URL overrides the target; screenshots land in .artifacts/.)
- import { createRequire } from 'node:module'
- // playwright is a devDependency of apps/web only — resolve through its tree.
- const require = createRequire(new URL('../apps/web/package.json', import.meta.url))
- const { chromium } = require('playwright')
- const BASE = process.env.BASE_URL ?? 'http://127.0.0.1:44285'
- const SHOTS = new URL('../.artifacts/screenshots/0729-0357-hero-unify/', import.meta.url).pathname
- const browser = await chromium.launch()
- const page = await browser.newPage({ viewport: { width: 1280, height: 800 } })
- page.on('console', msg => { if (msg.type() === 'error') console.log('[console.error]', msg.text()) })
- page.on('pageerror', err => { console.log('[pageerror]', err.message) })
- await page.goto(BASE)
- await page.waitForSelector('textarea', { timeout: 20000 })
- await page.screenshot({ path: SHOTS + '01-cold-start.png' })
- const initial = await page.evaluate(() => {
- const boxes = [...document.querySelectorAll('textarea')]
- boxes.forEach((b, i) => { b.__heroMark = 'alive-' + i })
- return boxes.map(b => ({ disabled: b.disabled, placeholder: b.placeholder }))
- })
- console.log('cold-start textareas:', JSON.stringify(initial))
- // Open the picker and create a workspace by name (typed-input flow). The name
- // must be unique per registry; keystrokes go through pressSequentially so the
- // dialog's React onChange enables the submit button.
- await page.getByRole('button', { name: 'Choose workspace' }).click()
- await page.getByText('Create a new workspace').click()
- await page.screenshot({ path: SHOTS + '03-create-form.png' })
- const nameBox = page.getByPlaceholder('Workspace name')
- await nameBox.click()
- const wsName = 'proj-' + Date.now().toString(36)
- await nameBox.pressSequentially(wsName, { delay: 30 })
- await page.locator('button:text-is("Create workspace")').click()
- // Wait for the composer to go live (placeholder flips, textarea enabled).
- await page.waitForFunction(() => {
- const box = document.querySelector('textarea')
- return box !== null && !box.disabled
- }, { timeout: 20000 })
- await page.screenshot({ path: SHOTS + '04-live.png' })
- const after = await page.evaluate(() => {
- const boxes = [...document.querySelectorAll('textarea')]
- return boxes.map(b => ({
- mark: b.__heroMark ?? 'REMOUNTED',
- disabled: b.disabled,
- placeholder: b.placeholder,
- }))
- })
- console.log('post-pick textareas:', JSON.stringify(after))
- // Type into the live composer.
- await page.locator('textarea').first().fill('hello from acceptance run')
- const typed = await page.evaluate(() => document.querySelector('textarea')?.value)
- console.log('typed value:', JSON.stringify(typed))
- await page.screenshot({ path: SHOTS + '05-typed.png' })
- const survived = after.length === 1 && after[0].mark === 'alive-0'
- console.log(survived
- ? 'DOM-CONTINUITY: PASS (same textarea node across cold-start -> live)'
- : 'DOM-CONTINUITY: FAIL ' + JSON.stringify(after))
- await browser.close()
- process.exit(survived && typed === 'hello from acceptance run' ? 0 : 1)
|