|
1 | 1 | import { fireEvent, render, screen, waitFor } from '@testing-library/react'; |
2 | 2 | import { Activity } from 'lucide-react'; |
3 | | -import { describe, expect, it, vi } from 'vitest'; |
| 3 | +import { beforeEach, describe, expect, it, vi } from 'vitest'; |
4 | 4 |
|
5 | | -import { HealthDetailSections } from '../healthDetailSections'; |
| 5 | +import { HealthDetailSections, HealthEditModals } from '../healthDetailSections'; |
6 | 6 |
|
7 | 7 | vi.mock('react-hot-toast', () => ({ |
8 | 8 | default: { |
@@ -30,6 +30,10 @@ vi.mock('@/services/endpoints/metrics', () => ({ |
30 | 30 | })); |
31 | 31 |
|
32 | 32 | describe('HealthDetailSections', () => { |
| 33 | + beforeEach(() => { |
| 34 | + document.body.style.overflow = ''; |
| 35 | + }); |
| 36 | + |
33 | 37 | it('opens the shared quick log modal with the clicked pending metric preselected', async () => { |
34 | 38 | render( |
35 | 39 | <HealthDetailSections |
@@ -65,4 +69,102 @@ describe('HealthDetailSections', () => { |
65 | 69 | expect(screen.getByLabelText(/Value \*/i)).toHaveFocus(); |
66 | 70 | }); |
67 | 71 | }); |
| 72 | + |
| 73 | + it('renders the shared frosted shell for the edit target modal', () => { |
| 74 | + render( |
| 75 | + <HealthEditModals |
| 76 | + editingMetric={{ |
| 77 | + id: 'metric-sleep', |
| 78 | + code: 'sleep_hours', |
| 79 | + name: 'Sleep Hours', |
| 80 | + unit: 'hours', |
| 81 | + targetDirection: 'AtOrAbove', |
| 82 | + } as never} |
| 83 | + setEditingMetric={vi.fn()} |
| 84 | + editTargetValue="8" |
| 85 | + setEditTargetValue={vi.fn()} |
| 86 | + editTargetDirection="AtOrAbove" |
| 87 | + setEditTargetDirection={vi.fn()} |
| 88 | + handleSaveTarget={vi.fn().mockResolvedValue(undefined)} |
| 89 | + editingModel={null} |
| 90 | + setEditingModel={vi.fn()} |
| 91 | + editModelParams={{}} |
| 92 | + setEditModelParams={vi.fn()} |
| 93 | + handleSaveModel={vi.fn().mockResolvedValue(undefined)} |
| 94 | + />, |
| 95 | + ); |
| 96 | + |
| 97 | + const dialog = screen.getByRole('dialog', { name: /Edit target for Sleep Hours/i }); |
| 98 | + |
| 99 | + expect(screen.getByRole('heading', { name: /Edit Target: Sleep Hours/i })).toBeInTheDocument(); |
| 100 | + expect(dialog).toHaveAttribute('data-app-modal', 'true'); |
| 101 | + expect(dialog).toHaveClass('auth-shell-backdrop'); |
| 102 | + expect(screen.getByText(/Update the target shown across health tracking and longevity guidance\./i)).toBeInTheDocument(); |
| 103 | + expect(screen.getByRole('button', { name: /Close edit target modal for Sleep Hours/i })).toBeInTheDocument(); |
| 104 | + }); |
| 105 | + |
| 106 | + it('closes the edit target modal on backdrop interaction', () => { |
| 107 | + const setEditingMetric = vi.fn(); |
| 108 | + |
| 109 | + render( |
| 110 | + <HealthEditModals |
| 111 | + editingMetric={{ |
| 112 | + id: 'metric-sleep', |
| 113 | + code: 'sleep_hours', |
| 114 | + name: 'Sleep Hours', |
| 115 | + unit: 'hours', |
| 116 | + targetDirection: 'AtOrAbove', |
| 117 | + } as never} |
| 118 | + setEditingMetric={setEditingMetric} |
| 119 | + editTargetValue="8" |
| 120 | + setEditTargetValue={vi.fn()} |
| 121 | + editTargetDirection="AtOrAbove" |
| 122 | + setEditTargetDirection={vi.fn()} |
| 123 | + handleSaveTarget={vi.fn().mockResolvedValue(undefined)} |
| 124 | + editingModel={null} |
| 125 | + setEditingModel={vi.fn()} |
| 126 | + editModelParams={{}} |
| 127 | + setEditModelParams={vi.fn()} |
| 128 | + handleSaveModel={vi.fn().mockResolvedValue(undefined)} |
| 129 | + />, |
| 130 | + ); |
| 131 | + |
| 132 | + const backdrop = document.querySelector('[data-app-modal="true"]'); |
| 133 | + expect(backdrop).not.toBeNull(); |
| 134 | + |
| 135 | + fireEvent.mouseDown(backdrop!); |
| 136 | + fireEvent.mouseUp(backdrop!); |
| 137 | + |
| 138 | + expect(setEditingMetric).toHaveBeenCalledWith(null); |
| 139 | + }); |
| 140 | + |
| 141 | + it('closes the edit rule modal on escape', () => { |
| 142 | + const setEditingModel = vi.fn(); |
| 143 | + |
| 144 | + render( |
| 145 | + <HealthEditModals |
| 146 | + editingMetric={null} |
| 147 | + setEditingMetric={vi.fn()} |
| 148 | + editTargetValue="" |
| 149 | + setEditTargetValue={vi.fn()} |
| 150 | + editTargetDirection="AtOrAbove" |
| 151 | + setEditTargetDirection={vi.fn()} |
| 152 | + handleSaveTarget={vi.fn().mockResolvedValue(undefined)} |
| 153 | + editingModel={{ |
| 154 | + id: 'rule-resting-heart-rate', |
| 155 | + code: 'resting_heart_rate', |
| 156 | + name: 'Resting Heart Rate', |
| 157 | + modelType: 'threshold', |
| 158 | + } as never} |
| 159 | + setEditingModel={setEditingModel} |
| 160 | + editModelParams={{ threshold: 60, direction: 'below', maxYearsAdded: 2 }} |
| 161 | + setEditModelParams={vi.fn()} |
| 162 | + handleSaveModel={vi.fn().mockResolvedValue(undefined)} |
| 163 | + />, |
| 164 | + ); |
| 165 | + |
| 166 | + fireEvent.keyDown(window, { key: 'Escape' }); |
| 167 | + |
| 168 | + expect(setEditingModel).toHaveBeenCalledWith(null); |
| 169 | + }); |
68 | 170 | }); |
0 commit comments