diff --git a/packages/obsidian-testing-framework/src/electron-singleton.ts b/packages/obsidian-testing-framework/src/electron-singleton.ts new file mode 100644 index 0000000..13bc9ce --- /dev/null +++ b/packages/obsidian-testing-framework/src/electron-singleton.ts @@ -0,0 +1,22 @@ +import type { ElectronApplication } from "playwright" +import {_electron as electron} from "playwright"; +import { addConsoleListener } from "./internal-util"; + +const singleton: { + app: ElectronApplication | null +} = { + app: null, +} + +export async function getOrCreateApp(obsidianPath: string, uriArg?: string) { + if (singleton.app) { + return singleton.app; + } + const app = await electron.launch({ + timeout: 60000, + args: [obsidianPath, uriArg].filter((a) => !!a) as string[], + }); + addConsoleListener(app); + singleton.app = app; + return app; +} diff --git a/packages/obsidian-testing-framework/src/index.ts b/packages/obsidian-testing-framework/src/index.ts index de1ba75..66e9057 100644 --- a/packages/obsidian-testing-framework/src/index.ts +++ b/packages/obsidian-testing-framework/src/index.ts @@ -5,50 +5,13 @@ import { ObsidianTestFixtures } from "./fixtures.js"; import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; import { pageUtils, waitForIndexingComplete } from "./util.js"; import { randomBytes } from "crypto"; +import { addConsoleListener, checkToy, getExe } from "./internal-util.js"; +import { getOrCreateApp } from "./electron-singleton.js"; export interface ObsidianTestingConfig { vault?: string; obsidianPath?: string; -} - -export function getExe(obsidianPath?: string): string { - checkToy(); - if (obsidianPath) { - return path.join(obsidianPath, "Resources", "app.asar"); - } - if (process.platform == "win32") { - const p = path.join( - process.env.LOCALAPPDATA!, - "Obsidian", - "Resources", - "app.asar" - ); - if (existsSync(p)) { - return p; - } - } - - const possibleDirs = [ - "/opt/Obsidian", - "/usr/lib/Obsidian", - "/opt/obsidian", - "/usr/lib/obsidian", - "/var/lib/flatpak/app/md.obsidian.Obsidian/current/active/files", - "/snap/obsidian/current", - ]; - for (let i = 0; i < possibleDirs.length; i++) { - if (existsSync(possibleDirs[i])) { - // console.log(execSync(`ls -l ${possibleDirs[i]}`).toString()); - return path.join(possibleDirs[i], "resources", "app.asar"); - } - } - return ""; -} - -function checkToy() { - if (process.platform == "darwin") { - throw new Error("use a non-toy operating system, dumbass"); - } + experimentalUseSingleton?: boolean; } function generateVaultConfig(vault: string) { @@ -87,17 +50,18 @@ function generateVaultConfig(vault: string) { writeFileSync(path.join(configLocation, `${vaultHash}.json`), "{}"); return vaultHash; } else { - return Object.entries(json.vaults).find(a => a[1].path === vault)![0]; + return Object.entries(json.vaults).find((a) => a[1].path === vault)![0]; } } // @ts-ignore some error about a string type now having `undefined` as part of it's union export const test = base.extend({ electronApp: [ - async ({obsidian}, run) => { + async ({ obsidian }, run) => { console.log("obsidian", obsidian); - const {obsidianPath = undefined} = obsidian ?? {}; - const vault = inject("vault") + const { obsidianPath = undefined, experimentalUseSingleton = false } = + obsidian ?? {}; + const vault = inject("vault"); process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true"; console.log("asar located at:", getExe(obsidianPath)); let uriArg = ""; @@ -108,19 +72,23 @@ export const test = base.extend({ } } - const electronApp = await electron.launch({ - timeout: 60000, - args: [getExe(obsidianPath), uriArg].filter((a) => !!a) as string[], - }); - electronApp.on("console", async (msg) => { - console.log( - ...(await Promise.all(msg.args().map((a) => a.jsonValue()))) - ); - }); + const electronApp = experimentalUseSingleton + ? await getOrCreateApp( + getExe(obsidianPath), + !!uriArg ? uriArg : undefined + ) + : await electron.launch({ + timeout: 60000, + args: [getExe(obsidianPath), uriArg].filter((a) => !!a) as string[], + }); + addConsoleListener(electronApp); await electronApp.firstWindow(); await run(electronApp); - await electronApp.close(); - }, {auto: true} + if (!experimentalUseSingleton) { + await electronApp.close(); + } + }, + { auto: true }, ], page: [ async ({ electronApp }, run) => { @@ -130,22 +98,23 @@ export const test = base.extend({ await page.waitForLoadState("domcontentloaded"); try { await waitForIndexingComplete(page); - } catch(e) { + } catch (e) { console.warn("timed out waiting for metadata cache. continuing..."); } - for(let fn of Object.entries(pageUtils)) { + for (let fn of Object.entries(pageUtils)) { await page.exposeFunction(fn[0], fn[1]); } - page.on("pageerror", exc => { + page.on("pageerror", (exc) => { console.error("EXCEPTION"); console.error(exc); - }) + }); page.on("console", async (msg) => { console.log( ...(await Promise.all(msg.args().map((a) => a.jsonValue()))) ); }); await run(page); - }, {auto: true} + }, + { auto: true }, ], }); diff --git a/packages/obsidian-testing-framework/src/internal-util.ts b/packages/obsidian-testing-framework/src/internal-util.ts new file mode 100644 index 0000000..ad19c1e --- /dev/null +++ b/packages/obsidian-testing-framework/src/internal-util.ts @@ -0,0 +1,51 @@ +import path from "path"; +import { existsSync } from "fs"; +import { ElectronApplication } from "playwright"; + +export function checkToy() { + if (process.platform == "darwin") { + throw new Error("use a non-toy operating system, dumbass"); + } +} + +export function getExe(obsidianPath?: string): string { + checkToy(); + if (obsidianPath) { + return path.join(obsidianPath, "Resources", "app.asar"); + } + if (process.platform == "win32") { + const p = path.join( + process.env.LOCALAPPDATA!, + "Obsidian", + "Resources", + "app.asar" + ); + if (existsSync(p)) { + return p; + } + } + + const possibleDirs = [ + "/opt/Obsidian", + "/usr/lib/Obsidian", + "/opt/obsidian", + "/usr/lib/obsidian", + "/var/lib/flatpak/app/md.obsidian.Obsidian/current/active/files", + "/snap/obsidian/current", + ]; + for (let i = 0; i < possibleDirs.length; i++) { + if (existsSync(possibleDirs[i])) { + // console.log(execSync(`ls -l ${possibleDirs[i]}`).toString()); + return path.join(possibleDirs[i], "resources", "app.asar"); + } + } + return ""; +} + +export function addConsoleListener(app: ElectronApplication) { + app.on("console", async (msg) => { + console.log( + ...(await Promise.all(msg.args().map((a) => a.jsonValue()))) + ); + }); +}