Files
ycode/scripts/verify-seven-ui-fixes.cjs
T

656 lines
30 KiB
JavaScript

// Isolated production YCode verification for N01, N03 and N07.
// No remote model request, notification fetch, or system integration is performed.
const fs = require('node:fs/promises')
const path = require('node:path')
const assert = require('node:assert/strict')
if (!process.versions.electron) {
;(async () => {
const directory = await fs.mkdtemp(path.resolve('.tmp/seven-ui-fixes-'))
const data = path.join(directory, 'data')
const home = path.join(directory, 'home')
const workspace = path.join(directory, 'workspace')
await Promise.all([
fs.mkdir(data, { recursive: true }),
fs.mkdir(home, { recursive: true }),
fs.mkdir(workspace, { recursive: true })
])
const now = Date.now()
const modelId = 'seven-ui-model'
const workspaceId = 'seven-ui-workspace'
const sessionId = 'seven-ui-session'
await fs.writeFile(path.join(data, 'models.json'), JSON.stringify({
version: 1,
selectedModelId: modelId,
models: [{
id: modelId,
name: 'UI Fixture Model',
providerId: 'custom',
model: 'fixture-model',
baseUrl: 'http://127.0.0.1:1/v1',
authMode: 'none',
supportsToolCall: false
}]
}))
await fs.writeFile(path.join(data, 'workspaces.json'), JSON.stringify({
selectedWorkspaceId: workspaceId,
workspaces: [{ id: workspaceId, name: 'Seven UI Fixture', root: workspace, createdAt: now }]
}))
await fs.writeFile(path.join(data, 'sessions.json'), JSON.stringify({
sessions: [{
id: sessionId,
title: '七项 UI 修复验收',
cwd: workspace,
workspaceId,
modelId,
createdAt: now,
updatedAt: now,
messages: [
{ id: 'seven-ui-user', role: 'user', content: '只做本地 UI 验收', createdAt: now - 1, status: 'done' },
{ id: 'seven-ui-assistant', role: 'assistant', content: '已准备本地 UI 验证。', createdAt: now, status: 'done', parts: [], tools: [] }
]
}]
}))
const env = {
...process.env,
YCODE_QA_DIRECTORY: directory,
YCODE_USER_DATA_DIR: data, YCODE_DISABLE_GPU: '1',
YCODE_QA_OFFSCREEN: '1',
YCODE_DISABLE_SYSTEM_INTEGRATION: '1'
}
delete env.ELECTRON_RUN_AS_NODE
delete env.ELECTRON_RENDERER_URL
const child = require('node:child_process').spawn(
require('electron'),
['--no-sandbox', '--disable-gpu-sandbox', __filename],
{ env, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }
)
let log = ''
child.stdout.on('data', (bytes) => {
log += bytes
if (bytes.toString().includes('SEVEN_UI_FIXES_')) process.stdout.write(bytes)
})
child.stderr.on('data', (bytes) => { log += bytes })
const timer = setTimeout(() => child.kill(), 180_000)
child.once('exit', async (code) => {
clearTimeout(timer)
await fs.writeFile(path.join(directory, 'process.log'), log)
console.log(JSON.stringify({ directory, exitCode: code }))
if (code) console.error(log.slice(-12_000))
process.exitCode = code ?? 1
})
})().catch((error) => {
console.error(error)
process.exitCode = 1
})
} else {
const { app, BrowserWindow } = require('electron')
const { pathToFileURL } = require('node:url')
const directory = process.env.YCODE_QA_DIRECTORY
const data = process.env.YCODE_USER_DATA_DIR
assert.ok(directory)
assert.ok(data)
const relative = path.relative(path.resolve('.tmp'), directory)
assert.ok(relative.startsWith('seven-ui-fixes-') && !relative.includes(path.sep))
app.setPath('userData', data)
app.setPath('sessionData', data)
app.setPath('home', path.join(directory, 'home'))
let main
let phase = 'boot'
const checks = []
const geometry = {}
const snapshots = {}
const screenshots = []
const rendererErrors = []
const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
async function read(source) {
let timer
try {
return await Promise.race([
main.webContents.executeJavaScript(source),
new Promise((_, reject) => {
timer = setTimeout(() => reject(new Error(`Renderer timeout: ${source.slice(0, 140)}`)), 20_000)
})
])
} finally {
clearTimeout(timer)
}
}
async function wait(source, label) {
for (let index = 0; index < 400; index += 1) {
const result = await read(source)
if (result) return result
await pause(40)
}
throw new Error(`Timeout: ${label}`)
}
async function step(label) {
phase = label
await fs.writeFile(path.join(directory, 'phase.txt'), label)
}
function check(id, label, actual, expected) {
const pass = typeof expected === 'function'
? Boolean(expected(actual))
: JSON.stringify(actual) === JSON.stringify(expected)
checks.push({
id,
label,
pass,
actual,
expected: typeof expected === 'function' ? expected.toString() : expected
})
}
function opaqueColor(value) {
if (!value || value === 'transparent') return false
const match = /^rgba?\((.+)\)$/i.exec(value)
if (!match) return true
const parts = match[1].split(',').map((part) => part.trim())
return parts.length < 4 || Number(parts[3]) >= 0.999
}
function insideViewport(box, viewport) {
return box[0] >= -0.5
&& box[1] >= -0.5
&& box[0] + box[2] <= viewport.width + 0.5
&& box[1] + box[3] <= viewport.height + 0.5
}
async function capture(name) {
await pause(160)
const image = (await main.webContents.capturePage()).toPNG()
const filename = `${name}.png`
await fs.writeFile(path.join(directory, filename), image)
screenshots.push(filename)
}
async function setTheme(theme) {
await read(`(() => {
localStorage.setItem('ycode:theme', ${JSON.stringify(theme)})
window.dispatchEvent(new StorageEvent('storage', {
key: 'ycode:theme',
newValue: ${JSON.stringify(theme)}
}))
return true
})()`)
await wait(
`document.documentElement.dataset.theme === ${JSON.stringify(theme)}`,
`${theme} theme`
)
await pause(180)
}
async function pressEscape() {
await read(`(() => {
document.dispatchEvent(new KeyboardEvent('keydown', {
key: 'Escape',
code: 'Escape',
bubbles: true,
cancelable: true
}))
return true
})()`)
}
async function verifyProjectMenu(theme) {
await setTheme(theme)
await step(`N01-project-menu-${theme}`)
const trigger = await read(`(() => {
const node = document.querySelector('.conversation-list-tab-button-more')
if (!node) return null
node.click()
return true
})()`)
assert.ok(trigger, 'project menu trigger')
await wait(`!!document.querySelector('.yc-project-more')`, 'project menu')
const snapshot = await read(`(() => {
const menu = document.querySelector('.yc-project-more')
const style = getComputedStyle(menu)
const rect = menu.getBoundingClientRect()
return {
box: [rect.x, rect.y, rect.width, rect.height],
background: style.backgroundColor,
opacity: style.opacity,
parentIsBody: menu.parentElement === document.body,
viewport: { width: innerWidth, height: innerHeight },
text: menu.textContent
}
})()`)
snapshots[`projectMenu-${theme}`] = snapshot
geometry[`projectMenu-${theme}`] = snapshot
check(`n01-${theme}-background`, 'project menu uses an opaque theme surface', opaqueColor(snapshot.background), true)
check(`n01-${theme}-viewport`, 'project menu stays inside the viewport', insideViewport(snapshot.box, snapshot.viewport), true)
check(`n01-${theme}-portal`, 'project menu is portaled to body', snapshot.parentIsBody, true)
check(`n01-${theme}-visible`, 'project menu is fully visible', snapshot.opacity, '1')
await capture(`n01-project-menu-${theme}`)
await pressEscape()
await wait(`!document.querySelector('.yc-project-more')`, 'project menu closes')
}
async function verifyNotifications(theme, closeWithEscape) {
await setTheme(theme)
await step(`N03-notifications-${theme}`)
await read(`document.querySelector('.user-menu-trigger-msg-center').click()`)
await wait(
`!!document.querySelector('.notif-panel[data-positioned="true"]')`,
'notification panel'
)
const snapshot = await read(`(() => {
const panel = document.querySelector('.notif-panel')
const bell = document.querySelector('.user-menu-trigger-msg-center')
const style = getComputedStyle(panel)
const rect = panel.getBoundingClientRect()
const points = [
[rect.left + rect.width * 0.25, rect.top + rect.height * 0.25],
[rect.left + rect.width * 0.75, rect.top + rect.height * 0.25],
[rect.left + rect.width * 0.5, rect.top + rect.height * 0.5],
[rect.left + rect.width * 0.5, rect.top + rect.height * 0.75]
]
return {
box: [rect.x, rect.y, rect.width, rect.height],
background: style.backgroundColor,
opacity: style.opacity,
parentIsBody: panel.parentElement === document.body,
viewport: { width: innerWidth, height: innerHeight },
bellExpanded: bell.getAttribute('aria-expanded'),
bellControls: bell.getAttribute('aria-controls'),
unobstructed: points.every(([x, y]) => {
const hit = document.elementFromPoint(x, y)
return !!hit && panel.contains(hit)
})
}
})()`)
snapshots[`notifications-${theme}`] = snapshot
geometry[`notifications-${theme}`] = snapshot
check(`n03-${theme}-background`, 'notification panel uses an opaque theme surface', opaqueColor(snapshot.background), true)
check(`n03-${theme}-viewport`, 'notification panel stays inside the viewport', insideViewport(snapshot.box, snapshot.viewport), true)
check(`n03-${theme}-portal`, 'notification panel is portaled to body', snapshot.parentIsBody, true)
check(`n03-${theme}-unobstructed`, 'notification content is not covered', snapshot.unobstructed, true)
check(`n03-${theme}-aria`, 'bell exposes the open dialog state', snapshot.bellExpanded, 'true')
check(`n03-${theme}-controls`, 'bell controls the notification dialog', snapshot.bellControls, 'ycode-notifications-panel')
await capture(`n03-notifications-${theme}`)
if (closeWithEscape) await pressEscape()
else await read(`document.querySelector('.notif-panel__close').click()`)
await wait(`!document.querySelector('.notif-panel')`, 'notification panel closes')
await pause(80)
check(
`n03-${theme}-focus`,
'closing the notification restores bell focus',
await read(`document.activeElement?.classList.contains('user-menu-trigger-msg-center')`),
true
)
check(
`n03-${theme}-closed`,
'bell reports the closed state',
await read(`document.querySelector('.user-menu-trigger-msg-center').getAttribute('aria-expanded')`),
'false'
)
}
async function openPermissionPanel() {
const opened = await read(`(() => {
const trigger = [...document.querySelectorAll('.cr-permission-setting')]
.find((node) => node.offsetParent !== null)
if (!trigger) return false
trigger.click()
return true
})()`)
assert.ok(opened, 'visible permission trigger')
await wait(`!!document.querySelector('.cr-permission-setting-popover .cr-permission-panel')`, 'permission panel')
}
async function verifyPermissionPanel(theme) {
await setTheme(theme)
await step(`N07-permission-panel-${theme}`)
await openPermissionPanel()
const snapshot = await read(`(() => {
const popover = document.querySelector('.cr-permission-setting-popover')
const panel = popover.querySelector('.cr-permission-panel')
const description = panel.querySelector('.cr-permission-panel__desc')
const rect = panel.getBoundingClientRect()
const style = getComputedStyle(panel)
const trigger = [...document.querySelectorAll('.cr-permission-setting')]
.find((node) => node.offsetParent !== null)
return {
box: [rect.x, rect.y, rect.width, rect.height],
popoverWidth: popover.getBoundingClientRect().width,
triggerLeft: trigger.getBoundingClientRect().left,
viewport: { width: innerWidth, height: innerHeight },
background: style.backgroundColor,
opacity: style.opacity,
outsideComposer: !document.querySelector('.conversation-input')?.contains(popover)
&& !document.querySelector('.chat-pane')?.contains(popover),
role: panel.getAttribute('role'),
switchCount: panel.querySelectorAll('[role="switch"]').length,
rowCount: panel.querySelectorAll('.cr-permission-panel__row').length,
description: description?.textContent ?? '',
triggerLabel: trigger?.querySelector('.cr-input-footer-item__label')?.textContent ?? ''
}
})()`)
snapshots[`permissionPanel-${theme}`] = snapshot
geometry[`permissionPanel-${theme}`] = snapshot
check(`n07-${theme}-width`, 'permission panel keeps the recovered 200px width', Math.round(snapshot.box[2]), 200)
check(`n07-${theme}-outer-width`, 'permission popover adds no second padded surface', Math.round(snapshot.popoverWidth), 200)
check(`n07-${theme}-alignment`, 'permission panel starts at the trigger edge', Math.abs(snapshot.box[0] - snapshot.triggerLeft) < 1, true)
check(`n07-${theme}-background`, 'permission panel uses an opaque theme surface', opaqueColor(snapshot.background), true)
check(`n07-${theme}-viewport`, 'permission panel stays inside the viewport', insideViewport(snapshot.box, snapshot.viewport), true)
check(`n07-${theme}-portal`, 'permission panel is outside the composer portal ancestor', snapshot.outsideComposer, true)
check(`n07-${theme}-switch-count`, 'permission panel has one full-access switch', snapshot.switchCount, 1)
check(`n07-${theme}-row-count`, 'permission panel has one permission row', snapshot.rowCount, 1)
check(
`n07-${theme}-description`,
'permission panel describes local default permissions without promising an OS sandbox',
snapshot.description,
'当前为默认权限,所有操作都会在默认权限约束内进行,超出范围会请求你的允许。'
)
check(`n07-${theme}-default-label`, 'permission trigger defaults to 默认权限', snapshot.triggerLabel, '默认权限')
await capture(`n07-permission-panel-${theme}`)
await pressEscape()
await wait(`!document.querySelector('.cr-permission-setting-popover')`, 'permission panel closes')
}
async function openPermissionConfirmation() {
if (!(await read(`(() => {
const panel = document.querySelector('.cr-permission-setting-popover .cr-permission-panel')
return !!panel && panel.getClientRects().length > 0
})()`))) {
await openPermissionPanel()
}
await read(`document.querySelector('.cr-permission-setting-popover .wb-switch__input').click()`)
await wait(`!!document.querySelector('.permission-confirm-dialog')`, 'permission confirmation')
}
async function verifyPermissionConfirmation(theme) {
await setTheme(theme)
await step(`N07-permission-confirm-${theme}`)
await openPermissionConfirmation()
const snapshot = await read(`(() => {
const dialog = document.querySelector('.permission-confirm-dialog')
const style = getComputedStyle(dialog)
const overlay = dialog.closest('.wb-modal__overlay')
const popover = document.querySelector('.cr-permission-setting-popover')
const panel = popover?.querySelector('.cr-permission-panel')
const overlayZIndex = overlay ? Number(getComputedStyle(overlay).zIndex) : Number.NaN
const panelZIndex = popover ? Number(getComputedStyle(popover).zIndex) : Number.NaN
const rect = dialog.getBoundingClientRect()
const buttons = [...dialog.querySelectorAll('button')]
const confirm = buttons.find((button) => button.textContent.trim() === '允许完全访问')
const cancel = buttons.find((button) => button.textContent.trim() === '取消')
return {
box: [rect.x, rect.y, rect.width, rect.height],
viewport: { width: innerWidth, height: innerHeight },
background: style.backgroundColor,
overlayZIndex,
panelZIndex,
modalAbovePanel: Number.isFinite(overlayZIndex)
&& Number.isFinite(panelZIndex)
&& overlayZIndex > panelZIndex,
description: dialog.querySelector('.cr-permission-confirm__desc')?.textContent ?? '',
acknowledgement: dialog.querySelector('.permission-confirm-acknowledgement')?.checked ?? null,
confirmDisabled: confirm?.disabled ?? null,
confirmDanger: confirm?.classList.contains('wb-composer__danger-button') ?? false,
hasCancel: !!cancel,
panelOpen: !!panel && panel.getClientRects().length > 0,
outsideComposer: !document.querySelector('.conversation-input')?.contains(dialog)
&& !document.querySelector('.chat-pane')?.contains(dialog),
triggerLabel: [...document.querySelectorAll('.cr-permission-setting')]
.find((node) => node.offsetParent !== null)
?.querySelector('.cr-input-footer-item__label')?.textContent ?? ''
}
})()`)
snapshots[`permissionConfirm-${theme}`] = snapshot
geometry[`permissionConfirm-${theme}`] = snapshot
check(`n07-${theme}-confirm-width`, 'permission confirmation keeps the recovered 480px width', Math.round(snapshot.box[2]), 480)
check(`n07-${theme}-confirm-viewport`, 'permission confirmation stays inside the viewport', insideViewport(snapshot.box, snapshot.viewport), true)
check(`n07-${theme}-confirm-background`, 'permission confirmation uses an opaque surface', opaqueColor(snapshot.background), true)
check(`n07-${theme}-confirm-portal`, 'permission confirmation is outside the composer portal ancestor', snapshot.outsideComposer, true)
check(`n07-${theme}-confirm-layer`, 'permission confirmation uses the recovered body portal layer', snapshot.overlayZIndex, 100001)
check(`n07-${theme}-confirm-above-panel`, 'permission confirmation stays above the open panel', snapshot.modalAbovePanel, true)
check(`n07-${theme}-panel-remains-open`, 'permission panel remains open behind confirmation', snapshot.panelOpen, true)
check(`n07-${theme}-confirm-description`, 'permission confirmation states the implemented task scope',
snapshot.description,
'开启完全访问后,当前发送的任务将减少确认步骤并直接操作您的电脑,请谨慎操作。包括以下内容:')
check(`n07-${theme}-ack-empty`, 'risk acknowledgement starts unchecked', snapshot.acknowledgement, false)
check(`n07-${theme}-danger-disabled`, 'full-access confirmation is disabled before acknowledgement', snapshot.confirmDisabled, true)
check(`n07-${theme}-danger-button`, 'full-access confirmation uses the dangerous action button', snapshot.confirmDanger, true)
check(`n07-${theme}-cancel`, 'permission confirmation exposes cancel', snapshot.hasCancel, true)
await capture(`n07-permission-confirm-${theme}`)
await read(`(() => {
const dialog = document.querySelector('.permission-confirm-dialog')
const cancel = [...dialog.querySelectorAll('button')]
.find((button) => button.textContent.trim() === '取消')
cancel.click()
return true
})()`)
await wait(`!document.querySelector('.permission-confirm-dialog')`, 'permission confirmation closes')
check(`n07-${theme}-cancel-keeps-default`, 'cancelling confirmation keeps default permission',
await read(`(() => {
const trigger = [...document.querySelectorAll('.cr-permission-setting')]
.find((node) => node.offsetParent !== null)
return {
label: trigger.querySelector('.cr-input-footer-item__label').textContent,
danger: trigger.classList.contains('cr-permission-setting--danger')
}
})()`),
{ label: '默认权限', danger: false })
const panelAfterCancel = await read(`(() => {
const panel = document.querySelector('.cr-permission-setting-popover .cr-permission-panel')
return !!panel && panel.getClientRects().length > 0
})()`)
check(`n07-${theme}-cancel-panel-open`, 'cancelling confirmation keeps the permission panel open', panelAfterCancel, true)
check(`n07-${theme}-cancel-label`, 'default label remains after cancellation', snapshot.triggerLabel, '默认权限')
await read(`document.querySelector('.cr-permission-setting-popover .wb-switch__input').click()`)
await wait(`!!document.querySelector('.permission-confirm-dialog')`, 'permission confirmation reopens from the same panel')
const reopened = await read(`(() => {
const dialog = document.querySelector('.permission-confirm-dialog')
const acknowledgement = dialog.querySelector('.permission-confirm-acknowledgement')
const confirm = [...dialog.querySelectorAll('button')]
.find((button) => button.textContent.trim() === '允许完全访问')
const panel = document.querySelector('.cr-permission-setting-popover .cr-permission-panel')
return {
panelOpen: !!panel && panel.getClientRects().length > 0,
acknowledgement: acknowledgement?.checked ?? null,
confirmDisabled: confirm?.disabled ?? null
}
})()`)
snapshots[`permissionConfirmReopened-${theme}`] = reopened
check(`n07-${theme}-cancel-direct-reopen`, 'the switch can reopen confirmation without reopening the trigger', reopened.panelOpen, true)
check(`n07-${theme}-reopen-ack-empty`, 'reopened confirmation still requires risk acknowledgement', reopened.acknowledgement, false)
check(`n07-${theme}-reopen-disabled`, 'reopened confirmation stays disabled until risk acknowledgement',
reopened.confirmDisabled, true)
await read(`(() => {
const dialog = document.querySelector('.permission-confirm-dialog')
const cancel = [...dialog.querySelectorAll('button')]
.find((button) => button.textContent.trim() === '取消')
cancel.click()
return true
})()`)
await wait(`!document.querySelector('.permission-confirm-dialog')`, 'permission confirmation closes after direct reopen')
await pressEscape()
await wait(`!document.querySelector('.cr-permission-setting-popover')`, 'permission panel closes after cancellation checks')
}
async function verifyFullAccessRoundTrip() {
await setTheme('light')
await step('N07-full-access-confirm')
await openPermissionConfirmation()
const confirmation = await read(`(() => {
const dialog = document.querySelector('.permission-confirm-dialog')
const acknowledgement = dialog.querySelector('.permission-confirm-acknowledgement')
acknowledgement.click()
const confirm = [...dialog.querySelectorAll('button')]
.find((button) => button.textContent.trim() === '允许完全访问')
return {
acknowledgement: acknowledgement.checked,
enabled: !confirm.disabled
}
})()`)
snapshots.fullAccessConfirmation = confirmation
check('n07-full-ack-checked', 'acknowledging the risk checks the source checkbox', confirmation.acknowledgement, true)
check('n07-full-confirm-enabled', 'acknowledgement enables full-access confirmation', confirmation.enabled, true)
await capture('n07-full-access-acknowledged')
await read(`(() => {
const dialog = document.querySelector('.permission-confirm-dialog')
const confirm = [...dialog.querySelectorAll('button')]
.find((button) => button.textContent.trim() === '允许完全访问')
confirm.click()
return true
})()`)
await wait(`(() => {
const trigger = [...document.querySelectorAll('.cr-permission-setting')]
.find((node) => node.offsetParent !== null)
return trigger?.classList.contains('cr-permission-setting--danger')
&& trigger.querySelector('.cr-input-footer-item__label')?.textContent === '允许完全访问'
})()`, 'full-access mode')
check('n07-full-dialog-closed', 'confirming full access closes the dialog', await read(`!document.querySelector('.permission-confirm-dialog')`), true)
check('n07-full-panel-closed', 'confirming full access closes the permission panel', await read(`!document.querySelector('.cr-permission-setting-popover')`), true)
snapshots.fullAccess = await read(`(() => {
const trigger = [...document.querySelectorAll('.cr-permission-setting')]
.find((node) => node.offsetParent !== null)
return {
label: trigger.querySelector('.cr-input-footer-item__label')?.textContent,
danger: trigger.classList.contains('cr-permission-setting--danger')
}
})()`)
check('n07-full-label', 'full access updates the visible trigger', snapshots.fullAccess,
{ label: '允许完全访问', danger: true })
await capture('n07-full-access')
await step('N07-restore-default')
await openPermissionPanel()
const reopened = await read(`(() => ({
switchCount: document.querySelectorAll('.cr-permission-setting-popover [role="switch"]').length,
checked: document.querySelector('.cr-permission-setting-popover .wb-switch__input').checked
}))()`)
snapshots.fullAccessReopened = reopened
check('n07-full-reopen-switch', 'reopened permission panel shows one enabled full-access switch', reopened, {
switchCount: 1,
checked: true
})
await read(`document.querySelector('.cr-permission-setting-popover .wb-switch__input').click()`)
await wait(`!document.querySelector('.cr-permission-setting-popover')`, 'permission panel closes after restoring default')
await wait(`(() => {
const trigger = [...document.querySelectorAll('.cr-permission-setting')]
.find((node) => node.offsetParent !== null)
return trigger.querySelector('.cr-input-footer-item__label')?.textContent === '默认权限'
&& !trigger.classList.contains('cr-permission-setting--danger')
})()`, 'default permission restored')
snapshots.restoredDefault = await read(`(() => {
const trigger = [...document.querySelectorAll('.cr-permission-setting')]
.find((node) => node.offsetParent !== null)
return {
label: trigger.querySelector('.cr-input-footer-item__label')?.textContent,
danger: trigger.classList.contains('cr-permission-setting--danger')
}
})()`)
check('n07-default-restored', 'turning the reopened switch off restores default permission',
snapshots.restoredDefault, { label: '默认权限', danger: false })
await capture('n07-default-restored')
}
;(async () => {
await import(pathToFileURL(path.resolve('out/main/index.mjs')).href)
await app.whenReady()
for (let index = 0; index < 300; index += 1) {
main = BrowserWindow.getAllWindows().find((window) => window.getTitle() === 'YCode')
if (main && !main.webContents.isLoading()) break
await pause(40)
}
assert.ok(main, 'YCode main window')
main.setSize(1336, 902)
main.showInactive()
main.webContents.on('console-message', (...args) => {
const event = args[0]
const level = typeof event === 'object' ? event.level : args[1]
const message = typeof event === 'object' ? event.message : args[2]
if (level === 'error' || Number(level) >= 3) rendererErrors.push(message)
})
await wait(
`!!window.ycode && !!document.querySelector('.sidebar') && !!document.querySelector('.chat-pane')`,
'isolated production renderer'
)
await read(`document.querySelector('[data-session-id="seven-ui-session"]')?.click()`)
await wait(
`(() => {
const trigger = [...document.querySelectorAll('.cr-permission-setting')]
.find((node) => node.offsetParent !== null)
return !!trigger
})()`,
'visible permission setting'
)
await verifyProjectMenu('light')
await verifyNotifications('light', false)
await verifyPermissionPanel('light')
await verifyPermissionConfirmation('light')
await verifyProjectMenu('dark')
await verifyNotifications('dark', true)
await verifyPermissionPanel('dark')
await verifyPermissionConfirmation('dark')
await verifyFullAccessRoundTrip()
check('console-errors', 'renderer has no console errors', rendererErrors, [])
const report = {
result: checks.every((item) => item.pass) ? 'PASS' : 'FAIL',
scope: 'Isolated production YCode renderer/preload/IPC with local model, workspace and session fixtures. No remote model, notification, connector, or system requests are issued.',
fixture: {
directory,
modelId: 'seven-ui-model',
workspaceId: 'seven-ui-workspace',
sessionId: 'seven-ui-session',
viewport: await read(`({ width: innerWidth, height: innerHeight, theme: document.documentElement.dataset.theme })`)
},
sourceEvidence: [
'ev_d1a438b97437b2f6a98f5dddcc5c3385184e24a439c5a351365fbbefc39e457d',
'ev_f5e9d71a676c453daab40928acfb33aeabae68ba57ffda342380cead7f1a1fe2',
'ev_377c8790468b3c67dad8577aabb327f98d3140659f0fffdbabfcff5a40cc9e94',
'ev_717fa5e846e64d67ac19c8925d121bff632c0d3aefb3603b10858559cabb2dcb'
],
checks,
snapshots,
geometry,
screenshots,
rendererErrors,
limitations: [
'The local model fixture is deliberately unreachable and no model request is made.',
'Notification content remains mock data; this verifier checks the shell, portal, geometry, and close behavior.',
'Project menu still contains only the currently implemented Project entry.'
]
}
await fs.writeFile(path.join(directory, 'report.json'), JSON.stringify(report, null, 2))
console.log(`SEVEN_UI_FIXES_${report.result} ${directory}`)
for (const failure of checks.filter((item) => !item.pass)) {
console.log(`SEVEN_UI_FIXES_FAILURE ${JSON.stringify(failure)}`)
}
app.exit(report.result === 'PASS' ? 0 : 1)
})().catch(async (error) => {
if (main && !main.isDestroyed()) {
await fs.writeFile(
path.join(directory, 'failure.png'),
(await main.webContents.capturePage()).toPNG()
).catch(() => undefined)
}
await fs.writeFile(path.join(directory, 'failure.json'), JSON.stringify({
phase,
error: String(error?.stack || error),
checks,
snapshots,
geometry,
rendererErrors
}, null, 2)).catch(() => undefined)
console.error(error)
app.exit(1)
})
}