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:
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 && (
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] : [] },
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,
diff --git a/tests/migration.spec.ts b/tests/migration.spec.ts
index 6bc8f92..f3bd502 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
@@ -71,16 +70,17 @@ const createVersion2Data = () => ({
],
searchHistory: ["previous-search"],
},
+
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 +89,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 has version set to 3 (broken)", () => {
+ 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/test.spec.ts b/tests/test.spec.ts
index 1008d34..959469c 100644
--- a/tests/test.spec.ts
+++ b/tests/test.spec.ts
@@ -1,4 +1,3 @@
-import { describe } from "node:test"
import { expect, test } from "@playwright/test"
import { markDatabaseAsModified, setup } from "./utils"
@@ -9,7 +8,7 @@ test.beforeEach(async ({ page }) => {
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 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) {