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
7 changes: 4 additions & 3 deletions src/components/search/search-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { isMocksEnabled, MOCK_NODES, MOCK_EDGES } from "@/lib/mock-data"
export function SearchBar() {
const setSearchTerm = useAppStore((s) => s.setSearchTerm)
const closeAllPanels = useAppStore((s) => s.closeAllPanels)
const activeSkin = useAppStore((s) => s.activeSkin)
const { setGraphData, setLoading, clearSelection } = useGraphStore()
const refreshBalance = useUserStore((s) => s.refreshBalance)
const openModal = useModalStore((s) => s.open)
Expand Down Expand Up @@ -60,7 +61,7 @@ export function SearchBar() {
})
setGraphData(filtered, MOCK_EDGES)
} else {
const result = await searchNodes(trimmed, { limit: 100 }, controller.signal)
const result = await searchNodes(trimmed, { limit: 100, ui_skin: activeSkin === "legal" ? "legal" : undefined }, controller.signal)
setGraphData(result.nodes ?? [], result.edges ?? [])
refreshBalance()
}
Expand All @@ -72,7 +73,7 @@ export function SearchBar() {
try {
await payL402(() => {})
// Retry search after payment
const result = await searchNodes(trimmed, { limit: 100 }, controller.signal)
const result = await searchNodes(trimmed, { limit: 100, ui_skin: activeSkin === "legal" ? "legal" : undefined }, controller.signal)
setGraphData(result.nodes ?? [], result.edges ?? [])
refreshBalance()
} catch {
Expand All @@ -87,7 +88,7 @@ export function SearchBar() {
setLoading(false)
}
},
[value, setSearchTerm, closeAllPanels, clearSelection, setGraphData, setLoading, refreshBalance, openModal]
[value, setSearchTerm, closeAllPanels, clearSelection, setGraphData, setLoading, refreshBalance, openModal, activeSkin]
)

const handleClear = useCallback(async () => {
Expand Down
70 changes: 70 additions & 0 deletions src/lib/__tests__/graph-api-search.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"

// Mock sphinx helpers so api.ts can be imported without side-effects
const { getL402Mock, getSignedMessageMock } = vi.hoisted(() => ({
getL402Mock: vi.fn(),
getSignedMessageMock: vi.fn(),
}))

vi.mock("@/lib/sphinx", () => ({
getL402: getL402Mock,
getSignedMessage: getSignedMessageMock,
}))

vi.mock("@/lib/mock-data", () => ({
isMocksEnabled: () => false,
MOCK_REVIEWS: [],
MOCK_WORKFLOW_MARKETPLACE: [],
}))

import { searchNodes } from "@/lib/graph-api"

const originalFetch = global.fetch

beforeEach(() => {
getSignedMessageMock.mockResolvedValue({ signature: "", message: "" })
getL402Mock.mockResolvedValue(null)
})

afterEach(() => {
global.fetch = originalFetch
vi.clearAllMocks()
})

describe("searchNodes — ui_skin param", () => {
it("appends ui_skin=legal when opt is provided", async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ nodes: [], edges: [] }),
}) as unknown as typeof fetch

await searchNodes("foo", { ui_skin: "legal" })

const [[url]] = (global.fetch as ReturnType<typeof vi.fn>).mock.calls
expect(url).toContain("ui_skin=legal")
})

it("omits ui_skin when opt is not provided", async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ nodes: [], edges: [] }),
}) as unknown as typeof fetch

await searchNodes("foo")

const [[url]] = (global.fetch as ReturnType<typeof vi.fn>).mock.calls
expect(url).not.toContain("ui_skin")
})

it("omits ui_skin when opt is undefined", async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ nodes: [], edges: [] }),
}) as unknown as typeof fetch

await searchNodes("foo", { ui_skin: undefined })

const [[url]] = (global.fetch as ReturnType<typeof vi.fn>).mock.calls
expect(url).not.toContain("ui_skin")
})
})
50 changes: 48 additions & 2 deletions src/lib/__tests__/search-bar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ vi.mock("@/stores/graph-store", () => ({

const setSearchTerm = vi.fn()
const closeAllPanels = vi.fn()
let mockActiveSkin: string | null = null

vi.mock("@/stores/app-store", () => ({
useAppStore: (sel?: (s: unknown) => unknown) => {
const state = { setSearchTerm, closeAllPanels }
const state = { setSearchTerm, closeAllPanels, activeSkin: mockActiveSkin }
return sel ? sel(state) : state
},
}))
Expand All @@ -48,8 +49,9 @@ vi.mock("@/stores/modal-store", () => ({
}))

const mockGetLatestNodes = vi.fn()
const mockSearchNodes = vi.fn()
vi.mock("@/lib/graph-api", () => ({
searchNodes: vi.fn(),
searchNodes: (...args: unknown[]) => mockSearchNodes(...args),
getLatestNodes: (...args: unknown[]) => mockGetLatestNodes(...args),
}))

Expand Down Expand Up @@ -82,7 +84,9 @@ describe("SearchBar handleClear", () => {
beforeEach(() => {
vi.clearAllMocks()
mocksEnabled = false
mockActiveSkin = null
mockGetLatestNodes.mockResolvedValue({ nodes: [{ ref_id: "n1" }], edges: [{ id: "e1" }] })
mockSearchNodes.mockResolvedValue({ nodes: [], edges: [] })
})

function renderWithValue(inputValue: string) {
Expand Down Expand Up @@ -141,3 +145,45 @@ describe("SearchBar handleClear", () => {
expect(setGraphData).toHaveBeenCalledWith([], [])
})
})

describe("SearchBar handleSubmit — ui_skin forwarding", () => {
beforeEach(() => {
vi.clearAllMocks()
mocksEnabled = false
mockActiveSkin = null
mockGetLatestNodes.mockResolvedValue({ nodes: [], edges: [] })
mockSearchNodes.mockResolvedValue({ nodes: [], edges: [] })
})

async function submitSearch(query: string) {
render(<SearchBar />)
const input = screen.getByPlaceholderText("Search the graph...")
fireEvent.change(input, { target: { value: query } })
fireEvent.submit(input.closest("form")!)
await waitFor(() => expect(mockSearchNodes).toHaveBeenCalled())
}

it("forwards ui_skin=legal when activeSkin is 'legal'", async () => {
mockActiveSkin = "legal"
await submitSearch("contract")
expect(mockSearchNodes).toHaveBeenCalledWith(
"contract",
expect.objectContaining({ ui_skin: "legal" }),
expect.anything()
)
})

it("omits ui_skin when activeSkin is 'default'", async () => {
mockActiveSkin = "default"
await submitSearch("contract")
const opts = mockSearchNodes.mock.calls[0][1]
expect(opts?.ui_skin).toBeUndefined()
})

it("omits ui_skin when activeSkin is null", async () => {
mockActiveSkin = null
await submitSearch("contract")
const opts = mockSearchNodes.mock.calls[0][1]
expect(opts?.ui_skin).toBeUndefined()
})
})
3 changes: 2 additions & 1 deletion src/lib/graph-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const DEFAULT_SEARCH_DOMAINS: readonly string[] = []
// Search nodes via v2 endpoint
export async function searchNodes(
query: string,
opts?: { limit?: number; skip?: number; node_type?: string; domains?: string[] },
opts?: { limit?: number; skip?: number; node_type?: string; domains?: string[]; ui_skin?: string },
signal?: AbortSignal
): Promise<NodesListResponse> {
const params = new URLSearchParams({
Expand All @@ -61,6 +61,7 @@ export async function searchNodes(
skip: String(opts?.skip ?? 0),
})
if (opts?.node_type) params.set("node_type", opts.node_type)
if (opts?.ui_skin) params.set("ui_skin", opts.ui_skin)

const domains = opts?.domains ?? DEFAULT_SEARCH_DOMAINS
if (domains.length > 0) {
Expand Down
Loading