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) {