From 819e796bb144d751ea47ccc4c6d1aa18fd80a28f Mon Sep 17 00:00:00 2001 From: Bob Qiu Date: Mon, 2 Nov 2026 15:19:46 +0800 Subject: [PATCH] refactor: replace NeDB file access with database queries for improved reliability --- packages/insomnia-test/flows/base.flow.ts | 29 ++++++-------------- packages/insomnia-test/flows/cookie.flow.ts | 15 ++++------ packages/insomnia-test/misc/db.ts | 21 ++++++++++++++ packages/insomnia-test/misc/nedb.ts | 28 ------------------- packages/insomnia-test/pages/base.page.ts | 19 ++++++------- packages/insomnia-test/pages/page-manager.ts | 6 ++++ 6 files changed, 49 insertions(+), 69 deletions(-) create mode 100644 packages/insomnia-test/misc/db.ts delete mode 100644 packages/insomnia-test/misc/nedb.ts diff --git a/packages/insomnia-test/flows/base.flow.ts b/packages/insomnia-test/flows/base.flow.ts index 10c8e893b7..f2940cabd0 100644 --- a/packages/insomnia-test/flows/base.flow.ts +++ b/packages/insomnia-test/flows/base.flow.ts @@ -1,7 +1,6 @@ import { expect } from "@playwright/test"; import { DEFAULT_TIMEOUT } from "../misc/constants"; -import { readNedbLines } from "../misc/nedb"; import type { RequestAuthentication } from "../models/authentication"; import type { AuthTabComponent } from "../pages/auth-tab.page"; import type { PageManager } from "../pages/page-manager"; @@ -62,13 +61,13 @@ export abstract class BaseFlow { } /** - * Polls the on-disk `dbFile` NeDB file directly until `id`'s persisted + * Polls the app's `dbFile` model until `id`'s persisted * document settles on `expected` for `field`, optionally re-running * `onMismatch` first whenever a read still disagrees. The app can patch * a just-written field into a document's on-disk state in the * background rather than synchronously, so a UI read-back right after a * setter call can observe a value that a later background write then - * reverts — reading the same on-disk store the app itself acts on is + * reverts — reading the same database the app itself acts on is * the only way to know a value has actually landed. * * Most callers only need to wait (`onMismatch` omitted) for that @@ -106,7 +105,7 @@ export abstract class BaseFlow { * "URL is required" error or hits the previous address. Passive waiting * never recovers from that — the value only lands once the write is * repeated after the initial patch has landed — so this re-sends the - * same write once the on-disk store shows it was lost. + * same write once the database shows it was lost. */ protected async waitForUrlPersisted( dbFile: string, @@ -124,7 +123,7 @@ export abstract class BaseFlow { } /** - * Polls the on-disk `dbFile` NeDB file until a document whose `parentId` + * Polls the app's `dbFile` model until a document whose `parentId` * is `parentId` exists, and returns its `_id`. For models created by the * app as a hidden child of a node the UI exposes (e.g. an MCP Client's * McpRequest under its workspace), so the tree node's id is the parent's @@ -134,29 +133,17 @@ export abstract class BaseFlow { dbFile: string, parentId: string, ): Promise { - const dataPath = await this.context.getDataPath(); - let id: string | undefined; await expect(async () => { - for (const doc of readNedbLines(dataPath, dbFile)) { - if (doc.parentId === parentId) id = doc._id; - } + const docs = await this.pageManager.findDocs(dbFile, { parentId }); + id = docs.at(-1)?._id; expect(id).toBeDefined(); }).toPass({ timeout: DEFAULT_TIMEOUT }); return id!; } - /** - * Reads the on-disk `dbFile` NeDB file directly for `id`'s - * currently-persisted document, keeping only its latest revision. - */ + /** Reads `id`'s currently-persisted document from the app's database. */ private async readPersistedDoc(dbFile: string, id: string): Promise { - const dataPath = await this.context.getDataPath(); - - let latest: any; - for (const doc of readNedbLines(dataPath, dbFile)) { - if (doc._id === id) latest = doc; - } - return latest; + return (await this.pageManager.findDocs(dbFile, { _id: id }))[0]; } } diff --git a/packages/insomnia-test/flows/cookie.flow.ts b/packages/insomnia-test/flows/cookie.flow.ts index f6e2eeeef8..12834603c8 100644 --- a/packages/insomnia-test/flows/cookie.flow.ts +++ b/packages/insomnia-test/flows/cookie.flow.ts @@ -1,7 +1,6 @@ import { expect } from "@playwright/test"; import { DEFAULT_TIMEOUT } from "../misc/constants"; -import { latestLiveDocs, readNedbLines } from "../misc/nedb"; import type { Collection } from "../models/collection"; import type { Cookie } from "../models/cookie"; import type { EventStreamRequest } from "../models/event-stream-request"; @@ -66,7 +65,7 @@ export class CookieFlow extends BaseFlow { } /** - * Polls the on-disk `CookieJar` NeDB file directly until it contains + * Polls the app's `CookieJar` documents until it contains * every one of `cookies` by key/value, re-running the whole * open→setCookies→close write once per mismatch. The dialog's unmount * can cancel the app's debounced save, so a lost cookie write has to be @@ -76,11 +75,9 @@ export class CookieFlow extends BaseFlow { item: CookieTarget, cookies: Cookie[], ): Promise { - const dataPath = await this.context.getDataPath(); - - const isPersisted = (): boolean => { - const persisted = latestLiveDocs( - readNedbLines(dataPath, "insomnia.CookieJar.db"), + const isPersisted = async (): Promise => { + const persisted = ( + await this.pageManager.findDocs("insomnia.CookieJar.db") ).flatMap((doc) => doc.cookies ?? []); return cookies.every((cookie) => @@ -91,13 +88,13 @@ export class CookieFlow extends BaseFlow { }; await expect(async () => { - if (!isPersisted()) { + if (!(await isPersisted())) { await this.openTarget(item); await this.pageManager.cookiePage.open(); await this.pageManager.cookiePage.setCookies(cookies); await this.pageManager.cookiePage.close(); } - expect(isPersisted()).toBe(true); + expect(await isPersisted()).toBe(true); }).toPass({ timeout: DEFAULT_TIMEOUT }); } diff --git a/packages/insomnia-test/misc/db.ts b/packages/insomnia-test/misc/db.ts new file mode 100644 index 0000000000..9c06d19821 --- /dev/null +++ b/packages/insomnia-test/misc/db.ts @@ -0,0 +1,21 @@ +import type { Page } from "@playwright/test"; + +/** + * Queries the app's database through the same `window.database` bridge the + * renderer uses, so tests read the documents the app acts on rather than + * parsing its on-disk NeDB files. `dbFile` is the model's file name + * (`insomnia.Request.db`), kept so callers still name the model they mean. + */ +export async function findDocs( + page: Page, + dbFile: string, + query: Record = {}, +): Promise { + const type = /^insomnia\.(.+)\.db$/.exec(dbFile)?.[1]; + if (!type) throw new Error(`Not a model db file name: ${dbFile}`); + return page.evaluate( + ([t, q]) => + (window as any).database.invoke("find", t, q) as Promise, + [type, query] as const, + ); +} diff --git a/packages/insomnia-test/misc/nedb.ts b/packages/insomnia-test/misc/nedb.ts deleted file mode 100644 index 0cb5d9e80f..0000000000 --- a/packages/insomnia-test/misc/nedb.ts +++ /dev/null @@ -1,28 +0,0 @@ -import * as fs from "node:fs"; -import path from "node:path"; - -/** - * Parses the app's on-disk NeDB append-log `dbFile` under `dataPath` into - * its raw lines, oldest first. A document appears once per revision, and a - * deletion is a later line carrying `$$deleted`. - */ -export function readNedbLines(dataPath: string, dbFile: string): any[] { - return fs - .readFileSync(path.join(dataPath, dbFile), "utf8") - .split("\n") - .filter(Boolean) - .map((line) => JSON.parse(line)); -} - -/** - * Collapses `lines` to each document's latest revision, dropping the ones - * whose latest revision is a deletion. - */ -export function latestLiveDocs(lines: any[]): any[] { - const latest = new Map(); - for (const doc of lines) { - if (doc.$$deleted) latest.delete(doc._id); - else latest.set(doc._id, doc); - } - return [...latest.values()]; -} diff --git a/packages/insomnia-test/pages/base.page.ts b/packages/insomnia-test/pages/base.page.ts index aa2808c30c..f16dc6f480 100644 --- a/packages/insomnia-test/pages/base.page.ts +++ b/packages/insomnia-test/pages/base.page.ts @@ -4,7 +4,7 @@ import type { Page } from "playwright-core"; import { CodeEditorComponent } from "../components/code-editor.component"; import { DEFAULT_TIMEOUT } from "../misc/constants"; -import { latestLiveDocs, readNedbLines } from "../misc/nedb"; +import { findDocs } from "../misc/db"; import { waitForRendererIdle, waitForStableValue } from "../misc/wait"; export abstract class BasePage { @@ -74,23 +74,20 @@ export abstract class BasePage { } /** - * Reads the app's on-disk NeDB file `dbFile`, keeping only each - * document's latest revision and dropping deleted ones. + * Reads every live document of the model stored in `dbFile` from the + * app's database. */ protected async readPersistedDocs(dbFile: string): Promise { - const dataPath = await this.insomnia!.evaluate(({ app }) => - app.getPath("userData"), - ); - return latestLiveDocs(readNedbLines(dataPath, dbFile)); + return findDocs(this.page, dbFile); } /** - * Polls the app's on-disk NeDB files until any live document in one of + * Polls the app's database until any live document in one of * `dbFiles` satisfies `predicate`. Use in place of a fixed sleep after * an edit the app saves through a debounce: the UI already shows the * new value, but a caller that navigates away or unmounts the editor * before the debounce fires can silently lose the write, and only the - * on-disk store the app itself reads from shows it has landed. + * app's database shows it has landed. */ protected async waitForPersisted( dbFiles: string[], @@ -107,7 +104,7 @@ export abstract class BasePage { } /** - * Polls the on-disk NeDB files until any live document in one of + * Polls the app's database until any live document in one of * `dbFiles` holds `expected` at the value `read` extracts from it — the * single-field form of `waitForPersisted()`. Use after an edit the app * autosaves through a debounce (a script, an auth field, docs, a spec). @@ -121,7 +118,7 @@ export abstract class BasePage { } /** - * Polls the on-disk NeDB files until any live document in one of + * Polls the app's database until any live document in one of * `dbFiles` lists a row named `name` in one of the arrays `rowsOf` * returns — confirming a name/value row's debounced save has landed * (and the row has settled onto its persisted key) before the next edit diff --git a/packages/insomnia-test/pages/page-manager.ts b/packages/insomnia-test/pages/page-manager.ts index f35d73f969..6f774745fa 100644 --- a/packages/insomnia-test/pages/page-manager.ts +++ b/packages/insomnia-test/pages/page-manager.ts @@ -1,6 +1,7 @@ import type { ElectronApplication } from "@playwright/test"; import type { Page } from "playwright-core"; +import { findDocs } from "../misc/db"; import type { BasePage } from "./base.page"; import { CertificatesPage } from "./certificates.page"; import { CloudSyncPage } from "./cloud-sync.page"; @@ -138,6 +139,11 @@ export class PageManager { private insomnia?: ElectronApplication, ) {} + /** Queries the app's database through the current window. See `findDocs`. */ + findDocs(dbFile: string, query: Record = {}): Promise { + return findDocs(this.page, dbFile, query); + } + /** * Rewires every already-created Page instance (and future ones) onto a * fresh window/ElectronApplication — called by `AppFlow.restart()`