Skip to content
Open
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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { describe, it, expect, vi } from "vitest"
import { render, screen, fireEvent } from "@testing-library/react"
import "@testing-library/jest-dom/vitest"
import ErrorMessageItem from "./item"

// ─── Module mocks ─────────────────────────────────────────────────────────────

vi.mock("lib/components/pretty_date", () => ({
PrettyDate: ({ date }: any) => <span data-testid="pretty-date">{date}</span>,
}))

// ─── Fixtures ─────────────────────────────────────────────────────────────────

const mockErrorMessage = {
id: "em-1",
project_id: "proj-1",
resource_type: "share",
resource_id: "res-1",
detail_id: "detail-1",
action_id: "action-1",
request_id: "req-1",
created_at: "2024-01-01T00:00:00Z",
expires_at: "2025-01-01T00:00:00Z",
message_level: "ERROR",
user_message: "Something went wrong",
}

// ─── Tests ────────────────────────────────────────────────────────────────────

describe("ErrorMessageItem", () => {
// ── Row rendering ──────────────────────────────────────────────────────────

describe("Row rendering", () => {
it("renders message_level in first column", () => {
render(
<table>
<tbody>
<ErrorMessageItem errorMessage={mockErrorMessage} />
</tbody>
</table>
)
expect(screen.getByText("ERROR")).toBeInTheDocument()
})

it("renders user_message in second column", () => {
render(
<table>
<tbody>
<ErrorMessageItem errorMessage={mockErrorMessage} />
</tbody>
</table>
)
expect(screen.getByText("Something went wrong")).toBeInTheDocument()
})

it("renders PrettyDate with created_at in third column", () => {
render(
<table>
<tbody>
<ErrorMessageItem errorMessage={mockErrorMessage} />
</tbody>
</table>
)
expect(screen.getByTestId("pretty-date")).toHaveTextContent("2024-01-01T00:00:00Z")
})

it("renders a toggle icon (caret-right) when details are hidden", () => {
render(
<table>
<tbody>
<ErrorMessageItem errorMessage={mockErrorMessage} />
</tbody>
</table>
)
expect(document.querySelector(".fa-caret-right")).toBeInTheDocument()
expect(document.querySelector(".fa-caret-down")).not.toBeInTheDocument()
})
})

// ── Toggle details ─────────────────────────────────────────────────────────

describe("Toggle details", () => {
it("shows details row when toggle is clicked", () => {
render(
<table>
<tbody>
<ErrorMessageItem errorMessage={mockErrorMessage} />
</tbody>
</table>
)
const toggle = document.querySelector("a")!
fireEvent.click(toggle)
expect(document.querySelector("tr.details")).toBeInTheDocument()
})

it("switches icon to caret-down when expanded", () => {
render(
<table>
<tbody>
<ErrorMessageItem errorMessage={mockErrorMessage} />
</tbody>
</table>
)
fireEvent.click(document.querySelector("a")!)
expect(document.querySelector(".fa-caret-down")).toBeInTheDocument()
expect(document.querySelector(".fa-caret-right")).not.toBeInTheDocument()
})

it("hides details row when toggled twice", () => {
render(
<table>
<tbody>
<ErrorMessageItem errorMessage={mockErrorMessage} />
</tbody>
</table>
)
const toggle = document.querySelector("a")!
fireEvent.click(toggle)
fireEvent.click(toggle)
expect(document.querySelector("tr.details")).not.toBeInTheDocument()
})

it("renders all detail fields in the details table", () => {
render(
<table>
<tbody>
<ErrorMessageItem errorMessage={mockErrorMessage} />
</tbody>
</table>
)
fireEvent.click(document.querySelector("a")!)

const expectedFields = [
"project_id",
"id",
"resource_type",
"resource_id",
"detail_id",
"action_id",
"request_id",
"created_at",
"expires_at",
]
for (const field of expectedFields) {
expect(screen.getByText(field)).toBeInTheDocument()
}
})

it("renders detail values in the details table", () => {
render(
<table>
<tbody>
<ErrorMessageItem errorMessage={mockErrorMessage} />
</tbody>
</table>
)
fireEvent.click(document.querySelector("a")!)
expect(screen.getByText("proj-1")).toBeInTheDocument()
expect(screen.getByText("res-1")).toBeInTheDocument()
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { useState } from "react"
import { PrettyDate } from "lib/components/pretty_date"

// ─── Types ────────────────────────────────────────────────────────────────────

interface ErrorMessage {
id?: string
project_id?: string
resource_type?: string
resource_id?: string
detail_id?: string
action_id?: string
request_id?: string
created_at?: string
expires_at?: string
message_level?: string
user_message?: string
}

interface ErrorMessageItemProps {
errorMessage: ErrorMessage
}

// ─── Component ────────────────────────────────────────────────────────────────

const DETAIL_FIELDS: Array<keyof ErrorMessage> = [
"project_id",
"id",
"resource_type",
"resource_id",
"detail_id",
"action_id",
"request_id",
"created_at",
"expires_at",
]

const ErrorMessageItem: React.FC<ErrorMessageItemProps> = ({ errorMessage }) => {
const [showDetails, setShowDetails] = useState(false)

const toggleDetails = () => setShowDetails((prev) => !prev)

return (
<>
<tr>
<td>
<a
onClick={(e) => {
e.preventDefault()
toggleDetails()
}}
>
{showDetails ? (
<i className="fa fa-fw fa-caret-down" />
) : (
<i className="fa fa-fw fa-caret-right" />
)}
</a>
{errorMessage.message_level}
</td>
<td>{errorMessage.user_message}</td>
<td>
<PrettyDate date={errorMessage.created_at} />
</td>
</tr>
{showDetails && (
<tr className="details">
<td colSpan={3}>
<table className="table no-borders">
<tbody>
{DETAIL_FIELDS.map((key) => (
<tr key={key}>
<th>{key}</th>
<td>{errorMessage[key]}</td>
</tr>
))}
</tbody>
</table>
</td>
</tr>
)}
</>
)
}

export default ErrorMessageItem
Loading
Loading