Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
branches: [ main, master ]
jobs:
test:
concurrency: "playwright-test"
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <TestEditor {...props} />
}

return <MonacoEditor {...props} />
}

const MonacoEditor = ({
language,
value,
onChange,
height,
showCopyButton,
readOnly,
}: CustomEditorProps) => {
const { active } = useTab()
const monaco = useMonaco()
const editorRef = useRef()
Expand Down Expand Up @@ -82,11 +93,24 @@ export const CustomEditor = ({
className={cn("group/editor relative", height === undefined && "h-full")}
style={{ height: height }}
>
{isTest ? (
<input aria-label="editor" value={value} onChange={(e) => onChange(e.target.value)} />
) : (
editor
{editor}
{showCopyButton && (
<CopyButton
value={value}
className="absolute right-0 top-0 hidden group-hover/editor:flex"
/>
)}
</div>
)
}

const TestEditor = ({ value, onChange, height, showCopyButton }: CustomEditorProps) => {
return (
<div
className={cn("group/editor relative", height === undefined && "h-full")}
style={{ height: height }}
>
<input aria-label="editor" value={value} onChange={(e) => onChange(e.target.value)} />
{showCopyButton && (
<CopyButton
value={value}
Expand Down
5 changes: 3 additions & 2 deletions src/store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,19 @@ export const DatabrowserProvider = ({
setItem: (_name, value) => storage.set(JSON.stringify(value)),
removeItem: () => {},
},
version: 4,
version: 5,
migrate: (originalState, version) => {
const state = originalState as DatabrowserStore

if (version <= 1) {
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] : [] },
Expand Down
4 changes: 2 additions & 2 deletions src/tab-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
28 changes: 20 additions & 8 deletions tests/migration.spec.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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")
Expand All @@ -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()
})
7 changes: 3 additions & 4 deletions tests/test.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { describe } from "node:test"
import { expect, test } from "@playwright/test"

import { markDatabaseAsModified, setup } from "./utils"
Expand All @@ -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")
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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")
Expand Down
10 changes: 10 additions & 0 deletions tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down