From 578bc6e546576303f6285c529ad790a4a922f7ad Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Mon, 21 Jul 2025 09:19:53 +0500 Subject: [PATCH] desktop: build app only once during tests --- apps/desktop/__tests__/auto-updates.test.ts | 2 +- apps/desktop/__tests__/global-setup.ts | 24 +++++ apps/desktop/__tests__/launch.test.ts | 2 +- apps/desktop/__tests__/test-override.ts | 50 ++++++++++ apps/desktop/__tests__/utils.ts | 102 +++++++------------- apps/desktop/vitest.config.ts | 1 + 6 files changed, 113 insertions(+), 68 deletions(-) create mode 100644 apps/desktop/__tests__/global-setup.ts create mode 100644 apps/desktop/__tests__/test-override.ts diff --git a/apps/desktop/__tests__/auto-updates.test.ts b/apps/desktop/__tests__/auto-updates.test.ts index cab7a2ca0..e70e0bb2a 100644 --- a/apps/desktop/__tests__/auto-updates.test.ts +++ b/apps/desktop/__tests__/auto-updates.test.ts @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -import { test, testCleanup } from "./utils.js"; +import { testCleanup, test } from "./test-override.js"; import { writeFile } from "fs/promises"; import { Page } from "playwright"; import { gt, lt } from "semver"; diff --git a/apps/desktop/__tests__/global-setup.ts b/apps/desktop/__tests__/global-setup.ts new file mode 100644 index 000000000..28337fce0 --- /dev/null +++ b/apps/desktop/__tests__/global-setup.ts @@ -0,0 +1,24 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2023 Streetwriters (Private) Limited + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +import { buildApp } from "./utils"; + +export default async function setup() { + await buildApp(); +} diff --git a/apps/desktop/__tests__/launch.test.ts b/apps/desktop/__tests__/launch.test.ts index 0617be6df..fd576a86f 100644 --- a/apps/desktop/__tests__/launch.test.ts +++ b/apps/desktop/__tests__/launch.test.ts @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -import { testCleanup, test } from "./utils.js"; +import { testCleanup, test } from "./test-override.js"; test("make sure app loads", async ({ ctx: { page }, diff --git a/apps/desktop/__tests__/test-override.ts b/apps/desktop/__tests__/test-override.ts new file mode 100644 index 000000000..92fe56612 --- /dev/null +++ b/apps/desktop/__tests__/test-override.ts @@ -0,0 +1,50 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2023 Streetwriters (Private) Limited + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +import { test as vitestTest, TestContext } from "vitest"; +import { buildAndLaunchApp, Fixtures, TestOptions } from "./utils"; +import { mkdir, rm } from "fs/promises"; +import path from "path"; +import slugify from "slugify"; + +export const test = vitestTest.extend({ + options: { version: "3.0.0" } as TestOptions, + ctx: async ({ options }, use) => { + const ctx = await buildAndLaunchApp(options); + await use(ctx); + } +}); + +export async function testCleanup(context: TestContext) { + const ctx = (context.task.context as unknown as Fixtures).ctx; + if (context.task.result?.state === "fail") { + await mkdir("test-results", { recursive: true }); + await ctx.page.screenshot({ + path: path.join( + "test-results", + `${slugify(context.task.name)}-${process.platform}-${ + process.arch + }-error.png` + ) + }); + } + await ctx.app.close(); + await rm(ctx.userDataDir, { force: true, recursive: true }); + await rm(ctx.outputDir, { force: true, recursive: true }); +} diff --git a/apps/desktop/__tests__/utils.ts b/apps/desktop/__tests__/utils.ts index 90d0d33ac..b4ecaf155 100644 --- a/apps/desktop/__tests__/utils.ts +++ b/apps/desktop/__tests__/utils.ts @@ -18,19 +18,19 @@ along with this program. If not, see . */ import { execSync } from "child_process"; -import { cp, mkdir, readFile, rm, writeFile } from "fs/promises"; +import { cp, readFile, writeFile } from "fs/promises"; import { fileURLToPath } from "node:url"; import path, { join, resolve } from "path"; import { _electron as electron } from "playwright"; -import slugify from "slugify"; -import { test as vitestTest, TestContext } from "vitest"; import { existsSync } from "fs"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const IS_DEBUG = process.env.NN_DEBUG === "true" || process.env.CI === "true"; +const productName = `NotesnookTestHarness`; +const SOURCE_DIR = resolve("output", productName); -interface AppContext { +export interface AppContext { app: import("playwright").ElectronApplication; page: import("playwright").Page; configPath: string; @@ -39,45 +39,21 @@ interface AppContext { relaunch: () => Promise; } -interface TestOptions { +export interface TestOptions { version: string; } -interface Fixtures { +export interface Fixtures { options: TestOptions; ctx: AppContext; } -export const test = vitestTest.extend({ - options: { version: "3.0.0" } as TestOptions, - ctx: async ({ options }, use) => { - const ctx = await buildAndLaunchApp(options); - await use(ctx); - } -}); - -export async function testCleanup(context: TestContext) { - const ctx = (context.task.context as unknown as Fixtures).ctx; - if (context.task.result?.state === "fail") { - await mkdir("test-results", { recursive: true }); - await ctx.page.screenshot({ - path: path.join( - "test-results", - `${slugify(context.task.name)}-${process.platform}-${ - process.arch - }-error.png` - ) - }); - } - await ctx.app.close(); - await rm(ctx.userDataDir, { force: true, recursive: true }); - await rm(ctx.outputDir, { force: true, recursive: true }); -} - -async function buildAndLaunchApp(options?: TestOptions): Promise { +export async function buildAndLaunchApp( + options?: TestOptions +): Promise { const productName = `notesnooktest${makeid(10)}`; const outputDir = path.join("test-artifacts", `${productName}-output`); - const executablePath = await buildApp({ + const executablePath = await copyBuild({ ...options, outputDir }); @@ -139,16 +115,8 @@ async function launchApp(executablePath: string, packageName: string) { } let MAX_RETRIES = 3; -async function buildApp({ - version, - outputDir -}: { - version?: string; - outputDir: string; -}) { - const productName = `NotesnookTestHarness`; - const sourceDir = resolve("output", productName); - if (!existsSync(sourceDir)) { +export async function buildApp(version?: string) { + if (!existsSync(SOURCE_DIR)) { const args = [ "electron-builder", "--dir", @@ -167,33 +135,40 @@ async function buildApp({ NOTESNOOK_STAGING: "true", NN_PRODUCT_NAME: productName, NN_APP_ID: `com.notesnook.test.${productName}`, - NN_OUTPUT_DIR: sourceDir + NN_OUTPUT_DIR: SOURCE_DIR } }); } catch (e) { if (--MAX_RETRIES) { console.log("retrying..."); - return await buildApp({ outputDir, version }); + return await buildApp(version); } else throw e; } } - return process.platform === "win32" - ? await copyBuildWindows(sourceDir, outputDir, productName, version) - : process.platform === "darwin" - ? await copyBuildMacOS(sourceDir, outputDir, productName, version) - : await copyBuildLinux(sourceDir, outputDir, productName, version); } -async function copyBuildLinux( - sourceDir: string, +async function copyBuild({ + version, + outputDir +}: { + version?: string; + outputDir: string; +}) { + return process.platform === "win32" + ? await makeBuildCopyWindows(outputDir, productName, version) + : process.platform === "darwin" + ? await makeBuildCopyMacOS(outputDir, productName, version) + : await makeBuildCopyLinux(outputDir, productName, version); +} + +async function makeBuildCopyLinux( outputDir: string, productName: string, version?: string ) { const platformDir = process.arch === "arm64" ? "linux-arm64-unpacked" : "linux-unpacked"; - const appDir = await copyBuild( - sourceDir, + const appDir = await makeBuildCopy( outputDir, platformDir, "resources", @@ -207,16 +182,14 @@ async function copyBuildLinux( ); } -async function copyBuildWindows( - sourceDir: string, +async function makeBuildCopyWindows( outputDir: string, productName: string, version?: string ) { const platformDir = process.arch === "arm64" ? "win-arm64-unpacked" : "win-unpacked"; - const appDir = await copyBuild( - sourceDir, + const appDir = await makeBuildCopy( outputDir, platformDir, "resources", @@ -225,15 +198,13 @@ async function copyBuildWindows( return resolve(__dirname, "..", appDir, `${productName}.exe`); } -async function copyBuildMacOS( - sourceDir: string, +async function makeBuildCopyMacOS( outputDir: string, productName: string, version?: string ) { const platformDir = process.arch === "arm64" ? "mac-arm64" : "mac"; - const appDir = await copyBuild( - sourceDir, + const appDir = await makeBuildCopy( outputDir, platformDir, join(`${productName}.app`, "Contents", "Resources"), @@ -250,15 +221,14 @@ async function copyBuildMacOS( ); } -async function copyBuild( - sourceDir: string, +async function makeBuildCopy( outputDir: string, platformDir: string, resourcesDir: string, version?: string ) { const appDir = outputDir; - await cp(join(sourceDir, platformDir), outputDir, { + await cp(join(SOURCE_DIR, platformDir), outputDir, { recursive: true, preserveTimestamps: true, verbatimSymlinks: true, diff --git a/apps/desktop/vitest.config.ts b/apps/desktop/vitest.config.ts index eacce4391..0541e15bf 100644 --- a/apps/desktop/vitest.config.ts +++ b/apps/desktop/vitest.config.ts @@ -26,6 +26,7 @@ export default defineConfig({ concurrent: true, shuffle: true }, + globalSetup: "./__tests__/global-setup.ts", dir: "./__tests__/", exclude: [ "**/node_modules/**",