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
61 changes: 61 additions & 0 deletions apps/dashboard/src/api/__tests__/graphqlService.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,9 @@ import { graphqlClient } from '../graphqlClient';
import {
useApprovePARequest,
useDenyPARequest,
useProcessPARequest,
useConnectionStatus,
QUERY_KEYS,
} from '../graphqlService';

const mockRequest = vi.mocked(graphqlClient.request);
Expand DownExpand Up@@ -75,6 +77,65 @@ describe('useDenyPARequest', () => {
});
});

describe('useProcessPARequest', () => {
beforeEach(() => {
vi.clearAllMocks();
});

it('should call processPARequest mutation with correct id', async () => {
mockRequest.mockResolvedValueOnce({
processPARequest: { id: 'PA-001', status: 'ready' },
});

const { result } = renderHook(() => useProcessPARequest(), {
wrapper: createWrapper(),
});

result.current.mutate('PA-001');

await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(mockRequest).toHaveBeenCalledOnce();
const callArgs = mockRequest.mock.calls[0];
expect(callArgs[0]).toContain('processPARequest');
expect(callArgs[1]).toEqual({ id: 'PA-001' });
Comment on lines +98 to +100

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix TypeScript tuple access error causing pipeline failure.

The pipeline reports TS2493: Tuple type '[options: RequestOptions<object, unknown>]' of length '1' has no element at index '1'. The mock type inference doesn't recognize the second argument. Apply the same workaround used in useDenyPARequest test at line 74.

🐛 Proposed fix
- const callArgs = mockRequest.mock.calls[0];+ const callArgs = mockRequest.mock.calls[0] as unknown[];
expect(callArgs[0]).toContain('processPARequest');
expect(callArgs[1]).toEqual({ id: 'PA-001' });
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
constcallArgs=mockRequest.mock.calls[0];
expect(callArgs[0]).toContain('processPARequest');
expect(callArgs[1]).toEqual({id: 'PA-001'});
constcallArgs=mockRequest.mock.calls[0]asunknown[];
expect(callArgs[0]).toContain('processPARequest');
expect(callArgs[1]).toEqual({id: 'PA-001'});
🧰 Tools
🪛 GitHub Actions: CI

[error] 100-100: TS2493: Tuple type '[options: RequestOptions<object, unknown>]' of length '1' has no element at index '1'.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/dashboard/src/api/__tests__/graphqlService.test.ts` around lines 98 -
100, The TypeScript tuple error comes from accessing
mockRequest.mock.calls[0][1]; fix it by asserting the call tuple as a flexible
array like the other test: change the assignment of callArgs to cast the mock
call to an any[] (e.g., const callArgs = mockRequest.mock.calls[0] as unknown as
any[]), then keep the checks expect(callArgs[0]).toContain('processPARequest')
and expect(callArgs[1]).toEqual({ id: 'PA-001' }) so TypeScript no longer
complains when accessing index 1.

});

it('useProcessPARequest_InvalidatesCorrectQueries_OnSuccess', async () => {
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
});

// Seed the cache with data for each query key we expect to be invalidated
queryClient.setQueryData(QUERY_KEYS.paRequests, []);
queryClient.setQueryData(QUERY_KEYS.paRequest('PA-002'), { id: 'PA-002' });
queryClient.setQueryData(QUERY_KEYS.paStats, { ready: 0 });
queryClient.setQueryData(QUERY_KEYS.activity, []);

const wrapper = ({ children }: { children: React.ReactNode }) =>
React.createElement(QueryClientProvider, { client: queryClient }, children);

mockRequest.mockResolvedValueOnce({
processPARequest: { id: 'PA-002', status: 'ready' },
});

const { result } = renderHook(() => useProcessPARequest(), { wrapper });

// Spy on invalidateQueries
const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

result.current.mutate('PA-002');

await waitFor(() => expect(result.current.isSuccess).toBe(true));

// Verify all four query keys are invalidated
const invalidatedKeys = invalidateSpy.mock.calls.map(call => call[0]?.queryKey);
expect(invalidatedKeys).toContainEqual(QUERY_KEYS.paRequests);
expect(invalidatedKeys).toContainEqual(QUERY_KEYS.paRequest('PA-002'));
expect(invalidatedKeys).toContainEqual(QUERY_KEYS.paStats);
expect(invalidatedKeys).toContainEqual(QUERY_KEYS.activity);
});
});

describe('useConnectionStatus', () => {
beforeEach(() => {
vi.clearAllMocks();
Expand Down
110 changes: 110 additions & 0 deletions apps/dashboard/src/components/__tests__/EvidencePanel.test.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -117,4 +117,114 @@ describe('EvidencePanel', () => {
expect(screen.getByTestId('evidence-skeleton')).toBeInTheDocument();
});
});

describe('Intelligence-shaped evidence', () => {
it('EvidencePanel_WithIntelligenceEvidence_DisplaysCriterionIdAsLabel', () => {
const intelligenceEvidence: EvidenceItem[] = [
{
criterionId: 'conservative_therapy',
status: 'MET',
evidence: 'Patient completed 8 weeks of physical therapy and NSAID treatment',
source: 'Clinical Notes - 2026-01-15',
confidence: 0.95,
},
{
criterionId: 'medical_necessity',
status: 'MET',
evidence: 'MRI indicated for evaluation of persistent symptoms',
source: 'Order Entry - 2026-01-20',
confidence: 0.88,
},
];
render(<EvidencePanel evidence={intelligenceEvidence} />);

expect(screen.getByText('Conservative Therapy')).toBeInTheDocument();
expect(screen.getByText('Medical Necessity')).toBeInTheDocument();
});

it('EvidencePanel_WithUnclearStatus_ShowsDistinctBadge', () => {
const evidence: EvidenceItem[] = [
{
criterionId: 'diagnosis_present',
status: 'UNCLEAR',
evidence: 'Diagnosis code needs verification',
source: 'System',
confidence: 0.5,
},
];
render(<EvidencePanel evidence={evidence} />);

const badge = screen.getByText('Unclear');
expect(badge).toBeInTheDocument();
// UNCLEAR uses warning styling
expect(badge.className).toMatch(/bg-warning/);
expect(badge.className).toMatch(/text-warning/);
// Should NOT use MET (success) or NOT_MET (destructive) styling
expect(badge.className).not.toMatch(/bg-success/);
expect(badge.className).not.toMatch(/text-success/);
expect(badge.className).not.toMatch(/bg-destructive/);
expect(badge.className).not.toMatch(/text-destructive/);
});

it('EvidencePanel_WithAllThreeStatuses_ShowsCorrectSummaryBreakdown', () => {
const mixedEvidence: EvidenceItem[] = [
{
criterionId: 'conservative_therapy',
status: 'MET',
evidence: 'PT completed',
source: 'Notes',
confidence: 0.95,
},
{
criterionId: 'diagnosis_present',
status: 'UNCLEAR',
evidence: 'Needs verification',
source: 'System',
confidence: 0.5,
},
{
criterionId: 'imaging_prior',
status: 'NOT_MET',
evidence: 'No prior imaging found',
source: 'Radiology',
confidence: 0.9,
},
];
render(<EvidencePanel evidence={mixedEvidence} />);

expect(screen.getByText('3 criteria analyzed')).toBeInTheDocument();
});

it('EvidencePanel_WithLowConfidence_ShowsWarningColor', () => {
const evidence: EvidenceItem[] = [
{
criterionId: 'medical_necessity',
status: 'MET',
evidence: 'Some evidence found',
source: 'Notes',
confidence: 0.35,
},
];
render(<EvidencePanel evidence={evidence} />);

// 35% confidence should show destructive color (below 0.5 threshold)
expect(screen.getByText(/35%/)).toBeInTheDocument();
});

it('EvidencePanel_WithBorderlineConfidence_ShowsCorrectColor', () => {
const evidence: EvidenceItem[] = [
{
criterionId: 'conservative_therapy',
status: 'MET',
evidence: 'Partial documentation',
source: 'Notes',
confidence: 0.5,
},
];
render(<EvidencePanel evidence={evidence} />);

// 50% is at the boundary — should show warning color
expect(screen.getByText(/50%/)).toBeInTheDocument();
});
});
});
148 changes: 148 additions & 0 deletions apps/dashboard/src/components/__tests__/PARequestCard.test.tsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { PARequestCard, type PARequest } from '../PARequestCard';

// Mock @tanstack/react-router Link component
vi.mock('@tanstack/react-router', () => ({
Link: ({ children, to, params }: { children: React.ReactNode; to: string; params?: Record<string, string> }) => (
<a href={`${to}/${params?.transactionId ?? ''}`} data-testid="router-link">
{children}
</a>
),
}));

function createMockPARequest(overrides: Partial<PARequest> = {}): PARequest {
return {
id: 'PA-001',
patientName: 'Jane Smith',
patientId: 'MRN-12345',
procedureCode: '72148',
procedureName: 'MRI Lumbar Spine',
payer: 'Blue Cross Blue Shield',
currentStep: 'process',
createdAt: new Date().toISOString(),
encounterId: 'ENC-001',
...overrides,
};
}

describe('PARequestCard', () => {
describe('rendering', () => {
it('PARequestCard_WithValidRequest_DisplaysPatientInfo', () => {
const request = createMockPARequest();
render(<PARequestCard request={request} />);

expect(screen.getByText('Jane Smith')).toBeInTheDocument();
expect(screen.getByText(/MRN-12345/)).toBeInTheDocument();
});

it('PARequestCard_WithValidRequest_DisplaysProcedure', () => {
const request = createMockPARequest();
render(<PARequestCard request={request} />);

expect(screen.getByText('72148')).toBeInTheDocument();
expect(screen.getByText('MRI Lumbar Spine')).toBeInTheDocument();
});

it('PARequestCard_WithValidRequest_DisplaysPayer', () => {
const request = createMockPARequest();
render(<PARequestCard request={request} />);

expect(screen.getByText('Blue Cross Blue Shield')).toBeInTheDocument();
});
});

describe('confidence display', () => {
it('PARequestCard_WithHighConfidence_DisplaysPercentage', () => {
const request = createMockPARequest({ confidenceScore: 0.92 });
render(<PARequestCard request={request} />);

expect(screen.getByText(/92%/)).toBeInTheDocument();
});

it('PARequestCard_WithMediumConfidence_DisplaysPercentage', () => {
const request = createMockPARequest({ confidenceScore: 0.65 });
render(<PARequestCard request={request} />);

expect(screen.getByText(/65%/)).toBeInTheDocument();
});

it('PARequestCard_WithLowConfidence_DisplaysReviewBadge', () => {
const request = createMockPARequest({ confidenceScore: 0.42 });
render(<PARequestCard request={request} />);

// Low confidence (< 0.5) shows "Review" text in a badge (data-slot="badge")
const badges = screen.getAllByText('Review');
const confidenceBadge = badges.find(el => el.getAttribute('data-slot') === 'badge');
expect(confidenceBadge).toBeDefined();
});

it('PARequestCard_WithUndefinedConfidence_ShowsNoConfidenceBadge', () => {
const request = createMockPARequest({ confidenceScore: undefined });
render(<PARequestCard request={request} />);

// No confidence percentage badge should render
expect(screen.queryByText(/\d+%/)).not.toBeInTheDocument();
// The only "Review" text should be from the WorkflowProgress step, not a badge
const reviewElements = screen.queryAllByText('Review');
const confidenceBadge = reviewElements.find(el => el.getAttribute('data-slot') === 'badge');
expect(confidenceBadge).toBeUndefined();
});

it('PARequestCard_WithRealConfidenceRange_DisplaysCorrectly', () => {
// Test with various realistic confidence values from Intelligence
const request = createMockPARequest({ confidenceScore: 0.78 });
render(<PARequestCard request={request} />);

expect(screen.getByText(/78%/)).toBeInTheDocument();
});
});

describe('workflow states', () => {
it('PARequestCard_InProcessingState_ShowsProcessingIndicator', () => {
const request = createMockPARequest({ currentStep: 'process' });
render(<PARequestCard request={request} />);

expect(screen.getByText('Processing...')).toBeInTheDocument();
});

it('PARequestCard_InDeliverState_ShowsReviewButton', () => {
const request = createMockPARequest({ currentStep: 'deliver' });
render(<PARequestCard request={request} />);

expect(screen.getByText('Review & Confirm')).toBeInTheDocument();
});

it('PARequestCard_InReviewCompletedState_ShowsSubmittedMessage', () => {
const request = createMockPARequest({
currentStep: 'review',
stepStatuses: { review: 'completed' },
});
render(<PARequestCard request={request} />);

expect(screen.getByText('Submitted to athenahealth')).toBeInTheDocument();
});
});

describe('attention flag', () => {
it('PARequestCard_RequiresAttention_HasWarningRing', () => {
const request = createMockPARequest({ requiresAttention: true });
const { container } = render(<PARequestCard request={request} />);

const card = container.firstChild as HTMLElement;
expect(card.className).toMatch(/ring-warning/);
});
});

describe('callbacks', () => {
it('PARequestCard_OnReviewClick_CallsCallback', () => {
const onReview = vi.fn();
const request = createMockPARequest({ currentStep: 'deliver' });
render(<PARequestCard request={request} onReview={onReview} />);

fireEvent.click(screen.getByText('Review & Confirm'));

expect(onReview).toHaveBeenCalledWith('PA-001');
});
});
});
Loading
Loading