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
177 changes: 107 additions & 70 deletions apps/admin_dashboard/src/configuration-view.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { cleanup, fireEvent, render, screen, waitFor, within } from "@testing-library/react"
import type { ComponentProps } from "react"
import { afterEach, describe, expect, it, vi } from "vitest"

import { type ConfigurationItem, ConfigurationView } from "./views/configuration-view"

afterEach(() => cleanup())
afterEach(() => {
cleanup()
window.history.replaceState({}, "", "/dashboard/configuration")
})

function configItem(overrides: Partial<ConfigurationItem>): ConfigurationItem {
return {
Expand Down Expand Up @@ -62,18 +66,30 @@ const items: ConfigurationItem[] = [
}),
]

function renderConfigurationView(
overrides: Partial<ComponentProps<typeof ConfigurationView>> = {},
) {
return render(
<ConfigurationView
items={items}
loading={{}}
canWrite
jobChannels={[]}
availableJobChannels={[]}
onRefresh={vi.fn()}
onRefreshJobChannels={vi.fn()}
onSave={vi.fn()}
onClear={vi.fn()}
onSaveJobChannel={vi.fn()}
onDeleteJobChannel={vi.fn()}
{...overrides}
/>,
)
}

describe("ConfigurationView", () => {
it("groups settings and keeps tuning rows behind advanced disclosure", () => {
render(
<ConfigurationView
items={items}
loading={{}}
canWrite
onRefresh={vi.fn()}
onSave={vi.fn()}
onClear={vi.fn()}
/>,
)
renderConfigurationView()

expect(screen.getByRole("heading", { name: "Onboarding" })).toBeVisible()
expect(screen.getByRole("heading", { name: "AI Providers" })).toBeVisible()
Expand All @@ -91,16 +107,7 @@ describe("ConfigurationView", () => {

it("filters to a selected group and disables impossible secret saves", () => {
const onSave = vi.fn()
render(
<ConfigurationView
items={items}
loading={{}}
canWrite
onRefresh={vi.fn()}
onSave={onSave}
onClear={vi.fn()}
/>,
)
renderConfigurationView({ onSave })

fireEvent.click(screen.getByRole("button", { name: /AI Providers/ }))

Expand All @@ -113,16 +120,7 @@ describe("ConfigurationView", () => {
})

it("disables save for empty non-secret drafts so clear remains explicit", () => {
render(
<ConfigurationView
items={items.slice(0, 3)}
loading={{}}
canWrite
onRefresh={vi.fn()}
onSave={vi.fn()}
onClear={vi.fn()}
/>,
)
renderConfigurationView({ items: items.slice(0, 3) })

fireEvent.change(screen.getByLabelText("DocuSeal base URL value"), {
target: { value: "" },
Expand All @@ -144,33 +142,26 @@ describe("ConfigurationView", () => {
})

it("keeps onboarding email SMTP settings in the primary onboarding table", () => {
render(
<ConfigurationView
items={[
configItem({
key: "ONBOARDING_EMAIL_SMTP_USERNAME",
label: "Onboarding email SMTP username",
description: "SMTP username used to authenticate onboarding email sends.",
configured: true,
value: "onboarding@508.dev",
}),
configItem({
key: "ONBOARDING_EMAIL_SMTP_PASSWORD",
label: "Onboarding email SMTP password",
description: "SMTP password or app password used to send onboarding emails.",
is_secret: true,
configured: true,
masked_value: "sec...ret",
secret_encryption_configured: true,
}),
]}
loading={{}}
canWrite
onRefresh={vi.fn()}
onSave={vi.fn()}
onClear={vi.fn()}
/>,
)
renderConfigurationView({
items: [
configItem({
key: "ONBOARDING_EMAIL_SMTP_USERNAME",
label: "Onboarding email SMTP username",
description: "SMTP username used to authenticate onboarding email sends.",
configured: true,
value: "onboarding@508.dev",
}),
configItem({
key: "ONBOARDING_EMAIL_SMTP_PASSWORD",
label: "Onboarding email SMTP password",
description: "SMTP password or app password used to send onboarding emails.",
is_secret: true,
configured: true,
masked_value: "sec...ret",
secret_encryption_configured: true,
}),
],
})

const onboardingTable = screen.getByRole("table", {
name: "Onboarding configuration settings",
Expand All @@ -181,22 +172,68 @@ describe("ConfigurationView", () => {
})

it("focuses the requested configuration category", async () => {
render(
<ConfigurationView
items={items}
loading={{}}
canWrite
focusCategory="Onboarding"
focusNonce={1}
onRefresh={vi.fn()}
onSave={vi.fn()}
onClear={vi.fn()}
/>,
)
renderConfigurationView({
focusCategory: "Onboarding",
focusNonce: 1,
})

await waitFor(() => {
expect(screen.queryByRole("heading", { name: "AI Providers" })).not.toBeInTheDocument()
})
expect(screen.getByRole("heading", { name: "Onboarding" })).toBeVisible()
})

it("keeps the job channels tab selected from the hash and manages channels", async () => {
window.history.replaceState({}, "", "/dashboard/configuration#job-channels")
const onSaveJobChannel = vi.fn().mockResolvedValue(true)
const onDeleteJobChannel = vi.fn()
renderConfigurationView({
jobChannels: [
{
channel_id: "123",
channel_name: "gigs",
posting_type: "part_time",
requires_tag: true,
available_tags: [{ name: "React" }],
},
],
availableJobChannels: [
{
channel_id: "123",
channel_name: "gigs",
posting_type: "part_time",
registered: true,
},
{
channel_id: "456",
channel_name: "fulltime-roles",
posting_type: "unknown",
registered: false,
},
],
onSaveJobChannel,
onDeleteJobChannel,
})

expect(screen.getByRole("table", { name: "Registered job channels" })).toBeVisible()
expect(screen.getByRole("button", { name: /Job channels/ })).toHaveAttribute(
"aria-pressed",
"true",
)

fireEvent.change(screen.getByLabelText("Job channel to register"), {
target: { value: "456" },
})
fireEvent.change(screen.getByLabelText("New job channel posting type"), {
target: { value: "full_time" },
})
fireEvent.click(screen.getByRole("button", { name: "Register channel" }))

await waitFor(() => {
expect(onSaveJobChannel).toHaveBeenCalledWith("456", "full_time")
})

fireEvent.click(screen.getByRole("button", { name: "Deregister" }))
expect(onDeleteJobChannel).toHaveBeenCalledWith("123")
})
})
Loading