@@ -0,0 +1,312 @@
// TODO 10 验收:设置 → 通用面板的原版几何/排版 + YCode 真实能力接线 + 未接入项不伪装。
// 真实隔离 YCode(真实 preload/IPC/渲染进程/文件系统)+ 原生设置窗口,视口 900×680(与原版一致)。
//
// 原版依据(REA):
// - ev_2309de12a003fca2c863a223cbda5bccad36b3af73e29bc309fdaed94745cf9e(面板 DOM/几何/computed style/svg)
// - ev_a2dbeb23e30b9770743e857279a2e43da38fcc48d10612f75ace2938c5b29114( 5.8.3 面板滚动采样 + 文案)
// 原版实测:面板 [232,40,638,1415];组 常规 218 / 权限 514 / 存储 265 / 通知 234 / 隐私 120;
// 行高 [44,68,52 | 56,68,68,76,68,68,56 | 56,68,56 | 66];卡片 bg #fff / radius 12 / padding 12px 16px;
// 标题 14px/500/22px;描述 14px/400/22px rgba(0,0,0,.5);开关 34× 20;下拉 120× 32;滑块 224× 40。
const fs = require ( 'node:fs/promises' )
const path = require ( 'node:path' )
const assert = require ( 'node:assert/strict' )
const EXPECTED _TITLES = [ '语言' , '链接打开方式' , '字体大小' , '允许锁屏运行' , '运行时防止系统休眠' , '开机自动启动' ,
'网络代理' , '自动安装可信技能' , '技能自动更新' , '套件自动更新' , '桌面通知' , '通知提示音' , '定时任务产物通知' , '体验优化计划' ]
const EXPECTED _ROW _HEIGHTS = [ 44 , 68 , 52 , 56 , 68 , 68 , 76 , 68 , 68 , 56 , 56 , 68 , 56 , 66 ]
const EXPECTED _GROUPS = [ '常规' , '权限' , '通知' , '隐私' ]
const EXPECTED _PLACEHOLDERS = 11
if ( ! process . versions . electron ) {
; ( async ( ) => {
const directory = await fs . mkdtemp ( path . resolve ( '.tmp/general-settings-' ) )
const data = path . join ( directory , 'data' )
const home = path . join ( directory , 'home' )
const nextWorkspace = path . join ( directory , 'workspace-root' )
await fs . mkdir ( data , { recursive : true } )
await fs . mkdir ( home , { recursive : true } )
await fs . mkdir ( nextWorkspace , { recursive : true } )
const env = { ... process . env , YCODE _QA _DIRECTORY : directory , YCODE _USER _DATA _DIR : data , YCODE _OMP _HOME : home ,
YCODE _DISABLE _GPU : '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 output = ''
child . stdout . on ( 'data' , bytes => { output += bytes ; if ( bytes . toString ( ) . includes ( 'GENERAL_SETTINGS_' ) ) process . stdout . write ( bytes ) } )
child . stderr . on ( 'data' , bytes => { output += bytes } )
const timer = setTimeout ( ( ) => child . kill ( ) , 300000 )
child . once ( 'exit' , async code => {
clearTimeout ( timer )
await fs . writeFile ( path . join ( directory , 'process.log' ) , output )
console . log ( JSON . stringify ( { directory , exitCode : code } ) )
if ( code ) console . error ( output . slice ( - 4000 ) )
process . exitCode = code ? 1 : 0
} )
} ) ( ) . 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 dataDir = process . env . YCODE _USER _DATA _DIR
const nextWorkspace = path . join ( directory , 'workspace-root' )
app . setPath ( 'home' , path . join ( directory , 'home' ) )
const checks = [ ] , snapshots = { } , rendererErrors = [ ]
let main , settings , phase = 'boot'
const pause = ms => new Promise ( resolve => setTimeout ( resolve , ms ) )
async function read ( source , win = main ) {
let timer
try {
return await Promise . race ( [ win . webContents . executeJavaScript ( source ) , new Promise ( ( _ , reject ) => {
timer = setTimeout ( ( ) => reject ( Error ( 'Renderer timeout: ' + source . slice ( 0 , 120 ) ) ) , 15000 )
} ) ] )
} finally { clearTimeout ( timer ) }
}
async function wait ( source , label , win = main ) {
for ( let index = 0 ; index < 300 ; index ++ ) { const value = await read ( source , win ) ; if ( value ) return value ; await pause ( 40 ) }
throw Error ( 'Timeout: ' + label )
}
async function step ( label ) { phase = label ; await fs . writeFile ( path . join ( directory , 'phase.txt' ) , label ) }
function check ( label , actual , expected ) {
const pass = typeof expected === 'function' ? expected ( actual ) : JSON . stringify ( actual ) === JSON . stringify ( expected )
checks . push ( { label , pass , actual , expected : typeof expected === 'function' ? expected . toString ( ) : expected } )
}
function configFile ( ) {
try { return JSON . parse ( require ( 'node:fs' ) . readFileSync ( path . join ( dataDir , 'config.json' ) , 'utf8' ) ) } catch { return { } }
}
async function openSettings ( section ) {
await read ( ` window.ycode.app.openSettings( ${ JSON . stringify ( section ) } ) ` )
for ( let index = 0 ; index < 200 ; index ++ ) {
settings = BrowserWindow . getAllWindows ( ) . find ( win => win !== main )
if ( settings && ! settings . webContents . isLoading ( ) ) break
await pause ( 25 )
}
assert . ok ( settings , 'settings window' )
settings . webContents . on ( 'console-message' , ( _event , level , message ) => { if ( level >= 2 ) rendererErrors . push ( message ) } )
await wait ( '!!document.querySelector(".settings-window-root")' , 'settings root' , settings )
await pause ( 400 )
return settings
}
const panelSource = ` (()=>{
const rect = node => { const r = node.getBoundingClientRect(); return [Math.round(r.x), Math.round(r.y), Math.round(r.width), Math.round(r.height)] }
const panel = document.querySelector('.general-settings-panel')
const groups = [...document.querySelectorAll('.general-settings-panel__group')]
const sections = [...document.querySelectorAll('.general-settings-panel__group-card > .general-settings-panel__section')]
const storage = document.querySelector('.wb-storage')
const styles = node => { const s = getComputedStyle(node); return { fontSize: s.fontSize, fontWeight: s.fontWeight, lineHeight: s.lineHeight,
color: s.color, background: s.backgroundColor, borderRadius: s.borderRadius, padding: s.padding, gap: s.gap, paddingLeft: s.paddingLeft } }
const placeholderNodes = [...document.querySelectorAll('.general-settings-panel [data-settings-unavailable=true]')]
return {
panel: panel ? rect(panel) : null,
spacing3: panel ? getComputedStyle(panel).getPropertyValue('--wb-spacing-3').trim() : null,
labelPaddingLeft: panel ? getComputedStyle(panel.querySelector('.general-settings-panel__group-label')).paddingLeft : null,
order: panel ? [...panel.children].map(node => node.className.split(' ')[0]) : [],
groups: groups.map(node => ({ label: node.querySelector('.general-settings-panel__group-label')?.textContent?.trim(),
bounds: rect(node), labelBounds: rect(node.querySelector('.general-settings-panel__group-label')),
card: styles(node.querySelector('.general-settings-panel__group-card')) })),
groupCount: groups.length,
titles: sections.map(node => node.querySelector('.general-settings-panel__section-title')?.textContent?.trim()),
rowBounds: sections.map(rect),
rowHeight: sections.map(node => Math.round(node.getBoundingClientRect().height)),
titleStyle: styles(sections[0].querySelector('.general-settings-panel__section-title')),
descStyle: styles(sections[1].querySelector('.general-settings-panel__section-desc')),
switchWrap: rect(document.querySelector('.general-settings-panel__switch-wrap')),
switchTrack: rect(document.querySelector('.wb-switch__track')),
selects: [...document.querySelectorAll('.general-settings-panel .wb-select')].map(rect),
slider: rect(document.querySelector('.font-size-slider')),
sliderLabels: [...document.querySelectorAll('.font-size-slider__label')].map(node => node.textContent.trim()),
notificationButton: (() => { const button = [...document.querySelectorAll('.general-settings-panel .wb-button')].find(node => node.textContent.includes('前往系统设置'))
return button ? { bounds: rect(button), text: button.textContent.trim(), icons: button.querySelectorAll('svg').length } : null })(),
storage: storage ? { bounds: rect(storage), label: storage.querySelector('.wb-storage__group-label')?.textContent?.trim(),
card: rect(storage.querySelector('.wb-storage__card')), cardTitle: storage.querySelector('.wb-storage__card-title')?.textContent?.trim(),
cache: (() => { const node = storage.querySelector('.wb-storage__cache'); return node ? rect(node) : null })(),
titleRow: (() => { const node = storage.querySelector('.wb-storage__card-title-row'); return node ? rect(node) : null })(),
cachePathRow: (() => { const node = storage.querySelector('.wb-storage__path-row'); return node ? rect(node) : null })(),
overview: (() => { const node = storage.querySelector('.general-settings-panel__storage-overview'); return node ? rect(node) : null })(),
legend: (() => { const node = storage.querySelector('.general-settings-panel__storage-legend'); return node ? rect(node) : null })(),
workspace: (() => { const node = storage.querySelector('.general-settings-panel__workspace'); return node ? rect(node) : null })(),
workspaceHeader: (() => { const node = storage.querySelector('.general-settings-panel__workspace-header'); return node ? rect(node) : null })(),
workspaceDesc: (() => { const node = storage.querySelector('.general-settings-panel__workspace-path-desc'); return node ? rect(node) : null })(),
openFolder: rect(storage.querySelector('.wb-storage__open-folder')), pathText: storage.querySelector('.wb-storage__path-text')?.textContent?.trim(),
usageText: storage.querySelector('.general-settings-panel__storage-usage-text')?.textContent?.replace(/ \\ s+/g, ' ').trim(),
legend: [...storage.querySelectorAll('.general-settings-panel__storage-legend-label')].map(node => node.textContent.trim()),
segments: [...storage.querySelectorAll('.general-settings-panel__storage-progress-seg')].map(node => Math.round(node.getBoundingClientRect().width * 100) / 100),
divider: rect(storage.querySelector('.wb-storage__divider')),
workspaceTitle: storage.querySelector('.general-settings-panel__workspace-path-title')?.textContent?.trim(),
workspaceTitleStyle: styles(storage.querySelector('.general-settings-panel__workspace-path-title')),
workspaceDesc: storage.querySelector('.general-settings-panel__workspace-path-desc')?.textContent?.trim(),
input: (() => { const node = storage.querySelector('.general-settings-panel__path-input')
return node ? { bounds: rect(node), value: node.value, readOnly: node.readOnly, style: styles(node) } : null })(),
change: (() => { const button = [...storage.querySelectorAll('.wb-button')].find(node => node.textContent.trim() === '更改')
return button ? { bounds: rect(button), text: button.textContent.trim() } : null })() } : null,
placeholders: placeholderNodes.length,
placeholderHints: placeholderNodes.map(node => [node.getAttribute('aria-disabled'), node.getAttribute('title')]),
placeholderControlsDisabled: placeholderNodes.every(node => { const input = node.querySelector('input, button'); return !input || input.tabIndex === -1 }),
inlineLink: (() => { const link = document.querySelector('.general-settings-panel__inline-link')
return link ? { text: link.textContent.trim(), disabled: link.getAttribute('aria-disabled'), title: link.getAttribute('title'), bounds: rect(link) } : null })(),
switchStates: [...document.querySelectorAll('.general-settings-panel__switch-wrap')].map(node => node.querySelector('input')?.checked),
unavailableSwitches: [...document.querySelectorAll('.general-settings-panel__switch-wrap[data-settings-unavailable=true] input')].map(node => node.hasAttribute('readonly'))
}})() `
; ( async ( ) => {
const sync = require ( 'node:fs' )
sync . mkdirSync ( dataDir , { recursive : true } )
app . setPath ( 'userData' , dataDir )
app . setPath ( 'sessionData' , dataDir )
await import ( pathToFileURL ( path . resolve ( 'out/main/index.mjs' ) ) . href )
await app . whenReady ( )
for ( let index = 0 ; index < 200 ; index ++ ) {
main = BrowserWindow . getAllWindows ( ) . find ( win => win . getTitle ( ) === 'YCode' )
if ( main && ! main . webContents . isLoading ( ) ) break
await pause ( 40 )
}
assert . ok ( main , 'main window' )
main . webContents . on ( 'console-message' , ( _event , level , message ) => { if ( level >= 2 ) rendererErrors . push ( message ) } )
await wait ( '!!window.ycode' , 'main renderer' )
await pause ( 1200 )
await step ( 'general-panel-structure' )
await openSettings ( 'settings' )
const nav = await read ( ` (()=>{const item=[...document.querySelectorAll('.settings-navigation__item')].find(node=>node.dataset.settingsTab==='settings')
return { enabled: item?.getAttribute('aria-disabled') !== 'true', active: !!document.querySelector('.settings-navigation__item--active') &&
document.querySelector('.settings-navigation__item--active').dataset.settingsTab === 'settings' }})() ` , settings )
check ( 'nav 通用 entry is enabled and active' , [ nav . enabled , nav . active ] , [ true , true ] )
const panel = await read ( panelSource , settings )
snapshots . panel = panel
check ( 'panel keeps the recovered 232,40 638-wide column' , [ panel . panel [ 0 ] , panel . panel [ 2 ] ] , [ 232 , 638 ] )
// 原版 1415 高;数据目录路径比原版长时该行会像原版一样换行,因此按实际行高换算期望值。
const pathRowOverflow = panel . storage . cachePathRow [ 3 ] - 22
check ( 'panel keeps the recovered content height (path row wrapping accounted)' ,
[ panel . panel [ 3 ] , pathRowOverflow ] , [ 1415 + pathRowOverflow , pathRowOverflow >= 0 ? pathRowOverflow : - 1 ] )
check ( 'panel top-level order stays group/group/storage/group/group' ,
panel . order . map ( name => name . startsWith ( 'general-settings-panel__group' ) ? 'group' : name ) , [ 'group' , 'group' , 'wb-storage' , 'group' , 'group' ] )
check ( 'panel keeps the four recovered group labels' , panel . groups . map ( group => group . label ) , EXPECTED _GROUPS )
check ( 'panel keeps the 14 recovered row titles in order' , panel . titles , EXPECTED _TITLES )
check ( 'panel keeps the recovered row heights' , panel . rowHeight , EXPECTED _ROW _HEIGHTS )
check ( 'group label keeps the recovered 638x22 with 8px left padding' ,
[ panel . groups [ 0 ] . labelBounds [ 2 ] , panel . groups [ 0 ] . labelBounds [ 3 ] , panel . labelPaddingLeft , panel . spacing3 ] , [ 638 , 22 , '8px' , '8px' ] )
check ( 'group card keeps the recovered padding and radius' ,
[ panel . groups [ 1 ] . card . padding , panel . groups [ 1 ] . card . borderRadius , panel . groups [ 1 ] . card . background ] , [ '12px 16px' , '12px' , 'rgb(255, 255, 255)' ] )
check ( 'row title keeps 14px/500/22px' , [ panel . titleStyle . fontSize , panel . titleStyle . fontWeight , panel . titleStyle . lineHeight ] , [ '14px' , '500' , '22px' ] )
check ( 'row description keeps 14px/400/22px tertiary' ,
[ panel . descStyle . fontSize , panel . descStyle . fontWeight , panel . descStyle . lineHeight , panel . descStyle . color ] , [ '14px' , '400' , '22px' , 'rgba(0, 0, 0, 0.5)' ] )
check ( 'switch keeps the recovered 34x20 control' , [ panel . switchTrack [ 2 ] , panel . switchTrack [ 3 ] ] , [ 34 , 20 ] )
check ( 'panel selects keep the recovered 120x32 geometry' , panel . selects . map ( size => [ size [ 2 ] , size [ 3 ] ] ) ,
panel . selects . map ( ( ) => [ 120 , 32 ] ) )
check ( 'font size slider keeps the recovered 224x40 with 小/默认/大 labels' ,
[ panel . slider [ 2 ] , panel . slider [ 3 ] , panel . sliderLabels ] , [ 224 , 40 , [ '小' , '默认' , '大' ] ] )
check ( '桌面通知 keeps the recovered 130x32 button with a trailing icon' ,
[ panel . notificationButton . bounds [ 2 ] , panel . notificationButton . bounds [ 3 ] , panel . notificationButton . text , panel . notificationButton . icons ] ,
[ 130 , 32 , '前往系统设置' , 1 ] )
await step ( 'general-panel-storage' )
const storage = panel . storage
check ( 'storage keeps the recovered 638-wide card and legend wording' ,
[ storage . bounds [ 2 ] , storage . bounds [ 3 ] , storage . label , storage . legend ] , [ 638 , 265 + pathRowOverflow , '存储' , [ '系统缓存' , '磁盘已用' , '磁盘可用' ] ] )
check ( 'storage keeps the recovered titles and helper texts' ,
[ storage . cardTitle , storage . workspaceTitle , storage . workspaceDesc , storage . change . text ] ,
[ '系统缓存目录' , 'YCode 默认工作空间存储路径' , '新建任务、工作空间时将自动存放在该路径下;修改后不影响已有数据。' , '更改' ] )
check ( 'storage keeps the recovered 76x22 打开目录 button' , [ storage . openFolder [ 2 ] , storage . openFolder [ 3 ] ] , [ 76 , 22 ] )
check ( 'storage keeps the recovered input and 更改 button geometry' ,
[ [ storage . input . bounds [ 2 ] , storage . input . bounds [ 3 ] ] , [ storage . change . bounds [ 2 ] , storage . change . bounds [ 3 ] ] ] , [ [ 530 , 32 ] , [ 68 , 32 ] ] )
check ( 'storage input is read-only and filled' , [ storage . input . readOnly , storage . input . style . background , storage . input . style . borderRadius ] ,
[ true , 'rgb(242, 242, 242)' , '8px' ] )
check ( 'storage shows the real isolated data directory and disk usage' ,
[ storage . pathText . startsWith ( dataDir ) , /^已用 \d+(\.\d+)? [KMGT]?B \/ 共 \d+(\.\d+)? [KMGT]B$/ . test ( storage . usageText ) ] , [ true , true ] )
// 606 宽的进度条内部有 2 个 1px 间隙(原版 gap:1px),三段合计 604。
check ( 'storage progress keeps the recovered 606x4 bar with 3 segments filling 604px' ,
[ Math . round ( storage . segments . reduce ( ( sum , value ) => sum + value , 0 ) ) , storage . segments . length ] , [ 604 , 3 ] )
check ( 'storage divider stays 606x1' , [ storage . divider [ 2 ] , storage . divider [ 3 ] ] , [ 606 , 1 ] )
check ( 'workspace path title keeps 14px/500/22px' ,
[ storage . workspaceTitleStyle . fontSize , storage . workspaceTitleStyle . fontWeight , storage . workspaceTitleStyle . lineHeight ] , [ '14px' , '500' , '22px' ] )
await step ( 'general-panel-placeholders' )
check ( 'unwired rows stay explicit placeholders with 暂未接入 hints' ,
[ panel . placeholders , panel . placeholderHints . every ( ( [ disabled , title ] ) => disabled === 'true' && title === '暂未接入' ) , panel . placeholderControlsDisabled ] ,
[ EXPECTED _PLACEHOLDERS , true , true ] )
check ( 'unwired switches stay read-only' , panel . unavailableSwitches . every ( Boolean ) , true )
check ( '了解更多 stays a disabled inline link' , [ panel . inlineLink . text , panel . inlineLink . disabled , panel . inlineLink . title ] , [ '了解更多' , 'true' , '暂未接入' ] )
const placeholderClick = await read ( ` (()=>{const select=document.querySelector('.general-settings-panel .wb-select[aria-disabled=true]')
select.click()
const switchInput=document.querySelector('.general-settings-panel__switch-wrap[data-settings-unavailable=true] input')
const before=switchInput.checked; switchInput.click()
return { popover: !!document.querySelector('.wb-select-popover'), switchBefore: before, switchAfter: switchInput.checked }})() ` , settings )
check ( 'placeholder select does not open a popover and placeholder switch does not toggle' ,
[ placeholderClick . popover , placeholderClick . switchBefore === placeholderClick . switchAfter ] , [ false , true ] )
await step ( 'general-settings-wired-behaviour' )
const initial = await read ( 'window.ycode.general.get()' , settings )
snapshots . initial = { ... initial , dataDir : initial . dataDir }
check ( 'general view reports the isolated data directory and default preferences' ,
[ initial . dataDir === dataDir , initial . preventSleep , initial . autoLaunch , initial . preventSleepActive ] , [ true , true , false , false ] )
await read ( ` (()=>{const input= ${ 'document.querySelectorAll(".general-settings-panel__switch-wrap:not([data-settings-unavailable]) input")[0]' }
input.click(); return true})() ` , settings )
await pause ( 600 )
const afterSleep = await read ( 'window.ycode.general.get()' , settings )
snapshots . afterSleep = afterSleep
check ( 'toggling 运行时防止系统休眠 persists the preference' ,
[ afterSleep . preventSleep , configFile ( ) . preventSleep ] , [ false , false ] )
await read ( ` (()=>{ ${ 'document.querySelectorAll(".general-settings-panel__switch-wrap:not([data-settings-unavailable]) input")[0]' } .click(); return true})() ` , settings )
await pause ( 600 )
const restoredSleep = await read ( 'window.ycode.general.get()' , settings )
check ( 'prevent-sleep preference can be restored to on' , [ restoredSleep . preventSleep , configFile ( ) . preventSleep ] , [ true , true ] )
await read ( ` (()=>{ ${ 'document.querySelectorAll(".general-settings-panel__switch-wrap:not([data-settings-unavailable]) input")[1]' } .click(); return true})() ` , settings )
await pause ( 600 )
const afterLaunch = await read ( 'window.ycode.general.get()' , settings )
snapshots . afterLaunch = afterLaunch
check ( 'toggling 开机自动启动 persists the preference (system integration isolated)' ,
[ afterLaunch . autoLaunch , configFile ( ) . autoLaunch ] , [ true , true ] )
await read ( ` (()=>{ ${ 'document.querySelectorAll(".general-settings-panel__switch-wrap:not([data-settings-unavailable]) input")[1]' } .click(); return true})() ` , settings )
await pause ( 600 )
const restoredLaunch = await read ( 'window.ycode.general.get()' , settings )
check ( 'auto-launch preference can be restored to off' , [ restoredLaunch . autoLaunch , configFile ( ) . autoLaunch ] , [ false , false ] )
const changed = await read ( ` window.ycode.general.changeWorkspaceRoot( ${ JSON . stringify ( nextWorkspace ) } ) ` , settings )
await pause ( 400 )
const rootListed = await read ( ` (async()=>(await window.ycode.workspace.get()).root)() ` , settings )
check ( '更改工作空间路径 persists the real workspace root' ,
[ changed . workspaceRoot === nextWorkspace , rootListed === nextWorkspace ] , [ true , true ] )
// 说明:「更改」按钮会打开系统目录选择器,无法自动化点击;这里断言按钮可用,
// 并在下面的重开窗口步骤里断言面板确实渲染持久化后的路径。
const changeButton = await read ( ` (()=>{const button=[...document.querySelectorAll('.wb-storage .wb-button')].find(node => node.textContent.trim() === '更改')
return { disabled: button.disabled, label: button.textContent.trim() }})() ` , settings )
check ( '更改 button stays enabled and labelled' , changeButton , { disabled : false , label : '更改' } )
check ( 'workspace root persists to config.json' , configFile ( ) . workspaceRoot , nextWorkspace )
check ( 'general API exposes the wired actions' ,
await read ( ` (()=>{const api=window.ycode.general; return [typeof api.openFolder, typeof api.changeWorkspaceRoot, typeof api.openSystemNotificationSettings]})() ` , settings ) ,
[ 'function' , 'function' , 'function' ] )
await read ( 'window.ycode.general.openSystemNotificationSettings()' , settings )
check ( 'notification settings call resolves while system integration is isolated' , true , true )
await step ( 'general-panel-persistence' )
settings . close ( )
await pause ( 600 )
await openSettings ( 'settings' )
const reopened = await read ( panelSource , settings )
const reopenedView = await read ( 'window.ycode.general.get()' , settings )
snapshots . reopened = reopenedView
check ( 'reopening the settings window keeps the persisted preferences' ,
[ reopenedView . preventSleep , reopenedView . autoLaunch , reopenedView . workspaceRoot === nextWorkspace ] , [ true , false , true ] )
check ( 'reopened panel renders the persisted workspace path' , reopened . storage . input . value === nextWorkspace , true )
check ( 'reopened panel still renders the recovered rows' , reopened . rowHeight , EXPECTED _ROW _HEIGHTS )
await step ( 'general-panel-dark-theme' )
const dark = await read ( ` (()=>{document.documentElement.dataset.theme='dark'
const card=document.querySelector('.general-settings-panel__group-card')
const title=document.querySelector('.general-settings-panel__section-title')
return { card: getComputedStyle(card).backgroundColor, title: getComputedStyle(title).color }})() ` , settings )
check ( 'dark theme repaints the panel card and titles' , [ dark . card !== 'rgb(255, 255, 255)' , dark . title !== 'rgb(0, 0, 0)' ] , [ true , true ] )
await read ( ` (()=>{document.documentElement.dataset.theme='light'; return true})() ` , settings )
await step ( 'general-panel-errors' )
check ( 'no renderer error logs' , rendererErrors , [ ] )
const result = checks . every ( item => item . pass ) ? 'PASS' : 'FAIL'
await fs . writeFile ( path . join ( directory , 'report.json' ) , JSON . stringify ( { result , phase , checks , snapshots } , null , 2 ) )
console . log ( 'GENERAL_SETTINGS_' + result + ' ' + directory )
for ( const failure of checks . filter ( item => ! item . pass ) ) console . log ( 'GENERAL_SETTINGS_FAILURE ' + JSON . stringify ( failure ) )
app . exit ( result === 'PASS' ? 0 : 1 )
} ) ( ) . catch ( async error => {
console . error ( error )
try { await fs . writeFile ( path . join ( directory , 'report.json' ) , JSON . stringify ( { result : 'ERROR' , phase , checks , snapshots , error : String ( error ? . stack ? ? error ) } , null , 2 ) ) } catch { /* ignore */ }
app . exit ( 1 )
} )
}