You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Desktop: Seamless-Updates - added tests for autoUpdaterService (#10935)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
This commit is contained in:
		| @@ -491,6 +491,7 @@ packages/app-desktop/integration-tests/util/test.js | ||||
| packages/app-desktop/integration-tests/util/waitForNextOpenPath.js | ||||
| packages/app-desktop/playwright.config.js | ||||
| packages/app-desktop/plugins/GotoAnything.js | ||||
| packages/app-desktop/services/autoUpdater/AutoUpdaterService.test.js | ||||
| packages/app-desktop/services/autoUpdater/AutoUpdaterService.js | ||||
| packages/app-desktop/services/bridge.js | ||||
| packages/app-desktop/services/commands/stateToWhenClauseContext.js | ||||
|   | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -468,6 +468,7 @@ packages/app-desktop/integration-tests/util/test.js | ||||
| packages/app-desktop/integration-tests/util/waitForNextOpenPath.js | ||||
| packages/app-desktop/playwright.config.js | ||||
| packages/app-desktop/plugins/GotoAnything.js | ||||
| packages/app-desktop/services/autoUpdater/AutoUpdaterService.test.js | ||||
| packages/app-desktop/services/autoUpdater/AutoUpdaterService.js | ||||
| packages/app-desktop/services/bridge.js | ||||
| packages/app-desktop/services/commands/stateToWhenClauseContext.js | ||||
|   | ||||
| @@ -0,0 +1,68 @@ | ||||
| import { LoggerWrapper } from '@joplin/utils/Logger'; | ||||
| import { releases3 } from '../../utils/checkForUpdatesUtilsTestData'; | ||||
| import AutoUpdaterService from './AutoUpdaterService'; | ||||
| import { BrowserWindow } from 'electron'; | ||||
|  | ||||
| jest.mock('electron', () => ({ | ||||
| 	BrowserWindow: jest.fn(), | ||||
| })); | ||||
|  | ||||
| jest.mock('electron-updater', () => { | ||||
| 	const mockAutoUpdater = { | ||||
| 		setFeedURL: jest.fn(), | ||||
| 		checkForUpdates: jest.fn(), | ||||
| 		on: jest.fn(), | ||||
| 		quitAndInstall: jest.fn(), | ||||
| 	}; | ||||
| 	return { autoUpdater: mockAutoUpdater }; | ||||
| }); | ||||
|  | ||||
|  | ||||
| describe('AutoUpdaterService', () => { | ||||
| 	let service: AutoUpdaterService; | ||||
| 	let mockLogger: LoggerWrapper; | ||||
|  | ||||
| 	beforeEach(() => { | ||||
| 		const mockWindow = new BrowserWindow(); | ||||
| 		mockLogger = { info: jest.fn(), error: jest.fn(), debug: jest.fn(), warn: jest.fn() }; | ||||
| 		service = new AutoUpdaterService(mockWindow, mockLogger, false, true); | ||||
| 		global.fetch = jest.fn(() => | ||||
| 			Promise.resolve({ | ||||
| 				ok: true, | ||||
| 				json: () => Promise.resolve(releases3), | ||||
| 			}), | ||||
| 		) as jest.Mock; | ||||
| 	}); | ||||
|  | ||||
| 	it('should correctly fetch and process the latest prerelease', async () => { | ||||
| 		const release = await service.fetchLatestRelease(true); | ||||
| 		expect(release).toBeDefined(); | ||||
| 		expect(release.tag_name).toBe('v3.1.3'); | ||||
| 	}); | ||||
|  | ||||
| 	it('should correctly fetch and process the latest release', async () => { | ||||
| 		const release = await service.fetchLatestRelease(false); | ||||
| 		expect(release).toBeDefined(); | ||||
| 		expect(release.tag_name).toBe('v3.1.2'); | ||||
| 	}); | ||||
|  | ||||
| 	it('should return the correct download URL for Windows', async () => { | ||||
| 		const release = await service.fetchLatestRelease(true); | ||||
| 		expect(release).toBeDefined(); | ||||
| 		const url = service.getDownloadUrlForPlatform(release, 'win32'); | ||||
| 		expect(url).toBe('https://github.com/laurent22/joplin/releases/download/v3.1.3/latest.yml'); | ||||
| 	}); | ||||
|  | ||||
| 	it('should return the correct download URL for Mac', async () => { | ||||
| 		const release = await service.fetchLatestRelease(true); | ||||
| 		expect(release).toBeDefined(); | ||||
| 		const url = service.getDownloadUrlForPlatform(release, 'darwin'); | ||||
| 		expect(url).toBe('https://github.com/laurent22/joplin/releases/download/v3.1.3/latest-mac.yml'); | ||||
| 	}); | ||||
|  | ||||
| 	it('should throw an error for Linux', async () => { | ||||
| 		const release = await service.fetchLatestRelease(true); | ||||
| 		expect(release).toBeDefined(); | ||||
| 		expect(() => service.getDownloadUrlForPlatform(release, 'linux')).toThrow('The AutoUpdaterService does not support the following platform: linux'); | ||||
| 	}); | ||||
| }); | ||||
| @@ -19,10 +19,16 @@ export enum AutoUpdaterEvents { | ||||
| export const defaultUpdateInterval = 12 * 60 * 60 * 1000; | ||||
| export const initialUpdateStartup = 5 * 1000; | ||||
| const releasesLink = 'https://objects.joplinusercontent.com/r/releases'; | ||||
| const supportedPlatformAssets: { [key in string]: string } = { | ||||
| 	'darwin': 'latest-mac.yml', | ||||
| 	'win32': 'latest.yml', | ||||
| }; | ||||
|  | ||||
| export interface AutoUpdaterServiceInterface { | ||||
| 	checkForUpdates(): void; | ||||
| 	updateApp(): void; | ||||
| 	fetchLatestRelease(includePreReleases: boolean): Promise<GitHubRelease>; | ||||
| 	getDownloadUrlForPlatform(release: GitHubRelease, platform: string): string; | ||||
| } | ||||
|  | ||||
| export default class AutoUpdaterService implements AutoUpdaterServiceInterface { | ||||
| @@ -45,7 +51,7 @@ export default class AutoUpdaterService implements AutoUpdaterServiceInterface { | ||||
|  | ||||
| 	public checkForUpdates = async (): Promise<void> => { | ||||
| 		try { | ||||
| 			await this.fetchLatestRelease(); | ||||
| 			await this.checkForLatestRelease(); | ||||
| 		} catch (error) { | ||||
| 			this.logger_.error('Failed to check for updates:', error); | ||||
| 			if (error.message.includes('ERR_CONNECTION_REFUSED')) { | ||||
| @@ -58,7 +64,33 @@ export default class AutoUpdaterService implements AutoUpdaterServiceInterface { | ||||
| 		autoUpdater.quitAndInstall(false, true); | ||||
| 	}; | ||||
|  | ||||
| 	private fetchLatestReleases = async (): Promise<GitHubRelease[]> => { | ||||
| 	public fetchLatestRelease = async (includePreReleases: boolean): Promise<GitHubRelease> => { | ||||
| 		const releases = await this.fetchReleases(includePreReleases); | ||||
| 		const release = releases[0]; | ||||
|  | ||||
| 		if (!release) { | ||||
| 			throw new Error('No suitable release found'); | ||||
| 		} | ||||
|  | ||||
| 		return release; | ||||
| 	}; | ||||
|  | ||||
|  | ||||
| 	public getDownloadUrlForPlatform(release: GitHubRelease, platform: string): string { | ||||
| 		const assetName: string = supportedPlatformAssets[platform]; | ||||
| 		if (!assetName) { | ||||
| 			throw new Error(`The AutoUpdaterService does not support the following platform: ${platform}`); | ||||
| 		} | ||||
|  | ||||
| 		const asset: GitHubReleaseAsset = release.assets.find(a => a.name === assetName); | ||||
| 		if (!asset) { | ||||
| 			throw new Error('No suitable update asset found for this platform.'); | ||||
| 		} | ||||
|  | ||||
| 		return asset.browser_download_url; | ||||
| 	} | ||||
|  | ||||
| 	private fetchReleases = async (includePreReleases: boolean): Promise<GitHubRelease[]> => { | ||||
| 		const response = await fetch(releasesLink); | ||||
|  | ||||
| 		if (!response.ok) { | ||||
| @@ -66,48 +98,29 @@ export default class AutoUpdaterService implements AutoUpdaterServiceInterface { | ||||
| 			throw new Error(`Cannot get latest release info: ${responseText.substr(0, 500)}`); | ||||
| 		} | ||||
|  | ||||
| 		return (await response.json()) as GitHubRelease[]; | ||||
| 		const releases: GitHubRelease[] = await response.json(); | ||||
| 		const sortedReleasesByVersion = releases.sort((a, b) => semver.rcompare(a.tag_name, b.tag_name)); | ||||
| 		const filteredReleases = sortedReleasesByVersion.filter(release => includePreReleases || !release.prerelease); | ||||
|  | ||||
| 		return filteredReleases; | ||||
| 	}; | ||||
|  | ||||
| 	private fetchLatestRelease = async (): Promise<void> => { | ||||
| 	private checkForLatestRelease = async (): Promise<void> => { | ||||
| 		try { | ||||
| 			const releases = await this.fetchLatestReleases(); | ||||
| 			const release: GitHubRelease = await this.fetchLatestRelease(this.includePreReleases_); | ||||
|  | ||||
| 			const sortedReleasesByVersion = releases.sort((a, b) => { | ||||
| 				return semver.rcompare(a.tag_name, b.tag_name); | ||||
| 			}); | ||||
| 			const filteredReleases = sortedReleasesByVersion.filter(release => { | ||||
| 				return this.includePreReleases_ || !release.prerelease; | ||||
| 			}); | ||||
| 			const release = filteredReleases[0]; | ||||
|  | ||||
| 			if (release) { | ||||
| 				let assetUrl = null; | ||||
|  | ||||
| 				if (shim.isWindows()) { | ||||
| 					const asset = release.assets.find((asset: GitHubReleaseAsset) => asset.name === 'latest.yml'); | ||||
| 					if (asset) { | ||||
| 						assetUrl = asset.browser_download_url.replace('/latest.yml', ''); | ||||
| 					} | ||||
| 				} else if (shim.isMac()) { | ||||
| 					const asset = release.assets.find((asset: GitHubReleaseAsset) => asset.name === 'latest-mac.yml'); | ||||
| 					if (asset) { | ||||
| 						assetUrl = asset.browser_download_url.replace('/latest-mac.yml', ''); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				if (assetUrl) { | ||||
| 					autoUpdater.setFeedURL({ | ||||
| 						provider: 'generic', | ||||
| 						url: assetUrl, | ||||
| 					}); | ||||
| 					await autoUpdater.checkForUpdates(); | ||||
| 				} else { | ||||
| 					this.logger_.error('No suitable update asset found for this platform.'); | ||||
| 				} | ||||
| 			try { | ||||
| 				let assetUrl = this.getDownloadUrlForPlatform(release, shim.platformName()); | ||||
| 				// electron's autoUpdater appends automatically the platform's yml file to the link so we should remove it | ||||
| 				assetUrl = assetUrl.substring(0, assetUrl.lastIndexOf('/')); | ||||
| 				autoUpdater.setFeedURL({ provider: 'generic', url: assetUrl }); | ||||
| 				await autoUpdater.checkForUpdates(); | ||||
| 			} catch (error) { | ||||
| 				this.logger_.error(`Update download url failed: ${error.message}`); | ||||
| 			} | ||||
|  | ||||
| 		} catch (error) { | ||||
| 			this.logger_.error(error); | ||||
| 			this.logger_.error(`Fetching releases failed:  ${error.message}`); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user