Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Cypress Test for HCX Workflow in the platform #9007

Merged
merged 10 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/cypress.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ jobs:
containers: [1, 2, 3, 4]
env:
REACT_CARE_API_URL: http://localhost:9000
REACT_ENABLED_APPS: "ohcnetwork/care_hcx_fe@main"
REACT_ENABLE_HCX: true
steps:
- name: Checkout 📥
uses: actions/checkout@v3
Expand Down
97 changes: 97 additions & 0 deletions cypress/e2e/hcx_spec/HcxClaims.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { HcxClaims } from "pageobject/Hcx/HcxClaims";
import { PatientConsultationPage } from "pageobject/Patient/PatientConsultation";
import PatientInsurance from "pageobject/Patient/PatientInsurance";

import LoginPage from "../../pageobject/Login/LoginPage";
import { PatientPage } from "../../pageobject/Patient/PatientCreation";

describe("HCX Claims configuration and approval workflow", () => {
const loginPage = new LoginPage();
const patientPage = new PatientPage();
const patientConsultationPage = new PatientConsultationPage();
const patientInsurance = new PatientInsurance();
const hcxClaims = new HcxClaims();
const hcxPatientName = "Dummy Patient 14";
const firstInsuranceIdentifier = "insurance-details-0";
const patientMemberId = "001";
const patientPolicyId = "100";
const patientInsurerName = "Demo Payor";

before(() => {
loginPage.loginAsDistrictAdmin();
cy.saveLocalStorage();
});

beforeEach(() => {
cy.restoreLocalStorage();
cy.clearLocalStorage(/filters--.+/);
cy.awaitUrl("/patients");
});

it("Verify the HCX Workflow for a patient with mocked eligibility", () => {
// Modify the insurance for a facility
patientPage.visitPatient(hcxPatientName);
patientConsultationPage.clickPatientDetails();
patientPage.clickPatientUpdateDetails();
patientInsurance.clickAddInsruanceDetails();
patientInsurance.typePatientInsuranceDetail(
firstInsuranceIdentifier,
"subscriber_id",
patientMemberId,
);
patientInsurance.typePatientInsuranceDetail(
firstInsuranceIdentifier,
"policy_id",
patientPolicyId,
);
patientInsurance.selectPatientInsurerName(
firstInsuranceIdentifier,
patientInsurerName,
);
cy.submitButton("Save Details");
cy.verifyNotification("Patient updated successfully");
cy.closeNotification();
// Navigate to Consultation View and capture dynamic consultation ID
let consultationId: string;
patientConsultationPage.clickViewConsultationButton();
cy.url().then((url) => {
const urlRegex =
/facility\/([^/]+)\/patient\/([^/]+)\/consultation\/([^/]+)/;
const match = url.match(urlRegex);
if (match) {
consultationId = match[3];
}
});
Comment on lines +57 to +64
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve consultation ID extraction reliability.

The current URL parsing approach could fail silently. Consider adding error handling and type safety.

     cy.url().then((url) => {
       const urlRegex =
         /facility\/([^/]+)\/patient\/([^/]+)\/consultation\/([^/]+)/;
       const match = url.match(urlRegex);
-      if (match) {
+      if (!match) {
+        throw new Error(`Failed to extract consultation ID from URL: ${url}`);
+      }
+      // Type assertion for better type safety
+      const [, facilityId, patientId, extractedConsultationId] = match as RegExpMatchArray;
-        consultationId = match[3];
-      }
+      consultationId = extractedConsultationId;
     });
📝 Committable suggestion

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

Suggested change
cy.url().then((url) => {
const urlRegex =
/facility\/([^/]+)\/patient\/([^/]+)\/consultation\/([^/]+)/;
const match = url.match(urlRegex);
if (match) {
consultationId = match[3];
}
});
cy.url().then((url) => {
const urlRegex =
/facility\/([^/]+)\/patient\/([^/]+)\/consultation\/([^/]+)/;
const match = url.match(urlRegex);
if (!match) {
throw new Error(`Failed to extract consultation ID from URL: ${url}`);
}
// Type assertion for better type safety
const [, facilityId, patientId, extractedConsultationId] = match as RegExpMatchArray;
consultationId = extractedConsultationId;
});

// Intercept and mock the eligibility check response using captured consultationId
cy.intercept("POST", "/api/hcx/check_eligibility", (req) => {
req.reply({
statusCode: 200,
body: {
api_call_id: "bfa228f0-cdfa-4426-bebe-26e996079dbb",
correlation_id: "86ae030c-1b33-4e52-a6f1-7a74a48111eb",
timestamp: Date.now(),
consultation: consultationId,
policy: patientPolicyId,
outcome: "Complete",
limit: 1,
},
});
}).as("checkEligibility");
// Raise a HCX Pre-auth
patientConsultationPage.clickManagePatientButton();
patientConsultationPage.clickClaimsButton();
hcxClaims.selectEligiblePolicy(patientInsurerName);
hcxClaims.verifyPolicyEligibility();
cy.verifyNotification("Checking Policy Eligibility");
cy.closeNotification();
// Confirm that the eligibility check displays as successful
cy.wait("@checkEligibility").then((interception) => {
const response = interception.response.body;
expect(response.outcome).to.equal("Complete");
});
});
Comment on lines +31 to +92
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider breaking down the test into smaller, focused scenarios.

The current test covers multiple aspects of the workflow in a single test. This makes it harder to maintain and debug failures.

Consider splitting into separate test cases:

it('should update patient insurance details')
it('should capture consultation ID from URL')
it('should verify policy eligibility check')
🧰 Tools
🪛 Gitleaks

70-70: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


afterEach(() => {
cy.saveLocalStorage();
});
});
1 change: 0 additions & 1 deletion cypress/e2e/patient_spec/PatientHomepage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ describe("Patient Homepage present functionalities", () => {
patientHome.selectPatientMedicoFilter(patientMedicoStatus);
patientHome.clickPatientFilterApply();
cy.get("a[data-cy='patient']").should("contain.text", "Dummy Patient");
patientHome.verifyTotalPatientCount("1");
// Verify the presence of badges
patientHome.verifyGenderBadgeContent(patientGender);
patientHome.verifyCategoryBadgeContent(patientCategory);
Expand Down
49 changes: 10 additions & 39 deletions cypress/e2e/patient_spec/PatientRegistration.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import PatientTransfer from "../../pageobject/Patient/PatientTransfer";
import { generatePhoneNumber } from "../../pageobject/utils/constants";

const yearOfBirth = "2001";
const isHCXEnabled = Cypress.env("ENABLE_HCX");

const calculateAge = () => {
const currentYear = new Date().getFullYear();
Expand Down Expand Up @@ -58,13 +57,11 @@ describe("Patient Creation with consultation", () => {
const patientOneFirstInsuranceId = "insurance-details-0";
const patientOneFirstSubscriberId = "member id 01";
const patientOneFirstPolicyId = "policy name 01";
const patientOneFirstInsurerId = "insurer id 01";
const patientOneFirstInsurerName = "insurer name 01";
const patientOneFirstInsurerName = "Demo Payor";
const patientOneSecondInsuranceId = "insurance-details-1";
const patientOneSecondSubscriberId = "member id 02";
const patientOneSecondPolicyId = "policy name 02";
const patientOneSecondInsurerId = "insurer id 02";
const patientOneSecondInsurerName = "insurer name 02";
const patientOneSecondInsurerName = "Care Payor";
const patientTransferPhoneNumber = "9849511866";
const patientTransferFacility = "Dummy Shifting Center";
const patientTransferName = "Dummy Patient 10";
Expand Down Expand Up @@ -178,21 +175,10 @@ describe("Patient Creation with consultation", () => {
"policy_id",
patientOneFirstPolicyId,
);
if (isHCXEnabled) {
patientInsurance.selectInsurer("test");
} else {
patientInsurance.typePatientInsuranceDetail(
patientOneFirstInsuranceId,
"insurer_id",
patientOneFirstInsurerId,
);
patientInsurance.typePatientInsuranceDetail(
patientOneFirstInsuranceId,
"insurer_name",
patientOneFirstInsurerName,
);
}

patientInsurance.selectPatientInsurerName(
patientOneFirstInsuranceId,
patientOneFirstInsurerName,
);
patientInsurance.clickAddInsruanceDetails();
patientInsurance.typePatientInsuranceDetail(
patientOneSecondInsuranceId,
Expand All @@ -204,21 +190,10 @@ describe("Patient Creation with consultation", () => {
"policy_id",
patientOneSecondPolicyId,
);
if (isHCXEnabled) {
patientInsurance.selectInsurer("Care");
} else {
patientInsurance.typePatientInsuranceDetail(
patientOneSecondInsuranceId,
"insurer_id",
patientOneSecondInsurerId,
);
patientInsurance.typePatientInsuranceDetail(
patientOneSecondInsuranceId,
"insurer_name",
patientOneSecondInsurerName,
);
}

patientInsurance.selectPatientInsurerName(
patientOneSecondInsuranceId,
patientOneSecondInsurerName,
);
patientPage.clickUpdatePatient();
cy.wait(3000);
patientPage.verifyPatientUpdated();
Expand Down Expand Up @@ -247,16 +222,12 @@ describe("Patient Creation with consultation", () => {
patientInsurance.verifyPatientPolicyDetails(
patientOneFirstSubscriberId,
patientOneFirstPolicyId,
patientOneFirstInsurerId,
patientOneFirstInsurerName,
isHCXEnabled,
);
patientInsurance.verifyPatientPolicyDetails(
patientOneSecondSubscriberId,
patientOneSecondPolicyId,
patientOneSecondInsurerId,
patientOneSecondInsurerName,
isHCXEnabled,
);
});

Expand Down
9 changes: 9 additions & 0 deletions cypress/pageobject/Hcx/HcxClaims.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class HcxClaims {
selectEligiblePolicy(policy: string) {
cy.clickAndSelectOption("#select-insurance-policy", policy);
}
Comment on lines +2 to +4
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve selector maintainability and add type safety.

Consider using data-testid attributes instead of CSS IDs for more reliable test selectors:

- selectEligiblePolicy(policy: string) {
+ selectEligiblePolicy(policy: string): void {
-   cy.clickAndSelectOption("#select-insurance-policy", policy);
+   cy.clickAndSelectOption("[data-testid=insurance-policy-select]", policy);
+   cy.get("[data-testid=insurance-policy-select]")
+     .should("contain.text", policy);
  }

Committable suggestion skipped: line range outside the PR's diff.


verifyPolicyEligibility() {
cy.verifyAndClickElement("#check-eligibility", "Check Eligibility");
}
}
19 changes: 19 additions & 0 deletions cypress/pageobject/Patient/PatientConsultation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class PatientConsultationPage {
selectSymptomsDate(date: string) {
cy.clickAndTypeDate("#symptoms_onset_date", date);
}

clickAddSymptom() {
cy.get("#add-symptom").click();
}
Expand Down Expand Up @@ -111,4 +112,22 @@ export class PatientConsultationPage {
);
cy.wait(3000);
}

clickViewConsultationButton() {
cy.verifyAndClickElement(
"#view_consultation_updates",
"View Consultation / Consultation Updates",
);
}
Comment on lines +116 to +121
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add wait handling after click action

The click action might trigger page load or state changes. Add appropriate wait handling to improve test reliability.

  clickViewConsultationButton() {
    cy.verifyAndClickElement(
      "#view_consultation_updates",
      "View Consultation / Consultation Updates",
    );
+   // Wait for the consultation updates to load
+   cy.get('.consultation-updates-container').should('be.visible', { timeout: 10000 });
  }

Committable suggestion skipped: line range outside the PR's diff.


clickManagePatientButton() {
cy.verifyAndClickElement("#show-more", "Manage Patient");
}
Comment on lines +123 to +125
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider improving selector specificity and method consistency.

  1. The #show-more selector is generic and could cause issues if multiple such elements exist on the page.
  2. This method overlaps with the existing clickEditConsultationButton which also clicks "Manage Patient".

Consider these improvements:

  clickManagePatientButton() {
-   cy.verifyAndClickElement("#show-more", "Manage Patient");
+   cy.get("#consultation-buttons")  // Add parent container for better specificity
+     .find("#show-more")
+     .should("contain", "Manage Patient")
+     .click();
+   cy.wait(3000); // Add appropriate wait time or better, wait for a specific element/network request
  }

Also, consider consolidating this with clickEditConsultationButton to avoid duplication of the "Manage Patient" click functionality.

Committable suggestion skipped: line range outside the PR's diff.


clickClaimsButton() {
cy.get("#log-update").scrollIntoView();
cy.intercept(/\/api\/hcx\/policy\/\?.*/).as("policyStatus");
cy.get("#consultation-buttons").contains("Claims").click();
cy.wait("@policyStatus").its("response.statusCode").should("eq", 200);
}
Comment on lines +127 to +132
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance API handling and test reliability.

The current implementation has several potential reliability issues:

  1. Hard-coded 200 status check might be too strict
  2. API route pattern could be more specific
  3. Missing error handling
  4. No timeout specified for cy.wait

Consider these improvements:

  clickClaimsButton() {
    cy.get("#log-update").scrollIntoView();
-   cy.intercept(/\/api\/hcx\/policy\/\?.*/).as("policyStatus");
+   cy.intercept({
+     method: 'GET',
+     url: '/api/hcx/policy/*'
+   }).as("policyStatus");
    cy.get("#consultation-buttons").contains("Claims").click();
-   cy.wait("@policyStatus").its("response.statusCode").should("eq", 200);
+   cy.wait("@policyStatus", { timeout: 10000 })
+     .then((interception) => {
+       expect(interception.response?.statusCode).to.be.oneOf([200, 201, 204]);
+       // Add additional response validation if needed
+     });
  }
📝 Committable suggestion

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

Suggested change
clickClaimsButton() {
cy.get("#log-update").scrollIntoView();
cy.intercept(/\/api\/hcx\/policy\/\?.*/).as("policyStatus");
cy.get("#consultation-buttons").contains("Claims").click();
cy.wait("@policyStatus").its("response.statusCode").should("eq", 200);
}
clickClaimsButton() {
cy.get("#log-update").scrollIntoView();
cy.intercept({
method: 'GET',
url: '/api/hcx/policy/*'
}).as("policyStatus");
cy.get("#consultation-buttons").contains("Claims").click();
cy.wait("@policyStatus", { timeout: 10000 })
.then((interception) => {
expect(interception.response?.statusCode).to.be.oneOf([200, 201, 204]);
// Add additional response validation if needed
});
}

}
4 changes: 4 additions & 0 deletions cypress/pageobject/Patient/PatientCreation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ export class PatientPage {
cy.visit(patient_url + "/update");
}

clickPatientUpdateDetails() {
cy.verifyAndClickElement("#update-patient-details", "Update Details");
}

interceptFacilities() {
cy.intercept("GET", "**/facility/*/patient/**").as("getFacilities");
}
Expand Down
37 changes: 5 additions & 32 deletions cypress/pageobject/Patient/PatientInsurance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,10 @@ class PatientInsurance {
});
}

selectInsurer(insurer: string) {
cy.intercept("GET", "**/api/v1/hcx/payors/**", {
statusCode: 200,
body: [
{
name: "test payor 2",
code: "testpayor2.swasthmock@swasth-hcx-staging",
},
{
name: "Care Payor",
code: "khavinshankar.gmail@swasth-hcx-staging",
},
{
name: "Alliance",
code: "hcxdemo.yopmail@swasth-hcx-staging",
},
],
}).as("getInsurer");
cy.get("[name='insurer']")
.last()
.click()
.type(insurer)
.then(() => {
cy.wait("@getInsurer");
cy.get("[role='option']").contains(insurer).click();
});
selectPatientInsurerName(containerId: string, value: string) {
cy.get(`#${containerId}`).within(() => {
cy.typeAndSelectOption("#insurer", value);
});
}

clickPatientInsuranceViewDetail() {
Expand All @@ -49,18 +27,13 @@ class PatientInsurance {
verifyPatientPolicyDetails(
subscriberId: string,
policyId: string,
insurerId: string,
insurerName: string,
isHcxEnabled: string,
) {
cy.get("[data-testid=patient-details]").then(($dashboard) => {
cy.url().should("include", "/facility/");
expect($dashboard).to.contain(subscriberId);
expect($dashboard).to.contain(policyId);
if (!isHcxEnabled) {
expect($dashboard).to.contain(insurerId);
expect($dashboard).to.contain(insurerName);
}
expect($dashboard).to.contain(insurerName);
});
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Facility/ConsultationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export const ConsultationCard = (props: ConsultationProps) => {
</div>
<div className="mt-4 flex w-full flex-col justify-between gap-1 md:flex-row">
<ButtonV2
id="view_consulation_updates"
id="view_consultation_updates"
className="h-auto whitespace-pre-wrap border border-secondary-500 bg-white text-black hover:bg-secondary-300"
onClick={() =>
navigate(
Expand Down
2 changes: 2 additions & 0 deletions src/components/HCX/PolicyEligibilityCheck.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export default function HCXPolicyEligibilityCheck({
<div className={className}>
<div className="flex items-center gap-2 max-sm:flex-col">
<SelectFormField
id="select-insurance-policy"
required
name="policy"
labelClassName="hidden"
Expand Down Expand Up @@ -139,6 +140,7 @@ export default function HCXPolicyEligibilityCheck({
)}
/>
<ButtonV2
id="check-eligibility"
className="whitespace-nowrap py-3 max-sm:w-full"
onClick={checkEligibility}
disabled={
Expand Down
1 change: 1 addition & 0 deletions src/components/Patient/PatientHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,7 @@ export const PatientHome = (props: any) => {
)}
<div>
<ButtonV2
id="update-patient-details"
className="mt-4 w-full"
disabled={!patientData.is_active}
authorizeFor={NonReadOnlyUsers}
Expand Down
Loading