feat: use singleton electron app if an experimental flag is set
Some checks failed
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
☙◦ The Tablet ❀ GamerGirlandCo ◦❧ 2026-02-11 17:55:00 -05:00
parent 36903f0a37
commit 010d56a87d
Signed by: tablet
GPG Key ID: 924A5F6AF051E87C
3 changed files with 102 additions and 60 deletions

View File

@ -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;
}

View File

@ -5,50 +5,13 @@ import { ObsidianTestFixtures } from "./fixtures.js";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import { pageUtils, waitForIndexingComplete } from "./util.js"; import { pageUtils, waitForIndexingComplete } from "./util.js";
import { randomBytes } from "crypto"; import { randomBytes } from "crypto";
import { addConsoleListener, checkToy, getExe } from "./internal-util.js";
import { getOrCreateApp } from "./electron-singleton.js";
export interface ObsidianTestingConfig { export interface ObsidianTestingConfig {
vault?: string; vault?: string;
obsidianPath?: string; obsidianPath?: string;
} experimentalUseSingleton?: boolean;
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");
}
} }
function generateVaultConfig(vault: string) { function generateVaultConfig(vault: string) {
@ -87,17 +50,18 @@ function generateVaultConfig(vault: string) {
writeFileSync(path.join(configLocation, `${vaultHash}.json`), "{}"); writeFileSync(path.join(configLocation, `${vaultHash}.json`), "{}");
return vaultHash; return vaultHash;
} else { } 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 // @ts-ignore some error about a string type now having `undefined` as part of it's union
export const test = base.extend<ObsidianTestFixtures>({ export const test = base.extend<ObsidianTestFixtures>({
electronApp: [ electronApp: [
async ({obsidian}, run) => { async ({ obsidian }, run) => {
console.log("obsidian", obsidian); console.log("obsidian", obsidian);
const {obsidianPath = undefined} = obsidian ?? {}; const { obsidianPath = undefined, experimentalUseSingleton = false } =
const vault = <string> inject("vault") obsidian ?? {};
const vault = <string>inject("vault");
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true"; process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true";
console.log("asar located at:", getExe(obsidianPath)); console.log("asar located at:", getExe(obsidianPath));
let uriArg = ""; let uriArg = "";
@ -108,19 +72,23 @@ export const test = base.extend<ObsidianTestFixtures>({
} }
} }
const electronApp = await electron.launch({ const electronApp = experimentalUseSingleton
? await getOrCreateApp(
getExe(obsidianPath),
!!uriArg ? uriArg : undefined
)
: await electron.launch({
timeout: 60000, timeout: 60000,
args: [getExe(obsidianPath), uriArg].filter((a) => !!a) as string[], args: [getExe(obsidianPath), uriArg].filter((a) => !!a) as string[],
}); });
electronApp.on("console", async (msg) => { addConsoleListener(electronApp);
console.log(
...(await Promise.all(msg.args().map((a) => a.jsonValue())))
);
});
await electronApp.firstWindow(); await electronApp.firstWindow();
await run(electronApp); await run(electronApp);
if (!experimentalUseSingleton) {
await electronApp.close(); await electronApp.close();
}, {auto: true} }
},
{ auto: true },
], ],
page: [ page: [
async ({ electronApp }, run) => { async ({ electronApp }, run) => {
@ -130,22 +98,23 @@ export const test = base.extend<ObsidianTestFixtures>({
await page.waitForLoadState("domcontentloaded"); await page.waitForLoadState("domcontentloaded");
try { try {
await waitForIndexingComplete(page); await waitForIndexingComplete(page);
} catch(e) { } catch (e) {
console.warn("timed out waiting for metadata cache. continuing..."); 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]); await page.exposeFunction(fn[0], fn[1]);
} }
page.on("pageerror", exc => { page.on("pageerror", (exc) => {
console.error("EXCEPTION"); console.error("EXCEPTION");
console.error(exc); console.error(exc);
}) });
page.on("console", async (msg) => { page.on("console", async (msg) => {
console.log( console.log(
...(await Promise.all(msg.args().map((a) => a.jsonValue()))) ...(await Promise.all(msg.args().map((a) => a.jsonValue())))
); );
}); });
await run(page); await run(page);
}, {auto: true} },
{ auto: true },
], ],
}); });

View File

@ -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())))
);
});
}