From c6f71fdce2b58f5a3059a74f0237a30fc5a39cda Mon Sep 17 00:00:00 2001 From: Adam Pickering Date: Wed, 25 Jan 2023 15:47:09 -0700 Subject: [PATCH 1/2] Quit when windows closed if quitOnClose true Signed-off-by: Adam Pickering --- background.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/background.ts b/background.ts index 108fe7336e9..16e4ddebde4 100644 --- a/background.ts +++ b/background.ts @@ -399,6 +399,10 @@ Electron.app.on('before-quit', async(event) => { }); Electron.app.on('window-all-closed', () => { + if (cfg.window.quitOnClose) { + Electron.app.quit(); + } + // On macOS, hide the dock icon. Electron.app.dock?.hide(); }); From c9e68a81e768881d1c7433f95b4915f724f54403 Mon Sep 17 00:00:00 2001 From: Adam Pickering Date: Fri, 27 Jan 2023 09:47:34 -0700 Subject: [PATCH 2/2] Add E2E tests for quitOnClose setting Signed-off-by: Adam Pickering --- e2e/quit-on-close.e2e.spec.ts | 59 +++++++++++++++++++++++++++++++++++ e2e/utils/TestUtils.ts | 36 +++++++++++++++++++-- 2 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 e2e/quit-on-close.e2e.spec.ts diff --git a/e2e/quit-on-close.e2e.spec.ts b/e2e/quit-on-close.e2e.spec.ts new file mode 100644 index 00000000000..c3fcb6ca293 --- /dev/null +++ b/e2e/quit-on-close.e2e.spec.ts @@ -0,0 +1,59 @@ +import path from 'path'; + +import { test, expect, _electron, ElectronApplication } from '@playwright/test'; + +import { createDefaultSettings, packageLogs, startRancherDesktop } from './utils/TestUtils'; + +/** + * Using test.describe.serial make the test execute step by step, as described on each `test()` order + * Playwright executes test in parallel by default and it will not work for our app backend loading process. + * */ +test.describe.serial('quitOnClose setting', () => { + test.afterAll(async() => { + await packageLogs(__filename); + }); + + test('should quit when quitOnClose is true and window is closed', async() => { + createDefaultSettings({ window: { quitOnClose: true } }); + const { electronApp } = await startRancherDesktop(__filename, false); + + await expect(closeWindowsAndCheckQuit(electronApp)).resolves.toBe(true); + }); + + test('should not quit when quitOnClose is false and window is closed', async() => { + createDefaultSettings({ window: { quitOnClose: false } }); + const { electronApp } = await startRancherDesktop(__filename, true); + + await expect(closeWindowsAndCheckQuit(electronApp)).resolves.toBe(false); + const tracePath = path.join(__dirname, 'reports', `${ path.basename(__filename) }-quitOnCloseFalse.zip`); + + electronApp.context().tracing.stop({ path: tracePath }); + await electronApp.close(); + }); +}); + +/** + * Closes all of the windows in a running app. Returns a promise that + * resolves to true when the app has quit within a certain period of time, + * or that resolves to false when the app does not quit within that period + * of time. + * */ +function closeWindowsAndCheckQuit(electronApp: ElectronApplication): Promise { + return electronApp.evaluate(async({ app, BrowserWindow }) => { + const quitReady = new Promise((resolve) => { + app.on('will-quit', () => resolve(true)); + app.on('window-all-closed', () => { + setTimeout(() => resolve(false), 3_000); + }); + }); + + await Promise.all(BrowserWindow.getAllWindows().map((window) => { + return new Promise((resolve) => { + window.on('closed', resolve); + window.close(); + }); + })); + + return await quitReady; + }); +} diff --git a/e2e/utils/TestUtils.ts b/e2e/utils/TestUtils.ts index d0cffe3cada..5ab0bdcf519 100644 --- a/e2e/utils/TestUtils.ts +++ b/e2e/utils/TestUtils.ts @@ -6,7 +6,7 @@ import os from 'os'; import path from 'path'; import util from 'util'; -import { ElectronApplication, expect } from '@playwright/test'; +import { expect, _electron, ElectronApplication, Page } from '@playwright/test'; import _ from 'lodash'; import { defaultSettings, Settings } from '@pkg/config/settings'; @@ -65,7 +65,7 @@ export function reportAsset(testPath: string, type: 'trace' | 'log' = 'trace') { return path.join(__dirname, '..', 'reports', `${ path.basename(testPath) }-${ name }`); } -async function packageLogs(testPath: string) { +export async function packageLogs(testPath: string) { if (!process.env.CIRRUS_CI) { console.log('Skipping packaging logs, not running in Cirrus CI'); @@ -171,3 +171,35 @@ export async function retry(proc: () => Promise, options?: { delay?: numbe } } } + +/** + * Run Rancher Desktop; return promise that resolves to commonly-used + * playwright objects when it has started. + * @param testPath The path to the test file. + * @param tracing Whether to start tracing. + */ +export async function startRancherDesktop(testPath: string, tracing: boolean): Promise<{electronApp: ElectronApplication, page: Page}> { + const electronApp = await _electron.launch({ + args: [ + path.join(__dirname, '../../'), + '--disable-gpu', + '--whitelisted-ips=', + // See pkg/rancher-desktop/utils/commandLine.ts before changing the next item as the final option. + '--disable-dev-shm-usage', + '--no-modal-dialogs', + ], + env: { + ...process.env, + RD_LOGS_DIR: reportAsset(testPath, 'log'), + RD_MOCK_BACKEND: '1', + }, + }); + + if (tracing) { + electronApp.context().tracing.start({ screenshots: true, snapshots: true }); + } + + const page = await electronApp.firstWindow(); + + return { electronApp, page }; +}