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
45 changes: 45 additions & 0 deletions client/src/pages/AdminErrors.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { describe, it, expect } from "vitest";
import type { ErrorLog } from "@shared/schema";

/**
* Regression test for issue #296: the AdminErrors page used snake_case
* property names (error_type, stack_trace, etc.) but Drizzle ORM returns
* camelCase. This test ensures the schema fields that AdminErrors.tsx
* relies on exist with the expected camelCase names.
*/
describe("AdminErrors ErrorLogEntry alignment with schema", () => {
// Build a minimal ErrorLog to verify the camelCase field names exist
// at the type level. If the schema ever renames these fields, this
// test will fail at compile time (npm run check) and at runtime.
const schemaKeys: (keyof ErrorLog)[] = [
"id",
"timestamp",
"level",
"source",
"errorType",
"message",
"stackTrace",
"context",
"resolved",
"firstOccurrence",
"occurrenceCount",
];

it("schema exports camelCase field names used by AdminErrors", () => {
// These are the fields AdminErrors.tsx references — if the schema
// renames them, this array literal will cause a TS error and the
// runtime check below will also catch it.
expect(schemaKeys).toContain("errorType");
expect(schemaKeys).toContain("stackTrace");
expect(schemaKeys).toContain("firstOccurrence");
expect(schemaKeys).toContain("occurrenceCount");
});

it("schema does NOT export snake_case names", () => {
const keys = schemaKeys as string[];
expect(keys).not.toContain("error_type");
expect(keys).not.toContain("stack_trace");
expect(keys).not.toContain("first_occurrence");
expect(keys).not.toContain("occurrence_count");
});
Comment on lines +28 to +44
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider expanding test coverage for functional edge cases.

While this schema alignment test is effective for its purpose, the AdminErrors component itself lacks test coverage for:

  • Loading state rendering
  • Error state rendering
  • Empty logs array handling
  • Batch selection/deletion flows

This is outside the scope of the current fix, but worth tracking separately.

Do you want me to open an issue to track adding component-level tests for AdminErrors.tsx?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/src/pages/AdminErrors.test.ts` around lines 28 - 44, Add a tracking
issue to cover missing component-level tests for AdminErrors.tsx: create an
issue titled like "Add AdminErrors component tests" and include a clear
checklist of test cases to implement (loading state rendering, error state
rendering, empty logs array handling, and batch selection/deletion flows),
reference the existing AdminErrors.tsx component and AdminErrors.test.ts for
schema checks, and note recommended tools (React Testing Library + Jest) and any
props/APIs to mock (data fetching hook or props used by AdminErrors) so future
PRs can implement each scenario.

});
36 changes: 14 additions & 22 deletions client/src/pages/AdminErrors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,15 @@ import { Checkbox } from "@/components/ui/checkbox";
import { XCircle, AlertTriangle, Info, ArrowLeft, RefreshCw, Globe, Mail, Users, Trash2, Loader2, X } from "lucide-react";
import { Link } from "wouter";
import { ERROR_LOG_SOURCES } from "@shared/routes";
import type { ErrorLog } from "@shared/schema";
import { queryClient } from "@/lib/queryClient";
import { useToast } from "@/hooks/use-toast";
import { ToastAction } from "@/components/ui/toast";

interface ErrorLogEntry {
id: number;
timestamp: string;
level: string;
source: string;
error_type: string | null;
message: string;
stack_trace: string | null;
context: any;
resolved: boolean;
first_occurrence: string | null;
occurrence_count: number;
}
/** JSON-serialized ErrorLog — Date fields become strings over the wire. */
type ErrorLogEntry = {
[K in keyof ErrorLog]: ErrorLog[K] extends Date ? string : ErrorLog[K] extends Date | null ? string | null : ErrorLog[K];
};

interface BrowserlessUsageData {
systemUsage: number;
Expand Down Expand Up @@ -706,17 +698,17 @@ export default function AdminErrors() {
<Badge variant="outline" data-testid={`badge-source-${log.id}`}>
{log.source}
</Badge>
{log.error_type && (
<span className="text-xs text-muted-foreground">{log.error_type}</span>
{log.errorType && (
<span className="text-xs text-muted-foreground">{log.errorType}</span>
)}
{log.occurrence_count > 1 && (
{log.occurrenceCount > 1 && (
<Badge variant="secondary" className="text-[10px] px-1.5 py-0" data-testid={`badge-count-${log.id}`}>
{log.occurrence_count}x
{log.occurrenceCount}x
</Badge>
)}
<span className="text-xs text-muted-foreground ml-auto shrink-0">
{log.occurrence_count > 1 && log.first_occurrence
? `${formatTimestamp(log.first_occurrence)} \u2014 ${formatTimestamp(log.timestamp)}`
{log.occurrenceCount > 1 && log.firstOccurrence
? `${formatTimestamp(log.firstOccurrence)} \u2014 ${formatTimestamp(log.timestamp)}`
: formatTimestamp(log.timestamp)}
</span>
<Button
Expand All @@ -739,15 +731,15 @@ export default function AdminErrors() {
</p>
{isExpanded && (
<div className="mt-3 space-y-2" onClick={(e) => e.stopPropagation()}>
{log.stack_trace && (
{log.stackTrace && (
<div>
<p className="text-xs font-medium text-muted-foreground mb-1">Stack Trace</p>
<pre className="text-xs bg-secondary/50 p-3 rounded-md overflow-x-auto max-h-48 overflow-y-auto select-text">
{log.stack_trace}
{log.stackTrace}
</pre>
</div>
)}
{log.context && (
{log.context != null && (
<div>
<p className="text-xs font-medium text-muted-foreground mb-1">Context</p>
<pre className="text-xs bg-secondary/50 p-3 rounded-md overflow-x-auto max-h-32 overflow-y-auto select-text">
Expand Down
Loading