From d01554bc3bf836d4865167de5c22c0f68a52612b Mon Sep 17 00:00:00 2001 From: Carlos Camacho Date: Tue, 2 Jun 2026 03:30:55 -0400 Subject: [PATCH 1/7] add new methos saved search --- .gitignore | 1 + model/processModel.js | 15 +++++++-- model/savedSearchModel.js | 27 +++++++++++++++ package.json | 3 +- pages/processAPI.js | 22 ++++++++++++ pages/processV1.js | 4 +++ pages/savedSearchAPI.js | 26 +++++++++++++++ pages/savedSearchV1.js | 68 ++++++++++++++++++++++++++++++++++++++ selectors/savedSearchV1.js | 8 +++++ verify/qaShould.js | 38 +++++++++++++++++++++ 10 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 model/savedSearchModel.js create mode 100644 pages/savedSearchAPI.js create mode 100644 pages/savedSearchV1.js create mode 100644 selectors/savedSearchV1.js create mode 100644 verify/qaShould.js diff --git a/.gitignore b/.gitignore index 1b517018..5d64c572 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ coverage/ node_modules/ npm-debug.log +debug.log \ No newline at end of file diff --git a/model/processModel.js b/model/processModel.js index afbed0cf..5bfe3e30 100644 --- a/model/processModel.js +++ b/model/processModel.js @@ -1,6 +1,7 @@ -import { ProcessV1 } from "../pages/processV1"; +import { ProcessV1 } from "#pages/processV1" +import { ProcessAPI } from "#pages/processAPI" const processPage = new ProcessV1() - +const processAPI = new ProcessAPI() export class ProcessModel { /** * Publishes a process with optional version data @@ -52,4 +53,14 @@ export class ProcessModel { processPage.pressDiscardButtonModal() } + openCreatedRequest(processData){ + processData.then(processData => { + processPage.openRequestByID(processData.id) + }) + } + + createRequestAPI(processId, nodeId, data = {}){ + return processAPI.returnDataStartProcessAPI(processId, nodeId, data) + } + } \ No newline at end of file diff --git a/model/savedSearchModel.js b/model/savedSearchModel.js new file mode 100644 index 00000000..d93b1032 --- /dev/null +++ b/model/savedSearchModel.js @@ -0,0 +1,27 @@ +import savedSearchAPI from "@pages/savedSearchAPI"; +import savedSearchAction from "@pages/savedSearchV1"; + +class savedSearchModel { + + createSavedSearchAPI(data) { + return savedSearchAPI.createSavedSearchAPI(data) + } + + deleteSavedSearchByIdAPI(id) { + return savedSearchAPI.deleteSavedSearchByIdAPI(id) + } + + openSavedSearchByObject(objSS, type) { + savedSearchAction.openSavedSearchByObject(objSS, type) + } + + openSavedSearchById(id, type){ + savedSearchAction.gotoSavedSearchByID(type, id) + } + + searchSavedSearch(criteria) { + savedSearchAction.searchSavedSearch(criteria) + } +} + +export default new savedSearchModel(); diff --git a/package.json b/package.json index a706137d..a7e38915 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "#pages/*": "./pages/*", "#selectors/*": "./selectors/*", "#support/*": "./support/*", - "#model/*": "./model/*" + "#model/*": "./model/*", + "#verify/*": "./verify/*" }, "devDependencies": { "@4tw/cypress-drag-drop": "^2.2.5", diff --git a/pages/processAPI.js b/pages/processAPI.js index 9026c546..8ccf7fbe 100644 --- a/pages/processAPI.js +++ b/pages/processAPI.js @@ -165,4 +165,26 @@ export class ProcessAPI { }); }); } + + + /** + * Starts a process instance using the ProcessMaker API + * @param {string|number} processId - The ID of the process to start + * @param {string} nodeId - The node ID where the process should start + * @param {Object} data - Optional data to pass to the process instance + * @returns {Promise} - A promise that resolves to the process data + */ + returnDataStartProcessAPI(processId, nodeId, data = {}) { + return cy.window().then(win => { + return win.ProcessMaker.apiClient.post( + 'process_events/' + processId, + data, + { + params: { event: nodeId } + } + ).then(response => { + return response.data; + }); + }) + } } diff --git a/pages/processV1.js b/pages/processV1.js index e95fc9cd..2bc8f733 100644 --- a/pages/processV1.js +++ b/pages/processV1.js @@ -96,4 +96,8 @@ export class ProcessV1 { cy.get(selectors.discardButton).contains(optionButton).click() }) } + + openRequestByID(id){ + cy.visit(`/requests/${id}`) + } } diff --git a/pages/savedSearchAPI.js b/pages/savedSearchAPI.js new file mode 100644 index 00000000..ff13f48d --- /dev/null +++ b/pages/savedSearchAPI.js @@ -0,0 +1,26 @@ +class SavedSearchAPI { + + createSavedSearchAPI(data) { + return cy.window().then(win => { + return win.ProcessMaker.apiClient.post( + '/saved-searches', + data + ).then(response => { + console.log(">>>"+response.data) + return response.data + }); + }); + } + + deleteSavedSearchByIdAPI(savedSearchID) { + return cy.window().then(win => { + return win.ProcessMaker.apiClient.delete( + '/saved-searches/'+savedSearchID + ).then(response => { + console.log(`saved search by ID ${savedSearchID} was deleted > `+response) + }); + }); + } +} + +export default new SavedSearchAPI(); \ No newline at end of file diff --git a/pages/savedSearchV1.js b/pages/savedSearchV1.js new file mode 100644 index 00000000..48ac44a0 --- /dev/null +++ b/pages/savedSearchV1.js @@ -0,0 +1,68 @@ +import selector from "#selectors/savedSearchV1"; + +class savedSearchAction { + + gotoSavedSearchByID(type="requets", id) { + let url + if(type = "requests") + url = `/requests/saved-searches/${id}` + else + url = `/tasks/saved-searches/${id}` + cy.visit(url) + } + + openSavedSearchByObject(objSS, type) { + objSS.then((objSS) => { + this.gotoSavedSearchByID(type, objSS.id) + }) + } + + getSaveSearchTab() { + return cy.get(selector.tabList) + } + + getSearchSavedSeachField() { + return cy.get(selector.searchField) + } + + getPressSendReportButton() { + return cy.get(selector.sendReportBtn) + } + + getPressScheduleReportsButton() { + return cy.get(selector.scheduleReportBtn) + } + + getNotificationButton() { + return cy.get(selector.notificationBtn) + } + + getConfigurationButton() { + return cy.get(selector.configurationBtn) + } + //accepts "Data" or "Charts" + pressTab(tabName) { + const index = (tabName == "Data") ? 0 : 1; + this.getSaveSearchTab().eq(index).click() + } + + searchSavedSearch(criteria) { + this.getSearchSavedSeachField().clear().type(criteria) + this.getSearchSavedSeachField().type('{enter}') + } + pressSendReportButton() { + this.getPressSendReportButton().click() + } + pressScheduleReportsButton() { + this.getPressScheduleReportsButton().click() + } + pressNotificationButton() { + this.getNotificationButton().click() + } + pressConfigurationButton() { + this.getConfigurationButton().click() + } + +} + +export default new savedSearchAction(); \ No newline at end of file diff --git a/selectors/savedSearchV1.js b/selectors/savedSearchV1.js new file mode 100644 index 00000000..07ad9067 --- /dev/null +++ b/selectors/savedSearchV1.js @@ -0,0 +1,8 @@ +export default { + tabList: '[role="tablist"] li a', + searchField: 'textarea[placeholder="Search here"]', + sendReportBtn: 'button[title="Send Report"]', + scheduleReportBtn: 'a[title="Scheduled Reports"]', + notificationBtn: 'button[title="Enable Notifications"]', + configurationBtn: 'a[title="Configure Saved Search"]', +} \ No newline at end of file diff --git a/verify/qaShould.js b/verify/qaShould.js new file mode 100644 index 00000000..403ecbc2 --- /dev/null +++ b/verify/qaShould.js @@ -0,0 +1,38 @@ +class qaShould { + + returnElement(type, element){ + switch (type) { + case "css": + return cy.get(element) + case "xpath": + return cy.xpath(element) + default: + cy.log(`Error: The type ${type} or ${element} is not defined`); + } + } + elementIsVisible(type, element) { + this.returnElement(type, element).should("be.visible") + } + + elementHaveText(type, element, value) { + this.returnElement(type, element).invoke('text').should("have.text", value); + } + + elementToEqual(type, element, value) { + this.returnElement(type, element).should($el=>{ + expect($el.text().trim()).to.equal(value); + }) + } + + elementToContain(type, element, value) { + this.returnElement(type, element).should($el=>{ + expect($el.text().trim()).to.contain(value) + }) + } + + elementContainText(type, element, subString){ + this.returnElement(type, element).should("contain.text", subString) + } +} + +export default new qaShould(); From 43f6eec9edea9dd8c0717251365319a2c3645d3a Mon Sep 17 00:00:00 2001 From: Carlos Camacho Date: Wed, 3 Jun 2026 03:49:47 -0400 Subject: [PATCH 2/7] add chart methods --- model/savedSearchModel.js | 12 ++++++++++ pages/savedSearchV1.js | 48 ++++++++++++++++++++++++++++++++++++++ selectors/savedSearchV1.js | 1 + 3 files changed, 61 insertions(+) diff --git a/model/savedSearchModel.js b/model/savedSearchModel.js index d93b1032..06a450c7 100644 --- a/model/savedSearchModel.js +++ b/model/savedSearchModel.js @@ -22,6 +22,18 @@ class savedSearchModel { searchSavedSearch(criteria) { savedSearchAction.searchSavedSearch(criteria) } + + pressConfigurationButton(){ + savedSearchAction.pressConfigurationButton() + } + + pressColumnTabSavedSearchById(){ + savedSearchAction.pressColumnsButton() + } + + pressTab(name){ + savedSearchAction.pressTab(name); + } } export default new savedSearchModel(); diff --git a/pages/savedSearchV1.js b/pages/savedSearchV1.js index 48ac44a0..a3f0a7f5 100644 --- a/pages/savedSearchV1.js +++ b/pages/savedSearchV1.js @@ -21,6 +21,10 @@ class savedSearchAction { return cy.get(selector.tabList) } + getColumnTabSaveSearch(){ + return cy.get(selector.columnTab) + } + getSearchSavedSeachField() { return cy.get(selector.searchField) } @@ -63,6 +67,50 @@ class savedSearchAction { this.getConfigurationButton().click() } + pressColumnsButton(){ + this.getColumnTabSaveSearch().click() + } + + addNameChartConfig(nameChart){ + cy.get("div[class='saved-search-chart-config'] input[name='title']").type(nameChart) + } + selectChartTypeConfig(type){ + let chartType + switch(type){ + case "vertical": + chartType = cy.get("div[class='saved-search-chart-config'] canvas[id='bar-chart']") + break + case "Line": + chartType = cy.get("div[class='saved-search-chart-config'] canvas[id='line-chart']") + break + case "Pie": + chartType = cy.get("div[class='saved-search-chart-config'] canvas[id='pie-chart']") + break + case "Doughnut": + chartType = cy.get("div[class='saved-search-chart-config'] canvas[id='doughnut-chart']") + break + case "Count": + chartType = cy.get("div[class='saved-search-chart-config'] div.count-chart-preview") + break + case "List": + chartType = cy.get("div[class='saved-search-chart-config'] div.list-chart-preview") + break + default: + //"bar (horizontal)" + chartType = cy.get("div[class='saved-search-chart-config'] canvas[id='horizontalbar-chart']") + break + } + chartType.click() + } + selectGeneralTabChartConfig(){ + cy.get("div[class='saved-search-chart-config'] a[id='general-tab']").click() + } + selectSourceTabChartConfig(){ + cy.get("div[class='saved-search-chart-config'] a[id='data-tab']").click() + } + selectDisplayTabChartConfig(){ + cy.get("div[class='saved-search-chart-config'] a[id='display-tab']").click() + } } export default new savedSearchAction(); \ No newline at end of file diff --git a/selectors/savedSearchV1.js b/selectors/savedSearchV1.js index 07ad9067..b7da1f08 100644 --- a/selectors/savedSearchV1.js +++ b/selectors/savedSearchV1.js @@ -5,4 +5,5 @@ export default { scheduleReportBtn: 'a[title="Scheduled Reports"]', notificationBtn: 'button[title="Enable Notifications"]', configurationBtn: 'a[title="Configure Saved Search"]', + columnTab: "[id='nav-columns-tab']", } \ No newline at end of file From a4e3c7d14e38030f3201833b31d0d9ca62f26656 Mon Sep 17 00:00:00 2001 From: Carlos Camacho Date: Thu, 4 Jun 2026 01:00:58 -0400 Subject: [PATCH 3/7] Add methods --- model/savedSearchModel.js | 9 +++++++++ pages/savedSearchAPI.js | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/model/savedSearchModel.js b/model/savedSearchModel.js index 06a450c7..2a780b0a 100644 --- a/model/savedSearchModel.js +++ b/model/savedSearchModel.js @@ -23,6 +23,10 @@ class savedSearchModel { savedSearchAction.searchSavedSearch(criteria) } + pressNotificationButton(){ + savedSearchAction.pressNotificationButton() + } + pressConfigurationButton(){ savedSearchAction.pressConfigurationButton() } @@ -34,6 +38,11 @@ class savedSearchModel { pressTab(name){ savedSearchAction.pressTab(name); } + + createChartAPI(savedSearchID, payload){ + savedSearchAPI.createChartAPI(savedSearchID, payload) + } + } export default new savedSearchModel(); diff --git a/pages/savedSearchAPI.js b/pages/savedSearchAPI.js index ff13f48d..afae3b97 100644 --- a/pages/savedSearchAPI.js +++ b/pages/savedSearchAPI.js @@ -21,6 +21,17 @@ class SavedSearchAPI { }); }); } + + createChartAPI(savedSearchID, payload){ + return cy.window().then(win => { + return win.ProcessMaker.apiClient.post( + `/saved-searches/${savedSearchID}/charts`, + payload + ).then(response => { + return response.data + }); + }); + } } export default new SavedSearchAPI(); \ No newline at end of file From 33ba2ca0288686471c9df6621d04f0ee4e8a1471 Mon Sep 17 00:00:00 2001 From: Carlos Camacho Date: Fri, 5 Jun 2026 02:13:57 -0400 Subject: [PATCH 4/7] add methods --- model/savedSearchModel.js | 12 ++++++++++++ pages/savedSearchAPI.js | 11 +++++++++++ pages/savedSearchV1.js | 23 +++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/model/savedSearchModel.js b/model/savedSearchModel.js index 2a780b0a..229f972e 100644 --- a/model/savedSearchModel.js +++ b/model/savedSearchModel.js @@ -42,6 +42,18 @@ class savedSearchModel { createChartAPI(savedSearchID, payload){ savedSearchAPI.createChartAPI(savedSearchID, payload) } + + updateSavedSearch(savedSearchID, payload){ + savedSearchAPI.updateSavedSearchAPI(savedSearchID, payload) + } + + sendReportSavedSearch(payload){ + savedSearchAction.pressSendReportButton() + savedSearchAction.fillSendTo(payload.sendTo.type, payload.sendTo.selector, payload.sendTo.value) + savedSearchAction.fillEmailSubject(payload.subject.type, payload.subject.selector, payload.subject.value) + savedSearchAction.fillBody(payload.body.type, payload.body.selector, payload.body.value) + savedSearchAction.pressSaveReport(payload.sendReport.type, payload.sendReport.selector) + } } diff --git a/pages/savedSearchAPI.js b/pages/savedSearchAPI.js index afae3b97..82b94bc8 100644 --- a/pages/savedSearchAPI.js +++ b/pages/savedSearchAPI.js @@ -32,6 +32,17 @@ class SavedSearchAPI { }); }); } + + updateSavedSearchAPI(savedSearchID, payload){ + return cy.window().then(win => { + return win.ProcessMaker.apiClient.put( + `/saved-searches/${savedSearchID}`, + payload + ).then(response => { + return response.data + }); + }); + } } export default new SavedSearchAPI(); \ No newline at end of file diff --git a/pages/savedSearchV1.js b/pages/savedSearchV1.js index a3f0a7f5..49ad1aac 100644 --- a/pages/savedSearchV1.js +++ b/pages/savedSearchV1.js @@ -111,6 +111,29 @@ class savedSearchAction { selectDisplayTabChartConfig(){ cy.get("div[class='saved-search-chart-config'] a[id='display-tab']").click() } + + fillSendTo(t, element, text){ + if(t == "css"){ + cy.get(element).type(text) + }else{ + cy.xpath(element).type(text) + } + } + + fillEmailSubject(t, element, text){ + const selector = (t == "css") ? cy.get(element) : cy.xpath(element); + selector.type(text); + } + + fillBody(t, element, text){ + const selector = (t == "css") ? cy.get(element) : cy.xpath(element); + selector.type(text); + } + + pressSaveReport(t, element){ + const selector = (t=="css") ? cy.get(element): cy.xpath(element) + selector.click() + } } export default new savedSearchAction(); \ No newline at end of file From 09f028033ae61e682f07a7a8f03bbfb4148a00c3 Mon Sep 17 00:00:00 2001 From: Carlos Camacho Date: Sun, 7 Jun 2026 02:06:51 -0400 Subject: [PATCH 5/7] update methods --- model/endTestEmailModel.js | 8 ++++++++ model/savedSearchModel.js | 6 +++--- pages/endTestEmail.js | 33 +++++++++++++++++++++++++++++++++ pages/savedSearchAPI.js | 3 ++- 4 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 model/endTestEmailModel.js create mode 100644 pages/endTestEmail.js diff --git a/model/endTestEmailModel.js b/model/endTestEmailModel.js new file mode 100644 index 00000000..5fa083d7 --- /dev/null +++ b/model/endTestEmailModel.js @@ -0,0 +1,8 @@ +import EndTestEmail from "@pages/EndTestEmail" + +class EndTestEmailModel { + verifyFirstEndTestEmail(email){ + EndTestEmail.verifyFirstEndTestEmail(email) + } +} +export default new EndTestEmailModel(); \ No newline at end of file diff --git a/model/savedSearchModel.js b/model/savedSearchModel.js index 229f972e..a4b76fc2 100644 --- a/model/savedSearchModel.js +++ b/model/savedSearchModel.js @@ -7,8 +7,8 @@ class savedSearchModel { return savedSearchAPI.createSavedSearchAPI(data) } - deleteSavedSearchByIdAPI(id) { - return savedSearchAPI.deleteSavedSearchByIdAPI(id) + deleteSavedSearchByIdAPI(id, time=2000) { + return savedSearchAPI.deleteSavedSearchByIdAPI(id, time) } openSavedSearchByObject(objSS, type) { @@ -51,7 +51,7 @@ class savedSearchModel { savedSearchAction.pressSendReportButton() savedSearchAction.fillSendTo(payload.sendTo.type, payload.sendTo.selector, payload.sendTo.value) savedSearchAction.fillEmailSubject(payload.subject.type, payload.subject.selector, payload.subject.value) - savedSearchAction.fillBody(payload.body.type, payload.body.selector, payload.body.value) + savedSearchAction.fillBody(payload.body.type, payload.body.selector, payload.body.value) savedSearchAction.pressSaveReport(payload.sendReport.type, payload.sendReport.selector) } diff --git a/pages/endTestEmail.js b/pages/endTestEmail.js new file mode 100644 index 00000000..0e845bb2 --- /dev/null +++ b/pages/endTestEmail.js @@ -0,0 +1,33 @@ +class EndTestEmail { + returnEndTestMainElement(){ + return cy.get("div.email_list div.email_item") + } + + returnSubjectEndTest(){ + return cy.get("div.email_subject") + } + + returnFromEndTest(){ + return cy.get("div.email_from") + } + + verifyFirstEndTestEmail(emailData){ + cy.session("Verify email endTest", () => { + cy.visit("https://app.endtest.io/") + cy.wait(emailData.time) + cy.visit(`https://app.endtest.io/mailbox?email=${emailData.email}`) + this.returnEndTestMainElement().should("be.visible").first().then(($el)=>{ + cy.wrap($el).within(() => { + this.returnSubjectEndTest().then(($subject)=>{ + expect(emailData.subject).to.include($subject.text().trim()) + }) + this.returnFromEndTest().then(($email)=>{ + expect($email.text().trim()).to.be.eq(emailData.from) + }) + }) + }) + }); + } +} + +export default new EndTestEmail(); \ No newline at end of file diff --git a/pages/savedSearchAPI.js b/pages/savedSearchAPI.js index 82b94bc8..c10371a4 100644 --- a/pages/savedSearchAPI.js +++ b/pages/savedSearchAPI.js @@ -12,7 +12,8 @@ class SavedSearchAPI { }); } - deleteSavedSearchByIdAPI(savedSearchID) { + deleteSavedSearchByIdAPI(savedSearchID, staticTime=2000) { + cy.wait(staticTime) return cy.window().then(win => { return win.ProcessMaker.apiClient.delete( '/saved-searches/'+savedSearchID From acf8cb8516123c5d84cb49fc4a2e00582b4cc56d Mon Sep 17 00:00:00 2001 From: Carlos Camacho Date: Mon, 8 Jun 2026 01:56:52 -0400 Subject: [PATCH 6/7] add methods --- verify/qaShould.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/verify/qaShould.js b/verify/qaShould.js index 403ecbc2..48aa1506 100644 --- a/verify/qaShould.js +++ b/verify/qaShould.js @@ -13,6 +13,10 @@ class qaShould { elementIsVisible(type, element) { this.returnElement(type, element).should("be.visible") } + + elementHaveLenght(type, element, value){ + this.returnElement(type, element).should('have.length',value); + } elementHaveText(type, element, value) { this.returnElement(type, element).invoke('text').should("have.text", value); From 4fc2611228e60be190486294c13266bc2b53374a Mon Sep 17 00:00:00 2001 From: Carlos Camacho Date: Mon, 8 Jun 2026 23:35:52 -0400 Subject: [PATCH 7/7] add delete saved search method --- model/endTestEmailModel.js | 6 +++++- pages/endTestEmail.js | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/model/endTestEmailModel.js b/model/endTestEmailModel.js index 5fa083d7..16cfd900 100644 --- a/model/endTestEmailModel.js +++ b/model/endTestEmailModel.js @@ -3,6 +3,10 @@ import EndTestEmail from "@pages/EndTestEmail" class EndTestEmailModel { verifyFirstEndTestEmail(email){ EndTestEmail.verifyFirstEndTestEmail(email) - } + } + + deleteEmailEndTest(email){ + EndTestEmail.deleteEmails(email) + } } export default new EndTestEmailModel(); \ No newline at end of file diff --git a/pages/endTestEmail.js b/pages/endTestEmail.js index 0e845bb2..068ae932 100644 --- a/pages/endTestEmail.js +++ b/pages/endTestEmail.js @@ -11,6 +11,11 @@ class EndTestEmail { return cy.get("div.email_from") } + deleteEmails(email){ + cy.session("delete email endTest", () => { + cy.visit(`https://app.endtest.io/mailbox?email=${email}&action=delete`) + }) + } verifyFirstEndTestEmail(emailData){ cy.session("Verify email endTest", () => { cy.visit("https://app.endtest.io/")