diff --git a/app/public/config.js b/app/public/config.js index 2033b7319..1e8cb6a30 100644 --- a/app/public/config.js +++ b/app/public/config.js @@ -117,7 +117,7 @@ export function loadConfigs() { count++; }); - if (!configs["repository"]) { + if (count === 0) { configs["repository"] = true; firstRun = true; } diff --git a/app/public/electron.js b/app/public/electron.js index 33135b88d..7a2e1e502 100644 --- a/app/public/electron.js +++ b/app/public/electron.js @@ -21,6 +21,9 @@ let tray = null let repositoryWindows = {}; let repoIDForWebContents = {}; +if (process.env.KOPIA_CUSTOM_APPDATA) { + app.setPath('appData', process.env.KOPIA_CUSTOM_APPDATA); +} if (isPortableConfig()) { // in portable mode, write cache under 'repositories' @@ -435,6 +438,7 @@ app.on('ready', () => { app.testHooks = { tray: tray, showRepoWindow: showRepoWindow, + allConfigs: allConfigs, } } @@ -582,6 +586,5 @@ function updateTrayContextMenu() { ] }, { label: 'Quit', role: 'quit' }, ]); - tray.setContextMenu(Menu.buildFromTemplate(template)); } diff --git a/app/tests/main.spec.js b/app/tests/main.spec.js index 3993416d9..5e464a92c 100644 --- a/app/tests/main.spec.js +++ b/app/tests/main.spec.js @@ -1,9 +1,16 @@ import { test, expect } from '@playwright/test' import { _electron as electron } from 'playwright' +import fs from 'fs'; +import os from 'os'; import path from 'path'; +const DEFAULT_REPO_ID = 'repository'; + let electronApp +let mainPath +let executablePath +let tmpAppDataDir function getKopiaUIDir() { switch (process.platform + "/" + process.arch) { @@ -44,22 +51,36 @@ function getExecutablePath(kopiauiDir) { } } -test.beforeAll(async () => { - const kopiauiDir = getKopiaUIDir(); - expect(kopiauiDir).not.toBeNull(); +/** + * Creates a temporary application data directory along with the kopia + * directory for testing purposes. + * + * @returns {string} The path to the created temporary directory. + */ +function createTemporaryAppDataDir() { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kopia-test-')); + fs.mkdirSync(path.join(tmpDir, 'kopia')); + return tmpDir; +} - const mainPath = getMainPath(kopiauiDir); - const executablePath = getExecutablePath(kopiauiDir); - - console.log('main path', mainPath); - console.log('executable path', executablePath); - - process.env.CI = 'e2e' - process.env.KOPIA_UI_TESTING = '1' - electronApp = await electron.launch({ +/** + * Launches a new instance of the Electron app with the given app data directory. + * + * Also captures page errors and console messages and logs them to the console. + * + * @param {string} appDataDir - the path to the app data directory + * @returns {Promise} - a promise that resolves to the launched app + */ +async function launchApp(appDataDir) { + const electronApp = await electron.launch({ args: [mainPath], executablePath: executablePath, + env: { + ...process.env, + KOPIA_CUSTOM_APPDATA: appDataDir, + } }) + electronApp.on('window', async (page) => { const filename = page.url()?.split('/').pop() console.log(`Window opened: ${filename}`) @@ -73,13 +94,49 @@ test.beforeAll(async () => { console.log(msg.text()) }) }) + + return electronApp; +} + +/** + * Waits for Kopia to start up by delaying for a specified duration. + * + * @returns {Promise} A promise that resolves after the delay. + */ +function waitForKopiaToStartup() { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, 2500); + }); +} + +test.beforeAll(() => { + const kopiauiDir = getKopiaUIDir(); + expect(kopiauiDir).not.toBeNull(); + + mainPath = getMainPath(kopiauiDir); + executablePath = getExecutablePath(kopiauiDir); + + console.log('main path', mainPath); + console.log('executable path', executablePath); + + process.env.CI = 'e2e'; + process.env.KOPIA_UI_TESTING = '1'; +}) + +test.beforeEach(async () => { + tmpAppDataDir = createTemporaryAppDataDir(); }) -test.afterAll(async () => { - await electronApp.close() +test.afterEach(async () => { + await electronApp.close(); + fs.rmSync(tmpAppDataDir, { recursive: true, force: true }); }) -test('opens repository window', async () => { +test('opens repository window on first start', async () => { + electronApp = await launchApp(tmpAppDataDir); + await electronApp.evaluate(async ({app}) => { app.testHooks.showRepoWindow('repository'); }); @@ -100,3 +157,51 @@ test('opens repository window', async () => { return app.testHooks.tray.closeContextMenu(); }) }); + +test("adds default repository if no repository is configured", async () => { + electronApp = await launchApp(tmpAppDataDir); + + await waitForKopiaToStartup(); + + const configs = await electronApp.evaluate(async ({app}) => { + return app.testHooks.allConfigs(); + }) + expect(configs).toStrictEqual([DEFAULT_REPO_ID]); +}); + +test("doesn't open repository window if the default repository config exists", async () => { + fs.writeFileSync(path.join(tmpAppDataDir, 'kopia', `${DEFAULT_REPO_ID}.config`), ''); + + electronApp = await launchApp(tmpAppDataDir); + + await waitForKopiaToStartup(); + const windows = electronApp.windows(); + expect(windows).toHaveLength(0); +}); + +test.describe("when non-default repository config exists", () => { + const NON_DEFAULT_REPO_ID = 'repository-42'; + + test.beforeEach(async () => { + fs.writeFileSync(path.join(tmpAppDataDir, 'kopia', `${NON_DEFAULT_REPO_ID}.config`), ''); + }) + + test("doesn't open repository window if non-default repository config exists", async () => { + electronApp = await launchApp(tmpAppDataDir); + + await waitForKopiaToStartup(); + const windows = electronApp.windows(); + expect(windows).toHaveLength(0); + }); + + test("doesn't add default repository", async () => { + electronApp = await launchApp(tmpAppDataDir); + + await waitForKopiaToStartup(); + + const configs = await electronApp.evaluate(async ({app}) => { + return app.testHooks.allConfigs(); + }) + expect(configs).toStrictEqual([NON_DEFAULT_REPO_ID]); + }); +})