diff --git a/Pookie.Tests/UnitTests/TargetChildID/TARGETCHILDID.md b/Pookie.Tests/UnitTests/TargetChildID/TARGETCHILDID.md new file mode 100644 index 0000000..d097722 --- /dev/null +++ b/Pookie.Tests/UnitTests/TargetChildID/TARGETCHILDID.md @@ -0,0 +1,1294 @@ +# Target Child ID Tests Documentation + +## Overview + +The **TargetChildID** test suite contains automated end-to-end tests for the Target Child Information and Birth Outcomes (TCID) form. This multi-tab form collects comprehensive information about the target child (newborn) including birth outcomes, health insurance, medical provider information, PHQ-9 depression screening, and MIECHV-specific data. + +## What is Target Child ID (TCID)? + +**Target Child ID** is a form completed upon the birth of the target child in a home visiting case. The target child is the newborn that the case was opened for. + +**Purpose**: +- Document birth outcomes and vital statistics +- Track health insurance and medical provider information +- Conduct PHQ-9 depression screening for caregivers +- Collect MIECHV-required data elements +- Record additional optional items (parity, delivery type, breastfeeding) + +**Workflow**: +``` +Navigate to Forms Tab + ↓ +Click "Target Child Information and Birth Outcomes" link + ↓ +View TCID Grid (existing entries) + ↓ +Click "New TCID" OR Edit existing entry + ↓ +Complete Multi-Tab Form: + ├─ Target Child (birth info, gestational age, birth weight) + ├─ Health Insurance (Medicaid, insurance types, medical provider/facility) + ├─ Additional Items (parity, delivery type, breastfeeding) + ├─ PHQ-9 (depression screening with scoring) + └─ MIECHV (medical care source, prenatal care) + ↓ +Submit Form + ↓ +Success Toast Displayed +``` + +--- + +## Test Files Overview + +| File | Tests | Primary Focus | +|------|-------|---------------| +| **TargetChildIDTests.cs** | 6 | Main form tests: navigation, validation, birth weight, health insurance, medical provider/facility modals | +| **TargetChildIDAdditionalItemsTests.cs** | 1 | Additional Items tab: parity, delivery type, breastfeeding, tooltips | +| **TargetChildIDPhq9Tests.cs** | 1 | PHQ-9 tab: date validation, participant selection, scoring, worker assignment | +| **TargetChildIDMiechvTests.cs** | 1 | MIECHV tab: medical care source with "Other specify" toggle | + +**Total Tests**: 9 (all parameterized with PC1 IDs) + +**Test Priorities**: Tests are ordered 1-9 using `[TestPriority]` attribute for sequential execution. + +**Partial Class Pattern**: All test files use `partial class TargetChildIDTests` to share common helper methods and constants. + +--- + +## Form Structure + +### 5 Tabs + +| Tab | ID | Purpose | +|-----|----|---------| +| **Target Child** | `#TargetChild` | Birth outcomes (name, DOB, term, gestational age, birth weight) | +| **Health Insurance** | `#HealthInsurance` | Medicaid, insurance types, medical provider/facility | +| **Additional Items** | `#OptionalItems` | Optional fields (parity, delivery type, breastfeeding) | +| **PHQ-9** | `#PHQ9` | Depression screening for caregiver with scoring | +| **MIECHV** | `#MIECHV` | MIECHV-required fields (medical care source, prenatal care) | + +--- + +## Test Execution Order + +Tests run in priority order (1-9): + +1. **Priority 1**: Navigate to Target Child Information form +2. **Priority 2**: New TCID button displays info alert +3. **Priority 3**: Validation and submission (progressive validation) +4. **Priority 4**: Existing TCID prenatal care date validation +5. **Priority 5**: Existing TCID birth weight validation +6. **Priority 6**: Health insurance options respect Medicaid selection + medical provider/facility modals +7. **Priority 7**: Additional Items parity and delivery flow +8. **Priority 8**: PHQ-9 date validation and scoring +9. **Priority 9**: MIECHV medical care source "Other specify" toggle + +--- + +## Detailed Test Breakdown + +### Test 1: NavigateToTargetChildInformationForm (Priority 1) + +**File**: TargetChildIDTests.cs + +**Purpose**: Smoke test to verify navigation to Target Child Information page. + +**Test Flow**: +1. Sign in as DataEntry user +2. Navigate to Forms tab for target PC1 ID +3. Find Target Child Information link: `a.moreInfo[data-formtype='TCIBO']` +4. Click link +5. Wait for page load + +**Assertions**: +- URL contains `TCIDs.aspx` +- URL contains `pc1id={pc1Id}` + +--- + +### Test 2: NewTcidButtonDisplaysInfoAlert (Priority 2) + +**File**: TargetChildIDTests.cs + +**Purpose**: Verify "New TCID" button opens form with info alert. + +**Test Flow**: +1. Navigate to Target Child page +2. Click "New TCID" button +3. Wait for info alert: `.alert.alert-info` + +**Info Alert Expected Text**: +- "Complete this form upon the birth of the target child" +- "target child is the newborn" + +**Assertions**: +- Info alert is displayed +- Validation summary container exists on page + +--- + +### Test 3: ValidationAndSubmission (Priority 3) + +**File**: TargetChildIDTests.cs + +**Purpose**: Comprehensive progressive validation test covering multiple tabs. + +**Test Flow**: + +#### Part 1: Submit Empty Form +1. Open New TCID form +2. Switch to MIECHV tab +3. Click Submit +4. Capture validation errors + +**Expected Validations**: +- "Term of Birth is required." +- "First Name is required!" (appears 2 times - Target Child and MIECHV) +- "Parity is required." + +#### Part 2: Fill Birth Term +5. Switch to Target Child tab +6. Select random birth term +7. Switch to MIECHV tab +8. Click Submit + +**Expected**: "Term of Birth is required." cleared + +#### Part 3: Fill Target Child First Name +9. Switch to Target Child tab +10. Enter first name: "Gwen" +11. Switch to MIECHV tab +12. Click Submit + +**Expected**: One "First Name is required!" cleared (1 remaining) + +#### Part 4: Fill Target Child Last Name +13. Switch to Target Child tab +14. Enter last name: "Venom" +15. Switch to MIECHV tab +16. Click Submit + +**Expected**: All "First Name is required!" cleared + +#### Part 5: Fill Parity +17. Switch to Additional Items tab +18. Select random parity +19. Switch to MIECHV tab +20. Click Submit + +**Expected**: +- "Parity is required." cleared +- "Gestational Age is required and must be a valid whole number!" appears + +#### Part 6: Test Invalid Gestational Age +21. Switch to Target Child tab +22. Enter gestational age: "-1" +23. Switch to MIECHV tab +24. Click Submit + +**Expected**: "Gestation Age must be between 0 and 40 weeks!" + +#### Part 7: Fill Valid Gestational Age +25. Switch to Target Child tab +26. Enter gestational age: "38" + +**Purpose**: Demonstrates progressive validation where each field clears its own validation message. + +--- + +### Test 4: ExistingTcidPrenatalCareValidation (Priority 4) + +**File**: TargetChildIDTests.cs + +**Purpose**: Test prenatal care date validation - must be before target child's DOB. + +**Test Flow**: + +#### Part 1: Enter Invalid Date (Future) +1. Navigate to Target Child page +2. Open existing TCID entry +3. Switch to Target Child tab +4. Set prenatal care date: "12/01/25" (future date) +5. Switch to MIECHV tab +6. Click Submit + +**Expected Validation**: "Date Began Receiving Prenatal Care can not start after the birth of the child" + +#### Part 2: Enter Valid Date +7. Switch to Target Child tab +8. Set prenatal care date: "11/01/25" (past date) +9. Switch to MIECHV tab +10. Click Submit (no validation expected) + +**Expected**: +- Success toast displayed +- Toast contains: "Form Saved", "Target Child Identification", PC1 ID + +--- + +### Test 5: ExistingTcidBirthWeightValidation (Priority 5) + +**File**: TargetChildIDTests.cs + +**Purpose**: Test birth weight validation - pounds < 17, ounces < 16. + +**Test Flow**: + +#### Part 1: Test Invalid Weights +1. Navigate to Target Child page +2. Open existing TCID entry + +**Test 3 Invalid Combinations**: + +| Pounds | Ounces | Expected Validation | +|--------|--------|---------------------| +| -1 | -2 | "Birth weight pounds must be less than 17" AND "Birth weight ounces must be less than 16" | +| 20 | 20 | Same as above | +| 17 | 16 | Same as above | + +#### Part 2: Enter Valid Weight +3. Generate random valid weight: lbs 0-16, oz 0-15 +4. Enter birth weight +5. Click Submit + +**Expected**: +- Success toast displayed +- Toast contains: "Form Saved", "Target Child Identification", PC1 ID + +--- + +### Test 6: HealthInsuranceOptionsRespectMedicaidSelection (Priority 6) + +**File**: TargetChildIDTests.cs + +**Purpose**: Comprehensive test of Health Insurance tab, including Medicaid logic and medical provider/facility modals. + +**Test Flow**: + +#### Part 1: Medicaid = Yes → Health Insurance Checkboxes Disabled +1. Navigate to Target Child page +2. Open existing TCID entry +3. Switch to Health Insurance tab +4. Select Medicaid: "Yes" +5. Assert Medicaid case number input is visible +6. Assert all health insurance checkboxes are **disabled**: + - Family/Child Health + - Private Insurance + - Other + - Uninsured + - Unknown + +#### Part 2: Medicaid = No → Health Insurance Checkboxes Enabled +7. Select Medicaid: "No" +8. Assert all health insurance checkboxes are **enabled** +9. Check "Other" checkbox +10. Assert "Other specify" text input is **visible** +11. Uncheck "Other" checkbox + +#### Part 3: Medicaid = Unknown → Health Insurance Checkboxes Enabled +12. Select Medicaid: "Unknown" +13. Assert all health insurance checkboxes are **enabled** +14. Check "Other" checkbox +15. Assert "Other specify" text input is **visible** + +#### Part 4: Medical Provider Conditional Logic (Q19) +16. Select Medicaid: "Yes" +17. Enter Medicaid case number: "MCN12345" +18. Select Medical Provider Question (Q19): **"No"** +19. Assert medical provider dropdown is **disabled** +20. Assert medical facility dropdown is **disabled** +21. Assert "Not in List" doctor link has **disabled** class +22. Assert "Not in List" facility link has **disabled** class + +#### Part 5: Medical Provider Question = Yes +23. Select Medical Provider Question (Q19): **"Yes"** +24. Assert medical provider dropdown is **enabled** +25. Assert medical facility dropdown is **enabled** +26. Assert "Not in List" doctor link is **enabled** +27. Assert "Not in List" facility link is **enabled** + +#### Part 6: Medical Provider Modal - Validation +28. Click "Not in List" doctor link +29. Wait for modal: `.modal.show .modal-content` +30. Leave first name and last name empty +31. Click Submit in modal +32. Assert validation appears: "Provider's Last Name" + +#### Part 7: Medical Provider Modal - Add Provider +33. Fill modal fields: + - First Name: "testone" + - Last Name: "testtwo" + - Address: "aaaaaaaa" + - State: "aa" + - Zip: "00000" + - Phone: "000000000" +34. Click Submit +35. Wait for modal to close +36. Switch back to Health Insurance tab + +**Expected**: +- Medical provider dropdown now contains "testtwo" +- "testtwo" is auto-selected in dropdown + +#### Part 8: Medical Facility Modal - Validation +37. Click "Not in List" facility link +38. Wait for modal +39. Click Submit without filling +40. Assert validation appears: "Facility" + +#### Part 9: Medical Facility Modal - Add Facility +41. Fill modal fields: + - Name: "avengers" + - Address: "aaaa" + - City: "aaaaaa" + - State: "aa" + - Zip: "00000" + - Phone: "3434343434" +42. Click Submit +43. Wait for modal to close +44. Switch back to Health Insurance tab + +**Expected**: +- Medical facility dropdown now contains "avengers" +- "avengers" is auto-selected in dropdown + +#### Part 10: Submit Form +45. Click Submit (no validation expected) + +**Expected**: +- Success toast displayed +- Toast contains: "Form Saved", "Target Child Identification", PC1 ID + +--- + +### Test 7: AdditionalItemsParityAndDeliveryFlow (Priority 7) + +**File**: TargetChildIDAdditionalItemsTests.cs + +**Purpose**: Test Additional Items tab fields and tooltips. + +**Test Flow**: + +#### Part 1: Parity Validation +1. Navigate to Target Child page +2. Open existing TCID entry +3. Switch to Additional Items tab +4. Clear parity selection (select placeholder) +5. Click Submit + +**Expected Validation**: "Parity is required." + +#### Part 2: Fill Additional Items Fields +6. Switch to Additional Items tab +7. Select random parity +8. Select random delivery type +9. Select random "Child fed breast milk" option +10. Store selected values for verification + +#### Part 3: Test Tooltip Icons +11. Find all tooltip icons (glyphicon-question-sign) +12. For first 2 icons: + - Click icon + - Wait for tooltip to appear: `.tooltip-inner` + - Assert tooltip has text + - Log tooltip text + +**Expected**: At least 2 tooltip icons with helpful text + +#### Part 4: Submit and Verify Persistence +13. Click Submit +14. Verify success toast +15. Navigate back to TCID grid +16. Reopen same entry +17. Switch to Additional Items tab +18. Verify parity, delivery type, and breast milk selections persisted + +--- + +### Test 8: Phq9DateValidationAndSave (Priority 8) + +**File**: TargetChildIDPhq9Tests.cs + +**Purpose**: Comprehensive PHQ-9 tab test covering date validation, participant selection, scoring, and worker assignment. + +**Test Flow**: + +#### Part 1: Invalid Date Entry +1. Navigate to Target Child page +2. Open existing TCID entry +3. Switch to PHQ-9 tab +4. Enter invalid date: "000000" +5. Press Enter + +**Expected**: Input clears (invalid entry rejected) + +#### Part 2: Date Before Intake Validation +6. Get Intake Date from label +7. Get Target Child DOB from label +8. Set PHQ date to (Intake Date - 1 day) +9. Click Submit + +**Expected Validation**: "[PHQ-9] The PHQ date administered must be on or after the TC's date of birth!" + +#### Part 3: Valid Date and Participant Selection +10. Switch to PHQ-9 tab +11. Set PHQ date to (Target Child DOB + 1 day) +12. Select PHQ participant: "Other" (value "04") +13. Assert "Participant specify" input is **visible** +14. Enter specify text: "Non-family support" +15. Select PHQ participant: "Primary Caretaker 1" (value "01") +16. Assert "Participant specify" input is **hidden** + +#### Part 4: Worker Selection +17. Select PHQ participant: "Primary Caretaker 2" (value "02") +18. Select PHQ worker: "105" + +#### Part 5: Test Incomplete PHQ Scoring +19. Set PHQ scores (some blank): `["01", "02", "01", "", "01", "02", "", "02", "01"]` +20. Clear difficulty and referral +21. Wait for labels to update + +**Expected**: +- PHQ Result: "N/A" +- PHQ Score Validity: "Invalid" + +#### Part 6: Select Difficulty and Referral +22. Select difficulty: "Somewhat difficult" (value "02") +23. Check referral checkbox + +#### Part 7: Test Complete PHQ Scoring (Positive/Valid) +24. Set all PHQ scores to max: `["04", "04", "04", "04", "04", "04", "04", "04", "04"]` +25. Wait for labels to update + +**Expected**: +- PHQ Result: "Positive" +- PHQ Score Validity: "Valid" + +**PHQ Scoring Logic**: +- **Score Range**: 0-27 (9 questions × 0-3 points each) +- **Result**: + - < 10: Negative + - >= 10: Positive +- **Validity**: + - All 9 questions answered: Valid + - Any question blank: Invalid, shows "N/A" + +#### Part 8: Submit and Verify Persistence +26. Click Submit +27. Verify success toast +28. Navigate back to TCID grid +29. Reopen same entry +30. Switch to PHQ-9 tab +31. Verify participant, worker, and score values persisted + +--- + +### Test 9: MiechvMedicalCareSourceOtherSpecifyToggle (Priority 9) + +**File**: TargetChildIDMiechvTests.cs + +**Purpose**: Test MIECHV tab "Medical Care Source" field with conditional "Other specify" input. + +**Test Flow**: + +#### Part 1: Select "Other" → Show Specify Input +1. Navigate to Target Child page +2. Open existing TCID entry +3. Switch to MIECHV tab +4. Select Medical Care Source: "Other" (value "06") +5. Assert "Medical care source specify" input is **visible** +6. Enter specify text: "Community clinic" + +#### Part 2: Select Non-"Other" → Hide Specify Input +7. Select Medical Care Source: different option (value "02") +8. Wait for page update +9. Assert "Medical care source specify" input is **hidden** + +#### Part 3: Submit and Verify Persistence +10. Click Submit +11. Verify success toast +12. Navigate back to TCID grid +13. Reopen same entry +14. Switch to MIECHV tab +15. Verify medical care source selection persisted + +--- + +## Required Fields + +### Target Child Tab + +| Field | Selector | Required | Validation | Notes | +|-------|----------|----------|------------|-------| +| **Birth Term** | `select[id$='ddlBirthTerm']` | Yes | - | Full term, Premature, etc. | +| **First Name** | `input[id$='txtTCFirstName']` | Yes | - | Also validated on MIECHV tab | +| **Last Name** | `input[id$='txtTCLastName']` | Yes | - | | +| **Gestational Age** | `input[id$='txtGestationalAge']` | Yes | 0-40 weeks | Must be valid whole number | +| **Birth Weight Lbs** | `input[id$='txtBirthWtLbs']` | No | < 17 | | +| **Birth Weight Oz** | `input[id$='txtBirthWtOz']` | No | < 16 | | +| **Prenatal Care Date** | `input[id*='Prenatal'][type='text']` | No | < Target Child DOB | Must be before birth | + +### Health Insurance Tab + +| Field | Selector | Required | Conditional | Notes | +|-------|----------|----------|-------------|-------| +| **Medicaid** | `select[id$='ddlTCReceivingMedicaid']` | No | - | Yes/No/Unknown | +| **Medicaid Case Number** | `input[id$='txtTcHIMedicaidCaseNumber']` | No | If Medicaid = Yes | Visible only when Yes | +| **Health Insurance Checkboxes** | Various `input[id$='chkTCHI...']` | No | Disabled if Medicaid = Yes | Family/Child Health, Private, Other, Uninsured, Unknown | +| **Other Specify** | `input[id$='txtTCHIOtherSpecify']` | No | If "Other" checked | | +| **Has Medical Provider (Q19)** | `select[id$='ddlTCHasMedicalProvider']` | No | - | Controls Q20 | +| **Medical Provider** | `select[id$='ddlTCMedicalProviderFK']` | No | If Q19 = Yes | Disabled if Q19 = No | +| **Medical Facility** | `select[id$='ddlTCMedicalFacilityFK']` | No | If Q19 = Yes | Disabled if Q19 = No | + +### Additional Items Tab + +| Field | Selector | Required | Notes | +|-------|----------|----------|-------| +| **Parity** | `select[id$='ddlParity']` | Yes | Number of pregnancies | +| **Delivery Type** | `select[id$='ddlDeliveryType']` | No | Vaginal, C-section, etc. | +| **Child Fed Breast Milk** | `select[id$='ddlChildFedBreastMilk']` | No | Yes/No/Unknown | + +### PHQ-9 Tab + +| Field | Selector | Required | Conditional | Notes | +|-------|----------|----------|-------------|-------| +| **PHQ Date** | `input[id$='txtPHQDateAdministered']` | No | - | Must be >= Target Child DOB and >= Intake Date | +| **Participant** | `select[id$='ddlPHQ9Participant']` | No | - | PC1, PC2, Other, etc. | +| **Participant Specify** | `input[id$='txtPHQ9ParticipantSpecify']` | No | If Participant = Other | | +| **PHQ Worker** | `select[id$='ddlPHQWorker']` | No | - | | +| **9 PHQ Questions** | `select.phq9score` | No | - | Dropdowns with 0-3 scale | +| **Difficulty** | `select[id$='ddlDifficulty']` | No | - | Not/Somewhat/Very/Extremely difficult | +| **Referral Made** | `input[id$='chkDepressionReferralMade']` | No | - | Checkbox | + +### MIECHV Tab + +| Field | Selector | Required | Conditional | Notes | +|-------|----------|----------|-------------|-------| +| **Medical Care Source** | `select[id$='ddlTCMedicalCareSource']` | No | - | Various options + Other | +| **Medical Care Source Specify** | `input[id$='txtTCMedicalCareSourceOtherSpecify']` | No | If Medical Care Source = Other | | + +--- + +## Helper Methods Summary + +### Navigation + +#### NavigateToTargetChildPage() +Navigate from Forms pane to Target Child Information page. + +**Parameters**: `driver`, `formsPane`, `pc1Id` + +**Assertions**: +- URL contains `TCIDs.aspx` +- URL contains `pc1id={pc1Id}` + +--- + +#### OpenNewTcidForm() +Click "New TCID" button to open new form. + +--- + +#### OpenExistingTcidEntry() +Find and click the first existing TCID link in the grid. + +**Returns**: Opens existing TCID form +**Assertions**: URL contains `tcid.aspx` and `tcpk=` + +--- + +#### NavigateBackToExistingTcid() +Navigate directly to TCID grid and reopen existing entry. + +**Parameters**: `driver`, `pc1Id` + +--- + +### Tab Management + +#### SwitchToTab() +Switch to a specific tab in the multi-tab form. + +**Parameters**: +- `tabHref`: Tab href selector (e.g., "#TargetChild") +- `tabTitle`: Tab title for logging + +**Assertions**: Parent `li` has "active" class + +--- + +### Form Submission + +#### SubmitForm() +Submit form and optionally capture validation. + +**Parameters**: `expectValidation` (default true) + +**Returns**: Validation summary text if `expectValidation` is true + +--- + +#### SubmitFormFromAdditionalItemsTab() +Submit form and automatically switch back to Additional Items tab. + +**Use Case**: When submitting from Additional Items tab and validation redirects to another tab + +--- + +### Validation + +#### CountOccurrences() +Count how many times a string appears in text (case-insensitive). + +**Use Case**: Verify validation message appears expected number of times + +**Example**: "First Name is required!" appears 2 times initially + +--- + +### Dropdown Helpers + +#### SelectRandomDropdownOption() +Select a random option from a dropdown (excludes empty values). + +**Parameters**: `selector`, `description` + +**Logs**: Selected option text and value + +--- + +#### SelectDropdownPlaceholderOption() +Select the placeholder option (empty value) in a dropdown. + +**Use Case**: Clear dropdown selection to trigger validation + +--- + +### Field Setters + +#### SetPrenatalCareDate() +Set the prenatal care date input. + +**Parameters**: `driver`, `dateValue` + +--- + +#### SetBirthWeight() +Set birth weight pounds and ounces. + +**Parameters**: `driver`, `pounds`, `ounces` + +--- + +#### ValidateBirthWeight() +Test birth weight validation by setting invalid values and verifying error messages. + +**Parameters**: `driver`, `targetTab`, `miechvTab`, `pounds`, `ounces` + +**Expected Validations**: +- "Birth weight pounds must be less than 17" +- "Birth weight ounces must be less than 16" + +--- + +### PHQ-9 Helpers + +#### SetPhqDate() +Set PHQ-9 date input with formatted date. + +**Format**: `MM/dd/yy` + +--- + +#### SetPhqScores() +Set all 9 PHQ-9 question scores. + +**Parameters**: `driver`, `values` (array of 9 strings, "" for blank) + +**Example**: `["01", "02", "01", "", "01", "02", "", "02", "01"]` + +--- + +#### ClearPhqScores() +Clear all 9 PHQ-9 question scores (select placeholder). + +--- + +#### EnsureDifficultyAndReferralCleared() +Clear PHQ-9 difficulty dropdown and uncheck referral checkbox. + +--- + +#### WaitForLabelText() +Wait for a label's text to meet a specific condition. + +**Parameters**: +- `selector`: Label selector +- `predicate`: Function that returns true when condition met +- `timeoutSeconds`: Default 5 + +**Returns**: Final label text + +**Use Case**: Wait for PHQ-9 score labels to update after changing answers + +--- + +### Health Insurance Helpers + +#### GetHealthInsuranceCheckboxes() +Get all health insurance checkboxes. + +**Returns**: List of 5 checkboxes (Family/Child Health, Private, Other, Uninsured, Unknown) + +--- + +#### GetHealthInsuranceOtherCheckbox() +Get the "Other" checkbox. + +--- + +#### GetHealthInsuranceOtherSpecifyInput() +Get the "Other specify" text input. + +--- + +#### IsElementVisible() +Check if element is visible (displayed and not `display: none`). + +**Returns**: `bool` + +--- + +#### AssertMedicalProviderDropdownsEnabledState() +Assert medical provider and facility dropdowns are enabled/disabled. + +**Parameters**: `driver`, `shouldBeEnabled` + +--- + +#### AssertDropdownEnabledState() +Assert a single dropdown is enabled/disabled. + +**Parameters**: `dropdown`, `shouldBeEnabled`, `description` + +--- + +#### GetMedicalProviderNotInListLink() +Get the "Not in List" link for medical provider. + +--- + +#### GetMedicalFacilityNotInListLink() +Get the "Not in List" link for medical facility. + +--- + +#### HasDisabledClass() +Check if element has "disabled" CSS class. + +**Returns**: `bool` + +--- + +#### EnsureMedicaidCaseNumberInputVisible() +Assert Medicaid case number input is visible and return it. + +**Returns**: IWebElement (Medicaid case number input) + +--- + +### Modal Helpers + +#### WaitForElementToDisappear() +Wait for an element to disappear from the page. + +**Parameters**: `driver`, `cssSelector`, `timeoutSeconds` (default 10) + +**Use Case**: Wait for modal to close after submission + +--- + +#### FindInputInContainerByIdParts() +Find an input within a container by matching multiple ID parts. + +**Parameters**: `container`, `description`, `idParts` (variable number) + +**Example**: Find input with ID containing both "MedicalFacility" and "Name" + +**Returns**: IWebElement + +--- + +### Utility + +#### TrySwitchBackToAdditionalItems() +Attempt to switch back to Additional Items tab (gracefully handles errors). + +**Use Case**: After form submission, automatically return to Additional Items tab + +--- + +#### ParseDate() +Parse date string in MM/dd/yy or MM/dd/yyyy format. + +**Returns**: DateTime + +**Throws**: InvalidOperationException if parse fails + +--- + +## Important Concepts + +### 1. Multi-Tab Form Structure + +**5 Tabs**: +- Target Child (birth info) +- Health Insurance (Medicaid, insurance, medical provider) +- Additional Items (parity, delivery, breastfeeding) +- PHQ-9 (depression screening) +- MIECHV (MIECHV-specific data) + +**Navigation Pattern**: Use `SwitchToTab()` helper between tabs + +**Submission**: Submit button is in panel footer, visible on all tabs + +**Validation**: Validation summary appears on all tabs, shows errors from all tabs + +--- + +### 2. Progressive Validation + +**Pattern**: Submit form multiple times, filling one required field at a time. + +**Flow**: +``` +Submit empty → 4 validations +Fill Birth Term → 3 validations +Fill First Name → 2 validations +Fill Last Name → 1 validation +Fill Parity → 0 validations for these fields, new validation for Gestational Age +Fill Gestational Age → 0 validations +``` + +**Purpose**: Demonstrates real-time validation feedback and field dependencies + +--- + +### 3. Medicaid Conditional Logic + +**Rule**: If Medicaid = "Yes", all health insurance checkboxes are **disabled**. + +**Logic**: +``` +IF Medicaid = "Yes" (value "1") +THEN + Health insurance checkboxes → Disabled + Medicaid case number input → Visible +ELSE IF Medicaid = "No" (value "0") OR "Unknown" (value "9") +THEN + Health insurance checkboxes → Enabled + Medicaid case number input → Hidden (for "No") +``` + +**Test Coverage**: Test 6 validates this logic thoroughly + +--- + +### 4. Medical Provider Question (Q19) Conditional Logic + +**Rule**: Question 20 (medical provider/facility dropdowns) is controlled by Question 19. + +**Logic**: +``` +IF Q19 = "No" (value "0") +THEN + Medical provider dropdown → Disabled + Medical facility dropdown → Disabled + "Not in List" doctor link → Disabled class + "Not in List" facility link → Disabled class +ELSE IF Q19 = "Yes" (value "1") +THEN + Medical provider dropdown → Enabled + Medical facility dropdown → Enabled + "Not in List" doctor link → Enabled + "Not in List" facility link → Enabled +``` + +--- + +### 5. Medical Provider/Facility Modals + +**"Not in List" Pattern**: When medical provider/facility is not in dropdown, click "Not in List" link to open modal and add new entry. + +**Modal Workflow**: +``` +Click "Not in List" link + ↓ +Modal opens + ↓ +Fill required fields (Last Name for provider, Name for facility) + ↓ +Submit modal + ↓ +Modal closes + ↓ +New entry appears in dropdown + ↓ +New entry is auto-selected +``` + +**Validation**: Both modals validate required fields before allowing submission + +**Test Coverage**: Test 6 validates both provider and facility modals + +--- + +### 6. PHQ-9 Scoring System + +**9 Questions**: Each scored 0-3 (Not at all, Several days, More than half the days, Nearly every day) + +**Total Score**: Sum of 9 questions (0-27) + +**Result Calculation**: +- **Score < 10**: Negative +- **Score >= 10**: Positive + +**Validity**: +- **All 9 answered**: Valid +- **Any blank**: Invalid, shows "N/A" + +**Real-Time Calculation**: Score, Result, and Validity update immediately as user answers questions + +**Labels**: +- `lblPHQ9Score`: Displays total score (0-27) or "N/A" +- `lblPHQ9Result`: Displays "Positive" or "Negative" or "N/A" +- `lblPHQ9ScoreValidity`: Displays "Valid" or "Invalid" + +**Test Coverage**: Test 8 validates scoring logic with incomplete and complete scenarios + +--- + +### 7. Birth Weight Validation + +**Validation Rules**: +- **Pounds**: Must be < 17 +- **Ounces**: Must be < 16 + +**Both Rules Enforced**: Both validations can appear simultaneously + +**Test Scenarios**: +- (-1, -2): Both fail +- (20, 20): Both fail +- (17, 16): Both fail (boundary) +- (0-16, 0-15): Pass + +**Test Coverage**: Test 5 validates all scenarios + +--- + +### 8. Date Validations + +**Prenatal Care Date**: +- Must be **before** Target Child DOB +- Validation: "Date Began Receiving Prenatal Care can not start after the birth of the child" + +**PHQ-9 Date**: +- Must be **on or after** Target Child DOB +- Must be **on or after** Intake Date +- Validation: "[PHQ-9] The PHQ date administered must be on or after the TC's date of birth!" + +**Date Formats Accepted**: `MM/dd/yy` or `MM/dd/yyyy` + +--- + +### 9. Conditional "Other Specify" Fields + +**Pattern**: When "Other" option is selected, a "specify" text input appears. + +**3 Instances in Form**: + +1. **Health Insurance → Other**: + - Checkbox: `input[id$='chkTCHIOther']` + - Specify: `input[id$='txtTCHIOtherSpecify']` + +2. **PHQ-9 → Participant = Other**: + - Dropdown: `select[id$='ddlPHQ9Participant']` (value "04") + - Specify: `input[id$='txtPHQ9ParticipantSpecify']` + +3. **MIECHV → Medical Care Source = Other**: + - Dropdown: `select[id$='ddlTCMedicalCareSource']` (value "06") + - Specify: `input[id$='txtTCMedicalCareSourceOtherSpecify']` + +**Test Coverage**: +- Test 6: Health Insurance Other +- Test 8: PHQ-9 Participant Other +- Test 9: MIECHV Medical Care Source Other + +--- + +### 10. Tooltip Icons + +**Location**: Additional Items tab + +**Icon**: `span.glyphicon.glyphicon-question-sign` + +**Tooltip Display**: `.tooltip-inner` + +**Purpose**: Provide helpful explanations for optional fields + +**Test Coverage**: Test 7 validates tooltip functionality + +--- + +## What to Keep in Mind + +### When Modifying Tests + +1. **Test Order Matters**: Tests 4-9 require existing TCID entries created by manual setup or previous test runs. + +2. **Multi-Tab Form**: Always switch to correct tab before interacting with fields. + +3. **Submit Button Location**: Submit button is in panel footer, visible from all tabs. + +4. **Validation Appears on All Tabs**: Validation summary shows errors from all tabs, regardless of which tab you're on. + +5. **Partial Class Pattern**: Changes to helper methods in one file affect all test files. + +6. **Selector Constants**: All selectors defined as constants at top of TargetChildIDTests.cs. + +--- + +### When Adding New Tests + +1. **Assign Priority**: Add `[TestPriority(N)]` with number > 9. + +2. **Use Parameterization**: Add `[Theory]` and `[MemberData(nameof(GetTestPc1Ids))]`. + +3. **Start with Navigation**: Use `NavigateToTargetChildPage()` and `OpenExistingTcidEntry()` or `OpenNewTcidForm()`. + +4. **Use Tab Helpers**: Use `SwitchToTab()` to navigate between tabs. + +5. **Log Actions**: Use `_output.WriteLine()` for all significant actions. + +6. **Wait After Tab Switch**: Use `WaitForReady()` and `Thread.Sleep()` after tab switches. + +7. **Verify with Toast**: Success should show jQuery toast - use `WebElementHelper.GetToastMessage()`. + +--- + +### Common Pitfalls + +1. **Tab Not Active**: Forgot to switch to correct tab before interacting with field. + - **Solution**: Always use `SwitchToTab()` before finding elements. + +2. **Validation Summary Not Found**: Validation redirected to different tab. + - **Solution**: Validation summary exists on all tabs - don't switch tabs to find it. + +3. **Medicaid Checkbox Enabled When Should Be Disabled**: Medicaid dropdown not set to "Yes". + - **Solution**: Ensure Medicaid dropdown selection completes before checking checkboxes. + +4. **Modal Not Closing**: Modal close animation takes time. + - **Solution**: Use `WaitForElementToDisappear()` with sufficient timeout. + +5. **PHQ Score Not Updating**: JavaScript calculation not triggered. + - **Solution**: Wait with `WaitForReady()` and `Thread.Sleep()` after setting scores. + +6. **Provider/Facility Not Auto-Selected**: Dropdown not refreshed after modal close. + - **Solution**: Switch back to Health Insurance tab after modal closes. + +7. **Prenatal Date Validation Not Appearing**: Date is actually valid (before birth). + - **Solution**: Verify Target Child DOB before setting test date. + +8. **Birth Weight Validation Not Clearing**: Values still out of range. + - **Solution**: Ensure values are strictly less than limits (< 17 lbs, < 16 oz). + +--- + +## Test Data Requirements + +### Prerequisites +- Test user with DataEntry role +- Valid PC1 IDs in `appsettings.json` under `TestPc1Ids` +- Cases must have: + - At least one existing TCID entry (for Tests 4-9) + - Target Child DOB and Intake Date visible on form + +### Test Creates +- New TCID entries (Test 2, Test 3) +- Medical providers via modal (Test 6) +- Medical facilities via modal (Test 6) + +### Test Modifies +- Existing TCID entries (Tests 4-9) +- PHQ-9 scores +- Additional Items selections + +### Test Deletes +- None + +**Net Impact**: Some TCID entries created, some modified. + +--- + +## Running the Tests + +### Run All TargetChildID Tests +```bash +dotnet test --filter "FullyQualifiedName~TargetChildID" +``` + +### Run Specific Test +```bash +# Navigation (Priority 1) +dotnet test --filter "FullyQualifiedName~TargetChildIDTests.NavigateToTargetChildInformationForm" + +# Info alert (Priority 2) +dotnet test --filter "FullyQualifiedName~TargetChildIDTests.NewTcidButtonDisplaysInfoAlert" + +# Progressive validation (Priority 3) +dotnet test --filter "FullyQualifiedName~TargetChildIDTests.ValidationAndSubmission" + +# Prenatal care date (Priority 4) +dotnet test --filter "FullyQualifiedName~TargetChildIDTests.ExistingTcidPrenatalCareValidation" + +# Birth weight (Priority 5) +dotnet test --filter "FullyQualifiedName~TargetChildIDTests.ExistingTcidBirthWeightValidation" + +# Health insurance (Priority 6) +dotnet test --filter "FullyQualifiedName~TargetChildIDTests.HealthInsuranceOptionsRespectMedicaidSelection" + +# Additional Items (Priority 7) +dotnet test --filter "FullyQualifiedName~TargetChildIDTests.AdditionalItemsParityAndDeliveryFlow" + +# PHQ-9 (Priority 8) +dotnet test --filter "FullyQualifiedName~TargetChildIDTests.Phq9DateValidationAndSave" + +# MIECHV (Priority 9) +dotnet test --filter "FullyQualifiedName~TargetChildIDTests.MiechvMedicalCareSourceOtherSpecifyToggle" +``` + +--- + +## Troubleshooting + +### Test Fails: "Target Child Information link was not found" +- **Issue**: Link not in Forms pane or selector changed +- **Solution**: Verify Forms tab loaded, check link selector + +### Test Fails: "TCID grid was not found" +- **Issue**: Page didn't load or no TCID entries exist +- **Solution**: Verify case has at least one TCID entry, check grid selector + +### Test Fails: Birth weight validation doesn't appear +- **Issue**: Values are actually within valid range +- **Solution**: Verify test values are >= 17 lbs or >= 16 oz + +### Test Fails: Health insurance checkboxes not disabled +- **Issue**: Medicaid dropdown selection didn't trigger update +- **Solution**: Increase wait time after Medicaid selection, verify page refresh + +### Test Fails: Medical provider modal validation not appearing +- **Issue**: Validation logic changed or selector incorrect +- **Solution**: Verify modal validation selector, check required fields + +### Test Fails: PHQ scores not calculating +- **Issue**: JavaScript not running or scores not set correctly +- **Solution**: Verify all 9 dropdowns set, increase wait time for calculation + +### Test Fails: Prenatal care date validation appears unexpectedly +- **Issue**: Target Child DOB changed or test date is actually after birth +- **Solution**: Verify Target Child DOB, adjust test dates accordingly + +### Test Fails: Tab switch doesn't work +- **Issue**: Tab selector changed or JavaScript not loaded +- **Solution**: Verify tab selectors, check for JavaScript errors + +### Test Fails: "Other specify" input not visible +- **Issue**: "Other" option not triggering show/hide logic +- **Solution**: Verify "Other" value is correct, increase wait time after selection + +--- + +## Selectors Reference + +### Navigation + +| Element | Selector | +|---------|----------| +| **Target Child Link** | `a.moreInfo[data-formtype='TCIBO']` | +| **New TCID Button** | `a.btn.btn-default[title*='New']` | +| **TCID Grid** | `table[id*='grTCIDs']` | +| **TCID Row Link** | `td a[href*='tcid.aspx']` | + +### Form Elements + +| Element | Selector | +|---------|----------| +| **Submit Button** | `div.panel-footer a.btn.btn-primary` | +| **Validation Summary** | `div[id$='ValidationSummary1']` | +| **Info Alert** | `div.alert.alert-info` | + +### Target Child Tab + +| Element | Selector | +|---------|----------| +| **Birth Term Dropdown** | `select[id$='ddlBirthTerm']` | +| **First Name Input** | `input[id$='txtTCFirstName']` | +| **Last Name Input** | `input[id$='txtTCLastName']` | +| **Gestational Age Input** | `input[id$='txtGestationalAge']` | +| **Birth Weight Lbs Input** | `input[id$='txtBirthWtLbs']` | +| **Birth Weight Oz Input** | `input[id$='txtBirthWtOz']` | +| **Prenatal Care Input** | `input[id*='Prenatal'][type='text']` | + +### Health Insurance Tab + +| Element | Selector | +|---------|----------| +| **Medicaid Dropdown** | `select[id$='ddlTCReceivingMedicaid']` | +| **Medicaid Case Number Input** | `input[id$='txtTcHIMedicaidCaseNumber']` | +| **Health Insurance Checkboxes** | `input[id$='chkTCHI...']` | +| **Has Medical Provider Dropdown** | `select[id$='ddlTCHasMedicalProvider']` | +| **Medical Provider Dropdown** | `select[id$='ddlTCMedicalProviderFK']` | +| **Medical Facility Dropdown** | `select[id$='ddlTCMedicalFacilityFK']` | +| **Provider "Not in List" Link** | `a[id$='lnkNewMedicalProvider']` | +| **Facility "Not in List" Link** | `a[id$='lnkNewMedicalFacility']` | + +### Additional Items Tab + +| Element | Selector | +|---------|----------| +| **Parity Dropdown** | `select[id$='ddlParity']` | +| **Delivery Type Dropdown** | `select[id$='ddlDeliveryType']` | +| **Breast Milk Dropdown** | `select[id$='ddlChildFedBreastMilk']` | +| **Tooltip Icons** | `#OptionalItems span.glyphicon-question-sign` | + +### PHQ-9 Tab + +| Element | Selector | +|---------|----------| +| **PHQ Date Input** | `input[id$='txtPHQDateAdministered']` | +| **Participant Dropdown** | `select[id$='ddlPHQ9Participant']` | +| **Worker Dropdown** | `select[id$='ddlPHQWorker']` | +| **PHQ Question Dropdowns** | `#PHQ9 select.phq9score` | +| **Difficulty Dropdown** | `select[id$='ddlDifficulty']` | +| **Referral Checkbox** | `input[id$='chkDepressionReferralMade']` | +| **Score Label** | `span[id$='lblPHQ9Score']` | +| **Result Label** | `span[id$='lblPHQ9Result']` | +| **Validity Label** | `span[id$='lblPHQ9ScoreValidity']` | + +### MIECHV Tab + +| Element | Selector | +|---------|----------| +| **Medical Care Source Dropdown** | `select[id$='ddlTCMedicalCareSource']` | +| **Medical Care Source Specify Input** | `input[id$='txtTCMedicalCareSourceOtherSpecify']` | + +### Modals + +| Element | Selector | +|---------|----------| +| **Medical Provider Modal** | `.modal.show .modal-content` | +| **Medical Facility Modal** | `.modal.show .modal-content` | +| **Modal Submit Button (Provider)** | `a[id$='ctlMedicalProvider_btnSubmitProvider']` | +| **Modal Submit Button (Facility)** | `a[id$='ctlMedicalFacility_btnSubmitFacility']` | diff --git a/Pookie.Tests/UnitTests/TargetChildID/TargetChildIDAdditionalItemsTests.cs b/Pookie.Tests/UnitTests/TargetChildID/TargetChildIDAdditionalItemsTests.cs new file mode 100644 index 0000000..f680199 --- /dev/null +++ b/Pookie.Tests/UnitTests/TargetChildID/TargetChildIDAdditionalItemsTests.cs @@ -0,0 +1,117 @@ +using System; +using System.Linq; +using System.Threading; +using AFUT.Tests.Driver; +using AFUT.Tests.Helpers; +using AFUT.Tests.UnitTests.Attributes; +using OpenQA.Selenium; +using OpenQA.Selenium.Support.UI; +using Xunit; + +namespace AFUT.Tests.UnitTests.TargetChildID +{ + public partial class TargetChildIDTests + { + [Theory] + [MemberData(nameof(GetTestPc1Ids))] + [TestPriority(7)] + public void AdditionalItemsParityAndDeliveryFlow(string pc1Id) + { + using var driver = _driverFactory.CreateDriver(); + + var (homePage, formsPane) = CommonTestHelper.NavigateToFormsTab(driver, _config, pc1Id); + Assert.NotNull(homePage); + Assert.True(homePage.IsLoaded, "Home page did not load after selecting DataEntry role."); + + NavigateToTargetChildPage(driver, formsPane, pc1Id); + OpenExistingTcidEntry(driver); + + const string additionalItemsTab = "#OptionalItems"; + + SwitchToTab(driver, additionalItemsTab, "Additional Items"); + + var parityDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(ParityDropdownSelector), 10) + ?? throw new InvalidOperationException("Parity dropdown was not found on the Additional Items tab."); + + _output.WriteLine("[INFO] Clearing parity selection to trigger validation."); + SelectDropdownPlaceholderOption(parityDropdown, "Parity dropdown"); + + var summaryText = SubmitFormFromAdditionalItemsTab(driver); + Assert.Contains("Parity is required.", summaryText, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] Parity validation appeared after submitting with placeholder selected."); + + SwitchToTab(driver, additionalItemsTab, "Additional Items"); + SelectRandomDropdownOption(driver, ParityDropdownSelector, "Parity dropdown"); + parityDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(ParityDropdownSelector), 10) + ?? throw new InvalidOperationException("Parity dropdown was not found after selecting a value."); + var paritySelect = new SelectElement(parityDropdown); + var expectedParityValue = paritySelect.SelectedOption.GetAttribute("value") ?? string.Empty; + _output.WriteLine($"[INFO] Selected parity value {expectedParityValue}."); + + SelectRandomDropdownOption(driver, DeliveryTypeDropdownSelector, "Delivery type dropdown"); + var deliveryDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(DeliveryTypeDropdownSelector), 10) + ?? throw new InvalidOperationException("Delivery type dropdown was not found after selecting a value."); + var deliverySelect = new SelectElement(deliveryDropdown); + var expectedDeliveryValue = deliverySelect.SelectedOption.GetAttribute("value") ?? string.Empty; + _output.WriteLine($"[INFO] Selected delivery type value {expectedDeliveryValue}."); + + SelectRandomDropdownOption(driver, ChildFedBreastMilkDropdownSelector, "Child fed breast milk dropdown"); + var breastDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(ChildFedBreastMilkDropdownSelector), 10) + ?? throw new InvalidOperationException("Child fed breast milk dropdown was not found after selecting a value."); + var breastSelect = new SelectElement(breastDropdown); + var expectedBreastValue = breastSelect.SelectedOption.GetAttribute("value") ?? string.Empty; + _output.WriteLine($"[INFO] Selected breast milk value {expectedBreastValue}."); + + var tooltipIcons = driver.FindElements(By.CssSelector(AdditionalItemsTooltipSelector)) + .Where(icon => icon.Displayed) + .ToList(); + Assert.True(tooltipIcons.Count >= 2, "Expected at least two tooltip icons on Additional Items tab."); + + foreach (var icon in tooltipIcons.Take(2)) + { + CommonTestHelper.ClickElement(driver, icon); + driver.WaitForReady(1); + Thread.Sleep(300); + var tooltipElement = driver.FindElements(By.CssSelector(".tooltip-inner")) + .FirstOrDefault(el => el.Displayed && !string.IsNullOrWhiteSpace(el.Text)); + + Assert.NotNull(tooltipElement); + _output.WriteLine($"[INFO] Tooltip text: {tooltipElement?.Text}"); + } + _output.WriteLine("[PASS] Tooltip question mark icons displayed helper text when interacted with."); + + _output.WriteLine("[INFO] Submitting TCID form after updating Additional Items."); + SubmitFormFromAdditionalItemsTab(driver, expectValidation: false); + + var toastMessage = WebElementHelper.GetToastMessage(driver, 2000); + Assert.False(string.IsNullOrWhiteSpace(toastMessage), "Success toast message was not displayed after saving Additional Items."); + Assert.Contains("Form Saved", toastMessage, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Target Child Identification", toastMessage, StringComparison.OrdinalIgnoreCase); + Assert.Contains(pc1Id, toastMessage, StringComparison.OrdinalIgnoreCase); + _output.WriteLine($"[PASS] TCID form saved successfully with toast: {toastMessage}"); + + _output.WriteLine("[INFO] Returning to TCID grid to reopen the same entry."); + driver.Navigate().GoToUrl($"{_config.AppUrl}/Pages/TCIDs.aspx?pc1id={pc1Id}"); + driver.WaitForReady(15); + OpenExistingTcidEntry(driver); + SwitchToTab(driver, additionalItemsTab, "Additional Items"); + + parityDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(ParityDropdownSelector), 10) + ?? throw new InvalidOperationException("Parity dropdown was not found when verifying saved values."); + paritySelect = new SelectElement(parityDropdown); + Assert.Equal(expectedParityValue, paritySelect.SelectedOption.GetAttribute("value")); + + deliveryDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(DeliveryTypeDropdownSelector), 10) + ?? throw new InvalidOperationException("Delivery type dropdown was not found when verifying saved values."); + deliverySelect = new SelectElement(deliveryDropdown); + Assert.Equal(expectedDeliveryValue, deliverySelect.SelectedOption.GetAttribute("value")); + + breastDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(ChildFedBreastMilkDropdownSelector), 10) + ?? throw new InvalidOperationException("Child breast milk dropdown was not found when verifying saved values."); + breastSelect = new SelectElement(breastDropdown); + Assert.Equal(expectedBreastValue, breastSelect.SelectedOption.GetAttribute("value")); + _output.WriteLine("[PASS] Additional Items selections persisted after saving."); + } + } +} + diff --git a/Pookie.Tests/UnitTests/TargetChildID/TargetChildIDMiechvTests.cs b/Pookie.Tests/UnitTests/TargetChildID/TargetChildIDMiechvTests.cs new file mode 100644 index 0000000..8b96718 --- /dev/null +++ b/Pookie.Tests/UnitTests/TargetChildID/TargetChildIDMiechvTests.cs @@ -0,0 +1,70 @@ +using System; +using System.Threading; +using AFUT.Tests.Driver; +using AFUT.Tests.Helpers; +using AFUT.Tests.UnitTests.Attributes; +using OpenQA.Selenium; +using OpenQA.Selenium.Support.UI; +using Xunit; + +namespace AFUT.Tests.UnitTests.TargetChildID +{ + public partial class TargetChildIDTests + { + [Theory] + [MemberData(nameof(GetTestPc1Ids))] + [TestPriority(9)] + public void MiechvMedicalCareSourceOtherSpecifyToggle(string pc1Id) + { + using var driver = _driverFactory.CreateDriver(); + + var (homePage, formsPane) = CommonTestHelper.NavigateToFormsTab(driver, _config, pc1Id); + Assert.NotNull(homePage); + Assert.True(homePage.IsLoaded, "Home page did not load after selecting DataEntry role."); + + NavigateToTargetChildPage(driver, formsPane, pc1Id); + OpenExistingTcidEntry(driver); + + SwitchToTab(driver, MiechvTabSelector, "MIECHV"); + + var careSourceDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(MedicalCareSourceDropdownSelector), 10) + ?? throw new InvalidOperationException("Medical care source dropdown was not found on MIECHV tab."); + var careSourceSelect = new SelectElement(careSourceDropdown); + + _output.WriteLine("[INFO] Selecting 'Other' for medical care source to expose specify input."); + careSourceSelect.SelectByValue("06"); + + var specifyInput = driver.WaitforElementToBeInDOM(By.CssSelector(MedicalCareSourceSpecifyInputSelector), 10) + ?? throw new InvalidOperationException("Medical care source specify input was not found."); + Assert.True(specifyInput.Displayed, "Medical care source specify input should be visible when 'Other' is selected."); + WebElementHelper.SetInputValue(driver, specifyInput, "Community clinic", "Medical care source specify input", triggerBlur: true); + + _output.WriteLine("[INFO] Selecting another option to hide specify input."); + careSourceSelect.SelectByValue("02"); + driver.WaitForReady(1); + Thread.Sleep(200); + Assert.False(specifyInput.Displayed, "Medical care source specify input should be hidden when selecting non-'Other' option."); + + SubmitForm(driver, expectValidation: false); + + var toastMessage = WebElementHelper.GetToastMessage(driver, 2000); + Assert.False(string.IsNullOrWhiteSpace(toastMessage), "Success toast message was not displayed after saving MIECHV updates."); + Assert.Contains("Form Saved", toastMessage, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Target Child Identification", toastMessage, StringComparison.OrdinalIgnoreCase); + Assert.Contains(pc1Id, toastMessage, StringComparison.OrdinalIgnoreCase); + _output.WriteLine($"[PASS] MIECHV medical care source updates saved successfully with toast: {toastMessage}"); + + driver.Navigate().GoToUrl($"{_config.AppUrl}/Pages/TCIDs.aspx?pc1id={pc1Id}"); + driver.WaitForReady(15); + OpenExistingTcidEntry(driver); + SwitchToTab(driver, MiechvTabSelector, "MIECHV"); + + careSourceDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(MedicalCareSourceDropdownSelector), 10) + ?? throw new InvalidOperationException("Medical care source dropdown was not found when verifying saved values."); + careSourceSelect = new SelectElement(careSourceDropdown); + Assert.Equal("02", careSourceSelect.SelectedOption.GetAttribute("value")); + _output.WriteLine("[PASS] Medical care source selection persisted after saving."); + } + } +} + diff --git a/Pookie.Tests/UnitTests/TargetChildID/TargetChildIDPhq9Tests.cs b/Pookie.Tests/UnitTests/TargetChildID/TargetChildIDPhq9Tests.cs new file mode 100644 index 0000000..ea6089d --- /dev/null +++ b/Pookie.Tests/UnitTests/TargetChildID/TargetChildIDPhq9Tests.cs @@ -0,0 +1,239 @@ +using System; +using System.Globalization; +using System.Threading; +using AFUT.Tests.Driver; +using AFUT.Tests.Helpers; +using AFUT.Tests.UnitTests.Attributes; +using OpenQA.Selenium; +using OpenQA.Selenium.Support.UI; +using Xunit; + +namespace AFUT.Tests.UnitTests.TargetChildID +{ + public partial class TargetChildIDTests + { + private const string DateFormatShortYear = "MM/dd/yy"; + private static readonly string[] AcceptedDateFormats = { "MM/dd/yy", "MM/dd/yyyy" }; + + [Theory] + [MemberData(nameof(GetTestPc1Ids))] + [TestPriority(8)] + public void Phq9DateValidationAndSave(string pc1Id) + { + using var driver = _driverFactory.CreateDriver(); + + var (homePage, formsPane) = CommonTestHelper.NavigateToFormsTab(driver, _config, pc1Id); + Assert.NotNull(homePage); + Assert.True(homePage.IsLoaded, "Home page did not load after selecting DataEntry role."); + + NavigateToTargetChildPage(driver, formsPane, pc1Id); + OpenExistingTcidEntry(driver); + + const string phqTabTitle = "PHQ-9"; + SwitchToTab(driver, PhqTabSelector, phqTabTitle); + + var phqInput = driver.WaitforElementToBeInDOM(By.CssSelector(PhqDateInputSelector), 10) + ?? throw new InvalidOperationException("PHQ date input was not found."); + + _output.WriteLine("[INFO] Entering invalid PHQ date value '000000'."); + phqInput.Clear(); + phqInput.SendKeys("000000"); + phqInput.SendKeys(Keys.Enter); + driver.WaitForReady(2); + Thread.Sleep(200); + var valueAfterInvalid = phqInput.GetAttribute("value")?.Trim() ?? string.Empty; + Assert.True(string.IsNullOrEmpty(valueAfterInvalid), "PHQ date input should clear invalid entry '000000'."); + + var intakeLabel = driver.WaitforElementToBeInDOM(By.CssSelector(IntakeDateLabelSelector), 10) + ?? throw new InvalidOperationException("Intake date label was not found."); + var intakeDateText = intakeLabel.Text?.Trim() ?? string.Empty; + var intakeDate = ParseDate(intakeDateText, "Intake date label"); + var dobLabel = driver.WaitforElementToBeInDOM(By.CssSelector(TargetChildDobLabelSelector), 10) + ?? throw new InvalidOperationException("Target child DOB label was not found."); + var dobText = dobLabel.Text?.Trim() ?? string.Empty; + var targetChildDob = ParseDate(dobText, "Target child DOB label"); + + var invalidPhqDate = intakeDate.AddDays(-1); + SetPhqDate(driver, phqInput, invalidPhqDate); + var summaryText = SubmitForm(driver); + Assert.Contains("[PHQ-9] The PHQ date administered must be on or after the TC's date of birth!", summaryText, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] Validation message appeared for PHQ date prior to intake."); + + SwitchToTab(driver, PhqTabSelector, phqTabTitle); + phqInput = driver.WaitforElementToBeInDOM(By.CssSelector(PhqDateInputSelector), 10) + ?? throw new InvalidOperationException("PHQ date input was not found after validation."); + + var validPhqDate = targetChildDob.AddDays(1); + SetPhqDate(driver, phqInput, validPhqDate); + + _output.WriteLine("[INFO] Selecting 'Other' for PHQ participant to verify specify input."); + var participantDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(PhqParticipantDropdownSelector), 10) + ?? throw new InvalidOperationException("PHQ participant dropdown was not found."); + var participantSelect = new SelectElement(participantDropdown); + participantSelect.SelectByValue("04"); + + var participantSpecifyInput = driver.WaitforElementToBeInDOM(By.CssSelector(PhqParticipantSpecifyInputSelector), 10) + ?? throw new InvalidOperationException("PHQ participant specify input was not found."); + Assert.True(participantSpecifyInput.Displayed, "PHQ participant specify input should be visible when 'Other' is selected."); + WebElementHelper.SetInputValue(driver, participantSpecifyInput, "Non-family support", "PHQ participant specify input", triggerBlur: true); + + participantSelect.SelectByValue("01"); + Assert.False(participantSpecifyInput.Displayed, "PHQ participant specify input should be hidden when not selecting 'Other'."); + + participantSelect.SelectByValue("02"); + var phqWorkerDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(PhqWorkerDropdownSelector), 10) + ?? throw new InvalidOperationException("PHQ worker dropdown was not found."); + var workerSelect = new SelectElement(phqWorkerDropdown); + workerSelect.SelectByValue("105"); + + _output.WriteLine("[INFO] Verifying PHQ totals remain N/A/Invalid when any question is left unselected."); + SetPhqScores(driver, new[] { "01", "02", "01", "", "01", "02", "", "02", "01" }); + EnsureDifficultyAndReferralCleared(driver); + driver.WaitForReady(1); + Thread.Sleep(300); + WaitForLabelText(driver, PhqResultLabelSelector, text => string.Equals(text, "N/A", StringComparison.OrdinalIgnoreCase)); + WaitForLabelText(driver, PhqScoreValidityLabelSelector, text => text.Contains("Invalid", StringComparison.OrdinalIgnoreCase)); + + var difficultyDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(PhqDifficultyDropdownSelector), 10) + ?? throw new InvalidOperationException("PHQ difficulty dropdown was not found."); + var difficultySelect = new SelectElement(difficultyDropdown); + difficultySelect.SelectByValue("02"); + + var referralCheckbox = driver.WaitforElementToBeInDOM(By.CssSelector(PhqDepressionReferralCheckboxSelector), 10) + ?? throw new InvalidOperationException("PHQ referral checkbox was not found."); + if (!referralCheckbox.Selected) + { + CommonTestHelper.ClickElement(driver, referralCheckbox); + driver.WaitForReady(1); + Thread.Sleep(200); + } + + _output.WriteLine("[INFO] Filling PHQ questions so total exceeds 9 to ensure Positive/Valid."); + SetPhqScores(driver, new[] { "04", "04", "04", "04", "04", "04", "04", "04", "04" }); + driver.WaitForReady(1); + Thread.Sleep(300); + WaitForLabelText(driver, PhqResultLabelSelector, text => text.Contains("Positive", StringComparison.OrdinalIgnoreCase)); + WaitForLabelText(driver, PhqScoreValidityLabelSelector, text => text.Contains("Valid", StringComparison.OrdinalIgnoreCase)); + + SubmitForm(driver, expectValidation: false); + + var toastMessage = WebElementHelper.GetToastMessage(driver, 2000); + Assert.False(string.IsNullOrWhiteSpace(toastMessage), "Success toast message was not displayed after saving PHQ participant and score updates."); + Assert.Contains("Form Saved", toastMessage, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Target Child Identification", toastMessage, StringComparison.OrdinalIgnoreCase); + Assert.Contains(pc1Id, toastMessage, StringComparison.OrdinalIgnoreCase); + _output.WriteLine($"[PASS] PHQ participant and scoring updates saved successfully with toast: {toastMessage}"); + + driver.Navigate().GoToUrl($"{_config.AppUrl}/Pages/TCIDs.aspx?pc1id={pc1Id}"); + driver.WaitForReady(15); + OpenExistingTcidEntry(driver); + SwitchToTab(driver, PhqTabSelector, phqTabTitle); + + participantDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(PhqParticipantDropdownSelector), 10) + ?? throw new InvalidOperationException("PHQ participant dropdown was not found when verifying saved values."); + participantSelect = new SelectElement(participantDropdown); + Assert.Equal("02", participantSelect.SelectedOption.GetAttribute("value")); + + phqWorkerDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(PhqWorkerDropdownSelector), 10) + ?? throw new InvalidOperationException("PHQ worker dropdown was not found when verifying saved values."); + workerSelect = new SelectElement(phqWorkerDropdown); + Assert.Equal("105", workerSelect.SelectedOption.GetAttribute("value")); + + WaitForLabelText(driver, PhqResultLabelSelector, text => text.Contains("Positive", StringComparison.OrdinalIgnoreCase)); + WaitForLabelText(driver, PhqScoreValidityLabelSelector, text => text.Contains("Valid", StringComparison.OrdinalIgnoreCase)); + _output.WriteLine("[PASS] PHQ participant selection, worker, and scores persisted after saving."); + } + + private static DateTime ParseDate(string value, string description) + { + if (DateTime.TryParseExact(value, AcceptedDateFormats, CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsed)) + { + return parsed; + } + + throw new InvalidOperationException($"Unable to parse {description} '{value}'."); + } + + private static void SetPhqDate(IPookieWebDriver driver, IWebElement input, DateTime date) + { + var formatted = date.ToString(DateFormatShortYear, CultureInfo.InvariantCulture); + WebElementHelper.SetInputValue(driver, input, formatted, "PHQ date input", triggerBlur: true); + } + + private static void SetPhqScores(IPookieWebDriver driver, string[] values) + { + var dropdowns = driver.FindElements(By.CssSelector(PhqQuestionDropdownsSelector)); + for (var i = 0; i < dropdowns.Count && i < values.Length; i++) + { + var dropdown = dropdowns[i]; + if (!dropdown.Enabled) + { + continue; + } + + var select = new SelectElement(dropdown); + var value = values[i]; + if (string.IsNullOrWhiteSpace(value)) + { + SelectDropdownPlaceholderOption(dropdown, "PHQ question dropdown"); + continue; + } + + select.SelectByValue(value); + } + } + + private static void ClearPhqScores(IPookieWebDriver driver) + { + var dropdowns = driver.FindElements(By.CssSelector(PhqQuestionDropdownsSelector)); + foreach (var dropdown in dropdowns) + { + if (!dropdown.Enabled) + { + continue; + } + + SelectDropdownPlaceholderOption(dropdown, "PHQ question dropdown"); + } + } + + private void EnsureDifficultyAndReferralCleared(IPookieWebDriver driver) + { + var difficultyDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(PhqDifficultyDropdownSelector), 10) + ?? throw new InvalidOperationException("PHQ difficulty dropdown was not found."); + SelectDropdownPlaceholderOption(difficultyDropdown, "PHQ difficulty dropdown"); + + var referralCheckbox = driver.WaitforElementToBeInDOM(By.CssSelector(PhqDepressionReferralCheckboxSelector), 10) + ?? throw new InvalidOperationException("PHQ referral checkbox was not found."); + if (referralCheckbox.Selected) + { + CommonTestHelper.ClickElement(driver, referralCheckbox); + driver.WaitForReady(1); + Thread.Sleep(200); + } + } + + private static string WaitForLabelText(IPookieWebDriver driver, string selector, Func predicate, int timeoutSeconds = 5) + { + var endTime = DateTime.Now.AddSeconds(timeoutSeconds); + string lastValue = string.Empty; + while (DateTime.Now <= endTime) + { + var element = driver.FindElements(By.CssSelector(selector)).FirstOrDefault(); + if (element != null) + { + lastValue = element.Text?.Trim() ?? string.Empty; + if (predicate(lastValue)) + { + return lastValue; + } + } + + Thread.Sleep(200); + } + + throw new TimeoutException($"Label '{selector}' did not reach expected state within {timeoutSeconds} seconds. Last value '{lastValue}'."); + } + } +} + diff --git a/Pookie.Tests/UnitTests/TargetChildID/TargetChildIDTests.cs b/Pookie.Tests/UnitTests/TargetChildID/TargetChildIDTests.cs new file mode 100644 index 0000000..b9280de --- /dev/null +++ b/Pookie.Tests/UnitTests/TargetChildID/TargetChildIDTests.cs @@ -0,0 +1,921 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading; +using AFUT.Tests.Config; +using AFUT.Tests.Driver; +using AFUT.Tests.Helpers; +using AFUT.Tests.Pages; +using AFUT.Tests.UnitTests.Attributes; +using Microsoft.Extensions.DependencyInjection; +using OpenQA.Selenium; +using OpenQA.Selenium.Support.UI; +using Xunit.Abstractions; + +namespace AFUT.Tests.UnitTests.TargetChildID +{ + [TestCaseOrderer("AFUT.Tests.UnitTests.Attributes.PriorityOrderer", "AFUT.Tests")] + public partial class TargetChildIDTests : IClassFixture + { + private const string TargetChildLinkSelector = + "a.list-group-item.moreInfo[href*='TCIDs.aspx'], " + + "a.moreInfo[data-formtype='TCIBO'], " + + "a.list-group-item[title*='Target Child Information']"; + private const string InfoAlertSelector = "div.alert.alert-info"; + private const string NewTcidButtonSelector = + "div.panel-heading a.btn.btn-default.pull-right[title*='New'], " + + "div.panel-heading a.btn.btn-default[href*='btnSubmit'], " + + "a.btn.btn-default[href*='btnSubmit']"; + private const string SubmitButtonSelector = + "div.panel-footer a.btn.btn-primary, " + + "a.btn.btn-primary[title*='Save'], " + + "a.btn.btn-primary[href*='btnSubmit']"; + private const string ValidationSummarySelector = + "div[id$='ValidationSummary1'], div.validation-summary, ul.validation-summary-errors"; + private const string BirthTermDropdownSelector = "select.form-control[id$='ddlBirthTerm']"; + private const string FirstNameInputSelector = "input.form-control[id$='txtTCFirstName']"; + private const string LastNameInputSelector = "input.form-control[id$='txtTCLastName']"; + private const string ParityDropdownSelector = "select.form-control[id$='ddlParity']"; + private const string GestationalAgeInputSelector = "input.form-control[id$='txtGestationalAge']"; + private const string BirthWeightLbsInputSelector = "input.form-control[id$='txtBirthWtLbs']"; + private const string BirthWeightOzInputSelector = "input.form-control[id$='txtBirthWtOz']"; + private const string MedicaidDropdownSelector = "select.form-control[id$='ddlTCReceivingMedicaid']"; + private const string HealthInsuranceCheckboxesSelector = + "input[id$='chkTCHIFamilyChildHealth'], " + + "input[id$='chkTCHIPrivateInsurance'], " + + "input[id$='chkTCHIOther'], " + + "input[id$='chkTCHIUninsured'], " + + "input[id$='chkTCHIUnknown']"; + private const string HealthInsuranceOtherCheckboxSelector = "input[id$='chkTCHIOther']"; + private const string HealthInsuranceOtherSpecifyInputSelector = "input.form-control[id$='txtTCHIOtherSpecify']"; + private const string HasMedicalProviderDropdownSelector = "select.form-control[id$='ddlTCHasMedicalProvider']"; + private const string MedicalProviderDropdownSelector = "select.form-control[id$='ddlTCMedicalProviderFK']"; + private const string MedicalFacilityDropdownSelector = "select.form-control[id$='ddlTCMedicalFacilityFK']"; + private const string MedicalProviderNotInListLinkSelector = "a[id$='lnkNewMedicalProvider']"; + private const string MedicalProviderModalSelector = ".modal.show .modal-content, .modal.fade.in .modal-content"; + private const string MedicalProviderModalValidationSelector = "div[id$='ctlMedicalProvider_vsMP']"; + private const string MedicalProviderModalSubmitButtonSelector = "a[id$='ctlMedicalProvider_btnSubmitProvider']"; + private const string MedicalProviderModalFirstNameInputSelector = "input[id$='ctlMedicalProvider_txtMPFirstName']"; + private const string MedicalProviderModalLastNameInputSelector = "input[id$='ctlMedicalProvider_txtMPLastName']"; + private const string MedicalProviderModalAddressInputSelector = "input[id$='ctlMedicalProvider_txtMPAddress']"; + private const string MedicalProviderModalStateInputSelector = "input[id$='ctlMedicalProvider_txtMPState']"; + private const string MedicalProviderModalZipInputSelector = "input[id$='ctlMedicalProvider_txtMPZip']"; + private const string MedicalProviderModalPhoneInputSelector = "input[id$='ctlMedicalProvider_txtMPPhone']"; + private const string MedicaidCaseNumberInputSelector = "input.form-control[id$='txtTcHIMedicaidCaseNumber']"; + private const string MedicalFacilityNotInListLinkSelector = "a[id$='lnkNewMedicalFacility']"; + private const string MedicalFacilityModalSelector = ".modal.show .modal-content, .modal.fade.in .modal-content"; + private const string MedicalFacilityModalValidationSelector = "div[id$='ctlMedicalFacility_vsMF']"; + private const string MedicalFacilityModalSubmitButtonSelector = "a[id$='ctlMedicalFacility_btnSubmitFacility']"; + private const string DeliveryTypeDropdownSelector = "select.form-control[id$='ddlDeliveryType']"; + private const string ChildFedBreastMilkDropdownSelector = "select.form-control[id$='ddlChildFedBreastMilk']"; + private const string AdditionalItemsTooltipSelector = "#OptionalItems span.glyphicon.glyphicon-question-sign"; + private const string AdditionalItemsTabSelector = "#OptionalItems"; + private const string PhqTabSelector = "#PHQ9"; + private const string PhqDateInputSelector = "input.form-control[id$='txtPHQDateAdministered']"; + private const string IntakeDateLabelSelector = "span[id$='lblIntakeDate']"; + private const string TargetChildDobLabelSelector = "span[id$='lblTCDOB']"; + private const string PhqParticipantDropdownSelector = "select.form-control[id$='ddlPHQ9Participant']"; + private const string PhqParticipantSpecifyInputSelector = "input.form-control[id$='txtPHQ9ParticipantSpecify']"; + private const string PhqRefusedCheckboxSelector = "input[id$='chkPHQ9Refused']"; + private const string PhqWorkerDropdownSelector = "select.form-control[id$='ddlPHQWorker']"; + private const string PhqScoreLabelSelector = "span[id$='lblPHQ9Score']"; + private const string PhqResultLabelSelector = "span[id$='lblPHQ9Result']"; + private const string PhqScoreValidityLabelSelector = "span[id$='lblPHQ9ScoreValidity']"; + private const string PhqQuestionDropdownsSelector = "#PHQ9 select.phq9score"; + private const string PhqDifficultyDropdownSelector = "select.form-control[id$='ddlDifficulty']"; + private const string PhqDepressionReferralCheckboxSelector = "input[id$='chkDepressionReferralMade']"; + private const string MiechvTabSelector = "#MIECHV"; + private const string MedicalCareSourceDropdownSelector = "select.form-control[id$='ddlTCMedicalCareSource']"; + private const string MedicalCareSourceSpecifyInputSelector = "input.form-control[id$='txtTCMedicalCareSourceOtherSpecify']"; + private const string PrenatalCareInputSelector = + "input.form-control[id*='Prenatal'][type='text'], " + + "input.form-control[name*='Prenatal'], " + + "input.form-control[id*='txtFirstPrenatal'], " + + "input.form-control[id*='txtPrenatalCare']"; + private const string TcidGridSelector = "table.table.table-condensed[id*='grTCIDs'], table[id*='grTCIDs']"; + private const string TcidRowLinkSelector = "td a[href*='tcid.aspx']"; + private static readonly Random Randomizer = new(); + + private readonly AppConfig _config; + private readonly IPookieDriverFactory _driverFactory; + private readonly ITestOutputHelper _output; + + public TargetChildIDTests(AppConfig config, ITestOutputHelper output) + { + _config = config ?? throw new ArgumentNullException(nameof(config)); + _output = output ?? throw new ArgumentNullException(nameof(output)); + + _driverFactory = _config.ServiceProvider.GetService() + ?? throw new InvalidOperationException("Driver factory was not registered in the service provider."); + + CaseHomePage.ConfigureDefaultTabs(_config.CaseHomeTabs); + } + + public static IEnumerable GetTestPc1Ids() + { + var config = new AppConfig(); + return config.TestPc1Ids.Select(id => new object[] { id }); + } + + [Theory] + [MemberData(nameof(GetTestPc1Ids))] + [TestPriority(1)] + public void NavigateToTargetChildInformationForm(string pc1Id) + { + using var driver = _driverFactory.CreateDriver(); + + var (homePage, formsPane) = CommonTestHelper.NavigateToFormsTab(driver, _config, pc1Id); + Assert.NotNull(homePage); + Assert.True(homePage.IsLoaded, "Home page did not load after selecting DataEntry role."); + _output.WriteLine("[PASS] Successfully navigated to Forms tab."); + + NavigateToTargetChildPage(driver, formsPane, pc1Id); + } + + [Theory] + [MemberData(nameof(GetTestPc1Ids))] + [TestPriority(2)] + public void NewTcidButtonDisplaysInfoAlert(string pc1Id) + { + using var driver = _driverFactory.CreateDriver(); + + var (homePage, formsPane) = CommonTestHelper.NavigateToFormsTab(driver, _config, pc1Id); + Assert.NotNull(homePage); + Assert.True(homePage.IsLoaded, "Home page did not load after selecting DataEntry role."); + + NavigateToTargetChildPage(driver, formsPane, pc1Id); + + OpenNewTcidForm(driver); + + var infoAlert = driver.WaitforElementToBeInDOM(By.CssSelector(InfoAlertSelector), 10) + ?? throw new InvalidOperationException("Info alert did not appear after opening New TCID form."); + + var alertText = infoAlert.Text?.Trim() ?? string.Empty; + _output.WriteLine($"[INFO] Info alert text: {alertText}"); + Assert.Contains("Complete this form upon the birth of the target child", alertText, StringComparison.OrdinalIgnoreCase); + Assert.Contains("target child is the newborn", alertText, StringComparison.OrdinalIgnoreCase); + + var validationSummary = driver.FindElements(By.CssSelector("div[id$='ValidationSummary1'], div.validation-summary")) + .FirstOrDefault(); + Assert.NotNull(validationSummary); + _output.WriteLine("[PASS] Validation summary container located on the New TCID form."); + } + + [Theory] + [MemberData(nameof(GetTestPc1Ids))] + [TestPriority(3)] + public void ValidationAndSubmission(string pc1Id) + { + using var driver = _driverFactory.CreateDriver(); + + var (homePage, formsPane) = CommonTestHelper.NavigateToFormsTab(driver, _config, pc1Id); + Assert.NotNull(homePage); + Assert.True(homePage.IsLoaded, "Home page did not load after selecting DataEntry role."); + + NavigateToTargetChildPage(driver, formsPane, pc1Id); + OpenNewTcidForm(driver); + + const string targetChildTab = "#TargetChild"; + const string miechvTab = "#MIECHV"; + const string additionalItemsTab = "#OptionalItems"; + var targetChildFullName = "Gwen"; + var targetChildLastName = "Venom"; + + SwitchToTab(driver, miechvTab, "MIECHV"); + var summaryText = SubmitForm(driver); + Assert.Contains("Term of Birth is required.", summaryText, StringComparison.OrdinalIgnoreCase); + Assert.Equal(2, CountOccurrences(summaryText, "First Name is required!")); + Assert.Contains("Parity is required.", summaryText, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] Initial validation errors displayed."); + + SwitchToTab(driver, targetChildTab, "Target Child"); + SelectRandomDropdownOption(driver, BirthTermDropdownSelector, "Target child birth term"); + SwitchToTab(driver, miechvTab, "MIECHV"); + summaryText = SubmitForm(driver); + Assert.DoesNotContain("Term of Birth is required.", summaryText, StringComparison.OrdinalIgnoreCase); + Assert.Equal(2, CountOccurrences(summaryText, "First Name is required!")); + Assert.Contains("Parity is required.", summaryText, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] Term of Birth validation cleared after selection."); + + SwitchToTab(driver, targetChildTab, "Target Child"); + var firstNameInput = driver.WaitforElementToBeInDOM(By.CssSelector(FirstNameInputSelector), 10) + ?? throw new InvalidOperationException("Target child first name input was not found."); + WebElementHelper.SetInputValue(driver, firstNameInput, targetChildFullName, "Target child first name", triggerBlur: true); + SwitchToTab(driver, miechvTab, "MIECHV"); + summaryText = SubmitForm(driver); + Assert.Equal(1, CountOccurrences(summaryText, "First Name is required!")); + Assert.Contains("Parity is required.", summaryText, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] One First Name validation message cleared after entering first name."); + + SwitchToTab(driver, targetChildTab, "Target Child"); + var lastNameInput = driver.WaitforElementToBeInDOM(By.CssSelector(LastNameInputSelector), 10) + ?? throw new InvalidOperationException("Target child last name input was not found."); + WebElementHelper.SetInputValue(driver, lastNameInput, targetChildLastName, "Target child last name", triggerBlur: true); + SwitchToTab(driver, miechvTab, "MIECHV"); + summaryText = SubmitForm(driver); + Assert.Equal(0, CountOccurrences(summaryText, "First Name is required!")); + Assert.Contains("Parity is required.", summaryText, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] All name validation messages cleared after entering last name."); + + SwitchToTab(driver, additionalItemsTab, "Additional Items"); + SelectRandomDropdownOption(driver, ParityDropdownSelector, "Parity dropdown"); + SwitchToTab(driver, miechvTab, "MIECHV"); + summaryText = SubmitForm(driver); + Assert.DoesNotContain("Parity is required.", summaryText, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Gestational Age is required and must be a valid whole number!", summaryText, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] Parity selection accepted; Gestational Age validation displayed."); + + SwitchToTab(driver, targetChildTab, "Target Child"); + var gestationalInput = driver.WaitforElementToBeInDOM(By.CssSelector(GestationalAgeInputSelector), 10) + ?? throw new InvalidOperationException("Gestational age input was not found."); + WebElementHelper.SetInputValue(driver, gestationalInput, "-1", "Gestational age", triggerBlur: true); + SwitchToTab(driver, miechvTab, "MIECHV"); + summaryText = SubmitForm(driver); + Assert.Contains("Gestation Age must be between 0 and 40 weeks!", summaryText, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] Invalid gestational age triggered range validation."); + + SwitchToTab(driver, targetChildTab, "Target Child"); + WebElementHelper.SetInputValue(driver, gestationalInput, "38", "Gestational age", triggerBlur: true); + _output.WriteLine("[PASS] Set gestational age within valid range without performing a final submit."); + } + + [Theory] + [MemberData(nameof(GetTestPc1Ids))] + [TestPriority(4)] + public void ExistingTcidPrenatalCareValidation(string pc1Id) + { + using var driver = _driverFactory.CreateDriver(); + + var (homePage, formsPane) = CommonTestHelper.NavigateToFormsTab(driver, _config, pc1Id); + Assert.NotNull(homePage); + Assert.True(homePage.IsLoaded, "Home page did not load after selecting DataEntry role."); + + NavigateToTargetChildPage(driver, formsPane, pc1Id); + OpenExistingTcidEntry(driver); + + const string targetChildTab = "#TargetChild"; + const string miechvTab = "#MIECHV"; + + SwitchToTab(driver, targetChildTab, "Target Child"); + SetPrenatalCareDate(driver, "12/01/25"); + + SwitchToTab(driver, miechvTab, "MIECHV"); + var summaryText = SubmitForm(driver); + Assert.Contains("Date Began Receiving Prenatal Care", summaryText, StringComparison.OrdinalIgnoreCase); + Assert.Contains("can not start after the birth of the child", summaryText, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] Prenatal care date validation displayed for future date."); + + SwitchToTab(driver, targetChildTab, "Target Child"); + SetPrenatalCareDate(driver, "11/01/25"); + + SwitchToTab(driver, miechvTab, "MIECHV"); + SubmitForm(driver, expectValidation: false); + + var toastMessage = WebElementHelper.GetToastMessage(driver, 2000); + Assert.False(string.IsNullOrWhiteSpace(toastMessage), "Success toast message was not displayed after saving existing TCID."); + _output.WriteLine($"[INFO] Toast message: {toastMessage}"); + + Assert.Contains("Form Saved", toastMessage, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Target Child Identification", toastMessage, StringComparison.OrdinalIgnoreCase); + Assert.Contains(pc1Id, toastMessage, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] Existing TCID saved successfully after correcting prenatal care date."); + } + + [Theory] + [MemberData(nameof(GetTestPc1Ids))] + [TestPriority(5)] + public void ExistingTcidBirthWeightValidation(string pc1Id) + { + using var driver = _driverFactory.CreateDriver(); + + var (homePage, formsPane) = CommonTestHelper.NavigateToFormsTab(driver, _config, pc1Id); + Assert.NotNull(homePage); + Assert.True(homePage.IsLoaded, "Home page did not load after selecting DataEntry role."); + + NavigateToTargetChildPage(driver, formsPane, pc1Id); + OpenExistingTcidEntry(driver); + + const string targetChildTab = "#TargetChild"; + const string miechvTab = "#MIECHV"; + + ValidateBirthWeight(driver, targetChildTab, miechvTab, "-1", "-2"); + ValidateBirthWeight(driver, targetChildTab, miechvTab, "20", "20"); + ValidateBirthWeight(driver, targetChildTab, miechvTab, "17", "16"); + + var validLbs = Randomizer.Next(0, 17); + var validOz = Randomizer.Next(0, 16); + + SwitchToTab(driver, targetChildTab, "Target Child"); + SetBirthWeight(driver, validLbs.ToString(), validOz.ToString()); + + SwitchToTab(driver, miechvTab, "MIECHV"); + SubmitForm(driver, expectValidation: false); + + var toastMessage = WebElementHelper.GetToastMessage(driver, 2000); + Assert.False(string.IsNullOrWhiteSpace(toastMessage), "Success toast message was not displayed after saving birth weight updates."); + _output.WriteLine($"[INFO] Toast message: {toastMessage}"); + + Assert.Contains("Form Saved", toastMessage, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Target Child Identification", toastMessage, StringComparison.OrdinalIgnoreCase); + Assert.Contains(pc1Id, toastMessage, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] Existing TCID saved successfully after entering valid birth weight."); + } + + [Theory] + [MemberData(nameof(GetTestPc1Ids))] + [TestPriority(6)] + public void HealthInsuranceOptionsRespectMedicaidSelection(string pc1Id) + { + using var driver = _driverFactory.CreateDriver(); + + var (homePage, formsPane) = CommonTestHelper.NavigateToFormsTab(driver, _config, pc1Id); + Assert.NotNull(homePage); + Assert.True(homePage.IsLoaded, "Home page did not load after selecting DataEntry role."); + + NavigateToTargetChildPage(driver, formsPane, pc1Id); + OpenExistingTcidEntry(driver); + + const string healthInsuranceTab = "#HealthInsurance"; + SwitchToTab(driver, healthInsuranceTab, "Health Insurance"); + + var medicaidDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(MedicaidDropdownSelector), 10) + ?? throw new InvalidOperationException("Medicaid dropdown was not found on the Health Insurance tab."); + + _output.WriteLine("[INFO] Selecting 'Yes' for Medicaid."); + WebElementHelper.SelectDropdownOption(driver, medicaidDropdown, "Medicaid dropdown", "Yes", "1"); + EnsureMedicaidCaseNumberInputVisible(driver); + var insuranceCheckboxes = GetHealthInsuranceCheckboxes(driver); + foreach (var checkbox in insuranceCheckboxes) + { + Assert.False(checkbox.Enabled, $"Checkbox '{checkbox.GetAttribute("id")}' should be disabled when Medicaid is 'Yes'."); + } + _output.WriteLine("[PASS] Health insurance options were disabled when Medicaid was set to Yes."); + + _output.WriteLine("[INFO] Selecting 'No' for Medicaid."); + WebElementHelper.SelectDropdownOption(driver, medicaidDropdown, "Medicaid dropdown", "No", "0"); + insuranceCheckboxes = GetHealthInsuranceCheckboxes(driver); + foreach (var checkbox in insuranceCheckboxes) + { + Assert.True(checkbox.Enabled, $"Checkbox '{checkbox.GetAttribute("id")}' should be editable when Medicaid is 'No'."); + } + + var otherCheckbox = GetHealthInsuranceOtherCheckbox(driver); + var otherSpecifyInput = GetHealthInsuranceOtherSpecifyInput(driver); + + if (!otherCheckbox.Selected) + { + CommonTestHelper.ClickElement(driver, otherCheckbox); + driver.WaitForReady(2); + Thread.Sleep(300); + } + + Assert.True(IsElementVisible(otherSpecifyInput), "Health insurance 'Other specify' field should be visible when 'Other' is selected and Medicaid is 'No'."); + _output.WriteLine("[PASS] 'Other specify' text box displayed after selecting Other with Medicaid = No."); + + if (otherCheckbox.Selected) + { + CommonTestHelper.ClickElement(driver, otherCheckbox); + driver.WaitForReady(2); + Thread.Sleep(300); + } + + _output.WriteLine("[INFO] Selecting 'Unknown' for Medicaid."); + WebElementHelper.SelectDropdownOption(driver, medicaidDropdown, "Medicaid dropdown", "Unknown", "9"); + insuranceCheckboxes = GetHealthInsuranceCheckboxes(driver); + foreach (var checkbox in insuranceCheckboxes) + { + Assert.True(checkbox.Enabled, $"Checkbox '{checkbox.GetAttribute("id")}' should be editable when Medicaid is 'Unknown'."); + } + + otherCheckbox = GetHealthInsuranceOtherCheckbox(driver); + otherSpecifyInput = GetHealthInsuranceOtherSpecifyInput(driver); + + if (!otherCheckbox.Selected) + { + CommonTestHelper.ClickElement(driver, otherCheckbox); + driver.WaitForReady(2); + Thread.Sleep(300); + } + + Assert.True(IsElementVisible(otherSpecifyInput), "Health insurance 'Other specify' field should be visible when 'Other' is selected and Medicaid is 'Unknown'."); + _output.WriteLine("[PASS] 'Other specify' text box displayed after selecting Other with Medicaid = Unknown."); + + medicaidDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(MedicaidDropdownSelector), 10) + ?? throw new InvalidOperationException("Medicaid dropdown was not found after finishing Unknown validation."); + _output.WriteLine("[INFO] Selecting 'Yes' for Medicaid to capture case number."); + WebElementHelper.SelectDropdownOption(driver, medicaidDropdown, "Medicaid dropdown", "Yes", "1"); + var medicaidCaseNumberInput = EnsureMedicaidCaseNumberInputVisible(driver); + WebElementHelper.SetInputValue(driver, medicaidCaseNumberInput, "MCN12345", "Medicaid case number", triggerBlur: true); + + var hasMedicalProviderDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(HasMedicalProviderDropdownSelector), 10) + ?? throw new InvalidOperationException("Medical provider question dropdown was not found on the Health Insurance tab."); + + _output.WriteLine("[INFO] Selecting 'No' for medical provider question (Q19)."); + WebElementHelper.SelectDropdownOption(driver, hasMedicalProviderDropdown, "Medical provider availability dropdown", "No", "0"); + AssertMedicalProviderDropdownsEnabledState(driver, shouldBeEnabled: false); + var medicalProviderNotInListLink = GetMedicalProviderNotInListLink(driver); + Assert.True(HasDisabledClass(medicalProviderNotInListLink), "'Not in List' doctor link should be disabled when Q19 is 'No'."); + var medicalFacilityNotInListLink = GetMedicalFacilityNotInListLink(driver); + Assert.True(HasDisabledClass(medicalFacilityNotInListLink), "'Not in List' facility link should be disabled when Q19 is 'No'."); + _output.WriteLine("[PASS] Question 20 dropdowns were disabled when Has Medical Provider = No."); + + hasMedicalProviderDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(HasMedicalProviderDropdownSelector), 10) + ?? throw new InvalidOperationException("Medical provider question dropdown was not found after updating the selection."); + + _output.WriteLine("[INFO] Selecting 'Yes' for medical provider question (Q19)."); + WebElementHelper.SelectDropdownOption(driver, hasMedicalProviderDropdown, "Medical provider availability dropdown", "Yes", "1"); + AssertMedicalProviderDropdownsEnabledState(driver, shouldBeEnabled: true); + medicalProviderNotInListLink = GetMedicalProviderNotInListLink(driver); + Assert.False(HasDisabledClass(medicalProviderNotInListLink), "'Not in List' doctor link should be enabled when Q19 is 'Yes'."); + medicalFacilityNotInListLink = GetMedicalFacilityNotInListLink(driver); + Assert.False(HasDisabledClass(medicalFacilityNotInListLink), "'Not in List' facility link should be enabled when Q19 is 'Yes'."); + + _output.WriteLine("[INFO] Opening the Medical Provider modal via 'Not in List'."); + CommonTestHelper.ClickElement(driver, medicalProviderNotInListLink); + driver.WaitForReady(5); + Thread.Sleep(500); + + var modal = driver.WaitforElementToBeInDOM(By.CssSelector(MedicalProviderModalSelector), 10) + ?? throw new InvalidOperationException("Medical provider modal did not appear after clicking 'Not in List'."); + + var modalFirstNameInput = WebElementHelper.FindElementInModalOrPage(driver, MedicalProviderModalFirstNameInputSelector, "Medical provider first name input", 5); + var modalLastNameInput = WebElementHelper.FindElementInModalOrPage(driver, MedicalProviderModalLastNameInputSelector, "Medical provider last name input", 5); + modalFirstNameInput.Clear(); + modalLastNameInput.Clear(); + + var modalSubmitButton = WebElementHelper.FindElementInModalOrPage(driver, MedicalProviderModalSubmitButtonSelector, "Medical provider modal submit button", 5); + CommonTestHelper.ClickElement(driver, modalSubmitButton); + driver.WaitForReady(5); + Thread.Sleep(300); + + var modalValidation = driver.FindElements(By.CssSelector(MedicalProviderModalValidationSelector)) + .FirstOrDefault(el => el.Displayed && !string.IsNullOrWhiteSpace(el.Text)) + ?? throw new InvalidOperationException("Medical provider validation summary did not appear after submitting without a last name."); + Assert.Contains("Provider's Last Name", modalValidation.Text, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] Medical provider modal displayed validation when last name was missing."); + + var modalAddressInput = WebElementHelper.FindElementInModalOrPage(driver, MedicalProviderModalAddressInputSelector, "Medical provider address input", 5); + var modalStateInput = WebElementHelper.FindElementInModalOrPage(driver, MedicalProviderModalStateInputSelector, "Medical provider state input", 5); + var modalZipInput = WebElementHelper.FindElementInModalOrPage(driver, MedicalProviderModalZipInputSelector, "Medical provider zip input", 5); + var modalPhoneInput = WebElementHelper.FindElementInModalOrPage(driver, MedicalProviderModalPhoneInputSelector, "Medical provider phone input", 5); + + WebElementHelper.SetInputValue(driver, modalFirstNameInput, "testone", "Medical provider first name input", triggerBlur: true); + WebElementHelper.SetInputValue(driver, modalLastNameInput, "testtwo", "Medical provider last name input", triggerBlur: true); + WebElementHelper.SetInputValue(driver, modalAddressInput, "aaaaaaaa", "Medical provider address input", triggerBlur: true); + WebElementHelper.SetInputValue(driver, modalStateInput, "aa", "Medical provider state input", triggerBlur: true); + WebElementHelper.SetInputValue(driver, modalZipInput, "00000", "Medical provider zip input", triggerBlur: true); + WebElementHelper.SetInputValue(driver, modalPhoneInput, "000000000", "Medical provider phone input", triggerBlur: true); + + CommonTestHelper.ClickElement(driver, modalSubmitButton); + driver.WaitForUpdatePanel(20); + driver.WaitForReady(20); + Thread.Sleep(1000); + WaitForElementToDisappear(driver, MedicalProviderModalSelector, 10); + SwitchToTab(driver, healthInsuranceTab, "Health Insurance"); + + var providerDropdownAfterSave = driver.WaitforElementToBeInDOM(By.CssSelector(MedicalProviderDropdownSelector), 10) + ?? throw new InvalidOperationException("Medical provider dropdown was not found after saving the new provider."); + var providerSelectAfterSave = new SelectElement(providerDropdownAfterSave); + var matchingProviderOptions = providerSelectAfterSave.Options + .Where(opt => opt.Text.Contains("testtwo", StringComparison.OrdinalIgnoreCase)) + .ToList(); + Assert.True(matchingProviderOptions.Any(), "Expected at least one provider option containing 'testtwo' after save."); + var selectedOptionValue = providerSelectAfterSave.SelectedOption.GetAttribute("value"); + Assert.Contains(matchingProviderOptions, opt => string.Equals(opt.GetAttribute("value"), selectedOptionValue, StringComparison.OrdinalIgnoreCase)); + _output.WriteLine($"[PASS] Newly added provider '{providerSelectAfterSave.SelectedOption.Text}' appeared in Question 20 and was auto-selected."); + + medicalFacilityNotInListLink = GetMedicalFacilityNotInListLink(driver); + _output.WriteLine("[INFO] Opening the Medical Facility modal via 'Not in List'."); + CommonTestHelper.ClickElement(driver, medicalFacilityNotInListLink); + driver.WaitForReady(5); + Thread.Sleep(500); + + var facilityModal = driver.WaitforElementToBeInDOM(By.CssSelector(MedicalFacilityModalSelector), 10) + ?? throw new InvalidOperationException("Medical facility modal did not appear after clicking 'Not in List'."); + var facilitySubmitButton = WebElementHelper.FindElementInModalOrPage(driver, MedicalFacilityModalSubmitButtonSelector, "Medical facility modal submit button", 5); + CommonTestHelper.ClickElement(driver, facilitySubmitButton); + driver.WaitForReady(5); + Thread.Sleep(300); + + var facilityValidation = driver.FindElements(By.CssSelector(MedicalFacilityModalValidationSelector)) + .FirstOrDefault(el => el.Displayed && !string.IsNullOrWhiteSpace(el.Text)) + ?? throw new InvalidOperationException("Medical facility validation summary did not appear after submitting without data."); + Assert.Contains("Facility", facilityValidation.Text, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] Medical facility modal displayed validation when facility name was missing."); + + var facilityNameInput = FindInputInContainerByIdParts(facilityModal, "Medical facility name input", "MedicalFacility", "Name"); + var facilityAddressInput = FindInputInContainerByIdParts(facilityModal, "Medical facility address input", "MedicalFacility", "Address"); + var facilityCityInput = FindInputInContainerByIdParts(facilityModal, "Medical facility city input", "MedicalFacility", "City"); + var facilityStateInput = FindInputInContainerByIdParts(facilityModal, "Medical facility state input", "MedicalFacility", "State"); + var facilityZipInput = FindInputInContainerByIdParts(facilityModal, "Medical facility zip input", "MedicalFacility", "Zip"); + var facilityPhoneInput = FindInputInContainerByIdParts(facilityModal, "Medical facility phone input", "MedicalFacility", "Phone"); + + WebElementHelper.SetInputValue(driver, facilityNameInput, "avengers", "Medical facility name input", triggerBlur: true); + WebElementHelper.SetInputValue(driver, facilityAddressInput, "aaaa", "Medical facility address input", triggerBlur: true); + WebElementHelper.SetInputValue(driver, facilityCityInput, "aaaaaa", "Medical facility city input", triggerBlur: true); + WebElementHelper.SetInputValue(driver, facilityStateInput, "aa", "Medical facility state input", triggerBlur: true); + WebElementHelper.SetInputValue(driver, facilityZipInput, "00000", "Medical facility zip input", triggerBlur: true); + WebElementHelper.SetInputValue(driver, facilityPhoneInput, "3434343434", "Medical facility phone input", triggerBlur: true); + + var facilitySubmitButtonAfterInputs = WebElementHelper.FindElementInModalOrPage(driver, MedicalFacilityModalSubmitButtonSelector, "Medical facility modal submit button", 5); + CommonTestHelper.ClickElement(driver, facilitySubmitButtonAfterInputs); + driver.WaitForUpdatePanel(20); + driver.WaitForReady(20); + Thread.Sleep(1000); + WaitForElementToDisappear(driver, MedicalFacilityModalSelector, 10); + SwitchToTab(driver, healthInsuranceTab, "Health Insurance"); + + var facilityDropdownAfterSave = driver.WaitforElementToBeInDOM(By.CssSelector(MedicalFacilityDropdownSelector), 10) + ?? throw new InvalidOperationException("Medical facility dropdown was not found after saving the new facility."); + var facilitySelectAfterSave = new SelectElement(facilityDropdownAfterSave); + var matchingFacilityOptions = facilitySelectAfterSave.Options + .Where(opt => opt.Text.Contains("avengers", StringComparison.OrdinalIgnoreCase)) + .ToList(); + Assert.True(matchingFacilityOptions.Any(), "Expected at least one facility option containing 'avengers' after save."); + var selectedFacilityValue = facilitySelectAfterSave.SelectedOption.GetAttribute("value"); + Assert.Contains(matchingFacilityOptions, opt => string.Equals(opt.GetAttribute("value"), selectedFacilityValue, StringComparison.OrdinalIgnoreCase)); + _output.WriteLine($"[PASS] Newly added facility '{facilitySelectAfterSave.SelectedOption.Text}' appeared in Question 20 and was auto-selected."); + + _output.WriteLine("[INFO] Submitting TCID form to persist health insurance updates."); + SubmitForm(driver, expectValidation: false); + + var toastMessage = WebElementHelper.GetToastMessage(driver, 2000); + Assert.False(string.IsNullOrWhiteSpace(toastMessage), "Success toast message was not displayed after saving the TCID form."); + Assert.Contains("Form Saved", toastMessage, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Target Child Identification", toastMessage, StringComparison.OrdinalIgnoreCase); + Assert.Contains(pc1Id, toastMessage, StringComparison.OrdinalIgnoreCase); + _output.WriteLine($"[PASS] TCID form saved successfully with message: {toastMessage}"); + } + + private void NavigateToTargetChildPage(IPookieWebDriver driver, IWebElement formsPane, string pc1Id) + { + var targetChildLink = formsPane.FindElements(By.CssSelector(TargetChildLinkSelector)) + .FirstOrDefault(el => el.Displayed) + ?? throw new InvalidOperationException("Target Child Information and Birth Outcomes link was not found in the Forms tab."); + + _output.WriteLine($"[INFO] Clicking link: {targetChildLink.Text?.Trim()}"); + CommonTestHelper.ClickElement(driver, targetChildLink); + driver.WaitForUpdatePanel(30); + driver.WaitForReady(30); + Thread.Sleep(1000); + + var currentUrl = driver.Url ?? string.Empty; + _output.WriteLine($"[INFO] Current URL: {currentUrl}"); + + Assert.Contains("TCIDs.aspx", currentUrl, StringComparison.OrdinalIgnoreCase); + Assert.Contains($"pc1id={pc1Id}", currentUrl, StringComparison.OrdinalIgnoreCase); + _output.WriteLine("[PASS] Target Child Information page opened with expected PC1 query string."); + } + + private void OpenNewTcidForm(IPookieWebDriver driver) + { + var newTcidButton = driver.FindElements(By.CssSelector(NewTcidButtonSelector)) + .FirstOrDefault(el => el.Displayed && el.Text.Contains("New TCID", StringComparison.OrdinalIgnoreCase)) + ?? throw new InvalidOperationException("New TCID button was not found on the Target Child Information page."); + + _output.WriteLine("[INFO] Clicking New TCID button."); + CommonTestHelper.ClickElement(driver, newTcidButton); + driver.WaitForUpdatePanel(30); + driver.WaitForReady(30); + Thread.Sleep(1000); + } + + private void SwitchToTab(IPookieWebDriver driver, string tabHref, string tabTitle) + { + var tabLink = driver.FindElements(By.CssSelector( + $"ul.nav.nav-pills li a[data-toggle='tab'][href='{tabHref}'], " + + $"ul.nav.nav-pills li a[title*='{tabTitle}']")) + .FirstOrDefault(el => el.Displayed) + ?? throw new InvalidOperationException($"Tab link '{tabTitle}' was not found on the TCID form."); + + _output.WriteLine($"[INFO] Switching to tab: {tabTitle}"); + CommonTestHelper.ClickElement(driver, tabLink); + driver.WaitForReady(5); + Thread.Sleep(300); + + var parentLi = tabLink.FindElements(By.XPath("ancestor::li[1]")).FirstOrDefault(); + if (parentLi != null) + { + Assert.Contains("active", parentLi.GetAttribute("class") ?? string.Empty, StringComparison.OrdinalIgnoreCase); + } + } + + private string SubmitForm(IPookieWebDriver driver, bool expectValidation = true) + { + return SubmitFormCore(driver, expectValidation, switchBackToAdditionalItems: false); + } + + protected string SubmitFormFromAdditionalItemsTab(IPookieWebDriver driver, bool expectValidation = true) + { + return SubmitFormCore(driver, expectValidation, switchBackToAdditionalItems: true); + } + + private string SubmitFormCore(IPookieWebDriver driver, bool expectValidation, bool switchBackToAdditionalItems) + { + var submitButton = driver.FindElements(By.CssSelector(SubmitButtonSelector)) + .FirstOrDefault(el => el.Displayed && el.Text.Contains("Submit", StringComparison.OrdinalIgnoreCase)) + ?? throw new InvalidOperationException("Submit button was not found on the TCID form."); + + CommonTestHelper.ClickElement(driver, submitButton); + driver.WaitForUpdatePanel(30); + driver.WaitForReady(30); + Thread.Sleep(1500); + + if (switchBackToAdditionalItems) + { + TrySwitchBackToAdditionalItems(driver); + } + + if (!expectValidation) + { + return string.Empty; + } + + var validationSummary = driver.FindElements(By.CssSelector(ValidationSummarySelector)) + .FirstOrDefault(el => el.Displayed && !string.IsNullOrWhiteSpace(el.Text)) + ?? throw new InvalidOperationException("Validation summary errors were not displayed after submitting the TCID form."); + + var summaryText = validationSummary.Text?.Trim() ?? string.Empty; + _output.WriteLine($"[INFO] Validation summary: {summaryText}"); + return summaryText; + } + + private void SelectRandomDropdownOption(IPookieWebDriver driver, string selector, string description) + { + var dropdown = WebElementHelper.FindElementInModalOrPage(driver, selector, description, 10); + var select = new SelectElement(dropdown); + var validOptions = select.Options + .Where(opt => !string.IsNullOrWhiteSpace(opt.GetAttribute("value"))) + .ToList(); + + if (!validOptions.Any()) + { + throw new InvalidOperationException($"No selectable options were found for {description}."); + } + + var randomOption = validOptions[Randomizer.Next(validOptions.Count)]; + var optionText = randomOption.Text?.Trim() ?? randomOption.GetAttribute("value") ?? string.Empty; + var optionValue = randomOption.GetAttribute("value"); + WebElementHelper.SelectDropdownOption(driver, dropdown, description, optionText, optionValue); + _output.WriteLine($"[INFO] Selected {description}: {optionText} ({optionValue})"); + } + + private void OpenExistingTcidEntry(IPookieWebDriver driver) + { + var grid = driver.WaitforElementToBeInDOM(By.CssSelector(TcidGridSelector), 20) + ?? throw new InvalidOperationException("TCID grid was not found on the Target Child Information page."); + + var entryLink = grid.FindElements(By.CssSelector(TcidRowLinkSelector)) + .FirstOrDefault(link => link.Displayed && !string.IsNullOrWhiteSpace(link.Text)) + ?? throw new InvalidOperationException("No TCID link was available to open."); + + var linkText = entryLink.Text?.Trim() ?? "(unnamed)"; + _output.WriteLine($"[INFO] Opening existing TCID entry: {linkText}"); + + CommonTestHelper.ClickElement(driver, entryLink); + driver.WaitForReady(30); + driver.WaitForUpdatePanel(30); + Thread.Sleep(1000); + + var currentUrl = driver.Url ?? string.Empty; + Assert.Contains("tcid.aspx", currentUrl, StringComparison.OrdinalIgnoreCase); + Assert.Contains("tcpk=", currentUrl, StringComparison.OrdinalIgnoreCase); + _output.WriteLine($"[PASS] Navigated to existing TCID form: {currentUrl}"); + } + + private void SetPrenatalCareDate(IPookieWebDriver driver, string dateValue) + { + var prenatalInput = driver.FindElements(By.CssSelector(PrenatalCareInputSelector)) + .FirstOrDefault(el => el.Displayed) + ?? throw new InvalidOperationException("Prenatal care date input was not found on the TCID form."); + + WebElementHelper.SetInputValue(driver, prenatalInput, dateValue, "First Prenatal Care Visit date", triggerBlur: true); + _output.WriteLine($"[INFO] Set prenatal care date to {dateValue}"); + } + + private void ValidateBirthWeight(IPookieWebDriver driver, string targetTab, string miechvTab, string pounds, string ounces) + { + SwitchToTab(driver, targetTab, "Target Child"); + SetBirthWeight(driver, pounds, ounces); + + SwitchToTab(driver, miechvTab, "MIECHV"); + var summaryText = SubmitForm(driver); + + Assert.Contains("Birth weight pounds must be less than 17", summaryText, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Birth weight ounces must be less than 16", summaryText, StringComparison.OrdinalIgnoreCase); + _output.WriteLine($"[PASS] Birth weight validation displayed for values lbs={pounds}, oz={ounces}"); + } + + private void SetBirthWeight(IPookieWebDriver driver, string pounds, string ounces) + { + var lbsInput = driver.FindElements(By.CssSelector(BirthWeightLbsInputSelector)) + .FirstOrDefault(el => el.Displayed) + ?? throw new InvalidOperationException("Birth weight pounds input was not found."); + + var ozInput = driver.FindElements(By.CssSelector(BirthWeightOzInputSelector)) + .FirstOrDefault(el => el.Displayed) + ?? throw new InvalidOperationException("Birth weight ounces input was not found."); + + WebElementHelper.SetInputValue(driver, lbsInput, pounds, "Birth weight pounds", triggerBlur: true); + WebElementHelper.SetInputValue(driver, ozInput, ounces, "Birth weight ounces", triggerBlur: true); + _output.WriteLine($"[INFO] Set birth weight to {pounds} lbs {ounces} oz"); + } + + private static int CountOccurrences(string text, string value) + { + if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(value)) + { + return 0; + } + + return Regex.Matches(text, Regex.Escape(value), RegexOptions.IgnoreCase).Count; + } + + private static IReadOnlyList GetHealthInsuranceCheckboxes(IPookieWebDriver driver) + { + var checkboxes = driver.FindElements(By.CssSelector(HealthInsuranceCheckboxesSelector)) + .Where(el => el.Displayed) + .ToList(); + + if (!checkboxes.Any()) + { + throw new InvalidOperationException("Health insurance checkboxes were not found on the TCID form."); + } + + return checkboxes; + } + + private static IWebElement GetHealthInsuranceOtherCheckbox(IPookieWebDriver driver) + { + return driver.FindElements(By.CssSelector(HealthInsuranceOtherCheckboxSelector)) + .FirstOrDefault(el => el.Displayed) + ?? throw new InvalidOperationException("Health insurance 'Other' checkbox was not found on the TCID form."); + } + + private static IWebElement GetHealthInsuranceOtherSpecifyInput(IPookieWebDriver driver) + { + return driver.FindElements(By.CssSelector(HealthInsuranceOtherSpecifyInputSelector)) + .FirstOrDefault() + ?? throw new InvalidOperationException("Health insurance 'Other specify' input was not found on the TCID form."); + } + + private static bool IsElementVisible(IWebElement element) + { + if (element == null) + { + return false; + } + + try + { + return element.Displayed && !string.Equals(element.GetCssValue("display"), "none", StringComparison.OrdinalIgnoreCase); + } + catch (StaleElementReferenceException) + { + return false; + } + } + + private void AssertMedicalProviderDropdownsEnabledState(IPookieWebDriver driver, bool shouldBeEnabled) + { + var providerDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(MedicalProviderDropdownSelector), 10) + ?? throw new InvalidOperationException("Medical provider dropdown was not found on the Health Insurance tab."); + var facilityDropdown = driver.WaitforElementToBeInDOM(By.CssSelector(MedicalFacilityDropdownSelector), 10) + ?? throw new InvalidOperationException("Medical facility dropdown was not found on the Health Insurance tab."); + + AssertDropdownEnabledState(providerDropdown, shouldBeEnabled, "Medical provider dropdown"); + AssertDropdownEnabledState(facilityDropdown, shouldBeEnabled, "Medical facility dropdown"); + } + + private static void AssertDropdownEnabledState(IWebElement dropdown, bool shouldBeEnabled, string description) + { + var disabledAttr = dropdown.GetAttribute("disabled"); + if (shouldBeEnabled) + { + Assert.True(dropdown.Enabled && string.IsNullOrWhiteSpace(disabledAttr), $"{description} should be enabled."); + } + else + { + var isDisabled = !dropdown.Enabled || + string.Equals(disabledAttr, "true", StringComparison.OrdinalIgnoreCase) || + string.Equals(disabledAttr, "disabled", StringComparison.OrdinalIgnoreCase); + Assert.True(isDisabled, $"{description} should be disabled."); + } + } + + private static IWebElement GetMedicalProviderNotInListLink(IPookieWebDriver driver) + { + return driver.FindElements(By.CssSelector(MedicalProviderNotInListLinkSelector)) + .FirstOrDefault(el => el.Displayed) + ?? throw new InvalidOperationException("'Not in List' medical provider link was not found on the Health Insurance tab."); + } + + private static bool HasDisabledClass(IWebElement element) + { + var classAttr = element.GetAttribute("class") ?? string.Empty; + return classAttr + .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) + .Any(c => c.Equals("disabled", StringComparison.OrdinalIgnoreCase)); + } + + private static void WaitForElementToDisappear(IPookieWebDriver driver, string cssSelector, int timeoutSeconds = 10) + { + var endTime = DateTime.Now.AddSeconds(timeoutSeconds); + while (DateTime.Now <= endTime) + { + var visibleElement = driver.FindElements(By.CssSelector(cssSelector)) + .FirstOrDefault(el => el.Displayed); + if (visibleElement == null) + { + return; + } + + Thread.Sleep(200); + } + + throw new TimeoutException($"Element '{cssSelector}' did not disappear within {timeoutSeconds} seconds."); + } + + private static IWebElement GetMedicalFacilityNotInListLink(IPookieWebDriver driver) + { + return driver.FindElements(By.CssSelector(MedicalFacilityNotInListLinkSelector)) + .FirstOrDefault(el => el.Displayed) + ?? throw new InvalidOperationException("'Not in List' medical facility link was not found on the Health Insurance tab."); + } + + private IWebElement EnsureMedicaidCaseNumberInputVisible(IPookieWebDriver driver) + { + var medicaidCaseInput = driver.FindElements(By.CssSelector(MedicaidCaseNumberInputSelector)) + .FirstOrDefault() + ?? throw new InvalidOperationException("Medicaid case number input was not found on the Health Insurance tab."); + + Assert.True(IsElementVisible(medicaidCaseInput), "Medicaid case number input should be visible when Medicaid is 'Yes'."); + return medicaidCaseInput; + } + + private static IWebElement FindInputInContainerByIdParts(IWebElement container, string description, params string[] idParts) + { + var inputs = container.FindElements(By.CssSelector("input")); + foreach (var input in inputs) + { + var inputId = input.GetAttribute("id") ?? string.Empty; + if (idParts.All(part => inputId.IndexOf(part, StringComparison.OrdinalIgnoreCase) >= 0)) + { + return input; + } + } + + throw new InvalidOperationException($"Unable to locate {description}."); + } + + private static void SelectDropdownPlaceholderOption(IWebElement dropdown, string description) + { + var selectElement = new SelectElement(dropdown); + var placeholderOption = selectElement.Options.FirstOrDefault(opt => string.IsNullOrWhiteSpace(opt.GetAttribute("value"))); + if (placeholderOption == null) + { + throw new InvalidOperationException($"No placeholder option was available for {description}."); + } + + try + { + selectElement.SelectByValue(string.Empty); + } + catch (NoSuchElementException) + { + placeholderOption.Click(); + } + } + + private void TrySwitchBackToAdditionalItems(IPookieWebDriver driver) + { + try + { + var tabLink = driver.FindElements(By.CssSelector( + $"ul.nav.nav-pills li a[data-toggle='tab'][href='{AdditionalItemsTabSelector}'], " + + $"ul.nav.nav-pills li a[title*='Additional Items']")) + .FirstOrDefault(el => el.Displayed); + + if (tabLink != null) + { + CommonTestHelper.ClickElement(driver, tabLink); + driver.WaitForReady(5); + Thread.Sleep(300); + } + } + catch (Exception ex) + { + _output.WriteLine($"[WARN] Unable to switch back to Additional Items tab automatically: {ex.Message}"); + } + } + + protected void NavigateBackToExistingTcid(IPookieWebDriver driver, string pc1Id) + { + driver.Navigate().GoToUrl($"{_config.AppUrl}/Pages/TCIDs.aspx?pc1id={pc1Id}"); + driver.WaitForReady(15); + OpenExistingTcidEntry(driver); + } + } +} +