maybe?
Some checks failed
Playwright Tests / test (push) Failing after 1m4s

This commit is contained in:
parent b515ddfafe
commit 7d99907277
Signed by: tablet
GPG Key ID: 924A5F6AF051E87C
12 changed files with 942 additions and 133 deletions

BIN
.yarn/install-state.gz vendored

Binary file not shown.

@ -5,6 +5,10 @@
"@codemirror/language": "https://github.com/lishid/cm-language", "@codemirror/language": "https://github.com/lishid/cm-language",
"@codemirror/state": "^6.0.1", "@codemirror/state": "^6.0.1",
"@codemirror/view": "^6.0.1", "@codemirror/view": "^6.0.1",
"asar": "^3.2.0",
"playwright": "^1.48.1",
"tmp": "^0.2.3",
"electron": "^33.0.2",
"typescript": "^5.6.3" "typescript": "^5.6.3"
}, },
"version": "", "version": "",
@ -14,8 +18,8 @@
"default": "./lib/index.js" "default": "./lib/index.js"
}, },
"./utils": { "./utils": {
"types": "./lib/utils.d.ts", "types": "./lib/util.d.ts",
"default": "./lib/utils.js" "default": "./lib/util.js"
}, },
"./fixture": { "./fixture": {
"types": "./lib/fixtures.d.ts", "types": "./lib/fixtures.d.ts",
@ -31,8 +35,8 @@
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.48.1", "@playwright/test": "^1.48.1",
"@types/tmp": "^0",
"obsidian": "latest", "obsidian": "latest",
"playwright": "^1.48.1",
"vitest": "^2.1.3" "vitest": "^2.1.3"
}, },
"type": "module" "type": "module"

@ -1,10 +1,11 @@
import { ElectronApplication, JSHandle, Page } from "playwright";
import { ObsidianTestingConfig } from "./index.js";
import { App } from "obsidian"; import { App } from "obsidian";
import { ElectronApplication, Page } from "playwright"; // import { getFile } from "./util.js";
import { ObsidianTestingConfig } from "./index";
export interface ObsidianTestFixtures { export interface ObsidianTestFixtures {
electronApp: ElectronApplication; electronApp: ElectronApplication;
page: Page; page: Page;
app: App;
obsidian: ObsidianTestingConfig; obsidian: ObsidianTestingConfig;
appHandle: JSHandle<App>;
} }

@ -1,10 +1,13 @@
import { App } from "obsidian";
import { test as base } from "@playwright/test"; import { test as base } from "@playwright/test";
import { _electron as electron } from "playwright"; import { _electron as electron } from "playwright";
import { Fixtures } from "@playwright/test"; import { Fixtures } from "@playwright/test";
import { execSync } from "child_process"; import { execSync } from "child_process";
import path from "path"; import path from "path";
import { ObsidianTestFixtures } from "./fixtures"; import { extractAll } from "asar";
import { ObsidianTestFixtures } from "./fixtures.js";
import tmp from "tmp";
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
import { getApp, waitForIndexingComplete } from "./util.js";
export interface ObsidianTestingConfig { export interface ObsidianTestingConfig {
vault?: string; vault?: string;
@ -12,41 +15,79 @@ export interface ObsidianTestingConfig {
export function getExe(): string { export function getExe(): string {
if (process.platform == "win32") { if (process.platform == "win32") {
return path.join(process.env.LOCALAPPDATA!, "Obsidian", "Obsidian.exe"); return path.join(
process.env.LOCALAPPDATA!,
"Obsidian",
"Resources",
"obsidian.asar"
);
} }
if (process.platform == "darwin") { if (process.platform == "darwin") {
throw new Error("use a non-toy operating system, dumbass"); throw new Error("use a non-toy operating system, dumbass");
} }
return execSync("command -v obsidian").toString(); return execSync("/usr/lib/obsidian/obsidian.asar").toString();
} }
const obsidianTestFixtures: Fixtures< const obsidianTestFixtures: Fixtures<ObsidianTestFixtures> = {
ObsidianTestFixtures
> = {
electronApp: [ electronApp: [
async ({ obsidian: {vault} }, run) => { async ({ obsidian: { vault } }, run) => {
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true"; process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true";
let mainFile = path.join(tmp.tmpdir, "obsidian");
let electronFile = path.join(tmp.tmpdir, "electron");
const packageJsonFile = path.join(tmp.tmpdir, "obsidian", "package.json");
console.log("hello", getExe(), vault, mainFile);
try {
rmSync(mainFile, { recursive: true });
rmSync(electronFile, { recursive: true });
mkdirSync(electronFile);
mkdirSync(mainFile);
} catch (e) {}
extractAll(getExe(), mainFile);
extractAll(path.join(path.dirname(getExe()), "app.asar"), electronFile);
renameSync(
path.join(mainFile, "main.js"),
path.join(mainFile, "index.js")
);
const packageJson = JSON.parse(readFileSync(packageJsonFile).toString());
// packageJson.main = "app.js";
writeFileSync(packageJsonFile, JSON.stringify(packageJson));
const electronApp = await electron.launch({ const electronApp = await electron.launch({
executablePath: getExe(), timeout: 10000,
args: [!!vault && `obsidian://open?vault=${encodeURI(vault)}`].filter(a => !!a) as string[], cwd: mainFile,
args: [
path.join(path.dirname(getExe()), "app.asar"),
!!vault && `obsidian://open?vault=${encodeURI(vault)}`,
].filter((a) => !!a) as string[],
}); });
electronApp.waitForEvent("window");
electronApp.on("console", async (msg) => {
console.log(
...(await Promise.all(msg.args().map((a) => a.jsonValue())))
);
});
await run(electronApp); await run(electronApp);
await electronApp.close();
}, },
{ auto: true }, { timeout: 60000 },
], ],
page: [ page: [
async ({ electronApp }, run) => { async ({ electronApp }, run) => {
const page = await electronApp.firstWindow(); const page = await electronApp.firstWindow();
await page.waitForEvent("load")
await page.waitForLoadState("domcontentloaded");
await waitForIndexingComplete(await getApp(page));
page.on("console", async (msg) => {
console.log(
...(await Promise.all(msg.args().map((a) => a.jsonValue())))
);
});
await run(page); await run(page);
await electronApp.close();
}, },
{} { timeout: 30000 },
], ],
app: [async ({ page }, run) => { obsidian: [{}, { option: true }],
const app: App = await page.evaluate("window.app");
await run(app);
}, {auto: true}],
obsidian: [{}, {option: true}]
}; };
// @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>(obsidianTestFixtures); export const test = base.extend<ObsidianTestFixtures>(obsidianTestFixtures);

@ -1,41 +1,45 @@
import { App, TFile } from "obsidian"; import { App, TFile } from "obsidian";
import { expect } from "vitest"; import { JSHandle, Page } from "playwright";
import { expect } from "@playwright/test";
// type TestArgs = Parameters<Parameters<typeof test>[2]>[0]; // type TestArgs = Parameters<Parameters<typeof test>[2]>[0];
export async function assertFileEquals( export async function assertFileEquals(
app: App, page: Page,
path: string, path: string,
expectedContent: string, expectedContent: string,
cached: boolean = true cached: boolean = true
) { ) {
const fileContent = await readFile(app, path, cached); const fileContent = await readFile(page, path, cached);
expect(fileContent).toEqual(normalizeEOL(expectedContent)); expect(fileContent).toEqual(normalizeEOL(expectedContent));
} }
export async function assertLineEquals( export async function assertLineEquals(
app: App, page: Page,
path: string, path: string,
lineNumber: number, lineNumber: number,
expectedContent: string, expectedContent: string,
cached: boolean = true cached: boolean = true
) { ) {
const fileContent = await readFile(app, path, cached); const fileContent = await readFile(page, path, cached);
expect(fileContent.split("\n")[lineNumber]).toEqual( expect(fileContent.split("\n")[lineNumber]).toEqual(
normalizeEOL(expectedContent) normalizeEOL(expectedContent)
); );
} }
export async function getApp(page: Page): Promise<JSHandle<App>> {
return await page.evaluateHandle("window.app");
}
export async function assertLinesEqual( export async function assertLinesEqual(
app: App, page: Page,
filePath: string, filePath: string,
start: number, start: number,
end: number, end: number,
expected: string, expected: string,
cached: boolean = true cached: boolean = true
) { ) {
const fileContent = await readFile(app, filePath, cached); const fileContent = await readFile(page, filePath, cached);
const lines = fileContent.split("\n").slice(start, end); const lines = fileContent.split("\n").slice(start, end);
const expectedLines = normalizeEOL(expected).split("\n"); const expectedLines = normalizeEOL(expected).split("\n");
expect(lines.every((l, i) => l == expectedLines[i])).toEqual(true); expect(lines.every((l, i) => l == expectedLines[i])).toEqual(true);
@ -53,10 +57,45 @@ function normalizeEOL(str: string): string {
return str.split(/\r\n|\r|\n/).join("\n"); return str.split(/\r\n|\r|\n/).join("\n");
} }
async function readFile(app: App, path: string, cached: boolean = true): Promise<string> { export async function readFile(
const file = getFile(app, path); app: Page,
path: string,
cached: boolean = true
): Promise<string> {
return normalizeEOL( return normalizeEOL(
await (cached ? app.vault.cachedRead(file) : app.vault.read(file)) await doWithApp(app, (a) => {
const file = getFile(a, path);
return cached ? a.vault.cachedRead(file) : a.vault.read(file);
})
); );
} }
export async function doWithApp<T = unknown>(
page: Page,
callback: (a: App) => T | Promise<T>
): Promise<T> {
const cbStr = callback.toString();
await page.exposeFunction("__callback", callback)
return await page.evaluate<T, string>(async (cb) => {
console.log("bye", Object.keys(app));
const func = new Function(`return (${cb})(window.app)`)
return await func();
}, cbStr);
}
export function waitForIndexingComplete(appHandle: JSHandle<App>) {
return appHandle.evaluate(() => {
return new Promise(res2 => {
// let resolved = false;
app.metadataCache.on("resolved", () => {
res2(null);
// resolved = true;
});
// setTimeout(() => !resolved && rej2("timeout"), 10000);
});
});
}
export const pageUtils = {
getFile,
}

@ -2,12 +2,13 @@
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"moduleResolution": "bundler", "moduleResolution": "Node",
"lib": [ "lib": [
"es2015", "es2015",
"DOM", "DOM",
"ESNext.Disposable" "ESNext.Disposable"
], ],
"outDir": "lib", "outDir": "lib",
"baseUrl": ".", "baseUrl": ".",
"strict": true, "strict": true,

@ -1,5 +1,6 @@
import {App} from "obsidian"; import {App} from "obsidian";
declare global { declare global {
// const app: App; var app: App;
var __callback: <T>(app: App) => T;
} }
export {} export {}

@ -1,9 +1,22 @@
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import {test} from "obsidian-testing-framework" import {test} from "obsidian-testing-framework"
import {doWithApp, getApp} from "obsidian-testing-framework/utils";
test('something', async ({ page }) => { test('something', async ({ page }) => {
expect(page).toHaveURL(/obsidian\.md/i); console.log(page.url());
expect(/obsidian\.md/i.test(page.url())).toBeTruthy()
}); });
test("idk", async({app}) => { test("idk", async({page}) => {
let thing = app.metadataCache.getFirstLinkpathDest("Welcome", "."); console.log("idk")
expect(thing?.basename).toEqual("Welcome"); let what = await doWithApp(page,async (app) => {
console.log("hi", Object.keys(app), (app.metadataCache));
let thing = app.metadataCache.getFirstLinkpathDest("Welcome", "/");
console.log("THING", thing);
await new Promise(res => setTimeout(res, 5000))
console.log(thing?.path);
const what = {...thing};
delete what.parent;
delete what.vault;
return what;
});
expect(what.basename).toEqual("Welcome")
}) })

@ -20,9 +20,9 @@ export default defineConfig<ObsidianTestFixtures>({
/* Fail the build on CI if you accidentally left test.only in the source code. */ /* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI, forbidOnly: !!process.env.CI,
/* Retry on CI only */ /* Retry on CI only */
retries: process.env.CI ? 2 : 0, retries: 1,
/* Opt out of parallel tests on CI. */ /* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined, workers: 1,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html', reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
@ -32,46 +32,14 @@ export default defineConfig<ObsidianTestFixtures>({
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry', trace: 'on-first-retry',
obsidian: {
vault: "test-framework-tester"
}
}, },
/* Configure projects for major browsers */ /* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Run your local dev server before starting the tests */ /* Run your local dev server before starting the tests */
// webServer: { // webServer: {

@ -18,5 +18,5 @@
"linkStrength": 1, "linkStrength": 1,
"linkDistance": 250, "linkDistance": 250,
"scale": 1, "scale": 1,
"close": true "close": false
} }

@ -1,40 +1,20 @@
{ {
"main": { "main": {
"id": "c2fe4c323d36fc5e", "id": "930871bd163b9a7a",
"type": "split", "type": "split",
"children": [ "children": [
{ {
"id": "8298a75d0a6318ad", "id": "634dfa4a86e61f7f",
"type": "tabs", "type": "tabs",
"children": [ "children": [
{ {
"id": "c166e51cf23ccba4", "id": "b123fea9f84365d8",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "markdown", "type": "empty",
"state": {
"file": "Welcome.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Welcome"
}
}
]
},
{
"id": "ba8373549c03c775",
"type": "tabs",
"children": [
{
"id": "95e0d337b44eab37",
"type": "leaf",
"state": {
"type": "graph",
"state": {}, "state": {},
"icon": "lucide-git-fork", "icon": "lucide-file",
"title": "Graph view" "title": "New tab"
} }
} }
] ]
@ -43,15 +23,15 @@
"direction": "vertical" "direction": "vertical"
}, },
"left": { "left": {
"id": "15ee0403a7a614b4", "id": "a21ebe5947a5c71f",
"type": "split", "type": "split",
"children": [ "children": [
{ {
"id": "f297c8bc39873aa8", "id": "32175daed5963aa7",
"type": "tabs", "type": "tabs",
"children": [ "children": [
{ {
"id": "cee48933474ae820", "id": "868b1c060db3a110",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "file-explorer", "type": "file-explorer",
@ -63,7 +43,7 @@
} }
}, },
{ {
"id": "a5eb514f5fbe0a9d", "id": "4bc75e71b51e11b0",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "search", "type": "search",
@ -80,7 +60,7 @@
} }
}, },
{ {
"id": "2ea1b4740307baf6", "id": "6d851af11dfb13da",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "bookmarks", "type": "bookmarks",
@ -96,20 +76,19 @@
"width": 300 "width": 300
}, },
"right": { "right": {
"id": "9da40e5b7fceeacd", "id": "5d6a3e51ae6c758f",
"type": "split", "type": "split",
"children": [ "children": [
{ {
"id": "2e127fb9abc8dd11", "id": "1c37035be69547e5",
"type": "tabs", "type": "tabs",
"children": [ "children": [
{ {
"id": "21b5909f77e5cae0", "id": "33ec4bd3963497ab",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "backlink", "type": "backlink",
"state": { "state": {
"file": "Welcome.md",
"collapseAll": false, "collapseAll": false,
"extraContext": false, "extraContext": false,
"sortOrder": "alphabetical", "sortOrder": "alphabetical",
@ -119,25 +98,24 @@
"unlinkedCollapsed": true "unlinkedCollapsed": true
}, },
"icon": "links-coming-in", "icon": "links-coming-in",
"title": "Backlinks for Welcome" "title": "Backlinks"
} }
}, },
{ {
"id": "bc9d35775b8807b0", "id": "2487192d0b99fdd8",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "outgoing-link", "type": "outgoing-link",
"state": { "state": {
"file": "Welcome.md",
"linksCollapsed": false, "linksCollapsed": false,
"unlinkedCollapsed": true "unlinkedCollapsed": true
}, },
"icon": "links-going-out", "icon": "links-going-out",
"title": "Outgoing links from Welcome" "title": "Outgoing links"
} }
}, },
{ {
"id": "982543d44de11473", "id": "fec498f9a6d0f797",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "tag", "type": "tag",
@ -150,15 +128,13 @@
} }
}, },
{ {
"id": "52f3666280022d63", "id": "5e98b7f33c5bb964",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "outline", "type": "outline",
"state": { "state": {},
"file": "Welcome.md"
},
"icon": "lucide-list", "icon": "lucide-list",
"title": "Outline of Welcome" "title": "Outline"
} }
} }
] ]
@ -178,8 +154,6 @@
"command-palette:Open command palette": false "command-palette:Open command palette": false
} }
}, },
"active": "c166e51cf23ccba4", "active": "b123fea9f84365d8",
"lastOpenFiles": [ "lastOpenFiles": []
"Welcome.md"
]
} }

777
yarn.lock

File diff suppressed because it is too large Load Diff