From 04f0360ce29250729e832fe7ef03ec19aca27cf1 Mon Sep 17 00:00:00 2001 From: ytkimirti Date: Fri, 19 Dec 2025 13:03:17 +0300 Subject: [PATCH 1/8] test: add test to catch the broken v3 migration case --- tests/migration.spec.ts | 27 +++++++++++++++++++-------- tests/utils.ts | 10 ++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/tests/migration.spec.ts b/tests/migration.spec.ts index 6bc8f92..667439c 100644 --- a/tests/migration.spec.ts +++ b/tests/migration.spec.ts @@ -1,7 +1,6 @@ import { expect, test, type Page } from "@playwright/test" import { setup } from "./utils" -import { describe } from "node:test" /** * Helper to set localStorage before page loads @@ -74,13 +73,13 @@ const createVersion2Data = () => ({ version: 2, }) -const testFunctionality = (version: number) => { - test(`v${version} add new tab`, async ({ page }) => { +const testFunctionality = () => { + test("add new tab", async ({ page }) => { await page.getByRole("button", { name: "Add new tab" }).click() await expect(getTabs(page)).toHaveCount(3) }) - test(`v${version} select new key`, async ({ page }) => { + test("select new key", async ({ page }) => { await page.getByRole("textbox", { name: "Search" }).click() await page.getByRole("textbox", { name: "Search" }).fill("mykey-33") await page.getByRole("textbox", { name: "Search" }).press("Enter") @@ -89,22 +88,34 @@ const testFunctionality = (version: number) => { }) } -describe("migration from version 1", () => { +test.describe("migrate from v1", () => { test.beforeEach(async ({ page }) => { await setup(page) await setStorageBeforeLoad(page, createVersion1Data()) await page.goto("/") }) - testFunctionality(1) + testFunctionality() }) -describe("migration from version 2", () => { +test.describe("migrate from v2", () => { test.beforeEach(async ({ page }) => { await setup(page) await setStorageBeforeLoad(page, createVersion2Data()) await page.goto("/") }) - testFunctionality(2) + testFunctionality() +}) + +test.describe("migrate from v2 that looks has version set to 3", () => { + test.beforeEach(async ({ page }) => { + await setup(page) + const state = createVersion2Data() + state.version = 3 + await setStorageBeforeLoad(page, state) + await page.goto("/") + }) + + testFunctionality() }) diff --git a/tests/utils.ts b/tests/utils.ts index db146b0..28f5220 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -15,6 +15,16 @@ export const setup = async (page: Page) => { ;(window as any).__PLAYWRIGHT__ = true }) + // Fail fast on browser-side errors instead of waiting for timeouts + page.on("pageerror", (error) => { + throw error + }) + page.on("console", (msg) => { + if (msg.type() === "error") { + throw new Error(`Console error: ${msg.text()}`) + } + }) + const isOriginal = await redis.exists(IS_ORIGINAL_KEY) if (!isOriginal) { From 6b7abef411b4c8be7dc232dfba54b1d72efdc2ac Mon Sep 17 00:00:00 2001 From: ytkimirti Date: Fri, 19 Dec 2025 13:04:21 +0300 Subject: [PATCH 2/8] fix: migration logic for broken v3 --- src/store.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/store.tsx b/src/store.tsx index 23dab07..6523846 100644 --- a/src/store.tsx +++ b/src/store.tsx @@ -45,7 +45,7 @@ export const DatabrowserProvider = ({ setItem: (_name, value) => storage.set(JSON.stringify(value)), removeItem: () => {}, }, - version: 4, + version: 5, migrate: (originalState, version) => { const state = originalState as DatabrowserStore @@ -53,10 +53,11 @@ export const DatabrowserProvider = ({ state.tabs = state.tabs.map(([id, data]) => [id, { ...data, id }]) } - if (version <= 2) { + if (version <= 4) { // Migrate from selectedKey to selectedKeys state.tabs = state.tabs.map(([id, data]) => { const oldData = data as any + if (oldData.selectedKeys && Array.isArray(oldData.selectedKeys)) return [id, data] return [ id, { ...data, selectedKeys: oldData.selectedKey ? [oldData.selectedKey] : [] }, From f796cd3310e2db7237218770b7f866442cf909ab Mon Sep 17 00:00:00 2001 From: ytkimirti Date: Fri, 19 Dec 2025 18:35:29 +0300 Subject: [PATCH 3/8] chore: typo --- tests/migration.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/migration.spec.ts b/tests/migration.spec.ts index 667439c..01f2ff2 100644 --- a/tests/migration.spec.ts +++ b/tests/migration.spec.ts @@ -108,7 +108,7 @@ test.describe("migrate from v2", () => { testFunctionality() }) -test.describe("migrate from v2 that looks has version set to 3", () => { +test.describe("migrate from v2 that has version set to 3 (broken)", () => { test.beforeEach(async ({ page }) => { await setup(page) const state = createVersion2Data() From 5696568196ac392e2cd723528bc6f31ba560cc2d Mon Sep 17 00:00:00 2001 From: ytkimirti Date: Fri, 19 Dec 2025 19:02:34 +0300 Subject: [PATCH 4/8] chore: rerun tests --- tests/migration.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/migration.spec.ts b/tests/migration.spec.ts index 01f2ff2..f3bd502 100644 --- a/tests/migration.spec.ts +++ b/tests/migration.spec.ts @@ -70,6 +70,7 @@ const createVersion2Data = () => ({ ], searchHistory: ["previous-search"], }, + version: 2, }) From 9298d048ad699eee3fac06b6c658207acd29cb72 Mon Sep 17 00:00:00 2001 From: ytkimirti Date: Mon, 22 Dec 2025 10:34:11 +0300 Subject: [PATCH 5/8] test: fix tests --- .../display/input/custom-editor.tsx | 50 ++++++++++++++----- tests/test.spec.ts | 7 ++- tests/utils.ts | 7 +++ 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/components/databrowser/components/display/input/custom-editor.tsx b/src/components/databrowser/components/display/input/custom-editor.tsx index 9344977..f2f5777 100644 --- a/src/components/databrowser/components/display/input/custom-editor.tsx +++ b/src/components/databrowser/components/display/input/custom-editor.tsx @@ -6,21 +6,32 @@ import { Editor, useMonaco } from "@monaco-editor/react" import { cn, isTest } from "@/lib/utils" import { CopyButton } from "@/components/databrowser/copy-button" -export const CustomEditor = ({ - language, - value, - onChange, - height, - showCopyButton, - readOnly, -}: { +type CustomEditorProps = { language: string value: string onChange: (value: string) => void height?: number showCopyButton?: boolean readOnly?: boolean -}) => { +} + +export const CustomEditor = (props: CustomEditorProps) => { + // Avoid mounting Monaco at all during Playwright runs + if (isTest) { + return + } + + return +} + +const MonacoEditor = ({ + language, + value, + onChange, + height, + showCopyButton, + readOnly, +}: CustomEditorProps) => { const { active } = useTab() const monaco = useMonaco() const editorRef = useRef() @@ -82,11 +93,24 @@ export const CustomEditor = ({ className={cn("group/editor relative", height === undefined && "h-full")} style={{ height: height }} > - {isTest ? ( - onChange(e.target.value)} /> - ) : ( - editor + {editor} + {showCopyButton && ( + )} + + ) +} + +const TestEditor = ({ value, onChange, height, showCopyButton }: CustomEditorProps) => { + return ( +
+ onChange(e.target.value)} /> {showCopyButton && ( { await page.goto("/") }) -describe("keys", () => { +test.describe("keys", () => { test("can search for a key", async ({ page }) => { await page.getByRole("textbox", { name: "Search" }).click() await page.getByRole("textbox", { name: "Search" }).fill("mykey-2") @@ -114,7 +113,7 @@ describe("keys", () => { }) }) -describe("hash", () => { +test.describe("hash", () => { test("can read a hash value and cancel", async ({ page }) => { await page.getByRole("textbox", { name: "Search" }).click() await page.getByRole("textbox", { name: "Search" }).fill("myhash") @@ -182,7 +181,7 @@ describe("hash", () => { }) }) -describe("tabs", () => { +test.describe("tabs", () => { test("can switch tabs", async ({ page }) => { await page.getByRole("textbox", { name: "Search" }).click() await page.getByRole("textbox", { name: "Search" }).fill("mykey-42") diff --git a/tests/utils.ts b/tests/utils.ts index 28f5220..68ade2e 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -21,6 +21,13 @@ export const setup = async (page: Page) => { }) page.on("console", (msg) => { if (msg.type() === "error") { + // const text = msg.text() + + // // Ignore Monaco loader cancelation noise (expected when editors unmount) + // if (text.includes("operation is manually canceled")) { + // return + // } + throw new Error(`Console error: ${msg.text()}`) } }) From 40492094becdf9d8b67a0884250bd75d11ca884b Mon Sep 17 00:00:00 2001 From: ytkimirti Date: Mon, 22 Dec 2025 12:24:22 +0300 Subject: [PATCH 6/8] fix: add safety net for the selectedKeys field --- src/tab-provider.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tab-provider.tsx b/src/tab-provider.tsx index 3599ce8..9790e25 100644 --- a/src/tab-provider.tsx +++ b/src/tab-provider.tsx @@ -38,8 +38,8 @@ export const useTab = () => { return useMemo( () => ({ active: selectedTab === tabId, - selectedKey: tabData.selectedKeys[0], // Backwards compatibility - first selected key - selectedKeys: tabData.selectedKeys, + selectedKey: tabData.selectedKeys?.[0], // Backwards compatibility - first selected key + selectedKeys: tabData.selectedKeys ?? [], selectedListItem: tabData.selectedListItem, search: tabData.search, pinned: tabData.pinned, From f9e2d86946f9dc8965b3c4c8cba34e6fbb08f1f7 Mon Sep 17 00:00:00 2001 From: ytkimirti Date: Mon, 22 Dec 2025 12:25:23 +0300 Subject: [PATCH 7/8] chore: remove comment --- tests/utils.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/utils.ts b/tests/utils.ts index 68ade2e..28f5220 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -21,13 +21,6 @@ export const setup = async (page: Page) => { }) page.on("console", (msg) => { if (msg.type() === "error") { - // const text = msg.text() - - // // Ignore Monaco loader cancelation noise (expected when editors unmount) - // if (text.includes("operation is manually canceled")) { - // return - // } - throw new Error(`Console error: ${msg.text()}`) } }) From b38da3ed8703854efd26e9090fe4772089fd5b21 Mon Sep 17 00:00:00 2001 From: ytkimirti Date: Mon, 22 Dec 2025 12:27:51 +0300 Subject: [PATCH 8/8] test: add concurrency flag to playwright tests --- .github/workflows/playwright.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 7e407c9..5c2beda 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -6,6 +6,7 @@ on: branches: [ main, master ] jobs: test: + concurrency: "playwright-test" timeout-minutes: 60 runs-on: ubuntu-latest steps: