Files
goodbuddy/tests/supervisor-layout.electron.test.ts
T
mesalogo b9186a616c fix: retain historical events in the story spiral
Render every selected review event, including events without current story membership, and label current grouping without inventing historical assignments. Keep history selection inside the central canvas heading, use shared camera controls, and fit the default scene within the viewport.

Validation: 46 focused tests, synthetic and read-only portable-data Electron checks, Node/Agent/Web typechecks, targeted ESLint, and diff checks passed. All 21 reported events remain represented at wide and narrow widths; no live data writes or model calls.

Release note: Historical story spirals now retain unassigned events. Review history sits inside the canvas column, and camera controls have consistent spacing and framing.
2026-10-04 21:09:32 +08:00

118 lines
4.7 KiB
TypeScript

// @vitest-environment node
import { spawn } from 'node:child_process'
import { createRequire } from 'node:module'
import { copyFile, mkdtemp, readFile, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join, resolve } from 'node:path'
import { createServer } from 'vite'
import react from '@vitejs/plugin-react'
import { expect, it } from 'vitest'
import { DatabaseSync } from 'node:sqlite'
import { AssistantDatabase } from '../src/main/assistant/assistant-database'
import { SupervisionStoryStore } from '../src/main/assistant/supervision-stories'
import { SupervisionExperienceStore } from '../src/main/assistant/supervision-experiences'
it(process.env.GOODBUDDY_SUPERVISOR_SIDEBAR
? 'renders the production supervision card at narrow widths in both themes'
: 'renders the production supervisor at desktop, narrow, and mobile widths', async () => {
const directory = await mkdtemp(join(tmpdir(), 'goodbuddy-supervisor-'))
// Optional local acceptance run against an online backup. Never initialize/migrate or run models.
let portable: string | undefined
if (process.env.GOODBUDDY_SUPERVISOR_BACKUP) {
const connection = new DatabaseSync(process.env.GOODBUDDY_SUPERVISOR_BACKUP, { readOnly: true })
try {
const database = new AssistantDatabase(process.env.GOODBUDDY_SUPERVISOR_BACKUP)
Reflect.set(database, 'database', connection)
const results = database.listSupervisionResults()
portable = JSON.stringify({ results,
projects: connection.prepare('SELECT id, name FROM projects').all(),
graphs: Object.fromEntries(results.map(result => [result.id, database.getSupervisionGraph({ resultId: result.id })])),
stories: Object.fromEntries(results.map(result => [JSON.stringify(result.scope), {
stories: new SupervisionStoryStore(connection).list(result.scope),
experiences: new SupervisionExperienceStore(connection).list(result.scope.kind === 'projects' ? result.scope.projectIds : undefined),
unassigned: new SupervisionStoryStore(connection).unassignedCount(result.scope), canUndo: false
}]))
})
} finally { connection.close() }
}
const css = await readFile(
'src/renderer/src/supervisor-workspace.css',
'utf8'
)
const server = await createServer({
configFile: false,
root: resolve('.'),
cacheDir: join(directory, 'vite'),
plugins: [
react(),
{
name: 'supervisor-fixture',
configureServer(server) {
if (portable) server.middlewares.use('/portable-review.json', (_request, response) => {
response.setHeader('Content-Type', 'application/json')
response.end(portable)
})
server.middlewares.use(
'/supervisor.html',
async (_request, response) => {
response.setHeader('Content-Type', 'text/html; charset=utf-8')
response.end(
await server.transformIndexHtml(
'/supervisor.html',
'<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>SIMULATED Supervisor validation</title><body><div id="root"></div><script type="module" src="/tests/support/supervisor-layout-regression.tsx"></script></body></html>'
)
)
}
)
}
}
],
server: { host: '127.0.0.1', port: 0, watch: null }
})
try {
await server.listen()
const env: NodeJS.ProcessEnv = {
...process.env,
GOODBUDDY_SUPERVISOR_URL:
server.resolvedUrls!.local[0] + 'supervisor.html' + (process.env.GOODBUDDY_SUPERVISOR_SIDEBAR ? '?sidebar=1' : ''),
GOODBUDDY_SUPERVISOR_DIRECTORY: directory,
GOODBUDDY_SUPERVISOR_TOKENS: JSON.stringify([
...new Set(
[...css.matchAll(/var\((--[\w-]+)\)/g)].map((match) => match[1])
)
])
}
delete env.ELECTRON_RUN_AS_NODE
const driver = join(directory, 'driver.mjs')
await copyFile(
resolve('tests/support/supervisor-layout-driver.mjs'),
driver
)
const child = spawn(createRequire(import.meta.url)('electron'), [driver], {
env,
stdio: ['ignore', 'pipe', 'pipe']
})
let output = ''
child.stdout.on('data', (data) => {
output += data
})
child.stderr.on('data', (data) => {
output += data
})
const timeout = setTimeout(() => child.kill(), 90000)
try {
const code = await new Promise((resolve, reject) => {
child.once('exit', resolve)
child.once('error', reject)
})
expect(code, output).toBe(0)
console.log(output)
} finally {
clearTimeout(timeout)
}
} finally {
await server.close()
await rm(directory, { recursive: true, force: true })
}
}, 120000)