From 723bef3fcdf966c219e8a9402d8305233f959847 Mon Sep 17 00:00:00 2001 From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com> Date: Wed, 27 Dec 2023 00:16:12 +0530 Subject: [PATCH 1/5] Add video connect link to user profile --- .../Facility/DoctorVideoSlideover.tsx | 14 ++++++ src/Components/Users/UserProfile.tsx | 43 ++++++++++++++++++- src/Components/Users/models.tsx | 2 + src/Utils/utils.ts | 9 ++++ 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/Components/Facility/DoctorVideoSlideover.tsx b/src/Components/Facility/DoctorVideoSlideover.tsx index 5302f7f9d53..053e0ec8fad 100644 --- a/src/Components/Facility/DoctorVideoSlideover.tsx +++ b/src/Components/Facility/DoctorVideoSlideover.tsx @@ -152,6 +152,20 @@ function UserListItem(props: { user: UserAssignedModel }) { {user.first_name} {user.last_name}
+ {user.video_connect_link && ( + +
+ + Connect on a Video Call + + +
+
+ )}
+ )} @@ -679,6 +714,12 @@ export default function UserProfile() { min={0} max={168} /> +
diff --git a/src/Components/Users/models.tsx b/src/Components/Users/models.tsx index 6fa0bfb3e42..a6cd0d7c8ff 100644 --- a/src/Components/Users/models.tsx +++ b/src/Components/Users/models.tsx @@ -28,6 +28,7 @@ export type UserModel = UserBareMinimum & { local_body?: number; district?: number; state?: number; + video_connect_link: string; phone_number?: string; alt_phone_number?: string; gender?: GenderType; @@ -62,6 +63,7 @@ export interface UserAssignedModel extends UserBareMinimum { state?: number; phone_number?: string; alt_phone_number?: string; + video_connect_link: string; gender?: number; age?: number; is_superuser?: boolean; diff --git a/src/Utils/utils.ts b/src/Utils/utils.ts index df6bd46d131..c518bcffe7d 100644 --- a/src/Utils/utils.ts +++ b/src/Utils/utils.ts @@ -462,3 +462,12 @@ export const compareBy = (key: keyof T) => { return a[key] < b[key] ? -1 : a[key] > b[key] ? 1 : 0; }; }; + +export const isValidUrl = (url?: string) => { + try { + new URL(url ?? ""); + return true; + } catch { + return false; + } +}; From 4efe08ccaffbe64cc59bc07e7bd9dcfed9608cf2 Mon Sep 17 00:00:00 2001 From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com> Date: Wed, 27 Dec 2023 12:55:33 +0530 Subject: [PATCH 2/5] Add useAuthUser hook and triggerGoal function --- .../Facility/DoctorVideoSlideover.tsx | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Components/Facility/DoctorVideoSlideover.tsx b/src/Components/Facility/DoctorVideoSlideover.tsx index 053e0ec8fad..092ebfce69a 100644 --- a/src/Components/Facility/DoctorVideoSlideover.tsx +++ b/src/Components/Facility/DoctorVideoSlideover.tsx @@ -6,6 +6,8 @@ import { UserAssignedModel } from "../Users/models"; import { SkillObjectModel } from "../Users/models"; import CareIcon from "../../CAREUI/icons/CareIcon"; import { relativeTime } from "../../Utils/utils"; +import useAuthUser from "../../Common/hooks/useAuthUser"; +import { triggerGoal } from "../../Integrations/Plausible"; export default function DoctorVideoSlideover(props: { show: boolean; @@ -106,6 +108,7 @@ function UserListItem(props: { user: UserAssignedModel }) { const user = props.user; const icon = user.user_type === "Doctor" ? "fa-user-doctor " : " fa-user-nurse"; + const authUser = useAuthUser(); return (
  • @@ -155,6 +158,13 @@ function UserListItem(props: { user: UserAssignedModel }) { {user.video_connect_link && ( { + triggerGoal("Doctor Connect Click", { + medium: "Video Call", + userId: authUser?.id, + targetUserType: user.user_type, + }); + }} target="_blank" rel="noopener noreferrer" > @@ -176,6 +186,13 @@ function UserListItem(props: { user: UserAssignedModel }) { )}` : "#" } + onClick={() => { + triggerGoal("Doctor Connect Click", { + medium: "WhatsApp", + userId: authUser?.id, + targetUserType: user.user_type, + }); + }} target="_blank" rel="noopener noreferrer" > @@ -190,6 +207,13 @@ function UserListItem(props: { user: UserAssignedModel }) { href={ user.alt_phone_number ? `tel:${user.alt_phone_number}` : "#" } + onClick={() => { + triggerGoal("Doctor Connect Click", { + medium: "Phone Call", + userId: authUser?.id, + targetUserType: user.user_type, + }); + }} >
    From aeac8f1ce79331c2cb72689ac43032f1f63aa8f0 Mon Sep 17 00:00:00 2001 From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com> Date: Wed, 3 Jan 2024 12:43:50 +0530 Subject: [PATCH 3/5] Refactor DoctorVideoSlideover to filter doctors with alt_phone_number or video_connect_link --- src/Components/Facility/DoctorVideoSlideover.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Components/Facility/DoctorVideoSlideover.tsx b/src/Components/Facility/DoctorVideoSlideover.tsx index 092ebfce69a..f16b7ba1811 100644 --- a/src/Components/Facility/DoctorVideoSlideover.tsx +++ b/src/Components/Facility/DoctorVideoSlideover.tsx @@ -24,10 +24,12 @@ export default function DoctorVideoSlideover(props: { const res = await dispatchAction( getFacilityUsers(facilityId, { limit: 50 }) ); - if (res && res.data) { + if (res?.data) { setDoctors( res.data.results - .filter((user: any) => user.alt_phone_number) + .filter( + (user: any) => user.alt_phone_number || user.video_connect_link + ) .sort((a: any, b: any) => { return Number(a.last_login) - Number(b.last_login); }) From 89127d81d543d0aa8bbd57b44b7d92211a98f8b3 Mon Sep 17 00:00:00 2001 From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com> Date: Wed, 10 Jan 2024 15:32:20 +0530 Subject: [PATCH 4/5] Add user profile tests --- cypress/e2e/users_spec/user_profile.cy.ts | 85 +++++++++++++++++++++ cypress/pageobject/Login/LoginPage.ts | 4 + cypress/pageobject/Users/ManageUserPage.ts | 12 +++ cypress/pageobject/Users/UserProfilePage.ts | 83 ++++++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 cypress/e2e/users_spec/user_profile.cy.ts create mode 100644 cypress/pageobject/Users/UserProfilePage.ts diff --git a/cypress/e2e/users_spec/user_profile.cy.ts b/cypress/e2e/users_spec/user_profile.cy.ts new file mode 100644 index 00000000000..b6fe64b2455 --- /dev/null +++ b/cypress/e2e/users_spec/user_profile.cy.ts @@ -0,0 +1,85 @@ +import { cy, describe, before, beforeEach, it, afterEach } from "local-cypress"; +import LoginPage from "../../pageobject/Login/LoginPage"; +import UserProfilePage from "../../pageobject/Users/UserProfilePage"; +import ManageUserPage from "../../pageobject/Users/ManageUserPage"; + +describe("Manage User Profile", () => { + const loginPage = new LoginPage(); + const userProfilePage = new UserProfilePage(); + const manageUserPage = new ManageUserPage(); + + const age = "30"; + const gender = "Male"; + const email = "test@example.com"; + const phone = "+918899887788"; + const workinghours = "8"; + const doctorQualification = "MBBS"; + const doctorYoE = "10"; + const medicalCouncilRegistration = "1234567890"; + + const facilitySearch = "Dummy Facility 1"; + + before(() => { + loginPage.loginAsDevDoctor(); + cy.saveLocalStorage(); + }); + + beforeEach(() => { + cy.restoreLocalStorage(); + console.log(localStorage); + cy.clearLocalStorage(/filters--.+/); + console.log(localStorage); + cy.awaitUrl("/user/profile"); + }); + + it("Set Age, Gender, Email, Phone and Working Hours for a user and verify its reflection in user profile", () => { + userProfilePage.clickEditProfileButton(); + + userProfilePage.typeAge(age); + userProfilePage.selectGender(gender); + userProfilePage.typeEmail(email); + userProfilePage.typePhone(phone); + userProfilePage.typeWhatsApp(phone); + userProfilePage.typeWorkingHours(workinghours); + userProfilePage.typeDoctorQualification(doctorQualification); + userProfilePage.typeDoctorYoE(doctorYoE); + userProfilePage.typeMedicalCouncilRegistration(medicalCouncilRegistration); + + userProfilePage.clickUpdateButton(); + + cy.verifyNotification("Details updated successfully"); + + userProfilePage.assertAge(age); + userProfilePage.assertGender(gender); + userProfilePage.assertEmail(email); + userProfilePage.assertPhone(phone); + userProfilePage.assertWhatsApp(phone); + userProfilePage.assertWorkingHours(workinghours); + }); + + it("Adding video connect link for a user and verify its reflection in user profile and doctor connect", () => { + // verify the user doesn't have any video connect link + userProfilePage.assertVideoConnectLink("-"); + // Link a new video connect link and ensure it is under video connect link + userProfilePage.clickEditProfileButton(); + userProfilePage.typeVideoConnectLink("https://www.example.com"); + userProfilePage.clickUpdateButton(); + userProfilePage.assertVideoConnectLink("https://www.example.com"); + // Edit the video connect link and ensure it is updated + userProfilePage.clickEditProfileButton(); + userProfilePage.typeVideoConnectLink("https://www.test.com"); + userProfilePage.clickUpdateButton(); + userProfilePage.assertVideoConnectLink("https://www.test.com"); + // Go to particular facility doctor connect and verify the video connect link is present + manageUserPage.navigateToFacility(); + manageUserPage.typeFacilitySearch(facilitySearch); + manageUserPage.assertFacilityInCard(facilitySearch); + manageUserPage.clickFacilityPatients(); + manageUserPage.clickDoctorConnectButton(); + manageUserPage.assertVideoConnectLink("Dev Doctor", "https://www.test.com"); + }); + + afterEach(() => { + cy.saveLocalStorage(); + }); +}); diff --git a/cypress/pageobject/Login/LoginPage.ts b/cypress/pageobject/Login/LoginPage.ts index f691d5f9e15..f4f188f11d6 100644 --- a/cypress/pageobject/Login/LoginPage.ts +++ b/cypress/pageobject/Login/LoginPage.ts @@ -6,6 +6,10 @@ class LoginPage { cy.loginByApi("devdistrictadmin", "Coronasafe@123"); } + loginAsDevDoctor(): void { + cy.loginByApi("devdoctor", "Coronasafe@123"); + } + login(username: string, password: string): void { cy.loginByApi(username, password); } diff --git a/cypress/pageobject/Users/ManageUserPage.ts b/cypress/pageobject/Users/ManageUserPage.ts index 2d1ebbc14f0..622229745f7 100644 --- a/cypress/pageobject/Users/ManageUserPage.ts +++ b/cypress/pageobject/Users/ManageUserPage.ts @@ -149,6 +149,18 @@ export class ManageUserPage { cy.get("#doctor-connect-home-doctor").should("contain.text", realName); cy.get("#doctor-connect-remote-doctor").should("contain.text", realName); } + + assertVideoConnectLink(docName: string, link: string) { + cy.get("ul#options") + .find("li") + .contains(docName) + .within(() => { + cy.get("a").should(($a) => { + const hrefs = $a.map((i, el) => Cypress.$(el).attr("href")).get(); + expect(hrefs).to.include(link); + }); + }); + } } export default ManageUserPage; diff --git a/cypress/pageobject/Users/UserProfilePage.ts b/cypress/pageobject/Users/UserProfilePage.ts new file mode 100644 index 00000000000..040002f5205 --- /dev/null +++ b/cypress/pageobject/Users/UserProfilePage.ts @@ -0,0 +1,83 @@ +export default class UserProfilePage { + assertVideoConnectLink(link: string) { + cy.get("#videoconnectlink-profile-details").should("contain.text", link); + } + + clickEditProfileButton() { + cy.get("#edit-cancel-profile-button").click(); + } + + typeVideoConnectLink(link: string) { + cy.get("#video_connect_link").click().clear().type(link); + } + + clickUpdateButton() { + cy.get("#submit").click(); + } + + typeAge(age: string) { + cy.get("#age").clear().type(age); + } + + selectGender(gender: string) { + cy.get("#gender").click(); + cy.get("#gender-option-" + gender).click(); + } + + typeEmail(email: string) { + cy.get("#email").clear().type(email); + } + + typePhone(phone: string) { + cy.get("#phoneNumber").clear().type(phone); + } + + typeWhatsApp(phone: string) { + cy.get("#altPhoneNumber").clear().type(phone); + } + + typeWorkingHours(workinghours: string) { + cy.get("#weekly_working_hours").clear().type(workinghours); + } + + typeDoctorQualification = (doctorQualification: string) => { + cy.get("#doctor_qualification").clear().type(doctorQualification); + }; + + typeDoctorYoE = (doctorYoE: string) => { + cy.get("#doctor_experience_commenced_on").clear().type(doctorYoE); + }; + + typeMedicalCouncilRegistration = (medicalCouncilRegistration: string) => { + cy.get("#doctor_medical_council_registration") + .clear() + .type(medicalCouncilRegistration); + }; + + assertAge(age: string) { + cy.get("#age-profile-details").should("contain.text", age); + } + + assertGender(gender: string) { + cy.get("#gender-profile-details").should("contain.text", gender); + } + + assertEmail(email: string) { + cy.get("#emailid-profile-details").should("contain.text", email); + } + + assertPhone(phone: string) { + cy.get("#contactno-profile-details").should("contain.text", phone); + } + + assertWhatsApp(phone: string) { + cy.get("#whatsapp-profile-details").should("contain.text", phone); + } + + assertWorkingHours(workinghours: string) { + cy.get("#averageworkinghour-profile-details").should( + "contain.text", + workinghours + ); + } +} From 64523a93c3486adab29a4b5761767ee5d9fe06b3 Mon Sep 17 00:00:00 2001 From: Ashesh3 <3626859+Ashesh3@users.noreply.github.com> Date: Wed, 10 Jan 2024 15:35:05 +0530 Subject: [PATCH 5/5] Update input fields with click before clearing and typing --- cypress/pageobject/Users/UserProfilePage.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cypress/pageobject/Users/UserProfilePage.ts b/cypress/pageobject/Users/UserProfilePage.ts index 040002f5205..3f71a29181b 100644 --- a/cypress/pageobject/Users/UserProfilePage.ts +++ b/cypress/pageobject/Users/UserProfilePage.ts @@ -16,7 +16,7 @@ export default class UserProfilePage { } typeAge(age: string) { - cy.get("#age").clear().type(age); + cy.get("#age").click().clear().type(age); } selectGender(gender: string) { @@ -25,31 +25,32 @@ export default class UserProfilePage { } typeEmail(email: string) { - cy.get("#email").clear().type(email); + cy.get("#email").click().clear().type(email); } typePhone(phone: string) { - cy.get("#phoneNumber").clear().type(phone); + cy.get("#phoneNumber").click().clear().type(phone); } typeWhatsApp(phone: string) { - cy.get("#altPhoneNumber").clear().type(phone); + cy.get("#altPhoneNumber").click().clear().type(phone); } typeWorkingHours(workinghours: string) { - cy.get("#weekly_working_hours").clear().type(workinghours); + cy.get("#weekly_working_hours").click().clear().type(workinghours); } typeDoctorQualification = (doctorQualification: string) => { - cy.get("#doctor_qualification").clear().type(doctorQualification); + cy.get("#doctor_qualification").click().clear().type(doctorQualification); }; typeDoctorYoE = (doctorYoE: string) => { - cy.get("#doctor_experience_commenced_on").clear().type(doctorYoE); + cy.get("#doctor_experience_commenced_on").click().clear().type(doctorYoE); }; typeMedicalCouncilRegistration = (medicalCouncilRegistration: string) => { cy.get("#doctor_medical_council_registration") + .click() .clear() .type(medicalCouncilRegistration); };