@@ -26,16 +26,19 @@ async function counts(app: ElectronApplication) {
2626 } ) ;
2727}
2828
29+ /** Quits, checks the evidence every test shares and returns it for the test's own checks. */
2930async function cleanup ( app : ElectronApplication , home : string , repo : string ) {
31+ let final : { nativeCalls : number ; downloadedDisposed : number ; stagedDisposed : number ; quitStages : number } | undefined ;
3032 try {
3133 // An assertion can fail while the fake verifier is suspended. Release it so
3234 // production disposal can finish and the failed test cannot retain Electron.
3335 await app . evaluate ( ( ) => ( globalThis as any ) . __updateScenario ?. finishVerification ?.( false ) ) ;
3436 } finally {
3537 try {
3638 await app . close ( ) ;
37- const final = JSON . parse ( fs . readFileSync ( path . join ( home , "final-installation-evidence.json" ) , "utf8" ) ) ;
38- expect ( final . nativeCalls ) . toBe ( 0 ) ; expect ( final . downloadedDisposed ) . toBe ( final . stagedDisposed ) ;
39+ final = JSON . parse ( fs . readFileSync ( path . join ( home , "final-installation-evidence.json" ) , "utf8" ) ) ;
40+ // quitAndInstall relaunches; install-on-quit only stages and lets the quit finish.
41+ expect ( final ! . nativeCalls ) . toBe ( 0 ) ; expect ( final ! . downloadedDisposed ) . toBe ( final ! . stagedDisposed ) ;
3942 const receipt = test . info ( ) . outputPath ( "installation-controller-evidence.json" ) ;
4043 fs . writeFileSync ( receipt , JSON . stringify ( final ) ) ;
4144 await test . info ( ) . attach ( "installation-controller-evidence" , { path : receipt , contentType : "application/json" } ) ;
@@ -45,28 +48,34 @@ async function cleanup(app: ElectronApplication, home: string, repo: string) {
4548 fs . rmSync ( repo , { recursive : true , force : true } ) ;
4649 }
4750 }
51+ return final ! ;
4852}
4953
50- test ( "installation UI shows progress , cancels and retries, then refuses restart with open Settings" , async ( ) => {
54+ test ( "installation downloads by itself , cancels and retries, refuses restart with open Settings, then installs on quit " , async ( ) => {
5155 test . skip ( process . platform !== "darwin" , "macOS native update controller" ) ;
5256 const { home, repo, app, page } = await launchInstallation ( ) ;
57+ let final ;
5358 try {
5459 const banner = tid ( page , "update-banner" ) ;
55- await expect ( banner . getByRole ( "button" , { name : "Download update" , exact : true } ) ) . toBeVisible ( ) ;
56- await banner . getByRole ( "button" , { name : "Download update" , exact : true } ) . click ( ) ;
60+ // Nobody clicks: finding the release starts the download.
5761 await expect ( page . getByRole ( "progressbar" , { name : "Update download" } ) ) . toHaveAttribute ( "value" , "25" ) ;
5862 await expect ( banner ) . toContainText ( "Downloading the update…" ) ;
5963 await expect ( banner . getByRole ( "button" , { name : "Dismiss update notification" } ) ) . toHaveCount ( 0 ) ;
6064 await banner . getByRole ( "button" , { name : "Cancel download" , exact : true } ) . click ( ) ;
6165 await expect ( banner ) . toContainText ( "Update preparation cancelled." ) ;
66+ // A cancelled version stays cancelled when the window regains focus.
67+ await page . evaluate ( ( ) => window . dispatchEvent ( new Event ( "focus" ) ) ) ;
68+ await expect ( banner . getByRole ( "button" , { name : "Download update" , exact : true } ) ) . toBeVisible ( ) ;
69+ expect ( ( await counts ( app ) ) . downloads ) . toBe ( 1 ) ;
6270 await banner . getByRole ( "button" , { name : "Download update" , exact : true } ) . click ( ) ;
6371 await expect ( banner ) . toContainText ( "Downloading the update…" ) ;
6472 await finishDownload ( app ) ;
6573 await expect ( banner ) . toContainText ( "Verifying the publisher and update…" ) ;
6674 await finishVerification ( app , true ) ;
6775 await expect ( banner . getByRole ( "button" , { name : "Restart to update" , exact : true } ) ) . toBeEnabled ( ) ;
68- await expect ( banner ) . toContainText ( "Verified and ready." ) ;
69- await expect ( banner . getByRole ( "button" , { name : "Dismiss update notification" } ) ) . toHaveCount ( 0 ) ;
76+ await expect ( banner ) . toContainText ( "Modex 999.0.0 is ready to install" ) ;
77+ await expect ( banner ) . toContainText ( "installs the next time you quit" ) ;
78+ await expect ( banner . getByRole ( "link" , { name : "What’s new" } ) ) . toHaveAttribute ( "href" , "https://github.com/TypeSafeAI/modex/releases/tag/v999.0.0" ) ;
7079 await tid ( page , "open-settings" ) . click ( ) ;
7180 await expect ( page . getByRole ( "dialog" , { name : "Settings" } ) ) . toBeVisible ( ) ;
7281 // Settings may still have admitted a model/health request. Its ordinary-work
@@ -84,25 +93,24 @@ test("installation UI shows progress, cancels and retries, then refuses restart
8493 expect ( await counts ( app ) ) . toEqual ( { downloads : 2 , downloadedDisposed : 1 , stagedDisposed : 0 , nativeCalls : 0 } ) ;
8594 await banner . screenshot ( { path : test . info ( ) . outputPath ( "update-ready-after-refusal.png" ) } ) ;
8695 } finally {
87- await cleanup ( app , home , repo ) ;
96+ final = await cleanup ( app , home , repo ) ;
8897 }
98+ expect ( final . quitStages ) . toBe ( 1 ) ;
8999} ) ;
90100
91101test ( "failed publisher verification offers retry and never exposes an install action" , async ( ) => {
92102 test . skip ( process . platform !== "darwin" , "macOS native update controller" ) ;
93103 const { home, repo, app, page } = await launchInstallation ( ) ;
94104 try {
95105 const banner = tid ( page , "update-banner" ) ;
96- await expect ( banner . getByRole ( "button" , { name : "Download update" , exact : true } ) ) . toBeVisible ( ) ;
97- await banner . getByRole ( "button" , { name : "Download update" , exact : true } ) . click ( ) ;
98106 await expect ( banner ) . toContainText ( "Downloading the update…" ) ;
99107 await finishDownload ( app ) ;
100108 await expect ( banner ) . toContainText ( "Verifying the publisher and update…" ) ;
101109 await finishVerification ( app , false ) ;
102110 await expect ( banner ) . toContainText ( "could not be downloaded or verified" ) ;
103111 await expect ( tid ( banner , "update-error-detail" ) ) . toHaveText ( "Publisher verification failed" ) ;
104112 await expect ( banner . getByRole ( "button" , { name : "Restart to update" , exact : true } ) ) . toHaveCount ( 0 ) ;
105- await expect ( banner . getByRole ( "link" , { name : "View update " } ) ) . toHaveAttribute ( "href" , "https://github.com/TypeSafeAI/modex/releases/tag/v999.0.0" ) ;
113+ await expect ( banner . getByRole ( "link" , { name : "What’s new " } ) ) . toHaveAttribute ( "href" , "https://github.com/TypeSafeAI/modex/releases/tag/v999.0.0" ) ;
106114 await expect . poll ( ( ) => counts ( app ) ) . toEqual ( { downloads : 1 , downloadedDisposed : 1 , stagedDisposed : 1 , nativeCalls : 0 } ) ;
107115 await banner . getByRole ( "button" , { name : "Download update" , exact : true } ) . click ( ) ;
108116 await expect ( banner ) . toContainText ( "Downloading the update…" ) ;
0 commit comments