import type { Page } from '@playwright/test'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { createServer } from 'node:http'; import { join } from 'node:path'; import { expect, getStableWindow, test } from './fixtures/electron'; import type { WorkPreviewResult } from '../../shared/coding-work-preview'; async function hostRequest(page: Page, path: string, body?: unknown): Promise { const response = await page.evaluate(async (request) => await window.electron.ipcRenderer.invoke('hostapi:fetch', request) as { ok: boolean; data?: { ok: boolean; json?: unknown }; }, { path, method: body === undefined ? 'GET' : 'POST', body }); expect(response, `${path}: ${JSON.stringify(response)}`).toMatchObject({ ok: true, data: { ok: true } }); return response.data!.json as T; } test('existing work survives a full app restart and restarts a stopped service without choosing a model', async ({ launchElectronApp, electronTestResources }) => { const projectPath = join(electronTestResources.rootDir, 'remembered-work'); await mkdir(projectPath); const html = 'Remembered project work

My existing game

'; await writeFile(join(projectPath, 'index.html'), html); const server = createServer((_request, response) => { response.setHeader('Content-Type', 'text/html; charset=utf-8'); response.end(html); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const address = server.address(); if (!address || typeof address === 'string') throw new Error('Missing test port'); const url = `http://127.0.0.1:${address.port}/`; let serverClosed = false; try { const first = await launchElectronApp({ skipSetup: true }); let page = await getStableWindow(first); const { project } = await hostRequest<{ project: { id: string } }>(page, '/api/coding/projects/open', { projectPath }); const listPath = `/api/coding/projects/conversations?project_id=${project.id}`; const before = await hostRequest<{ conversations: unknown[] }>(page, listPath); await page.evaluate(() => window.electron.ipcRenderer.invoke('lifecycle:activity', { visible: true, module: 'programming' })); await hostRequest(page, '/api/agent-browser/open', { project_id: project.id, url, visible: false, bounds: { x: 20, y: 70, width: 420, height: 300 } }); const record = JSON.parse(await readFile(join(projectPath, '.makelore/work-entry.json'), 'utf8')); expect(record).toMatchObject({ projectId: project.id, url, launch: { kind: 'static', directory: '.', entry: 'index.html' } }); await first.close(); const restarted = await launchElectronApp({ skipSetup: true }); page = await getStableWindow(restarted); await hostRequest(page, '/api/coding/projects/open', { projectPath }); const restored = await hostRequest(page, '/api/agent-browser/ensure-work', { project_id: project.id, request_id: 'app-restarted' }); expect(restored).toMatchObject({ status: 'ready', browser: { url, state: 'attached' } }); expect(await hostRequest(page, listPath)).toEqual(before); expect(await restarted.evaluate(({ webContents }, expectedUrl) => webContents.getAllWebContents().some((contents) => contents.getURL() === expectedUrl && contents.getTitle() === 'Remembered project work'), url)).toBe(true); await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve())); serverClosed = true; const started = await hostRequest(page, '/api/agent-browser/ensure-work', { project_id: project.id, request_id: 'service-stopped' }); expect(started).toMatchObject({ status: 'ready', browser: { state: 'attached' } }); expect(await (await fetch(started.browser!.url)).text()).toBe(html); expect(await hostRequest(page, listPath)).toEqual(before); // Use the real native presentation route; this fixture has no authenticated // cloud account and must not fabricate one to enter the guarded Code UI. await hostRequest(page, '/api/agent-browser/present', { project_id: project.id, visible: true, bounds: { x: 20, y: 70, width: 420, height: 300 } }); expect(await restarted.evaluate(async ({ webContents }, expectedUrl) => { const contents = webContents.getAllWebContents().find((item) => item.getURL() === expectedUrl); return await contents?.executeJavaScript('document.querySelector("h1")?.textContent'); }, started.browser!.url)).toBe('My existing game'); await page.screenshot({ path: test.info().outputPath('restored-work-without-model.png') }); } finally { if (!serverClosed) await new Promise((resolve) => server.close(() => resolve())); const log = await readFile(join(projectPath, '.makelore/logs/static-preview.log'), 'utf8').catch(() => ''); for (const match of log.matchAll(/Makelore launch pid=(\d+) /g)) { const pid = Number(match[1]); try { process.kill(process.platform === 'win32' ? pid : -pid, 'SIGTERM'); } catch { /* Test child has already exited. */ } } } });