Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/studio/__mocks__/electron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ export const autoUpdater = {
on: vi.fn(),
};

export const clipboard = {
writeText: vi.fn(),
};

export const session = {
defaultSession: {
setPermissionRequestHandler: vi.fn(),
Expand Down
51 changes: 50 additions & 1 deletion apps/studio/src/tests/updates.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* @vitest-environment node
*/
import { app, dialog, shell, type MessageBoxOptions } from 'electron';
import { app, clipboard, dialog, shell, type MessageBoxOptions } from 'electron';
import * as Sentry from '@sentry/electron/main';
import { vi } from 'vitest';
import { manualCheckForUpdates } from 'src/updates';
Expand Down Expand Up @@ -68,6 +68,55 @@ describe( 'Linux updater', () => {
);
} );

it( 'copies the install command to the clipboard when the user clicks the primary button', async () => {
global.fetch = vi.fn().mockResolvedValue( {
status: 200,
ok: true,
json: async () => ( {
version: '1.9.0',
downloadUrl: 'https://appscdn.example.com/path/studio_1.9.0_arm64.deb',
} ),
} as Response );
vi.mocked( dialog.showMessageBox ).mockResolvedValue( {
response: 0,
checkboxChecked: false,
} );

await manualCheckForUpdates();

await vi.waitFor( () => {
expect( shell.openExternal ).toHaveBeenCalled();
} );

expect( clipboard.writeText ).toHaveBeenCalledWith(
'sudo apt install ~/Downloads/studio_1.9.0_arm64.deb'
);
} );

it( 'does not copy to the clipboard or open the browser when the user dismisses the dialog', async () => {
global.fetch = vi.fn().mockResolvedValue( {
status: 200,
ok: true,
json: async () => ( {
version: '1.9.0',
downloadUrl: 'https://appscdn.example.com/path/studio_1.9.0_arm64.deb',
} ),
} as Response );
vi.mocked( dialog.showMessageBox ).mockResolvedValue( {
response: 1,
checkboxChecked: false,
} );

await manualCheckForUpdates();

await vi.waitFor( () => {
expect( dialog.showMessageBox ).toHaveBeenCalled();
} );

expect( clipboard.writeText ).not.toHaveBeenCalled();
expect( shell.openExternal ).not.toHaveBeenCalled();
} );

it( 'shows "No updates available" on a manual check when the server returns 204', async () => {
global.fetch = vi.fn().mockResolvedValue( {
status: 204,
Expand Down
15 changes: 8 additions & 7 deletions apps/studio/src/updates.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { app, autoUpdater, dialog } from 'electron';
import { app, autoUpdater, clipboard, dialog } from 'electron';
import * as Sentry from '@sentry/electron/main';
import { sprintf, __ } from '@wordpress/i18n';
import { AUTO_UPDATE_INTERVAL_MS } from 'src/constants';
Expand Down Expand Up @@ -300,20 +300,20 @@ async function showLinuxUpdateAvailableNotice( version: string, downloadUrl: str
const mainWindow = await getMainWindow();

const command = `sudo apt install ~/Downloads/${ debFilenameFromUrl( downloadUrl ) }`;
const introLine = __(
'After downloading, quit Studio and run this command from a terminal to install:'
const actionDescription = __(
'Clicking the button below will download the new package and copy the install command to your clipboard.'
);
const doubleClickHint = __(
'On some distributions, double-clicking the downloaded file may also work.'
const followUpInstruction = __(
'Once the download finishes, quit Studio, open a terminal, and paste the command to install:'
);

const { response } = await dialog.showMessageBox( mainWindow, {
type: 'info',
buttons: [ __( 'Download' ), __( 'Later' ) ],
buttons: [ __( 'Download & copy command' ), __( 'Later' ) ],
title: __( 'New Version Available' ),
// translators: %s is the version number, e.g. "1.9.0".
message: sprintf( __( 'Studio %s is available' ), version ),
detail: `${ introLine }\n\n${ command }\n\n${ doubleClickHint }`,
detail: `${ actionDescription }\n\n${ followUpInstruction }\n\n${ command }`,
defaultId: 0,
cancelId: 1,
} );
Expand All @@ -330,6 +330,7 @@ async function showLinuxUpdateAvailableNotice( version: string, downloadUrl: str
);
return;
}
clipboard.writeText( command );
void shellOpenExternalWrapper( parsedUrl.toString() );
Comment thread
ivan-ottinger marked this conversation as resolved.
} catch {
Sentry.captureException( new Error( `Malformed downloadUrl: ${ downloadUrl }` ) );
Expand Down