@@ -56,6 +56,9 @@ afterEach(() => {
5656 vi . clearAllMocks ( )
5757 autoUpdater . autoDownload = undefined as unknown as boolean
5858 autoUpdater . autoInstallOnAppQuit = undefined as unknown as boolean
59+ autoUpdater . allowPrerelease = undefined as unknown as boolean
60+ autoUpdater . allowDowngrade = undefined as unknown as boolean
61+ autoUpdater . channel = null
5962} )
6063
6164function temporaryDirectory ( ) : string {
@@ -66,27 +69,56 @@ function temporaryDirectory(): string {
6669
6770describe ( 'update settings' , ( ) => {
6871 it ( 'defaults automatic updates to enabled when the file is missing' , ( ) => {
69- expect ( readUpdateSettings ( temporaryDirectory ( ) ) ) . toEqual ( { autoUpdate : true } )
72+ expect ( readUpdateSettings ( temporaryDirectory ( ) ) ) . toEqual ( {
73+ autoUpdate : true ,
74+ channel : 'stable' ,
75+ notifyUpdate : true ,
76+ } )
7077 } )
7178
7279 it ( 'defaults automatic updates to enabled when the file is corrupt' , ( ) => {
7380 const directory = temporaryDirectory ( )
7481 writeFileSync ( join ( directory , 'update-settings.json' ) , '{not-json' , 'utf8' )
7582
76- expect ( readUpdateSettings ( directory ) ) . toEqual ( { autoUpdate : true } )
83+ expect ( readUpdateSettings ( directory ) ) . toEqual ( {
84+ autoUpdate : true ,
85+ channel : 'stable' ,
86+ notifyUpdate : true ,
87+ } )
88+ } )
89+
90+ it ( 'round-trips automatic checks, update notifications, and the channel' , ( ) => {
91+ const directory = temporaryDirectory ( )
92+ writeUpdateSettings ( directory , {
93+ autoUpdate : false ,
94+ channel : 'beta' ,
95+ notifyUpdate : false ,
96+ } )
97+
98+ expect ( readUpdateSettings ( directory ) ) . toEqual ( {
99+ autoUpdate : false ,
100+ channel : 'beta' ,
101+ notifyUpdate : false ,
102+ } )
77103 } )
78104
79- it ( 'round-trips the automatic-updates setting ' , ( ) => {
105+ it ( 'uses safe defaults for missing or invalid new settings ' , ( ) => {
80106 const directory = temporaryDirectory ( )
81- writeUpdateSettings ( directory , { autoUpdate : false } )
107+ writeFileSync ( join ( directory , 'update-settings.json' ) , '{" autoUpdate": false,"channel":"preview"}\n' , 'utf8' )
82108
83- expect ( readUpdateSettings ( directory ) ) . toEqual ( { autoUpdate : false } )
109+ expect ( readUpdateSettings ( directory ) ) . toEqual ( {
110+ autoUpdate : false ,
111+ channel : 'stable' ,
112+ notifyUpdate : true ,
113+ } )
84114 } )
85115
86116 it ( 'persists update notification, skip, install, and completion receipts separately' , ( ) => {
87117 const directory = temporaryDirectory ( )
88118 const value = {
89119 autoUpdate : true ,
120+ channel : 'nightly' as const ,
121+ notifyUpdate : true ,
90122 notifiedVersion : '1.2.3' ,
91123 skippedVersion : '1.2.3' ,
92124 pendingInstallVersion : '1.3.0' ,
@@ -106,7 +138,13 @@ describe('update telemetry transitions', () => {
106138 const track = ( event : string ) : void => {
107139 events . push ( event )
108140 }
109- const previous : UpdateState = { status : 'idle' , installedVersion : '1.0.0' , autoUpdate : true }
141+ const previous : UpdateState = {
142+ status : 'idle' ,
143+ installedVersion : '1.0.0' ,
144+ autoUpdate : true ,
145+ channel : 'stable' ,
146+ notifyUpdate : true ,
147+ }
110148
111149 trackUpdateTransition ( previous , { ...previous , status : 'checking' } , track )
112150 trackUpdateTransition ( previous , { ...previous , status : 'available' , availableVersion : '0.2.0' } , track )
@@ -136,7 +174,11 @@ describe('release notes URL', () => {
136174describe ( 'packaged builds without update metadata' , ( ) => {
137175 it ( 'disables updates without wiring updater events' , ( ) => {
138176 const directory = temporaryDirectory ( )
139- writeUpdateSettings ( directory , { autoUpdate : false } )
177+ writeUpdateSettings ( directory , {
178+ autoUpdate : false ,
179+ channel : 'stable' ,
180+ notifyUpdate : true ,
181+ } )
140182 vi . mocked ( app . getPath ) . mockReturnValue ( directory )
141183 Object . defineProperty ( app , 'isPackaged' , { configurable : true , value : true } )
142184 Object . defineProperty ( process , 'resourcesPath' , { configurable : true , value : directory } )
@@ -154,6 +196,45 @@ describe('packaged builds without update metadata', () => {
154196} )
155197
156198describe ( 'strict update consent' , ( ) => {
199+ it ( 'changes channels and notification preference without checking, downloading, installing, or downgrading' , async ( ) => {
200+ vi . resetModules ( )
201+ const directory = temporaryDirectory ( )
202+ writeFileSync ( join ( directory , 'app-update.yml' ) , '' , 'utf8' )
203+ writeFileSync (
204+ join ( directory , 'update-settings.json' ) ,
205+ '{"autoUpdate":false,"channel":"beta","notifyUpdate":true}\n' ,
206+ 'utf8' ,
207+ )
208+ const { app : localApp } = await import ( 'electron' )
209+ const { default : localElectronUpdater } = await import ( 'electron-updater' )
210+ const {
211+ initUpdater : initLocalUpdater ,
212+ setNotifyUpdate : setLocalNotifyUpdate ,
213+ setUpdateChannel : setLocalUpdateChannel ,
214+ } = await import ( '../src/updater' )
215+ const localAutoUpdater = localElectronUpdater . autoUpdater
216+ vi . mocked ( localApp . getPath ) . mockReturnValue ( directory )
217+ Object . defineProperty ( localApp , 'isPackaged' , { configurable : true , value : true } )
218+ Object . defineProperty ( process , 'resourcesPath' , { configurable : true , value : directory } )
219+
220+ initLocalUpdater ( ( ) => undefined )
221+ expect ( localAutoUpdater . channel ) . toBe ( 'beta' )
222+ expect ( localAutoUpdater . allowPrerelease ) . toBe ( true )
223+ expect ( localAutoUpdater . allowDowngrade ) . toBe ( false )
224+
225+ expect ( setLocalUpdateChannel ( 'nightly' ) ) . toMatchObject ( { channel : 'nightly' , status : 'idle' } )
226+ expect ( setLocalNotifyUpdate ( false ) ) . toMatchObject ( { notifyUpdate : false } )
227+ expect ( setLocalUpdateChannel ( 'stable' ) ) . toMatchObject ( { channel : 'stable' , status : 'idle' } )
228+ expect ( localAutoUpdater . channel ) . toBeNull ( )
229+ expect ( localAutoUpdater . allowPrerelease ) . toBe ( false )
230+ expect ( localAutoUpdater . allowDowngrade ) . toBe ( false )
231+ expect ( localAutoUpdater . autoDownload ) . toBe ( false )
232+ expect ( localAutoUpdater . autoInstallOnAppQuit ) . toBe ( false )
233+ expect ( localAutoUpdater . checkForUpdates ) . not . toHaveBeenCalled ( )
234+ expect ( localAutoUpdater . downloadUpdate ) . not . toHaveBeenCalled ( )
235+ expect ( localAutoUpdater . quitAndInstall ) . not . toHaveBeenCalled ( )
236+ } )
237+
157238 it ( 'manual check cannot download an update' , async ( ) => {
158239 vi . resetModules ( )
159240 const directory = temporaryDirectory ( )
0 commit comments