From ca0db92731a1d4683b991faad78081181742789d Mon Sep 17 00:00:00 2001 From: Lore Date: Mon, 2 Dec 2024 16:50:20 -0300 Subject: [PATCH] chore(#9471): ui nav work image replacement - contact and user management (#9505) Co-authored-by: Maria Lorena Rodriguez Viruel Co-authored-by: Maria Lorena Rodriguez Viruel Co-authored-by: Jennifer Q <66472237+latin-panda@users.noreply.github.com> --- package.json | 3 +- .../contacts/config/contact-summary-extras.js | 590 +++++ .../config/contact-summary.templated.js | 323 +++ ...ntact-user-hierarchy-creation.wdio-spec.js | 117 + .../contact-user-management.wdio-spec.js | 148 ++ .../forms/death_report.properties.json | 9 + .../visual/contacts/forms/death_report.xml | 285 +++ .../pregnancy_danger_sign.properties.json | 9 + .../contacts/forms/pregnancy_danger_sign.xml | 518 ++++ .../pregnancy_home_visit.properties.json | 9 + .../contacts/forms/pregnancy_home_visit.xml | 2247 +++++++++++++++++ .../list-view-login-visual.wdio-spec.js | 179 +- tests/e2e/visual/wdio.conf.js | 25 +- tests/factories/cht/generate.js | 137 +- tests/factories/cht/reports/inmunization.js | 73 + .../integration/api/controllers/users.spec.js | 7 +- .../default/common/common.wdio.page.js | 54 +- .../default/contacts/contacts.wdio.page.js | 66 +- .../default/enketo/common-enketo.wdio.page.js | 7 + .../default/search/search.wdio.page.js | 11 +- .../default/users/user.wdio.page.js | 5 + tests/utils/cht-conf.js | 5 +- tests/utils/index.js | 8 +- tests/utils/screenshots.js | 13 +- 24 files changed, 4707 insertions(+), 141 deletions(-) create mode 100644 tests/e2e/visual/contacts/config/contact-summary-extras.js create mode 100644 tests/e2e/visual/contacts/config/contact-summary.templated.js create mode 100644 tests/e2e/visual/contacts/contact-user-hierarchy-creation.wdio-spec.js create mode 100644 tests/e2e/visual/contacts/contact-user-management.wdio-spec.js create mode 100644 tests/e2e/visual/contacts/forms/death_report.properties.json create mode 100644 tests/e2e/visual/contacts/forms/death_report.xml create mode 100644 tests/e2e/visual/contacts/forms/pregnancy_danger_sign.properties.json create mode 100644 tests/e2e/visual/contacts/forms/pregnancy_danger_sign.xml create mode 100644 tests/e2e/visual/contacts/forms/pregnancy_home_visit.properties.json create mode 100644 tests/e2e/visual/contacts/forms/pregnancy_home_visit.xml create mode 100644 tests/factories/cht/reports/inmunization.js diff --git a/package.json b/package.json index c2d025b9bd4..70cd07cc139 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,8 @@ "unit-haproxy-healthcheck": "cd haproxy-healthcheck && make test", "unit-cht-deploy": "cd scripts/deploy && npm test", "wdio-default-mobile-local": "export VERSION=$(node ./scripts/build/get-version.js) && ./scripts/build/build-service-images.sh && wdio run ./tests/e2e/default-mobile/wdio.conf.js --suite=all", - "wdio-visual-local": "export VERSION=$(node ./scripts/build/get-version.js) && ./scripts/build/build-service-images.sh && wdio run ./tests/e2e/visual/wdio.conf.js --suite=all", + "wdio-visual-desktop": "export VERSION=$(node ./scripts/build/get-version.js) && ./scripts/build/build-service-images.sh && wdio run ./tests/e2e/visual/wdio.conf.js --suite=desktopTests", + "wdio-visual-mobile": "export VERSION=$(node ./scripts/build/get-version.js) && ./scripts/build/build-service-images.sh && wdio run ./tests/e2e/visual/wdio.conf.js --suite=mobileTests", "wdio-local": "export VERSION=$(node ./scripts/build/get-version.js) && ./scripts/build/build-service-images.sh && wdio run ./tests/e2e/default/wdio.conf.js", "apdex-test": "wdio run ./tests/performance/apdex-score/wdio.conf.js", "-- CI SCRIPTS ": "-----------------------------------------------------------------------------------------------", diff --git a/tests/e2e/visual/contacts/config/contact-summary-extras.js b/tests/e2e/visual/contacts/config/contact-summary-extras.js new file mode 100644 index 00000000000..a2db24950ee --- /dev/null +++ b/tests/e2e/visual/contacts/config/contact-summary-extras.js @@ -0,0 +1,590 @@ +const moment = require('moment'); +const today = moment().startOf('day'); +const now = Date.now(); + +const pregnancyForms = ['pregnancy']; +const antenatalForms = ['pregnancy_home_visit']; +const deliveryForms = ['delivery']; +const pregnancDangerSignForms = [ + 'pregnancy', + 'pregnancy_home_visit', + 'pregnancy_danger_sign', + 'pregnancy_danger_sign_follow_up' +]; +const MAX_DAYS_IN_PREGNANCY = 42 * 7; +const AVG_DAYS_IN_PREGNANCY = 280; +const IMMUNIZATION_DOSES = [ + ['hep_a_1', 'HA1'], + ['hep_a_2', 'HA2'], + ['flu', 'FLU'], + ['polio_0', 'OPV0'], + ['polio_1', 'OPV1'], + ['polio_2', 'OPV2'], + ['polio_3', 'OPV3'], + ['penta_1', 'PENTA1'], + ['penta_2', 'PENTA2'], + ['penta_3', 'PENTA3'], + ['rotavirus_1', 'ROTA1'], + ['rotavirus_2', 'ROTA2'], + ['rotavirus_3', 'ROTA3'] +]; +const IMMUNIZATION_LIST = [ + 'hep_a', + 'flu', + 'polio', + 'penta', + 'rotavirus' +]; +const immunizationForms = [ + 'PENTA1', + 'PENTA2', + 'PENTA3', + 'OPV0', + 'OPV1', + 'OPV2', + 'OPV3', + 'ROTA1', + 'ROTA2', + 'ROTA3', + 'FLU', + 'HA1', + 'HA2' +]; + +const isReportValid = (report) => { + return ( + report.form && + report.fields && + report.reported_date + ); +}; + +const getField = (report, fieldPath) => ['fields', ...(fieldPath || '').split('.')] + .reduce((prev, fieldName) => { + if (prev === undefined) { + return undefined; + } + return prev[fieldName]; + }, report); + +const getFormArraySubmittedInWindow = (allReports, formArray, start, end) => allReports.filter( + (report) => formArray.includes(report.form) && + report.reported_date >= start && + report.reported_date <= end +); + +const getNewestReport = (allReports, forms) => { + const result = allReports + .filter((report) => isReportValid(report) && forms.includes(report.form)) + .reduce((newest, report) => { + return !newest || report.reported_date > newest.reported_date ? report : newest; + }, null); + + return result; +}; + +const getLMPDateFromPregnancy = (report) => { + const lmpDate = getField(report, 'lmp_date_8601'); + return isPregnancyForm(report) && lmpDate && moment(lmpDate); +}; + +const getLMPDateFromPregnancyFollowUp = (report) => { + const lmpDate = getField(report, 'lmp_date_8601'); + return isPregnancyFollowUpForm(report) && lmpDate && moment(lmpDate); +}; + +const getMostRecentLMPDateForPregnancy = (allReports, pregnancyReport) => { + let mostRecentLMP = getLMPDateFromPregnancy(pregnancyReport); + let mostRecentReportDate = pregnancyReport.reported_date; + + getSubsequentPregnancyFollowUps(allReports, pregnancyReport).forEach((visit) => { + const lmpFromPregnancyFollowUp = getLMPDateFromPregnancyFollowUp(visit); + const lmpUpdated = getField(visit, 'lmp_updated') === 'yes'; + + if (visit.reported_date > mostRecentReportDate && lmpUpdated) { + mostRecentReportDate = visit.reported_date; + mostRecentLMP = lmpFromPregnancyFollowUp; + } + }); + + return mostRecentLMP; +}; + +const getMostRecentEDDForPregnancy = (allReports, report) => { + const lmpDate = getMostRecentLMPDateForPregnancy(allReports, report); + + if (!lmpDate) { + return; + } + + return lmpDate.clone().add(AVG_DAYS_IN_PREGNANCY, 'days'); +}; + +const getDeliveryDate = (report) => { + const deliveryDate = getField(report, 'delivery_outcome.delivery_date'); + + return isDeliveryForm(report) && deliveryDate && moment(deliveryDate); +}; + +const getNextANCVisitDate = (allReports, report) => { + let nextVisit = getField(report, 't_pregnancy_follow_up_date'); + let eddReportDate = report.reported_date; + + const followUps = getSubsequentPregnancyFollowUps(allReports, report); + + followUps.forEach((followUpReport) => { + const followUpDate = getField(followUpReport, 't_pregnancy_follow_up_date'); + + if (followUpReport.reported_date > eddReportDate && followUpDate) { + eddReportDate = followUpReport.reported_date; + nextVisit = followUpDate; + } + }); + + return moment(nextVisit); +}; + +const getDangerSignCodes = (report) => { + const dangerSignCodes = []; + + if (getField(report, 't_danger_signs_referral_follow_up') === 'yes') { + const dangerSignsObj = getField(report, 'danger_signs'); + + if (dangerSignsObj) { + Object.keys(dangerSignsObj).forEach((dangerSign) => { + if (dangerSignsObj[dangerSign] === 'yes' && dangerSign !== 'r_danger_sign_present') { + dangerSignCodes.push(dangerSign); + } + }); + } + } + + return dangerSignCodes; +}; + +const getLatestDangerSignsForPregnancy = (allReports, pregnancy) => { + if (!pregnancy) { + return []; + } + + let lmpDate = getMostRecentLMPDateForPregnancy(allReports, pregnancy); + if (!lmpDate) { + lmpDate = moment(pregnancy.reported_date); + } + + const allReportsWithDangerSigns = getFormArraySubmittedInWindow( + allReports, + pregnancDangerSignForms, + lmpDate.toDate(), + lmpDate.clone().add(MAX_DAYS_IN_PREGNANCY, 'days').toDate() + ); + + const allRelevantReports = []; + allReportsWithDangerSigns.forEach((report) => { + if (isPregnancyFollowUpForm(report)) { + if (getField(report, 'pregnancy_summary.visit_option') === 'yes') { + allRelevantReports.push(report); + } + } else { + allRelevantReports.push(report); + } + }); + + const recentReport = getNewestReport(allRelevantReports, pregnancDangerSignForms); + if (!recentReport) { + return []; + } + + return getDangerSignCodes(recentReport); +}; + + +const getRiskFactorsFromPregnancy = (report) => { + const riskFactors = []; + + if (!isPregnancyForm(report)) { + return []; + } + + if (getField(report, 'risk_factors.r_risk_factor_present') === 'yes') { + if (getField(report, 'risk_factors.risk_factors_history.first_pregnancy') === 'yes') { + riskFactors.push('first_pregnancy'); + } + + if (getField(report, 'risk_factors.risk_factors_history.previous_miscarriage') === 'yes') { + riskFactors.push('previous_miscarriage'); + } + + const riskFactorsPrimary = getField(report, 'risk_factors.risk_factors_present.primary_condition'); + const riskFactorsSecondary = getField(report, 'risk_factors.risk_factors_present.secondary_condition'); + + if (riskFactorsPrimary) { + riskFactors.push(...riskFactorsPrimary.split(' ')); + } + + if (riskFactorsSecondary) { + riskFactors.push(...riskFactorsSecondary.split(' ')); + } + } + + return riskFactors; +}; + +const getNewRiskFactorsFromFollowUps = (report) => { + const riskFactors = []; + + if (!isPregnancyFollowUpForm(report)) { + return []; + } + + if (getField(report, 'anc_visits_hf.risk_factors.r_risk_factor_present') === 'yes') { + const newRiskFactors = getField(report, 'anc_visits_hf.risk_factors.new_risks'); + + if (newRiskFactors) { + riskFactors.push(...newRiskFactors.split(' ')); + } + } + + return riskFactors; +}; + +const getAllRiskFactors = (allReports, pregnancy) => { + const riskFactorCodes = getRiskFactorsFromPregnancy(pregnancy); + const pregnancyFollowUps = getSubsequentPregnancyFollowUps(allReports, pregnancy); + + pregnancyFollowUps.forEach((visit) => { + riskFactorCodes.push(...getNewRiskFactorsFromFollowUps(visit)); + }); + + return riskFactorCodes; +}; + +const getRiskFactorExtra = (report) => { + if (!report){ + return; + } + + if (isPregnancyForm(report)) { + return getField(report, 'risk_factors.risk_factors_present.additional_risk'); + } + + if (isPregnancyFollowUpForm(report)) { + return getField(report, 'anc_visits_hf.risk_factors.additional_risk'); + } +}; + +const getAllRiskFactorExtra = (allReports, pregnancy) => { + const riskFactorsExtra = []; + + const riskFactorExtraFromPregnancy = getRiskFactorExtra(pregnancy); + if (riskFactorExtraFromPregnancy) { + riskFactorsExtra.push(riskFactorExtraFromPregnancy); + } + + const pregnancyFollowUps = getSubsequentPregnancyFollowUps(allReports, pregnancy); + pregnancyFollowUps.forEach((visit) => { + const riskFactorExtraFromVisit = getRiskFactorExtra(visit); + if (riskFactorExtraFromVisit) { + riskFactorsExtra.push(riskFactorExtraFromVisit); + } + }); + + return riskFactorsExtra; +}; + +const isHighRiskPregnancy = (allReports, pregnancy) => { + const riskFactors = getAllRiskFactors(allReports, pregnancy); + const riskFactorExtra = getAllRiskFactorExtra(allReports, pregnancy); + const dangerSigns = getDangerSignCodes(pregnancy); + + return riskFactors.length > 0 || riskFactorExtra.length > 0 || dangerSigns.length > 0; +}; + +const isAlive = (thisContact) => thisContact && !thisContact.date_of_death; + +const isPregnancyForm = (report) => report && pregnancyForms.includes(report.form); + +const isPregnancyFollowUpForm = (report) => report && antenatalForms.includes(report.form); + +const isDeliveryForm = (report) => report && deliveryForms.includes(report.form); + +const getSubsequentPregnancies = (allReports, refReport) => allReports.filter( + (report) => isPregnancyForm(report) && + report.reported_date > refReport.reported_date +); + +const isActivePregnancy = (thisContact, allReports, report) => { + if ( + thisContact.type !== 'person' || + !isAlive(thisContact) || + !isPregnancyForm(report) + ) { + return false; + } + + const lmpDate = + getMostRecentLMPDateForPregnancy(allReports, report) || + report.reported_date; + + const isPregnancyRegisteredWithin9Months = + lmpDate > today.clone().subtract(MAX_DAYS_IN_PREGNANCY, 'days'); + + const isPregnancyTerminatedByDeliveryInLast6Weeks = + getSubsequentDeliveries(allReports, report, 6 * 7).length > 0; + + const isPregnancyTerminatedByAnotherPregnancyReport = + getSubsequentPregnancies(allReports, report).length > 0; + + return ( + isPregnancyRegisteredWithin9Months && + !isPregnancyTerminatedByDeliveryInLast6Weeks && + !isPregnancyTerminatedByAnotherPregnancyReport && + !getRecentANCVisitWithEvent(allReports, report, 'abortion') && + !getRecentANCVisitWithEvent(allReports, report, 'miscarriage') + ); +}; + +const isPregnant = (allReports) => allReports.some((report) => isActivePregnancy(report)); + +const isReadyForNewPregnancy = (thisContact, allReports) => { + if (thisContact.type !== 'person') { + return false; + } + + const mostRecentPregnancyReport = getNewestReport(allReports, pregnancyForms); + const mostRecentDeliveryReport = getNewestReport(allReports, deliveryForms); + + if (!mostRecentPregnancyReport && !mostRecentDeliveryReport) { + // No previous pregnancy or delivery recorded, fresh profile + return true; + } + + if (!mostRecentPregnancyReport) { + // Delivery report without pregnancy report + const deliveryDate = getDeliveryDate(mostRecentDeliveryReport); + return deliveryDate && deliveryDate < today.clone().subtract(6 * 7, 'day'); + } + + const isPregnancyNewer = + !mostRecentDeliveryReport || + mostRecentDeliveryReport.reported_date < mostRecentPregnancyReport.reported_date; + + if (isPregnancyNewer) { + // Pregnancy report newer than delivery or no delivery report + const lmpDate = + getMostRecentLMPDateForPregnancy(allReports, mostRecentPregnancyReport) || + moment(mostRecentPregnancyReport.reported_date); + + if (lmpDate < today.clone().subtract(MAX_DAYS_IN_PREGNANCY, 'day')) { + return true; + } + + return ( + getRecentANCVisitWithEvent(allReports, mostRecentPregnancyReport, 'abortion') || + getRecentANCVisitWithEvent(allReports, mostRecentPregnancyReport, 'miscarriage') + ); + } + + // Both pregnancy and delivery report; delivery report is newer + return ( + getRecentANCVisitWithEvent(allReports, mostRecentPregnancyReport, 'abortion') || + getRecentANCVisitWithEvent(allReports, mostRecentPregnancyReport, 'miscarriage') + ); +}; + +const isReadyForDelivery = (thisContact, allReports) => { + if (thisContact.type !== 'person') { + return false; + } + + const latestPregnancy = getNewestReport(allReports, pregnancyForms); + const latestDelivery = getNewestReport(allReports, deliveryForms); + + if (!latestPregnancy && !latestDelivery) { + // No previous pregnancy or delivery + return true; + } + + if ( + latestDelivery && + (!latestPregnancy || latestDelivery.reported_date > latestPregnancy.reported_date) + ) { + // No pregnancy registration; check if previous delivery was at least 7 months ago + return getDeliveryDate(latestDelivery) < today.clone().subtract(7, 'months'); + } + + if (latestPregnancy) { + const lmpDate = getMostRecentLMPDateForPregnancy(allReports, latestPregnancy); + if (!lmpDate) { + // No LMP; show readiness until 280 days + 6 weeks from the registration date + return moment(latestPregnancy.reported_date) + .clone() + .startOf('day') + .add(280 + 6 * 7, 'days') + .isSameOrBefore(today); + } + + const edd = getMostRecentEDDForPregnancy(allReports, latestPregnancy); + // Pregnancy registration with LMP; check readiness between 6 months after LMP and EDD + 6 weeks + return today.isBetween( + lmpDate.clone().add(6, 'months'), + edd.clone().add(6, 'weeks') + ); + } + + return false; +}; + +const getRecentANCVisitWithEvent = (allReports, pregnancyReport, event) => { + const followUps = getSubsequentPregnancyFollowUps(allReports, pregnancyReport); + const latestFollowUp = getNewestReport(followUps, antenatalForms); + + return latestFollowUp && + getField(latestFollowUp, 'pregnancy_summary.visit_option') === event + ? latestFollowUp + : undefined; +}; + +const getSubsequentDeliveries = (allReports, pregnancyReport, withinLastXDays) => allReports.filter((report) => { + const isAfterPregnancy = report.reported_date > pregnancyReport.reported_date; + const isWithinDays = !withinLastXDays || report.reported_date >= today.clone().subtract(withinLastXDays, 'days'); + return isDeliveryForm(report) && isAfterPregnancy && isWithinDays; +}); + +const getSubsequentPregnancyFollowUps = (allReports, pregnancyReport) => { + const lmpDate = getLMPDateFromPregnancy(pregnancyReport) || moment(pregnancyReport.reported_date); + + return allReports.filter(visitReport => isPregnancyFollowUpForm(visitReport) && + visitReport.reported_date > pregnancyReport.reported_date && + moment(visitReport.reported_date).isBefore(lmpDate.clone().add(MAX_DAYS_IN_PREGNANCY, 'days'))); +}; + +const countANCFacilityVisits = (allReports, pregnancyReport) => { + let ancHFVisits = 0; + + const initialVisits = getField(pregnancyReport, 'anc_visits_hf.anc_visits_hf_past.visited_hf_count'); + if (initialVisits && !isNaN(initialVisits)) { + ancHFVisits += parseInt(initialVisits, 10); + } + + const pregnancyFollowUps = getSubsequentPregnancyFollowUps(allReports, pregnancyReport); + ancHFVisits += pregnancyFollowUps.reduce((sum, report) => { + const pastANCHFVisits = getField(report, 'anc_visits_hf.anc_visits_hf_past'); + + if (!pastANCHFVisits) { + return sum; + } + + if (pastANCHFVisits.last_visit_attended === 'yes') { + sum += 1; + } + + const otherVisitCount = pastANCHFVisits.visited_hf_count; + if (pastANCHFVisits.report_other_visits === 'yes' && !isNaN(otherVisitCount)) { + sum += parseInt(otherVisitCount, 10); + } + + return sum; + }, 0); + + return ancHFVisits; +}; + +const knowsHIVStatusInPast3Months = (allReports) => { + const pregnancyFormsIn3Months = getFormArraySubmittedInWindow( + allReports, + pregnancyForms, + today.clone().subtract(3, 'months'), + today + ); + + return pregnancyFormsIn3Months.some( + (report) => getField(report, 'pregnancy_new_or_current.hiv_status.hiv_status_know') === 'yes' + ); +}; + +const addImmunizations = (master, vaccinesReceived) => { + IMMUNIZATION_DOSES.forEach((dose) => { + if (!master[dose[0]]) { + master[dose[0]] = typeof vaccinesReceived === 'string' + ? vaccinesReceived.toUpperCase() === dose[1] + : vaccinesReceived[`received_${dose[0]}`] === 'yes'; + } + }); +}; + +const getAgeInMonths = () => { + // eslint-disable-next-line no-undef + if (contact.date_of_birth && contact.date_of_birth !== '') { + // eslint-disable-next-line no-undef + const birthDate = new Date(contact.date_of_birth); + const ageInMs = new Date(Date.now() - birthDate.getTime()); + return (Math.abs(ageInMs.getFullYear() - 1970) * 12) + ageInMs.getMonth(); + } + return null; +}; + +const initImmunizations = () => { + const master = {}; + IMMUNIZATION_DOSES.forEach((dose) => { + master[dose[0]] = false; + }); + return master; +}; + +const isSingleDose = (name) => IMMUNIZATION_DOSES.some((d) => d[0] === name); + +const count = (arr, fn) => arr.filter(fn).length; + +const countDosesReceived = (master, name) => { + return count(IMMUNIZATION_DOSES, (dose) => { + return master[dose[0]] && dose[0].startsWith(name + '_'); + }); +}; + +const countDosesPossible = name => count(IMMUNIZATION_DOSES, dose => dose[0].startsWith(name + '_')); + +const countReportsSubmittedInWindow = (form, end) => { + // eslint-disable-next-line no-undef + return count(reports, (r) => { + return r.reported_date <= end && form.includes(r.form); + }); +}; + +module.exports = { + today, + MAX_DAYS_IN_PREGNANCY, + IMMUNIZATION_LIST, + isHighRiskPregnancy, + getNewestReport, + getSubsequentPregnancyFollowUps, + getSubsequentDeliveries, + isAlive, + isPregnant, + isActivePregnancy, + countANCFacilityVisits, + knowsHIVStatusInPast3Months, + getAllRiskFactors, + getAllRiskFactorExtra, + getDangerSignCodes, + getLatestDangerSignsForPregnancy, + getNextANCVisitDate, + isReadyForNewPregnancy, + isReadyForDelivery, + getMostRecentLMPDateForPregnancy, + getMostRecentEDDForPregnancy, + getDeliveryDate, + getFormArraySubmittedInWindow, + getRecentANCVisitWithEvent, + getField, + getRiskFactorsFromPregnancy, + now, + addImmunizations, + getAgeInMonths, + initImmunizations, + isSingleDose, + countDosesReceived, + countDosesPossible, + countReportsSubmittedInWindow, + immunizationForms, +}; diff --git a/tests/e2e/visual/contacts/config/contact-summary.templated.js b/tests/e2e/visual/contacts/config/contact-summary.templated.js new file mode 100644 index 00000000000..c705a85d25a --- /dev/null +++ b/tests/e2e/visual/contacts/config/contact-summary.templated.js @@ -0,0 +1,323 @@ +const moment = require('moment'); +const extras = require('./contact-summary-extras'); + +const { + today, + isHighRiskPregnancy, + getNewestReport, + getSubsequentPregnancyFollowUps, + isAlive, + isReadyForNewPregnancy, + isReadyForDelivery, + isActivePregnancy, + countANCFacilityVisits, + getAllRiskFactors, + getLatestDangerSignsForPregnancy, + getNextANCVisitDate, + getMostRecentLMPDateForPregnancy, + getMostRecentEDDForPregnancy, + getRecentANCVisitWithEvent, + getAllRiskFactorExtra, + getField, + now, + IMMUNIZATION_LIST, + addImmunizations, + getAgeInMonths, + initImmunizations, + isSingleDose, + countDosesReceived, + countDosesPossible, + countReportsSubmittedInWindow, + immunizationForms, +} = extras; + +// contact, reports, lineage are globally available for contact-summary +// eslint-disable-next-line no-undef +const thisContact = contact; +// eslint-disable-next-line no-undef +const thisLineage = lineage; +// eslint-disable-next-line no-undef +const allReports = reports; + +const context = { + alive: isAlive(thisContact), + muted: false, + showPregnancyForm: isReadyForNewPregnancy(thisContact, allReports), + showDeliveryForm: isReadyForDelivery(thisContact, allReports), +}; + +const fields = [ + { + appliesToType: 'person', + label: 'patient_id', + value: thisContact.patient_id, + width: 4, + }, + { + appliesToType: 'person', + label: 'contact.age', + value: thisContact.date_of_birth, + width: 4, + filter: 'age', + }, + { + appliesToType: 'person', + label: 'contact.sex', + value: `contact.sex.${thisContact.sex}`, + translate: true, + width: 4, + }, + { + appliesToType: 'person', + label: 'person.field.phone', + value: thisContact.phone, + width: 4, + }, + { + appliesToType: 'person', + label: 'contact.parent', + value: thisLineage, + filter: 'lineage', + }, + { + appliesToType: '!person', + label: 'contact', + value: thisContact.contact && thisContact.contact.name, + width: 4, + }, + { + appliesToType: '!person', + label: 'contact.phone', + value: thisContact.contact && thisContact.contact.phone, + width: 4, + }, + { + appliesToType: 'clinic', + label: 'Last Visited', + value: '36 days ago', + width: 4, + }, + { + appliesToType: '!person', + appliesIf: function () { + return thisContact.parent && thisLineage[0]; + }, + label: 'contact.parent', + value: thisLineage, + filter: 'lineage', + }, +]; + +if (thisContact.short_name) { + fields.unshift({ + appliesToType: 'person', + label: 'contact.short_name', + value: thisContact.short_name, + width: 4, + }); +} + +const cards = [ + { + label: 'contact.profile.pregnancy.active', + appliesToType: 'report', + appliesIf: function (report) { + return isActivePregnancy(thisContact, allReports, report); + }, + fields: function (report) { + const fields = []; + const riskFactors = getAllRiskFactors(allReports, report); + const riskFactorsCustom = getAllRiskFactorExtra(allReports, report); + const dangerSigns = getLatestDangerSignsForPregnancy(allReports, report); + const highRisk = isHighRiskPregnancy(allReports, report); + const mostRecentANC = getNewestReport(allReports, ['pregnancy', 'pregnancy_home_visit']); + const mostRecentANCDate = moment(mostRecentANC.reported_date); + const lmp_date = getMostRecentLMPDateForPregnancy(allReports, report); + const edd_ms = getMostRecentEDDForPregnancy(allReports, report); + const nextAncVisitDate = getNextANCVisitDate(allReports, report); + const weeksPregnant = lmp_date ? today.diff(lmp_date, 'weeks') : null; + + let lmp_approx = getField(report, 'lmp_approx'); + let reportDate = report.reported_date; + + getSubsequentPregnancyFollowUps(allReports, report).forEach(function (followUpReport) { + if (followUpReport.reported_date > reportDate && getField(followUpReport, 'lmp_updated') === 'yes') { + reportDate = followUpReport.reported_date; + if (getField(followUpReport, 'lmp_method_approx')) { + lmp_approx = getField(followUpReport, 'lmp_method_approx'); + } + } + }); + + const migratedReport = getRecentANCVisitWithEvent(allReports, report, 'migrated'); + const refusedReport = getRecentANCVisitWithEvent(allReports, report, 'refused'); + const stopReport = migratedReport || refusedReport; + + if (stopReport) { + const clearAll = getField(stopReport, 'pregnancy_ended.clear_option') === 'clear_all'; + fields.push( + { + label: 'contact.profile.change_care', + value: migratedReport ? 'Migrated out of area' : 'Refusing care', + width: 6 + }, + { label: 'contact.profile.tasks_on_off', value: clearAll ? 'Off' : 'On', width: 6 } + ); + } + + fields.push( + { + label: 'Weeks Pregnant', + value: weeksPregnant !== null + ? { number: weeksPregnant, approximate: lmp_approx === 'yes' } + : 'contact.profile.value.unknown', + translate: weeksPregnant === null, + filter: weeksPregnant !== null ? 'weeksPregnant' : '', + width: 6 + }, + { + label: 'contact.profile.edd', + value: edd_ms ? edd_ms.valueOf() : 'contact.profile.value.unknown', + translate: !edd_ms, + filter: edd_ms ? 'simpleDate' : '', + width: 6 + } + ); + + if (highRisk) { + let riskValue = ''; + if (!riskFactors && riskFactorsCustom) { + riskValue = riskFactorsCustom.join(', '); + } else if (riskFactors.length > 1 || (riskFactors && riskFactorsCustom)) { + riskValue = 'contact.profile.risk.multiple'; + } else { + riskValue = 'contact.profile.danger_sign.' + riskFactors[0]; + } + fields.push( + { label: 'contact.profile.risk.high', value: riskValue, translate: true, icon: 'icon-risk', width: 6 } + ); + } + + if (dangerSigns.length > 0) { + fields.push({ + label: 'contact.profile.danger_signs.current', + value: dangerSigns.length > 1 + ? 'contact.profile.danger_sign.multiple' + : `contact.profile.danger_sign.${dangerSigns[0]}`, + translate: true, + width: 6 + }); + } + + fields.push( + { + label: 'contact.profile.visit', + value: 'contact.profile.visits.of', + context: { count: countANCFacilityVisits(allReports, report), total: 8 }, + translate: true, + width: 6 + }, + { label: 'contact.profile.last_visited', value: mostRecentANCDate.valueOf(), filter: 'relativeDay', width: 6 } + ); + + if (nextAncVisitDate && nextAncVisitDate.isSameOrAfter(today)) { + fields.push( + { label: 'contact.profile.anc.next', value: nextAncVisitDate.valueOf(), filter: 'simpleDate', width: 6 } + ); + } + + return fields; + }, + modifyContext: function (ctx, report) { + let lmpDate = getField(report, 'lmp_date_8601'); + let lmpMethodApprox = getField(report, 'lmp_method_approx'); + let hivTested = getField(report, 'hiv_status_known'); + let dewormingMedicationReceived = getField(report, 'deworming_med_received'); + let ttReceived = getField(report, 'tt_received'); + const riskFactorCodes = getAllRiskFactors(allReports, report); + const riskFactorsCustom = getAllRiskFactorExtra(allReports, report); + let pregnancyFollowupDateRecent = getField(report, 't_pregnancy_follow_up_date'); + + getSubsequentPregnancyFollowUps(allReports, report).forEach(function (followUpReport) { + if (getField(followUpReport, 'lmp_updated') === 'yes') { + lmpDate = getField(followUpReport, 'lmp_date_8601'); + lmpMethodApprox = getField(followUpReport, 'lmp_method_approx'); + } + hivTested = getField(followUpReport, 'hiv_status_known'); + dewormingMedicationReceived = getField(followUpReport, 'deworming_med_received'); + ttReceived = getField(followUpReport, 'tt_received'); + if (getField(followUpReport, 't_pregnancy_follow_up') === 'yes') { + pregnancyFollowupDateRecent = getField(followUpReport, 't_pregnancy_follow_up_date'); + } + }); + + ctx.lmp_date_8601 = lmpDate; + ctx.lmp_method_approx = lmpMethodApprox; + ctx.is_active_pregnancy = true; + ctx.deworming_med_received = dewormingMedicationReceived; + ctx.hiv_tested_past = hivTested; + ctx.tt_received_past = ttReceived; + ctx.risk_factor_codes = riskFactorCodes.join(' '); + ctx.risk_factor_extra = riskFactorsCustom.join('; '); + ctx.pregnancy_follow_up_date_recent = pregnancyFollowupDateRecent; + ctx.pregnancy_uuid = report._id; + } + }, + { + label: 'contact.profile.immunizations', + appliesToType: 'person', + appliesIf: function() { + return getAgeInMonths() < 144; + }, + fields: function() { + const immunizations = initImmunizations(); + // eslint-disable-next-line no-undef + reports.forEach(function(report) { + if (report.form === 'immunization_visit') { + if (report.fields && report.fields.vaccines_received) { + addImmunizations(immunizations, report.fields.vaccines_received); + } + } else if (report.form === 'C_IMM') { + addImmunizations(immunizations, report.fields); + } else { + addImmunizations(immunizations, report.form); + } + }); + + const fields = IMMUNIZATION_LIST.map(function(imm) { + const field = { + label: 'contact.profile.imm.' + imm, + translate: true, + width: 6, + }; + if (isSingleDose(imm)) { + field.value = immunizations[imm] ? 'yes' : 'no'; + } else { + field.value = 'contact.profile.imm.doses'; + field.context = { + count: countDosesReceived(immunizations, imm), + total: countDosesPossible(imm), + }; + } + return field; + }); + + if (!fields.length) { + fields.push({ + label: 'contact.profile.imm.generic', + translate: true, + value: countReportsSubmittedInWindow(immunizationForms, now), + width: 12, + }); + } + + return fields; + }, + }, +]; + +module.exports = { + context: context, + cards: cards, + fields: fields +}; diff --git a/tests/e2e/visual/contacts/contact-user-hierarchy-creation.wdio-spec.js b/tests/e2e/visual/contacts/contact-user-hierarchy-creation.wdio-spec.js new file mode 100644 index 00000000000..0ba1c7dd251 --- /dev/null +++ b/tests/e2e/visual/contacts/contact-user-hierarchy-creation.wdio-spec.js @@ -0,0 +1,117 @@ + +const commonPage = require('@page-objects/default/common/common.wdio.page'); +const loginPage = require('@page-objects/default/login/login.wdio.page'); +const contactPage = require('@page-objects/default/contacts/contacts.wdio.page'); +const genericForm = require('@page-objects/default/enketo/generic-form.wdio.page'); +const commonEnketoPage = require('@page-objects/default/enketo/common-enketo.wdio.page'); +const searchPage = require('@page-objects/default/search/search.wdio.page'); +const usersAdminPage = require('@page-objects/default/users/user.wdio.page'); + +const utils = require('@utils'); +const { generateScreenshot } = require('@utils/screenshots'); + +describe('Creating and editing contacts and users', () => { + const healthFacilityName = 'Nairobi North Facility'; + + before(async () => { + await loginPage.cookieLogin(); + await commonPage.goToPeople(); + }); + + after(async () => { + await utils.revertDb([/^form:/], true); + }); + + afterEach(async () => { + await commonPage.goToPeople(); + }); + + it('should create health facility, chw area and chw '+ + 'chw supervisor and chw user', async () => { + //create health facility + await generateScreenshot('new-facility', 'select-new-facility'); + await commonPage.clickFastActionFlat({ waitForList: false }); + await commonEnketoPage.selectRadioButton('Set the Primary Contact', 'Skip this step'); + await generateScreenshot('new-facility', 'skip-primary-contact'); + await genericForm.nextPage(); + await commonEnketoPage.setInputValue('Name', healthFacilityName); + await generateScreenshot('new-facility', 'enter-facility-name'); + await genericForm.submitForm({ waitForPageLoaded: false }); + await contactPage.waitForContactLoaded(); + await commonPage.hideSnackbar(); + await generateScreenshot('new-facility', 'created-facility'); + + //create chw area and chw + await commonPage.clickFastActionFAB({ waitForList: false }); + await browser.pause(150); // Waiting for animation to avoid blurry screenshots + await generateScreenshot('new-chw-area', 'new-chw-area'); + await commonPage.closeFastActionList(); + await commonPage.clickFastActionFAB({ actionId: 'health_center' }); + await commonEnketoPage.selectRadioButton('Set the Primary Contact', 'Create a new person'); + await commonEnketoPage.setInputValue('Full Name', 'Jane Doe'); + await commonEnketoPage.selectRadioButton('Set the Primary Contact', 'Create a new person'); + await generateScreenshot('new-chw-area', 'create-new-person'); + await commonEnketoPage.setDateValue('Age', '1990-01-21'); + await commonEnketoPage.selectRadioButton('Sex', 'Male'); + await commonEnketoPage.scrollToQuestion('Age'); + await generateScreenshot('new-chw-area', 'fill-required-fields'); + await commonEnketoPage.selectRadioButton('Role', 'CHW'); + await genericForm.nextPage(); + await commonEnketoPage.selectRadioButton( + 'Would you like to name the place after the primary contact:', + 'Yes' + ); + await generateScreenshot('new-chw-area', 'name-after-primary-contact'); + await genericForm.submitForm({ waitForPageLoaded: false }); + await contactPage.waitForContactLoaded(); + await commonPage.hideSnackbar(); + await generateScreenshot('new-chw-area', 'created-chw-area'); + + //create chw supervisor + await contactPage.selectLHSRowByText(healthFacilityName); + await searchPage.clearSearch(); + await commonPage.clickFastActionFAB({ waitForList: false }); + await browser.pause(150); // Waiting for animation to avoid blurry screenshots + await generateScreenshot('new-chw-supervisor', 'new-person'); + await commonPage.closeFastActionList(); + await commonPage.clickFastActionFAB({ actionId: 'person' }); + await commonEnketoPage.setInputValue('Full name', 'John Doe'); + await generateScreenshot('new-chw-supervisor', 'belongs-to'); + await commonEnketoPage.selectRadioButton('Sex', 'male'); + await commonEnketoPage.setDateValue('Age', '1988-03-07'); + await genericForm.submitForm(); + await contactPage.selectLHSRowByText(healthFacilityName); + await searchPage.clearSearch(); + await commonPage.openMoreOptionsMenu(); + await browser.pause(150); // Waiting for animation to avoid blurry screenshots + await generateScreenshot('new-chw-supervisor', 'edit-facility'); + await commonPage.accessEditOption(true); + await contactPage.openPrimaryContactSearchDropdown(); + await contactPage.inputPrimaryContactSearchValue('John'); + await generateScreenshot('new-chw-supervisor', 'set-primary-contact'); + await contactPage.selectPrimaryContactSearchFirstResult(); + await contactPage.genericForm.submitForm(); + await contactPage.selectLHSRowByText(healthFacilityName); + await searchPage.clearSearch(); + await generateScreenshot('new-chw-supervisor', 'primary-contact-selected'); + + //create chw user + await commonPage.openHamburgerMenu(); + await generateScreenshot('new-chw-user', 'app-settings'); + await commonPage.openAppManagement(); + await usersAdminPage.goToAdminUser(); + await (await usersAdminPage.addUserButton()).waitForDisplayed(); + await generateScreenshot('new-chw-user', 'add-user'); + await usersAdminPage.openAddUserDialog(); + await usersAdminPage.inputAddUserFields( + 'Janet', + '', + 'chw', + `Jane Doe's Area`, + 'John Doe', + 'Secret_1' + ); + await usersAdminPage.scrollToRole(); + await generateScreenshot('new-chw-user', 'fill-user-details'); + }); +}); diff --git a/tests/e2e/visual/contacts/contact-user-management.wdio-spec.js b/tests/e2e/visual/contacts/contact-user-management.wdio-spec.js new file mode 100644 index 00000000000..114e6fedefe --- /dev/null +++ b/tests/e2e/visual/contacts/contact-user-management.wdio-spec.js @@ -0,0 +1,148 @@ +const commonPage = require('@page-objects/default/common/common.wdio.page'); +const loginPage = require('@page-objects/default/login/login.wdio.page'); +const dataFactory = require('@factories/cht/generate'); +const searchPage = require('@page-objects/default/search/search.wdio.page'); +const contactPage = require('@page-objects/default/contacts/contacts.wdio.page'); +const utils = require('@utils'); +const chtConfUtils = require('@utils/cht-conf'); +const path = require('path'); +const { generateScreenshot, isMobile } = require('@utils/screenshots'); + +describe('Contact and User Management', () => { + const updateContactSummarySettings = async () => { + await chtConfUtils.initializeConfigDir(); + const contactSummaryFile = path.join(__dirname, 'config/contact-summary.templated.js'); + const contactSummaryExtrasFile = path.join(__dirname, 'config/contact-summary-extras.js'); + const { contactSummary } = await chtConfUtils.compileNoolsConfig({ + contactSummary: contactSummaryFile, + contactSummaryExtras: contactSummaryExtrasFile + }); + await utils.updateSettings( + { contact_summary: contactSummary }, + { revert: true, ignoreReload: true, refresh: true, sync: true } + ); + await commonPage.waitForPageLoaded(); + }; + + const compileAndUploadForms = async () => { + await chtConfUtils.initializeConfigDir(); + const formsPath = path.join(__dirname, 'forms'); + await chtConfUtils.compileAndUploadAppForms(formsPath); + }; + + const docs = dataFactory.createHierarchy({ + name: 'Janet Mwangi', + user: true, + nbrClinics: 10, + nbrPersons: 4, + useRealNames: true, + }); + + before(async () => { + await utils.saveDocs([...docs.places, ...docs.clinics, ...docs.persons, ...docs.reports]); + await utils.createUsers([docs.user]); + await utils.addTranslations('en', {'contact.last.visit.unknown': 'Last Visited'}); + await loginPage.login(docs.user); + await updateContactSummarySettings(); + }); + + after(async () => { + await utils.deleteUsers([docs.user]); + await utils.revertDb([/^form:/], true); + await utils.revertSettings(); + }); + + afterEach(async () => { + await commonPage.goToBase(); + }); + + describe('Contact and user overview', () => { + it('should show contacts list, search, profiles (person and family), '+ + 'contact summary', async function() { + await commonPage.goToPeople(); + expect(await commonPage.isPeopleListPresent()).to.be.true; + await generateScreenshot('people', 'list'); + await searchPage.performSearch('Beatrice'); + await generateScreenshot('people', 'search'); + await searchPage.clearSearch(); + await contactPage.selectLHSRowByText('Beatrice Bass Family'); + await searchPage.clearSearch(); + await generateScreenshot('people', 'profile-family'); + await commonPage.goToBase(); + await commonPage.goToPeople(); + await contactPage.selectLHSRowByText('Beatrice Bass'); + await generateScreenshot('people', 'profile-person'); + }); + + it('should show condition cards', async function() { + if (!await isMobile()){ + this.skip(); + } + await commonPage.goToPeople(); + expect(await commonPage.isPeopleListPresent()).to.be.true; + await contactPage.selectLHSRowByText('Beatrice Bass'); + await (await contactPage.pregnancyCardSelectors.pregnancyCard()).scrollIntoView(); + await generateScreenshot('people', 'condition-card-active-pregnancy'); + await commonPage.goToBase(); + await commonPage.goToPeople(); + await contactPage.selectLHSRowByText('John Bass'); + await (await contactPage.inmunizationCardSelectors.inmunizationCard()).scrollIntoView(); + await generateScreenshot('people', 'condition-card-inmunization'); + }); + + it('should show profiles (area and branch)', async function() { + if (!await isMobile()){ + this.skip(); + } + await commonPage.logout(); + await loginPage.cookieLogin(); + await commonPage.goToPeople(); + expect(await commonPage.isPeopleListPresent()).to.be.true; + await contactPage.selectLHSRowByText(`Janet Mwangi's Area`); + await generateScreenshot('people', 'profile-area'); + await commonPage.goToBase(); + await commonPage.goToPeople(); + await contactPage.selectLHSRowByText(`Kiambu Branch`); + await generateScreenshot('people', 'profile-branch'); + await commonPage.goToBase(); + await commonPage.logout(); + await loginPage.login(docs.user); + }); + + it('should show UHC sort', async function() { + if (!await isMobile()){ + this.skip(); + } + await utils.updatePermissions(docs.user.roles, ['can_view_uhc_stats', 'can_view_last_visited_date'], [], { + ignoreReload: true, + revert: true, + refresh: true, + sync: true + }); + await commonPage.waitForPageLoaded(); + await commonPage.goToPeople(); + await contactPage.selectSortOrder('By date last visited'); + await contactPage.openSortMenu(); + await generateScreenshot('people', 'sort'); + }); + + it('should show cares guides', async function() { + if (!await isMobile()){ + this.skip(); + } + await compileAndUploadForms(); + await utils.updatePermissions(docs.user.roles, [], ['can_view_call_action', 'can_view_message_action'], { + ignoreReload: true, + revert: true, + refresh: true, + sync: true + }); + await commonPage.waitForPageLoaded(); + await commonPage.goToPeople(); + expect(await commonPage.isPeopleListPresent()).to.be.true; + await contactPage.selectLHSRowByText('Dana Dearborn'); + await commonPage.clickFastActionFAB({ waitForList: false }); + await generateScreenshot('people', 'care-guides'); + }); + }); +}); diff --git a/tests/e2e/visual/contacts/forms/death_report.properties.json b/tests/e2e/visual/contacts/forms/death_report.properties.json new file mode 100644 index 00000000000..097afebe7be --- /dev/null +++ b/tests/e2e/visual/contacts/forms/death_report.properties.json @@ -0,0 +1,9 @@ +{ + "icon": "icon-death-general", + "title": "Death report", + "context": { + "person": false, + "place": false, + "expression": "contact.type === 'person' && summary.alive && !summary.show_delivery_form && user.parent.type === 'health_center' && (!contact.sex || contact.sex === 'female') && (!contact.date_of_birth || (ageInYears(contact) >= 12 && ageInYears(contact) <= 49)) && user._id != contact._id" + } +} diff --git a/tests/e2e/visual/contacts/forms/death_report.xml b/tests/e2e/visual/contacts/forms/death_report.xml new file mode 100644 index 00000000000..a156627ad94 --- /dev/null +++ b/tests/e2e/visual/contacts/forms/death_report.xml @@ -0,0 +1,285 @@ + + + + Death report + + + + + Date of death can only be from today up to 1 year ago. + + + Date of Death + + + Provide any relevant information related to the death of . + + + Place of death + + + Specify other + + + Death details + + + <div style="text-align:center;">Relevant Information: </div> + + + <b>Important Information</b><i class="fa fa-warning"></i> + + + <div style="text-align:center;"> + +Date of Death: </div> + + + <b>You will never be able to do any follow ups on when you submit this death report.</b> + + + Patient Details<I class="fa fa-user"></i> + + + You will be able to undo this death report later, if needed. + + + <h4 style="text-align:center;">To finish, be sure to click the Submit button.</h4> + + + What is the patient's name? + + + Date of Birth + + + Name + + + Household ID + + + CHW name + + + CHW phone + + + Contact + + + Patient ID + + + Sex + + + Short Name + + + Contact + + + Source + + + Source ID + + + + + + + + + + + + + + + user + + + <_id/> + + + + 0 + + + <_id/> + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + <__date_of_death/> + <__place_of_death/> + <__place_of_death_other/> + <__death_information/> + + <__patient_uuid/> + <__patient_id/> + <__household_uuid/> + <__source/> + <__source_id/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/e2e/visual/contacts/forms/pregnancy_danger_sign.properties.json b/tests/e2e/visual/contacts/forms/pregnancy_danger_sign.properties.json new file mode 100644 index 00000000000..66449fc0494 --- /dev/null +++ b/tests/e2e/visual/contacts/forms/pregnancy_danger_sign.properties.json @@ -0,0 +1,9 @@ +{ + "icon": "icon-pregnancy-danger", + "title": "Pregnancy danger sign", + "context": { + "person": false, + "place": false, + "expression": "contact.type === 'person' && summary.alive && !summary.muted && summary.is_active_pregnancy && user.parent.type === 'health_center' && (!contact.sex || contact.sex === 'female') && (!contact.date_of_birth || (ageInYears(contact) >= 12 && ageInYears(contact) <= 49)) && user._id != contact._id" + } +} diff --git a/tests/e2e/visual/contacts/forms/pregnancy_danger_sign.xml b/tests/e2e/visual/contacts/forms/pregnancy_danger_sign.xml new file mode 100644 index 00000000000..77e55b1d6d2 --- /dev/null +++ b/tests/e2e/visual/contacts/forms/pregnancy_danger_sign.xml @@ -0,0 +1,518 @@ + + + + Pregnancy danger sign + + + + + No + + + Yes + + + Breaking of water + + + No + + + Yes + + + Breathlessness + + + Great news! Please closely monitor her until her next scheduled pregnancy visit. + + + the woman + + + The woman + + + Ask to monitor these danger signs throughout the pregnancy. + + + Does currently have any of these danger signs? + + + No + + + Yes + + + Getting tired easily + + + No + + + Yes + + + Swelling of face and hands + + + No + + + Yes + + + Fever + + + No + + + Yes + + + Fits + + + No + + + Yes + + + Reduced or no fetal movements + + + <span style="color:red">Please refer to the health facility immediately. Accompany her if possible.</span> + + + <span style="color:red">Please complete the follow-up task within 3 days.</span> + + + No + + + Yes + + + Severe abdominal pain + + + No + + + Yes + + + Severe headache + + + No + + + Yes + + + Vaginal bleeding + + + No + + + Yes + + + Very pale + + + Danger Sign Check + + + What is the patient's name? + + + Date of Birth + + + Name + + + Parent ID + + + CHW name + + + CHW phone + + + Patient ID + + + Sex + + + Short Name + + + Contact + + + Source + + + Source ID + + + + + + + + + + + + + + + user + + + <_id/> + + + + 0 + + + <_id/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <__vaginal_bleeding/> + <__fits/> + <__severe_abdominal_pain/> + <__severe_headache/> + <__very_pale/> + <__fever/> + <__reduced_or_no_fetal_movements/> + <__breaking_water/> + <__easily_tired/> + <__face_hand_swelling/> + <__breathlessness/> + <__has_danger_sign/> + + <__patient_uuid/> + <__patient_id/> + <__household_uuid/> + <__source/> + <__source_id/> + <__pregnancy_uuid/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/e2e/visual/contacts/forms/pregnancy_home_visit.properties.json b/tests/e2e/visual/contacts/forms/pregnancy_home_visit.properties.json new file mode 100644 index 00000000000..0f4033b8a6d --- /dev/null +++ b/tests/e2e/visual/contacts/forms/pregnancy_home_visit.properties.json @@ -0,0 +1,9 @@ +{ + "icon": "icon-pregnancy", + "title": "ANC visit", + "context": { + "person": true, + "place": false, + "expression": "contact.type === 'person' && summary.alive && !summary.muted && summary.is_active_pregnancy && user.parent.type === 'health_center' && (!contact.sex || contact.sex === 'female') && (!contact.date_of_birth || (ageInYears(contact) >= 12 && ageInYears(contact) <= 49)) && user._id != contact._id" + } +} diff --git a/tests/e2e/visual/contacts/forms/pregnancy_home_visit.xml b/tests/e2e/visual/contacts/forms/pregnancy_home_visit.xml new file mode 100644 index 00000000000..eb7bcac4f04 --- /dev/null +++ b/tests/e2e/visual/contacts/forms/pregnancy_home_visit.xml @@ -0,0 +1,2247 @@ + + + + Pregnancy home visit + + + + + Date cannot be in the past. Date cannot be more than one month from today. + + + I don't know + + + Enter date + + + If has a specific upcoming ANC appointment date, enter it here. You will receive a task three days before to remind her to attend. + + + is ** weeks** pregnant. + + + Please refer to the health facility at the appropriate time. + + + The WHO recommends ANC visits at 12, 20, 26, 30, 34, 36, 38, 40 weeks. + + + ANC Visits at Health Facility (Upcoming) + + + No + + + Yes + + + Did the woman complete the health facility ANC visit scheduled for ? + + + No + + + Yes + + + Would you like to report any additional unreported health facility ANC visits? + + + I don't know + + + Enter date + + + Please enter the date if you know it. + + + Enter the correct date. Date must be within this pregnancy and cannot be in the future! + + + Date + + + Enter the correct date. Date must be within this pregnancy and cannot be in the future! + + + Date + + + I don't know + + + Enter date + + + Visit + + + Please enter the dates if you have them. + + + Each "Visit" section below asks about one individual visit. Please complete all sections. + + + Enter 0 if she has not been yet. + + + Must be an integer between 0 and 9. + + + How many? + + + ANC Visits at Health Facility (Past) + + + max characters = 100 + + + If yes, please describe. + + + No + + + Yes + + + Are there additional factors that could make this pregnancy high-risk? + + + Asthma + + + Diabetes + + + + + + First pregnancy + + + Heart condition + + + High blood pressure + + + Last baby born less than one year ago + + + Has delivered four or more children + + + Select all that apply. + + + If "None of the above" selected, cannot select any other option. + + + Does have additional risk factors that you have not previously reported? + + + None + + + Previous difficulties in childbirth + + + Previous miscarriages or stillbirths + + + You previously reported the following risk factors: + + + Risk Factors + + + No + + + Yes + + + Breaking of water + + + No + + + Yes + + + Breathlessness + + + Great news! Please closely monitor her until her next scheduled pregnancy visit. + + + Ask to monitor these danger signs throughout the pregnancy. + + + Does currently have any of these danger signs? + + + No + + + Yes + + + Getting tired easily + + + No + + + Yes + + + Swelling of face and hands + + + No + + + Yes + + + Fever + + + No + + + Yes + + + Fits + + + No + + + Yes + + + Reduced or no fetal movements + + + <span style="color:red">Please refer to the health facility immediately. Accompany her if possible.</span> + + + <span style="color:red">Please complete the follow-up task within 3 days.</span> + + + No + + + Yes + + + Severe abdominal pain + + + No + + + Yes + + + Severe headache + + + No + + + Yes + + + Vaginal bleeding + + + No + + + Yes + + + Very pale + + + Danger Sign Check + + + What is the patient's name? + + + Date of Birth + + + Name + + + Parent ID + + + CHW name + + + CHW phone + + + Patient ID + + + Sex + + + Short Name + + + Source + + + Source ID + + + Date cannot be in the future. Date cannot be older than LMP. + + + Date of abortion + + + You have reported the woman has had an abortion. If she has not been seen by a care provider, please refer to the health facility. + + + Click "< Prev" to go back. + + + You can still submit pregnancy visits from the profile until the EDD is reached. + + + Do not receive any more tasks about this pregnancy. + + + Clear task for this visit only. Continue to receive tasks for this pregnancy. + + + What would you like to do? + + + Submitting this form will end the pregnancy. You will not receive any additional tasks. + + + You have reported the woman has moved out of the area. + + + Date cannot be in the future. Date cannot be older than LMP. + + + Date of miscarriage + + + You have reported the woman has had a miscarriage. If she has not been seen by a care provider, please refer to the health facility. + + + You have reported the woman has refused care. + + + Click Submit to confirm. + + + Update Pregnancy + + + Expected Date of Delivery(EDD): **** + + + Expected Date of Delivery(EDD): **unknown** + + + Current Weeks Pregnant: **** + + + Current Weeks Pregnant: **unknown** + + + No, I want to update. + + + Yes, it is correct. + + + Is the gestational age above correct? + + + No, Abortion + + + No, Migrated out of area + + + No, Miscarriage + + + No, Refusing care + + + Yes + + + Select one. + + + Do you want to start this pregnancy visit? + + + Pregnancy Summary + + + No + + + Yes + + + Has received deworming medication? + + + Worms can affect the nutritional status of and baby. + + + Frequent testing ensures that receives medicine to prevent transmission of HIV to the baby. + + + No + + + Yes + + + Has been tested for HIV in the past 3 months? + + + No + + + Yes + + + Is taking iron folate daily? + + + Iron folate aids in the development of child's brain and spinal cord. It also prevents premature birth, sepsis, anemia and low birth weight. + + + Sleeping under a LLIN **EVERY night** prevents malaria. + + + No + + + Yes + + + Does use a long-lasting insecticidal net (LLIN)? + + + Get malaria prophylaxis in second trimester if living in malaria endemic area. + + + It's safest to deliver in a health facility. Discuss a birth plan with . + + + Eat more often than usual and a balanced diet to give you strength and help the baby grow. + + + Respond to the baby's movements-kicks by gentle touching or massaging your tummy. + + + Talk softly to the unborn baby. The baby can hear you and will be able to recognize voices. + + + No + + + Yes + + + Has received any Tetanus Toxoid (TT) immunizations during this pregnancy? + + + Immunizing with at least two doses of tetanus toxoid before or during pregnancy protects the newborn for the first few weeks of life and protects the mother. + + + Women can receive up to two TT vaccines per pregnancy. After five TT vaccines, they are vaccinated for life. + + + Safe Pregnancy Practices + + + the woman + + + The woman + + + Breaking of water + + + Breathlessness + + + Getting tired easily + + + Swelling of face and hands + + + Fever + + + Fits + + + Reduced or no fetal movements + + + Severe abdominal pain + + + Severe headache + + + Vaginal bleeding + + + Very pale + + + New Danger Signs + + + Follow-up Tasks<I class="fa fa-flag"></i> + + + The following tasks will appear: + + + Please conduct a danger sign follow-up in 3 days. + + + Make sure attends her clinic visit on . Please remind her three days before. + + + Please conduct the next pregnancy home visit in week(s). + + + Please conduct the next pregnancy home visit in 2 weeks. + + + <h2 style="text-align:center;margin-bottom:0px;"></h2> <p style="text-align:center;"> years old</p> + + + <p> weeks pregnant.</p> <p> EDD: </p> + + + Unknown weeks pregnant. + + + Refer to clinic immediately for: + + + Danger Sign + + + Please refer to the health facility at the appropriate time. + + + Please refer to the health facility immediately to receive the EDD and appropriate care. + + + Referrals<I class="fa fa-hospital-o"></i> + + + Deworming + + + HIV test + + + Iron folate + + + TT + + + Request the following services: + + + + + + Asthma + + + Diabetes + + + New Risk Factors + + + Heart condition + + + High blood pressure + + + Please attend ANC on: + + + <h4 style="text-align:center;">Click the Submit button at the bottom of the form.</h4> + + + Summary<I class="fa fa-user"></i> + + + Patient<i class="fa fa-user"></i> + + + The WHO recommends routine ANC visits at 12, 20, 26, 30, 34, 36, 38, 40 weeks. + + + Expected date of delivery + + + Current weeks pregnant + + + Select one. + + + How would you like to update gestational age? + + + Must be between 4 and 40. + + + Please enter the new current weeks pregnant. + + + Date cannot be in the past. Date cannot be more than 9 months in the future. + + + Please enter the new EDD. + + + If this seems incorrect, click "< Prev" to update the pregnancy information. + + + EDD: + + + weeks pregnant + + + **New** + + + EDD: + + + EDD: unknown + + + weeks pregnant + + + Unknown weeks pregnant + + + **Previous** + + + **Please confirm the new information and then click Next.** + + + Update Pregnancy + + + Yes, it is correct. + + + No, I want to update. + + + Clear task for this visit only. Continue to receive tasks for this pregnancy. + + + Do not receive any more tasks about this pregnancy. + + + Current weeks pregnant + + + Expected date of delivery + + + I don't know + + + Enter date + + + Weeks + + + Months + + + The woman is visibly pregnant but does not know of how long. + + + You performed a pregnancy test and it is positive but the woman does not know the age of the pregnancy or LMP. + + + The woman is not on any family planning methods and has missed her periods. + + + Heart condition + + + Asthma + + + High blood pressure + + + Diabetes + + + None of the above + + + the woman + + + The woman + + + Yes + + + No, Miscarriage + + + No, Abortion + + + No, Refusing care + + + No, Migrated out of area + + + Yes + + + No + + + + + + + + + + + + + + + user + + + <_id/> + + + + 0 + + + <_id/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <__activity_to_report/> + <__gestational_age_correct/> + <__miscarriage_date/> + <__abortion_date/> + <__visit_task_clear_option/> + <__gestational_age_update_method/> + <__gestational_age_update_weeks/> + <__gestational_age_update_edd/> + <__lmp_updated/> + <__lmp_date_new/> + <__edd_new/> + <__last_visit_attended/> + <__report_additional_anc_hf_visits/> + <__num_additional_anc_hf_visits/> + <__additional_anc_hf_visit_dates/> + <__has_risk_factors_not_previously_reported/> + <__heart_condition/> + <__asthma/> + <__high_blood_pressure/> + <__diabetes/> + <__additional_high_risk_condition_to_report/> + <__additional_high_risk_condition/> + <__next_anc_hf_visit_date_known/> + <__next_anc_hf_visit_date/> + <__vaginal_bleeding/> + <__fits/> + <__severe_abdominal_pain/> + <__severe_headache/> + <__very_pale/> + <__fever/> + <__reduced_or_no_fetal_movements/> + <__breaking_water/> + <__easily_tired/> + <__face_hand_swelling/> + <__breathlessness/> + <__has_danger_sign/> + <__uses_llin/> + <__takes_iron_folate_daily/> + <__received_deworming_meds/> + <__tested_for_hiv_in_past_3_months/> + <__received_tetanus_toxoid_this_pregnancy/> + + <__patient_uuid/> + <__patient_id/> + <__household_uuid/> + <__source/> + <__source_id/> + <__pregnancy_uuid/> + + + + + + + + + + + + static_instance-yes_no-0 + yes + + + static_instance-yes_no-1 + no + + + + + + + static_instance-visit_options-0 + yes + + + static_instance-visit_options-1 + miscarriage + + + static_instance-visit_options-2 + abortion + + + static_instance-visit_options-3 + refused + + + static_instance-visit_options-4 + migrated + + + + + + + static_instance-age_correct_yes_no-0 + yes + + + static_instance-age_correct_yes_no-1 + no + + + + + + + static_instance-clear_options-0 + clear_this + + + static_instance-clear_options-1 + clear_all + + + + + + + static_instance-g_age_update_methods-0 + method_weeks + + + static_instance-g_age_update_methods-1 + method_edd + + + + + + + static_instance-lmp_approximations-0 + approx_weeks + + + static_instance-lmp_approximations-1 + approx_months + + + + + + + static_instance-no_info_pregnancy_reasons-0 + visibly_pregnant + + + static_instance-no_info_pregnancy_reasons-1 + test_positive + + + static_instance-no_info_pregnancy_reasons-2 + missed_periods + + + + + + + static_instance-knows_date-0 + no + + + static_instance-knows_date-1 + yes + + + + + + + static_instance-risk_conditions-0 + heart_condition + + + static_instance-risk_conditions-1 + asthma + + + static_instance-risk_conditions-2 + high_blood_pressure + + + static_instance-risk_conditions-3 + diabetes + + + static_instance-risk_conditions-4 + none + + + + + + + static_instance-translate_woman_label-0 + woman + + + + + + + static_instance-translate_woman_start_label-0 + woman-start + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/e2e/visual/contacts/list-view-login-visual.wdio-spec.js b/tests/e2e/visual/contacts/list-view-login-visual.wdio-spec.js index a6925ca6fd4..9b0d0e13288 100644 --- a/tests/e2e/visual/contacts/list-view-login-visual.wdio-spec.js +++ b/tests/e2e/visual/contacts/list-view-login-visual.wdio-spec.js @@ -3,21 +3,12 @@ const contactPage = require('@page-objects/default/contacts/contacts.wdio.page') const loginPage = require('@page-objects/default/login/login.wdio.page'); const dataFactory = require('@factories/cht/generate'); const reportsPage = require('@page-objects/default/reports/reports.wdio.page'); +const searchPage = require('@page-objects/default/search/search.wdio.page'); const utils = require('@utils'); const { resizeWindowForScreenshots, generateScreenshot } = require('@utils/screenshots'); describe('Contact List Page', () => { - const updateRolePermissions = async (roleValue, addPermissions, removePermissions = []) => { - const roles = [roleValue]; - const settings = await utils.getSettings(); - const permissions = await utils.getUpdatedPermissions(roles, addPermissions, removePermissions); - await utils.updateSettings( - { roles: settings.roles, permissions }, - { revert: true, ignoreReload: true, refresh: true, sync: true } - ); - }; - const docs = dataFactory.createHierarchy({ name: 'Janet Mwangi', user: true, @@ -45,89 +36,101 @@ describe('Contact List Page', () => { await commonPage.logout(); }); - describe('Log in', () => { - it('should show contacts page tab '+ - 'when can_view_contact and can_view_contacts_tab permissions are enabled', async () => { - await (await commonPage.contactsTab()).waitForDisplayed(); - await generateScreenshot('contact-page', 'tab-visible'); - await commonPage.openHamburgerMenu(); - await generateScreenshot('contact-page', 'menu-opened'); - await commonPage.closeHamburgerMenu(); - await commonPage.goToPeople(); - expect(await commonPage.isPeopleListPresent()).to.be.true; - await generateScreenshot('contact-page', 'people-list-visible'); - await commonPage.goToReports(); - await reportsPage.openFirstReport(); - await reportsPage.rightPanelSelectors.patientName().waitForClickable(); - await generateScreenshot('contact-page', 'reports-visible'); - await reportsPage.rightPanelSelectors.patientName().click(); - await contactPage.waitForContactLoaded(); - await generateScreenshot('contact-page', 'contact-loaded'); - await commonPage.goToMessages(); - }); + it('should show contacts page tab '+ + 'when can_view_contact and can_view_contacts_tab permissions are enabled', async () => { + expect(await commonPage.isContactTabPresent()).to.be.true; + await generateScreenshot('contact-page', 'tab-visible'); + await commonPage.openHamburgerMenu(); + await generateScreenshot('contact-page', 'menu-opened'); + await commonPage.closeHamburgerMenu(); + await commonPage.goToPeople(); + expect(await commonPage.isPeopleListPresent()).to.be.true; + await generateScreenshot('contact-page', 'people-list-visible'); + await commonPage.goToReports(); + await searchPage.performSearch('Amanda Allen'); + await commonPage.waitForLoaders(); + await reportsPage.openFirstReport(); + await reportsPage.rightPanelSelectors.patientName().waitForClickable(); + await generateScreenshot('contact-page', 'reports-visible'); + await reportsPage.rightPanelSelectors.patientName().click(); + await contactPage.waitForContactLoaded(); + await generateScreenshot('contact-page', 'contact-loaded'); + await commonPage.goToMessages(); + }); - it('should hide contacts page as tab and from menu option ' + - 'when can_view_contacts_tab permissions is enable but can_view_contact permission is not', async () => { - await updateRolePermissions('chw', [], ['can_view_contacts']); - await commonPage.waitForPageLoaded(); - await commonPage.goToMessages(); - await (await commonPage.contactsTab()).waitForDisplayed({ reverse: true }); - await generateScreenshot('contact-page', 'no-tab-visible'); - await commonPage.openHamburgerMenu(); - await (await commonPage.contactsButton()).waitForClickable({ reverse: true }); - await generateScreenshot('contact-page', 'no-menu-option'); - await commonPage.closeHamburgerMenu(); - await commonPage.goToReports(); - await reportsPage.openFirstReport(); - await (reportsPage.rightPanelSelectors.patientName()).waitForClickable(); - await generateScreenshot('contact-page', 'report-view-no-contacts'); - await reportsPage.rightPanelSelectors.patientName().click(); - await generateScreenshot('contact-page', 'report-no-contact-loaded'); - await commonPage.goToMessages(); + it('should hide contacts page as tab and from menu option ' + + 'when can_view_contacts_tab permissions is enable but can_view_contact permission is not', async () => { + await utils.updatePermissions(docs.user.roles, [], ['can_view_contacts'], { + ignoreReload: true, + revert: true, + refresh: true, + sync: true }); + await commonPage.waitForPageLoaded(); + await commonPage.goToMessages(); + expect(await commonPage.isContactTabPresent()).to.be.false; + await generateScreenshot('contact-page', 'no-tab-visible'); + await commonPage.toggleMenuAndCaptureScreenshot('People', true, 'contact-page', 'no-menu-option'); + await commonPage.goToReports(); + await searchPage.performSearch('Amanda Allen'); + await commonPage.waitForLoaders(); + await reportsPage.openFirstReport(); + await (reportsPage.rightPanelSelectors.patientName()).waitForClickable(); + await generateScreenshot('contact-page', 'report-view-no-contacts'); + await reportsPage.rightPanelSelectors.patientName().click(); + await generateScreenshot('contact-page', 'report-no-contact-loaded'); + await commonPage.goToMessages(); + }); - it('should hide contacts page as tab, show from menu option ' + - 'when can_view_contact permissions is enable but can_view_contact permission is not', async () => { - await updateRolePermissions('chw', ['can_view_contacts'], ['can_view_contacts_tab']); - await commonPage.waitForPageLoaded(); - await commonPage.goToMessages(); - await (await commonPage.contactsTab()).waitForDisplayed({ reverse: true }); - await generateScreenshot('contact-page', 'no-tab-visible'); - await commonPage.openHamburgerMenu(); - await (await commonPage.contactsButton()).waitForClickable(); - await generateScreenshot('contact-page', 'menu-option-visible'); - await (await commonPage.contactsButton()).click(); - expect(await commonPage.isPeopleListPresent()).to.be.true; - await commonPage.waitForPageLoaded(); - await generateScreenshot('contact-page', 'contacts-in-people-list'); - await commonPage.goToReports(); - await reportsPage.openFirstReport(); - await (reportsPage.rightPanelSelectors.patientName()).waitForClickable(); - await generateScreenshot('contact-page', 'report-view-with-contacts'); - await reportsPage.rightPanelSelectors.patientName().click(); - await contactPage.waitForContactLoaded(); - await generateScreenshot('contact-page', 'report-contact-loaded'); - await commonPage.goToMessages(); + it('should hide contacts page as tab, show from menu option ' + + 'when can_view_contact permissions is enable but can_view_contact permission is not', async () => { + await utils.updatePermissions(docs.user.roles, ['can_view_contacts'], ['can_view_contacts_tab'], { + ignoreReload: true, + revert: true, + refresh: true, + sync: true }); + await commonPage.waitForPageLoaded(); + await commonPage.goToMessages(); + expect(await commonPage.isContactTabPresent()).to.be.false; + await generateScreenshot('contact-page', 'no-tab-visible'); + await commonPage.toggleMenuAndCaptureScreenshot('People', false, 'contact-page', 'menu-option-visible'); + expect(await commonPage.isPeopleListPresent()).to.be.true; + await commonPage.waitForPageLoaded(); + await generateScreenshot('contact-page', 'contacts-in-people-list'); + await commonPage.goToReports(); + await searchPage.performSearch('Amanda Allen'); + await commonPage.waitForLoaders(); + await reportsPage.openFirstReport(); + await (reportsPage.rightPanelSelectors.patientName()).waitForClickable(); + await generateScreenshot('contact-page', 'report-view-with-contacts'); + await reportsPage.rightPanelSelectors.patientName().click(); + await contactPage.waitForContactLoaded(); + await generateScreenshot('contact-page', 'report-contact-loaded'); + await commonPage.goToMessages(); + }); - it('should hide contacts page as a tab and from menu option ' + - 'when can_view_contact and can_view_contact permissions are disable', async () => { - await updateRolePermissions('chw', [], ['can_view_contacts_tab', 'can_view_contacts']); - await commonPage.waitForPageLoaded(); - await commonPage.goToMessages(); - await (await commonPage.contactsTab()).waitForDisplayed({ reverse: true }); - await generateScreenshot('contact-page', 'no-tab-visible-oPerms'); - await commonPage.openHamburgerMenu(); - await (await commonPage.contactsButton()).waitForClickable({ reverse: true }); - await generateScreenshot('contact-page', 'no-menu-option-no-Perms'); - await commonPage.closeHamburgerMenu(); - await commonPage.goToReports(); - await reportsPage.openFirstReport(); - await (reportsPage.rightPanelSelectors.patientName()).waitForClickable(); - await generateScreenshot('contact-page', 'report-view-no-contacts-no-perms'); - await reportsPage.rightPanelSelectors.patientName().click(); - await generateScreenshot('contact-page', 'report-no-contact-loaded-no-perms'); - await commonPage.goToMessages(); + it('should hide contacts page as a tab and from menu option ' + + 'when can_view_contact and can_view_contact permissions are disable', async () => { + await utils.updatePermissions(docs.user.roles, [], ['can_view_contacts_tab', 'can_view_contacts'], { + ignoreReload: true, + revert: true, + refresh: true, + sync: true }); + await commonPage.waitForPageLoaded(); + await commonPage.goToMessages(); + expect(await commonPage.isContactTabPresent()).to.be.false; + await generateScreenshot('contact-page', 'no-tab-visible-oPerms'); + await commonPage.toggleMenuAndCaptureScreenshot('People', true, 'contact-page', 'no-menu-option-no-Perms'); + await commonPage.goToReports(); + await searchPage.performSearch('Amanda Allen'); + await commonPage.waitForLoaders(); + await reportsPage.openFirstReport(); + await (reportsPage.rightPanelSelectors.patientName()).waitForClickable(); + await generateScreenshot('contact-page', 'report-view-no-contacts-no-perms'); + await reportsPage.rightPanelSelectors.patientName().click(); + await generateScreenshot('contact-page', 'report-no-contact-loaded-no-perms'); + await commonPage.goToMessages(); }); }); diff --git a/tests/e2e/visual/wdio.conf.js b/tests/e2e/visual/wdio.conf.js index 1ba9009f00a..69af6994252 100644 --- a/tests/e2e/visual/wdio.conf.js +++ b/tests/e2e/visual/wdio.conf.js @@ -1,4 +1,5 @@ const wdioBaseConfig = require('../../wdio.conf'); +const { resizeWindowForScreenshots } = require('@utils/screenshots'); const chai = require('chai'); chai.use(require('chai-exclude')); @@ -9,9 +10,9 @@ const mobileCapability = { ...wdioBaseConfig.config.capabilities[0]['goog:chromeOptions'], args: [ ...wdioBaseConfig.config.capabilities[0]['goog:chromeOptions'].args, - 'window-size=450,700', + 'window-size=375,850', ], - }, + } }; const desktopCapability = { @@ -20,17 +21,27 @@ const desktopCapability = { ...wdioBaseConfig.config.capabilities[0]['goog:chromeOptions'], args: [ ...wdioBaseConfig.config.capabilities[0]['goog:chromeOptions'].args, - 'window-size=1000,800', + 'window-size=1440, 1024', ], - }, + } }; exports.config = Object.assign(wdioBaseConfig.config, { suites: { - all: [ - './**/*.wdio-spec.js', + desktopTests: [ + 'contacts/contact-user-management.wdio-spec.js', + 'contacts/contact-user-hierarchy-creation.wdio-spec.js', + ], + mobileTests: [ + 'contacts/contact-user-management.wdio-spec.js', + 'contacts/list-view-login-visual.wdio-spec.js', ] }, - capabilities: [mobileCapability, desktopCapability], + capabilities: process.argv.includes('--suite=mobileTests') + ? [mobileCapability] + : [desktopCapability], maxInstances: 1, + beforeSuite: async function () { + await resizeWindowForScreenshots(); + }, }); diff --git a/tests/factories/cht/generate.js b/tests/factories/cht/generate.js index 86ebbac7d13..cd73edd2c2e 100644 --- a/tests/factories/cht/generate.js +++ b/tests/factories/cht/generate.js @@ -4,10 +4,22 @@ const personFactory = require('@factories/cht/contacts/person'); const deliveryFactory = require('@factories/cht/reports/delivery'); const pregnancyFactory = require('@factories/cht/reports/pregnancy'); const pregnancyVisitFactory = require('@factories/cht/reports/pregnancy-visit'); +const immunizationFactory = require('@factories/cht/reports/inmunization'); +const { faker } = require('@faker-js/faker'); // Fixed collection of real-world data -const FIRST_NAMES = ['Amanda', 'Beatrice', 'Dana', 'Fatima', 'Gina', 'Helen', 'Isabelle', 'Jessica', 'Ivy', 'Sara']; -const LAST_NAMES = ['Allen', 'Bass', 'Dearborn', 'Flair', 'Gorman', 'Hamburg', 'Ivanas', 'James', 'Moore', 'Taylor']; +const PRIMARY_CONTACT_FIRST_NAMES = [ + 'Amanda', 'Beatrice', 'Dana', 'Fatima', + 'Gina', 'Helen', 'Isabelle', 'Jessica', + 'Ivy', 'Sara' +]; +const ADDITIONAL_KID_FIRST_NAMES = ['John', 'Timmy', 'Elias']; +const ADDITIONAL_WOMAN_FIRST_NAMES = ['Hawa', 'Ana', 'Tania']; +const FAMILY_LAST_NAMES = [ + 'Allen', 'Bass', 'Dearborn', 'Flair', + 'Gorman', 'Hamburg', 'Ivanas', 'James', + 'Moore', 'Taylor' +]; const PHONE_NUMBERS = [ '+256414345783', '+256414345784', '+256414345785', '+256414345786', '+256414345787', '+256414345788', @@ -16,12 +28,37 @@ const PHONE_NUMBERS = [ ]; const PATIENT_IDS = [65421, 65422, 65423, 65424, 65425, 65426, 65427, 65428, 65429, 65430]; +const calculateDateOfBirth = (age) => { + const today = new Date(); + const birthYear = today.getFullYear() - age; + const birthMonth = today.getMonth() + 1; + const birthDay = today.getDate(); + return `${birthYear}-${String(birthMonth).padStart(2, '0')}-${String(birthDay).padStart(2, '0')}`; +}; +const KIDS_AGES = [2, 7, 10]; +const ADULTS_AGES = [25, 35]; +const DATE_OF_BIRTHS_KIDS = KIDS_AGES.map(calculateDateOfBirth); +const DATE_OF_BIRTHS_ADULTS = ADULTS_AGES.map(calculateDateOfBirth); + +const calculateLastMenstrualPeriod = (date) => { + const PREGNANCY_DAYS = 252; + date.setDate(date.getDate() - PREGNANCY_DAYS); + return date.toISOString().split('T')[0]; // YYYY-MM-DD +}; + const getReportContext = (patient, submitter) => { + const daysAgo = faker.number.int({ min: 1, max: 10 }); + const currentDate = new Date(); + currentDate.setDate(currentDate.getDate() - daysAgo); + const lastMestrualPeriod = calculateLastMenstrualPeriod(new Date()); const context = { fields: { patient_id: patient._id, patient_uuid: patient._id, patient_name: patient.name, + visited_contact_uuid: patient.parent._id, + visited_date: currentDate, + lmp_date_8601: lastMestrualPeriod, }, }; if (submitter) { @@ -61,20 +98,22 @@ const createDataWithFixedData = ({ healthCenter, user, nbrClinics = 10, nbrPerso }; const createClinic = (index, healthCenter) => { - const firstName = FIRST_NAMES[index % FIRST_NAMES.length]; - const lastName = LAST_NAMES[index % LAST_NAMES.length]; + const firstName = PRIMARY_CONTACT_FIRST_NAMES[index % PRIMARY_CONTACT_FIRST_NAMES.length]; + const lastName = FAMILY_LAST_NAMES[index % FAMILY_LAST_NAMES.length]; const personName = `${firstName} ${lastName}`; const personPhoneNumber = PHONE_NUMBERS[index % PHONE_NUMBERS.length]; const primaryContact = personFactory.build({ name: personName, - phone: personPhoneNumber + phone: personPhoneNumber, + patient_id: PATIENT_IDS[0], }); const clinic = placeFactory.place().build({ type: 'clinic', parent: { _id: healthCenter._id, parent: healthCenter.parent }, name: `${personName} Family`, + last_name: lastName, contact: primaryContact }); @@ -83,26 +122,51 @@ const createClinic = (index, healthCenter) => { return { clinic, primaryContact }; }; -const createAdditionalPersons = (nbrPersons, clinic) => { +const createAdditionalPersons = (nbrPersons, clinic, nameList, dateOfBirthList) => { return Array - .from({ length: nbrPersons - 1 }) + .from({ length: nbrPersons }) .map((_, i) => { - const additionalPersonName = `${FIRST_NAMES[i % FIRST_NAMES.length]} ${LAST_NAMES[i % LAST_NAMES.length]}`; const additionalPhoneNumber = PHONE_NUMBERS[i % PHONE_NUMBERS.length]; + const name = nameList[i % nameList.length]; + const date_of_birth = dateOfBirthList[i % dateOfBirthList.length]; return personFactory.build({ parent: { _id: clinic._id, parent: clinic.parent }, - name: additionalPersonName, + name: `${name} ${clinic.last_name}`, patient_id: PATIENT_IDS[i % PATIENT_IDS.length], - phone: additionalPhoneNumber + phone: additionalPhoneNumber, + date_of_birth: date_of_birth, }); }); }; -const createReportsForPerson = (person, user) => { +const createAdditionalKid = (nbrPersons, clinic) => { + return createAdditionalPersons( + nbrPersons, + clinic, + ADDITIONAL_KID_FIRST_NAMES, + DATE_OF_BIRTHS_KIDS + ); +}; + +const createAdditionalWoman = (nbrPersons, clinic) => { + return createAdditionalPersons( + nbrPersons, + clinic, + ADDITIONAL_WOMAN_FIRST_NAMES, + DATE_OF_BIRTHS_ADULTS, + ); +}; + +const createReportsForWoman = (person, user) => { return [ - deliveryFactory.build(getReportContext(person, user)), pregnancyFactory.build(getReportContext(person, user)), - pregnancyVisitFactory.build(getReportContext(person, user)) + pregnancyVisitFactory.build(getReportContext(person, user)), + ]; +}; + +const createReportsForKid = (person, user) => { + return [ + immunizationFactory.build({ contact: user, patient: person }) ]; }; @@ -112,19 +176,28 @@ const createDataWithRealNames = ({ healthCenter, user, nbrClinics = 10, nbrPerso .map((_, index) => { const { clinic, primaryContact } = createClinic(index, healthCenter); - const additionalPersons = createAdditionalPersons(nbrPersons, clinic); - - const allPersons = [primaryContact, ...additionalPersons]; + const kids = createAdditionalKid( Math.floor(nbrPersons / 2), clinic); + const adults = createAdditionalWoman( Math.floor(nbrPersons / 2), clinic); + adults.unshift(primaryContact); - return { clinic, persons: allPersons }; + return { clinic, kids, adults }; }); - const allPersons = clinicsData.flatMap(data => data.persons); + const allPersons = clinicsData.flatMap(data => ([...data.kids, ...data.adults])); const clinicList = clinicsData.map(data => data.clinic); - const reports = allPersons.flatMap(person => createReportsForPerson(person, user)); + const reportsForKids = clinicsData + .flatMap(data => data.kids) + .flatMap(person => createReportsForKid(person, user)); + const reportsForWoman = clinicsData + .flatMap(data => data.adults) + .flatMap(person => createReportsForWoman(person, user)); - return { clinics: clinicList, reports, persons: allPersons }; + return { + clinics: clinicList, + reports: [...reportsForKids, ...reportsForWoman], + persons: [...allPersons] + }; }; const createData = ({ healthCenter, user, nbrClinics, nbrPersons, useRealNames = false }) => { @@ -137,14 +210,31 @@ const createData = ({ healthCenter, user, nbrClinics, nbrPersons, useRealNames = const createHierarchy = ({ name, user = false, nbrClinics = 50, nbrPersons = 10, useRealNames = false }) => { const hierarchy = placeFactory.generateHierarchy(); const healthCenter = hierarchy.get('health_center'); - user = user && userFactory.build({ place: healthCenter._id, roles: ['chw'] }); + healthCenter.name = `${name}'s Area`; + const branch = hierarchy.get('district_hospital'); + branch.name = 'Kiambu Branch'; + const branchContact = { + _id: 'fixture:user:user2', + name: 'Manager Ann', + phone: '+123456789' + }; + branch.contact = branchContact; + const contact = { + _id: 'fixture:user:user1', + name: name, + phone: '+12068881234' + }; + healthCenter.contact = contact; + + user = user && userFactory.build({ place: healthCenter._id, roles: ['chw'], contact: contact }); const places = [...hierarchy.values()].map(place => { - place.name = `${name} ${place.type}`; + if (place.type === 'clinic') { + place.name = `${name} ${place.type}`; + } return place; }); - healthCenter.name = `${name}'s Area`; const { clinics, reports, persons } = createData({ healthCenter, nbrClinics, nbrPersons, user, useRealNames }); return { @@ -161,4 +251,5 @@ module.exports = { createHierarchy, createData, ids: docs => docs.map(doc => doc._id || doc.id), + PATIENT_IDS, }; diff --git a/tests/factories/cht/reports/inmunization.js b/tests/factories/cht/reports/inmunization.js new file mode 100644 index 00000000000..0d3a3a17333 --- /dev/null +++ b/tests/factories/cht/reports/inmunization.js @@ -0,0 +1,73 @@ +const { v4: uuidv4 } = require('uuid'); + +const immunizationVisitFactory = { + build: ({ contact, patient }) => ({ + _id: uuidv4(), + form: 'immunization_visit', + type: 'data_record', + content_type: 'xml', + reported_date: Date.now(), + contact: contact, + fields: { + inputs: { + source: 'contact', + contact: { + _id: patient._id, + patient_id: patient.patient_id, + name: patient.name, + date_of_birth: patient.date_of_birth, + sex: patient.sex, + phone: patient.phone, + parent: { + contact: { + name: patient.name + } + } + } + }, + patient_age_in_years: '2', + patient_phone: patient.phone || '', + patient_uuid: patient._id, + patient_id: patient.patient_id, + patient_name: patient.name, + chw_name: patient.name, + chw_sms: `Nice work`, + visit_confirmed: 'yes', + vaccines_received: { + received_flu: 'yes' + }, + group_select_vaccines: { + g_vaccines: 'flu' + }, + group_flu: { + g_flu: 'yes' + }, + group_note: { + default_chw_sms: 'default', + default_chw_sms_text: `Nice work`, + is_sms_edited: 'yes' + }, + group_review: { + r_patient_id: patient.patient_id + }, + meta: { + instanceID: `uuid:${uuidv4()}` + } + }, + geolocation_log: [ + { + timestamp: Date.now(), + recording: { + code: 1, + message: 'User denied Geolocation' + } + } + ], + geolocation: { + code: 1, + message: 'User denied Geolocation' + } + }) +}; + +module.exports = immunizationVisitFactory; diff --git a/tests/integration/api/controllers/users.spec.js b/tests/integration/api/controllers/users.spec.js index 68910df19c6..fdc1b88442f 100644 --- a/tests/integration/api/controllers/users.spec.js +++ b/tests/integration/api/controllers/users.spec.js @@ -1944,7 +1944,12 @@ describe('Users API', () => { }); it('should create users with multiple facilities', async () => { - await utils.updatePermissions(['national_admin', 'chw'], ['can_have_multiple_places'], [], true); + await utils.updatePermissions( + ['national_admin', 'chw'], + ['can_have_multiple_places'], + [], + { ignoreReload: true } + ); const onlineUserPayload = { username: uuid(), password: password, diff --git a/tests/page-objects/default/common/common.wdio.page.js b/tests/page-objects/default/common/common.wdio.page.js index af1e3e03cf6..88464a95ab0 100644 --- a/tests/page-objects/default/common/common.wdio.page.js +++ b/tests/page-objects/default/common/common.wdio.page.js @@ -11,6 +11,7 @@ const tabsSelector = { taskTab: () => $('#tasks-tab'), analyticsTab: () => $('#analytics-tab'), }; +const { generateScreenshot } = require('@utils/screenshots'); const hamburgerMenuSelectors = { hamburgerMenu: () => $('aria/Application menu'), @@ -47,6 +48,15 @@ const fabSelectors = { fastActionItems: () => $$(`${FAST_ACTION_LIST_CONTAINER} .fast-action-item`), reportsFastActionFAB: () => $('#reports-content .fast-action-fab-button mat-icon'), }; +const hamburgerMenuItemSelector = 'mat-sidenav-content'; +const logoutButton = () => $('aria/Log out'); +const syncButton = () => $('aria/Sync now'); +const hamburguerMenuItemByOption = (menuOption) => $(hamburgerMenuItemSelector).$(`//span[text()="${menuOption}"]`); +const messagesTab = () => $('#messages-tab'); +const analyticsTab = () => $('#analytics-tab'); +const getReportsButtonLabel = () => $('#reports-tab .button-label'); +const getMessagesButtonLabel = () => $('#messages-tab .button-label'); +const getTasksButtonLabel = () => $('#tasks-tab .button-label'); const userSettingsSelectors = { editProfileButton: () => $('.user .configuration.page i.fa-user'), @@ -81,15 +91,17 @@ const openMoreOptionsMenu = async () => { await (await kebabMenuSelectors.moreOptionsMenu()).click(); }; -const performMenuAction = async (actionSelector) => { - await openMoreOptionsMenu(); +const performMenuAction = async (actionSelector, isOptionsMenuOpen = false) => { + if (!isOptionsMenuOpen){ + await openMoreOptionsMenu(); + } const actionElement = await actionSelector(); await actionElement.waitForClickable(); await actionElement.click(); }; -const accessEditOption = async () => { - await performMenuAction(kebabMenuSelectors.edit); +const accessEditOption = async (isOptionsMenuOpen = false) => { + await performMenuAction(kebabMenuSelectors.edit, isOptionsMenuOpen); }; const accessDeleteOption = async () => { @@ -104,6 +116,17 @@ const accessReviewOption = async () => { await performMenuAction(kebabMenuSelectors.review); }; +const toggleMenuAndCaptureScreenshot = async (menuOption, reverse, pageName, screenshotName) => { + await openHamburgerMenu(); + await hamburguerMenuItemByOption(menuOption).waitForClickable({ reverse }); + await generateScreenshot(pageName, screenshotName); + if (reverse) { + await closeHamburgerMenu(); + } else { + await hamburguerMenuItemByOption(menuOption).click(); + } +}; + const waitForSnackbarToClose = async () => { const snackbar = await $('#snackbar.active .snackbar-message'); if (await snackbar.isExisting()) { @@ -266,6 +289,8 @@ const isReportsListPresent = () => isElementPresent('#reports-list'); const isPeopleListPresent = () => isElementPresent('#contacts-list'); +const isContactTabPresent = () => isElementPresent('#contacts-tab'); + const isTargetMenuItemPresent = () => isElementPresent('=Target'); const isTargetAggregatesMenuItemPresent = () => isElementPresent('=Target aggregates'); @@ -545,9 +570,7 @@ module.exports = { accessDeleteOption, accessExportOption, accessReviewOption, - hideSnackbar, waitForLoaderToDisappear, - waitForLoaders, waitForAngularLoaded, waitForPageLoaded, clickFastActionFAB, @@ -559,6 +582,21 @@ module.exports = { closeFastActionList, isReportActionDisplayed, isElementPresent, + logoutButton, + isContactTabPresent, + messagesTab, + analyticsTab, + getReportsButtonLabel, + getMessagesButtonLabel, + getTasksButtonLabel, + hideSnackbar, + waitForLoaders, + syncButton, + toggleMenuAndCaptureScreenshot, + closeReloadModal, + goToMessages, + goToTasks, + goToAnalytics, isMessagesListPresent, isTasksListPresent, isPeopleListPresent, @@ -572,12 +610,8 @@ module.exports = { refresh, goToBase, goToAboutPage, - goToMessages, - goToTasks, goToReports, goToPeople, - goToAnalytics, - closeReloadModal, syncAndNotWaitForSuccess, sync, openReportBugAndFetchProperties, diff --git a/tests/page-objects/default/contacts/contacts.wdio.page.js b/tests/page-objects/default/contacts/contacts.wdio.page.js index c55384273ea..02e6931b0b0 100644 --- a/tests/page-objects/default/contacts/contacts.wdio.page.js +++ b/tests/page-objects/default/contacts/contacts.wdio.page.js @@ -19,6 +19,7 @@ const leftPanelSelectors = { contentRows: () => $$(CONTENT_ROW_SELECTOR), contactName: () => $$(`${CONTENT_ROW_SELECTOR} .heading h4 span`), contactListLoadingStatus: () => $(`${CONTACT_LIST_SELECTOR} .loading-status`), + firstContact: () => $(`${CONTACT_LIST_SELECTOR} li:first-child`), }; const rightPanelSelectors = { @@ -77,6 +78,39 @@ const deathCardSelectors = { deathPlace: () => $(`${DEATH_CARD_TEST_ID} div[test-id="contact.profile.death.place"] p.card-field-value`), }; +const inmunizationCardSelectors = { + inmunizationCard: () => $('div[test-id="contact.profile.immunizations"]'), +}; + +const editDistrictHospitalSelectors = { + primaryContactSearchDropdown: () => $( 'span.select2-selection--single' + + '[aria-labelledby^="select2-/data/district_hospital/contact/_id"]'), + primaryContactSearchInput: () => $('input.select2-search__field'), + primaryContactSearchFirstResult: () => $('.select2-results__option--highlighted'), +}; + +const sortMenuSelectors = { + sortIcon: () => $('#sort-results'), + sortDropdown: () => $('#sort-results-dropdown'), + sortMenuItems: () => $$('#sort-results-dropdown a[role="menuitem"]'), +}; + +const openSortMenu = async () => { + await sortMenuSelectors.sortIcon().waitForClickable(); + await sortMenuSelectors.sortIcon().click(); + await sortMenuSelectors.sortDropdown().waitForDisplayed(); +}; + +const selectSortOrder = async (sortLabel) => { + await openSortMenu(); + const option = await sortMenuSelectors.sortDropdown().$(`a[role="menuitem"]*=${sortLabel}`); + if (await option.isExisting()) { + await option.click(); + } else { + throw new Error(`Sort option "${sortLabel}" not found`); + } +}; + const search = async (query) => { if (!await (await searchSelectors.searchBox()).isDisplayed()) { await mobileSearchPage.performSearch(query); @@ -88,7 +122,7 @@ const search = async (query) => { const findRowByText = async (text, strict) => { for (const row of await leftPanelSelectors.contentRows()) { const rowText = await row.getText(); - if ((strict && rowText === text) || rowText.includes(text)) { + if ((strict && rowText === text) || (!strict && rowText.includes(text))) { return row; } } @@ -366,6 +400,29 @@ const filterReportViewAll = async () => { await (await tabsContainer.$('*=View all')).click(); }; +const openFirstContact = async () => { + const firstContact = leftPanelSelectors.firstContact(); + await firstContact.waitForClickable(); + await firstContact.click(); +}; + +const openPrimaryContactSearchDropdown = async () => { + await editDistrictHospitalSelectors.primaryContactSearchDropdown().waitForClickable(); + await editDistrictHospitalSelectors.primaryContactSearchDropdown().click(); +}; + +const inputPrimaryContactSearchValue = async (searchQuery) => { + await editDistrictHospitalSelectors.primaryContactSearchInput().waitForDisplayed(); + await editDistrictHospitalSelectors.primaryContactSearchInput().setValue(searchQuery); + await editDistrictHospitalSelectors.primaryContactSearchFirstResult().waitForDisplayed(); +}; + +const selectPrimaryContactSearchFirstResult = async () => { + await editDistrictHospitalSelectors.primaryContactSearchFirstResult().waitForClickable(); + await editDistrictHospitalSelectors.primaryContactSearchFirstResult().click(); +}; + + module.exports = { genericForm, leftPanelSelectors, @@ -375,6 +432,7 @@ module.exports = { reportsCardSelectors, pregnancyCardSelectors, deathCardSelectors, + inmunizationCardSelectors, selectLHSRowByText, selectRHSRowById, getReportFiltersText, @@ -407,4 +465,10 @@ module.exports = { getDisplayedContactsNames, getCurrentPersonEditFormValues, filterReportViewAll, + openFirstContact, + openPrimaryContactSearchDropdown, + inputPrimaryContactSearchValue, + selectPrimaryContactSearchFirstResult, + openSortMenu, + selectSortOrder, }; diff --git a/tests/page-objects/default/enketo/common-enketo.wdio.page.js b/tests/page-objects/default/enketo/common-enketo.wdio.page.js index cd335d2bfd3..9ac02eb075c 100644 --- a/tests/page-objects/default/enketo/common-enketo.wdio.page.js +++ b/tests/page-objects/default/enketo/common-enketo.wdio.page.js @@ -95,6 +95,12 @@ const getTextareaValue = async (question) => { return await getValue('textarea', question); }; +const scrollToQuestion = async (label) => { + return await (await getCurrentPageSection()) + .$(`label*=${label}`) + .scrollIntoView(false); +}; + const isRequiredMessageDisplayed = async (question) => { await formTitle().click(); const requiredMsg = (await getCurrentPageSection()) @@ -153,4 +159,5 @@ module.exports = { addRepeatSection, drawShapeOnCanvas, isRadioButtonSelected, + scrollToQuestion, }; diff --git a/tests/page-objects/default/search/search.wdio.page.js b/tests/page-objects/default/search/search.wdio.page.js index eb7ca9bfb05..d72230a613d 100644 --- a/tests/page-objects/default/search/search.wdio.page.js +++ b/tests/page-objects/default/search/search.wdio.page.js @@ -4,9 +4,17 @@ const commonElements = require('@page-objects/default/common/common.wdio.page'); const searchBox = () => $('.mm-search-bar-container input#freetext'); const resetSearch = () => $('.mm-search-bar-container .search-bar-clear'); +const searchIcon = () => $('.search-bar-left-icon .fa-search'); + +const ensureSearchInputVisible = async () => { + if (!(await searchBox().isDisplayed())) { + await (await searchIcon()).click(); + } +}; // click freetext search box const performSearch = async (searchString) => { + await ensureSearchInputVisible(); await (await searchBox()).click(); await (await searchBox()).clearValue(); await (await searchBox()).addValue(searchString); @@ -25,5 +33,6 @@ const clearSearch = async () => { module.exports = { performSearch, - clearSearch + clearSearch, + ensureSearchInputVisible }; diff --git a/tests/page-objects/default/users/user.wdio.page.js b/tests/page-objects/default/users/user.wdio.page.js index 8da7f95267d..50974276d54 100644 --- a/tests/page-objects/default/users/user.wdio.page.js +++ b/tests/page-objects/default/users/user.wdio.page.js @@ -222,6 +222,9 @@ const closeAddUserDialog = async () => { await (await cancelButton()).click(); await (await addUserDialog()).waitForDisplayed({ reverse: true }); }; +const scrollToRole = async () => { + await (await $('#role-select')).scrollIntoView(); +}; module.exports = { addUserDialog, @@ -248,4 +251,6 @@ module.exports = { getFailedUploadedUsers, backToUserList, closeAddUserDialog, + scrollToRole, + addUserButton, }; diff --git a/tests/utils/cht-conf.js b/tests/utils/cht-conf.js index 7a7456495b2..03f8f9e01cf 100644 --- a/tests/utils/cht-conf.js +++ b/tests/utils/cht-conf.js @@ -36,7 +36,7 @@ const initializeConfigDir = async () => { await fs.promises.writeFile(eslintPath, JSON.stringify(eslintRules)); }; -const compileNoolsConfig = async ({ tasks, targets, contactSummary }) => { +const compileNoolsConfig = async ({ tasks, targets, contactSummary, contactSummaryExtras }) => { const dir = getDirPath(); if (tasks && fs.existsSync(tasks)) { @@ -48,6 +48,9 @@ const compileNoolsConfig = async ({ tasks, targets, contactSummary }) => { if (contactSummary && fs.existsSync(contactSummary)) { fs.copyFileSync(contactSummary, path.join(dir, 'contact-summary.templated.js')); } + if (contactSummaryExtras && fs.existsSync(contactSummaryExtras)) { + fs.copyFileSync(contactSummaryExtras, path.join(dir, 'contact-summary-extras.js')); + } await runCommand('compile-app-settings', dir); const appSettings = JSON.parse(fs.readFileSync(path.join(dir, 'app_settings.json'), 'utf-8')); diff --git a/tests/utils/index.js b/tests/utils/index.js index 89e1c5bda1c..5198160acd6 100644 --- a/tests/utils/index.js +++ b/tests/utils/index.js @@ -1510,9 +1510,13 @@ const getUpdatedPermissions = async (roles, addPermissions, removePermissions) = return settings.permissions; }; -const updatePermissions = async (roles, addPermissions, removePermissions, ignoreReload) => { +const updatePermissions = async (roles, addPermissions, removePermissions, options = {}) => { const permissions = await getUpdatedPermissions(roles, addPermissions, removePermissions); - await updateSettings({permissions}, { ignoreReload: ignoreReload }); + const { ignoreReload = false, revert = false, refresh = false, sync = false } = options; + await updateSettings( + { permissions }, + { ignoreReload, revert, refresh, sync } + ); }; const getSentinelDate = () => getContainerDate('sentinel'); diff --git a/tests/utils/screenshots.js b/tests/utils/screenshots.js index 0bfe1d6fcaa..20c15202703 100644 --- a/tests/utils/screenshots.js +++ b/tests/utils/screenshots.js @@ -13,10 +13,10 @@ const sharp = require('sharp'); const MOBILE_WINDOW_WIDTH = 768; -const MOBILE_VIEWPORT_WIDTH = 320; -const MOBILE_VIEWPORT_HEIGHT = 570; -const DESKTOP_WINDOW_WIDTH = 1000; -const DESKTOP_WINDOW_HEIGHT = 820; +const MOBILE_VIEWPORT_WIDTH = 375; +const MOBILE_VIEWPORT_HEIGHT = 645; +const DESKTOP_WINDOW_WIDTH = 1440; +const DESKTOP_WINDOW_HEIGHT = 1024; const HIGH_DENSITY_DISPLAY_2X = 2; const isMobile = async () => { @@ -37,8 +37,8 @@ const resizeWindowForScreenshots = async () => { 'AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/92.0.4515.159 Mobile Safari/537.36' }); - } - + } + return await browser.setWindowSize(DESKTOP_WINDOW_WIDTH, DESKTOP_WINDOW_HEIGHT); }; @@ -72,4 +72,5 @@ const generateScreenshot = async (scenario, step) => { module.exports = { resizeWindowForScreenshots, generateScreenshot, + isMobile, };