startup-feedback.test.mjs 6.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import { enterLocal } from './test-support/onboarding.mjs'
  2. import {test} from 'node:test'
  3. import assert from 'node:assert/strict'
  4. import {mkdtemp,realpath,readFile,rm,mkdir,cp,writeFile,readdir} from 'node:fs/promises'
  5. import {tmpdir} from 'node:os'
  6. import {join,resolve} from 'node:path'
  7. import {createServer} from 'node:http'
  8. import {createHash} from 'node:crypto'
  9. import {_electron as electron} from '@playwright/test'
  10. import {LaunchAgentService,localRequest,synchronizeDesktop} from '../bin/lib/service-update.mjs'
  11. import {sealRuntime,verifyRuntimePackage} from '../bin/lib/runtime-release.mjs'
  12. const root=resolve(import.meta.dirname,'..')
  13. test('built startup page shows a real helper error and Retry recovers into the Web',{timeout:90000},async()=>{
  14. // The production packaging filter previously omitted this file entirely.
  15. assert.ok((await readFile(join(root,'.desktop-build/shell/boot.js'),'utf8')).includes('yaoyaoDesktop.status'))
  16. const home=await realpath(await mkdtemp(join(tmpdir(),'yaoyao-startup-feedback-')))
  17. const blocker=createServer((_request,response)=>{response.setHeader('Content-Type','application/json');response.end('{"ok":true}')})
  18. await new Promise(done=>blocker.listen(0,'127.0.0.1',done))
  19. const port=blocker.address().port
  20. const driver=new LaunchAgentService({home,port,releaseRoot:join(home,'updates','fixture-releases'),
  21. label:`cn.samien.yaoyao.sync-test.${createHash('sha256').update(home).digest('hex').slice(0,16)}`,
  22. plistPath:join(home,'updates','fixture-service.plist')})
  23. let app
  24. try{
  25. app=await electron.launch({...(process.env.DESKTOP_TEST_EXECUTABLE?{executablePath:process.env.DESKTOP_TEST_EXECUTABLE}:{}),
  26. args:process.env.DESKTOP_TEST_EXECUTABLE?[]:[root],cwd:root,
  27. env:{...process.env,HERMES_YAOYAO_DESKTOP_TEST_HOME:home,HERMES_YAOYAO_DESKTOP_PORT:String(port),HERMES_YAOYAO_DESKTOP_TEST_SYNC:'1',HERMES_YAOYAO_UPSTREAM:'http://127.0.0.1:1'},timeout:30000})
  28. const page=await app.firstWindow()
  29. await page.locator('#status[role="alert"]').waitFor({timeout:30000})
  30. assert.match(await page.locator('#status').textContent(),/已有服务未注册/)
  31. assert.equal(await page.getByRole('button',{name:'重试',exact:true}).isVisible(),true)
  32. await page.locator('#install-details summary').click()
  33. assert.equal(await page.getByRole('button',{name:'查看日志',exact:true}).isVisible(),true)
  34. await mkdir(join(root,'test-results/desktop'),{recursive:true})
  35. await page.screenshot({path:join(root,'test-results/desktop/startup-error-retry.png')})
  36. await new Promise(done=>blocker.close(done))
  37. await page.getByRole('button',{name:'重试',exact:true}).click()
  38. await enterLocal(page)
  39. await page.waitForURL(`http://127.0.0.1:${port}/**`,{timeout:45000})
  40. assert.equal((await localRequest(`http://127.0.0.1:${port}/healthz`)).body.ok,true)
  41. await page.screenshot({path:join(root,'test-results/desktop/startup-recovered.png')})
  42. }finally{
  43. await app?.close().catch(()=>{})
  44. if(blocker.listening)await new Promise(done=>blocker.close(done))
  45. await driver.stop();await rm(home,{recursive:true,force:true})
  46. }
  47. })
  48. test('same-version unknown ancestry offers an explicit overwrite and preserves existing user data',{timeout:90000},async()=>{
  49. const home=await realpath(await mkdtemp(join(tmpdir(),'yaoyao-force-overwrite-')))
  50. const reservation=createServer();await new Promise(done=>reservation.listen(0,'127.0.0.1',done))
  51. const port=reservation.address().port;await new Promise(done=>reservation.close(done))
  52. const payload=process.env.DESKTOP_TEST_EXECUTABLE?resolve(process.env.DESKTOP_TEST_EXECUTABLE,'../../Resources/runtime/web-service'):join(root,'.desktop-build/web-service')
  53. const original=verifyRuntimePackage(payload),old=join(home,'updates/old-runtime')
  54. await cp(payload,old,{recursive:true})
  55. await writeFile(join(old,'build-info.json'),JSON.stringify({commit:'f'.repeat(40),ancestors:[],buildNumber:1,dirty:false}))
  56. sealRuntime(old)
  57. const releaseRoot=join(home,'updates','fixture-releases')
  58. const driver=new LaunchAgentService({home,port,releaseRoot,label:`cn.samien.yaoyao.sync-test.${createHash('sha256').update(home).digest('hex').slice(0,16)}`,plistPath:join(home,'updates','fixture-service.plist'),environment:{HERMES_YAOYAO_UPSTREAM:'http://127.0.0.1:1',HERMES_YAOYAO_SUPERVISE_DASHBOARD:'0'}})
  59. let app
  60. try{
  61. await synchronizeDesktop({home,runtimeRoot:old,releaseRoot,driver})
  62. await writeFile(join(home,'user-proof.txt'),'preserve existing user files')
  63. app=await electron.launch({...(process.env.DESKTOP_TEST_EXECUTABLE?{executablePath:process.env.DESKTOP_TEST_EXECUTABLE}:{}),args:process.env.DESKTOP_TEST_EXECUTABLE?[]:[root],cwd:root,
  64. env:{...process.env,HERMES_YAOYAO_DESKTOP_TEST_HOME:home,HERMES_YAOYAO_DESKTOP_PORT:String(port),HERMES_YAOYAO_DESKTOP_TEST_SYNC:'1',HERMES_YAOYAO_UPSTREAM:'http://127.0.0.1:1'},timeout:30000})
  65. const page=await app.firstWindow(),button=page.getByRole('button',{name:'使用当前 App 覆盖同版本 Web',exact:true})
  66. await button.waitFor({timeout:30000})
  67. assert.match(await page.locator('#status').textContent(),/构建先后关系无法确定/)
  68. assert.equal(verifyRuntimePackage(driver.snapshot().root).commit,'f'.repeat(40))
  69. await mkdir(join(root,'test-results/desktop'),{recursive:true});await page.screenshot({path:join(root,'test-results/desktop/same-version-force.png')})
  70. await button.click();await enterLocal(page);await page.waitForURL(`http://127.0.0.1:${port}/**`,{timeout:45000})
  71. const record=JSON.parse(await readFile(join(home,'service-instance.json'),'utf8'))
  72. const identity=await localRequest(`http://127.0.0.1:${port}/desktop/service`,record.token)
  73. assert.equal(identity.body.build.artifactDigest,original.artifactDigest)
  74. assert.equal(await readFile(join(home,'user-proof.txt'),'utf8'),'preserve existing user files')
  75. assert.equal(JSON.parse(await readFile(join(home,'updates/desktop-sync.json'),'utf8')).action,'overwrite')
  76. assert.ok((await readdir(join(home,'updates/backups'))).length>=2)
  77. const denied=await page.evaluate(()=>window.yaoyaoDesktop.forceSync().then(()=>false,()=>true))
  78. assert.equal(denied,true,'the Web renderer cannot invoke the boot-only overwrite channel')
  79. await page.screenshot({path:join(root,'test-results/desktop/same-version-recovered.png')})
  80. }finally{await app?.close().catch(()=>{});await driver.stop();await rm(home,{recursive:true,force:true})}
  81. })