auto-update-window.test.mjs 4.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. import { test } from 'node:test'
  2. import assert from 'node:assert/strict'
  3. import { mkdtemp, writeFile, rm, mkdir } from 'node:fs/promises'
  4. import { tmpdir } from 'node:os'
  5. import { join, resolve } from 'node:path'
  6. import { pathToFileURL } from 'node:url'
  7. import { _electron as electron, expect } from '@playwright/test'
  8. test('inline native updater shows circular progress, preserves staging across reload and restarts once', { timeout: 60000 }, async () => {
  9. const root = resolve(import.meta.dirname, '..'), home = await mkdtemp(join(tmpdir(), 'yaoyao-update-ui-'))
  10. const entry = join(home, 'main.mjs')
  11. // The real view/preload/controller run in disposable Electron; only the
  12. // downloader and native installer are fake, so no installed app is changed.
  13. await writeFile(entry, `
  14. import {app,BrowserWindow,ipcMain} from 'electron';
  15. import {EventEmitter} from 'node:events';
  16. import {DesktopAutoUpdateManager} from ${JSON.stringify(pathToFileURL(join(root, 'desktop/auto-update-manager.mjs')).href)};
  17. app.setPath('userData',${JSON.stringify(join(home, 'user-data'))});
  18. const driver=new EventEmitter();let ready,win;
  19. globalThis.fixture={checks:0,installs:0};
  20. driver.checkForUpdates=async()=>{fixture.checks++;return {isUpdateAvailable:true,updateInfo:{version:'0.4.63',releaseNotes:'<b>原样显示</b>'}}};
  21. driver.downloadUpdate=()=>new Promise(resolve=>{ready=resolve;driver.emit('download-progress',{transferred:50,total:100})});
  22. driver.quitAndInstall=()=>{fixture.installs++};
  23. const manager=new DesktopAutoUpdateManager({driver,version:'0.4.62'});await manager.check();
  24. fixture.stage=()=>driver.emit('update-downloaded');fixture.ready=()=>ready([]);fixture.fail=()=>driver.emit('error',new Error('signature failed'));
  25. fixture.state=()=>manager.snapshot();
  26. const open=async()=>{win=new BrowserWindow({width:1000,height:800,webPreferences:{contextIsolation:true,sandbox:true,preload:${JSON.stringify(join(root, 'desktop/preload.cjs'))}}});await win.loadFile(${JSON.stringify(join(root, 'desktop/boot.html'))})};
  27. fixture.open=open;ipcMain.handle('desktop:status',()=>({phase:'idle',mode:null}));
  28. for(const action of ['state','check','download','cancel','install'])ipcMain.handle('desktop-update:'+action,()=>action==='state'?manager.snapshot():manager[action]());
  29. app.on('window-all-closed',()=>{});app.whenReady().then(open);
  30. `)
  31. let app
  32. try {
  33. app = await electron.launch({ args: [entry], cwd: root })
  34. let page = await app.firstWindow()
  35. await expect(page.getByRole('button', { name: '可更新', exact: true })).toBeEnabled()
  36. await page.getByRole('button', { name: '可更新', exact: true }).click()
  37. await expect(page.getByRole('progressbar', { name: '正在下载 · 50%' })).toHaveAttribute('aria-valuenow', '50')
  38. assert.equal(app.windows().length, 1)
  39. await mkdir(join(root, 'test-results/desktop'), { recursive: true })
  40. await page.screenshot({ path: join(root, 'test-results/desktop/auto-update-downloading.png') })
  41. await app.evaluate(() => fixture.stage())
  42. await expect(page.locator('#desktop-update-label')).toHaveText('正在准备…')
  43. await expect(page.getByRole('button', { name: '重新启动', exact: true })).toHaveCount(0)
  44. await page.reload()
  45. await expect(page.locator('#desktop-update-label')).toHaveText('正在准备…')
  46. assert.equal(await app.evaluate(() => fixture.checks), 1)
  47. await app.evaluate(() => fixture.ready())
  48. const install = page.getByRole('button', { name: '重新启动', exact: true })
  49. await expect(install).toBeEnabled()
  50. assert.equal(await app.evaluate(() => fixture.installs), 0)
  51. await mkdir(join(root, 'test-results/desktop'), { recursive: true })
  52. await page.screenshot({ path: join(root, 'test-results/desktop/auto-update-ready.png') })
  53. await page.emulateMedia({ colorScheme: 'dark', reducedMotion: 'reduce' })
  54. await page.setViewportSize({ width: 380, height: 620 })
  55. assert.equal(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), true)
  56. await page.screenshot({ path: join(root, 'test-results/desktop/auto-update-ready-dark.png') })
  57. await install.click()
  58. await expect(page.getByRole('button', { name: '正在重启…' })).toBeDisabled()
  59. assert.equal(await app.evaluate(() => fixture.installs), 1)
  60. await app.evaluate(() => fixture.fail())
  61. await expect(page.locator('#desktop-update-error')).toContainText('签名验证失败')
  62. await expect(page.getByRole('button', { name: '更新未完成', exact: true })).toBeDisabled()
  63. } finally { await app?.close().catch(() => {}); await rm(home, { recursive: true, force: true }) }
  64. })