mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-21 09:38:01 +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:
parent
7e9c7d7d23
commit
6163364b26
@ -491,6 +491,7 @@ packages/app-desktop/integration-tests/util/test.js
|
|||||||
packages/app-desktop/integration-tests/util/waitForNextOpenPath.js
|
packages/app-desktop/integration-tests/util/waitForNextOpenPath.js
|
||||||
packages/app-desktop/playwright.config.js
|
packages/app-desktop/playwright.config.js
|
||||||
packages/app-desktop/plugins/GotoAnything.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/autoUpdater/AutoUpdaterService.js
|
||||||
packages/app-desktop/services/bridge.js
|
packages/app-desktop/services/bridge.js
|
||||||
packages/app-desktop/services/commands/stateToWhenClauseContext.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/integration-tests/util/waitForNextOpenPath.js
|
||||||
packages/app-desktop/playwright.config.js
|
packages/app-desktop/playwright.config.js
|
||||||
packages/app-desktop/plugins/GotoAnything.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/autoUpdater/AutoUpdaterService.js
|
||||||
packages/app-desktop/services/bridge.js
|
packages/app-desktop/services/bridge.js
|
||||||
packages/app-desktop/services/commands/stateToWhenClauseContext.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 defaultUpdateInterval = 12 * 60 * 60 * 1000;
|
||||||
export const initialUpdateStartup = 5 * 1000;
|
export const initialUpdateStartup = 5 * 1000;
|
||||||
const releasesLink = 'https://objects.joplinusercontent.com/r/releases';
|
const releasesLink = 'https://objects.joplinusercontent.com/r/releases';
|
||||||
|
const supportedPlatformAssets: { [key in string]: string } = {
|
||||||
|
'darwin': 'latest-mac.yml',
|
||||||
|
'win32': 'latest.yml',
|
||||||
|
};
|
||||||
|
|
||||||
export interface AutoUpdaterServiceInterface {
|
export interface AutoUpdaterServiceInterface {
|
||||||
checkForUpdates(): void;
|
checkForUpdates(): void;
|
||||||
updateApp(): void;
|
updateApp(): void;
|
||||||
|
fetchLatestRelease(includePreReleases: boolean): Promise<GitHubRelease>;
|
||||||
|
getDownloadUrlForPlatform(release: GitHubRelease, platform: string): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class AutoUpdaterService implements AutoUpdaterServiceInterface {
|
export default class AutoUpdaterService implements AutoUpdaterServiceInterface {
|
||||||
@ -45,7 +51,7 @@ export default class AutoUpdaterService implements AutoUpdaterServiceInterface {
|
|||||||
|
|
||||||
public checkForUpdates = async (): Promise<void> => {
|
public checkForUpdates = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await this.fetchLatestRelease();
|
await this.checkForLatestRelease();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger_.error('Failed to check for updates:', error);
|
this.logger_.error('Failed to check for updates:', error);
|
||||||
if (error.message.includes('ERR_CONNECTION_REFUSED')) {
|
if (error.message.includes('ERR_CONNECTION_REFUSED')) {
|
||||||
@ -58,7 +64,33 @@ export default class AutoUpdaterService implements AutoUpdaterServiceInterface {
|
|||||||
autoUpdater.quitAndInstall(false, true);
|
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);
|
const response = await fetch(releasesLink);
|
||||||
|
|
||||||
if (!response.ok) {
|
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)}`);
|
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 {
|
try {
|
||||||
const releases = await this.fetchLatestReleases();
|
const release: GitHubRelease = await this.fetchLatestRelease(this.includePreReleases_);
|
||||||
|
|
||||||
const sortedReleasesByVersion = releases.sort((a, b) => {
|
try {
|
||||||
return semver.rcompare(a.tag_name, b.tag_name);
|
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
|
||||||
const filteredReleases = sortedReleasesByVersion.filter(release => {
|
assetUrl = assetUrl.substring(0, assetUrl.lastIndexOf('/'));
|
||||||
return this.includePreReleases_ || !release.prerelease;
|
autoUpdater.setFeedURL({ provider: 'generic', url: assetUrl });
|
||||||
});
|
|
||||||
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();
|
await autoUpdater.checkForUpdates();
|
||||||
} else {
|
|
||||||
this.logger_.error('No suitable update asset found for this platform.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger_.error(error);
|
this.logger_.error(`Update download url failed: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.logger_.error(`Fetching releases failed: ${error.message}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user