From 9970b84e0f56330f9d9243b8cf6f8c915b1d19b7 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Thu, 30 May 2024 22:13:56 -0700 Subject: [PATCH 01/40] suppressed notification test tests when a user modifies the clinic profile, the supressednotification setting is preserved (true) --- custom_commands/captureElementScreenshot.js | 43 ++++++++++++ .../suppressedNotificationsTrue.js | 50 +++++++++++++ .../suppressedNotificationsTrueCheck.js | 70 +++++++++++++++++++ package.json | 3 +- pageobjects/clinicPatientListPage.js | 16 +++++ .../e2e/suppressedNotificationsFunctional.js | 45 ++++++++++++ 6 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 custom_commands/captureElementScreenshot.js create mode 100644 custom_commands/suppressedNotificationsTrue.js create mode 100644 custom_commands/suppressedNotificationsTrueCheck.js create mode 100644 tests/e2e/suppressedNotificationsFunctional.js diff --git a/custom_commands/captureElementScreenshot.js b/custom_commands/captureElementScreenshot.js new file mode 100644 index 0000000..667ffa4 --- /dev/null +++ b/custom_commands/captureElementScreenshot.js @@ -0,0 +1,43 @@ +const EventEmitter = require('events').EventEmitter; +const util = require('util'); +const Jimp = require('jimp'); +const Buffer = require('buffer').Buffer; +const promisifyCommand = require('../lib/promisify-command'); + +/** + * Takes a screenshot of the visible region encompassed by the bounding rectangle + * of an element. +* + * @link + * @param {string} id ID of the element to route the command to. + * @param {function} callback Callback function which is called with the captured screenshot as an argument. + * @returns {Object} The captured screenshot. This object is a Jimp (library) image instance. + */ +function CaptureElementScreenshot() { + EventEmitter.call(this); +} + +util.inherits(CaptureElementScreenshot, EventEmitter); + +CaptureElementScreenshot.prototype.command = function command( + selector, + callback = () => {}, // eslint-disable-line no-empty-function +) { + const api = this.client.api; + + Promise.all([ + promisifyCommand(api, 'takeElementScreenshot', [selector]), + ]).then(([screenshotEncoded]) => { + Jimp.read(new Buffer(screenshotEncoded, 'base64')).then((screenshot) => { + this.api.assert.ok(true, `The screenshot for selector <${selector.name}> was captured successfully.`); + + callback(screenshot); + this.emit('complete', screenshot); + }); + }).catch((errorMessage) => { + this.api.assert.fail(`The screenshot for selector <${selector.name}> could not be captured.`); + this.emit('complete', errorMessage, this); + }); +}; + +module.exports = CaptureElementScreenshot; diff --git a/custom_commands/suppressedNotificationsTrue.js b/custom_commands/suppressedNotificationsTrue.js new file mode 100644 index 0000000..298b1d6 --- /dev/null +++ b/custom_commands/suppressedNotificationsTrue.js @@ -0,0 +1,50 @@ +const axios = require('axios'); + +module.exports = { + command: async function () { + + + +const getToken = async () => { + const response = await axios.post( + 'https://qa2.development.tidepool.org/auth/login', + '', + { + headers: { + 'Authorization': 'Basic YnJpYW4rdXBsb2FkNzg2d2ViMTMyN2NsaW5pYzM1YUB0aWRlcG9vbC5vcmc6dGlkZXBvb2w=', + 'Content-Type': 'application/x-www-form-urlencoded' + } + }) + return response.headers['x-tidepool-session-token'] +} + + + +const setNotification = async () => { + const token = await getToken(); + response = await axios.post( + 'https://qa2.development.tidepool.org/v1/clinics/62419f38f85189a39ac4b68d/suppressed_notifications', + // '{\n "suppressedNotifications": {\n "patientClinicInvitation": true\n }\n}', + { + 'suppressedNotifications': { + 'patientClinicInvitation': true + } + }, + { + headers: { + 'X-Tidepool-Session-Token': token, + 'Content-Type': 'application/json' + } + } + ) + return response.status +} +const setNotificationRun = async () => { + const data = await setNotification() + this.assert.strictEqual(data, 200, '/suppressed_notification set to true returned 200 ') + //console.log("status: " + data) +} +setNotificationRun() +; + } +}; \ No newline at end of file diff --git a/custom_commands/suppressedNotificationsTrueCheck.js b/custom_commands/suppressedNotificationsTrueCheck.js new file mode 100644 index 0000000..f153288 --- /dev/null +++ b/custom_commands/suppressedNotificationsTrueCheck.js @@ -0,0 +1,70 @@ +const axios = require('axios'); + +module.exports = { + command: async function () { + + const getToken = async () => { + const response = await axios.post( + 'https://qa2.development.tidepool.org/auth/login', + '', + { + headers: { + 'Authorization': 'Basic YnJpYW4rdXBsb2FkNzg2d2ViMTMyN2NsaW5pYzM1YUB0aWRlcG9vbC5vcmc6dGlkZXBvb2w=', + 'Content-Type': 'application/x-www-form-urlencoded' + } + }) + return response.headers['x-tidepool-session-token'] + } + + + const clinicData = async () => { + + const token = await getToken(); + response = await axios.get('https://qa2.development.tidepool.org/v1/clinicians/4ae71760b6/clinics', { + + params: { + 'limit': '1000', + 'offset': '0' + }, + headers: { + 'authority': 'qa2.development.tidepool.org', + 'accept': '*/*', + 'accept-language': 'en-US,en;q=0.9', + 'cookie': '_ga=GA1.1.516068539.1692684735; _ga_RWXQ3R57PB=GS1.1.1694192507.4.0.1694192507.0.0.0', + 'referer': 'https://qa2.development.tidepool.org/', + 'sec-ch-ua': '"Google Chrome";v="117", "Not;A=Brand";v="8", "Chromium";v="117"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-origin', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', + 'x-tidepool-session-token': token, + 'x-tidepool-trace-session': '92df086f-917e-4df5-b32d-cb30d15df795' + } + }); + return response.data; + } + + const clinicDataRun = async () => { + var found = false; + const data = await clinicData() + for (var i = 0; i < data.length; i++) { + if (data[i]["clinic"]["id"] == '62419f38f85189a39ac4b68d') { + if (data[i]["clinic"]["suppressedNotifications"]["patientClinicInvitation"] == true) { + this.assert.strictEqual(true, true, '/suppressed_notification status is true in clinic object ') + found = true; + break; + } + } + } + if (found == false) { + this.assert.strictEqual(false, true, '/suppressed_notification status is true in clinic object ') + } + + } + clinicDataRun() + + + } +}; \ No newline at end of file diff --git a/package.json b/package.json index b6096fc..40d9c61 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "testprdEdge": "nightwatch --env prdedge", "testintChrome": "nightwatch --env intchrome", "testdev1Chrome": "nightwatch --env dev1chrome", - "testqa2ChromeTag": "nightwatch --env qa2chrome --tag clinician" + "testqa2ChromeClinician": "nightwatch --env qa2chrome --tag clinician", + "testqa2ChromeNotifications": "nightwatch --env qa2chrome --tag notifications" }, "scriptsComments": { "testParallel": "Executes tests that are able to run in parallel on all environments", diff --git a/pageobjects/clinicPatientListPage.js b/pageobjects/clinicPatientListPage.js index 9878502..d649ca8 100644 --- a/pageobjects/clinicPatientListPage.js +++ b/pageobjects/clinicPatientListPage.js @@ -49,6 +49,22 @@ module.exports = { selector: '//*[@id="peopleTable-header-cgm-lastUploadDate"]//*[contains(@class," MuiTableSortLabel-iconDirectionDesc")]', locateStrategy: 'xpath', }, + workspaceSettings: { + selector: '//button[@id="profileNavigationButton"]', + locateStrategy: 'xpath', + }, + edit: { + selector: '//button//*[text()="Edit"]', + locateStrategy: 'xpath', + }, + city: { + selector: '//input[@id="city"]', + locateStrategy: 'xpath', + }, + clinicProfileSubmit: { + selector: '//button[@id="editClinicProfileSubmit"]', + locateStrategy: 'xpath', + }, }, commands: [{ diff --git a/tests/e2e/suppressedNotificationsFunctional.js b/tests/e2e/suppressedNotificationsFunctional.js new file mode 100644 index 0000000..49a8640 --- /dev/null +++ b/tests/e2e/suppressedNotificationsFunctional.js @@ -0,0 +1,45 @@ +/* eslint-disable linebreak-style */ +require('../../utilities/seleniumKeepAlive'); + +module.exports = { + '@tags': ['notifications'], + 'Clinician User Logs in with Existing Credentials with notification suppressed'(browser) { + const loginPage = browser.page.loginPage(); + const clinicianUsername = browser.globals.clinicianUsername; + const clinicianPassword = browser.globals.clinicianPassword; + loginPage.loadPage(); + loginPage.userLogin(clinicianUsername, clinicianPassword); + browser.suppressedNotificationsTrue(); + browser.suppressedNotificationsTrueCheck(); + }, + 'Clinic workspace selection'(browser) { + const clinicWorkspacePage = browser.page.clinicWorkspacePage(); + const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; + clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); + clinicWorkspace.click('@goToWorkspace'); + }, + 'Clinic workspace settings'(browser) { + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + clinicPatientList.waitForElementVisible('@workspaceSettings', browser.globals.elementTimeout); + clinicPatientList.click('@workspaceSettings'); + clinicPatientList.waitForElementVisible('@edit', browser.globals.elementTimeout); + }, + 'Clinic workspace settings edit'(browser) { + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + clinicPatientList.waitForElementVisible('@edit', browser.globals.elementTimeout); + clinicPatientList.click('@edit'); + clinicPatientList.waitForElementVisible('@city', browser.globals.elementTimeout); + }, + 'Clinic workspace settings apply changes and check suppressed notifications status'(browser) { + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + clinicPatientList.waitForElementVisible('@city', browser.globals.elementTimeout); + clinicPatientList.setValue('@city', 'new'); + clinicPatientList.click('@clinicProfileSubmit'); + clinicPatientList.waitForElementNotVisible('@city', browser.globals.elementTimeout); + browser.suppressedNotificationsTrueCheck(); + }, + +}; From f4fe319a22aab5954eef4ad8d4b0cac481ba53b2 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Fri, 31 May 2024 11:33:47 -0700 Subject: [PATCH 02/40] removed credentials,environment url and added them to environment variables replaced credentials and environment url with environment variables that are read in at runtime (hardcoded values replaced in suppressedNotificationsTrueCheck.js, suppressedNotificationsTrue.js) --- .../suppressedNotificationsTrue.js | 73 +++++------ .../suppressedNotificationsTrueCheck.js | 122 +++++++++--------- .../e2e/suppressedNotificationsFunctional.js | 10 +- 3 files changed, 101 insertions(+), 104 deletions(-) diff --git a/custom_commands/suppressedNotificationsTrue.js b/custom_commands/suppressedNotificationsTrue.js index 298b1d6..29d6f94 100644 --- a/custom_commands/suppressedNotificationsTrue.js +++ b/custom_commands/suppressedNotificationsTrue.js @@ -1,50 +1,47 @@ const axios = require('axios'); module.exports = { - command: async function () { - + command: async function (clinicianUsername, clinicianPassword, environment) { + const auth = btoa(`${clinicianUsername}:${clinicianPassword}`); - -const getToken = async () => { - const response = await axios.post( - 'https://qa2.development.tidepool.org/auth/login', + const getToken = async () => { + const response = await axios.post( + `${environment}/auth/login`, '', { - headers: { - 'Authorization': 'Basic YnJpYW4rdXBsb2FkNzg2d2ViMTMyN2NsaW5pYzM1YUB0aWRlcG9vbC5vcmc6dGlkZXBvb2w=', - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) - return response.headers['x-tidepool-session-token'] -} - - + headers: { + Authorization: `Basic ${auth}`, + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }, + ); + return response.headers['x-tidepool-session-token']; + }; -const setNotification = async () => { - const token = await getToken(); - response = await axios.post( + const setNotification = async () => { + const token = await getToken(); + response = await axios.post( 'https://qa2.development.tidepool.org/v1/clinics/62419f38f85189a39ac4b68d/suppressed_notifications', // '{\n "suppressedNotifications": {\n "patientClinicInvitation": true\n }\n}', { - 'suppressedNotifications': { - 'patientClinicInvitation': true - } + suppressedNotifications: { + patientClinicInvitation: true, + }, }, { - headers: { - 'X-Tidepool-Session-Token': token, - 'Content-Type': 'application/json' - } - } - ) - return response.status -} -const setNotificationRun = async () => { - const data = await setNotification() - this.assert.strictEqual(data, 200, '/suppressed_notification set to true returned 200 ') - //console.log("status: " + data) -} -setNotificationRun() -; - } -}; \ No newline at end of file + headers: { + 'X-Tidepool-Session-Token': token, + 'Content-Type': 'application/json', + }, + }, + ); + return response.status; + }; + const setNotificationRun = async () => { + const data = await setNotification(); + this.assert.strictEqual(data, 200, '/suppressed_notification set to true returned 200 '); + // console.log("status: " + data) + }; + setNotificationRun(); + }, +}; diff --git a/custom_commands/suppressedNotificationsTrueCheck.js b/custom_commands/suppressedNotificationsTrueCheck.js index f153288..738b76e 100644 --- a/custom_commands/suppressedNotificationsTrueCheck.js +++ b/custom_commands/suppressedNotificationsTrueCheck.js @@ -1,70 +1,66 @@ const axios = require('axios'); module.exports = { - command: async function () { - - const getToken = async () => { - const response = await axios.post( - 'https://qa2.development.tidepool.org/auth/login', - '', - { - headers: { - 'Authorization': 'Basic YnJpYW4rdXBsb2FkNzg2d2ViMTMyN2NsaW5pYzM1YUB0aWRlcG9vbC5vcmc6dGlkZXBvb2w=', - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) - return response.headers['x-tidepool-session-token'] - } + command: async function (clinicianUsername, clinicianPassword, environment) { + const auth = btoa(`${clinicianUsername}:${clinicianPassword}`); + const getToken = async () => { + const response = await axios.post( + `${environment}/auth/login`, + '', + { + headers: { + Authorization: `Basic ${auth}`, + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }, + ); + return response.headers['x-tidepool-session-token']; + }; + const clinicData = async () => { + const token = await getToken(); + response = await axios.get('https://qa2.development.tidepool.org/v1/clinicians/4ae71760b6/clinics', { - const clinicData = async () => { - - const token = await getToken(); - response = await axios.get('https://qa2.development.tidepool.org/v1/clinicians/4ae71760b6/clinics', { + params: { + limit: '1000', + offset: '0', + }, + headers: { + authority: 'qa2.development.tidepool.org', + accept: '*/*', + 'accept-language': 'en-US,en;q=0.9', + cookie: '_ga=GA1.1.516068539.1692684735; _ga_RWXQ3R57PB=GS1.1.1694192507.4.0.1694192507.0.0.0', + referer: 'https://qa2.development.tidepool.org/', + 'sec-ch-ua': '"Google Chrome";v="117", "Not;A=Brand";v="8", "Chromium";v="117"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-origin', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', + 'x-tidepool-session-token': token, + 'x-tidepool-trace-session': '92df086f-917e-4df5-b32d-cb30d15df795', + }, + }); + return response.data; + }; - params: { - 'limit': '1000', - 'offset': '0' - }, - headers: { - 'authority': 'qa2.development.tidepool.org', - 'accept': '*/*', - 'accept-language': 'en-US,en;q=0.9', - 'cookie': '_ga=GA1.1.516068539.1692684735; _ga_RWXQ3R57PB=GS1.1.1694192507.4.0.1694192507.0.0.0', - 'referer': 'https://qa2.development.tidepool.org/', - 'sec-ch-ua': '"Google Chrome";v="117", "Not;A=Brand";v="8", "Chromium";v="117"', - 'sec-ch-ua-mobile': '?0', - 'sec-ch-ua-platform': '"Windows"', - 'sec-fetch-dest': 'empty', - 'sec-fetch-mode': 'cors', - 'sec-fetch-site': 'same-origin', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', - 'x-tidepool-session-token': token, - 'x-tidepool-trace-session': '92df086f-917e-4df5-b32d-cb30d15df795' - } - }); - return response.data; + const clinicDataRun = async () => { + let found = false; + const data = await clinicData(); + for (let i = 0; i < data.length; i++) { + if (data[i].clinic.id == '62419f38f85189a39ac4b68d') { + if (data[i].clinic.suppressedNotifications.patientClinicInvitation == true) { + this.assert.strictEqual(true, true, '/suppressed_notification status is true in clinic object '); + found = true; + break; + } } - - const clinicDataRun = async () => { - var found = false; - const data = await clinicData() - for (var i = 0; i < data.length; i++) { - if (data[i]["clinic"]["id"] == '62419f38f85189a39ac4b68d') { - if (data[i]["clinic"]["suppressedNotifications"]["patientClinicInvitation"] == true) { - this.assert.strictEqual(true, true, '/suppressed_notification status is true in clinic object ') - found = true; - break; - } - } - } - if (found == false) { - this.assert.strictEqual(false, true, '/suppressed_notification status is true in clinic object ') - } - - } - clinicDataRun() - - - } -}; \ No newline at end of file + } + if (found == false) { + this.assert.strictEqual(false, true, '/suppressed_notification status is true in clinic object '); + } + }; + clinicDataRun(); + }, +}; diff --git a/tests/e2e/suppressedNotificationsFunctional.js b/tests/e2e/suppressedNotificationsFunctional.js index 49a8640..4538909 100644 --- a/tests/e2e/suppressedNotificationsFunctional.js +++ b/tests/e2e/suppressedNotificationsFunctional.js @@ -7,10 +7,11 @@ module.exports = { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; const clinicianPassword = browser.globals.clinicianPassword; + const environment = browser.launch_url; loginPage.loadPage(); loginPage.userLogin(clinicianUsername, clinicianPassword); - browser.suppressedNotificationsTrue(); - browser.suppressedNotificationsTrueCheck(); + browser.suppressedNotificationsTrue(clinicianUsername, clinicianPassword, environment); + browser.suppressedNotificationsTrueCheck(clinicianUsername, clinicianPassword, environment); }, 'Clinic workspace selection'(browser) { const clinicWorkspacePage = browser.page.clinicWorkspacePage(); @@ -33,13 +34,16 @@ module.exports = { clinicPatientList.waitForElementVisible('@city', browser.globals.elementTimeout); }, 'Clinic workspace settings apply changes and check suppressed notifications status'(browser) { + const clinicianUsername = browser.globals.clinicianUsername; + const clinicianPassword = browser.globals.clinicianPassword; + const environment = browser.launch_url; const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; clinicPatientList.waitForElementVisible('@city', browser.globals.elementTimeout); clinicPatientList.setValue('@city', 'new'); clinicPatientList.click('@clinicProfileSubmit'); clinicPatientList.waitForElementNotVisible('@city', browser.globals.elementTimeout); - browser.suppressedNotificationsTrueCheck(); + browser.suppressedNotificationsTrueCheck(clinicianUsername, clinicianPassword, environment); }, }; From 914dbef8a1f0ccffa02278291e8c3a2b8cd84385 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Tue, 4 Jun 2024 22:56:22 -0700 Subject: [PATCH 03/40] addressing moving assertion outsitde of custom commands and various updates -renamed custom commands files -refactored so that returned a result and moved assertion to calling test -added @role=button to various page objects -replaced === in various comparisons --- .../checkSuppressedNotification.js | 69 +++++++++++++++++++ custom_commands/setSuppressedNotification.js | 51 ++++++++++++++ .../suppressedNotificationsTrue.js | 47 ------------- .../suppressedNotificationsTrueCheck.js | 66 ------------------ nightwatch.conf.js | 2 +- pageobjects/clinicPatientListPage.js | 18 ++--- .../e2e/suppressedNotificationsFunctional.js | 14 ++-- 7 files changed, 139 insertions(+), 128 deletions(-) create mode 100644 custom_commands/checkSuppressedNotification.js create mode 100644 custom_commands/setSuppressedNotification.js delete mode 100644 custom_commands/suppressedNotificationsTrue.js delete mode 100644 custom_commands/suppressedNotificationsTrueCheck.js diff --git a/custom_commands/checkSuppressedNotification.js b/custom_commands/checkSuppressedNotification.js new file mode 100644 index 0000000..00e40ef --- /dev/null +++ b/custom_commands/checkSuppressedNotification.js @@ -0,0 +1,69 @@ +/* eslint-disable linebreak-style */ +const axios = require('axios'); + +module.exports = class checkSuppressedNotification { + command(clinicianUsername, clinicianPassword, environment) { + return new Promise((resolve) => { + const auth = btoa(`${clinicianUsername}:${clinicianPassword}`); + const getToken = async () => { + const response = await axios.post( + `${environment}/auth/login`, + '', + { + headers: { + Authorization: `Basic ${auth}`, + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }, + ); + return response.headers['x-tidepool-session-token']; + }; + + const clinicData = async () => { + const token = await getToken(); + const response = await axios.get('https://qa2.development.tidepool.org/v1/clinicians/4ae71760b6/clinics', { + + params: { + limit: '1000', + offset: '0', + }, + headers: { + authority: 'qa2.development.tidepool.org', + accept: '*/*', + 'accept-language': 'en-US,en;q=0.9', + cookie: '_ga=GA1.1.516068539.1692684735; _ga_RWXQ3R57PB=GS1.1.1694192507.4.0.1694192507.0.0.0', + referer: 'https://qa2.development.tidepool.org/', + 'sec-ch-ua': '"Google Chrome";v="117", "Not;A=Brand";v="8", "Chromium";v="117"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-origin', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', + 'x-tidepool-session-token': token, + 'x-tidepool-trace-session': '92df086f-917e-4df5-b32d-cb30d15df795', + }, + }); + return response.data; + }; + + const clinicDataRun = async () => { + let found = false; + const data = await clinicData(); + for (let i = 0; i < data.length; i++) { + if (data[i].clinic.id === '62419f38f85189a39ac4b68d') { + if (data[i].clinic.suppressedNotifications.patientClinicInvitation === true) { + resolve(true); + found = true; + break; + } + } + } + if (found === false) { + resolve(false); + } + }; + clinicDataRun(); + }); + } +}; diff --git a/custom_commands/setSuppressedNotification.js b/custom_commands/setSuppressedNotification.js new file mode 100644 index 0000000..65a7708 --- /dev/null +++ b/custom_commands/setSuppressedNotification.js @@ -0,0 +1,51 @@ +/* eslint-disable class-methods-use-this */ +/* eslint-disable linebreak-style */ +const axios = require('axios'); + +module.exports = class setSuppressedNotifcation { + command(clinicianUsername, clinicianPassword, environment) { + return new Promise((resolve) => { + const auth = btoa(`${clinicianUsername}:${clinicianPassword}`); + + const getToken = async () => { + const response = await axios.post( + `${environment}/auth/login`, + '', + { + headers: { + Authorization: `Basic ${auth}`, + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }, + ); + return response.headers['x-tidepool-session-token']; + }; + + const setNotification = async () => { + const token = await getToken(); + const response = await axios.post( + 'https://qa2.development.tidepool.org/v1/clinics/62419f38f85189a39ac4b68d/suppressed_notifications', + + { + suppressedNotifications: { + patientClinicInvitation: true, + }, + }, + { + headers: { + 'X-Tidepool-Session-Token': token, + 'Content-Type': 'application/json', + }, + }, + ); + return response.status; + }; + const setNotificationRun = async () => { + const data = await setNotification(); + resolve(data); + // console.log("status: " + data) + }; + setNotificationRun(); + }); + } +}; diff --git a/custom_commands/suppressedNotificationsTrue.js b/custom_commands/suppressedNotificationsTrue.js deleted file mode 100644 index 29d6f94..0000000 --- a/custom_commands/suppressedNotificationsTrue.js +++ /dev/null @@ -1,47 +0,0 @@ -const axios = require('axios'); - -module.exports = { - command: async function (clinicianUsername, clinicianPassword, environment) { - const auth = btoa(`${clinicianUsername}:${clinicianPassword}`); - - const getToken = async () => { - const response = await axios.post( - `${environment}/auth/login`, - '', - { - headers: { - Authorization: `Basic ${auth}`, - 'Content-Type': 'application/x-www-form-urlencoded', - }, - }, - ); - return response.headers['x-tidepool-session-token']; - }; - - const setNotification = async () => { - const token = await getToken(); - response = await axios.post( - 'https://qa2.development.tidepool.org/v1/clinics/62419f38f85189a39ac4b68d/suppressed_notifications', - // '{\n "suppressedNotifications": {\n "patientClinicInvitation": true\n }\n}', - { - suppressedNotifications: { - patientClinicInvitation: true, - }, - }, - { - headers: { - 'X-Tidepool-Session-Token': token, - 'Content-Type': 'application/json', - }, - }, - ); - return response.status; - }; - const setNotificationRun = async () => { - const data = await setNotification(); - this.assert.strictEqual(data, 200, '/suppressed_notification set to true returned 200 '); - // console.log("status: " + data) - }; - setNotificationRun(); - }, -}; diff --git a/custom_commands/suppressedNotificationsTrueCheck.js b/custom_commands/suppressedNotificationsTrueCheck.js deleted file mode 100644 index 738b76e..0000000 --- a/custom_commands/suppressedNotificationsTrueCheck.js +++ /dev/null @@ -1,66 +0,0 @@ -const axios = require('axios'); - -module.exports = { - command: async function (clinicianUsername, clinicianPassword, environment) { - const auth = btoa(`${clinicianUsername}:${clinicianPassword}`); - const getToken = async () => { - const response = await axios.post( - `${environment}/auth/login`, - '', - { - headers: { - Authorization: `Basic ${auth}`, - 'Content-Type': 'application/x-www-form-urlencoded', - }, - }, - ); - return response.headers['x-tidepool-session-token']; - }; - - const clinicData = async () => { - const token = await getToken(); - response = await axios.get('https://qa2.development.tidepool.org/v1/clinicians/4ae71760b6/clinics', { - - params: { - limit: '1000', - offset: '0', - }, - headers: { - authority: 'qa2.development.tidepool.org', - accept: '*/*', - 'accept-language': 'en-US,en;q=0.9', - cookie: '_ga=GA1.1.516068539.1692684735; _ga_RWXQ3R57PB=GS1.1.1694192507.4.0.1694192507.0.0.0', - referer: 'https://qa2.development.tidepool.org/', - 'sec-ch-ua': '"Google Chrome";v="117", "Not;A=Brand";v="8", "Chromium";v="117"', - 'sec-ch-ua-mobile': '?0', - 'sec-ch-ua-platform': '"Windows"', - 'sec-fetch-dest': 'empty', - 'sec-fetch-mode': 'cors', - 'sec-fetch-site': 'same-origin', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', - 'x-tidepool-session-token': token, - 'x-tidepool-trace-session': '92df086f-917e-4df5-b32d-cb30d15df795', - }, - }); - return response.data; - }; - - const clinicDataRun = async () => { - let found = false; - const data = await clinicData(); - for (let i = 0; i < data.length; i++) { - if (data[i].clinic.id == '62419f38f85189a39ac4b68d') { - if (data[i].clinic.suppressedNotifications.patientClinicInvitation == true) { - this.assert.strictEqual(true, true, '/suppressed_notification status is true in clinic object '); - found = true; - break; - } - } - } - if (found == false) { - this.assert.strictEqual(false, true, '/suppressed_notification status is true in clinic object '); - } - }; - clinicDataRun(); - }, -}; diff --git a/nightwatch.conf.js b/nightwatch.conf.js index 71debf2..571ba1e 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -5,7 +5,7 @@ module.exports = { src_folders: ['tests'], page_objects_path: ['pageobjects'], globals_path: 'global.js', - custom_commands_path: ['node_modules/nightwatch-vrt/commands'], + custom_commands_path: ['./custom_commands'], custom_assertions_path: ['node_modules/nightwatch-vrt/assertions'], webdriver: {}, diff --git a/pageobjects/clinicPatientListPage.js b/pageobjects/clinicPatientListPage.js index d649ca8..a655ef8 100644 --- a/pageobjects/clinicPatientListPage.js +++ b/pageobjects/clinicPatientListPage.js @@ -10,35 +10,35 @@ module.exports = { chartDateRangeModal: '#ChartDateRangePicker', patientDetails: { - selector: '//*[@id="peopleTable-header-fullName"]//span', + selector: '//*[@id="peopleTable-header-fullName"]//span[@role="button"]', locateStrategy: 'xpath', }, lastUpload: { - selector: '//*[@id="peopleTable-header-cgm-lastUploadDate"]//span', + selector: '//*[@id="peopleTable-header-cgm-lastUploadDate"]//span[@role="button"]', locateStrategy: 'xpath', }, patientTags: { - selector: '//*[@id="peopleTable-header-tags"]//span', + selector: '//*[@id="peopleTable-header-tags"]//span[@role="button"]', locateStrategy: 'xpath', }, GMI: { - selector: '//*[@id="peopleTable-header-cgm-glucoseManagementIndicator"]//span', + selector: '//*[@id="peopleTable-header-cgm-glucoseManagementIndicator"]//span[@role="button"]', locateStrategy: 'xpath', }, timeInRange: { - selector: '//*[@id="peopleTable-header-bgRangeSummary"]//span', + selector: '//*[@id="peopleTable-header-bgRangeSummary"]//span[@role="button"]', locateStrategy: 'xpath', }, avgGlucose: { - selector: '//*[@id="peopleTable-header-bgm-averageGlucoseMmol"]//span', + selector: '//*[@id="peopleTable-header-bgm-averageGlucoseMmol"]//span[@role="button"]', locateStrategy: 'xpath', }, lows: { - selector: '//*[@id="peopleTable-header-bgm-timeInVeryLowRecords"]//span', + selector: '//*[@id="peopleTable-header-bgm-timeInVeryLowRecords"]//span[@role="button"]', locateStrategy: 'xpath', }, highs: { - selector: '//*[@id="peopleTable-header-bgm-timeInVeryHighRecords"]//span', + selector: '//*[@id="peopleTable-header-bgm-timeInVeryHighRecords"]//span[@role="button"]', locateStrategy: 'xpath', }, showAll: { @@ -46,7 +46,7 @@ module.exports = { locateStrategy: 'xpath', }, lastUploadDesc: { - selector: '//*[@id="peopleTable-header-cgm-lastUploadDate"]//*[contains(@class," MuiTableSortLabel-iconDirectionDesc")]', + selector: '//*[@id="peopleTable-header-cgm-lastUploadDate"]//span[@role="Button"]', locateStrategy: 'xpath', }, workspaceSettings: { diff --git a/tests/e2e/suppressedNotificationsFunctional.js b/tests/e2e/suppressedNotificationsFunctional.js index 4538909..c60e108 100644 --- a/tests/e2e/suppressedNotificationsFunctional.js +++ b/tests/e2e/suppressedNotificationsFunctional.js @@ -1,17 +1,20 @@ +/* eslint-disable max-len */ /* eslint-disable linebreak-style */ require('../../utilities/seleniumKeepAlive'); module.exports = { '@tags': ['notifications'], - 'Clinician User Logs in with Existing Credentials with notification suppressed'(browser) { + 'Clinician User Logs in with Existing Credentials with notification suppressed': async function (browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; const clinicianPassword = browser.globals.clinicianPassword; const environment = browser.launch_url; loginPage.loadPage(); loginPage.userLogin(clinicianUsername, clinicianPassword); - browser.suppressedNotificationsTrue(clinicianUsername, clinicianPassword, environment); - browser.suppressedNotificationsTrueCheck(clinicianUsername, clinicianPassword, environment); + let res = await browser.setSuppressedNotification(clinicianUsername, clinicianPassword, environment); + browser.assert.strictEqual(res, 200, '/suppressed_notification returns 200 '); + res = await browser.checkSuppressedNotification(clinicianUsername, clinicianPassword, environment); + browser.assert.strictEqual(res, true, '/suppressed_notification status is true in clinic object '); }, 'Clinic workspace selection'(browser) { const clinicWorkspacePage = browser.page.clinicWorkspacePage(); @@ -33,7 +36,7 @@ module.exports = { clinicPatientList.click('@edit'); clinicPatientList.waitForElementVisible('@city', browser.globals.elementTimeout); }, - 'Clinic workspace settings apply changes and check suppressed notifications status'(browser) { + 'Clinic workspace settings apply changes and check suppressed notifications status': async function (browser) { const clinicianUsername = browser.globals.clinicianUsername; const clinicianPassword = browser.globals.clinicianPassword; const environment = browser.launch_url; @@ -43,7 +46,8 @@ module.exports = { clinicPatientList.setValue('@city', 'new'); clinicPatientList.click('@clinicProfileSubmit'); clinicPatientList.waitForElementNotVisible('@city', browser.globals.elementTimeout); - browser.suppressedNotificationsTrueCheck(clinicianUsername, clinicianPassword, environment); + const res = await browser.checkSuppressedNotification(clinicianUsername, clinicianPassword, environment); + browser.assert.strictEqual(res, true, '/suppressed_notification status is true in clinic object '); }, }; From e138f9274a6bf9b2d17620d2a10426b1651c4fb2 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Wed, 5 Jun 2024 19:18:24 -0700 Subject: [PATCH 04/40] comments added to suppressed notifiication functions, environment variables added -js doc style comments added for each sub function inside suppressed notification functions -hard coded values for clinic id and clinician id moved to global variables in nightwatch.conf.js - --- .../checkSuppressedNotification.js | 32 +++++++++++++++---- custom_commands/setSuppressedNotification.js | 24 ++++++++++++-- nightwatch.conf.js | 6 ++++ .../e2e/suppressedNotificationsFunctional.js | 11 +++++-- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/custom_commands/checkSuppressedNotification.js b/custom_commands/checkSuppressedNotification.js index 00e40ef..d2cd2b9 100644 --- a/custom_commands/checkSuppressedNotification.js +++ b/custom_commands/checkSuppressedNotification.js @@ -2,9 +2,20 @@ const axios = require('axios'); module.exports = class checkSuppressedNotification { - command(clinicianUsername, clinicianPassword, environment) { + /** + * command method of class checkSupressedNotification + * @param {*} clinicianUsername + * @param {*} clinicianPassword + * @param {*} environment + * @returns + */ + command(clinicId, clinicianId, clinicianUsername, clinicianPassword, environment) { return new Promise((resolve) => { const auth = btoa(`${clinicianUsername}:${clinicianPassword}`); + /** + * + * @returns @{string} session token + */ const getToken = async () => { const response = await axios.post( `${environment}/auth/login`, @@ -18,21 +29,25 @@ module.exports = class checkSuppressedNotification { ); return response.headers['x-tidepool-session-token']; }; - + /** + *asynchronous function checks if clinic id exists + *and that the suppressed notifications is enabled + * @returns {string} json body of clinic array object + */ const clinicData = async () => { const token = await getToken(); - const response = await axios.get('https://qa2.development.tidepool.org/v1/clinicians/4ae71760b6/clinics', { + const response = await axios.get(`${environment}/v1/clinicians/${clinicianId}/clinics`, { params: { limit: '1000', offset: '0', }, headers: { - authority: 'qa2.development.tidepool.org', + authority: this.api.browser_url, accept: '*/*', 'accept-language': 'en-US,en;q=0.9', cookie: '_ga=GA1.1.516068539.1692684735; _ga_RWXQ3R57PB=GS1.1.1694192507.4.0.1694192507.0.0.0', - referer: 'https://qa2.development.tidepool.org/', + referer: this.api.browser_url, 'sec-ch-ua': '"Google Chrome";v="117", "Not;A=Brand";v="8", "Chromium";v="117"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"', @@ -46,12 +61,15 @@ module.exports = class checkSuppressedNotification { }); return response.data; }; - + /** + * function calls asynchronous function clinicData and awaits for result + * @returns a resolved promise with true or false if suppressed notifications are enabled + */ const clinicDataRun = async () => { let found = false; const data = await clinicData(); for (let i = 0; i < data.length; i++) { - if (data[i].clinic.id === '62419f38f85189a39ac4b68d') { + if (data[i].clinic.id === clinicId) { if (data[i].clinic.suppressedNotifications.patientClinicInvitation === true) { resolve(true); found = true; diff --git a/custom_commands/setSuppressedNotification.js b/custom_commands/setSuppressedNotification.js index 65a7708..56b0c96 100644 --- a/custom_commands/setSuppressedNotification.js +++ b/custom_commands/setSuppressedNotification.js @@ -3,10 +3,20 @@ const axios = require('axios'); module.exports = class setSuppressedNotifcation { - command(clinicianUsername, clinicianPassword, environment) { + /** + * command method of class setSupressedNotification + * @param {*} clinicianUsername + * @param {*} clinicianPassword + * @param {*} environment + * @returns {number} http status code + */ + command(clinicId, clinicianUsername, clinicianPassword, environment) { return new Promise((resolve) => { const auth = btoa(`${clinicianUsername}:${clinicianPassword}`); - + /** + * + * @returns {string} session token + */ const getToken = async () => { const response = await axios.post( `${environment}/auth/login`, @@ -20,11 +30,15 @@ module.exports = class setSuppressedNotifcation { ); return response.headers['x-tidepool-session-token']; }; + /** + * sets suppressed notification to True status + * @returns {number} http status code + */ const setNotification = async () => { const token = await getToken(); const response = await axios.post( - 'https://qa2.development.tidepool.org/v1/clinics/62419f38f85189a39ac4b68d/suppressed_notifications', + `${environment}/v1/clinics/${clinicId}/suppressed_notifications`, { suppressedNotifications: { @@ -40,6 +54,10 @@ module.exports = class setSuppressedNotifcation { ); return response.status; }; + /** + * function calls asynchronous function setNotification and awaits for result + * @returns result http status in resolved promise + */ const setNotificationRun = async () => { const data = await setNotification(); resolve(data); diff --git a/nightwatch.conf.js b/nightwatch.conf.js index 571ba1e..c9fae28 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -61,6 +61,10 @@ module.exports = { qa2chrome: { extends: 'browserstack', launch_url: 'https://qa2.development.tidepool.org', + globals: { + clinic_id: `${process.env.QA2_CLINIC_ID}`, + clinician_id: `${process.env.QA2_CLINICIAN_ID}`, + }, environmentName: 'qa2chrome', // an extra key o help vrt distinguish between environments desiredCapabilities: { browserName: 'chrome', @@ -175,6 +179,8 @@ module.exports = { prdchrome: { extends: 'browserstack', launch_url: 'https://app.tidepool.org', + clinic_id: `${process.env.PRD_CLINIC_ID}`, + clinician_id: `${process.env.PRD_CLINICIAN_ID}`, environmentName: 'prdchrome', desiredCapabilities: { browserName: 'chrome', diff --git a/tests/e2e/suppressedNotificationsFunctional.js b/tests/e2e/suppressedNotificationsFunctional.js index c60e108..893b987 100644 --- a/tests/e2e/suppressedNotificationsFunctional.js +++ b/tests/e2e/suppressedNotificationsFunctional.js @@ -9,11 +9,14 @@ module.exports = { const clinicianUsername = browser.globals.clinicianUsername; const clinicianPassword = browser.globals.clinicianPassword; const environment = browser.launch_url; + const clinicId = browser.globals.clinic_id; + console.log(`clinicId${clinicId}`); + const clinicianId = browser.globals.clinician_id; loginPage.loadPage(); loginPage.userLogin(clinicianUsername, clinicianPassword); - let res = await browser.setSuppressedNotification(clinicianUsername, clinicianPassword, environment); + let res = await browser.setSuppressedNotification(clinicId, clinicianUsername, clinicianPassword, environment); browser.assert.strictEqual(res, 200, '/suppressed_notification returns 200 '); - res = await browser.checkSuppressedNotification(clinicianUsername, clinicianPassword, environment); + res = await browser.checkSuppressedNotification(clinicId, clinicianId, clinicianUsername, clinicianPassword, environment); browser.assert.strictEqual(res, true, '/suppressed_notification status is true in clinic object '); }, 'Clinic workspace selection'(browser) { @@ -39,6 +42,8 @@ module.exports = { 'Clinic workspace settings apply changes and check suppressed notifications status': async function (browser) { const clinicianUsername = browser.globals.clinicianUsername; const clinicianPassword = browser.globals.clinicianPassword; + const clinicId = browser.globals.clinic_id; + const clinicianId = browser.globals.clinician_id; const environment = browser.launch_url; const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; @@ -46,7 +51,7 @@ module.exports = { clinicPatientList.setValue('@city', 'new'); clinicPatientList.click('@clinicProfileSubmit'); clinicPatientList.waitForElementNotVisible('@city', browser.globals.elementTimeout); - const res = await browser.checkSuppressedNotification(clinicianUsername, clinicianPassword, environment); + const res = await browser.checkSuppressedNotification(clinicId, clinicianId, clinicianUsername, clinicianPassword, environment); browser.assert.strictEqual(res, true, '/suppressed_notification status is true in clinic object '); }, From 888e22f4b657f552267e4da49e4873b5236774da Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Mon, 10 Jun 2024 14:40:47 -0700 Subject: [PATCH 05/40] fix error due to captureElementScreenshot.js modifed file import in captureScreenshot.js so that it loads correctly --- custom_commands/captureElementScreenshot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_commands/captureElementScreenshot.js b/custom_commands/captureElementScreenshot.js index 667ffa4..2f92754 100644 --- a/custom_commands/captureElementScreenshot.js +++ b/custom_commands/captureElementScreenshot.js @@ -2,7 +2,7 @@ const EventEmitter = require('events').EventEmitter; const util = require('util'); const Jimp = require('jimp'); const Buffer = require('buffer').Buffer; -const promisifyCommand = require('../lib/promisify-command'); +const promisifyCommand = require('../node_modules/nightwatch-vrt/lib/promisify-command'); /** * Takes a screenshot of the visible region encompassed by the bounding rectangle From 71b141ccee769af1927111a1b674a5cf2fcedce6 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Tue, 11 Jun 2024 10:44:07 -0700 Subject: [PATCH 06/40] add parallel tag to clinicianPageFunctional and suppressedNotificationsFunctional add parallel tag to clinicianPageFunctional and suppressedNotificationsFunctional --- tests/e2e/clinicianPageFunctional.js | 2 +- tests/e2e/suppressedNotificationsFunctional.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/clinicianPageFunctional.js b/tests/e2e/clinicianPageFunctional.js index 5b444ca..8954768 100644 --- a/tests/e2e/clinicianPageFunctional.js +++ b/tests/e2e/clinicianPageFunctional.js @@ -2,7 +2,7 @@ require('../../utilities/seleniumKeepAlive'); module.exports = { - '@tags': ['clinician'], + '@tags': ['clinician', 'parallel'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; diff --git a/tests/e2e/suppressedNotificationsFunctional.js b/tests/e2e/suppressedNotificationsFunctional.js index 893b987..0cec163 100644 --- a/tests/e2e/suppressedNotificationsFunctional.js +++ b/tests/e2e/suppressedNotificationsFunctional.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); module.exports = { - '@tags': ['notifications'], + '@tags': ['notifications', 'parallel'], 'Clinician User Logs in with Existing Credentials with notification suppressed': async function (browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; From de2bd521cd2a7ba18c502f644c58531affd911bd Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Tue, 11 Jun 2024 19:25:22 -0700 Subject: [PATCH 07/40] rpm stats file comparison test first test for rpm stats file comparison test --- custom_commands/checkFileHash.js | 49 ++++++++++++++++++++++++++++ package.json | 3 +- pageobjects/clinicPatientListPage.js | 36 ++++++++++++++++++++ tests/e2e/rpmStatsFunctionality.js | 47 ++++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 custom_commands/checkFileHash.js create mode 100644 tests/e2e/rpmStatsFunctionality.js diff --git a/custom_commands/checkFileHash.js b/custom_commands/checkFileHash.js new file mode 100644 index 0000000..11bf15b --- /dev/null +++ b/custom_commands/checkFileHash.js @@ -0,0 +1,49 @@ +/* eslint-disable linebreak-style */ +const fs = require('fs'); +const crypto = require('crypto'); + +function sleep(millis) { + return new Promise((resolve) => setTimeout(resolve, millis)); +} + +module.exports = class checkFileHash { + /** + * command method of class checkSupressedNotification + * @param {*} hashValue string + * @returns true or false if hashvalue matches + */ + command(hashValue) { + return new Promise((resolve) => { + /** + * + * @returns @{string} file hash + */ + const getHash = (path) => new Promise((resolve, reject) => { + const hash = crypto.createHash('sha256'); + const rs = fs.createReadStream(path); + rs.on('error', reject); + rs.on('data', (chunk) => hash.update(chunk)); + rs.on('end', () => resolve(hash.digest('hex'))); + }); + /** + *asynchronous function checks if clinic id exists + *and that the suppressed notifications is enabled + * @returns {string} json body of clinic array object + */ + async function getHashRun() { + try { + await sleep(10000); + const fileHashValue = await getHash('/Users/hello/Downloads/RPM Report (05-13-2024 - 06-11-2024).csv'); + + console.log(fileHashValue === hashValue); + resolve(fileHashValue === '7d36d0458146b10b34a226205b8659f6a0748dd6b6f3135460741eb42226ae0d'); + console.log('hi'); + } catch (error) { + console.error('Error:', error); + } + } + + getHashRun(); + }); + } +}; diff --git a/package.json b/package.json index 40d9c61..310fc12 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "testintChrome": "nightwatch --env intchrome", "testdev1Chrome": "nightwatch --env dev1chrome", "testqa2ChromeClinician": "nightwatch --env qa2chrome --tag clinician", - "testqa2ChromeNotifications": "nightwatch --env qa2chrome --tag notifications" + "testqa2ChromeNotifications": "nightwatch --env qa2chrome --tag notifications", + "testqa2ChromeRpm": "nightwatch --env qa2chrome --tag rpm " }, "scriptsComments": { "testParallel": "Executes tests that are able to run in parallel on all environments", diff --git a/pageobjects/clinicPatientListPage.js b/pageobjects/clinicPatientListPage.js index a655ef8..f8cddb3 100644 --- a/pageobjects/clinicPatientListPage.js +++ b/pageobjects/clinicPatientListPage.js @@ -65,6 +65,42 @@ module.exports = { selector: '//button[@id="editClinicProfileSubmit"]', locateStrategy: 'xpath', }, + rpmReportButton: { + selector: '//button[@id="open-rpm-report-config"]', + locateStrategy: 'xpath', + }, + cgmUseFilterButton: { + selector: '//button[@id="cgm-use-filter-trigger"]', + locateStrategy: 'xpath', + }, + cgmUseLessThan70: { + selector: '//*[text()="Less than 70%"]', + locateStrategy: 'xpath', + }, + cgmUse70OrMore: { + selector: '//*[text()="70% or more"]', + locateStrategy: 'xpath', + }, + cgmUseClear: { + selector: '//button[@id="clear-cgm-use-filter"]', + locateStrategy: 'xpath', + }, + cgmUseApply: { + selector: '//button[@id="apply-cgm-use-filter"]', + locateStrategy: 'xpath', + }, + loadIconHidden: { + selector: '//*[contains(@class,"Loader--hidden")]', + locateStrategy: 'xpath', + }, + patientsSearch: { + selector: '//input[@id="patients-search"]', + locateStrategy: 'xpath', + }, + rpmReportConfirm: { + selector: '//button[@id="configureRpmReportConfirm"]', + locateStrategy: 'xpath', + }, }, commands: [{ diff --git a/tests/e2e/rpmStatsFunctionality.js b/tests/e2e/rpmStatsFunctionality.js new file mode 100644 index 0000000..9cfabe9 --- /dev/null +++ b/tests/e2e/rpmStatsFunctionality.js @@ -0,0 +1,47 @@ +/* eslint-disable linebreak-style */ +require('../../utilities/seleniumKeepAlive'); + +module.exports = { + '@tags': ['rpm', 'clinician', 'parallel'], + 'Clinician User Logs in with Existing Credentials'(browser) { + const loginPage = browser.page.loginPage(); + const clinicianUsername = browser.globals.clinicianUsername; + const clinicianPassword = browser.globals.clinicianPassword; + loginPage.loadPage(); + loginPage.userLogin(clinicianUsername, clinicianPassword); + }, + 'Clinic workspace selection'(browser) { + const clinicWorkspacePage = browser.page.clinicWorkspacePage(); + const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; + clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); + clinicWorkspace.click('@goToWorkspace'); + }, + 'Clinic workspace cgm filter 70% or more apply '(browser) { + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + clinicPatientList.waitForElementVisible('@cgmUseFilterButton', browser.globals.elementTimeout); + clinicPatientList.click('@cgmUseFilterButton'); + clinicPatientList.waitForElementVisible('@cgmUse70OrMore', browser.globals.elementTimeout); + clinicPatientList.click('@cgmUse70OrMore'); + clinicPatientList.waitForElementVisible('@cgmUseApply', browser.globals.elementTimeout); + clinicPatientList.click('@cgmUseApply'); + }, + 'Clinic workspace cgm filter search '(browser) { + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + clinicPatientList.waitForElementVisible('@patientsSearch', browser.globals.elementTimeout); + clinicPatientList.setValue('@patientsSearch', 'autodex'); + clinicPatientList.waitForElementVisible('@loadIconHidden', browser.globals.elementTimeout); + }, + 'Clinic workspace rpm stats export default': async function (browser) { + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + clinicPatientList.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout); + clinicPatientList.click('@rpmReportButton'); + clinicPatientList.waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout); + clinicPatientList.click('@rpmReportConfirm'); + const res = await browser.checkFileHash('d303e74d2e6163dd3f85d811ae335f86a5a8d3616aa8b439ad87427eaa518ee5'); + browser.assert.strictEqual(res, true, 'exported rpm csv file hash are equivalent '); + }, + +}; From 5d8ace002e3066b27047c6b75d80d8e8d5d56eba Mon Sep 17 00:00:00 2001 From: brian-tidepool <96146740+brian-tidepool@users.noreply.github.com> Date: Tue, 11 Jun 2024 19:48:51 -0700 Subject: [PATCH 08/40] Updated config.yml --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9fa38a4..8875b39 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ orbs: parameters: testEnvironment: type: string - default: "testParallel" + default: "testqa2ChromeRpm" testExecKey: type: string default: "none" From 0bcb695149f4f07b6945e39d9d2cf0dec790799f Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Mon, 24 Jun 2024 20:35:52 -0700 Subject: [PATCH 09/40] Added first set of tests for rpm stats Added functions for filters, rpm export button, and checks if sufficiency column meets criteria. Automated tests implemented checked as completed in test strategy spreadsheet: https://docs.google.com/spreadsheets/d/1gv5q_o5ghR_KMENYvsFmFxGVjKz020O9/edit?usp=sharing&ouid=117042773141856291352&rtpof=true&sd=true fix to start date and end date out of range click added logic to click previous month button if start date or end date was out of range in calendar view additional tests for filters last2days, last14days, last30days, today, and allranges split tests rpmStatsFunctionality file into multiple conditions --- .eslintrc.js | 8 + BrowserStack - List of devices to test on.csv | 4 + custom_commands/checkFileContents.js | 46 ++++ custom_commands/checkFileExists.js | 32 +++ custom_commands/checkFileHash.js | 49 ---- custom_commands/checkRPMExportSufficiency.js | 84 ++++++ package-lock.json | 118 +++++--- package.json | 9 +- pageobjects/clinicPatientListPage.js | 256 ++++++++++++++++++ rpm.csv | 3 + tests/e2e/rpmStatsFunctionality.js | 47 ---- ...sFunctionalityLastUploadFilterAllRanges.js | 194 +++++++++++++ ...FunctionalityLastUploadFilterLast14Days.js | 194 +++++++++++++ ...sFunctionalityLastUploadFilterLast2Days.js | 194 +++++++++++++ ...FunctionalityLastUploadFilterLast30days.js | 194 +++++++++++++ ...StatsFunctionalityLastUploadFilterToday.js | 193 +++++++++++++ 16 files changed, 1491 insertions(+), 134 deletions(-) create mode 100644 BrowserStack - List of devices to test on.csv create mode 100644 custom_commands/checkFileContents.js create mode 100644 custom_commands/checkFileExists.js delete mode 100644 custom_commands/checkFileHash.js create mode 100644 custom_commands/checkRPMExportSufficiency.js create mode 100644 rpm.csv delete mode 100644 tests/e2e/rpmStatsFunctionality.js create mode 100644 tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js create mode 100644 tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js create mode 100644 tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js create mode 100644 tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js create mode 100644 tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js diff --git a/.eslintrc.js b/.eslintrc.js index 82c0975..17a70ee 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -18,4 +18,12 @@ module.exports = { 'object-shorthand': ['error', 'properties'], 'no-console': 'off', }, + 'editor.codeActionsOnSave': { + 'source.fixAll.eslint': true, + 'source.fixAll': true, + }, + 'eslint.codeActionsOnSave.rules': [ + '!prefer-const' + ], + 'eslint.codeActionsOnSave.mode': 'all', }; diff --git a/BrowserStack - List of devices to test on.csv b/BrowserStack - List of devices to test on.csv new file mode 100644 index 0000000..4063bc2 --- /dev/null +++ b/BrowserStack - List of devices to test on.csv @@ -0,0 +1,4 @@ +Name,Date of Birth,MRN,# Days With Qualifying Data between 05/01/2024 and 05/30/2024,Sufficient Data for CPT-99454 +"autodex1","01/01/2000",N/A,30,TRUE +"autodex2","02/02/2002","DEXCOM",30,TRUE +"autodex3","01/01/2000","DEXCOM2",30,TRUE \ No newline at end of file diff --git a/custom_commands/checkFileContents.js b/custom_commands/checkFileContents.js new file mode 100644 index 0000000..1d794c7 --- /dev/null +++ b/custom_commands/checkFileContents.js @@ -0,0 +1,46 @@ +/* eslint-disable linebreak-style */ +const fs = require('fs'); +const crypto = require('crypto'); +const { hasUncaughtExceptionCaptureCallback } = require('process'); + + +module.exports = class checkFileContents { + /** + * command method of class checkSupressedNotification + * @ + * @returns transfers latest downloaded file from remote server to local machine + */ + command(fileName) { + return new Promise((resolve) => { + /** + * + * @returns writes file to local + */ + const getFile = (fileName) => new Promise((resolve, reject) => { + resolve(browser.executeScript(`browserstack_executor: {"action": "getFileContent", "arguments": {"fileName": "${fileName}"}}`).then((content) => { + // Decode the content to Base64 and write to a file + const decoded_data = Buffer.from(content, 'base64'); + fs.writeFile('rpm.csv', decoded_data, (err) => { + console.log(err) + //browser.quit(); + }); + + })) + }); + /** + *asynchronous function checks if clinic id exists + *and that the suppressed notifications is enabled + * @returns {string} json body of clinic array object + */ + async function getFileRun() { + try { + resolve(await getFile(fileName)); + } catch (error) { + console.error('Error:', error); + } + } + + getFileRun(); + }); + } +}; diff --git a/custom_commands/checkFileExists.js b/custom_commands/checkFileExists.js new file mode 100644 index 0000000..aa61a5d --- /dev/null +++ b/custom_commands/checkFileExists.js @@ -0,0 +1,32 @@ +/* eslint-disable linebreak-style */ +const fs = require('fs'); +const crypto = require('crypto'); + +const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +module.exports = class checkFileExists { + /** + * command method of class checkSupressedNotification + * @param {*} hashValue string + * @returns true if file exists otherwise false + */ + command(attempts, fileName) { + return new Promise((resolve, reject) => { + browser.executeScript( + `browserstack_executor: {"action": "fileExists","arguments":{"file_name":"${fileName}"}}`, + [], + (result) => { + if (result.value) { + resolve(result.value); + } else { + console.log(`fail${attempts}`); + reject(Error); + } + }, + ); + }).catch((err) => { + console.log(`att${attempts}${err}`); + if (--attempts <= 0) throw err; // give up + return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); + }); + } +}; diff --git a/custom_commands/checkFileHash.js b/custom_commands/checkFileHash.js deleted file mode 100644 index 11bf15b..0000000 --- a/custom_commands/checkFileHash.js +++ /dev/null @@ -1,49 +0,0 @@ -/* eslint-disable linebreak-style */ -const fs = require('fs'); -const crypto = require('crypto'); - -function sleep(millis) { - return new Promise((resolve) => setTimeout(resolve, millis)); -} - -module.exports = class checkFileHash { - /** - * command method of class checkSupressedNotification - * @param {*} hashValue string - * @returns true or false if hashvalue matches - */ - command(hashValue) { - return new Promise((resolve) => { - /** - * - * @returns @{string} file hash - */ - const getHash = (path) => new Promise((resolve, reject) => { - const hash = crypto.createHash('sha256'); - const rs = fs.createReadStream(path); - rs.on('error', reject); - rs.on('data', (chunk) => hash.update(chunk)); - rs.on('end', () => resolve(hash.digest('hex'))); - }); - /** - *asynchronous function checks if clinic id exists - *and that the suppressed notifications is enabled - * @returns {string} json body of clinic array object - */ - async function getHashRun() { - try { - await sleep(10000); - const fileHashValue = await getHash('/Users/hello/Downloads/RPM Report (05-13-2024 - 06-11-2024).csv'); - - console.log(fileHashValue === hashValue); - resolve(fileHashValue === '7d36d0458146b10b34a226205b8659f6a0748dd6b6f3135460741eb42226ae0d'); - console.log('hi'); - } catch (error) { - console.error('Error:', error); - } - } - - getHashRun(); - }); - } -}; diff --git a/custom_commands/checkRPMExportSufficiency.js b/custom_commands/checkRPMExportSufficiency.js new file mode 100644 index 0000000..f76e4ad --- /dev/null +++ b/custom_commands/checkRPMExportSufficiency.js @@ -0,0 +1,84 @@ +/* eslint-disable linebreak-style */ +const fs = require('fs'); +const crypto = require('crypto'); +const {parse} = require('csv-parse'); +const { hasUncaughtExceptionCaptureCallback } = require('process'); +async function getReadStreamPromise(filePath) { + return new Promise((resolve, reject) => { + let arr=[] + fs.createReadStream(filePath) + .pipe(parse({ delimiter: ',', from_line: 2 })) + .on('data', async (row) => { + arr.push(row); + //console.log(resArr) + }) + .on('end', () => { + console.log('finished'); + resolve(arr) + }) + .on('error', (error) => { + console.log(error.message); + reject(error) + }); + + }) +} +function checkSufficiency(item) { + if (item[3] > 15){ + return item[4] === 'TRUE' + } + else{ + return item[4] === 'FALSE' + } +} + + + +module.exports = class checkRPMReportSufficiency{ + /** + * command method of class checkSupressedNotification + * @param {*} filePath + * @returns true or false if hashvalue matches + */ + async command(filePath) { + return new Promise((resolve,reject) => { + /** + * + * @returns returns true if rpm exported file column 5 has the correct boolean value based on column 4 else false + */ + async function checkSufficiencyResult(){ + + let resArr = await getReadStreamPromise(filePath) + return new Promise((resolve, reject) => { + try { + + console.log('resarr'+resArr) + const result = resArr.map(checkSufficiency).every(Boolean); + resolve(result); + } + catch(error){ + reject(error) + } + + + }); + + } + /** + *asynchronous function checks if clinic id exists + *and that the suppressed notifications is enabled + * @returns {string} json body of clinic array object + */ + async function checkSufficiencyResultRun() { + try { + + resolve(await checkSufficiencyResult()); + } catch (error) { + console.error('Error:', error); + } + } + + checkSufficiencyResultRun(); + }); + } +}; diff --git a/package-lock.json b/package-lock.json index 3754050..3693450 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,15 +9,21 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "axios": "^1.6.7", + "adm-zip": "^0.5.14", + "axios": "1.6.7", + "bluebird": "3.7.2", + "csv-parse": "5.5.6", "dayjs": "1.11.10", "dotenv": "16.4.5", "eslint-config-airbnb": "19.0.4", "fast-xml-parser": "4.3.5", "form-data": "4.0.0", + "moment": "2.30.1", "nightwatch-vrt": "github:tidepool-org/nightwatch-vrt#master", "otplib": "12.0.1", - "request": "2.88.2" + "request": "2.88.2", + "selenium-webdriver": "^4.21.0", + "sleep": "6.3.0" }, "devDependencies": { "eslint": "8.57.0", @@ -368,6 +374,14 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/adm-zip": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.14.tgz", + "integrity": "sha512-DnyqqifT4Jrcvb8USYjp6FHtBpEIz1mnXu6pTRHZ0RL69LbQYiO+0lDFg5+OKA7U29oWSs3a/i8fhn8ZcceIWg==", + "engines": { + "node": ">=12.0" + } + }, "node_modules/agent-base": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", @@ -865,6 +879,11 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, "node_modules/bmp-js": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.3.tgz", @@ -1284,6 +1303,11 @@ "node": ">=18" } }, + "node_modules/csv-parse": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.6.tgz", + "integrity": "sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A==" + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -2802,8 +2826,7 @@ "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "dev": true + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" }, "node_modules/import-fresh": { "version": "3.3.0", @@ -3476,7 +3499,6 @@ "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "dev": true, "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", @@ -3487,14 +3509,12 @@ "node_modules/jszip/node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "node_modules/jszip/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3508,14 +3528,12 @@ "node_modules/jszip/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/jszip/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -3628,7 +3646,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, "dependencies": { "immediate": "~3.0.5" } @@ -3973,11 +3990,24 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/nan": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==" + }, "node_modules/nanoid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", @@ -4120,6 +4150,20 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "node_modules/nightwatch/node_modules/selenium-webdriver": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.16.0.tgz", + "integrity": "sha512-IbqpRpfGE7JDGgXHJeWuCqT/tUqnLvZ14csSwt+S8o4nJo3RtQoE9VR4jB47tP/A8ArkYsh/THuMY6kyRP6kuA==", + "dev": true, + "dependencies": { + "jszip": "^3.10.1", + "tmp": "^0.2.1", + "ws": ">=8.14.2" + }, + "engines": { + "node": ">= 14.20.0" + } + }, "node_modules/nightwatch/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -4419,8 +4463,7 @@ "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, "node_modules/parent-module": { "version": "1.0.1", @@ -4597,8 +4640,7 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "node_modules/prop-types": { "version": "15.8.1", @@ -5037,17 +5079,16 @@ } }, "node_modules/selenium-webdriver": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.16.0.tgz", - "integrity": "sha512-IbqpRpfGE7JDGgXHJeWuCqT/tUqnLvZ14csSwt+S8o4nJo3RtQoE9VR4jB47tP/A8ArkYsh/THuMY6kyRP6kuA==", - "dev": true, + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.21.0.tgz", + "integrity": "sha512-WaEJHZjOWNth1QG5FEpxpREER0qptZBMonFU6GtAqdCNLJVxbtC3E7oS/I/+Q1sf1W032Wg0Ebk+m46lANOXyQ==", "dependencies": { "jszip": "^3.10.1", - "tmp": "^0.2.1", - "ws": ">=8.14.2" + "tmp": "^0.2.3", + "ws": ">=8.16.0" }, "engines": { - "node": ">= 14.20.0" + "node": ">= 14.21.0" } }, "node_modules/semver": { @@ -5100,8 +5141,7 @@ "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, "node_modules/shebang-command": { "version": "2.0.0", @@ -5145,6 +5185,18 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/sleep": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/sleep/-/sleep-6.3.0.tgz", + "integrity": "sha512-+WgYl951qdUlb1iS97UvQ01pkauoBK9ML9I/CMPg41v0Ze4EyMlTgFTDDo32iYj98IYqxIjDMRd+L71lawFfpQ==", + "hasInstallScript": true, + "dependencies": { + "nan": "^2.14.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -5425,15 +5477,11 @@ "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" }, "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "engines": { - "node": ">=8.17.0" + "node": ">=14.14" } }, "node_modules/to-regex-range": { @@ -5672,8 +5720,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { "version": "8.3.2", @@ -5892,7 +5939,6 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "dev": true, "engines": { "node": ">=10.0.0" }, diff --git a/package.json b/package.json index 310fc12..7b0dc1c 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "testdev1Chrome": "nightwatch --env dev1chrome", "testqa2ChromeClinician": "nightwatch --env qa2chrome --tag clinician", "testqa2ChromeNotifications": "nightwatch --env qa2chrome --tag notifications", - "testqa2ChromeRpm": "nightwatch --env qa2chrome --tag rpm " + "testqa2ChromeRpm": "nightwatch --env qa2chrome --tag rpm" }, "scriptsComments": { "testParallel": "Executes tests that are able to run in parallel on all environments", @@ -34,6 +34,7 @@ }, "homepage": "https://github.com/tidepool-org/nightwatchtests#readme", "dependencies": { + "adm-zip": "^0.5.14", "axios": "1.6.7", "dayjs": "1.11.10", "dotenv": "16.4.5", @@ -42,7 +43,11 @@ "form-data": "4.0.0", "nightwatch-vrt": "github:tidepool-org/nightwatch-vrt#master", "otplib": "12.0.1", - "request": "2.88.2" + "request": "2.88.2", + "selenium-webdriver": "^4.21.0", + "csv-parse":"5.5.6", + "moment":"2.30.1" + }, "devDependencies": { "eslint": "8.57.0", diff --git a/pageobjects/clinicPatientListPage.js b/pageobjects/clinicPatientListPage.js index f8cddb3..24220d1 100644 --- a/pageobjects/clinicPatientListPage.js +++ b/pageobjects/clinicPatientListPage.js @@ -101,9 +101,265 @@ module.exports = { selector: '//button[@id="configureRpmReportConfirm"]', locateStrategy: 'xpath', }, + rpmReportStartDate: { + selector: '//input[@id="rpm-report-start-date"]', + locateStrategy: 'xpath', + }, + rpmReportEndDate: { + selector: '//input[@id="rpm-report-end-date"]', + locateStrategy: 'xpath', + }, + lastUploadFiltersDropdown:{ + selector:'//button[@id="last-upload-filter-trigger"]', + locateStrategy: 'xpath', + }, + lastUploadFiltersType:{ + selector:'//label//*[text()="CGM"]', + locateStrategy: 'xpath', + }, + lastUploadFiltersToday:{ + selector:'//label//*[text()="Today"]', + locateStrategy: 'xpath', + }, + lastUploadFiltersLast2Days:{ + selector:'//label//*[text()="Last 2 days"]', + locateStrategy: 'xpath', + }, + lastUploadFiltersLast14Days:{ + selector:'//label//*[text()="Last 14 days"]', + locateStrategy: 'xpath', + }, + lastUploadFiltersLast30Days:{ + selector:'//label//*[text()="Last 30 days"]', + locateStrategy: 'xpath', + }, + lastUploadFiltersApply:{ + selector:'//button[@id="apply-last-upload-filter"]', + locateStrategy: 'xpath', + } + , + rpmClearDates:{ + selector:'//button[contains(@aria-label,"Clear Dates")]', + locateStrategy: 'xpath', + } + , + previousMonth:{ + selector:'//div[contains(@aria-label,"Move backward to switch to the previous month.")]', + locateStrategy: 'xpath', + } + , + timeInRangeFilterButton:{ + selector:'//button[@id="time-in-range-filter-trigger"]', + locateStrategy: 'xpath', + } + , + timeInRangeFilterVeryLow:{ + selector:'//label[@for="range-timeInVeryLowPercent-filter"]', + locateStrategy: 'xpath', + } + , + timeInRangeFilterLow:{ + selector:'//label[@for="range-timeInLowPercent-filter"]', + locateStrategy: 'xpath', + } + , + timeInRangeFilterTarget:{ + selector:'//label[@for="range-timeInTargetPercent-filter"]', + locateStrategy: 'xpath', + } + , + timeInRangeFilterHigh:{ + selector:'//label[@for="range-timeInHighPercent-filter"]', + locateStrategy: 'xpath', + } + , + timeInRangeFilterVeryHigh:{ + selector:'//label[@for="range-timeInVeryHighPercent-filter"]', + locateStrategy: 'xpath', + } + , + timeInRangeFilterApply:{ + selector:'//button[@id="timeInRangeFilterConfirm"]', + locateStrategy: 'xpath', + } + + }, commands: [{ + cgmFilter70OrMore(){ + return this.waitForElementVisible('@cgmUseFilterButton', browser.globals.elementTimeout) + .click('@cgmUseFilterButton') + .waitForElementVisible('@cgmUse70OrMore', browser.globals.elementTimeout) + .click('@cgmUse70OrMore') + .waitForElementVisible('@cgmUseApply', browser.globals.elementTimeout) + .click('@cgmUseApply'); + }, + cgmFilterLessThan70(){ + return this.waitForElementVisible('@cgmUseFilterButton', browser.globals.elementTimeout) + .click('@cgmUseFilterButton') + .waitForElementVisible('@cgmUseLessThan70', browser.globals.elementTimeout) + .click('@cgmUse70OrMore') + .waitForElementVisible('@cgmUseApply', browser.globals.elementTimeout) + .click('@cgmUseApply'); + }, + patientFilterSearch(searchValue){ + return this.waitForElementVisible('@patientsSearch', browser.globals.elementTimeout) + .setValue('@patientsSearch', searchValue) + .waitForElementVisible('@loadIconHidden', browser.globals.elementTimeout); + }, + lastUploadFilterToday(){ + return this.waitForElementVisible('@lastUploadFiltersDropdown', browser.globals.elementTimeout) + .click('@lastUploadFiltersDropdown') + .waitForElementVisible('@lastUploadFiltersType', browser.globals.elementTimeout) + .click('@lastUploadFiltersType') + .waitForElementVisible('@lastUploadFiltersToday', browser.globals.elementTimeout) + .click('@lastUploadFiltersToday') + .waitForElementVisible('@lastUploadFiltersApply', browser.globals.elementTimeout) + .click('@lastUploadFiltersApply'); + }, + lastUploadFilterLast2days(){ + return this.waitForElementVisible('@lastUploadFiltersDropdown', browser.globals.elementTimeout) + .click('@lastUploadFiltersDropdown') + .waitForElementVisible('@lastUploadFiltersType', browser.globals.elementTimeout) + .click('@lastUploadFiltersType') + .waitForElementVisible('@lastUploadFiltersLast2Days', browser.globals.elementTimeout) + .click('@lastUploadFiltersLast2Days') + .waitForElementVisible('@lastUploadFiltersApply', browser.globals.elementTimeout) + .click('@lastUploadFiltersApply'); + }, + lastUploadFilterLast14days(){ + return this.waitForElementVisible('@lastUploadFiltersDropdown', browser.globals.elementTimeout) + .click('@lastUploadFiltersDropdown') + .waitForElementVisible('@lastUploadFiltersType', browser.globals.elementTimeout) + .click('@lastUploadFiltersType') + .waitForElementVisible('@lastUploadFiltersLast14Days', browser.globals.elementTimeout) + .click('@lastUploadFiltersLast14Days') + .waitForElementVisible('@lastUploadFiltersApply', browser.globals.elementTimeout) + .click('@lastUploadFiltersApply'); + }, + lastUploadFilterLast30days(){ + return this.waitForElementVisible('@lastUploadFiltersDropdown', browser.globals.elementTimeout) + .click('@lastUploadFiltersDropdown') + .waitForElementVisible('@lastUploadFiltersType', browser.globals.elementTimeout) + .click('@lastUploadFiltersType') + .waitForElementVisible('@lastUploadFiltersLast30Days', browser.globals.elementTimeout) + .click('@lastUploadFiltersLast30Days') + .waitForElementVisible('@lastUploadFiltersApply', browser.globals.elementTimeout) + .click('@lastUploadFiltersApply'); + }, + rpmExportClickCalendarStartDate(date){ + return this.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout) + .click('@rpmReportButton') + .click('@rpmClearDates') + .click('@rpmReportStartDate') + .click('xpath',`//*[contains(@aria-label,'${date}')]`) + .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) + .click('@rpmReportConfirm'); + }, + rpmExportClickCalendarStartDatePreviousMonth(date){ + return this.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout) + .click('@rpmReportButton') + .click('@rpmClearDates') + .click('@rpmReportStartDate') + .click('@previousMonth') + .click('xpath',`//*[contains(@aria-label,'${date}')]`) + .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) + .click('@rpmReportConfirm'); + }, + rpmExportClickCalendarEndDate(date){ + return this.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout) + .click('@rpmReportButton') + .click('@rpmClearDates') + .click('@rpmReportEndDate') + .click('xpath',`//*[contains(@aria-label,'${date}')]`) + .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) + .click('@rpmReportConfirm'); + }, + rpmExportClickCalendarEndDatePreviousMonth(date){ + return this.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout) + .click('@rpmReportButton') + .click('@rpmClearDates') + .click('@rpmReportEndDate') + .click('@previousMonth') + .click('xpath',`//*[contains(@aria-label,'${date}')]`) + .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) + .click('@rpmReportConfirm'); + }, + rpmExportTypeInputStartAndEndDate(start,end){ + return this.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout) + .click('@rpmReportButton') + .click('@rpmReportStartDate') + .setValue('@rpmReportStartDate', start ) + .setValue('@rpmReportEndDate', end ) + .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) + .click('@rpmReportConfirm'); + }, + rpmExportDefaultDate(){ + return this.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout) + .click('@rpmReportButton') + .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) + .click('@rpmReportConfirm'); + }, + timeInRangeVeryLow(){ + return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) + .click('@timeInRangeFilterButton') + .waitForElementVisible('@timeInRangeFilterVeryLow', browser.globals.elementTimeout) + .click('@timeInRangeFilterVeryLow') + .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) + .click('@timeInRangeFilterApply'); + }, + timeInRangeLow(){ + return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) + .click('@timeInRangeFilterButton') + .waitForElementVisible('@timeInRangeFilterLow', browser.globals.elementTimeout) + .click('@timeInRangeFilterLow') + .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) + .click('@timeInRangeFilterApply'); + }, + timeInRangeTarget(){ + return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) + .click('@timeInRangeFilterButton') + .waitForElementVisible('@timeInRangeFilterTarget', browser.globals.elementTimeout) + .click('@timeInRangeFilterTarget') + .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) + .click('@timeInRangeFilterApply'); + }, + timeInRangeHigh(){ + return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) + .click('@timeInRangeFilterButton') + .waitForElementVisible('@timeInRangeFilterHigh', browser.globals.elementTimeout) + .click('@timeInRangeFilterHigh') + .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) + .click('@timeInRangeFilterApply'); + }, + timeInRangeVeryHigh(){ + return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) + .click('@timeInRangeFilterButton') + .waitForElementVisible('@timeInRangeFilterVeryHigh', browser.globals.elementTimeout) + .click('@timeInRangeFilterVeryHigh') + .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) + .click('@timeInRangeFilterApply'); + }, + timeInRangeAll(){ + return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) + .click('@timeInRangeFilterButton') + //.moveToElement('@timeInRangeFilterVeryLow', browser.globals.elementTimeout) + .click('@timeInRangeFilterVeryLow') + //.moveToElement('@timeInRangeFilterLow', browser.globals.elementTimeout) + .click('@timeInRangeFilterLow') + //.moveToElement('@timeInRangeFilterTarget', browser.globals.elementTimeout) + .click('@timeInRangeFilterTarget') + //.moveToElement('@timeInRangeFilterVeryHigh', browser.globals.elementTimeout) + .click('@timeInRangeFilterVeryHigh') + //.moveToElement('@timeInRangeFilterHigh', browser.globals.elementTimeout) + .click('@timeInRangeFilterHigh') + .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) + .click('@timeInRangeFilterApply'); + }, + + + }], }, }, diff --git a/rpm.csv b/rpm.csv new file mode 100644 index 0000000..dd2cd25 --- /dev/null +++ b/rpm.csv @@ -0,0 +1,3 @@ +Name,Date of Birth,MRN,# Days With Qualifying Data between 06/01/2024 and 06/30/2024,Sufficient Data for CPT-99454 +"allconditions2","01/01/2001",N/A,0,FALSE +"allconditions1","01/01/2000",N/A,0,FALSE \ No newline at end of file diff --git a/tests/e2e/rpmStatsFunctionality.js b/tests/e2e/rpmStatsFunctionality.js deleted file mode 100644 index 9cfabe9..0000000 --- a/tests/e2e/rpmStatsFunctionality.js +++ /dev/null @@ -1,47 +0,0 @@ -/* eslint-disable linebreak-style */ -require('../../utilities/seleniumKeepAlive'); - -module.exports = { - '@tags': ['rpm', 'clinician', 'parallel'], - 'Clinician User Logs in with Existing Credentials'(browser) { - const loginPage = browser.page.loginPage(); - const clinicianUsername = browser.globals.clinicianUsername; - const clinicianPassword = browser.globals.clinicianPassword; - loginPage.loadPage(); - loginPage.userLogin(clinicianUsername, clinicianPassword); - }, - 'Clinic workspace selection'(browser) { - const clinicWorkspacePage = browser.page.clinicWorkspacePage(); - const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; - clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); - clinicWorkspace.click('@goToWorkspace'); - }, - 'Clinic workspace cgm filter 70% or more apply '(browser) { - const clinicPatientListPage = browser.page.clinicPatientListPage(); - const clinicPatientList = clinicPatientListPage.section.patientList; - clinicPatientList.waitForElementVisible('@cgmUseFilterButton', browser.globals.elementTimeout); - clinicPatientList.click('@cgmUseFilterButton'); - clinicPatientList.waitForElementVisible('@cgmUse70OrMore', browser.globals.elementTimeout); - clinicPatientList.click('@cgmUse70OrMore'); - clinicPatientList.waitForElementVisible('@cgmUseApply', browser.globals.elementTimeout); - clinicPatientList.click('@cgmUseApply'); - }, - 'Clinic workspace cgm filter search '(browser) { - const clinicPatientListPage = browser.page.clinicPatientListPage(); - const clinicPatientList = clinicPatientListPage.section.patientList; - clinicPatientList.waitForElementVisible('@patientsSearch', browser.globals.elementTimeout); - clinicPatientList.setValue('@patientsSearch', 'autodex'); - clinicPatientList.waitForElementVisible('@loadIconHidden', browser.globals.elementTimeout); - }, - 'Clinic workspace rpm stats export default': async function (browser) { - const clinicPatientListPage = browser.page.clinicPatientListPage(); - const clinicPatientList = clinicPatientListPage.section.patientList; - clinicPatientList.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout); - clinicPatientList.click('@rpmReportButton'); - clinicPatientList.waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout); - clinicPatientList.click('@rpmReportConfirm'); - const res = await browser.checkFileHash('d303e74d2e6163dd3f85d811ae335f86a5a8d3616aa8b439ad87427eaa518ee5'); - browser.assert.strictEqual(res, true, 'exported rpm csv file hash are equivalent '); - }, - -}; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js new file mode 100644 index 0000000..0617104 --- /dev/null +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js @@ -0,0 +1,194 @@ +/* eslint-disable linebreak-style */ +require('../../utilities/seleniumKeepAlive'); +const fs = require('fs'); +const {parse} = require('csv-parse'); +const moment = require('moment'); + +const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +const checkFileExists = (attempts, browser, fileName) => new Promise((resolve, reject) => { + browser.executeScript( + 'browserstack_executor: {"action": "fileExists","arguments":{"file_name":"RPM Report (05-15-2024 - 06-13-2024).csv"}}', + [], + (result) => { + if (result.value) { + resolve(result.value); + } else { + console.log(`fail${attempts}`); + reject(Error); + } + }, + ); +}).catch((err) => { + console.log(`att${attempts}${err}`); + if (--attempts <= 0) throw err; // give up + return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); +}); + +module.exports = { + '@tags': ['rpm', 'clinician', 'parallel'], + 'Clinician User Logs in with Existing Credentials'(browser) { + const loginPage = browser.page.loginPage(); + const clinicianUsername = browser.globals.clinicianUsername; + const clinicianPassword = browser.globals.clinicianPassword; + loginPage.loadPage(); + loginPage.userLogin(clinicianUsername, clinicianPassword); + }, + 'Clinic workspace selection'(browser) { + const clinicWorkspacePage = browser.page.clinicWorkspacePage(); + const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; + clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); + clinicWorkspace.click('@goToWorkspace'); + + }, + + 'Clinic workspace rpm stats export | timeInRangeAll |patientFilterSearch:allconditions \ + | rpmExportStartDateSelected:40 days from today ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 40 + let startDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') + let endDate = moment(startDate,'MMM D, YYYY').add(29,"days").format('MMMM D, YYYY') + let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(29,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.timeInRangeAll(); + clinicPatientList.patientFilterSearch("allconditions"); + let startDateObj = moment(startDate, 'MMMM D, YYYY') + if (moment().startOf('month').diff(startDateObj.startOf('month'),"months") === 2){ + clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate) + } + else{ + clinicPatientList.rpmExportClickCalendarStartDate(startDate) + } + + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const written =await browser.checkFileContents(fileName) + console.log('write'+written) + const sufficient = await browser.checkRPMExportSufficiency(filePath); + console.log('suff'+sufficient) + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid '); + + }, + 'Clinic workspace rpm stats export | timeInRangeAll | patientFilterSearch:allconditions \ + | rpmExportEndDateSelected:10 days from today ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 10 + let endDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') + let endDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let startDateFile = moment(endDate,'MMM D, YYYY').subtract(29,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.timeInRangeAll(); + clinicPatientList.patientFilterSearch("allconditions"); + + let endDateObj = moment(endDate, 'MMMM D, YYYY') + if (moment().startOf('month').diff(endDateObj.startOf('month'),"months") === 2){ + clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate) + } + else{ + clinicPatientList.rpmExportClickCalendarEndDate(endDate) + } + //validate file export results + try { + console.log(fileName) + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + 'Clinic workspace rpm stats export | timeInRangeAll | patientFilterSearch:allconditions \ + | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 57 + let dateRange = 15 + let startDate = moment().subtract(daysFromToday,"days").format('MMM D, YYYY') + let endDate = moment(startDate,'MMM D, YYYY').add(dateRange,"days").format('MMM D, YYYY') + let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(dateRange,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.timeInRangeAll() + clinicPatientList.patientFilterSearch("allconditions"); + + clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate,endDate); + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + 'Clinic workspace rpm stats export | timeInRangeAll | patientFilterSearch:allconditions \ + | rpmExportDefaultDate ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let startDateFile = moment().subtract(29,"days").format('MM-DD-YYYY') + let endDateFile = today + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.timeInRangeAll(); + clinicPatientList.patientFilterSearch("allconditions"); + + clinicPatientList.rpmExportDefaultDate(); + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + +}; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js new file mode 100644 index 0000000..c771f90 --- /dev/null +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js @@ -0,0 +1,194 @@ +/* eslint-disable linebreak-style */ +require('../../utilities/seleniumKeepAlive'); +const fs = require('fs'); +const {parse} = require('csv-parse'); +const moment = require('moment'); + +const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +const checkFileExists = (attempts, browser, fileName) => new Promise((resolve, reject) => { + browser.executeScript( + 'browserstack_executor: {"action": "fileExists","arguments":{"file_name":"RPM Report (05-15-2024 - 06-13-2024).csv"}}', + [], + (result) => { + if (result.value) { + resolve(result.value); + } else { + console.log(`fail${attempts}`); + reject(Error); + } + }, + ); +}).catch((err) => { + console.log(`att${attempts}${err}`); + if (--attempts <= 0) throw err; // give up + return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); +}); + +module.exports = { + '@tags': ['rpm', 'clinician', 'parallel'], + 'Clinician User Logs in with Existing Credentials'(browser) { + const loginPage = browser.page.loginPage(); + const clinicianUsername = browser.globals.clinicianUsername; + const clinicianPassword = browser.globals.clinicianPassword; + loginPage.loadPage(); + loginPage.userLogin(clinicianUsername, clinicianPassword); + }, + 'Clinic workspace selection'(browser) { + const clinicWorkspacePage = browser.page.clinicWorkspacePage(); + const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; + clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); + clinicWorkspace.click('@goToWorkspace'); + + }, + + 'Clinic workspace rpm stats export | lastUploadFilterLast14days | patientFilterSearch:autodex \ + | rpmExportStartDateSelected:40 days from today ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 40 + let startDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') + let endDate = moment(startDate,'MMM D, YYYY').add(29,"days").format('MMMM D, YYYY') + let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(29,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterLast14days(); + clinicPatientList.patientFilterSearch("autodex"); + let startDateObj = moment(startDate, 'MMMM D, YYYY') + if (moment().startOf('month').diff(startDateObj.startOf('month'),"months") === 2){ + clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate) + } + else{ + clinicPatientList.rpmExportClickCalendarStartDate(startDate) + } + + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const written =await browser.checkFileContents(fileName) + console.log('write'+written) + const sufficient = await browser.checkRPMExportSufficiency(filePath); + console.log('suff'+sufficient) + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid '); + + }, + 'Clinic workspace rpm stats export | lastUploadFilterLast14days | patientFilterSearch:autodex \ + | rpmExportEndDateSelected:10 days from today ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 10 + let endDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') + let endDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let startDateFile = moment(endDate,'MMM D, YYYY').subtract(29,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterLast14days(); + clinicPatientList.patientFilterSearch("autodex"); + + let endDateObj = moment(endDate, 'MMMM D, YYYY') + if (moment().startOf('month').diff(endDateObj.startOf('month'),"months") === 2){ + clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate) + } + else{ + clinicPatientList.rpmExportClickCalendarEndDate(endDate) + } + //validate file export results + try { + console.log(fileName) + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + 'Clinic workspace rpm stats export | lastUploadFilterLast14days | patientFilterSearch:autodex \ + | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 57 + let dateRange = 15 + let startDate = moment().subtract(daysFromToday,"days").format('MMM D, YYYY') + let endDate = moment(startDate,'MMM D, YYYY').add(dateRange,"days").format('MMM D, YYYY') + let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(dateRange,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterLast14days(); + clinicPatientList.patientFilterSearch("autodex"); + + clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate,endDate); + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + 'Clinic workspace rpm stats export | lastUploadFilterLast14days | patientFilterSearch:autodex \ + | rpmExportDefaultDate ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let startDateFile = moment().subtract(29,"days").format('MM-DD-YYYY') + let endDateFile = today + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterLast14days(); + clinicPatientList.patientFilterSearch("autodex"); + + clinicPatientList.rpmExportDefaultDate(); + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + +}; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js new file mode 100644 index 0000000..3fb98af --- /dev/null +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js @@ -0,0 +1,194 @@ +/* eslint-disable linebreak-style */ +require('../../utilities/seleniumKeepAlive'); +const fs = require('fs'); +const {parse} = require('csv-parse'); +const moment = require('moment'); + +const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +const checkFileExists = (attempts, browser, fileName) => new Promise((resolve, reject) => { + browser.executeScript( + 'browserstack_executor: {"action": "fileExists","arguments":{"file_name":"RPM Report (05-15-2024 - 06-13-2024).csv"}}', + [], + (result) => { + if (result.value) { + resolve(result.value); + } else { + console.log(`fail${attempts}`); + reject(Error); + } + }, + ); +}).catch((err) => { + console.log(`att${attempts}${err}`); + if (--attempts <= 0) throw err; // give up + return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); +}); + +module.exports = { + '@tags': ['rpm', 'clinician', 'parallel'], + 'Clinician User Logs in with Existing Credentials'(browser) { + const loginPage = browser.page.loginPage(); + const clinicianUsername = browser.globals.clinicianUsername; + const clinicianPassword = browser.globals.clinicianPassword; + loginPage.loadPage(); + loginPage.userLogin(clinicianUsername, clinicianPassword); + }, + 'Clinic workspace selection'(browser) { + const clinicWorkspacePage = browser.page.clinicWorkspacePage(); + const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; + clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); + clinicWorkspace.click('@goToWorkspace'); + + }, + + 'Clinic workspace rpm stats export | lastUploadFilterLast2days | patientFilterSearch:autodex \ + | rpmExportStartDateSelected:40 days from today ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 40 + let startDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') + let endDate = moment(startDate,'MMM D, YYYY').add(29,"days").format('MMMM D, YYYY') + let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(29,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterLast2days(); + clinicPatientList.patientFilterSearch("autodex"); + let startDateObj = moment(startDate, 'MMMM D, YYYY') + if (moment().startOf('month').diff(startDateObj.startOf('month'),"months") === 2){ + clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate) + } + else{ + clinicPatientList.rpmExportClickCalendarStartDate(startDate) + } + + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const written =await browser.checkFileContents(fileName) + console.log('write'+written) + const sufficient = await browser.checkRPMExportSufficiency(filePath); + console.log('suff'+sufficient) + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid '); + + }, + 'Clinic workspace rpm stats export | lastUploadFilterLast2days | patientFilterSearch:autodex \ + | rpmExportEndDateSelected:10 days from today ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 10 + let endDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') + let endDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let startDateFile = moment(endDate,'MMM D, YYYY').subtract(29,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterLast2days(); + clinicPatientList.patientFilterSearch("autodex"); + + let endDateObj = moment(endDate, 'MMMM D, YYYY') + if (moment().startOf('month').diff(endDateObj.startOf('month'),"months") === 2){ + clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate) + } + else{ + clinicPatientList.rpmExportClickCalendarEndDate(endDate) + } + //validate file export results + try { + console.log(fileName) + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + 'Clinic workspace rpm stats export | lastUploadFilterLast2days | patientFilterSearch:autodex \ + | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 57 + let dateRange = 15 + let startDate = moment().subtract(daysFromToday,"days").format('MMM D, YYYY') + let endDate = moment(startDate,'MMM D, YYYY').add(dateRange,"days").format('MMM D, YYYY') + let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(dateRange,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterLast2days(); + clinicPatientList.patientFilterSearch("autodex"); + + clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate,endDate); + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + 'Clinic workspace rpm stats export | lastUploadFilterLast2days | patientFilterSearch:autodex \ + | rpmExportDefaultDate ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let startDateFile = moment().subtract(29,"days").format('MM-DD-YYYY') + let endDateFile = today + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterLast2days(); + clinicPatientList.patientFilterSearch("autodex"); + + clinicPatientList.rpmExportDefaultDate(); + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + +}; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js new file mode 100644 index 0000000..abed458 --- /dev/null +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js @@ -0,0 +1,194 @@ +/* eslint-disable linebreak-style */ +require('../../utilities/seleniumKeepAlive'); +const fs = require('fs'); +const {parse} = require('csv-parse'); +const moment = require('moment'); + +const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +const checkFileExists = (attempts, browser, fileName) => new Promise((resolve, reject) => { + browser.executeScript( + 'browserstack_executor: {"action": "fileExists","arguments":{"file_name":"RPM Report (05-15-2024 - 06-13-2024).csv"}}', + [], + (result) => { + if (result.value) { + resolve(result.value); + } else { + console.log(`fail${attempts}`); + reject(Error); + } + }, + ); +}).catch((err) => { + console.log(`att${attempts}${err}`); + if (--attempts <= 0) throw err; // give up + return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); +}); + +module.exports = { + '@tags': ['rpm', 'clinician', 'parallel'], + 'Clinician User Logs in with Existing Credentials'(browser) { + const loginPage = browser.page.loginPage(); + const clinicianUsername = browser.globals.clinicianUsername; + const clinicianPassword = browser.globals.clinicianPassword; + loginPage.loadPage(); + loginPage.userLogin(clinicianUsername, clinicianPassword); + }, + 'Clinic workspace selection'(browser) { + const clinicWorkspacePage = browser.page.clinicWorkspacePage(); + const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; + clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); + clinicWorkspace.click('@goToWorkspace'); + + }, + + 'Clinic workspace rpm stats export | lastUploadFilterLast30days | patientFilterSearch:autodex \ + | rpmExportStartDateSelected:40 days from today ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 40 + let startDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') + let endDate = moment(startDate,'MMM D, YYYY').add(29,"days").format('MMMM D, YYYY') + let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(29,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterLast30days(); + clinicPatientList.patientFilterSearch("autodex"); + let startDateObj = moment(startDate, 'MMMM D, YYYY') + if (moment().startOf('month').diff(startDateObj.startOf('month'),"months") === 2){ + clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate) + } + else{ + clinicPatientList.rpmExportClickCalendarStartDate(startDate) + } + + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const written =await browser.checkFileContents(fileName) + console.log('write'+written) + const sufficient = await browser.checkRPMExportSufficiency(filePath); + console.log('suff'+sufficient) + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid '); + + }, + 'Clinic workspace rpm stats export | lastUploadFilterLast30days | patientFilterSearch:autodex \ + | rpmExportEndDateSelected:10 days from today ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 10 + let endDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') + let endDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let startDateFile = moment(endDate,'MMM D, YYYY').subtract(29,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterLast30days(); + clinicPatientList.patientFilterSearch("autodex"); + + let endDateObj = moment(endDate, 'MMMM D, YYYY') + if (moment().startOf('month').diff(endDateObj.startOf('month'),"months") === 2){ + clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate) + } + else{ + clinicPatientList.rpmExportClickCalendarEndDate(endDate) + } + //validate file export results + try { + console.log(fileName) + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + 'Clinic workspace rpm stats export | lastUploadFilterLast30days | patientFilterSearch:autodex \ + | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 57 + let dateRange = 15 + let startDate = moment().subtract(daysFromToday,"days").format('MMM D, YYYY') + let endDate = moment(startDate,'MMM D, YYYY').add(dateRange,"days").format('MMM D, YYYY') + let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(dateRange,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterLast30days(); + clinicPatientList.patientFilterSearch("autodex"); + + clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate,endDate); + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + 'Clinic workspace rpm stats export | lastUploadFilterLast30days | patientFilterSearch:autodex \ + | rpmExportDefaultDate ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let startDateFile = moment().subtract(29,"days").format('MM-DD-YYYY') + let endDateFile = today + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterLast30days(); + clinicPatientList.patientFilterSearch("autodex"); + + clinicPatientList.rpmExportDefaultDate(); + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + +}; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js new file mode 100644 index 0000000..a14cf01 --- /dev/null +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js @@ -0,0 +1,193 @@ +/* eslint-disable linebreak-style */ +require('../../utilities/seleniumKeepAlive'); +const fs = require('fs'); +const {parse} = require('csv-parse'); +const moment = require('moment'); + +const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +const checkFileExists = (attempts, browser, fileName) => new Promise((resolve, reject) => { + browser.executeScript( + 'browserstack_executor: {"action": "fileExists","arguments":{"file_name":"RPM Report (05-15-2024 - 06-13-2024).csv"}}', + [], + (result) => { + if (result.value) { + resolve(result.value); + } else { + console.log(`fail${attempts}`); + reject(Error); + } + }, + ); +}).catch((err) => { + console.log(`att${attempts}${err}`); + if (--attempts <= 0) throw err; // give up + return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); +}); + +module.exports = { + '@tags': ['rpm', 'clinician', 'parallel'], + 'Clinician User Logs in with Existing Credentials'(browser) { + const loginPage = browser.page.loginPage(); + const clinicianUsername = browser.globals.clinicianUsername; + const clinicianPassword = browser.globals.clinicianPassword; + loginPage.loadPage(); + loginPage.userLogin(clinicianUsername, clinicianPassword); + }, + 'Clinic workspace selection'(browser) { + const clinicWorkspacePage = browser.page.clinicWorkspacePage(); + const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; + clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); + clinicWorkspace.click('@goToWorkspace'); + + }, + + 'Clinic workspace rpm stats export | lastUploadFilterToday | patientFilterSearch:autodex \ + | rpmExportStartDateSelected:40 days from today ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 40 + let startDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') + let endDate = moment(startDate,'MMM D, YYYY').add(29,"days").format('MMMM D, YYYY') + let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(29,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterToday(); + clinicPatientList.patientFilterSearch("autodex"); + let startDateObj = moment(startDate, 'MMMM D, YYYY') + if (moment().startOf('month').diff(startDateObj.startOf('month'),"months") === 2){ + clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate) + } + else{ + clinicPatientList.rpmExportClickCalendarStartDate(startDate) + } + + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const written =await browser.checkFileContents(fileName) + console.log('write'+written) + const sufficient = await browser.checkRPMExportSufficiency(filePath); + console.log('suff'+sufficient) + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid '); + + }, + 'Clinic workspace rpm stats export | lastUploadFilterToday | patientFilterSearch:autodex \ + | rpmExportEndDateSelected:10 days from today ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 10 + let endDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') + let endDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let startDateFile = moment(endDate,'MMM D, YYYY').subtract(29,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterToday(); + clinicPatientList.patientFilterSearch("autodex"); + + let endDateObj = moment(endDate, 'MMMM D, YYYY') + if (moment().startOf('month').diff(endDateObj.startOf('month'),"months") === 2){ + clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate) + } + else{ + clinicPatientList.rpmExportClickCalendarEndDate(endDate) + } + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + 'Clinic workspace rpm stats export | lastUploadFilterToday | patientFilterSearch:autodex \ + | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let daysFromToday = 57 + let dateRange = 15 + let startDate = moment().subtract(daysFromToday,"days").format('MMM D, YYYY') + let endDate = moment(startDate,'MMM D, YYYY').add(dateRange,"days").format('MMM D, YYYY') + let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') + let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(dateRange,"days").format('MM-DD-YYYY') + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterToday(); + clinicPatientList.patientFilterSearch("autodex"); + + clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate,endDate); + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + 'Clinic workspace rpm stats export | lastUploadFilterToday | patientFilterSearch:autodex \ + | rpmExportDefaultDate ': async function (browser) { + //setup config + const clinicPatientListPage = browser.page.clinicPatientListPage(); + const clinicPatientList = clinicPatientListPage.section.patientList; + let today = moment().format('MM-DD-YYYY') + let startDateFile = moment().subtract(29,"days").format('MM-DD-YYYY') + let endDateFile = today + let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const attemptsCheckFileExists = 10; + const filePath = './rpm.csv' + + //filter criteria and rpm report submit + + + clinicPatientList.lastUploadFilterToday(); + clinicPatientList.patientFilterSearch("autodex"); + + clinicPatientList.rpmExportDefaultDate(); + //validate file export results + try { + const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); + browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); + } catch (error) { + console.log(error); + browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); + } + const sufficient = await browser.checkRPMExportSufficiency(filePath); + browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); + + }, + +}; From 2d90b574128c0365533e4bb77da0ed26ad01a532 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Thu, 18 Jul 2024 11:26:06 -0700 Subject: [PATCH 10/40] fix eslint errors fixed all es lint errors excluding class-methods-use-this since it results in a nighwatch error --- .eslintrc.js | 11 +- custom_commands/captureElementScreenshot.js | 4 +- custom_commands/checkFileContents.js | 24 +- custom_commands/checkFileExists.js | 12 +- custom_commands/checkRPMExportSufficiency.js | 89 ++-- .../checkSuppressedNotification.js | 2 +- nightwatch.conf.js | 2 + package.json | 6 +- pageobjects/clinicPatientListPage.js | 381 +++++++++--------- rpm.csv | 7 +- ...sFunctionalityLastUploadFilterAllRanges.js | 169 +++----- ...FunctionalityLastUploadFilterLast14Days.js | 167 +++----- ...sFunctionalityLastUploadFilterLast2Days.js | 167 +++----- ...FunctionalityLastUploadFilterLast30days.js | 167 +++----- ...StatsFunctionalityLastUploadFilterToday.js | 167 +++----- .../e2e/suppressedNotificationsFunctional.js | 4 +- 16 files changed, 579 insertions(+), 800 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 17a70ee..b8593dd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,13 +17,10 @@ module.exports = { 'chai-friendly/no-unused-expressions': 'off', 'object-shorthand': ['error', 'properties'], 'no-console': 'off', + 'import/no-extraneous-dependencies': ['error', { devDependencies: false, optionalDependencies: false, peerDependencies: false }], }, - 'editor.codeActionsOnSave': { - 'source.fixAll.eslint': true, - 'source.fixAll': true, + globals: { + browser: true, }, - 'eslint.codeActionsOnSave.rules': [ - '!prefer-const' - ], - 'eslint.codeActionsOnSave.mode': 'all', + }; diff --git a/custom_commands/captureElementScreenshot.js b/custom_commands/captureElementScreenshot.js index 2f92754..dc8d3fd 100644 --- a/custom_commands/captureElementScreenshot.js +++ b/custom_commands/captureElementScreenshot.js @@ -10,7 +10,8 @@ const promisifyCommand = require('../node_modules/nightwatch-vrt/lib/promisify-c * * @link * @param {string} id ID of the element to route the command to. - * @param {function} callback Callback function which is called with the captured screenshot as an argument. + * @param {function} callback Callback function which + * is called with the captured screenshot as an argument. * @returns {Object} The captured screenshot. This object is a Jimp (library) image instance. */ function CaptureElementScreenshot() { @@ -28,6 +29,7 @@ CaptureElementScreenshot.prototype.command = function command( Promise.all([ promisifyCommand(api, 'takeElementScreenshot', [selector]), ]).then(([screenshotEncoded]) => { + // eslint-disable-next-line no-buffer-constructor Jimp.read(new Buffer(screenshotEncoded, 'base64')).then((screenshot) => { this.api.assert.ok(true, `The screenshot for selector <${selector.name}> was captured successfully.`); diff --git a/custom_commands/checkFileContents.js b/custom_commands/checkFileContents.js index 1d794c7..98b9d7a 100644 --- a/custom_commands/checkFileContents.js +++ b/custom_commands/checkFileContents.js @@ -1,8 +1,16 @@ /* eslint-disable linebreak-style */ const fs = require('fs'); -const crypto = require('crypto'); -const { hasUncaughtExceptionCaptureCallback } = require('process'); +const getFile = (fileName) => new Promise((resolve) => { + resolve(browser.executeScript(`browserstack_executor: {"action": "getFileContent", "arguments": {"fileName": "${fileName}"}}`).then((content) => { + // Decode the content to Base64 and write to a file + const decodedData = Buffer.from(content, 'base64'); + fs.writeFile('rpm.csv', decodedData, (err) => { + console.log(err); + // browser.quit(); + }); + })); +}); module.exports = class checkFileContents { /** @@ -16,17 +24,7 @@ module.exports = class checkFileContents { * * @returns writes file to local */ - const getFile = (fileName) => new Promise((resolve, reject) => { - resolve(browser.executeScript(`browserstack_executor: {"action": "getFileContent", "arguments": {"fileName": "${fileName}"}}`).then((content) => { - // Decode the content to Base64 and write to a file - const decoded_data = Buffer.from(content, 'base64'); - fs.writeFile('rpm.csv', decoded_data, (err) => { - console.log(err) - //browser.quit(); - }); - - })) - }); + /** *asynchronous function checks if clinic id exists *and that the suppressed notifications is enabled diff --git a/custom_commands/checkFileExists.js b/custom_commands/checkFileExists.js index aa61a5d..3613da5 100644 --- a/custom_commands/checkFileExists.js +++ b/custom_commands/checkFileExists.js @@ -1,20 +1,23 @@ /* eslint-disable linebreak-style */ -const fs = require('fs'); -const crypto = require('crypto'); -const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +function delay(ms) { + return new Promise((resolve) => { setTimeout(resolve, ms); }); +} module.exports = class checkFileExists { /** * command method of class checkSupressedNotification * @param {*} hashValue string * @returns true if file exists otherwise false */ + command(attempts, fileName) { + let attempts1 = attempts; return new Promise((resolve, reject) => { browser.executeScript( `browserstack_executor: {"action": "fileExists","arguments":{"file_name":"${fileName}"}}`, [], (result) => { + console.log(`res${result}`); if (result.value) { resolve(result.value); } else { @@ -25,7 +28,8 @@ module.exports = class checkFileExists { ); }).catch((err) => { console.log(`att${attempts}${err}`); - if (--attempts <= 0) throw err; // give up + attempts1 -= 1; + if (attempts1 <= 0) throw err; // give up return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); }); } diff --git a/custom_commands/checkRPMExportSufficiency.js b/custom_commands/checkRPMExportSufficiency.js index f76e4ad..2e7ca60 100644 --- a/custom_commands/checkRPMExportSufficiency.js +++ b/custom_commands/checkRPMExportSufficiency.js @@ -1,69 +1,61 @@ /* eslint-disable linebreak-style */ const fs = require('fs'); -const crypto = require('crypto'); -const {parse} = require('csv-parse'); -const { hasUncaughtExceptionCaptureCallback } = require('process'); +const { parse } = require('csv-parse'); + async function getReadStreamPromise(filePath) { return new Promise((resolve, reject) => { - let arr=[] - fs.createReadStream(filePath) - .pipe(parse({ delimiter: ',', from_line: 2 })) - .on('data', async (row) => { - arr.push(row); - //console.log(resArr) - }) - .on('end', () => { - console.log('finished'); - resolve(arr) - }) - .on('error', (error) => { - console.log(error.message); - reject(error) + const arr = []; + fs.createReadStream(filePath) + .pipe(parse({ delimiter: ',', from_line: 2 })) + .on('data', async (row) => { + arr.push(row); + // console.log(resArr) + }) + .on('end', () => { + console.log('finished'); + resolve(arr); + }) + .on('error', (error) => { + console.log(error.message); + reject(error); + }); }); - - }) } function checkSufficiency(item) { - if (item[3] > 15){ - return item[4] === 'TRUE' + if (item[3] > 15) { + return item[4] === 'TRUE'; } - else{ - return item[4] === 'FALSE' - } -} + return item[4] === 'FALSE'; +} +async function checkSufficiencyResult(filePath) { + const resArr = await getReadStreamPromise(filePath); + return new Promise((resolve, reject) => { + try { + console.log(`resarr${resArr}`); + const result = resArr.map(checkSufficiency).every(Boolean); + resolve(result); + } catch (error) { + reject(error); + } + }); +} -module.exports = class checkRPMReportSufficiency{ +module.exports = class checkRPMReportSufficiency { /** * command method of class checkSupressedNotification - * @param {*} filePath + * @param {*} filePath * @returns true or false if hashvalue matches */ async command(filePath) { - return new Promise((resolve,reject) => { + return new Promise((resolve) => { /** * - * @returns returns true if rpm exported file column 5 has the correct boolean value based on column 4 else false + * @returns returns true if rpm exported file column 5 has the + * correct boolean value based on column 4 else false */ - async function checkSufficiencyResult(){ - - let resArr = await getReadStreamPromise(filePath) - return new Promise((resolve, reject) => { - try { - - console.log('resarr'+resArr) - const result = resArr.map(checkSufficiency).every(Boolean); - resolve(result); - } - catch(error){ - reject(error) - } - - - }); - } /** *asynchronous function checks if clinic id exists *and that the suppressed notifications is enabled @@ -71,13 +63,12 @@ module.exports = class checkRPMReportSufficiency{ */ async function checkSufficiencyResultRun() { try { - - resolve(await checkSufficiencyResult()); + resolve(await checkSufficiencyResult(filePath)); } catch (error) { console.error('Error:', error); } } - + checkSufficiencyResultRun(); }); } diff --git a/custom_commands/checkSuppressedNotification.js b/custom_commands/checkSuppressedNotification.js index d2cd2b9..cc4eef5 100644 --- a/custom_commands/checkSuppressedNotification.js +++ b/custom_commands/checkSuppressedNotification.js @@ -68,7 +68,7 @@ module.exports = class checkSuppressedNotification { const clinicDataRun = async () => { let found = false; const data = await clinicData(); - for (let i = 0; i < data.length; i++) { + for (let i = 0; i < data.length; i += 1) { if (data[i].clinic.id === clinicId) { if (data[i].clinic.suppressedNotifications.patientClinicInvitation === true) { resolve(true); diff --git a/nightwatch.conf.js b/nightwatch.conf.js index c9fae28..37cf75f 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -12,6 +12,8 @@ module.exports = { test_settings: { default: { + silent: true, + detailed_output: false, disable_error_log: false, launch_url: 'qa2.development.tidepool.org', diff --git a/package.json b/package.json index 7b0dc1c..22ec9b7 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,8 @@ "request": "2.88.2", "selenium-webdriver": "^4.21.0", "csv-parse":"5.5.6", - "moment":"2.30.1" + "moment":"2.30.1", + "jimp":"0.22.12" }, "devDependencies": { @@ -56,5 +57,6 @@ "eslint-plugin-import": "2.29.1", "junit-report-merger": "6.0.3", "nightwatch": "3.4.1" - } + }, + "env":{"browser": true} } diff --git a/pageobjects/clinicPatientListPage.js b/pageobjects/clinicPatientListPage.js index 24220d1..200ae83 100644 --- a/pageobjects/clinicPatientListPage.js +++ b/pageobjects/clinicPatientListPage.js @@ -109,256 +109,243 @@ module.exports = { selector: '//input[@id="rpm-report-end-date"]', locateStrategy: 'xpath', }, - lastUploadFiltersDropdown:{ - selector:'//button[@id="last-upload-filter-trigger"]', + lastUploadFiltersDropdown: { + selector: '//button[@id="last-upload-filter-trigger"]', locateStrategy: 'xpath', }, - lastUploadFiltersType:{ - selector:'//label//*[text()="CGM"]', + lastUploadFiltersType: { + selector: '//label//*[text()="CGM"]', locateStrategy: 'xpath', }, - lastUploadFiltersToday:{ - selector:'//label//*[text()="Today"]', + lastUploadFiltersToday: { + selector: '//label//*[text()="Today"]', locateStrategy: 'xpath', }, - lastUploadFiltersLast2Days:{ - selector:'//label//*[text()="Last 2 days"]', + lastUploadFiltersLast2Days: { + selector: '//label//*[text()="Last 2 days"]', locateStrategy: 'xpath', }, - lastUploadFiltersLast14Days:{ - selector:'//label//*[text()="Last 14 days"]', + lastUploadFiltersLast14Days: { + selector: '//label//*[text()="Last 14 days"]', locateStrategy: 'xpath', }, - lastUploadFiltersLast30Days:{ - selector:'//label//*[text()="Last 30 days"]', + lastUploadFiltersLast30Days: { + selector: '//label//*[text()="Last 30 days"]', locateStrategy: 'xpath', }, - lastUploadFiltersApply:{ - selector:'//button[@id="apply-last-upload-filter"]', + lastUploadFiltersApply: { + selector: '//button[@id="apply-last-upload-filter"]', locateStrategy: 'xpath', - } - , - rpmClearDates:{ - selector:'//button[contains(@aria-label,"Clear Dates")]', + }, + rpmClearDates: { + selector: '//button[contains(@aria-label,"Clear Dates")]', locateStrategy: 'xpath', - } - , - previousMonth:{ - selector:'//div[contains(@aria-label,"Move backward to switch to the previous month.")]', + }, + previousMonth: { + selector: '//div[contains(@aria-label,"Move backward to switch to the previous month.")]', locateStrategy: 'xpath', - } - , - timeInRangeFilterButton:{ - selector:'//button[@id="time-in-range-filter-trigger"]', + }, + timeInRangeFilterButton: { + selector: '//button[@id="time-in-range-filter-trigger"]', locateStrategy: 'xpath', - } - , - timeInRangeFilterVeryLow:{ - selector:'//label[@for="range-timeInVeryLowPercent-filter"]', + }, + timeInRangeFilterVeryLow: { + selector: '//label[@for="range-timeInVeryLowPercent-filter"]', locateStrategy: 'xpath', - } - , - timeInRangeFilterLow:{ - selector:'//label[@for="range-timeInLowPercent-filter"]', + }, + timeInRangeFilterLow: { + selector: '//label[@for="range-timeInLowPercent-filter"]', locateStrategy: 'xpath', - } - , - timeInRangeFilterTarget:{ - selector:'//label[@for="range-timeInTargetPercent-filter"]', + }, + timeInRangeFilterTarget: { + selector: '//label[@for="range-timeInTargetPercent-filter"]', locateStrategy: 'xpath', - } - , - timeInRangeFilterHigh:{ - selector:'//label[@for="range-timeInHighPercent-filter"]', + }, + timeInRangeFilterHigh: { + selector: '//label[@for="range-timeInHighPercent-filter"]', locateStrategy: 'xpath', - } - , - timeInRangeFilterVeryHigh:{ - selector:'//label[@for="range-timeInVeryHighPercent-filter"]', + }, + timeInRangeFilterVeryHigh: { + selector: '//label[@for="range-timeInVeryHighPercent-filter"]', locateStrategy: 'xpath', - } - , - timeInRangeFilterApply:{ - selector:'//button[@id="timeInRangeFilterConfirm"]', + }, + timeInRangeFilterApply: { + selector: '//button[@id="timeInRangeFilterConfirm"]', locateStrategy: 'xpath', - } - - + }, }, commands: [{ - cgmFilter70OrMore(){ + cgmFilter70OrMore() { return this.waitForElementVisible('@cgmUseFilterButton', browser.globals.elementTimeout) - .click('@cgmUseFilterButton') - .waitForElementVisible('@cgmUse70OrMore', browser.globals.elementTimeout) - .click('@cgmUse70OrMore') - .waitForElementVisible('@cgmUseApply', browser.globals.elementTimeout) - .click('@cgmUseApply'); + .click('@cgmUseFilterButton') + .waitForElementVisible('@cgmUse70OrMore', browser.globals.elementTimeout) + .click('@cgmUse70OrMore') + .waitForElementVisible('@cgmUseApply', browser.globals.elementTimeout) + .click('@cgmUseApply'); }, - cgmFilterLessThan70(){ + cgmFilterLessThan70() { return this.waitForElementVisible('@cgmUseFilterButton', browser.globals.elementTimeout) - .click('@cgmUseFilterButton') - .waitForElementVisible('@cgmUseLessThan70', browser.globals.elementTimeout) - .click('@cgmUse70OrMore') - .waitForElementVisible('@cgmUseApply', browser.globals.elementTimeout) - .click('@cgmUseApply'); + .click('@cgmUseFilterButton') + .waitForElementVisible('@cgmUseLessThan70', browser.globals.elementTimeout) + .click('@cgmUse70OrMore') + .waitForElementVisible('@cgmUseApply', browser.globals.elementTimeout) + .click('@cgmUseApply'); }, - patientFilterSearch(searchValue){ + patientFilterSearch(searchValue) { return this.waitForElementVisible('@patientsSearch', browser.globals.elementTimeout) - .setValue('@patientsSearch', searchValue) - .waitForElementVisible('@loadIconHidden', browser.globals.elementTimeout); + .setValue('@patientsSearch', searchValue) + .waitForElementVisible('@loadIconHidden', browser.globals.elementTimeout); }, - lastUploadFilterToday(){ + lastUploadFilterToday() { return this.waitForElementVisible('@lastUploadFiltersDropdown', browser.globals.elementTimeout) - .click('@lastUploadFiltersDropdown') - .waitForElementVisible('@lastUploadFiltersType', browser.globals.elementTimeout) - .click('@lastUploadFiltersType') - .waitForElementVisible('@lastUploadFiltersToday', browser.globals.elementTimeout) - .click('@lastUploadFiltersToday') - .waitForElementVisible('@lastUploadFiltersApply', browser.globals.elementTimeout) - .click('@lastUploadFiltersApply'); - }, - lastUploadFilterLast2days(){ + .click('@lastUploadFiltersDropdown') + .waitForElementVisible('@lastUploadFiltersType', browser.globals.elementTimeout) + .click('@lastUploadFiltersType') + .waitForElementVisible('@lastUploadFiltersLast2Days', browser.globals.elementTimeout) + .click('@lastUploadFiltersLast2Days') + .waitForElementVisible('@lastUploadFiltersApply', browser.globals.elementTimeout) + .click('@lastUploadFiltersApply'); + }, + lastUploadFilterLast2days() { return this.waitForElementVisible('@lastUploadFiltersDropdown', browser.globals.elementTimeout) - .click('@lastUploadFiltersDropdown') - .waitForElementVisible('@lastUploadFiltersType', browser.globals.elementTimeout) - .click('@lastUploadFiltersType') - .waitForElementVisible('@lastUploadFiltersLast2Days', browser.globals.elementTimeout) - .click('@lastUploadFiltersLast2Days') - .waitForElementVisible('@lastUploadFiltersApply', browser.globals.elementTimeout) - .click('@lastUploadFiltersApply'); - }, - lastUploadFilterLast14days(){ + .click('@lastUploadFiltersDropdown') + .waitForElementVisible('@lastUploadFiltersType', browser.globals.elementTimeout) + .click('@lastUploadFiltersType') + .waitForElementVisible('@lastUploadFiltersLast2Days', browser.globals.elementTimeout) + .click('@lastUploadFiltersLast2Days') + .waitForElementVisible('@lastUploadFiltersApply', browser.globals.elementTimeout) + .click('@lastUploadFiltersApply'); + }, + lastUploadFilterLast14days() { return this.waitForElementVisible('@lastUploadFiltersDropdown', browser.globals.elementTimeout) - .click('@lastUploadFiltersDropdown') - .waitForElementVisible('@lastUploadFiltersType', browser.globals.elementTimeout) - .click('@lastUploadFiltersType') - .waitForElementVisible('@lastUploadFiltersLast14Days', browser.globals.elementTimeout) - .click('@lastUploadFiltersLast14Days') - .waitForElementVisible('@lastUploadFiltersApply', browser.globals.elementTimeout) - .click('@lastUploadFiltersApply'); - }, - lastUploadFilterLast30days(){ + .click('@lastUploadFiltersDropdown') + .waitForElementVisible('@lastUploadFiltersType', browser.globals.elementTimeout) + .click('@lastUploadFiltersType') + .waitForElementVisible('@lastUploadFiltersLast14Days', browser.globals.elementTimeout) + .click('@lastUploadFiltersLast14Days') + .waitForElementVisible('@lastUploadFiltersApply', browser.globals.elementTimeout) + .click('@lastUploadFiltersApply'); + }, + lastUploadFilterLast30days() { return this.waitForElementVisible('@lastUploadFiltersDropdown', browser.globals.elementTimeout) - .click('@lastUploadFiltersDropdown') - .waitForElementVisible('@lastUploadFiltersType', browser.globals.elementTimeout) - .click('@lastUploadFiltersType') - .waitForElementVisible('@lastUploadFiltersLast30Days', browser.globals.elementTimeout) - .click('@lastUploadFiltersLast30Days') - .waitForElementVisible('@lastUploadFiltersApply', browser.globals.elementTimeout) - .click('@lastUploadFiltersApply'); - }, - rpmExportClickCalendarStartDate(date){ + .click('@lastUploadFiltersDropdown') + .waitForElementVisible('@lastUploadFiltersType', browser.globals.elementTimeout) + .click('@lastUploadFiltersType') + .waitForElementVisible('@lastUploadFiltersLast30Days', browser.globals.elementTimeout) + .click('@lastUploadFiltersLast30Days') + .waitForElementVisible('@lastUploadFiltersApply', browser.globals.elementTimeout) + .click('@lastUploadFiltersApply'); + }, + rpmExportClickCalendarStartDate(date) { return this.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout) - .click('@rpmReportButton') - .click('@rpmClearDates') - .click('@rpmReportStartDate') - .click('xpath',`//*[contains(@aria-label,'${date}')]`) - .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) - .click('@rpmReportConfirm'); - }, - rpmExportClickCalendarStartDatePreviousMonth(date){ + .click('@rpmReportButton') + .click('@rpmClearDates') + .click('@rpmReportStartDate') + .click('xpath', `//*[contains(@aria-label,'${date}')]`) + .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) + .click('@rpmReportConfirm'); + }, + rpmExportClickCalendarStartDatePreviousMonth(date) { return this.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout) - .click('@rpmReportButton') - .click('@rpmClearDates') - .click('@rpmReportStartDate') - .click('@previousMonth') - .click('xpath',`//*[contains(@aria-label,'${date}')]`) - .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) - .click('@rpmReportConfirm'); - }, - rpmExportClickCalendarEndDate(date){ + .click('@rpmReportButton') + .click('@rpmClearDates') + .click('@rpmReportStartDate') + .click('@previousMonth') + .click('xpath', `//*[contains(@aria-label,'${date}')]`) + .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) + .click('@rpmReportConfirm'); + }, + rpmExportClickCalendarEndDate(date) { return this.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout) - .click('@rpmReportButton') - .click('@rpmClearDates') - .click('@rpmReportEndDate') - .click('xpath',`//*[contains(@aria-label,'${date}')]`) - .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) - .click('@rpmReportConfirm'); - }, - rpmExportClickCalendarEndDatePreviousMonth(date){ + .click('@rpmReportButton') + .click('@rpmClearDates') + .click('@rpmReportEndDate') + .click('xpath', `//*[contains(@aria-label,'${date}')]`) + .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) + .click('@rpmReportConfirm'); + }, + rpmExportClickCalendarEndDatePreviousMonth(date) { return this.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout) - .click('@rpmReportButton') - .click('@rpmClearDates') - .click('@rpmReportEndDate') - .click('@previousMonth') - .click('xpath',`//*[contains(@aria-label,'${date}')]`) - .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) - .click('@rpmReportConfirm'); - }, - rpmExportTypeInputStartAndEndDate(start,end){ + .click('@rpmReportButton') + .click('@rpmClearDates') + .click('@rpmReportEndDate') + .click('@previousMonth') + .click('xpath', `//*[contains(@aria-label,'${date}')]`) + .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) + .click('@rpmReportConfirm'); + }, + rpmExportTypeInputStartAndEndDate(start, end) { return this.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout) - .click('@rpmReportButton') - .click('@rpmReportStartDate') - .setValue('@rpmReportStartDate', start ) - .setValue('@rpmReportEndDate', end ) - .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) - .click('@rpmReportConfirm'); - }, - rpmExportDefaultDate(){ + .click('@rpmReportButton') + .click('@rpmReportStartDate') + .setValue('@rpmReportStartDate', start) + .setValue('@rpmReportEndDate', end) + .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) + .click('@rpmReportConfirm'); + }, + rpmExportDefaultDate() { return this.waitForElementVisible('@rpmReportButton', browser.globals.elementTimeout) - .click('@rpmReportButton') - .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) - .click('@rpmReportConfirm'); + .click('@rpmReportButton') + .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) + .click('@rpmReportConfirm'); }, - timeInRangeVeryLow(){ + timeInRangeVeryLow() { return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) - .click('@timeInRangeFilterButton') - .waitForElementVisible('@timeInRangeFilterVeryLow', browser.globals.elementTimeout) - .click('@timeInRangeFilterVeryLow') - .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) - .click('@timeInRangeFilterApply'); + .click('@timeInRangeFilterButton') + .waitForElementVisible('@timeInRangeFilterVeryLow', browser.globals.elementTimeout) + .click('@timeInRangeFilterVeryLow') + .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) + .click('@timeInRangeFilterApply'); }, - timeInRangeLow(){ + timeInRangeLow() { return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) - .click('@timeInRangeFilterButton') - .waitForElementVisible('@timeInRangeFilterLow', browser.globals.elementTimeout) - .click('@timeInRangeFilterLow') - .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) - .click('@timeInRangeFilterApply'); + .click('@timeInRangeFilterButton') + .waitForElementVisible('@timeInRangeFilterLow', browser.globals.elementTimeout) + .click('@timeInRangeFilterLow') + .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) + .click('@timeInRangeFilterApply'); }, - timeInRangeTarget(){ + timeInRangeTarget() { return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) - .click('@timeInRangeFilterButton') - .waitForElementVisible('@timeInRangeFilterTarget', browser.globals.elementTimeout) - .click('@timeInRangeFilterTarget') - .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) - .click('@timeInRangeFilterApply'); + .click('@timeInRangeFilterButton') + .waitForElementVisible('@timeInRangeFilterTarget', browser.globals.elementTimeout) + .click('@timeInRangeFilterTarget') + .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) + .click('@timeInRangeFilterApply'); }, - timeInRangeHigh(){ + timeInRangeHigh() { return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) - .click('@timeInRangeFilterButton') - .waitForElementVisible('@timeInRangeFilterHigh', browser.globals.elementTimeout) - .click('@timeInRangeFilterHigh') - .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) - .click('@timeInRangeFilterApply'); + .click('@timeInRangeFilterButton') + .waitForElementVisible('@timeInRangeFilterHigh', browser.globals.elementTimeout) + .click('@timeInRangeFilterHigh') + .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) + .click('@timeInRangeFilterApply'); }, - timeInRangeVeryHigh(){ + timeInRangeVeryHigh() { return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) - .click('@timeInRangeFilterButton') - .waitForElementVisible('@timeInRangeFilterVeryHigh', browser.globals.elementTimeout) - .click('@timeInRangeFilterVeryHigh') - .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) - .click('@timeInRangeFilterApply'); + .click('@timeInRangeFilterButton') + .waitForElementVisible('@timeInRangeFilterVeryHigh', browser.globals.elementTimeout) + .click('@timeInRangeFilterVeryHigh') + .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) + .click('@timeInRangeFilterApply'); }, - timeInRangeAll(){ + timeInRangeAll() { return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) - .click('@timeInRangeFilterButton') - //.moveToElement('@timeInRangeFilterVeryLow', browser.globals.elementTimeout) - .click('@timeInRangeFilterVeryLow') - //.moveToElement('@timeInRangeFilterLow', browser.globals.elementTimeout) - .click('@timeInRangeFilterLow') - //.moveToElement('@timeInRangeFilterTarget', browser.globals.elementTimeout) - .click('@timeInRangeFilterTarget') - //.moveToElement('@timeInRangeFilterVeryHigh', browser.globals.elementTimeout) - .click('@timeInRangeFilterVeryHigh') - //.moveToElement('@timeInRangeFilterHigh', browser.globals.elementTimeout) - .click('@timeInRangeFilterHigh') - .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) - .click('@timeInRangeFilterApply'); - }, - - + .click('@timeInRangeFilterButton') + // .moveToElement('@timeInRangeFilterVeryLow', browser.globals.elementTimeout) + .click('@timeInRangeFilterVeryLow') + // .moveToElement('@timeInRangeFilterLow', browser.globals.elementTimeout) + .click('@timeInRangeFilterLow') + // .moveToElement('@timeInRangeFilterTarget', browser.globals.elementTimeout) + .click('@timeInRangeFilterTarget') + // .moveToElement('@timeInRangeFilterVeryHigh', browser.globals.elementTimeout) + .click('@timeInRangeFilterVeryHigh') + // .moveToElement('@timeInRangeFilterHigh', browser.globals.elementTimeout) + .click('@timeInRangeFilterHigh') + .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) + .click('@timeInRangeFilterApply'); + }, }], }, diff --git a/rpm.csv b/rpm.csv index dd2cd25..c7e8e54 100644 --- a/rpm.csv +++ b/rpm.csv @@ -1,3 +1,4 @@ -Name,Date of Birth,MRN,# Days With Qualifying Data between 06/01/2024 and 06/30/2024,Sufficient Data for CPT-99454 -"allconditions2","01/01/2001",N/A,0,FALSE -"allconditions1","01/01/2000",N/A,0,FALSE \ No newline at end of file +Name,Date of Birth,MRN,# Days With Qualifying Data between 06/07/2024 and 07/06/2024,Sufficient Data for CPT-99454 +"autodex1","01/01/2000",N/A,30,TRUE +"autodex2","02/02/2002","DEXCOM",30,TRUE +"autodex3","01/01/2000","DEXCOM2",30,TRUE \ No newline at end of file diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js index 0617104..e6712eb 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js @@ -1,28 +1,6 @@ /* eslint-disable linebreak-style */ require('../../utilities/seleniumKeepAlive'); -const fs = require('fs'); -const {parse} = require('csv-parse'); -const moment = require('moment'); - -const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); -const checkFileExists = (attempts, browser, fileName) => new Promise((resolve, reject) => { - browser.executeScript( - 'browserstack_executor: {"action": "fileExists","arguments":{"file_name":"RPM Report (05-15-2024 - 06-13-2024).csv"}}', - [], - (result) => { - if (result.value) { - resolve(result.value); - } else { - console.log(`fail${attempts}`); - reject(Error); - } - }, - ); -}).catch((err) => { - console.log(`att${attempts}${err}`); - if (--attempts <= 0) throw err; // give up - return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); -}); +const moment = require('moment'); module.exports = { '@tags': ['rpm', 'clinician', 'parallel'], @@ -38,38 +16,32 @@ module.exports = { const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); clinicWorkspace.click('@goToWorkspace'); - }, - - 'Clinic workspace rpm stats export | timeInRangeAll |patientFilterSearch:allconditions \ - | rpmExportStartDateSelected:40 days from today ': async function (browser) { - //setup config + + 'Clinic workspace rpm stats export | timeInRangeAll |patientFilterSearch:allconditions | rpmExportStartDateSelected:40 days from today ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 40 - let startDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') - let endDate = moment(startDate,'MMM D, YYYY').add(29,"days").format('MMMM D, YYYY') - let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(29,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 40; + const startDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); + const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(29, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.timeInRangeAll(); - clinicPatientList.patientFilterSearch("allconditions"); - let startDateObj = moment(startDate, 'MMMM D, YYYY') - if (moment().startOf('month').diff(startDateObj.startOf('month'),"months") === 2){ - clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate) - } - else{ - clinicPatientList.rpmExportClickCalendarStartDate(startDate) + clinicPatientList.patientFilterSearch('allconditions'); + const startDateObj = moment(startDate, 'MMMM D, YYYY'); + if (moment().startOf('month').diff(startDateObj.startOf('month'), 'months') === 2) { + clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate); + } else { + clinicPatientList.rpmExportClickCalendarStartDate(startDate); } - - //validate file export results + + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -77,43 +49,38 @@ module.exports = { console.log(error); browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); } - const written =await browser.checkFileContents(fileName) - console.log('write'+written) + const written = await browser.checkFileContents(fileName); + console.log(`write${written}`); const sufficient = await browser.checkRPMExportSufficiency(filePath); - console.log('suff'+sufficient) + console.log(`suff${sufficient}`); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid '); - }, - 'Clinic workspace rpm stats export | timeInRangeAll | patientFilterSearch:allconditions \ - | rpmExportEndDateSelected:10 days from today ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | timeInRangeAll | patientFilterSearch:allconditions | rpmExportEndDateSelected:10 days from today ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 10 - let endDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') - let endDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let startDateFile = moment(endDate,'MMM D, YYYY').subtract(29,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 10; + const endDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); + const endDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const startDateFile = moment(endDate, 'MMM D, YYYY').subtract(29, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.timeInRangeAll(); - clinicPatientList.patientFilterSearch("allconditions"); + clinicPatientList.patientFilterSearch('allconditions'); - let endDateObj = moment(endDate, 'MMMM D, YYYY') - if (moment().startOf('month').diff(endDateObj.startOf('month'),"months") === 2){ - clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate) + const endDateObj = moment(endDate, 'MMMM D, YYYY'); + if (moment().startOf('month').diff(endDateObj.startOf('month'), 'months') === 2) { + clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate); + } else { + clinicPatientList.rpmExportClickCalendarEndDate(endDate); } - else{ - clinicPatientList.rpmExportClickCalendarEndDate(endDate) - } - //validate file export results + // validate file export results try { - console.log(fileName) + console.log(fileName); const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); } catch (error) { @@ -122,32 +89,28 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, - 'Clinic workspace rpm stats export | timeInRangeAll | patientFilterSearch:allconditions \ - | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | timeInRangeAll | patientFilterSearch:allconditions | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 57 - let dateRange = 15 - let startDate = moment().subtract(daysFromToday,"days").format('MMM D, YYYY') - let endDate = moment(startDate,'MMM D, YYYY').add(dateRange,"days").format('MMM D, YYYY') - let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(dateRange,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 57; + const dateRange = 15; + const startDate = moment().subtract(daysFromToday, 'days').format('MMM D, YYYY'); + const endDate = moment(startDate, 'MMM D, YYYY').add(dateRange, 'days').format('MMM D, YYYY'); + const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(dateRange, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - - clinicPatientList.timeInRangeAll() - clinicPatientList.patientFilterSearch("allconditions"); + clinicPatientList.timeInRangeAll(); + clinicPatientList.patientFilterSearch('allconditions'); - clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate,endDate); - //validate file export results + clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate, endDate); + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -157,28 +120,25 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, - 'Clinic workspace rpm stats export | timeInRangeAll | patientFilterSearch:allconditions \ - | rpmExportDefaultDate ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | timeInRangeAll | patientFilterSearch:allconditions | rpmExportDefaultDate ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let startDateFile = moment().subtract(29,"days").format('MM-DD-YYYY') - let endDateFile = today - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const today = moment().format('MM-DD-YYYY'); + const startDateFile = moment().subtract(29, 'days').format('MM-DD-YYYY'); + const endDateFile = today; + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.timeInRangeAll(); - clinicPatientList.patientFilterSearch("allconditions"); + clinicPatientList.patientFilterSearch('allconditions'); clinicPatientList.rpmExportDefaultDate(); - //validate file export results + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -188,7 +148,6 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, }; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js index c771f90..6536e17 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js @@ -1,28 +1,6 @@ /* eslint-disable linebreak-style */ require('../../utilities/seleniumKeepAlive'); -const fs = require('fs'); -const {parse} = require('csv-parse'); -const moment = require('moment'); - -const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); -const checkFileExists = (attempts, browser, fileName) => new Promise((resolve, reject) => { - browser.executeScript( - 'browserstack_executor: {"action": "fileExists","arguments":{"file_name":"RPM Report (05-15-2024 - 06-13-2024).csv"}}', - [], - (result) => { - if (result.value) { - resolve(result.value); - } else { - console.log(`fail${attempts}`); - reject(Error); - } - }, - ); -}).catch((err) => { - console.log(`att${attempts}${err}`); - if (--attempts <= 0) throw err; // give up - return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); -}); +const moment = require('moment'); module.exports = { '@tags': ['rpm', 'clinician', 'parallel'], @@ -38,38 +16,32 @@ module.exports = { const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); clinicWorkspace.click('@goToWorkspace'); - }, - - 'Clinic workspace rpm stats export | lastUploadFilterLast14days | patientFilterSearch:autodex \ - | rpmExportStartDateSelected:40 days from today ': async function (browser) { - //setup config + + 'Clinic workspace rpm stats export | lastUploadFilterLast14days | patientFilterSearch:autodex | rpmExportStartDateSelected:40 days from today ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 40 - let startDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') - let endDate = moment(startDate,'MMM D, YYYY').add(29,"days").format('MMMM D, YYYY') - let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(29,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 40; + const startDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); + const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(29, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterLast14days(); - clinicPatientList.patientFilterSearch("autodex"); - let startDateObj = moment(startDate, 'MMMM D, YYYY') - if (moment().startOf('month').diff(startDateObj.startOf('month'),"months") === 2){ - clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate) - } - else{ - clinicPatientList.rpmExportClickCalendarStartDate(startDate) + clinicPatientList.patientFilterSearch('autodex'); + const startDateObj = moment(startDate, 'MMMM D, YYYY'); + if (moment().startOf('month').diff(startDateObj.startOf('month'), 'months') === 2) { + clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate); + } else { + clinicPatientList.rpmExportClickCalendarStartDate(startDate); } - - //validate file export results + + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -77,43 +49,38 @@ module.exports = { console.log(error); browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); } - const written =await browser.checkFileContents(fileName) - console.log('write'+written) + const written = await browser.checkFileContents(fileName); + console.log(`write${written}`); const sufficient = await browser.checkRPMExportSufficiency(filePath); - console.log('suff'+sufficient) + console.log(`suff${sufficient}`); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid '); - }, - 'Clinic workspace rpm stats export | lastUploadFilterLast14days | patientFilterSearch:autodex \ - | rpmExportEndDateSelected:10 days from today ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | lastUploadFilterLast14days | patientFilterSearch:autodex | rpmExportEndDateSelected:10 days from today ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 10 - let endDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') - let endDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let startDateFile = moment(endDate,'MMM D, YYYY').subtract(29,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 10; + const endDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); + const endDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const startDateFile = moment(endDate, 'MMM D, YYYY').subtract(29, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterLast14days(); - clinicPatientList.patientFilterSearch("autodex"); + clinicPatientList.patientFilterSearch('autodex'); - let endDateObj = moment(endDate, 'MMMM D, YYYY') - if (moment().startOf('month').diff(endDateObj.startOf('month'),"months") === 2){ - clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate) + const endDateObj = moment(endDate, 'MMMM D, YYYY'); + if (moment().startOf('month').diff(endDateObj.startOf('month'), 'months') === 2) { + clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate); + } else { + clinicPatientList.rpmExportClickCalendarEndDate(endDate); } - else{ - clinicPatientList.rpmExportClickCalendarEndDate(endDate) - } - //validate file export results + // validate file export results try { - console.log(fileName) + console.log(fileName); const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); } catch (error) { @@ -122,32 +89,28 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, - 'Clinic workspace rpm stats export | lastUploadFilterLast14days | patientFilterSearch:autodex \ - | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | lastUploadFilterLast14days | patientFilterSearch:autodex | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 57 - let dateRange = 15 - let startDate = moment().subtract(daysFromToday,"days").format('MMM D, YYYY') - let endDate = moment(startDate,'MMM D, YYYY').add(dateRange,"days").format('MMM D, YYYY') - let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(dateRange,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 57; + const dateRange = 15; + const startDate = moment().subtract(daysFromToday, 'days').format('MMM D, YYYY'); + const endDate = moment(startDate, 'MMM D, YYYY').add(dateRange, 'days').format('MMM D, YYYY'); + const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(dateRange, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterLast14days(); - clinicPatientList.patientFilterSearch("autodex"); + clinicPatientList.patientFilterSearch('autodex'); - clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate,endDate); - //validate file export results + clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate, endDate); + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -157,28 +120,25 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, - 'Clinic workspace rpm stats export | lastUploadFilterLast14days | patientFilterSearch:autodex \ - | rpmExportDefaultDate ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | lastUploadFilterLast14days | patientFilterSearch:autodex | rpmExportDefaultDate ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let startDateFile = moment().subtract(29,"days").format('MM-DD-YYYY') - let endDateFile = today - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const today = moment().format('MM-DD-YYYY'); + const startDateFile = moment().subtract(29, 'days').format('MM-DD-YYYY'); + const endDateFile = today; + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterLast14days(); - clinicPatientList.patientFilterSearch("autodex"); + clinicPatientList.patientFilterSearch('autodex'); clinicPatientList.rpmExportDefaultDate(); - //validate file export results + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -188,7 +148,6 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, }; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js index 3fb98af..9440b50 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js @@ -1,28 +1,6 @@ /* eslint-disable linebreak-style */ require('../../utilities/seleniumKeepAlive'); -const fs = require('fs'); -const {parse} = require('csv-parse'); -const moment = require('moment'); - -const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); -const checkFileExists = (attempts, browser, fileName) => new Promise((resolve, reject) => { - browser.executeScript( - 'browserstack_executor: {"action": "fileExists","arguments":{"file_name":"RPM Report (05-15-2024 - 06-13-2024).csv"}}', - [], - (result) => { - if (result.value) { - resolve(result.value); - } else { - console.log(`fail${attempts}`); - reject(Error); - } - }, - ); -}).catch((err) => { - console.log(`att${attempts}${err}`); - if (--attempts <= 0) throw err; // give up - return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); -}); +const moment = require('moment'); module.exports = { '@tags': ['rpm', 'clinician', 'parallel'], @@ -38,38 +16,32 @@ module.exports = { const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); clinicWorkspace.click('@goToWorkspace'); - }, - - 'Clinic workspace rpm stats export | lastUploadFilterLast2days | patientFilterSearch:autodex \ - | rpmExportStartDateSelected:40 days from today ': async function (browser) { - //setup config + + 'Clinic workspace rpm stats export | lastUploadFilterLast2days | patientFilterSearch:autodex | rpmExportStartDateSelected:40 days from today ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 40 - let startDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') - let endDate = moment(startDate,'MMM D, YYYY').add(29,"days").format('MMMM D, YYYY') - let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(29,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 40; + const startDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); + const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(29, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterLast2days(); - clinicPatientList.patientFilterSearch("autodex"); - let startDateObj = moment(startDate, 'MMMM D, YYYY') - if (moment().startOf('month').diff(startDateObj.startOf('month'),"months") === 2){ - clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate) - } - else{ - clinicPatientList.rpmExportClickCalendarStartDate(startDate) + clinicPatientList.patientFilterSearch('autodex'); + const startDateObj = moment(startDate, 'MMMM D, YYYY'); + if (moment().startOf('month').diff(startDateObj.startOf('month'), 'months') === 2) { + clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate); + } else { + clinicPatientList.rpmExportClickCalendarStartDate(startDate); } - - //validate file export results + + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -77,43 +49,38 @@ module.exports = { console.log(error); browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); } - const written =await browser.checkFileContents(fileName) - console.log('write'+written) + const written = await browser.checkFileContents(fileName); + console.log(`write${written}`); const sufficient = await browser.checkRPMExportSufficiency(filePath); - console.log('suff'+sufficient) + console.log(`suff${sufficient}`); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid '); - }, - 'Clinic workspace rpm stats export | lastUploadFilterLast2days | patientFilterSearch:autodex \ - | rpmExportEndDateSelected:10 days from today ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | lastUploadFilterLast2days | patientFilterSearch:autodex | rpmExportEndDateSelected:10 days from today ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 10 - let endDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') - let endDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let startDateFile = moment(endDate,'MMM D, YYYY').subtract(29,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 10; + const endDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); + const endDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const startDateFile = moment(endDate, 'MMM D, YYYY').subtract(29, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterLast2days(); - clinicPatientList.patientFilterSearch("autodex"); + clinicPatientList.patientFilterSearch('autodex'); - let endDateObj = moment(endDate, 'MMMM D, YYYY') - if (moment().startOf('month').diff(endDateObj.startOf('month'),"months") === 2){ - clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate) + const endDateObj = moment(endDate, 'MMMM D, YYYY'); + if (moment().startOf('month').diff(endDateObj.startOf('month'), 'months') === 2) { + clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate); + } else { + clinicPatientList.rpmExportClickCalendarEndDate(endDate); } - else{ - clinicPatientList.rpmExportClickCalendarEndDate(endDate) - } - //validate file export results + // validate file export results try { - console.log(fileName) + console.log(fileName); const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); } catch (error) { @@ -122,32 +89,28 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, - 'Clinic workspace rpm stats export | lastUploadFilterLast2days | patientFilterSearch:autodex \ - | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | lastUploadFilterLast2days | patientFilterSearch:autodex | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 57 - let dateRange = 15 - let startDate = moment().subtract(daysFromToday,"days").format('MMM D, YYYY') - let endDate = moment(startDate,'MMM D, YYYY').add(dateRange,"days").format('MMM D, YYYY') - let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(dateRange,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 57; + const dateRange = 15; + const startDate = moment().subtract(daysFromToday, 'days').format('MMM D, YYYY'); + const endDate = moment(startDate, 'MMM D, YYYY').add(dateRange, 'days').format('MMM D, YYYY'); + const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(dateRange, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterLast2days(); - clinicPatientList.patientFilterSearch("autodex"); + clinicPatientList.patientFilterSearch('autodex'); - clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate,endDate); - //validate file export results + clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate, endDate); + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -157,28 +120,25 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, - 'Clinic workspace rpm stats export | lastUploadFilterLast2days | patientFilterSearch:autodex \ - | rpmExportDefaultDate ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | lastUploadFilterLast2days | patientFilterSearch:autodex | rpmExportDefaultDate ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let startDateFile = moment().subtract(29,"days").format('MM-DD-YYYY') - let endDateFile = today - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const today = moment().format('MM-DD-YYYY'); + const startDateFile = moment().subtract(29, 'days').format('MM-DD-YYYY'); + const endDateFile = today; + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterLast2days(); - clinicPatientList.patientFilterSearch("autodex"); + clinicPatientList.patientFilterSearch('autodex'); clinicPatientList.rpmExportDefaultDate(); - //validate file export results + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -188,7 +148,6 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, }; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js index abed458..40e1733 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js @@ -1,28 +1,6 @@ /* eslint-disable linebreak-style */ require('../../utilities/seleniumKeepAlive'); -const fs = require('fs'); -const {parse} = require('csv-parse'); -const moment = require('moment'); - -const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); -const checkFileExists = (attempts, browser, fileName) => new Promise((resolve, reject) => { - browser.executeScript( - 'browserstack_executor: {"action": "fileExists","arguments":{"file_name":"RPM Report (05-15-2024 - 06-13-2024).csv"}}', - [], - (result) => { - if (result.value) { - resolve(result.value); - } else { - console.log(`fail${attempts}`); - reject(Error); - } - }, - ); -}).catch((err) => { - console.log(`att${attempts}${err}`); - if (--attempts <= 0) throw err; // give up - return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); -}); +const moment = require('moment'); module.exports = { '@tags': ['rpm', 'clinician', 'parallel'], @@ -38,38 +16,32 @@ module.exports = { const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); clinicWorkspace.click('@goToWorkspace'); - }, - - 'Clinic workspace rpm stats export | lastUploadFilterLast30days | patientFilterSearch:autodex \ - | rpmExportStartDateSelected:40 days from today ': async function (browser) { - //setup config + + 'Clinic workspace rpm stats export | lastUploadFilterLast30days | patientFilterSearch:autodex | rpmExportStartDateSelected:40 days from today ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 40 - let startDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') - let endDate = moment(startDate,'MMM D, YYYY').add(29,"days").format('MMMM D, YYYY') - let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(29,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 40; + const startDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); + const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(29, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterLast30days(); - clinicPatientList.patientFilterSearch("autodex"); - let startDateObj = moment(startDate, 'MMMM D, YYYY') - if (moment().startOf('month').diff(startDateObj.startOf('month'),"months") === 2){ - clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate) - } - else{ - clinicPatientList.rpmExportClickCalendarStartDate(startDate) + clinicPatientList.patientFilterSearch('autodex'); + const startDateObj = moment(startDate, 'MMMM D, YYYY'); + if (moment().startOf('month').diff(startDateObj.startOf('month'), 'months') === 2) { + clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate); + } else { + clinicPatientList.rpmExportClickCalendarStartDate(startDate); } - - //validate file export results + + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -77,43 +49,38 @@ module.exports = { console.log(error); browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); } - const written =await browser.checkFileContents(fileName) - console.log('write'+written) + const written = await browser.checkFileContents(fileName); + console.log(`write${written}`); const sufficient = await browser.checkRPMExportSufficiency(filePath); - console.log('suff'+sufficient) + console.log(`suff${sufficient}`); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid '); - }, - 'Clinic workspace rpm stats export | lastUploadFilterLast30days | patientFilterSearch:autodex \ - | rpmExportEndDateSelected:10 days from today ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | lastUploadFilterLast30days | patientFilterSearch:autodex | rpmExportEndDateSelected:10 days from today ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 10 - let endDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') - let endDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let startDateFile = moment(endDate,'MMM D, YYYY').subtract(29,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 10; + const endDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); + const endDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const startDateFile = moment(endDate, 'MMM D, YYYY').subtract(29, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterLast30days(); - clinicPatientList.patientFilterSearch("autodex"); + clinicPatientList.patientFilterSearch('autodex'); - let endDateObj = moment(endDate, 'MMMM D, YYYY') - if (moment().startOf('month').diff(endDateObj.startOf('month'),"months") === 2){ - clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate) + const endDateObj = moment(endDate, 'MMMM D, YYYY'); + if (moment().startOf('month').diff(endDateObj.startOf('month'), 'months') === 2) { + clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate); + } else { + clinicPatientList.rpmExportClickCalendarEndDate(endDate); } - else{ - clinicPatientList.rpmExportClickCalendarEndDate(endDate) - } - //validate file export results + // validate file export results try { - console.log(fileName) + console.log(fileName); const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); } catch (error) { @@ -122,32 +89,28 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, - 'Clinic workspace rpm stats export | lastUploadFilterLast30days | patientFilterSearch:autodex \ - | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | lastUploadFilterLast30days | patientFilterSearch:autodex | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 57 - let dateRange = 15 - let startDate = moment().subtract(daysFromToday,"days").format('MMM D, YYYY') - let endDate = moment(startDate,'MMM D, YYYY').add(dateRange,"days").format('MMM D, YYYY') - let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(dateRange,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 57; + const dateRange = 15; + const startDate = moment().subtract(daysFromToday, 'days').format('MMM D, YYYY'); + const endDate = moment(startDate, 'MMM D, YYYY').add(dateRange, 'days').format('MMM D, YYYY'); + const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(dateRange, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterLast30days(); - clinicPatientList.patientFilterSearch("autodex"); + clinicPatientList.patientFilterSearch('autodex'); - clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate,endDate); - //validate file export results + clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate, endDate); + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -157,28 +120,25 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, - 'Clinic workspace rpm stats export | lastUploadFilterLast30days | patientFilterSearch:autodex \ - | rpmExportDefaultDate ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | lastUploadFilterLast30days | patientFilterSearch:autodex | rpmExportDefaultDate ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let startDateFile = moment().subtract(29,"days").format('MM-DD-YYYY') - let endDateFile = today - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const today = moment().format('MM-DD-YYYY'); + const startDateFile = moment().subtract(29, 'days').format('MM-DD-YYYY'); + const endDateFile = today; + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterLast30days(); - clinicPatientList.patientFilterSearch("autodex"); + clinicPatientList.patientFilterSearch('autodex'); clinicPatientList.rpmExportDefaultDate(); - //validate file export results + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -188,7 +148,6 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, }; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js index a14cf01..56a2a4d 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js @@ -1,31 +1,9 @@ /* eslint-disable linebreak-style */ require('../../utilities/seleniumKeepAlive'); -const fs = require('fs'); -const {parse} = require('csv-parse'); -const moment = require('moment'); - -const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); -const checkFileExists = (attempts, browser, fileName) => new Promise((resolve, reject) => { - browser.executeScript( - 'browserstack_executor: {"action": "fileExists","arguments":{"file_name":"RPM Report (05-15-2024 - 06-13-2024).csv"}}', - [], - (result) => { - if (result.value) { - resolve(result.value); - } else { - console.log(`fail${attempts}`); - reject(Error); - } - }, - ); -}).catch((err) => { - console.log(`att${attempts}${err}`); - if (--attempts <= 0) throw err; // give up - return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); -}); +const moment = require('moment'); module.exports = { - '@tags': ['rpm', 'clinician', 'parallel'], + '@tags': ['rpm2', 'clinician', 'parallel'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; @@ -38,38 +16,32 @@ module.exports = { const clinicWorkspace = clinicWorkspacePage.section.clinicWorkspace; clinicWorkspace.waitForElementVisible('@title', browser.globals.elementTimeout); clinicWorkspace.click('@goToWorkspace'); - }, - - 'Clinic workspace rpm stats export | lastUploadFilterToday | patientFilterSearch:autodex \ - | rpmExportStartDateSelected:40 days from today ': async function (browser) { - //setup config + + 'Clinic workspace rpm stats export | lastUploadFilterToday | patientFilterSearch:autodex | rpmExportStartDateSelected:40 days from today ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 40 - let startDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') - let endDate = moment(startDate,'MMM D, YYYY').add(29,"days").format('MMMM D, YYYY') - let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(29,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 40; + const startDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); + const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(29, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterToday(); - clinicPatientList.patientFilterSearch("autodex"); - let startDateObj = moment(startDate, 'MMMM D, YYYY') - if (moment().startOf('month').diff(startDateObj.startOf('month'),"months") === 2){ - clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate) - } - else{ - clinicPatientList.rpmExportClickCalendarStartDate(startDate) + clinicPatientList.patientFilterSearch('autodex'); + const startDateObj = moment(startDate, 'MMMM D, YYYY'); + if (moment().startOf('month').diff(startDateObj.startOf('month'), 'months') === 2) { + clinicPatientList.rpmExportClickCalendarStartDatePreviousMonth(startDate); + } else { + clinicPatientList.rpmExportClickCalendarStartDate(startDate); } - - //validate file export results + + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -77,41 +49,36 @@ module.exports = { console.log(error); browser.assert.strictEqual(false, true, 'exported rpm csv file exists '); } - const written =await browser.checkFileContents(fileName) - console.log('write'+written) + const written = await browser.checkFileContents(fileName); + console.log(`write${written}`); const sufficient = await browser.checkRPMExportSufficiency(filePath); - console.log('suff'+sufficient) + console.log(`suff${sufficient}`); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid '); - }, - 'Clinic workspace rpm stats export | lastUploadFilterToday | patientFilterSearch:autodex \ - | rpmExportEndDateSelected:10 days from today ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | lastUploadFilterToday | patientFilterSearch:autodex | rpmExportEndDateSelected:10 days from today ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 10 - let endDate = moment().subtract(daysFromToday,"days").format('MMMM D, YYYY') - let endDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let startDateFile = moment(endDate,'MMM D, YYYY').subtract(29,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 10; + const endDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); + const endDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const startDateFile = moment(endDate, 'MMM D, YYYY').subtract(29, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterToday(); - clinicPatientList.patientFilterSearch("autodex"); + clinicPatientList.patientFilterSearch('autodex'); - let endDateObj = moment(endDate, 'MMMM D, YYYY') - if (moment().startOf('month').diff(endDateObj.startOf('month'),"months") === 2){ - clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate) + const endDateObj = moment(endDate, 'MMMM D, YYYY'); + if (moment().startOf('month').diff(endDateObj.startOf('month'), 'months') === 2) { + clinicPatientList.rpmExportClickCalendarEndDatePreviousMonth(endDate); + } else { + clinicPatientList.rpmExportClickCalendarEndDate(endDate); } - else{ - clinicPatientList.rpmExportClickCalendarEndDate(endDate) - } - //validate file export results + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -121,32 +88,28 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, - 'Clinic workspace rpm stats export | lastUploadFilterToday | patientFilterSearch:autodex \ - | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | lastUploadFilterToday | patientFilterSearch:autodex | rpmExportStartDateSelected:57 days from today | rpmExportEndDateSelected:start date + 15 days ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let daysFromToday = 57 - let dateRange = 15 - let startDate = moment().subtract(daysFromToday,"days").format('MMM D, YYYY') - let endDate = moment(startDate,'MMM D, YYYY').add(dateRange,"days").format('MMM D, YYYY') - let startDateFile = moment().subtract(daysFromToday,"days").format('MM-DD-YYYY') - let endDateFile = moment(startDateFile,'MM-DD-YYYY').add(dateRange,"days").format('MM-DD-YYYY') - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const daysFromToday = 57; + const dateRange = 15; + const startDate = moment().subtract(daysFromToday, 'days').format('MMM D, YYYY'); + const endDate = moment(startDate, 'MMM D, YYYY').add(dateRange, 'days').format('MMM D, YYYY'); + const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); + const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(dateRange, 'days').format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterToday(); - clinicPatientList.patientFilterSearch("autodex"); + clinicPatientList.patientFilterSearch('autodex'); - clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate,endDate); - //validate file export results + clinicPatientList.rpmExportTypeInputStartAndEndDate(startDate, endDate); + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -156,28 +119,25 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, - 'Clinic workspace rpm stats export | lastUploadFilterToday | patientFilterSearch:autodex \ - | rpmExportDefaultDate ': async function (browser) { - //setup config + 'Clinic workspace rpm stats export | lastUploadFilterToday | patientFilterSearch:autodex | rpmExportDefaultDate ': async (browser) => { + // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - let today = moment().format('MM-DD-YYYY') - let startDateFile = moment().subtract(29,"days").format('MM-DD-YYYY') - let endDateFile = today - let fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv` + const today = moment().format('MM-DD-YYYY'); + const startDateFile = moment().subtract(29, 'days').format('MM-DD-YYYY'); + const endDateFile = today; + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; const attemptsCheckFileExists = 10; - const filePath = './rpm.csv' + const filePath = './rpm.csv'; - //filter criteria and rpm report submit + // filter criteria and rpm report submit - clinicPatientList.lastUploadFilterToday(); - clinicPatientList.patientFilterSearch("autodex"); + clinicPatientList.patientFilterSearch('autodex'); clinicPatientList.rpmExportDefaultDate(); - //validate file export results + // validate file export results try { const exists = await browser.checkFileExists(attemptsCheckFileExists, fileName); browser.assert.strictEqual(exists, true, 'exported rpm csv file exists '); @@ -187,7 +147,6 @@ module.exports = { } const sufficient = await browser.checkRPMExportSufficiency(filePath); browser.assert.strictEqual(sufficient, true, 'exported rpm csv file sufficiency is valid'); - }, }; diff --git a/tests/e2e/suppressedNotificationsFunctional.js b/tests/e2e/suppressedNotificationsFunctional.js index 0cec163..08c1d3f 100644 --- a/tests/e2e/suppressedNotificationsFunctional.js +++ b/tests/e2e/suppressedNotificationsFunctional.js @@ -4,7 +4,7 @@ require('../../utilities/seleniumKeepAlive'); module.exports = { '@tags': ['notifications', 'parallel'], - 'Clinician User Logs in with Existing Credentials with notification suppressed': async function (browser) { + 'Clinician User Logs in with Existing Credentials with notification suppressed': async (browser) => { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; const clinicianPassword = browser.globals.clinicianPassword; @@ -39,7 +39,7 @@ module.exports = { clinicPatientList.click('@edit'); clinicPatientList.waitForElementVisible('@city', browser.globals.elementTimeout); }, - 'Clinic workspace settings apply changes and check suppressed notifications status': async function (browser) { + 'Clinic workspace settings apply changes and check suppressed notifications status': async (browser) => { const clinicianUsername = browser.globals.clinicianUsername; const clinicianPassword = browser.globals.clinicianPassword; const clinicId = browser.globals.clinic_id; From 0d8b84cafac8395cd30455f65d0c9447c7acdb36 Mon Sep 17 00:00:00 2001 From: Ginny Yadav Date: Fri, 19 Jul 2024 14:44:02 -0400 Subject: [PATCH 11/40] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7fba659..69c2aa4 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "testintChrome": "nightwatch --env intchrome", "testdev1Chrome": "nightwatch --env dev1chrome", "testqa2ChromeClinician": "nightwatch --env qa2chrome --tag clinician", - "testqa2ChromeNotifications": "nightwatch --env qa2chrome --tag notifications" + "testqa2ChromeNotifications": "nightwatch --env qa2chrome --tag notifications", "testqa2ChromeTag": "nightwatch --env qa2chrome --tag clinician", "eslint": "eslint .", "eslint:fix": "eslint . --fix" From dde872864358a9c7ea52f49e9450ca7863442ada Mon Sep 17 00:00:00 2001 From: Ginny Yadav Date: Fri, 19 Jul 2024 15:02:58 -0400 Subject: [PATCH 12/40] eslint config and fixes --- .eslintrc.js | 2 + custom_commands/captureElementScreenshot.js | 2 +- package-lock.json | 727 ++++++++++++++++++-- package.json | 1 + 4 files changed, 677 insertions(+), 55 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 82c0975..7d8b546 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,5 +17,7 @@ module.exports = { 'chai-friendly/no-unused-expressions': 'off', 'object-shorthand': ['error', 'properties'], 'no-console': 'off', + 'max-len': 'warn', + 'no-plusplus': 'warn', }, }; diff --git a/custom_commands/captureElementScreenshot.js b/custom_commands/captureElementScreenshot.js index 2f92754..ff3b15d 100644 --- a/custom_commands/captureElementScreenshot.js +++ b/custom_commands/captureElementScreenshot.js @@ -28,7 +28,7 @@ CaptureElementScreenshot.prototype.command = function command( Promise.all([ promisifyCommand(api, 'takeElementScreenshot', [selector]), ]).then(([screenshotEncoded]) => { - Jimp.read(new Buffer(screenshotEncoded, 'base64')).then((screenshot) => { + Jimp.read(Buffer.from(screenshotEncoded, 'base64')).then((screenshot) => { this.api.assert.ok(true, `The screenshot for selector <${selector.name}> was captured successfully.`); callback(screenshot); diff --git a/package-lock.json b/package-lock.json index 3754050..6ba8411 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,13 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "axios": "^1.6.7", + "axios": "1.6.7", "dayjs": "1.11.10", "dotenv": "16.4.5", "eslint-config-airbnb": "19.0.4", "fast-xml-parser": "4.3.5", "form-data": "4.0.0", + "jimp": "^0.22.12", "nightwatch-vrt": "github:tidepool-org/nightwatch-vrt#master", "otplib": "12.0.1", "request": "2.88.2" @@ -151,6 +152,399 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==" }, + "node_modules/@jimp/bmp": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.12.tgz", + "integrity": "sha512-aeI64HD0npropd+AR76MCcvvRaa+Qck6loCOS03CkkxGHN5/r336qTM5HPUdHKMDOGzqknuVPA8+kK1t03z12g==", + "dependencies": { + "@jimp/utils": "^0.22.12", + "bmp-js": "^0.1.0" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/core": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.22.12.tgz", + "integrity": "sha512-l0RR0dOPyzMKfjUW1uebzueFEDtCOj9fN6pyTYWWOM/VS4BciXQ1VVrJs8pO3kycGYZxncRKhCoygbNr8eEZQA==", + "dependencies": { + "@jimp/utils": "^0.22.12", + "any-base": "^1.1.0", + "buffer": "^5.2.0", + "exif-parser": "^0.1.12", + "file-type": "^16.5.4", + "isomorphic-fetch": "^3.0.0", + "pixelmatch": "^4.0.2", + "tinycolor2": "^1.6.0" + } + }, + "node_modules/@jimp/custom": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz", + "integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==", + "dependencies": { + "@jimp/core": "^0.22.12" + } + }, + "node_modules/@jimp/gif": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.22.12.tgz", + "integrity": "sha512-y6BFTJgch9mbor2H234VSjd9iwAhaNf/t3US5qpYIs0TSbAvM02Fbc28IaDETj9+4YB4676sz4RcN/zwhfu1pg==", + "dependencies": { + "@jimp/utils": "^0.22.12", + "gifwrap": "^0.10.1", + "omggif": "^1.0.9" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/jpeg": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.22.12.tgz", + "integrity": "sha512-Rq26XC/uQWaQKyb/5lksCTCxXhtY01NJeBN+dQv5yNYedN0i7iYu+fXEoRsfaJ8xZzjoANH8sns7rVP4GE7d/Q==", + "dependencies": { + "@jimp/utils": "^0.22.12", + "jpeg-js": "^0.4.4" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-blit": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.12.tgz", + "integrity": "sha512-xslz2ZoFZOPLY8EZ4dC29m168BtDx95D6K80TzgUi8gqT7LY6CsajWO0FAxDwHz6h0eomHMfyGX0stspBrTKnQ==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-blur": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.22.12.tgz", + "integrity": "sha512-S0vJADTuh1Q9F+cXAwFPlrKWzDj2F9t/9JAbUvaaDuivpyWuImEKXVz5PUZw2NbpuSHjwssbTpOZ8F13iJX4uw==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-circle": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.22.12.tgz", + "integrity": "sha512-SWVXx1yiuj5jZtMijqUfvVOJBwOifFn0918ou4ftoHgegc5aHWW5dZbYPjvC9fLpvz7oSlptNl2Sxr1zwofjTg==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-color": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.12.tgz", + "integrity": "sha512-xImhTE5BpS8xa+mAN6j4sMRWaUgUDLoaGHhJhpC+r7SKKErYDR0WQV4yCE4gP+N0gozD0F3Ka1LUSaMXrn7ZIA==", + "dependencies": { + "@jimp/utils": "^0.22.12", + "tinycolor2": "^1.6.0" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-contain": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.22.12.tgz", + "integrity": "sha512-Eo3DmfixJw3N79lWk8q/0SDYbqmKt1xSTJ69yy8XLYQj9svoBbyRpSnHR+n9hOw5pKXytHwUW6nU4u1wegHNoQ==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-blit": ">=0.3.5", + "@jimp/plugin-resize": ">=0.3.5", + "@jimp/plugin-scale": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-cover": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.22.12.tgz", + "integrity": "sha512-z0w/1xH/v/knZkpTNx+E8a7fnasQ2wHG5ze6y5oL2dhH1UufNua8gLQXlv8/W56+4nJ1brhSd233HBJCo01BXA==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-crop": ">=0.3.5", + "@jimp/plugin-resize": ">=0.3.5", + "@jimp/plugin-scale": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-crop": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.12.tgz", + "integrity": "sha512-FNuUN0OVzRCozx8XSgP9MyLGMxNHHJMFt+LJuFjn1mu3k0VQxrzqbN06yIl46TVejhyAhcq5gLzqmSCHvlcBVw==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-displace": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.22.12.tgz", + "integrity": "sha512-qpRM8JRicxfK6aPPqKZA6+GzBwUIitiHaZw0QrJ64Ygd3+AsTc7BXr+37k2x7QcyCvmKXY4haUrSIsBug4S3CA==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-dither": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.22.12.tgz", + "integrity": "sha512-jYgGdSdSKl1UUEanX8A85v4+QUm+PE8vHFwlamaKk89s+PXQe7eVE3eNeSZX4inCq63EHL7cX580dMqkoC3ZLw==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-fisheye": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.22.12.tgz", + "integrity": "sha512-LGuUTsFg+fOp6KBKrmLkX4LfyCy8IIsROwoUvsUPKzutSqMJnsm3JGDW2eOmWIS/jJpPaeaishjlxvczjgII+Q==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-flip": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.22.12.tgz", + "integrity": "sha512-m251Rop7GN8W0Yo/rF9LWk6kNclngyjIJs/VXHToGQ6EGveOSTSQaX2Isi9f9lCDLxt+inBIb7nlaLLxnvHX8Q==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-rotate": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-gaussian": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.22.12.tgz", + "integrity": "sha512-sBfbzoOmJ6FczfG2PquiK84NtVGeScw97JsCC3rpQv1PHVWyW+uqWFF53+n3c8Y0P2HWlUjflEla2h/vWShvhg==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-invert": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.22.12.tgz", + "integrity": "sha512-N+6rwxdB+7OCR6PYijaA/iizXXodpxOGvT/smd/lxeXsZ/empHmFFFJ/FaXcYh19Tm04dGDaXcNF/dN5nm6+xQ==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-mask": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.22.12.tgz", + "integrity": "sha512-4AWZg+DomtpUA099jRV8IEZUfn1wLv6+nem4NRJC7L/82vxzLCgXKTxvNvBcNmJjT9yS1LAAmiJGdWKXG63/NA==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-normalize": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.22.12.tgz", + "integrity": "sha512-0So0rexQivnWgnhacX4cfkM2223YdExnJTTy6d06WbkfZk5alHUx8MM3yEzwoCN0ErO7oyqEWRnEkGC+As1FtA==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-print": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.22.12.tgz", + "integrity": "sha512-c7TnhHlxm87DJeSnwr/XOLjJU/whoiKYY7r21SbuJ5nuH+7a78EW1teOaj5gEr2wYEd7QtkFqGlmyGXY/YclyQ==", + "dependencies": { + "@jimp/utils": "^0.22.12", + "load-bmfont": "^1.4.1" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-blit": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-resize": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.12.tgz", + "integrity": "sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-rotate": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.12.tgz", + "integrity": "sha512-9YNEt7BPAFfTls2FGfKBVgwwLUuKqy+E8bDGGEsOqHtbuhbshVGxN2WMZaD4gh5IDWvR+emmmPPWGgaYNYt1gA==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-blit": ">=0.3.5", + "@jimp/plugin-crop": ">=0.3.5", + "@jimp/plugin-resize": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-scale": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.12.tgz", + "integrity": "sha512-dghs92qM6MhHj0HrV2qAwKPMklQtjNpoYgAB94ysYpsXslhRTiPisueSIELRwZGEr0J0VUxpUY7HgJwlSIgGZw==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-resize": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-shadow": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.22.12.tgz", + "integrity": "sha512-FX8mTJuCt7/3zXVoeD/qHlm4YH2bVqBuWQHXSuBK054e7wFRnRnbSLPUqAwSeYP3lWqpuQzJtgiiBxV3+WWwTg==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-blur": ">=0.3.5", + "@jimp/plugin-resize": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-threshold": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.22.12.tgz", + "integrity": "sha512-4x5GrQr1a/9L0paBC/MZZJjjgjxLYrqSmWd+e+QfAEPvmRxdRoQ5uKEuNgXnm9/weHQBTnQBQsOY2iFja+XGAw==", + "dependencies": { + "@jimp/utils": "^0.22.12" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-color": ">=0.8.0", + "@jimp/plugin-resize": ">=0.8.0" + } + }, + "node_modules/@jimp/plugins": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.22.12.tgz", + "integrity": "sha512-yBJ8vQrDkBbTgQZLty9k4+KtUQdRjsIDJSPjuI21YdVeqZxYywifHl4/XWILoTZsjTUASQcGoH0TuC0N7xm3ww==", + "dependencies": { + "@jimp/plugin-blit": "^0.22.12", + "@jimp/plugin-blur": "^0.22.12", + "@jimp/plugin-circle": "^0.22.12", + "@jimp/plugin-color": "^0.22.12", + "@jimp/plugin-contain": "^0.22.12", + "@jimp/plugin-cover": "^0.22.12", + "@jimp/plugin-crop": "^0.22.12", + "@jimp/plugin-displace": "^0.22.12", + "@jimp/plugin-dither": "^0.22.12", + "@jimp/plugin-fisheye": "^0.22.12", + "@jimp/plugin-flip": "^0.22.12", + "@jimp/plugin-gaussian": "^0.22.12", + "@jimp/plugin-invert": "^0.22.12", + "@jimp/plugin-mask": "^0.22.12", + "@jimp/plugin-normalize": "^0.22.12", + "@jimp/plugin-print": "^0.22.12", + "@jimp/plugin-resize": "^0.22.12", + "@jimp/plugin-rotate": "^0.22.12", + "@jimp/plugin-scale": "^0.22.12", + "@jimp/plugin-shadow": "^0.22.12", + "@jimp/plugin-threshold": "^0.22.12", + "timm": "^1.6.1" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/png": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.22.12.tgz", + "integrity": "sha512-Mrp6dr3UTn+aLK8ty/dSKELz+Otdz1v4aAXzV5q53UDD2rbB5joKVJ/ChY310B+eRzNxIovbUF1KVrUsYdE8Hg==", + "dependencies": { + "@jimp/utils": "^0.22.12", + "pngjs": "^6.0.0" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/tiff": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.22.12.tgz", + "integrity": "sha512-E1LtMh4RyJsoCAfAkBRVSYyZDTtLq9p9LUiiYP0vPtXyxX4BiYBUYihTLSBlCQg5nF2e4OpQg7SPrLdJ66u7jg==", + "dependencies": { + "utif2": "^4.0.1" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/types": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.22.12.tgz", + "integrity": "sha512-wwKYzRdElE1MBXFREvCto5s699izFHNVvALUv79GXNbsOVqlwlOxlWJ8DuyOGIXoLP4JW/m30YyuTtfUJgMRMA==", + "dependencies": { + "@jimp/bmp": "^0.22.12", + "@jimp/gif": "^0.22.12", + "@jimp/jpeg": "^0.22.12", + "@jimp/png": "^0.22.12", + "@jimp/tiff": "^0.22.12", + "timm": "^1.6.1" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/utils": { + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.22.12.tgz", + "integrity": "sha512-yJ5cWUknGnilBq97ZXOyOS0HhsHOyAyjHwYfHxGbSyMTohgQI6sVyE8KPgDwH8HHW/nMKXk8TrSwAE71zt716Q==", + "dependencies": { + "regenerator-runtime": "^0.13.3" + } + }, + "node_modules/@jimp/utils/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, "node_modules/@nightwatch/chai": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/@nightwatch/chai/-/chai-5.0.3.tgz", @@ -306,6 +700,11 @@ "@otplib/plugin-thirty-two": "^12.0.1" } }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, "node_modules/@types/chai": { "version": "4.3.12", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.12.tgz", @@ -450,6 +849,11 @@ "node": ">=8.0.0" } }, + "node_modules/any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -804,7 +1208,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -866,9 +1269,9 @@ } }, "node_modules/bmp-js": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.3.tgz", - "integrity": "sha512-epsm3Z92j5xwek9p97pVw3KbsNc0F4QnbYh+N93SpbJYuHFQQ/UAh6K+bKFGyLePH3Hudtl/Sa95Quqp0gX8IQ==" + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==" }, "node_modules/boxen": { "version": "5.1.2", @@ -923,7 +1326,6 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, "funding": [ { "type": "github", @@ -1003,6 +1405,14 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, + "node_modules/centra": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/centra/-/centra-2.7.0.tgz", + "integrity": "sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==", + "dependencies": { + "follow-redirects": "^1.15.6" + } + }, "node_modules/chai-nightwatch": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/chai-nightwatch/-/chai-nightwatch-0.5.3.tgz", @@ -2257,11 +2667,19 @@ } }, "node_modules/file-type": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "version": "16.5.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", + "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "dependencies": { + "readable-web-to-node-stream": "^3.0.0", + "strtok3": "^6.2.4", + "token-types": "^4.1.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" } }, "node_modules/filelist": { @@ -2349,9 +2767,9 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -2514,6 +2932,15 @@ "assert-plus": "^1.0.0" } }, + "node_modules/gifwrap": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz", + "integrity": "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==", + "dependencies": { + "image-q": "^4.0.0", + "omggif": "^1.0.10" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2775,7 +3202,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -2799,6 +3225,19 @@ "node": ">= 4" } }, + "node_modules/image-q": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz", + "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==", + "dependencies": { + "@types/node": "16.9.1" + } + }, + "node_modules/image-q/node_modules/@types/node": { + "version": "16.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", + "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==" + }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -3281,6 +3720,15 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -3318,32 +3766,25 @@ } }, "node_modules/jimp": { - "version": "0.2.28", - "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.2.28.tgz", - "integrity": "sha512-9HT7DA279xkTlry2oG30s6AtOUglNiY2UdyYpj0yNI4/NBv8PmdNC0gcldgMU4HqvbUlrM3+v+6GaHnTkH23JQ==", + "version": "0.22.12", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.22.12.tgz", + "integrity": "sha512-R5jZaYDnfkxKJy1dwLpj/7cvyjxiclxU3F4TrI/J4j2rS0niq6YDUMoPn5hs8GDpO+OZGo7Ky057CRtWesyhfg==", "dependencies": { - "bignumber.js": "^2.1.0", - "bmp-js": "0.0.3", - "es6-promise": "^3.0.2", - "exif-parser": "^0.1.9", - "file-type": "^3.1.0", - "jpeg-js": "^0.2.0", - "load-bmfont": "^1.2.3", - "mime": "^1.3.4", - "mkdirp": "0.5.1", - "pixelmatch": "^4.0.0", - "pngjs": "^3.0.0", - "read-chunk": "^1.0.1", - "request": "^2.65.0", - "stream-to-buffer": "^0.1.0", - "tinycolor2": "^1.1.2", - "url-regex": "^3.0.0" + "@jimp/custom": "^0.22.12", + "@jimp/plugins": "^0.22.12", + "@jimp/types": "^0.22.12", + "regenerator-runtime": "^0.13.3" } }, + "node_modules/jimp/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, "node_modules/jpeg-js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz", - "integrity": "sha512-Ni9PffhJtYtdD7VwxH6V2MnievekGfUefosGCHadog0/jAevRu6HPjYeMHbUemn0IPE8d4wGa8UsOGsX+iKy2g==" + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==" }, "node_modules/js-tokens": { "version": "4.0.0", @@ -3634,16 +4075,16 @@ } }, "node_modules/load-bmfont": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.1.tgz", - "integrity": "sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.2.tgz", + "integrity": "sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==", "dependencies": { "buffer-equal": "0.0.1", "mime": "^1.3.4", "parse-bmfont-ascii": "^1.0.3", "parse-bmfont-binary": "^1.0.5", "parse-bmfont-xml": "^1.1.4", - "phin": "^2.9.1", + "phin": "^3.7.1", "xhr": "^2.0.1", "xtend": "^4.0.0" } @@ -4099,6 +4540,55 @@ "lodash": "^4.17.4" } }, + "node_modules/nightwatch-vrt/node_modules/bmp-js": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.3.tgz", + "integrity": "sha512-epsm3Z92j5xwek9p97pVw3KbsNc0F4QnbYh+N93SpbJYuHFQQ/UAh6K+bKFGyLePH3Hudtl/Sa95Quqp0gX8IQ==" + }, + "node_modules/nightwatch-vrt/node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nightwatch-vrt/node_modules/jimp": { + "version": "0.2.28", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.2.28.tgz", + "integrity": "sha512-9HT7DA279xkTlry2oG30s6AtOUglNiY2UdyYpj0yNI4/NBv8PmdNC0gcldgMU4HqvbUlrM3+v+6GaHnTkH23JQ==", + "dependencies": { + "bignumber.js": "^2.1.0", + "bmp-js": "0.0.3", + "es6-promise": "^3.0.2", + "exif-parser": "^0.1.9", + "file-type": "^3.1.0", + "jpeg-js": "^0.2.0", + "load-bmfont": "^1.2.3", + "mime": "^1.3.4", + "mkdirp": "0.5.1", + "pixelmatch": "^4.0.0", + "pngjs": "^3.0.0", + "read-chunk": "^1.0.1", + "request": "^2.65.0", + "stream-to-buffer": "^0.1.0", + "tinycolor2": "^1.1.2", + "url-regex": "^3.0.0" + } + }, + "node_modules/nightwatch-vrt/node_modules/jpeg-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz", + "integrity": "sha512-Ni9PffhJtYtdD7VwxH6V2MnievekGfUefosGCHadog0/jAevRu6HPjYeMHbUemn0IPE8d4wGa8UsOGsX+iKy2g==" + }, + "node_modules/nightwatch-vrt/node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/nightwatch/node_modules/aria-query": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", @@ -4142,6 +4632,44 @@ "dev": true, "optional": true }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-gyp-build": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", @@ -4299,6 +4827,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4419,8 +4952,7 @@ "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, "node_modules/parent-module": { "version": "1.0.1", @@ -4519,16 +5051,33 @@ "node": "*" } }, + "node_modules/peek-readable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", + "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, "node_modules/phin": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", - "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info." + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.1.tgz", + "integrity": "sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==", + "dependencies": { + "centra": "^2.7.0" + }, + "engines": { + "node": ">= 8" + } }, "node_modules/picomatch": { "version": "2.3.1", @@ -4562,7 +5111,7 @@ "pixelmatch": "bin/pixelmatch" } }, - "node_modules/pngjs": { + "node_modules/pixelmatch/node_modules/pngjs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", @@ -4570,6 +5119,14 @@ "node": ">=4.0.0" } }, + "node_modules/pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "engines": { + "node": ">=12.13.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -4689,7 +5246,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -4699,6 +5255,21 @@ "node": ">= 6" } }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "dependencies": { + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/readdir-glob": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", @@ -5020,9 +5591,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/saxes": { "version": "6.0.0", @@ -5240,7 +5811,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -5362,6 +5932,22 @@ "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, + "node_modules/strtok3": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", + "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^4.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5419,6 +6005,11 @@ "node": ">=0.2.6" } }, + "node_modules/timm": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/timm/-/timm-1.7.1.tgz", + "integrity": "sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==" + }, "node_modules/tinycolor2": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", @@ -5448,6 +6039,22 @@ "node": ">=8.0" } }, + "node_modules/token-types": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", + "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/tough-cookie": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", @@ -5669,11 +6276,18 @@ "node": ">=0.10.0" } }, + "node_modules/utif2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/utif2/-/utif2-4.1.0.tgz", + "integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==", + "dependencies": { + "pako": "^1.0.11" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { "version": "8.3.2", @@ -5739,6 +6353,11 @@ "node": ">=18" } }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + }, "node_modules/whatwg-mimetype": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", diff --git a/package.json b/package.json index 69c2aa4..a6402e6 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "eslint-config-airbnb": "19.0.4", "fast-xml-parser": "4.3.5", "form-data": "4.0.0", + "jimp": "0.22.12", "nightwatch-vrt": "github:tidepool-org/nightwatch-vrt#master", "otplib": "12.0.1", "request": "2.88.2" From 06795883dbb8cfff2f4631d9df45b90bf77a21e7 Mon Sep 17 00:00:00 2001 From: Ginny Yadav Date: Fri, 19 Jul 2024 15:16:07 -0400 Subject: [PATCH 13/40] Update Patient Tag header reference --- pageobjects/clinicPatientListPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pageobjects/clinicPatientListPage.js b/pageobjects/clinicPatientListPage.js index a655ef8..fa34d63 100644 --- a/pageobjects/clinicPatientListPage.js +++ b/pageobjects/clinicPatientListPage.js @@ -18,7 +18,7 @@ module.exports = { locateStrategy: 'xpath', }, patientTags: { - selector: '//*[@id="peopleTable-header-tags"]//span[@role="button"]', + selector: '//*[@id="peopleTable-header-tags"]//span', locateStrategy: 'xpath', }, GMI: { From 8155932cfcd0e3f85d0792b31060885af57f6651 Mon Sep 17 00:00:00 2001 From: Ginny Yadav Date: Fri, 19 Jul 2024 15:42:31 -0400 Subject: [PATCH 14/40] fix clinician list selectors and increase timeout --- nightwatch.conf.js | 1 + pageobjects/clinicPatientListPage.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nightwatch.conf.js b/nightwatch.conf.js index c9fae28..f25a846 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -36,6 +36,7 @@ module.exports = { userName: process.env.BROWSERSTACK_USER, accessKey: process.env.BROWSERSTACK_KEY, }, + idleTimeout: 300, }, disable_error_log: true, diff --git a/pageobjects/clinicPatientListPage.js b/pageobjects/clinicPatientListPage.js index fa34d63..f2643b4 100644 --- a/pageobjects/clinicPatientListPage.js +++ b/pageobjects/clinicPatientListPage.js @@ -26,7 +26,7 @@ module.exports = { locateStrategy: 'xpath', }, timeInRange: { - selector: '//*[@id="peopleTable-header-bgRangeSummary"]//span[@role="button"]', + selector: '//*[@id="peopleTable-header-bgRangeSummary"]//span', locateStrategy: 'xpath', }, avgGlucose: { @@ -46,7 +46,7 @@ module.exports = { locateStrategy: 'xpath', }, lastUploadDesc: { - selector: '//*[@id="peopleTable-header-cgm-lastUploadDate"]//span[@role="Button"]', + selector: '//*[@id="peopleTable-header-cgm-lastUploadDate"]//span', locateStrategy: 'xpath', }, workspaceSettings: { From d4eabfd67b266b2c88f73694f59545a25d98e4ba Mon Sep 17 00:00:00 2001 From: Ginny Yadav Date: Fri, 19 Jul 2024 15:53:18 -0400 Subject: [PATCH 15/40] Update nightwatch.conf.js --- nightwatch.conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nightwatch.conf.js b/nightwatch.conf.js index f25a846..87d2592 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -35,8 +35,8 @@ module.exports = { 'bstack:options': { userName: process.env.BROWSERSTACK_USER, accessKey: process.env.BROWSERSTACK_KEY, + 'browserstack.idletimeout': 300, }, - idleTimeout: 300, }, disable_error_log: true, From 828e47142aabeca64db644e3a2208f32c8dd48ed Mon Sep 17 00:00:00 2001 From: Ginny Yadav Date: Fri, 19 Jul 2024 15:59:52 -0400 Subject: [PATCH 16/40] Update nightwatch.conf.js --- nightwatch.conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nightwatch.conf.js b/nightwatch.conf.js index 87d2592..126ef96 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -35,7 +35,6 @@ module.exports = { 'bstack:options': { userName: process.env.BROWSERSTACK_USER, accessKey: process.env.BROWSERSTACK_KEY, - 'browserstack.idletimeout': 300, }, }, @@ -78,6 +77,7 @@ module.exports = { resolution: '1366x768', buildName: `QA2_CHROME ${dayjs().format('YYYY-MM-DD')} JIRA: ${process.env.TEST_EXECUTION_KEY}`, local: 'false', + idletimeout: 300, }, }, }, From 9434c11ecf12c126c55c148ae9ae77e44eda90bf Mon Sep 17 00:00:00 2001 From: Ginny Yadav Date: Fri, 19 Jul 2024 16:03:06 -0400 Subject: [PATCH 17/40] Update nightwatch.conf.js --- nightwatch.conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nightwatch.conf.js b/nightwatch.conf.js index 126ef96..6834936 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -77,7 +77,7 @@ module.exports = { resolution: '1366x768', buildName: `QA2_CHROME ${dayjs().format('YYYY-MM-DD')} JIRA: ${process.env.TEST_EXECUTION_KEY}`, local: 'false', - idletimeout: 300, + idleTimeout: 300, }, }, }, From 07ff831b25f8959122f3b6a507a367e794da055e Mon Sep 17 00:00:00 2001 From: Ginny Yadav Date: Fri, 19 Jul 2024 16:31:13 -0400 Subject: [PATCH 18/40] add quit commands after all tests to avoid timeout --- global.js | 1 + 1 file changed, 1 insertion(+) diff --git a/global.js b/global.js index 3d1b822..472a492 100644 --- a/global.js +++ b/global.js @@ -45,6 +45,7 @@ module.exports = { ); }); } + browser.quit(); }); if (browser.currentTest.results.failed > 0 || browser.currentTest.results.errors > 0) { request({ From 2d92e89f58555d819e2937003ac93509b8714909 Mon Sep 17 00:00:00 2001 From: Ginny Yadav Date: Fri, 19 Jul 2024 16:31:41 -0400 Subject: [PATCH 19/40] handle exception fter suppressed notification request --- custom_commands/setSuppressedNotification.js | 30 +++++++++++--------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/custom_commands/setSuppressedNotification.js b/custom_commands/setSuppressedNotification.js index 56b0c96..a3dda82 100644 --- a/custom_commands/setSuppressedNotification.js +++ b/custom_commands/setSuppressedNotification.js @@ -37,22 +37,26 @@ module.exports = class setSuppressedNotifcation { const setNotification = async () => { const token = await getToken(); - const response = await axios.post( - `${environment}/v1/clinics/${clinicId}/suppressed_notifications`, + try { + const response = await axios.post( + `${environment}/v1/clinics/${clinicId}/suppressed_notifications`, - { - suppressedNotifications: { - patientClinicInvitation: true, + { + suppressedNotifications: { + patientClinicInvitation: true, + }, }, - }, - { - headers: { - 'X-Tidepool-Session-Token': token, - 'Content-Type': 'application/json', + { + headers: { + 'X-Tidepool-Session-Token': token, + 'Content-Type': 'application/json', + }, }, - }, - ); - return response.status; + ); + return response.status; + } catch (error) { + return console.error(error); + } }; /** * function calls asynchronous function setNotification and awaits for result From cac0cbcd18470c0633f9e7c49f284c25cc560e1d Mon Sep 17 00:00:00 2001 From: Ginny Yadav Date: Fri, 19 Jul 2024 16:31:58 -0400 Subject: [PATCH 20/40] update environment variables on prd --- nightwatch.conf.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nightwatch.conf.js b/nightwatch.conf.js index 6834936..f2943fb 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -180,8 +180,10 @@ module.exports = { prdchrome: { extends: 'browserstack', launch_url: 'https://app.tidepool.org', - clinic_id: `${process.env.PRD_CLINIC_ID}`, - clinician_id: `${process.env.PRD_CLINICIAN_ID}`, + globals: { + clinic_id: `${process.env.PRD_CLINIC_ID}`, + clinician_id: `${process.env.PRD_CLINICIAN_ID}`, + }, environmentName: 'prdchrome', desiredCapabilities: { browserName: 'chrome', From 70405eb90d5365b00f9176ec1d2c36d2d5f939bd Mon Sep 17 00:00:00 2001 From: Ginny Yadav Date: Fri, 19 Jul 2024 16:32:17 -0400 Subject: [PATCH 21/40] add prd command for suppressed notifications --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index a6402e6..0dc0955 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "testdev1Chrome": "nightwatch --env dev1chrome", "testqa2ChromeClinician": "nightwatch --env qa2chrome --tag clinician", "testqa2ChromeNotifications": "nightwatch --env qa2chrome --tag notifications", + "testprdChromeNotifications": "nightwatch --env prd --tag notifications", "testqa2ChromeTag": "nightwatch --env qa2chrome --tag clinician", "eslint": "eslint .", "eslint:fix": "eslint . --fix" From e1710d98a01381e14e3f127b86648c8d326569d9 Mon Sep 17 00:00:00 2001 From: Ginny Yadav Date: Fri, 19 Jul 2024 17:00:08 -0400 Subject: [PATCH 22/40] remove extra screenshots --- custom_commands/captureElementScreenshot.js | 43 ------------------ ...ity_FAILED_Nov-17-2023-145255-GMT-0600.png | Bin 97536 -> 0 bytes ...ity_FAILED_Oct-06-2023-204045-GMT-0500.png | Bin 97612 -> 0 bytes ...als_FAILED_Oct-02-2023-154919-GMT-0500.png | Bin 30830 -> 0 bytes ...als_FAILED_Jun-14-2023-152329-GMT-0500.png | Bin 29814 -> 0 bytes 5 files changed, 43 deletions(-) delete mode 100644 custom_commands/captureElementScreenshot.js delete mode 100644 e2e/basicsPageFunctionality/Infusion-Site-Changes-dashboard-functionality_FAILED_Nov-17-2023-145255-GMT-0600.png delete mode 100644 e2e/basicsPageFunctionality/Infusion-Site-Changes-dashboard-functionality_FAILED_Oct-06-2023-204045-GMT-0500.png delete mode 100644 e2e/basicsPageFunctionality/User-Logs-in-with-Existing-Credentials_FAILED_Oct-02-2023-154919-GMT-0500.png delete mode 100644 e2e/existingUserLoginFlowTandem/User-Logs-in-with-Existing-Credentials_FAILED_Jun-14-2023-152329-GMT-0500.png diff --git a/custom_commands/captureElementScreenshot.js b/custom_commands/captureElementScreenshot.js deleted file mode 100644 index ff3b15d..0000000 --- a/custom_commands/captureElementScreenshot.js +++ /dev/null @@ -1,43 +0,0 @@ -const EventEmitter = require('events').EventEmitter; -const util = require('util'); -const Jimp = require('jimp'); -const Buffer = require('buffer').Buffer; -const promisifyCommand = require('../node_modules/nightwatch-vrt/lib/promisify-command'); - -/** - * Takes a screenshot of the visible region encompassed by the bounding rectangle - * of an element. -* - * @link - * @param {string} id ID of the element to route the command to. - * @param {function} callback Callback function which is called with the captured screenshot as an argument. - * @returns {Object} The captured screenshot. This object is a Jimp (library) image instance. - */ -function CaptureElementScreenshot() { - EventEmitter.call(this); -} - -util.inherits(CaptureElementScreenshot, EventEmitter); - -CaptureElementScreenshot.prototype.command = function command( - selector, - callback = () => {}, // eslint-disable-line no-empty-function -) { - const api = this.client.api; - - Promise.all([ - promisifyCommand(api, 'takeElementScreenshot', [selector]), - ]).then(([screenshotEncoded]) => { - Jimp.read(Buffer.from(screenshotEncoded, 'base64')).then((screenshot) => { - this.api.assert.ok(true, `The screenshot for selector <${selector.name}> was captured successfully.`); - - callback(screenshot); - this.emit('complete', screenshot); - }); - }).catch((errorMessage) => { - this.api.assert.fail(`The screenshot for selector <${selector.name}> could not be captured.`); - this.emit('complete', errorMessage, this); - }); -}; - -module.exports = CaptureElementScreenshot; diff --git a/e2e/basicsPageFunctionality/Infusion-Site-Changes-dashboard-functionality_FAILED_Nov-17-2023-145255-GMT-0600.png b/e2e/basicsPageFunctionality/Infusion-Site-Changes-dashboard-functionality_FAILED_Nov-17-2023-145255-GMT-0600.png deleted file mode 100644 index 37bb9911ebe763672200369795ca6f4ff1c0f7c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97536 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π=x`8j6J6HYM)2DcNJjk=^72B$OrOV_y7 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

vMeak_K2YU!2{$;j46 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 diff --git a/e2e/basicsPageFunctionality/User-Logs-in-with-Existing-Credentials_FAILED_Oct-02-2023-154919-GMT-0500.png b/e2e/basicsPageFunctionality/User-Logs-in-with-Existing-Credentials_FAILED_Oct-02-2023-154919-GMT-0500.png deleted file mode 100644 index 3cf3595c5f38e78d2d536651122dea128967c380..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30830 zcmb@uWl)@5w)EDIqM~_7yz&m`}{QsMd&|2I>_x=KrujU0g=S+75fH zI_7_0CMGf8Qf^q~f3Ecrn?k8oszTCG<-cw5s(wKQtsXFl>Ehxtp2YIyzkhdAA}cGa z_BD>;|LZfrwJk&bw>^CAo2zmk#8eMq!Pc za55(KMnfSkk1{p=)ssH8FjCdvzfTp=`$9Ep#JL?CGOIj%uxE482dRbyln3Y|QEto> zYl~M@UeKb!dz`$T++`G;!UDOsct8^FZ5BnQJIk77F$Gtof>dRqp~zNg2`wH>(ct(- zLL7<-!A?rPa&fGz@0J%tI1qbJqf4if2KRLzNe3DJ9$<>?U+HhtR5mxKEH7(n(_>2( z#Qy$`f&te*hXe?`j&(d17W&MkBvefn&Kg)rqE0n-{O!pdGk%Z0gL#)dQ5O9*BbBx)@ z`Z4omnD34;=tBuZ0Yxj@8Jwr|@tQJPiql73=lKz!)iZc)Nj6QrXKeQY<;JXoZGQ(h zu|rdQ;yY)gbR-_xHm*eY*i1r-BYxW?sMg*&uf{7L!o>`}$c3F|=SHRMqU5$_TmH_F ze=fN~??3x>sqE;;Tx)f!b=VOr1pq8;ZOuk4bgTRPWJD zy-5D5%ulkI4-udERAm=RaXk;9>M(kjB^`(9YBr~jC%J9XOVrR^MxB{=#&#KK@^c}x z!^%uryMB+Ajk(V@D9%@`g)GfsMKD_u%xD=wYMcIYyk0a?ng(X~TU7s~g3N+J4Aeik^?YhL7)zt&BOJjsGW|q8B9o%3&(W+M7T)1I4g!Qfv%nhDrR<7R{WhN z-mDWPTQuOhPY@JkJTj(?u&E=xNCgC%?4ZIJpni&Xg=arA-)0!|y?0-G^+JUG>(R6? z{&k~aCy8seW_QPP-dFwD8L9=(xnQ-PDU!RH5+}YIrpWpo8{0$Zwi+OocjR#(Wti7= z%lfsjnQc4dd zKqGT!`^^Hj9K;RO$jFz@68(up(7)u}1VV=+QB3Bifh3gAY>bZz7LCkIiJaFJS$E|b z7_J=(bB)noY519t22U9?N@^rqc5we>Y+Ap{e?dvYQS@1DnsuL42SutPR=lup>s+TL zRq;+{m5&0p9j!~&!j@gj>iZt>s2(G>wgPRZFGZTeW4iHP(Xm#o_GfEKLScdN3K79T z_*km35ESQ#qeis>UH^mJ#jARiF0WiKEVT}L^1UD}H-r-;aha}^?yayiGZ&>!Ya(9- zXW@ioZ#Ja80ljwjG1MEOny3NM-tm;QALTG#79hQTls&eSE>oj~o5AqsuQqFTO&RGn z4#I~Li-_>gKoMOX4vs8cVPAsUfBacJ`R0F|;}!sH8uOKUz}Kgnr3RZ7G71%j*~!VS zL^`d<9pd+)=erXxpI4-@{`d^|uiO;{WVO^_NDP?6!Hop!_AD_ESj;U5SrT*17-u^QNucwDqUaJrr@nq&cTast?_=Bw;B_)USCGVbo~ zmB20CI%=f2f#|h3hv~Fj1)F;}Q~(xM*?}N7@wn*IcOBY+-+aodn%y%aDu9JXzRHhe z+>rzH+!mHQKcpy)w0zk;u<&J*&H|SwQ z&bzJg;lQiA)?8%>f`=?Pm@sqt(}L3c**8^JYox>HOiS#F^@zH>U%dQ_`rYC@G)(S=OTwY@WHG|4rH+TVhoq{ysGAvvF|Lf-|8+dL7UHxKg@PS01S(dXs~R3|+eD zJw!7Nt|BM|QWN9Yl%=bU{3ga!>e1D?$dQS8#YX*;du1G^pny5+jz=nSvP4ZxPdxxL z--uMq)FeZ|EvcL$W|fMj`Jp4S8>VsK9GoWI^8NcP60ItQCD~!#-A7jS;%;1z1yv`cFR2Xt*qKxbl8sIwo&pOW}v+-#CT$f(uF} zm`CJZm@>=bubrrK<7UEH%y}CRU)-Z3KlM~OUXAvO6%_1Adb+Ls6D~U{{^N_S*3Z{k zhet+ob8=2**AgZtCk0hiv0B|O&V#2`BUGWqDNt4<=CatSGQ$du;^RMj;xkAu%+(Ps zDyRxGk~w>@e}Jaa>MOa3tiK!s8K9a4%6&Q|Dcs1f47~PH&@=QYP=h%!>Dlu za+xm}sgxr@sAB-WFN-M zgsNZMgdVs>gxzX|TAd`b6-dlqm8+j2^u_Fl}QFT(TsLxc%uw{?jIap7>-jk7DKUfc|K5*AFR2VzXWOVPMo`uQudvC-9U*qlPK)@|NL586%V1;~fv`Dips{)d!Z#BQ!LpkuzpO zbdtL0glQJFHNv6M`LJV4!^hJ@NH~(_A_THGAowI(UtCO%-BL;D(vI4jZ97=7?MwhJ zlr8E|_w>H!3G4`e4G$&hPi{p;h1bA9$K`8FF@>qD_H0jBk!MD+0?GC8PM``_<;x7CWFpfd16-D-@YfAp|C--*y zZZl!t=){-6t|IN9pm~?OXWNv)!dY{k zl5fhnM;77&#cazH0Gkzd!Ij}peCOh7gAP=l{4i?+bKtEGN{S9cQs?f3*=2=sBq=PR z{2bmr_~UuydHE}8aDV(ievDh;ZJ(p_2{eb4OGDh=8KO7-!m^K!HEl~Mtfa)!`924D z^?5UlR6s=_;zkkYqXku_vS}$>uj4MBb=vex_6VgORbDjzXe=oS7vR0a^GoYYhWxYD zheUYj^!n~`pyUhMQRB4X>K$*4{IL11>k%sOwom-=BjpbQw$%fsR>wnii1)`YVb>!N zVu)@z@?B~0>j%w6x_RLB@CW;LiU9`)0?EKmJ>WO~uUJOLA737b_QhjB0c~tr5?RU8 z_V&LQoao&xsQg~fPKsx#(PFpF?b)wyWAyf;I@<>~zfK{r;&QoMv)=5#F=$-u#WK^w z>TFC(@z3uRVOTE_{_s`!!d4IWNxYs-$I99{N1jri5+jLM&MPX4Uf1`HE|K;%Ij8rN z0Zz7^S4GYrh=TXCUuSGCudgsS8n-~qNNEB|KO=X$Ag~`;XqQQ2S5Q{oUFh(6zBQOg z=aNogh7Rl&)Yl)ph*hD+g!+V_zuM#w6dVk`Wih3=3mIM;cKq+(zxNFcJYyC;woIbr zuq)MAG3>c^IV2wE@jR7Xl6LpbG?Vco%*`Pw{8jpGtHQktKR-CSZzLoER^C^ooj|>@ zt31<0`qyOiLBJxYpPyeNQ@sBVdfWX74}|p!`VcmmWONE9-y4rNuYb(E*8|^65|7mI zgyu!ciB9MC`^l>)0f6!59)GliSHyd#Koln%R=StUEY}B$@4ipLz{2QbB)yPh2&)U=@ za@y0{*wm|aao~?twO>l<`dC<;>~w;T^l+2xi5d7`LN<&lU!38gvoIa(-%;0njXdmv zXu#LT7t!AX&R@-K@7s+OviN=6Z>Pj1e*8GRieL{G>K^CV7%S7LrI+mR%mddQbXpDi z)sHhECGQOL?LtN~SMoe9O-OT@eA$ft+dA_K`SyQO=Ac891!qGxhq~5W7UpHarW}UC zZiS$u)dwCjk5ax9A8lsViCrDGUl#VtRxT#7SqTN2DNT_@ZB(`^m2`7Ag@Fl+#n!(-O}>6EQrIR!ARRXcZb#ZwC~C$4;V z!4)4}llmzlhAU6K*4~#ZbH3A%QEo6d_{7S_7FTc07KQj$j>)?n^0ip`9Qo$0V;-!% zfPAnF!DjL__dzg6-u~sR5gi?UXT~>AENZ)9#rDR7-@ftVVx2{to`n7tTOPaquNaE; z8=&!T{F8s%3}y??F>{@5PpqxK?wP(BeE$C3^5@Ul*Mk{o0xs=gnG%jB4$BAJ{Y0jy zRpyqHT~&R3mOrTLK8I7GCy&4)cusBC$lWw_&@aDkE#Rfel<&43&d-s!N$r+x;3*Bg z(d^m7 zQ(ihSuZ>8LFn>EEp;BJcFMy616OcPCwI**)9UN*7aQi*&P-r=(CR!g z`T1PgK}76~`+;Ht?jyD)u|}*jIr%qyx>9?wb21#)Yx9*DODO z1|~C7^`96u|30%ul;eHnnONY_h*RqN5a!b=;c94M@L3PBnbHOpB`t0++WqE{4qYhBktlp#wfUv(`n-6Bb>%0 z`n*nDP6O(3K~SSocwQ1<;p7Z!LJiacSVQ(WYud`%y>z)f@WmDL+vGJAM%%sXV?ji> zJkO5+rBLy(dYZJjGdlywNuXo~kYdO@&?-CNW`a?$iG}6mwd(u*hdzH2aebA?#okb5 zQ#RpWd%h`&o8z0PW|o`#TEb1e-VbtP&4zF1{GRFHMQzp45v5ong1;R4$qb6P)(H9!KTP ze-`#!j#FjJbY)!Xk%&_6FhY@4FG0itLvC5#T?b5Wm$O#huURhn`D9nmu>vS4_6|SU z_=l_ofHAefi?g z;6={!tp=+P>nhJnv~43D;In>jLqpOb;SPR^ua+=}*Q9|%nO|o|Tm6ESes(PGgUmhk zz-3rR+wC&LVv{dV?Y{Jp>x#cWM5KZZ;DsI}G9KM7`3-TL_b$V;ZFo3~Q@5Yw;Vrh- z%+mcW;e9Z^wY72^Qz)#m=K0z4{&Z>Q;f*|Q_`(Mh4q7a4sk&MS-(I|}{&{X(KHnwEjUs(9M(OgzYtp^a{P;O) zqwLNc?w@vo*{|y_y`~Mf_ye^!(~P%mfquM$>lv~@DTXTR5z}>Q3S|B#Mi%cAQ?d%k zSoO1>w^&CcLQc4@q9uHgWZAx{-A8>d116QCG8~fDPQEIzkodJ8xHfffj~0Le-vtFP zDr`5jWHWeWrVh@LJFMh!1#@+2k z+fr7T?d~mzA#N<($nWw+zi*ujP7mI$u5aCpw-)pJVEMDXuaSbw;=Tn<6&1`P*^Fzi zcRdGyS;Q?-pVkv9Disp#*#!K2f!+_Af16-F`I%S1*AKPTzPP@GjcDzZ2Z`x<9$$ zis-GU^t+s=>b4zQ;-zo~{RtO_{VabO^}+(e+rfWmr6HZ6hz8q0+u+&V7B@H{gzD0n zVX0Wkr*5))>LlAC$AE)oAwKN5uHze}q7m>ibiN z-wrY*e1UX9D0BDo{Ka=V7!(y z$|xgDSbtz?f3J)_D=%K2=YuLsGI4%?6RCBgRsP4Ule-1PdRCk>@31}?;N{pc=$@eq z#2yt&_I(KNHH$Oh|$nx-@^Q54EILSRYn^5wqsMry1_gT%AQmkHvjv`NX z6Yvf89Q63f_LkuJe*Cauw9Y_$-=bG4l(Sjin6(#qNAPT)q35Ja1qlh+_IzRHa}Lvb zI3BZDWlFPo-u`^J^WfaFwl&hwQ98`gH2E||Unv(BlEn)uR$E~Qw-b!FBvZ9zylZ%4 zJUZRO?1crg4hYGuD67fU4Ue52l=~tvh0j~UOBOVoTE2% zjXhco*F(U@hP7wka};8R<*{ox3c9BT{z3!Zn>B<_R@wC*YQ*F;{qT9HwOJvkdp~EU zZW#~f_7dFfc6}R=-A#ZAZ2_xR?VxD?UfhhS@f}--Ku%_b?a}tzF(22Mu5;|4JR|utu4V4%5yr>^ z-_&k>xjXB#H{N{)ZfT8C-T-df7`GbV%F#}(^VU`@ye?pqasW4ZpWfn1eKd)Om4{1I zFnalwRm$6;vuVsBR9(C7;D8k+RedwiEjuxkNT&P#>QfEfnc?dl23p0!dgB75M49W( z7#H-QoW&2E>7eNt=nbngm(xdY25`=ZN;I_q@Bv$*fsc!~hv0A*qzoVV9=Fr0GK9?9 zDOQ>70LIZv6SgCJSNJi16D=XJ8sH0gO?YB`?)@pZps*2%(EvHhn(bB@Joc0&gF#rt zWk*1hw*AX^hcDg2pMnv|sRJ+EB>rFEoRoV_EJyw~IJn}^TknM^^d26V8ETdP++w2d z;NA4?Cg4N$*In>|vi4U}C2L3(6zjWR-?jpGLfcC!D+ev>b#z#Mp7D{CU`_hHcB^~jl(2ygeZwi1fbiOpfUdT0RTam{eZjEn8yzkdT5q2tW z1o~T2_)EpVc6sDvzmSk)n9bR)t5;iYEgF6%_x8C!J!-5L1-}7C**4zO^|VJP~2=ca`4rMm0u1 z1$tQE#>~@|{LGQ{i>nF?Xexy_14s63g24Cw{QJHiK`(yoCt(%H8*HKhX6KJUA18v| z9&Po8gX=LE_RCrWp@)TBy6+b{+P&`k>kg#1PLB2}^gugD=djV(r14rndSnKF)B(8& z+O+(5qMM_;(H5Muz!SNWR;RaHO)GhbS!Tv?)c}M0RWO4_i#?Q{LyY==aRD3}W%uv0 zki_aKLOm{s4%%hrcm+xhT8me_ZhB?X-PkYlxX`sO3SHUQu9jQzKhIdYTkpGfPVA`t zAysX)Ux)R3c4NHfZn`TJBTC-(a=Z|a;Tt?>xM%so{4-{uGn#v-wes^@t!DqePJsDK z*M;Bam&L*N^V<1)Uf`@2ezNCDK|=8RS;iM?S=V)$Z$6O`Fc*%ojh-}}k6T&hwzm7o z4C6-kBr{>Z*ikUbY;C+IQ!YEkU$d*fwKJF^UR zkN_j42f(b0J>5!r0#}&gdUI}2T32xIxk$;}vA6tC?wD2^FCBQ3`2=x`aUhu9D}1-Z?nse=Ox1 zPLyG1jMTrDe^@J~Y8;p>x}^lbS>d>ZN2se*EKNLn(jkwhiG>tVE3Nrq+;n&)=2a3I z50}Q}k6pIKeG+FriSE8tfh2iGkF(h`_x=_a+G2#0QXJaOZb_Kg&BlG6%KGbLJB3T$ z6#5J@9F45v@TIJmU&Fvnxis4C`JhmUDWjbwrkwGGfsx8L68KJiqxQtmeHn{#Qnq~Q zou$-=l*W3tkG1rganO%;dpGfEo?Y++Vc_`+cdP_?&}byFwOPgfA$lM0oqHX+MO|-Mi(ycnta$$!4kOKg?FBC4;1D2x(my zd|i%rEv(I~7!fAD>NozNpUa`$b90)hp_E-Tx3F>!HNwqSpyjSR&bkQ_TpQgkIX&D=+)3AO%5DrFyIVBk=k~-pjJR?bBCh+Q{kl$ z+L=OgM!x?R^4U2#kVz+=baT#D*4FtI6-XHsZko@F{{Yl^Y{y-$KW8esrMC18_|tzqdU<)Oh^}!HL54JCJWt(5TzP_5GR9f_E0ty ztaQ-lG7AF8VYbxJiQJ?A0TEbRvC>cNnB6S5UX-fTYrq%4yRt-=RNPUcW zJDvW6LEN3W%{+$lXBN!!@s@GND*x5=eK^>*1R!-9*jBZz!QqrqXw4 zx&FxAdtVq0&s!Wb++$zq0?rx_gL19o^KddP$hXZjxaWf-deCX zCytYXEcUA0;MCWLLGIOC09C=0e8UqRH;;kWgD)c7@?ES_gz$5^gS-?Q1$Drry&-** zfs4Ag-=f0VjrJy}{*b$3=ae3L+X!cAiw#Aj=kTp+0evB7=M;i+Z9>U!aoymw#j`7J zNX2U)X8Pd>N6=g^S93tEqF7m?mQWI!W}z+?lQVVp78Q>5NAL&zJB4 z(1K9$(q}2DNHQn~JsH{BX;Nu%zHLVQx}DP8)(B1}H`vxsz?DK=30;p+TblQBIU*~& zkd6-!GdA^0(@TR?YkP-}a)4x5xMgcF-w#B$Xtx-@i$J%_J?w%eZTfxQX93+9zqhuy zMm@dg(Yd}xKSubdER7pqPeQr3?XQlt9F7}}=f}(?a8+&(-&MVv62YYwIjF(7b03Ox z;T)b{#va`G>Xka1=3xbgeHTDQMRZ7rl$Rg3+qrh=1~8LP%EQAWX=QL=V7k#xt2MK* zP|9j}dU4TxcyO{X-DbrGheAM5jMYCX7n6pHHf%5+X)Lm|o>C%tUzT&8XG%7CB3~U*f1uirjjYw0G{1!$k z;gQY8)~Q3>M25-ePg-5R9HZxUbazgsSo5g0T1_A1s~{wUbvebux~Sp-34=^MNS9Hc zNXzr*dFeuNmzn^{C_MQ=obB=^F-`CmC7+&yL1U%V8T2 zK4={G$%&4B*W^ktkLSEvsptwcm9fT>Px`US`C8hpm8!e1vcrkO&)16%63*T790P6p zTV>bSLedF;HC|cBl9CUDRR@~ssv8f>dFxt{kE{w!@s;w3<%?id(vygh2usX{o6CeS zG2-iP&V<@c#9`&ZI~Hi-fN4YPRqyTT7zW}aG$7@r_vJPEVruZ>$nj6v{oBS4gS-uw z@Wd~my3V6r&#ML#Jf~;wS)8VEkg;Si%C=-_ak2jE;}s=uR0jY6(CF~?C>oYcknA^# z;hRrYcWKs6=XBWqHL$`51&dB$z{u^OPN%F{wy3@7+|afGlN}>xE`=h7KV{T^)W30- zx~NP}CX(l}SglT7ol5ABEUwwcQzFm2s#QHoCp4u#!A5|O+c@xUsHdwK?A zO)}bi!kr4lCFV=M0y!pw&K>5@tVwfDYyfkgx1le(${=^ZKm3}3)wr?$;sNuPQXsIqC)Ei7%in@Y& zx_c-@$^QI>-?a-f{ps0ZZNqJKadN1pQD7^hk-xBeNelB^`_5zloG$1_v175L@gR!t zm*leJ8mw?zC*gJ79>U-5wUs0L3&u{pWVxl6mJ;-{o}wZcrNRZcfu29`&6Pop_*V%0 zzRRwW?7mV0x39lb;~2;H7lVyXlA3{`sHNo$tMc&BJU5s7-5muJQ`Fg6%W~41-Rai+ zfjFyzmTcnMoB#1k=<7Bd!QrH?{Fl-xOCIYBIyS6dHA&UqDt{}&Q@Sii6SK^wB6*1G zDDt8ric?a-{L7nRKe}H1R2ZJt1Lanf!-B1#t}c?o5S^K!V1&NiUmh)SGZT% ztc}`c+evf!*w@(3Kw3iL$<`bb0;%t?OSz>+i^dF;lzzawR^94ZQCT!Q?5{#5zFG;G zbblgBXI<>vixm=Pf+(V!pLf6BSOazt?KB7AS6>h03enP0#X7yHKUOjfuREmIc2!A` zgyk8H{Vi+AUeG+(kY%=b$R8b_G_T7uM&xmhEqFtM_s+3KmRE$d;Ca3MI;sL@fUph zk3%oKM@HXYupne)#(7`Y?6mj7mR}!R-$%oDRN5LJJImIu?Sy(;-gURM>8uymT_px0 zZ~4Kgh26$xtxHy(Jmug1KE7?_4bQn%Hwam#f6sAh6`__mWq$z&3@}~AU9|gy;RE~& z-oeA&KWnz{$DLVZX8mTy#)W8(TNG*xAdXJDue_c?@bzvd31GS##D@j-_+ey8I~t)3DfWSAXcNCA=EVefKbc zOguzTl*#bkr5-giDoE5amF@N!Y)|Mh>;kr4th|+0DJFNqn4Kz{7I?IbBqYq7sns5T zMEitUU7a5Urvp6^HsP6kAAQ|s5|3xbOtvq$tRgqTK% zW|#-Ns4j-<;QVSeB%#p^ov@|oc?3Q{lP@@JI_8Lk73OKhWDQd=LM)b)^Teud2`(B? zxp|$B8e6MJ^-r$;6UpmPqw|3-UBQA|dFkb83HX4{;zACTlQdqJOSlCsm}dIf8k{8h zAV7H>7scW3UAz=f|5|`a2_4d@a z-SvE1hf~cfb(=;hJ>c0GH*O>PlHei7t$z8eyU*8-jXCL`TCGIE%vA`uIT5E!-5LYQ z=%G2n4Na_fVa1EAG5#t-cuDS@i{bHUZ^qI^$RlD3e%cr_d4oCMfsqeBPC=}~I~An_Cb(hsvXCj<$X%zOUai3D!YkOhZ? zpkZJH<=y!@zgsWW1P=}U7>L9T6w2w+XS@W{`kLxf*?4)=!EA*Od;mmngYss{EUVh< z;arM81gxUOSQXRRu9}N#%F=CAOEV{RAEf*GD=M9m0oRVWFn9|U^zk@wZuxS&3%k`3H)Uf5YVb5-#Ome0AX zRqt4CDi`3Q8S-smDmqI?Svb;KcIki4E6I;^ly+|b=knoF_@N5$=5Z2>^|3b1CL(g- zR^!4eQa6jHbEsLkCwhCo!T#_>npDY;C8=c1I){iQknGG>fSCQ{d*WDe-u%{wgj5t6 z6y8M#;@+Sa^;kt=8>sB7RhSCm;oeTX;a()*4&6%woY{5dK65ij5H#cQws#vHNFlq^ zp0qNa;EG%s-TsA0arlrb^HACMzEwFr8a!M8kr4759%}$DT;jm26F>AO9qNHRrE zp5p+}9yGXry&jBQ`fb>K+khF5mnn~ww9M!mV}8M4shKWoPEg%TvftHJ5X-~f*4WuL zL9>p%FOA!cZb>Yx+iLHthK-hn<2?l4^Zm)uA$^A~AJ~g-e|U~g>3L;3DB(N!7+>7; zoE_4z1N9|7=)SVwb)|lQ<>>qR$#3WLUA6)H{SMx?qCm`~!xp(R{>w|&}@Eg3V5nfG4bt`e!@8%WsA17u+4vS#05V&03%5KQg%te5^dM0{2v4VqP=B z(4^@;2#iZ@BYUlyfer%7#;m+JhJIL(PJaQZ1f%p|Th^ymRWVM00V0VVR(AI5tx#e` z6_x&ci8y({+{aI!n5`EXLlE)j4YWdgzst%Z@wlF5H)E@!Ai%>5m*4Z-U}vNbY^Fz* zjWMGaxENM9F_N17&W3U>iKNx(Z=vN;z=OdG#hUR)LDCps^c<-eh$gRgr%OvJ&mPMj zuyx5ru=SY!;b@6L!){icD-<(r+Kb5-7A8DcrD-mss@s<=qP_U_o>PXj@A-EsEzs=A z6h4rge|8zFer0k-k#c-}s0OnDhZX683zs9lcm5RKU zGL*-*JFp^zSj#VZocB0R$Tzyj9=uNJrz$G5Vr0yXdtOF^ko@${*}w3ns~^f3+Q3c; z_8aL~1hk8%4Zi~y)$#E$>B~&`h!UKx_b3RK6x^xw{^uP?jlYo- zE4J@>X52Ro9uPnC+*2=AbD*4XLGRDNExUb|r__J8c_UMi`Zel)2b98PH)MRjC=vhs z`Lq6$7ho7PW?`_A_> zu>Em<7cPyPB=GfaK$@_CB;o*T`?!!1wyrC^CyN1P z7c3MlC!cG=;+m70y*^5Gu&dQKPm)o*#4AHHM6aYh&f(R?%KgPsJ4~Nho{D0a?+*>4 zHP`5{lx*kFSe#LO3O*>`)__CsbN94Lna-ZBZ8t0DFJjQERy)gOL3svbmy=w(n2Zda zp7l!anOgxW+Hwq8R`F5Unx*@O>G`O0EazCK%I^Dh&x&`o4MUdQy3BHUo&*73I=0^f zxl9H5{VBiT{;bxJR(rzUMfs;Mlt21u?0Clo^gzFVBAz3sN#yM`A9VA0XTG2<;2uo{ zttbtxRAba@1}h#_AozePFBEiLBd=?WR(*gXv39cgaz9S^qCY~vyi-Z$$X`ALcNWa+ ziigdEWK^db+IP!d$5T@f*ap$^9>ZK`w20-ZkN&Lj=L_$N4_M-&YVRxjVGZ{zR3<> zPS&2uSD7Rr$)4>~YUE$RN*16Q#Y4*2qoAUKin&!}l%4%PKqZJE;t2}+|12yt&6N=Y z`aiZg>NFW9{3$&&$dCXN z9r(ZcJPv^3c<=`(=phLVjki^Ja7I2Uhd*f<>weLS5hDmgS9_}t&n1>dF zs8R5^sg#jELkJ7F@dO40M#i7h6zRF^fj{_+->Xpa!NT19_GlUQYhOJrHTADI(2J6W zhDW{dmB#xUC_FN1!}-N}fE zHNB&Q{o3NI2I}99;hxchxT-EbgPY9uFY$F&HBo`!iOHQAIf1=eApec!N>`U4m_s8X zBJy!+Y6?6~_0XpDmnmIRT-;?^>+pDKPzf-zYdvE>Z#k)u%GCQydIaaFPj_N5bCn|h zjzQ`XQpl!nmN%~V*fzQ~M}ZZRgd|YQuDxC@wnWFm+S(ZGb;~sCf3>u<&^MmU6d^ku zOptMNKiBks`-jaQbja8KNBwNAhUf3%VNw{lxWkAXtCM>-VB+T1%ElXeN=ix+TM69x zuf_OyjE;x)i>XSzPWnbEX=zew>f@S$|E$y0@Z;YdEntADf=v!Pldd(pUi$i9SWL(C zhkk!mV=V;d(nQ{u-LmM-n&pXgb*vakUTot3oat*Ho9MrTq)q!^PvEKYLI8cev0&20Taccc8Ak6QE43sHiAUouEcW z1l|Xj$fZ!StXdZt6-CC!r&Ibjl>m$J5$`{9tQ8V?Q6UyHg;Pax#1KM4LSbQHO{Gk@ z2qE)W(0>I`(RwbFH}%M&?tzNDdzzD5doU4yd6SS;+tHG`T>~e}1wwj4<1D5f+X^%p zRd2Ay9ZjU)3rzn;TMkT$>!gkNIRZe{S{S)W`ZK2?zxPBtO5K%aZzH-yJ4)9*buy)s za`n%K`VzkWXCkB)tPp*XxGbL$5J+ih5$ZL4RCW|fl!HP-8ty%#!o#opsum%oOZobL zo4D9Tc^Y|B@>{nx)%e5 zTxNcDwtrz^AplKWNeLYq8akcVBi8GpJxL)?OlLdnPh*x@v^iCdjYP!21FnPYE{)<`!6<Ta&V|#C3JiJeWy?Ga@c7F6)hs46pO%P$r{+TOmCaGKnkC?Q7X;D0Cw&B7WU& z0Ub@OzL1_2KHj$F0TQ>3p0lrAN1)fjO;VMw29X8PqGqY?tYis;DrC~M=G#Ez!lps0 zjS0l8m6Rm@KZ7tICZJyT^OPdnZ&Xe1Z^+pRr$Y4a&R$8Ur2N`K1KtLYIASXu;zbS{43y60IjD2)O~x_Xz`?deG&W-F z{Z=y+H=4|I)vR7*BQ=(#SVC7{82n2Z0Md1pv1hCCRA~y@{nq?v04c2U!+!>3Qy2J~ zTfk)eF;kX@4L^aeU%w{#oP&MEpM-?|9O2MpejiT;G8MjO@hknyq%ukuMs{7OCQ5n% zarsC2kdlC9*A;G>N3S<3X(Od4qsW&gVuAM1(1kPo$=UDTfsVhTdlv)by;o)5A%*~T zmyd!_%FF7w1Oq#|WGZWsM@l%2u5?z|45H`;-?-_cgBH!530s(09R>;wuFI%IXdtyv zX;f|V7>7ktJN|cg#QFNdIWv=qDf6eU7P<%_E!sF!G9%>=oRs6^hI5)wF~$SWe$lOy z&dZTjJF6T&Z(q;+QvPVu`{rL&^j@i3a`CDAq7s%0Z$J{Qihb!)_C z-(?}^7o|-L@Dy4ozFEydem=PqM*G)DlIuSFL%nkO4yNzU$Z%YN2f3VHA06J_9y*mY zH6@*#ZcI}C&K>i4-Cr(rz9saABC}Y{(<&+{y$g6f5EYtuEWW&Pz?dcnsC8{+2X>lv z3jP-t;EVC3gnXFDH`M;H;DYY?0eYo0Q;zv_Rs**?$+SY5nS^>ZIY4NJbNSlLxnx>J zLTbfgla-Ma9?2icxgV0H!C{62f?uR@_7FOH5uASB!DNS}+d_Bm-ZWPRGLweT7V;!UtW<<-{X*|mmrjH6*=N^SD?*#_Vwm7W%yygMHcAPjK$@6cGdf4JOzw| zScGT7AQ24hkEc0p_&k{0?2lVmSPXk@ysT?GPl*;8+*PvXw37)qyf^SRJWOlfw9`FB zY#r=fFqC=qyS_X>m}%bc<@ovB@98Ov9i*bNHD9;wdz-8!o-VN^R~j4pnwXMZyoK6$ zSYF&LwXk_CoYvqUhd#e3ElmE-|0;7JC;c*qKI1DYPSo`9hj)>Qi=&=+wZdX|Xy-JC z9)wRc!KVipZ3@RW-iAhJsX5xyYGZ8>0KJ?{w?5%NJ?|&kIekAF%I6urD&)F9jsYp? ztVspbTI?#c5~LIs8q3*Tpl00_`4zL100lg+6>#U`Z|_|xaNS;aPA`COthKd z$eO(^hy8^4+eOFQwKV5-#9K#x4%1RAiCfunTqrw2q0?BO_VjElK6Qr2VF}$40bUBS z-lF@l)$-Hb>O;YJ$|FhR-tkS$W1EGx7#kVT?SaVcFk+rBU7S5;jID7!-a= zF8@d!pCdoV^-D?%BYaW&>E^V+?C%gY*Zm!CzuB-;KVP$ZW6Nl7?c;_FFQf_q3Ae|vw8f&CA=amCHYrd=BJB@DN+?p5M z0WyTYQ)U$$MBCT7`Ty14mq#^~ZGGaiN?tt`x}K#J0#a2>4h%BSq^c~%6dI!V4?Ck`;XdK%YiuX3#hs zE--EOCeSoS`p4IL0B#_6s;Y(ZW_mQWwFi?(&cz!XYE5OO9t{`ghG=bV4IO!WvSX>P z-Xd#r#FmNqsxZ(pF4RRjHWjNA`R-P@uH>hum8Lqy>Mkwza0R_0>(kr``^&r*@!MiF zae>!u!|S>HbED>Sd)+LYjcyqQFS+3 z*7=!y%H+K(9T`jSLN{jfPK9SkCY(0j?whydijrZZ5e$Q<*=ACUKbJy%w@V3=r79EgDSaDsqhL^KMOpAKHow9bll%f3W`3U0oC z8<^Cs;cgv-Qb@{s#D%W91PR93j5@Z#uHfD%apds3MXx*cLlPG@w?a?A;$J*N=$Bkx zOE1A?XPRn)W*@v^RuC*?)mmi9%Qt>}s#c=>%gWIYcq0U(0Nc8WU^Uw*q&pC63jr2d8uj z-dUQvu-T&Q&*7;T^~hpo(5eLsInEsQi=k$ce5 zxamdiewC}XWhgWnZ7JwiRZ$tfdTp83Wd^JDnE%JuO=$#5l)pJ4QXI5i9MfJb$Q27zK$4Aw{F{nJW8qGbaqOzLf zn~Bo^j{5<=p3-ahi}K4ie+QHg7=Qb!UiqPN^N;h&kE=hwKC1lu@p}M9f$``b01UwR z?KChcFb?bgQ!alhk2U*v8cI*0P6ls3{^gwMD+4+anP1i99s4g`NX1r54o<aDB(Nk z6Mbv^J=*+>E6^2|Am6Z4@f`~~QE|(}#=Qj*eWR2`cf_v&tYN$%IiMzN)&vaPuUvf3 zVZLZO-6Lkp)|kL9M#ticv+;%FN1|ddg(Y#=l8#lJ`?r}1D*@?uuB6Dhe$&DBONwJE zD&1$(`j4u-)NI$&DKI6}VPRm|AMT~lF>2wsdsOLzBHyg>q9fSCeJdTK4hC^?UWwA7 zqfzCfMYu{4{6P`;7R$fKN;IA1xe|n9UC=5|Yed4}W1TGHhsg2%SKhV_gR)zd(Fkv9 zL?1%Z98SDAYW2NkwQfi<=fS>16 z_sbFizp!jn4C#7&S3Gwk(TxL%41{%UI3Rlgs&#zh$rPjP*CdMvN z8(&;yatl`*p4oOJWT4y1H|5O@zvg6E9r#?y{gds3_Q{Tl3IpC$q1-%VG?0GJV4yPv zZ{g@=IA#P3N>A*Y>gFFLMf;d`fr1L79SXFkk2@qW@OB}k>bm5MsVT-o&O~*cjrA}6 ztRnpTyQPl=6EL^tYSiG1h2cQ5e=#~5j#59A+?nb<>M;B9YJY8e=-OOmlby?#4x6W~we(gUbZGC>OxY0;=kiVbfHS}Z<4~wtP(|j+?cWurRPp!8P z4PB2359eFAhrY@uMNH1Vn{cw3uP<$m7Qb(pcCisOmX;C?1Bc zEo*7eVc)LN^mS-n$hpax`@qaj<&|t!KPNmqR+8eky2);7m?}zdsR?t(u?ech;eA`t z{;ZjzfCyZ;P^#%C82zcHqZXA?kY!ICKa|1s9;sBxj~fz3H%7&|hpq1i2XJna6wa7o zeu(qmF0Paf#$umz&fa%=mK>MrJ(W0bvfHu^7SA%n8xbLt_bMuO`npNFyEQmu)R&HE%+Irzx>OBECm{O0 z{R0Xv#8{UrLz(%yYQcR;OCrpA^0@=iu^x;fPoqM&8r(I#Ft4~e>B*C*^)q)B3R>`^b=RGf8&UElGCQa$6l7{L`oWZDbCFnP*12<=Eb;2wz{DIHygCXGTj`jTzXWlC2H@7BFRyuM&lK z6c!zca+s??o#^QzQ=Z!tn1Hkg)t0!6S?0!PFis8|;H3c)fvcOAezM*Z@oS7f8KbW` z-DALkq(-DgjB}R=W50T#j12tJ;YDFTHI}A?xE!xB-x!e8CpTZ>n zG27lH^~Pj#wbX-`a;ra{cL9BVpi>L>)Rxj}WWVV;eLMa_(_oKD`>=MYcUN=vP-jCx zyG^-e0%`7zy3lE8NewGE7^-#Ls?@_yqL-|(QQFQnNeFF$w@2p>Nk=&?iQFT>WdD*< zT6}rQ^V;ed848%dG{!OFP?jOLmOE?xG&J7LfN-&n2JK!FFnT|Y4`Qav&|Iz2wNTgb zCdR`eu_|Ua@T?tul>nI-{yGNdiyW)zKr}$`@+u5AgzSE~s6?cjM0{tKx_sIiEcj!wB>C+?VFz+F@iqK4;UU{gVu@JTM#Gs8ibfLJ)zf=hBeD)0}!^%T__Xb$3r zZ~zo;2qJGBB`MZhJ?%Ds#-_LxvqmUdc|4ykq$9x(H)#xYbd}}`| z*@vVr{HIzjj^w(dkAIIe^bVfFW&a#L-TNKa1bh@*(h~VowKt-dtJRFNoTPvrKKZ^7 z4EzvP3Js~wbKV)V;$LFawS{2ScC5kLLudB;hR!^uCD8_-8}(R65Ag@c6r`0W8snHy zUMw7cUvUDBJjv@aCm+4BIN5r%Ahm4HKXoJGIQT@^Ie&!r6lsn8b=0iQ|#5yhx7_Mq@*!bff`WRHoRn@>Np=Lu+b4U|7=8`D0-J#JQ*BGA<3~wc-7GA`NCL`(6QVXLb&LOYsz#Y7Gs?^iUY9O zPCDcR%;x8o0`v{lazMYD!y=;%tnQ4ok|UYmU9hMRLb@p#d=xv z)|!IhsZBhmxtqQMbr;Ll&hOn^@IRDTxOk)xe&dbDE;~5<cW~UHI#GL%j}(G#@57xh8dQLTrys>UmY4#GE4Q|oam>v0k)~-8NWHdzsqif zL&r6A)@QBvR6#lY-E+xBO-v>mKZ-gsUwTf|%n&RF14`R5pYOP0M{_%k*aIZ-!9H03 z^G>Pb4SwLj=Kwi8QZTfacHqHatXoTOSm|kEC8<2)|HKVL+ov!tIDaWhV0f zQV6pVk-5XwzdLR1KVpq_p%BeAD6>&UZ~wd?=${*c{rm3!tl;iX0=%7O?(Y8Kdwt470{a7r;sqo7J0(X zSnrfg#Os?OWQ)|~=one16L2|Lnb36I;BrWKC!7Ohs|)_Vg)#LIOG+TUSYt={OCC&T zaioJ?*{KI_*>+dk=j7xxhi|l`e{?HgmMVl;Cq|pK+49QpJHaXD>)Z2|f;+=De4lY1p!9+YFGJYpEsSGCf0^Cou zvCRqe!T%}23#3n9+Si0|q5Mp|d@(De4>^!q?%K((BMR0Sfn`KS26)ZM4Zj{K7c|Mw z22TVzFXAc(%8C`6G=*n#Dv$JqRJBqO^|#3k3T^mG*^f!5G) z6zUef&K@A?A{glAd3EAnRP1hfLnca_jMMdFs11%$08fiR`xnEzc@@j_0HNwqQfd3b zZn$j0>rNfGIg`4(L(<~b4C6FDPFD-q*qq7=UE>|HtTO};OzjnaBFTb#*EWnG6nOy- zu8?P~0A0>3X!dt4Q}Ki$D(pLqWDr)9e1_DQu6zU~+T>EgE5ldSS4WA%M2=u{BhUJTPJ}$FXH)(xax8+rO7Vg!aQy?QIOD6_rmW-Dzj*jt6lGIIQE{{#fZ5-(9LX zcl!72<&vsjUoF+|O6|E4Cq?2i2lX&p`!N#Q#h@66aXG z(A}D=xsTa8EZkbK?aF}L9 zhlH_r(%A^PS7UFg&zjRM*ai_oge%^V*a4n!_I`#(*ZQ2XyidepK}m3mL=_Mdl}5|N z72ng>{MIh*>o;}h>+cKo2 z`I2YEQhj%d{4sdn=-BiF=dSw6Eopv8ycbX~B!&kgMD%pG?9Fj;)8f%VoSP9l2*e-G z#Xm9tWNvZk7q7+_i5c+{LMdN~Q0=|kp{5K55XYInxU{k7N*@4dZow1e@_|G0cBj>< zTP^ZQk4apLtABqU7})=o%Oh>+Z1teRvz5o1qVg*$Dx7-xwbKfbqO2`uTxpqP+3T6y zZzs#X(#RhwHH#s7fGo@s@EPF;wTNf}kehUsWOrUVuk<6gbnGsFClTqs1AG!ZIyzce zV4j*nk+=4cbZDsyVm2-!0qQB_ay&!kpXENyefP(1|Iuw{)B_|fu2NSdM;g#uT52jP zX>`ii*ptSULHrlBVbc!*LrCgZ*3Nhurq0R`?*FB8zPb*0M_m~RxVZQmZzUPcBOXT)3#gf9Gq-# z0(oMyZDg{YlT=zUr2vHF#ed2OV(kJJUkB94fJ&8Z5xS?R7KKElpm#DTXVg`2TAJpy>)Z^DG+|$W#^$8*BiU2u>Ck;cnT2)0PBn<01 zI^Z`5kkg!#`$ieI_wYTEV`8Ky%(QaA+u{T0p*=NtZBB06z!`Kspon*o9^;W{GueLG zVm2HEoHRIycBZGVWCjSNNi=}~FZD`SsK$E)1;zP**4(q%AemohS0+NgI56Xi4NTP0 zEg8^Y)qplbA{MlFsI1=D&MVs0o>z3*;1ybMG-m5%m*A$E?5zCv$r{TT;kxwJ3DRkL zOBV_+2Wnqr-7S%rVV;E^5v#%ONe+%F$kec z2+PyGl0TB-88}B>S%D%#zBWr@isYY)lSpj2ImxBvdnF^m%Z3&29_zv)RwPT91>GW; zLot41G`T@G%t^8U`l*x^RwMHdpnFZ(;+S*}ZKe#~{%Oj}O3m%W{IhAfyi`!_W*8>+ zRREp9Zv^)tU6#~fkfUZC_S)mal2cbEkQK5MeruoVqzVN`4*Xj#-#xDD zFhF0Rm!%(n6RvOxt-`xrzM@e)2V)!k3(9p5Xr+7a8?9g*46&>`g{gQ|k*mc(2u|f? ztqEp5@@lo$0HC>C-Ej%1(eDG!V)yVOuf|@WmcV*oX4x}TDyG~ql`Sngo4!@b7}ijP zM`erfFe_aN2~>I#v^Bg(ZO4e;1 z%mA5w01H8@%Dn4U4f;ljnGzD#!Z7r;7Elpn>utH14w6Q?hyyT$8pSHmlOqmsWmB`0 zu9(7zWA3QYVoo-&G1EGD^z-?F(9u2(RuNorB3&4uH5$YnDp2U&ZH6W!WV3_?5rF$% z~6B&e;(kp{8EgxZ#q-7Q_NLt8fng0IZ=b*VU1-;={Xc?*qct?LO#uW@bxLn{(AOFh)=a1KbI3o98kUa~k zjBUzn=l+<7xc%lF`w0FWT+vPh&Xu?q5HG0!lxaa1|H5hS-ZzN=nd+{F0pzQep9=u- z%x|V;b}qVGtDejmeEN6ALF%;$cqlh0Dt`VITr?0vL14G`V{8`|6oYtxm(;b^UW`*~Y7|C)D-KX{B#pm3aQ?pd)4p*7Uz z1xg6LW%$eRsp!Fq?cKflH*A(k`p4VMa?#S6#7iZPT_B<EJW5Ze|n3r7o0W+`JR){Ab0ej1|o=OF_mi?k6{f(Nqkrm)0g35b@4- z=3TJC-Gpa(+{;~T;v+_Yr%t$Vgi6ox^6Ni=GNZF*rYEFrVFDwdsHJlCZ*;{4`d4<-p4lVP79V8? zl8Q=e1KUT#(kIF^#wM3o*ESRsl;7m2{6S@`V&ru8`Y#(KO$Ivfa%$r6b*62&5!v(D za02~)1EZ|F)i8a9@&w+H)wXDt9@+PYF{<4$E-uAL6SXbLt3|DIlcD7CXw37_Z2kb% z(DIBUe`?ykPk38^G@&7RPG#M=#wn)|f+MeOp|+ToL_o+=Ts1%29dPv)HhKUr=QDGT z-I?b)6Ub&pX5Hx6iL%yqqliKGVaUV4R=W~s6Y(gPpnk{=omMv$7(zhp{Va#bqh{FJ z_~lpUQamAitgZp6H-qWiggpl2o|>45Bt$^50X*5GIRRo`TEwKZqoCy!mWT2khAR8h z1>8{4Omx3X#7}4IR_Mjj`Su*s^XlWL`SlveCc1A!%a%9>xzZTjoM-KE6wMZacitaB@JC{h@=vx!%f^A~$FUO6yhKfB13BzEQPaZRAj^Y_TLOyzKi^DeSo&{Oc9?{hwsr2ui7ii&rnj1}GA!<=TedLf|gMxHM4kJ3#cvq}Es z@Og&fJMqRav%5fpJ*6J~DO*_oIGKwoG)6-_`k%A7(9)LIvBTcVX@09ftJiCard#d+ zea5E2Yr3sz*%sWfP+2vT*L!yGR8aeo5@%=eW{K(whvsX%``$Nhf4jFl*==N1}*ubtQxlzz53^ry3{y<%a7C_bxwf@Fu43e39nI+ick>IByr5}q(w_%jx{ zDHl9JO@}Y2mOGhs2y&Q2^&*m^wqt4Qg*Kp30T?FiCwHo~Y<7iXaP7J>bd`o7Mna$4 zFM{`iCfgMsI50`RbN}gSIc;av(s-yis1J7pY-qrSIOXn8+oCUByS}v!Xow+22FdeA z_A6b?)urS!wc*@h9Y>@+8Cl|`KIr5;&4UCjD;7X%<$Vs38q>S zUUQ^8`JMW%n^Vh6zt zV#i0D(fyb48NB<{#_q!t8(T?C#6GF zB!t%xzRW2yw3VM}#)HI*W(ZeB`{AHl7EwPpFW+GbzVy(5y_U?@AuJxb zLooxTWuficTJGZ^y_sVJ+-(n4v`sJ`8I))GG|x>&_saBHrMse?>u3(}1QHiUj-c^)r{Oe(5N@v~~OkEl2N6XD0^zNGB02LretN_Qlf zP>pKJ5z-l>yK#q0+xNTuwZRV(gnk}5GJr~gQQiV!0x(*ip3)P)r}ym{J(`!`X&UG~ zc#n%ZpH+4gJVQJCO_Le^zEgYWyEi*(NRiBh1X?)`H=>(qRrHrX?W`FcP@W~Yl~Qx@ zFkHCUQwVzzZFProrSjf3lj?u6`+(tY7T|K;q+muMz#HcL|8!!lT1^d(J8pHwFTRU- z;N!9FKYw%!NDEJ-^Z}E7*srld^h+ugRi3}`KinRu+fa-xpp;*~86jj_Vxr}fb+HE8^anqC13A%j;eQjcl5N5ITu)C=UgwEDOP@YHb==+zlX$?1Lv&C3^!flE_F1T0KAlLPnfI>~ZscWk?D%X< z+ST`&*9MTTm?VocHqxK`K`1~3+X#<$m5<)UDu~&-jShuQjF>c-hM?G8xHl4c#g5R z_8TithU&bCpqlnY8+^X(@|?r{|G7zzsy^RO<=2A{=(bK=?WCvD;#_-DftFV7E5o1d z_0huK^XTlc^v^5&G2i%Xflu9^ig#6Ed!ahpyB4LtEzkL{Bkdd<9G3F8XY&rFHU_1* qo<^*1Y~-cwQT_=982{(wTA_Mx5(2pj8VOcj2R?b`m&TtjUjOgXldXaP diff --git a/e2e/existingUserLoginFlowTandem/User-Logs-in-with-Existing-Credentials_FAILED_Jun-14-2023-152329-GMT-0500.png b/e2e/existingUserLoginFlowTandem/User-Logs-in-with-Existing-Credentials_FAILED_Jun-14-2023-152329-GMT-0500.png deleted file mode 100644 index 09bbce80ed7e5434e41a8f99076d8d9c0058b158..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29814 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 Date: Fri, 19 Jul 2024 17:08:50 -0400 Subject: [PATCH 23/40] added custom commands path --- nightwatch.conf.js | 2 +- package.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/nightwatch.conf.js b/nightwatch.conf.js index f2943fb..7ad2738 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -5,7 +5,7 @@ module.exports = { src_folders: ['tests'], page_objects_path: ['pageobjects'], globals_path: 'global.js', - custom_commands_path: ['./custom_commands'], + custom_commands_path: ['./custom_commands', 'node_modules/nightwatch-vrt/commands'], custom_assertions_path: ['node_modules/nightwatch-vrt/assertions'], webdriver: {}, diff --git a/package.json b/package.json index 0dc0955..d10eb89 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "eslint-config-airbnb": "19.0.4", "fast-xml-parser": "4.3.5", "form-data": "4.0.0", - "jimp": "0.22.12", "nightwatch-vrt": "github:tidepool-org/nightwatch-vrt#master", "otplib": "12.0.1", "request": "2.88.2" From 0631838a44bd8021133784bb0e0ab1d81f7a11a5 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Fri, 2 Aug 2024 14:44:01 -0700 Subject: [PATCH 24/40] package.json fix error --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3488ae7..d41b330 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "testdev1Chrome": "nightwatch --env dev1chrome", "testqa2ChromeClinician": "nightwatch --env qa2chrome --tag clinician", "testqa2ChromeNotifications": "nightwatch --env qa2chrome --tag notifications", - "testqa2ChromeRpm": "nightwatch --env qa2chrome --tag rpm" + "testqa2ChromeRpm": "nightwatch --env qa2chrome --tag rpm", "testprdChromeNotifications": "nightwatch --env prd --tag notifications", "testqa2ChromeTag": "nightwatch --env qa2chrome --tag clinician", "eslint": "eslint .", From d0fa3195e41347ba80cbd3b7827e0385ee678052 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Fri, 2 Aug 2024 14:49:34 -0700 Subject: [PATCH 25/40] fix eslintrc.js error --- .eslintrc.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b3eab58..01919d7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,12 +17,9 @@ module.exports = { 'chai-friendly/no-unused-expressions': 'off', 'object-shorthand': ['error', 'properties'], 'no-console': 'off', -<<<<<<< HEAD 'import/no-extraneous-dependencies': ['error', { devDependencies: false, optionalDependencies: false, peerDependencies: false }], -======= 'max-len': 'warn', 'no-plusplus': 'warn', ->>>>>>> supressednotificationstest }, globals: { browser: true, From b4e9c765a7194af8353ec7e6a2fdeab9dc940bb3 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Fri, 2 Aug 2024 14:58:26 -0700 Subject: [PATCH 26/40] class-methods-use-this warn --- .eslintrc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc.js b/.eslintrc.js index 01919d7..f770db4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -20,6 +20,7 @@ module.exports = { 'import/no-extraneous-dependencies': ['error', { devDependencies: false, optionalDependencies: false, peerDependencies: false }], 'max-len': 'warn', 'no-plusplus': 'warn', + 'class-methods-use-this': 'warn', }, globals: { browser: true, From 5002a5489456970f55bbafa3e5e0f46253a10291 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Fri, 2 Aug 2024 15:07:05 -0700 Subject: [PATCH 27/40] revert to testParallel --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4b1449c..ef9d403 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ orbs: parameters: testEnvironment: type: string - default: "testqa2ChromeRpm" + default: "testParallel" testExecKey: type: string default: "none" From 7f8d61fe1d3dbc323fccca51dc6b76e8d784338b Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Fri, 2 Aug 2024 15:18:17 -0700 Subject: [PATCH 28/40] Update checkFileContents.js fileName param documentation added --- custom_commands/checkFileContents.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_commands/checkFileContents.js b/custom_commands/checkFileContents.js index 98b9d7a..20d96b4 100644 --- a/custom_commands/checkFileContents.js +++ b/custom_commands/checkFileContents.js @@ -14,8 +14,8 @@ const getFile = (fileName) => new Promise((resolve) => { module.exports = class checkFileContents { /** - * command method of class checkSupressedNotification - * @ + * command method of class checkFileContents + * @param {*} fileName string * @returns transfers latest downloaded file from remote server to local machine */ command(fileName) { From bae15b7acf16422b647781bd02916db5c99bb598 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Fri, 2 Aug 2024 15:30:46 -0700 Subject: [PATCH 29/40] Update checkRPMExportSufficiency.js add documentation to checkSufficiency function --- custom_commands/checkRPMExportSufficiency.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/custom_commands/checkRPMExportSufficiency.js b/custom_commands/checkRPMExportSufficiency.js index 2e7ca60..7a07775 100644 --- a/custom_commands/checkRPMExportSufficiency.js +++ b/custom_commands/checkRPMExportSufficiency.js @@ -21,6 +21,12 @@ async function getReadStreamPromise(filePath) { }); }); } +/** + * check if row in rpm report that days with qualifying data, item[3] , + * contains the expected result for the sufficient data column + * @param {*} hashValue string + * @returns true if expected result for the sufficient data column is present + */ function checkSufficiency(item) { if (item[3] > 15) { return item[4] === 'TRUE'; From 322895bcf7a7f35db7ba1f39879a3619edda6863 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Mon, 5 Aug 2024 15:36:17 -0700 Subject: [PATCH 30/40] date string creation moved to function createDatesLong and createDateShort --- .eslintrc.js | 2 + custom_commands/createDatesLong.js | 21 ++++++++ custom_commands/createDatesShort.js | 21 ++++++++ package.json | 2 +- rpm.csv | 5 +- ...sFunctionalityLastUploadFilterAllRanges.js | 48 ++++++++++++------- ...FunctionalityLastUploadFilterLast14Days.js | 46 +++++++++++------- ...sFunctionalityLastUploadFilterLast2Days.js | 46 +++++++++++------- ...FunctionalityLastUploadFilterLast30days.js | 46 +++++++++++------- ...StatsFunctionalityLastUploadFilterToday.js | 46 +++++++++++------- 10 files changed, 195 insertions(+), 88 deletions(-) create mode 100644 custom_commands/createDatesLong.js create mode 100644 custom_commands/createDatesShort.js diff --git a/.eslintrc.js b/.eslintrc.js index f770db4..d819a7b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,6 +21,8 @@ module.exports = { 'max-len': 'warn', 'no-plusplus': 'warn', 'class-methods-use-this': 'warn', + 'no-unused-vars': 'warn', + 'prefer-const': 'warn', }, globals: { browser: true, diff --git a/custom_commands/createDatesLong.js b/custom_commands/createDatesLong.js new file mode 100644 index 0000000..a8da583 --- /dev/null +++ b/custom_commands/createDatesLong.js @@ -0,0 +1,21 @@ +module.exports = class createDatesLong { + /** + * command method of class checkSupressedNotification + * @param {*} start + * @param {*} end + * @returns startDate, endDate, startDateFile, endDateFile + */ + + command(start, end) { + return new Promise((resolve) => { + const startDate = start.format('MMMM D, YYYY'); + const endDate = end.format('MMMM D, YYYY'); + const startDateFile = start.format('MM-DD-YYYY'); + const endDateFile = end.format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + resolve({ + startDate, endDate, startDateFile, endDateFile, fileName, + }); + }); + } +}; diff --git a/custom_commands/createDatesShort.js b/custom_commands/createDatesShort.js new file mode 100644 index 0000000..6708052 --- /dev/null +++ b/custom_commands/createDatesShort.js @@ -0,0 +1,21 @@ +module.exports = class createDatesShort { + /** + * command method of class checkSupressedNotification + * @param {*} start + * @param {*} end + * @returns startDate, endDate, startDateFile, endDateFile + */ + + command(start, end) { + return new Promise((resolve) => { + const startDate = start.format('MMM D, YYYY'); + const endDate = end.format('MMM D, YYYY'); + const startDateFile = start.format('MM-DD-YYYY'); + const endDateFile = end.format('MM-DD-YYYY'); + const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + resolve({ + startDate, endDate, startDateFile, endDateFile, fileName, + }); + }); + } +}; diff --git a/package.json b/package.json index d41b330..f40cf4d 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "testdev1Chrome": "nightwatch --env dev1chrome", "testqa2ChromeClinician": "nightwatch --env qa2chrome --tag clinician", "testqa2ChromeNotifications": "nightwatch --env qa2chrome --tag notifications", - "testqa2ChromeRpm": "nightwatch --env qa2chrome --tag rpm", + "testqa2ChromeRpm": "nightwatch --env qa2chrome --tag rpm2", "testprdChromeNotifications": "nightwatch --env prd --tag notifications", "testqa2ChromeTag": "nightwatch --env qa2chrome --tag clinician", "eslint": "eslint .", diff --git a/rpm.csv b/rpm.csv index c7e8e54..53c19cb 100644 --- a/rpm.csv +++ b/rpm.csv @@ -1,4 +1,5 @@ -Name,Date of Birth,MRN,# Days With Qualifying Data between 06/07/2024 and 07/06/2024,Sufficient Data for CPT-99454 +Name,Date of Birth,MRN,# Days With Qualifying Data between 06/26/2024 and 07/25/2024,Sufficient Data for CPT-99454 "autodex1","01/01/2000",N/A,30,TRUE "autodex2","02/02/2002","DEXCOM",30,TRUE -"autodex3","01/01/2000","DEXCOM2",30,TRUE \ No newline at end of file +"autodex3","01/01/2000","DEXCOM2",30,TRUE +"autodex4","01/01/2000",N/A,2,FALSE \ No newline at end of file diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js index e6712eb..a9077bb 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js @@ -23,10 +23,15 @@ module.exports = { const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 40; - const startDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); - const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(29, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const start = moment().subtract(daysFromToday, 'days'); + const end = moment(start).add(29, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); + console.log(`startDate${startDate}`); + console.log(`endDate${endDate}`); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -60,10 +65,13 @@ module.exports = { const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 10; - const endDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); - const endDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const startDateFile = moment(endDate, 'MMM D, YYYY').subtract(29, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const end = moment().subtract(daysFromToday, 'days'); + const start = moment(end).subtract(29, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -96,11 +104,13 @@ module.exports = { const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 57; const dateRange = 15; - const startDate = moment().subtract(daysFromToday, 'days').format('MMM D, YYYY'); - const endDate = moment(startDate, 'MMM D, YYYY').add(dateRange, 'days').format('MMM D, YYYY'); - const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(dateRange, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const start = moment().subtract(daysFromToday, 'days'); + const end = moment(start).add(dateRange, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesShort(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -125,10 +135,14 @@ module.exports = { // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - const today = moment().format('MM-DD-YYYY'); - const startDateFile = moment().subtract(29, 'days').format('MM-DD-YYYY'); - const endDateFile = today; - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const today = moment(); + const start = moment().subtract(29, 'days'); + const end = today; + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js index 6536e17..fd10425 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js @@ -23,10 +23,13 @@ module.exports = { const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 40; - const startDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); - const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(29, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const start = moment().subtract(daysFromToday, 'days'); + const end = moment(start).add(29, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -60,10 +63,13 @@ module.exports = { const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 10; - const endDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); - const endDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const startDateFile = moment(endDate, 'MMM D, YYYY').subtract(29, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const end = moment().subtract(daysFromToday, 'days'); + const start = moment(end).subtract(29, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -96,11 +102,13 @@ module.exports = { const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 57; const dateRange = 15; - const startDate = moment().subtract(daysFromToday, 'days').format('MMM D, YYYY'); - const endDate = moment(startDate, 'MMM D, YYYY').add(dateRange, 'days').format('MMM D, YYYY'); - const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(dateRange, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const start = moment().subtract(daysFromToday, 'days'); + const end = moment(start).add(dateRange, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesShort(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -125,10 +133,14 @@ module.exports = { // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - const today = moment().format('MM-DD-YYYY'); - const startDateFile = moment().subtract(29, 'days').format('MM-DD-YYYY'); - const endDateFile = today; - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const today = moment(); + const start = moment().subtract(29, 'days'); + const end = today; + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js index 9440b50..c394368 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js @@ -23,10 +23,13 @@ module.exports = { const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 40; - const startDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); - const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(29, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const start = moment().subtract(daysFromToday, 'days'); + const end = moment(start).add(29, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -60,10 +63,13 @@ module.exports = { const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 10; - const endDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); - const endDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const startDateFile = moment(endDate, 'MMM D, YYYY').subtract(29, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const end = moment().subtract(daysFromToday, 'days'); + const start = moment(end).subtract(29, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -96,11 +102,13 @@ module.exports = { const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 57; const dateRange = 15; - const startDate = moment().subtract(daysFromToday, 'days').format('MMM D, YYYY'); - const endDate = moment(startDate, 'MMM D, YYYY').add(dateRange, 'days').format('MMM D, YYYY'); - const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(dateRange, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const start = moment().subtract(daysFromToday, 'days'); + const end = moment(start).add(dateRange, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesShort(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -125,10 +133,14 @@ module.exports = { // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - const today = moment().format('MM-DD-YYYY'); - const startDateFile = moment().subtract(29, 'days').format('MM-DD-YYYY'); - const endDateFile = today; - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const today = moment(); + const start = moment().subtract(29, 'days'); + const end = today; + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js index 40e1733..8e7ea40 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js @@ -23,10 +23,13 @@ module.exports = { const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 40; - const startDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); - const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(29, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const start = moment().subtract(daysFromToday, 'days'); + const end = moment(start).add(29, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -60,10 +63,13 @@ module.exports = { const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 10; - const endDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); - const endDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const startDateFile = moment(endDate, 'MMM D, YYYY').subtract(29, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const end = moment().subtract(daysFromToday, 'days'); + const start = moment(end).subtract(29, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -96,11 +102,13 @@ module.exports = { const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 57; const dateRange = 15; - const startDate = moment().subtract(daysFromToday, 'days').format('MMM D, YYYY'); - const endDate = moment(startDate, 'MMM D, YYYY').add(dateRange, 'days').format('MMM D, YYYY'); - const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(dateRange, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const start = moment().subtract(daysFromToday, 'days'); + const end = moment(start).add(dateRange, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesShort(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -125,10 +133,14 @@ module.exports = { // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - const today = moment().format('MM-DD-YYYY'); - const startDateFile = moment().subtract(29, 'days').format('MM-DD-YYYY'); - const endDateFile = today; - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const today = moment(); + const start = moment().subtract(29, 'days'); + const end = today; + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js index 56a2a4d..ff9d051 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js @@ -23,10 +23,13 @@ module.exports = { const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 40; - const startDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); - const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(29, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const start = moment().subtract(daysFromToday, 'days'); + const end = moment(start).add(29, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -60,10 +63,13 @@ module.exports = { const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 10; - const endDate = moment().subtract(daysFromToday, 'days').format('MMMM D, YYYY'); - const endDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const startDateFile = moment(endDate, 'MMM D, YYYY').subtract(29, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const end = moment().subtract(daysFromToday, 'days'); + const start = moment(end).subtract(29, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -95,11 +101,13 @@ module.exports = { const clinicPatientList = clinicPatientListPage.section.patientList; const daysFromToday = 57; const dateRange = 15; - const startDate = moment().subtract(daysFromToday, 'days').format('MMM D, YYYY'); - const endDate = moment(startDate, 'MMM D, YYYY').add(dateRange, 'days').format('MMM D, YYYY'); - const startDateFile = moment().subtract(daysFromToday, 'days').format('MM-DD-YYYY'); - const endDateFile = moment(startDateFile, 'MM-DD-YYYY').add(dateRange, 'days').format('MM-DD-YYYY'); - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const start = moment().subtract(daysFromToday, 'days'); + const end = moment(start).add(dateRange, 'days'); + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesShort(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; @@ -124,10 +132,14 @@ module.exports = { // setup config const clinicPatientListPage = browser.page.clinicPatientListPage(); const clinicPatientList = clinicPatientListPage.section.patientList; - const today = moment().format('MM-DD-YYYY'); - const startDateFile = moment().subtract(29, 'days').format('MM-DD-YYYY'); - const endDateFile = today; - const fileName = `RPM Report (${startDateFile} - ${endDateFile}).csv`; + const today = moment(); + const start = moment().subtract(29, 'days'); + const end = today; + let startDate; let endDate; let startDateFile; let endDatefile; let + fileName; + ({ + startDate, endDate, startDateFile, endDatefile, fileName, + } = await browser.createDatesLong(start, end)); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; From 0f4324d4bc59e927e3a0667ad94ac2f7ee5a42f4 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Mon, 5 Aug 2024 17:01:49 -0700 Subject: [PATCH 31/40] clinicPatientListPage comments removed --- pageobjects/clinicPatientListPage.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pageobjects/clinicPatientListPage.js b/pageobjects/clinicPatientListPage.js index 557e38d..494a402 100644 --- a/pageobjects/clinicPatientListPage.js +++ b/pageobjects/clinicPatientListPage.js @@ -333,15 +333,10 @@ module.exports = { timeInRangeAll() { return this.waitForElementVisible('@timeInRangeFilterButton', browser.globals.elementTimeout) .click('@timeInRangeFilterButton') - // .moveToElement('@timeInRangeFilterVeryLow', browser.globals.elementTimeout) .click('@timeInRangeFilterVeryLow') - // .moveToElement('@timeInRangeFilterLow', browser.globals.elementTimeout) .click('@timeInRangeFilterLow') - // .moveToElement('@timeInRangeFilterTarget', browser.globals.elementTimeout) .click('@timeInRangeFilterTarget') - // .moveToElement('@timeInRangeFilterVeryHigh', browser.globals.elementTimeout) .click('@timeInRangeFilterVeryHigh') - // .moveToElement('@timeInRangeFilterHigh', browser.globals.elementTimeout) .click('@timeInRangeFilterHigh') .waitForElementVisible('@timeInRangeFilterApply', browser.globals.elementTimeout) .click('@timeInRangeFilterApply'); From d80c48d7da5c98d90b2f1642b4017afce3112110 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Mon, 5 Aug 2024 17:09:17 -0700 Subject: [PATCH 32/40] add rpm.csv to .gitignore --- .gitignore | 3 ++- rpm.csv | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) delete mode 100644 rpm.csv diff --git a/.gitignore b/.gitignore index 96fb3d3..2d8bdac 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ vrt/diff vrt/latest ui screens -test_evidence \ No newline at end of file +test_evidence +rpm.csv \ No newline at end of file diff --git a/rpm.csv b/rpm.csv deleted file mode 100644 index 53c19cb..0000000 --- a/rpm.csv +++ /dev/null @@ -1,5 +0,0 @@ -Name,Date of Birth,MRN,# Days With Qualifying Data between 06/26/2024 and 07/25/2024,Sufficient Data for CPT-99454 -"autodex1","01/01/2000",N/A,30,TRUE -"autodex2","02/02/2002","DEXCOM",30,TRUE -"autodex3","01/01/2000","DEXCOM2",30,TRUE -"autodex4","01/01/2000",N/A,2,FALSE \ No newline at end of file From fdf2c3fe88de9ac7bfa2e3e3480ce00801a4e83a Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Tue, 6 Aug 2024 12:26:01 -0700 Subject: [PATCH 33/40] add try catch suppressednotifaction --- .../checkSuppressedNotification.js | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/custom_commands/checkSuppressedNotification.js b/custom_commands/checkSuppressedNotification.js index cc4eef5..b3d8737 100644 --- a/custom_commands/checkSuppressedNotification.js +++ b/custom_commands/checkSuppressedNotification.js @@ -36,30 +36,34 @@ module.exports = class checkSuppressedNotification { */ const clinicData = async () => { const token = await getToken(); - const response = await axios.get(`${environment}/v1/clinicians/${clinicianId}/clinics`, { + try { + const response = await axios.get(`${environment}/v1/clinicians/${clinicianId}/clinics`, { - params: { - limit: '1000', - offset: '0', - }, - headers: { - authority: this.api.browser_url, - accept: '*/*', - 'accept-language': 'en-US,en;q=0.9', - cookie: '_ga=GA1.1.516068539.1692684735; _ga_RWXQ3R57PB=GS1.1.1694192507.4.0.1694192507.0.0.0', - referer: this.api.browser_url, - 'sec-ch-ua': '"Google Chrome";v="117", "Not;A=Brand";v="8", "Chromium";v="117"', - 'sec-ch-ua-mobile': '?0', - 'sec-ch-ua-platform': '"Windows"', - 'sec-fetch-dest': 'empty', - 'sec-fetch-mode': 'cors', - 'sec-fetch-site': 'same-origin', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', - 'x-tidepool-session-token': token, - 'x-tidepool-trace-session': '92df086f-917e-4df5-b32d-cb30d15df795', - }, - }); - return response.data; + params: { + limit: '1000', + offset: '0', + }, + headers: { + authority: this.api.browser_url, + accept: '*/*', + 'accept-language': 'en-US,en;q=0.9', + cookie: '_ga=GA1.1.516068539.1692684735; _ga_RWXQ3R57PB=GS1.1.1694192507.4.0.1694192507.0.0.0', + referer: this.api.browser_url, + 'sec-ch-ua': '"Google Chrome";v="117", "Not;A=Brand";v="8", "Chromium";v="117"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-origin', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', + 'x-tidepool-session-token': token, + 'x-tidepool-trace-session': '92df086f-917e-4df5-b32d-cb30d15df795', + }, + }); + return response.data; + } catch (error) { + return console.error(error); + } }; /** * function calls asynchronous function clinicData and awaits for result From 4a8c6250c6ffd6602317a5890430f2282075df22 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Tue, 6 Aug 2024 18:59:04 -0700 Subject: [PATCH 34/40] add waitForElementVisible for dateselector --- pageobjects/clinicPatientListPage.js | 1 + tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js | 2 +- tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pageobjects/clinicPatientListPage.js b/pageobjects/clinicPatientListPage.js index 494a402..2511994 100644 --- a/pageobjects/clinicPatientListPage.js +++ b/pageobjects/clinicPatientListPage.js @@ -242,6 +242,7 @@ module.exports = { .click('@rpmReportButton') .click('@rpmClearDates') .click('@rpmReportStartDate') + .waitForElementVisible('xpath', `//*[contains(@aria-label,'${date}')]`, browser.globals.elementTimeout) .click('xpath', `//*[contains(@aria-label,'${date}')]`) .waitForElementVisible('@rpmReportConfirm', browser.globals.elementTimeout) .click('@rpmReportConfirm'); diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js index a9077bb..2cb747c 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm', 'clinician', 'parallel'], + '@tags': ['rpm2', 'clinician', 'parallel'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js index ff9d051..f0173a8 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm2', 'clinician', 'parallel'], + '@tags': ['rpm', 'clinician', 'parallel'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; @@ -30,6 +30,8 @@ module.exports = { ({ startDate, endDate, startDateFile, endDatefile, fileName, } = await browser.createDatesLong(start, end)); + console.log(`startDate${startDate}`); + console.log(`startDateFile${startDateFile}`); const attemptsCheckFileExists = 10; const filePath = './rpm.csv'; From 0a1cdc4d3ffcafaf3cf303ec37991f79831ea273 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Tue, 6 Aug 2024 21:39:17 -0700 Subject: [PATCH 35/40] add idletimeout to prdchrome section nightwatchconf.js --- nightwatch.conf.js | 1 + 1 file changed, 1 insertion(+) diff --git a/nightwatch.conf.js b/nightwatch.conf.js index 338b3b0..e028de5 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -198,6 +198,7 @@ module.exports = { resolution: '1366x768', buildName: `PRD_CHROME ${dayjs().format('YYYY-MM-DD')} JIRA: ${process.env.TEST_EXECUTION_KEY}`, local: 'false', + idleTimeout: 300, }, }, }, From cc79a17c8bf838482d50a3b48a465ece1c2a5667 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Tue, 6 Aug 2024 22:00:35 -0700 Subject: [PATCH 36/40] troubleshoot timeout 1 --- package.json | 2 +- tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js | 2 +- tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js | 2 +- tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js | 2 +- tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js | 2 +- tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index f40cf4d..f8a0bbe 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "testqa2ChromeClinician": "nightwatch --env qa2chrome --tag clinician", "testqa2ChromeNotifications": "nightwatch --env qa2chrome --tag notifications", "testqa2ChromeRpm": "nightwatch --env qa2chrome --tag rpm2", - "testprdChromeNotifications": "nightwatch --env prd --tag notifications", + "testprdChromeNotifications": "nightwatch --env prdchrome --tag notifications", "testqa2ChromeTag": "nightwatch --env qa2chrome --tag clinician", "eslint": "eslint .", "eslint:fix": "eslint . --fix" diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js index 2cb747c..4bff84b 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm2', 'clinician', 'parallel'], + '@tags': ['rpm2', 'clinician'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js index fd10425..149135a 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm', 'clinician', 'parallel'], + '@tags': ['rpm', 'clinician'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js index c394368..69afe4b 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm', 'clinician', 'parallel'], + '@tags': ['rpm', 'clinician'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js index 8e7ea40..9e49630 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm', 'clinician', 'parallel'], + '@tags': ['rpm', 'clinician'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js index f0173a8..4182954 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm', 'clinician', 'parallel'], + '@tags': ['rpm', 'clinician'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; From f4fe1f3ea0cfe1c25d268e62757b582f6dfe1606 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Tue, 6 Aug 2024 22:11:24 -0700 Subject: [PATCH 37/40] add try catch file exists --- custom_commands/checkFileExists.js | 7 +++++++ .../e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/custom_commands/checkFileExists.js b/custom_commands/checkFileExists.js index 3613da5..1143835 100644 --- a/custom_commands/checkFileExists.js +++ b/custom_commands/checkFileExists.js @@ -13,6 +13,7 @@ module.exports = class checkFileExists { command(attempts, fileName) { let attempts1 = attempts; return new Promise((resolve, reject) => { + try{ browser.executeScript( `browserstack_executor: {"action": "fileExists","arguments":{"file_name":"${fileName}"}}`, [], @@ -33,4 +34,10 @@ module.exports = class checkFileExists { return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); }); } + + } + catch (error) { + console.error('Error:', error); + } + }; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js index 4bff84b..2cb747c 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm2', 'clinician'], + '@tags': ['rpm2', 'clinician', 'parallel'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; From 7b37b5e1897f8798a6daf178c34d7fa7583b7ab2 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Tue, 6 Aug 2024 22:17:48 -0700 Subject: [PATCH 38/40] return error checkfileexists --- custom_commands/checkFileExists.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_commands/checkFileExists.js b/custom_commands/checkFileExists.js index 1143835..a6ef656 100644 --- a/custom_commands/checkFileExists.js +++ b/custom_commands/checkFileExists.js @@ -37,7 +37,7 @@ module.exports = class checkFileExists { } catch (error) { - console.error('Error:', error); + return console.error(error); } }; From 6b8831a99698bbc9fe1246c9d6712856b5f81216 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Tue, 6 Aug 2024 22:42:13 -0700 Subject: [PATCH 39/40] troubleshoot2 --- custom_commands/checkFileExists.js | 7 ------- package.json | 1 + .../e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js | 2 +- tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js | 2 +- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/custom_commands/checkFileExists.js b/custom_commands/checkFileExists.js index a6ef656..3613da5 100644 --- a/custom_commands/checkFileExists.js +++ b/custom_commands/checkFileExists.js @@ -13,7 +13,6 @@ module.exports = class checkFileExists { command(attempts, fileName) { let attempts1 = attempts; return new Promise((resolve, reject) => { - try{ browser.executeScript( `browserstack_executor: {"action": "fileExists","arguments":{"file_name":"${fileName}"}}`, [], @@ -34,10 +33,4 @@ module.exports = class checkFileExists { return delay(1000).then(() => checkFileExists(attempts, browser, fileName)); }); } - - } - catch (error) { - return console.error(error); - } - }; diff --git a/package.json b/package.json index f8a0bbe..0b8ddea 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "testqa2ChromeClinician": "nightwatch --env qa2chrome --tag clinician", "testqa2ChromeNotifications": "nightwatch --env qa2chrome --tag notifications", "testqa2ChromeRpm": "nightwatch --env qa2chrome --tag rpm2", + "testprdChromeRpm": "nightwatch --env prdchrome --tag rpm2", "testprdChromeNotifications": "nightwatch --env prdchrome --tag notifications", "testqa2ChromeTag": "nightwatch --env qa2chrome --tag clinician", "eslint": "eslint .", diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js index 2cb747c..a9077bb 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterAllRanges.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm2', 'clinician', 'parallel'], + '@tags': ['rpm', 'clinician', 'parallel'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js index 4182954..41e4a80 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm', 'clinician'], + '@tags': ['rpm2', 'clinician'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; From c2f90059ff7574bc67c2f4bf66cb7491f7843011 Mon Sep 17 00:00:00 2001 From: brian-tidepool Date: Tue, 6 Aug 2024 22:58:05 -0700 Subject: [PATCH 40/40] increase idle timeout --- nightwatch.conf.js | 4 ++-- tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js | 2 +- tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js | 2 +- tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js | 2 +- tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nightwatch.conf.js b/nightwatch.conf.js index e028de5..afd9406 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -79,7 +79,7 @@ module.exports = { resolution: '1366x768', buildName: `QA2_CHROME ${dayjs().format('YYYY-MM-DD')} JIRA: ${process.env.TEST_EXECUTION_KEY}`, local: 'false', - idleTimeout: 300, + idleTimeout: 1200, }, }, }, @@ -198,7 +198,7 @@ module.exports = { resolution: '1366x768', buildName: `PRD_CHROME ${dayjs().format('YYYY-MM-DD')} JIRA: ${process.env.TEST_EXECUTION_KEY}`, local: 'false', - idleTimeout: 300, + idleTimeout: 1200, }, }, }, diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js index 149135a..fd10425 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast14Days.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm', 'clinician'], + '@tags': ['rpm', 'clinician', 'parallel'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js index 69afe4b..c394368 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast2Days.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm', 'clinician'], + '@tags': ['rpm', 'clinician', 'parallel'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js index 9e49630..8e7ea40 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterLast30days.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm', 'clinician'], + '@tags': ['rpm', 'clinician', 'parallel'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername; diff --git a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js index 41e4a80..48c1408 100644 --- a/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js +++ b/tests/e2e/rpmStatsFunctionalityLastUploadFilterToday.js @@ -3,7 +3,7 @@ require('../../utilities/seleniumKeepAlive'); const moment = require('moment'); module.exports = { - '@tags': ['rpm2', 'clinician'], + '@tags': ['rpm2', 'clinician', 'parallel'], 'Clinician User Logs in with Existing Credentials'(browser) { const loginPage = browser.page.loginPage(); const clinicianUsername = browser.globals.clinicianUsername;