// @vitest-environment happy-dom // Pure prop-driven component, but match the act() convention used by // the other ClaimDrawer test files (ClaimDrawerHeader / // ClaimDrawerSkeleton / ClaimDrawerError). (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; import React, { act } from "react"; import { createRoot, type Root } from "react-dom/client"; import { describe, expect, it } from "vitest"; import { ValidationPanel } from "./ValidationPanel"; import type { ClaimDetailValidation, ClaimDetailValidationIssue } from "@/types"; function renderIntoContainer(element: React.ReactElement): { container: HTMLDivElement; unmount: () => void; } { const container = document.createElement("div"); document.body.appendChild(container); const root: Root = createRoot(container); act(() => { root.render(element); }); return { container, unmount: () => { act(() => root.unmount()); container.remove(); }, }; } function makeIssue( overrides: Partial = {} ): ClaimDetailValidationIssue { return { rule: "R050_diagnosis_present", message: "Primary diagnosis is required", severity: "error", ...overrides, }; } function makeValidation( overrides: Partial = {} ): ClaimDetailValidation { return { passed: true, errors: [], warnings: [], ...overrides, }; } describe("ValidationPanel", () => { it("test_all_passed_state_renders_checkmark_and_message", () => { const { container, unmount } = renderIntoContainer( ); // The pass-state root has data-testid="validation-passed". const passedRoot = container.querySelector('[data-testid="validation-passed"]'); expect(passedRoot).not.toBeNull(); // The success copy is in the rendered text. const text = (container.textContent ?? "").toLowerCase(); expect(text).toContain("all checks passed"); // The checkmark icon (lucide-react `CheckCircle2`) is an inline SVG. const svg = passedRoot?.querySelector("svg"); expect(svg).not.toBeNull(); // Neither sub-section is rendered when nothing is wrong. expect(container.querySelector('[data-testid="validation-errors"]')).toBeNull(); expect(container.querySelector('[data-testid="validation-warnings"]')).toBeNull(); unmount(); }); it("test_errors_only_renders_errors_subsection", () => { const { container, unmount } = renderIntoContainer( ); const errorsBlock = container.querySelector('[data-testid="validation-errors"]'); expect(errorsBlock).not.toBeNull(); // Red left-border token applied to the inner block — sniff the className. const cls = errorsBlock?.className ?? ""; expect(cls).toContain("border-[color:var(--m-error)]"); expect(cls).toContain("border-l-2"); // Rule code + message visible in the rendered text. const text = container.textContent ?? ""; expect(text).toContain("R050_diagnosis_present"); expect(text).toContain("Primary diagnosis is required"); // No warnings sub-section when only errors are present. expect(container.querySelector('[data-testid="validation-warnings"]')).toBeNull(); // And no all-passed pill. expect(container.querySelector('[data-testid="validation-passed"]')).toBeNull(); unmount(); }); it("test_warnings_only_renders_warnings_subsection", () => { const { container, unmount } = renderIntoContainer( ); const warningsBlock = container.querySelector('[data-testid="validation-warnings"]'); expect(warningsBlock).not.toBeNull(); // Amber left-border token applied to the inner block. const cls = warningsBlock?.className ?? ""; expect(cls).toContain("border-[color:var(--m-warning)]"); expect(cls).toContain("border-l-2"); const text = container.textContent ?? ""; expect(text).toContain("R200_units_recommended"); expect(text).toContain("Units reported as 0"); // No errors sub-section when only warnings are present. expect(container.querySelector('[data-testid="validation-errors"]')).toBeNull(); unmount(); }); it("test_both_errors_and_warnings_renders_both", () => { const { container, unmount } = renderIntoContainer( ); expect(container.querySelector('[data-testid="validation-errors"]')).not.toBeNull(); expect(container.querySelector('[data-testid="validation-warnings"]')).not.toBeNull(); // Errors sub-section appears before warnings sub-section (blocking // issues first, then advisories). const errorsEl = container.querySelector('[data-testid="validation-errors"]'); const warningsEl = container.querySelector('[data-testid="validation-warnings"]'); expect(errorsEl).not.toBeNull(); expect(warningsEl).not.toBeNull(); expect( errorsEl!.compareDocumentPosition(warningsEl!) & Node.DOCUMENT_POSITION_FOLLOWING ).toBeTruthy(); unmount(); }); it("test_errors_are_grouped_by_rule_with_count", () => { const { container, unmount } = renderIntoContainer( ); const errorsBlock = container.querySelector('[data-testid="validation-errors"]'); expect(errorsBlock).not.toBeNull(); const text = errorsBlock?.textContent ?? ""; // Header should show the rule code with the grouped count "(2)". expect(text).toContain("R050_diagnosis_present"); expect(text).toContain("(2)"); // Both messages appear underneath the group header. expect(text).toContain("Primary diagnosis is required"); expect(text).toContain("Diagnosis qualifier is required"); unmount(); }); it("test_passed_with_warnings_still_shows_warnings", () => { // The "all checks passed" pill is reserved for passed && no-warnings; // passed=true with warnings present should still surface the warnings // sub-section so the user notices the advisories. const { container, unmount } = renderIntoContainer( ); expect(container.querySelector('[data-testid="validation-warnings"]')).not.toBeNull(); expect(container.querySelector('[data-testid="validation-passed"]')).toBeNull(); unmount(); }); });