From 24be8c8080348f1baa8b3d697f0a8b8f3526d107 Mon Sep 17 00:00:00 2001 From: can-angun Date: Thu, 6 Nov 2025 20:57:28 +0000 Subject: [PATCH 01/12] Added soft assert --- .../cypress/e2e/onboarding/onboarding.cy.js | 4 + ui-tests/cypress/support/commands.js | 146 ++++++++++++------ 2 files changed, 105 insertions(+), 45 deletions(-) diff --git a/ui-tests/cypress/e2e/onboarding/onboarding.cy.js b/ui-tests/cypress/e2e/onboarding/onboarding.cy.js index 6b5bc896314..e21f535af90 100644 --- a/ui-tests/cypress/e2e/onboarding/onboarding.cy.js +++ b/ui-tests/cypress/e2e/onboarding/onboarding.cy.js @@ -155,6 +155,7 @@ describe('Complete Onboarding', () => { }); navigationHelpers.isNavigatedToDashboard(); + cy.initSoftAssert(); quickstartPopoeverHelpers.verifyDefaultPageElements(); quickstartPopoeverHelpers.closeQuickStartPopover(); homePageHelpers.verifyEmptyPageElements(); @@ -268,6 +269,7 @@ describe('Complete Onboarding', () => { dbCountlyOutPageHelpers.verifyEmptyPageElements(); dbCountlyOutPageHelpers.clickCountlyFileSystemDatabaseTab(); dbCountlyFsPageHelpers.verifyEmptyPageElements(); + cy.assertAll(); }); it('should be complete onboarding flow with creating own desktop type application with default app key and enable tracking and subscribe to newsletter', function() { @@ -323,6 +325,7 @@ describe('Complete Onboarding', () => { }); navigationHelpers.isNavigatedToDashboard(); + cy.initSoftAssert(); quickstartPopoeverHelpers.closeQuickStartPopover(); homePageHelpers.verifyFullDataPageElements(); navigationHelpers.goToAnalyticsUsersOverview(); @@ -445,5 +448,6 @@ describe('Complete Onboarding', () => { dbCountlyOutPageHelpers.clickCountlyFileSystemDatabaseTab(); //dbCountlyFsPageHelpers.verifyFullDataPageElements(); //TODO: Data is not being generated with the populator. Need to generate the data dbCountlyFsPageHelpers.verifyEmptyPageElements(); + cy.assertAll(); }); }); diff --git a/ui-tests/cypress/support/commands.js b/ui-tests/cypress/support/commands.js index a6491fb4744..3a6432d4fb6 100644 --- a/ui-tests/cypress/support/commands.js +++ b/ui-tests/cypress/support/commands.js @@ -76,7 +76,7 @@ Cypress.Commands.add("selectCheckboxOption", (element, ...options) => { }); Cypress.Commands.add("clickOption", (element, option) => { - cy.getElement(element).contains(new RegExp("^" + option + "$", "g")).click({force: true}); + cy.getElement(element).contains(new RegExp("^" + option + "$", "g")).click({ force: true }); }); Cypress.Commands.add("selectValue", (element, valueText) => { @@ -255,106 +255,162 @@ Cypress.Commands.add("scrollDataTableToLeft", (element = '.el-table__body-wrappe cy.get(element).eq(index).scrollTo('left', { ensureScrollable: false }); }); -Cypress.Commands.add('verifyElement', ({ - labelElement, - labelText, - tooltipElement, - tooltipText, - element, - isElementVisible = true, - elementText, - elementPlaceHolder, - hrefContainUrl, - value, - isChecked, - isDisabled, - unVisibleElement, - selectedIconColor, - selectedMainColor, - selectedFontColor, - attr, - attrText, - shouldNot = false -}) => { +Cypress.Commands.add('verifyElement', (options) => { + const { + labelElement, + labelText, + tooltipElement, + tooltipText, + element, + isElementVisible = true, + elementText, + elementPlaceHolder, + hrefContainUrl, + value, + isChecked, + isDisabled, + unVisibleElement, + selectedIconColor, + selectedMainColor, + selectedFontColor, + attr, + attrText, + shouldNot = false + } = options; + + const safeCheck = (fn, description) => { + try { + fn(); + } + catch (err) { + cy.softFail(`${description} - ${err.message}`); + } + }; if (!shouldNot) { - if (labelElement != null && isElementVisible === true) { - cy.shouldBeVisible(labelElement); + safeCheck(() => cy.shouldBeVisible(labelElement), `Element not visible: ${labelElement}`); } if (labelText != null) { - cy.shouldContainText(labelElement, labelText); + safeCheck(() => cy.shouldContainText(labelElement, labelText), `Label text mismatch: ${labelElement}`); } if (tooltipElement != null) { - cy.shouldBeVisible(tooltipElement); + safeCheck(() => cy.shouldBeVisible(tooltipElement), `Tooltip not visible: ${tooltipElement}`); } if (tooltipText != null) { - cy.shouldTooltipContainText(tooltipElement, tooltipText); + safeCheck(() => cy.shouldTooltipContainText(tooltipElement, tooltipText), `Tooltip text mismatch`); } if (element != null && isElementVisible === true) { - cy.shouldBeVisible(element); + safeCheck(() => cy.shouldBeVisible(element), `Element not visible: ${element}`); } if (elementText != null) { - cy.shouldContainText(element, elementText); + safeCheck(() => cy.shouldContainText(element, elementText), `Element text mismatch: ${element}`); } if (elementPlaceHolder != null) { - cy.shouldPlaceholderContainText(element, elementPlaceHolder); + safeCheck(() => cy.shouldPlaceholderContainText(element, elementPlaceHolder), `Placeholder mismatch: ${element}`); } if (hrefContainUrl != null) { - cy.shouldHrefContainUrl(element, hrefContainUrl); + safeCheck(() => cy.shouldHrefContainUrl(element, hrefContainUrl), `Href mismatch: ${element}`); } if (value != null) { - cy.shouldHaveValue(element, value); + safeCheck(() => cy.shouldHaveValue(element, value), `Value mismatch: ${element}`); } if (isChecked != null) { - isChecked ? cy.shouldBeVisible(`[data-test-id="${element}"]` + '.is-checked') : cy.shouldNotExist(`[data-test-id="${element}"]` + '.is-checked'); + safeCheck(() => { + isChecked + ? cy.shouldBeVisible(`[data-test-id="${element}"].is-checked`) + : cy.shouldNotExist(`[data-test-id="${element}"].is-checked`); + }, `Checkbox state mismatch: ${element}`); } if (isDisabled != null) { - isDisabled ? cy.shouldBeDisabled(element) : cy.shouldNotBeDisabled(element); + safeCheck(() => { + isDisabled ? cy.shouldBeDisabled(element) : cy.shouldNotBeDisabled(element); + }, `Disabled state mismatch: ${element}`); } if (selectedIconColor != null) { - var selector; - unVisibleElement != null ? selector = unVisibleElement : selector = element; - cy.getElement(`[data-test-id="${selector}"]`).invoke("attr", "style").should("contain", helper.hexToRgb(selectedIconColor)); + safeCheck(() => { + const selector = unVisibleElement ?? element; + cy.getElement(`[data-test-id="${selector}"]`) + .invoke('attr', 'style') + .should('contain', helper.hexToRgb(selectedIconColor)); + }, `Icon color mismatch: ${element}`); } if (selectedFontColor != null) { - cy.getElement(`[data-test-id="${element}"]`).invoke("attr", "style").should("contain", helper.hexToRgb(selectedFontColor)); + safeCheck(() => { + cy.getElement(`[data-test-id="${element}"]`) + .invoke('attr', 'style') + .should('contain', helper.hexToRgb(selectedFontColor)); + }, `Font color mismatch: ${element}`); } if (selectedMainColor != null) { - cy.getElement(`[data-test-id="${element}"]`).invoke("attr", "style").should("contain", helper.hexToRgb(selectedMainColor)); + safeCheck(() => { + cy.getElement(`[data-test-id="${element}"]`) + .invoke('attr', 'style') + .should('contain', helper.hexToRgb(selectedMainColor)); + }, `Main color mismatch: ${element}`); } if (attr != null && attrText != null) { - cy.getElement(`[data-test-id="${element}"]`).invoke("attr", attr).should("contain", attrText); + safeCheck(() => { + cy.getElement(`[data-test-id="${element}"]`) + .invoke('attr', attr) + .should('contain', attrText); + }, `Attribute mismatch: ${element}`); } } else { - if (element != null && isElementVisible === true) { - cy.shouldBeVisible(element); - cy.shouldNotBeEqual(element, elementText); + safeCheck(() => { + cy.shouldBeVisible(element); + cy.shouldNotBeEqual(element, elementText); + }, `Negative assertion failed: ${element}`); } if (labelElement != null && isElementVisible === true) { - cy.shouldBeVisible(labelElement); - cy.shouldNotBeEqual(labelElement, labelText); + safeCheck(() => { + cy.shouldBeVisible(labelElement); + cy.shouldNotBeEqual(labelElement, labelText); + }, `Negative assertion failed: ${labelElement}`); } } }); +// Soft assert setup +// initializes the error list at the beginning of a new test. +Cypress.Commands.add('initSoftAssert', () => { + cy.wrap([]).as('softErrors'); +}); + +// collects soft assertion failures without failing the test immediately. +Cypress.Commands.add('softFail', (message) => { + cy.get('@softErrors').then((errors) => { + errors.push(message); + cy.wrap(errors).as('softErrors'); + }); +}); + +// checks if there were any soft assertion failures and fails the test if there were. +Cypress.Commands.add('assertAll', () => { + cy.get('@softErrors').then((errors) => { + if (errors.length > 0) { + throw new Error(`Soft assertion errors:\n${errors.join('\n')}`); + } + }); +}); + Cypress.Commands.add('dropMongoDatabase', () => { cy.exec("mongosh mongodb/countly --eval 'db.dropDatabase()'"); }); From 8a7bc80dc477c9ec532a942c7ed400966e45806d Mon Sep 17 00:00:00 2001 From: can-angun Date: Thu, 6 Nov 2025 21:32:14 +0000 Subject: [PATCH 02/12] Updated verify element metod --- ui-tests/cypress/support/commands.js | 82 ++++++++++++++++------------ 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/ui-tests/cypress/support/commands.js b/ui-tests/cypress/support/commands.js index 3a6432d4fb6..ebcd81f4e98 100644 --- a/ui-tests/cypress/support/commands.js +++ b/ui-tests/cypress/support/commands.js @@ -281,23 +281,22 @@ Cypress.Commands.add('verifyElement', (options) => { const safeCheck = (fn, description) => { try { fn(); - } - catch (err) { + } catch (err) { cy.softFail(`${description} - ${err.message}`); } }; if (!shouldNot) { if (labelElement != null && isElementVisible === true) { - safeCheck(() => cy.shouldBeVisible(labelElement), `Element not visible: ${labelElement}`); + safeCheck(() => cy.getElement(labelElement, null, { soft: true }).should("be.visible"), `Element not visible: ${labelElement}`); } if (labelText != null) { - safeCheck(() => cy.shouldContainText(labelElement, labelText), `Label text mismatch: ${labelElement}`); + safeCheck(() => cy.getElement(labelElement, null, { soft: true }).should("contain", labelText), `Label text mismatch: ${labelElement}`); } if (tooltipElement != null) { - safeCheck(() => cy.shouldBeVisible(tooltipElement), `Tooltip not visible: ${tooltipElement}`); + safeCheck(() => cy.getElement(tooltipElement, null, { soft: true }).should("be.visible"), `Tooltip not visible: ${tooltipElement}`); } if (tooltipText != null) { @@ -305,43 +304,45 @@ Cypress.Commands.add('verifyElement', (options) => { } if (element != null && isElementVisible === true) { - safeCheck(() => cy.shouldBeVisible(element), `Element not visible: ${element}`); + safeCheck(() => cy.getElement(element, null, { soft: true }).should("be.visible"), `Element not visible: ${element}`); } if (elementText != null) { - safeCheck(() => cy.shouldContainText(element, elementText), `Element text mismatch: ${element}`); + safeCheck(() => cy.getElement(element, null, { soft: true }).should("contain", elementText), `Element text mismatch: ${element}`); } if (elementPlaceHolder != null) { - safeCheck(() => cy.shouldPlaceholderContainText(element, elementPlaceHolder), `Placeholder mismatch: ${element}`); + safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "placeholder").should("contain", elementPlaceHolder), `Placeholder mismatch: ${element}`); } if (hrefContainUrl != null) { - safeCheck(() => cy.shouldHrefContainUrl(element, hrefContainUrl), `Href mismatch: ${element}`); + safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "href").should("contain", hrefContainUrl), `Href mismatch: ${element}`); } if (value != null) { - safeCheck(() => cy.shouldHaveValue(element, value), `Value mismatch: ${element}`); + safeCheck(() => cy.getElement(element, null, { soft: true }).should("have.value", value), `Value mismatch: ${element}`); } if (isChecked != null) { safeCheck(() => { isChecked - ? cy.shouldBeVisible(`[data-test-id="${element}"].is-checked`) - : cy.shouldNotExist(`[data-test-id="${element}"].is-checked`); + ? cy.getElement(`${element}.is-checked`, null, { soft: true }).should('exist') + : cy.getElement(`${element}.is-checked`, null, { soft: true }).should('not.exist'); }, `Checkbox state mismatch: ${element}`); } if (isDisabled != null) { safeCheck(() => { - isDisabled ? cy.shouldBeDisabled(element) : cy.shouldNotBeDisabled(element); + isDisabled + ? cy.getElement(element, null, { soft: true }).should("be.disabled") + : cy.getElement(element, null, { soft: true }).should("not.be.disabled"); }, `Disabled state mismatch: ${element}`); } if (selectedIconColor != null) { safeCheck(() => { const selector = unVisibleElement ?? element; - cy.getElement(`[data-test-id="${selector}"]`) + cy.getElement(selector, null, { soft: true }) .invoke('attr', 'style') .should('contain', helper.hexToRgb(selectedIconColor)); }, `Icon color mismatch: ${element}`); @@ -349,7 +350,7 @@ Cypress.Commands.add('verifyElement', (options) => { if (selectedFontColor != null) { safeCheck(() => { - cy.getElement(`[data-test-id="${element}"]`) + cy.getElement(element, null, { soft: true }) .invoke('attr', 'style') .should('contain', helper.hexToRgb(selectedFontColor)); }, `Font color mismatch: ${element}`); @@ -357,7 +358,7 @@ Cypress.Commands.add('verifyElement', (options) => { if (selectedMainColor != null) { safeCheck(() => { - cy.getElement(`[data-test-id="${element}"]`) + cy.getElement(element, null, { soft: true }) .invoke('attr', 'style') .should('contain', helper.hexToRgb(selectedMainColor)); }, `Main color mismatch: ${element}`); @@ -365,24 +366,23 @@ Cypress.Commands.add('verifyElement', (options) => { if (attr != null && attrText != null) { safeCheck(() => { - cy.getElement(`[data-test-id="${element}"]`) + cy.getElement(element, null, { soft: true }) .invoke('attr', attr) .should('contain', attrText); }, `Attribute mismatch: ${element}`); } - } - else { + } else { if (element != null && isElementVisible === true) { safeCheck(() => { - cy.shouldBeVisible(element); - cy.shouldNotBeEqual(element, elementText); + cy.getElement(element, null, { soft: true }).should("be.visible"); + cy.getElement(element, null, { soft: true }).should("not.contain", elementText); }, `Negative assertion failed: ${element}`); } if (labelElement != null && isElementVisible === true) { safeCheck(() => { - cy.shouldBeVisible(labelElement); - cy.shouldNotBeEqual(labelElement, labelText); + cy.getElement(labelElement, null, { soft: true }).should("be.visible"); + cy.getElement(labelElement, null, { soft: true }).should("not.contain", labelText); }, `Negative assertion failed: ${labelElement}`); } } @@ -415,23 +415,33 @@ Cypress.Commands.add('dropMongoDatabase', () => { cy.exec("mongosh mongodb/countly --eval 'db.dropDatabase()'"); }); -Cypress.Commands.add('getElement', (selector, parent = null) => { +Cypress.Commands.overwrite('getElement', (originalFn, selector, parent = null, options = {}) => { + const { soft = false, timeout = 5000 } = options; + let finalSelector; if (!selector.includes('[data-test-id=')) { if (selector[0].includes('.') || selector[0].includes('#')) { - return cy.get(selector); - } - else { - if (parent !== null) { - selector = `${parent} [data-test-id="${selector}"]`; - } - else { - selector = `[data-test-id="${selector}"]`; - } - return cy.get(selector); + finalSelector = selector; + } else { + finalSelector = parent + ? `${parent} [data-test-id="${selector}"]` + : `[data-test-id="${selector}"]`; } + } else { + finalSelector = selector; } - else { - return cy.get(selector); + + if (soft) { + return cy.get('body', { timeout }).then(($body) => { + const found = $body.find(finalSelector); + if (found.length > 0) { + return cy.wrap(found); + } else { + cy.softFail(`❌ Element not found: ${finalSelector}`); + return cy.wrap(Cypress.$([])); + } + }); + } else { + return originalFn(finalSelector, { timeout }); } }); \ No newline at end of file From 3bad855273ba726b680c66521c3fa7a13f935a2c Mon Sep 17 00:00:00 2001 From: can-angun Date: Thu, 6 Nov 2025 21:42:30 +0000 Subject: [PATCH 03/12] Fixed getElement metod parameter --- ui-tests/cypress/support/commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-tests/cypress/support/commands.js b/ui-tests/cypress/support/commands.js index ebcd81f4e98..183f4683a4f 100644 --- a/ui-tests/cypress/support/commands.js +++ b/ui-tests/cypress/support/commands.js @@ -415,7 +415,7 @@ Cypress.Commands.add('dropMongoDatabase', () => { cy.exec("mongosh mongodb/countly --eval 'db.dropDatabase()'"); }); -Cypress.Commands.overwrite('getElement', (originalFn, selector, parent = null, options = {}) => { +Cypress.Commands.add(('getElement', (originalFn, selector, parent = null, options = {}) => { const { soft = false, timeout = 5000 } = options; let finalSelector; From f7dd6df2e7515acfa434acb93ba051ac2607641b Mon Sep 17 00:00:00 2001 From: can-angun Date: Thu, 6 Nov 2025 21:43:35 +0000 Subject: [PATCH 04/12] Fixed linter fails --- ui-tests/cypress/support/commands.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ui-tests/cypress/support/commands.js b/ui-tests/cypress/support/commands.js index 183f4683a4f..47cb390cf1e 100644 --- a/ui-tests/cypress/support/commands.js +++ b/ui-tests/cypress/support/commands.js @@ -281,7 +281,8 @@ Cypress.Commands.add('verifyElement', (options) => { const safeCheck = (fn, description) => { try { fn(); - } catch (err) { + } + catch (err) { cy.softFail(`${description} - ${err.message}`); } }; @@ -371,7 +372,8 @@ Cypress.Commands.add('verifyElement', (options) => { .should('contain', attrText); }, `Attribute mismatch: ${element}`); } - } else { + } + else { if (element != null && isElementVisible === true) { safeCheck(() => { cy.getElement(element, null, { soft: true }).should("be.visible"); @@ -422,12 +424,14 @@ Cypress.Commands.add(('getElement', (originalFn, selector, parent = null, option if (!selector.includes('[data-test-id=')) { if (selector[0].includes('.') || selector[0].includes('#')) { finalSelector = selector; - } else { + } + else { finalSelector = parent ? `${parent} [data-test-id="${selector}"]` : `[data-test-id="${selector}"]`; } - } else { + } + else { finalSelector = selector; } @@ -436,12 +440,14 @@ Cypress.Commands.add(('getElement', (originalFn, selector, parent = null, option const found = $body.find(finalSelector); if (found.length > 0) { return cy.wrap(found); - } else { + } + else { cy.softFail(`❌ Element not found: ${finalSelector}`); return cy.wrap(Cypress.$([])); } }); - } else { + } + else { return originalFn(finalSelector, { timeout }); } -}); \ No newline at end of file +})); \ No newline at end of file From b1e83d667f627755b7f82dbdb1c470276487b8ae Mon Sep 17 00:00:00 2001 From: can-angun Date: Thu, 6 Nov 2025 21:56:02 +0000 Subject: [PATCH 05/12] Fixed fail on the getElement method --- ui-tests/cypress/support/commands.js | 58 ++++++++++++++-------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/ui-tests/cypress/support/commands.js b/ui-tests/cypress/support/commands.js index 47cb390cf1e..6cb04f42de1 100644 --- a/ui-tests/cypress/support/commands.js +++ b/ui-tests/cypress/support/commands.js @@ -418,36 +418,38 @@ Cypress.Commands.add('dropMongoDatabase', () => { }); Cypress.Commands.add(('getElement', (originalFn, selector, parent = null, options = {}) => { - const { soft = false, timeout = 5000 } = options; - let finalSelector; + Cypress.Commands.add('getElement', (selector, parent = null, options = {}) => { + const { soft = false, timeout = 5000 } = options; + let finalSelector; - if (!selector.includes('[data-test-id=')) { - if (selector[0].includes('.') || selector[0].includes('#')) { - finalSelector = selector; + if (!selector.includes('[data-test-id=')) { + if (selector.startsWith('.') || selector.startsWith('#')) { + finalSelector = selector; + } + else { + finalSelector = parent + ? `${parent} [data-test-id="${selector}"]` + : `[data-test-id="${selector}"]`; + } } else { - finalSelector = parent - ? `${parent} [data-test-id="${selector}"]` - : `[data-test-id="${selector}"]`; + finalSelector = selector; } - } - else { - finalSelector = selector; - } - if (soft) { - return cy.get('body', { timeout }).then(($body) => { - const found = $body.find(finalSelector); - if (found.length > 0) { - return cy.wrap(found); - } - else { - cy.softFail(`❌ Element not found: ${finalSelector}`); - return cy.wrap(Cypress.$([])); - } - }); - } - else { - return originalFn(finalSelector, { timeout }); - } -})); \ No newline at end of file + if (soft) { + return cy.get('body', { timeout }).then(($body) => { + const found = $body.find(finalSelector); + if (found.length > 0) { + return cy.wrap(found); + } + else { + cy.softFail(`❌ Element not found: ${finalSelector}`); + return cy.wrap(Cypress.$([])); // Return empty set + } + }); + } + else { + return cy.get(finalSelector, { timeout }); + } + }); +})); From b3dbb81b8bbcef8b309ca86f1e1e116b90e1e16f Mon Sep 17 00:00:00 2001 From: can-angun Date: Thu, 6 Nov 2025 22:03:23 +0000 Subject: [PATCH 06/12] Fixed fail on the getElement method --- ui-tests/cypress/support/commands.js | 62 ++++++++++++++-------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/ui-tests/cypress/support/commands.js b/ui-tests/cypress/support/commands.js index 6cb04f42de1..73abca34842 100644 --- a/ui-tests/cypress/support/commands.js +++ b/ui-tests/cypress/support/commands.js @@ -417,39 +417,37 @@ Cypress.Commands.add('dropMongoDatabase', () => { cy.exec("mongosh mongodb/countly --eval 'db.dropDatabase()'"); }); -Cypress.Commands.add(('getElement', (originalFn, selector, parent = null, options = {}) => { - Cypress.Commands.add('getElement', (selector, parent = null, options = {}) => { - const { soft = false, timeout = 5000 } = options; - let finalSelector; - - if (!selector.includes('[data-test-id=')) { - if (selector.startsWith('.') || selector.startsWith('#')) { - finalSelector = selector; - } - else { - finalSelector = parent - ? `${parent} [data-test-id="${selector}"]` - : `[data-test-id="${selector}"]`; - } - } - else { - finalSelector = selector; - } +Cypress.Commands.add('getElement', (selector, parent = null, options = {}) => { + const { soft = false, timeout = 5000 } = options; + let finalSelector; - if (soft) { - return cy.get('body', { timeout }).then(($body) => { - const found = $body.find(finalSelector); - if (found.length > 0) { - return cy.wrap(found); - } - else { - cy.softFail(`❌ Element not found: ${finalSelector}`); - return cy.wrap(Cypress.$([])); // Return empty set - } - }); + if (!selector.includes('[data-test-id=')) { + if (selector.startsWith('.') || selector.startsWith('#')) { + finalSelector = selector; } else { - return cy.get(finalSelector, { timeout }); + finalSelector = parent + ? `${parent} [data-test-id="${selector}"]` + : `[data-test-id="${selector}"]`; } - }); -})); + } + else { + finalSelector = selector; + } + + if (soft) { + return cy.get('body', { timeout }).then(($body) => { + const found = $body.find(finalSelector); + if (found.length > 0) { + return cy.wrap(found); + } + else { + cy.softFail(`❌ Element not found: ${finalSelector}`); + return cy.wrap(Cypress.$([])); // Return empty set + } + }); + } + else { + return cy.get(finalSelector, { timeout }); + } +}); \ No newline at end of file From 2a4874043ff89c25e8274da4f3c2d30674a03e44 Mon Sep 17 00:00:00 2001 From: can-angun Date: Thu, 6 Nov 2025 22:20:13 +0000 Subject: [PATCH 07/12] Updated page title on the setup page --- ui-tests/cypress/lib/onboarding/initialSetup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-tests/cypress/lib/onboarding/initialSetup.js b/ui-tests/cypress/lib/onboarding/initialSetup.js index 9a45579b27f..2bd24dede9f 100644 --- a/ui-tests/cypress/lib/onboarding/initialSetup.js +++ b/ui-tests/cypress/lib/onboarding/initialSetup.js @@ -60,7 +60,7 @@ const verifyDefaultPageElements = (isDemoApp) => { if (isDemoApp) { cy.verifyElement({ labelElement: initialSetupPageElements.PAGE_TITLE, - labelText: "Let's create a demo app for you!" + labelText: "Let\'s create a demo app for you!" }); cy.verifyElement({ @@ -108,7 +108,7 @@ const verifyDefaultPageElements = (isDemoApp) => { else { cy.verifyElement({ labelElement: initialSetupPageElements.PAGE_TITLE, - labelText: "Let's add your first application" + labelText: "Let\'s add your first application" }); cy.verifyElement({ From b3a5f853362180a39e2a00a7fed032ecabb0a487 Mon Sep 17 00:00:00 2001 From: can-angun Date: Fri, 7 Nov 2025 00:00:27 +0000 Subject: [PATCH 08/12] Fixed text verify --- .../cypress/lib/onboarding/initialSetup.js | 4 +-- ui-tests/cypress/support/commands.js | 32 +++++++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/ui-tests/cypress/lib/onboarding/initialSetup.js b/ui-tests/cypress/lib/onboarding/initialSetup.js index 2bd24dede9f..9a45579b27f 100644 --- a/ui-tests/cypress/lib/onboarding/initialSetup.js +++ b/ui-tests/cypress/lib/onboarding/initialSetup.js @@ -60,7 +60,7 @@ const verifyDefaultPageElements = (isDemoApp) => { if (isDemoApp) { cy.verifyElement({ labelElement: initialSetupPageElements.PAGE_TITLE, - labelText: "Let\'s create a demo app for you!" + labelText: "Let's create a demo app for you!" }); cy.verifyElement({ @@ -108,7 +108,7 @@ const verifyDefaultPageElements = (isDemoApp) => { else { cy.verifyElement({ labelElement: initialSetupPageElements.PAGE_TITLE, - labelText: "Let\'s add your first application" + labelText: "Let's add your first application" }); cy.verifyElement({ diff --git a/ui-tests/cypress/support/commands.js b/ui-tests/cypress/support/commands.js index 73abca34842..a16ed8e20e1 100644 --- a/ui-tests/cypress/support/commands.js +++ b/ui-tests/cypress/support/commands.js @@ -144,11 +144,11 @@ Cypress.Commands.add("shouldNotBeDisabled", (element) => { }); Cypress.Commands.add("shouldContainText", (element, text) => { - cy.getElement(element).should("contain", text); + cy.getElement(element).should("contain.text", text); }); Cypress.Commands.add("shouldNotContainText", (element, text) => { - cy.getElement(element).eq(0).should("not.contain", text); + cy.getElement(element).eq(0).should("not.contain.text", text); }); Cypress.Commands.add("shouldBeEqual", (element, text) => { @@ -162,15 +162,15 @@ Cypress.Commands.add("shouldNotBeEqual", (element, text) => { }); Cypress.Commands.add("shouldPlaceholderContainText", (element, text) => { - cy.getElement(element).invoke("attr", "placeholder").should("contain", text); + cy.getElement(element).invoke("attr", "placeholder").should("contain.text", text); }); Cypress.Commands.add("shouldDataOriginalTitleContainText", (element, text) => { - cy.getElement(element).invoke("attr", "data-original-title").should("contain", text); + cy.getElement(element).invoke("attr", "data-original-title").should("contain.text", text); }); Cypress.Commands.add("shouldHrefContainUrl", (element, url) => { - cy.getElement(element).invoke("attr", "href").should("contain", url); + cy.getElement(element).invoke("attr", "href").should("contain.text", url); }); Cypress.Commands.add("shouldHaveValue", (element, value) => { @@ -293,7 +293,7 @@ Cypress.Commands.add('verifyElement', (options) => { } if (labelText != null) { - safeCheck(() => cy.getElement(labelElement, null, { soft: true }).should("contain", labelText), `Label text mismatch: ${labelElement}`); + safeCheck(() => cy.getElement(labelElement, null, { soft: true }).should("contain.text", labelText), `Label text mismatch: ${labelElement}`); } if (tooltipElement != null) { @@ -309,15 +309,15 @@ Cypress.Commands.add('verifyElement', (options) => { } if (elementText != null) { - safeCheck(() => cy.getElement(element, null, { soft: true }).should("contain", elementText), `Element text mismatch: ${element}`); + safeCheck(() => cy.getElement(element, null, { soft: true }).should("contain.text", elementText), `Element text mismatch: ${element}`); } if (elementPlaceHolder != null) { - safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "placeholder").should("contain", elementPlaceHolder), `Placeholder mismatch: ${element}`); + safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "placeholder").should("contain.text", elementPlaceHolder), `Placeholder mismatch: ${element}`); } if (hrefContainUrl != null) { - safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "href").should("contain", hrefContainUrl), `Href mismatch: ${element}`); + safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "href").should("contain.text", hrefContainUrl), `Href mismatch: ${element}`); } if (value != null) { @@ -398,10 +398,16 @@ Cypress.Commands.add('initSoftAssert', () => { // collects soft assertion failures without failing the test immediately. Cypress.Commands.add('softFail', (message) => { - cy.get('@softErrors').then((errors) => { - errors.push(message); - cy.wrap(errors).as('softErrors'); - }); + // First, check if @softErrors alias exists + cy.state('aliases')?.softErrors + ? cy.get('@softErrors', { log: false }).then((errors) => { + errors.push(message); + cy.wrap(errors, { log: false }).as('softErrors'); + }) + : (() => { + // Soft assert not initialized — fail immediately + throw new Error(message); + })(); }); // checks if there were any soft assertion failures and fails the test if there were. From c59e5cab5f243a9d042e15d22e657fe9b57ad282 Mon Sep 17 00:00:00 2001 From: can-angun Date: Fri, 7 Nov 2025 07:05:04 +0000 Subject: [PATCH 09/12] Fixed placeholder text verfiy --- ui-tests/cypress/support/commands.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui-tests/cypress/support/commands.js b/ui-tests/cypress/support/commands.js index a16ed8e20e1..ab85580086a 100644 --- a/ui-tests/cypress/support/commands.js +++ b/ui-tests/cypress/support/commands.js @@ -162,15 +162,15 @@ Cypress.Commands.add("shouldNotBeEqual", (element, text) => { }); Cypress.Commands.add("shouldPlaceholderContainText", (element, text) => { - cy.getElement(element).invoke("attr", "placeholder").should("contain.text", text); + cy.getElement(element).invoke("attr", "placeholder").should("contain", text); }); Cypress.Commands.add("shouldDataOriginalTitleContainText", (element, text) => { - cy.getElement(element).invoke("attr", "data-original-title").should("contain.text", text); + cy.getElement(element).invoke("attr", "data-original-title").should("contain", text); }); Cypress.Commands.add("shouldHrefContainUrl", (element, url) => { - cy.getElement(element).invoke("attr", "href").should("contain.text", url); + cy.getElement(element).invoke("attr", "href").should("contain", url); }); Cypress.Commands.add("shouldHaveValue", (element, value) => { @@ -313,11 +313,11 @@ Cypress.Commands.add('verifyElement', (options) => { } if (elementPlaceHolder != null) { - safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "placeholder").should("contain.text", elementPlaceHolder), `Placeholder mismatch: ${element}`); + safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "placeholder").should("contain", elementPlaceHolder), `Placeholder mismatch: ${element}`); } if (hrefContainUrl != null) { - safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "href").should("contain.text", hrefContainUrl), `Href mismatch: ${element}`); + safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "href").should("contain", hrefContainUrl), `Href mismatch: ${element}`); } if (value != null) { From 6f7c72b3c3d6bed51a01cf41955ce9b53a49f181 Mon Sep 17 00:00:00 2001 From: can-angun Date: Fri, 7 Nov 2025 10:24:32 +0000 Subject: [PATCH 10/12] Updated command.js --- ui-tests/cypress/support/commands.js | 379 +++++++-------------------- 1 file changed, 102 insertions(+), 277 deletions(-) diff --git a/ui-tests/cypress/support/commands.js b/ui-tests/cypress/support/commands.js index ab85580086a..8598811971d 100644 --- a/ui-tests/cypress/support/commands.js +++ b/ui-tests/cypress/support/commands.js @@ -3,6 +3,88 @@ const helper = require('./helper'); const chai = require('chai'); const expect = chai.expect; +// ───────────────────────────────────────────── +// Soft assert helpers +// ───────────────────────────────────────────── + +// initializes the error list at the beginning of a new test. +Cypress.Commands.add('initSoftAssert', () => { + cy.wrap([]).as('softErrors'); +}); + +// collects soft assertion failures without failing the test immediately. +Cypress.Commands.add('softFail', (message) => { + cy.get('@softErrors', { log: false }).then((errors) => { + errors.push(message); + cy.wrap(errors, { log: false }).as('softErrors'); + }).catch(() => { + // if alias missing → fail immediately + throw new Error(message); + }); +}); + +// safeCheck wraps Cypress assertions to continue even if they fail +Cypress.Commands.add('safeCheck', (fn, description) => { + cy.then(() => { + return fn() + .catch((err) => { + cy.softFail(`${description} - ${err.message}`); + return null; // allow flow to continue + }); + }); +}); + +// checks if there were any soft assertion failures and fails the test if there were. +Cypress.Commands.add('assertAll', () => { + cy.get('@softErrors').then((errors) => { + if (errors.length > 0) { + throw new Error(`Soft assertion errors:\n${errors.join('\n')}`); + } + }); +}); + +// ───────────────────────────────────────────── +// getElement helper (supports { soft: true }) +// ───────────────────────────────────────────── +Cypress.Commands.add('getElement', (selector, parent = null, options = {}) => { + const { soft = false, timeout = 5000 } = options; + let finalSelector; + + if (!selector.includes('[data-test-id=')) { + if (selector.startsWith('.') || selector.startsWith('#')) { + finalSelector = selector; + } + else { + finalSelector = parent + ? `${parent} [data-test-id="${selector}"]` + : `[data-test-id="${selector}"]`; + } + } + else { + finalSelector = selector; + } + + if (soft) { + return cy.get('body', { timeout }).then(($body) => { + const found = $body.find(finalSelector); + if (found.length > 0) { + return cy.wrap(found); + } + else { + cy.softFail(`❌ Element not found: ${finalSelector}`); + return cy.wrap(Cypress.$([])); // Return empty set + } + }); + } + else { + return cy.get(finalSelector, { timeout }); + } +}); + +// ───────────────────────────────────────────── +// Core interaction commands +// ───────────────────────────────────────────── + Cypress.Commands.add("typeInput", (element, tag) => { cy.getElement(element).clear().type(tag); }); @@ -19,7 +101,7 @@ Cypress.Commands.add("clearInput", (element) => { }); Cypress.Commands.add("typeSelectInput", (element, ...tags) => { - for (var i = 0; i < tags.length; i++) { + for (let i = 0; i < tags.length; i++) { cy.getElement(element).type(tags[i] + '{enter}', { force: true }); } cy.clickBody(); @@ -59,18 +141,15 @@ Cypress.Commands.add("selectListBoxItem", (element, item) => { Cypress.Commands.add("selectCheckboxOption", (element, ...options) => { cy.getElement(element).click(); - for (var i = 0; i < options.length; i++) { + for (let i = 0; i < options.length; i++) { cy.clickOption('.el-checkbox__label', options[i]); } - cy - .elementExists(`${element}-select-x-confirm-button`) - .then((isExists) => { - if (isExists) { - cy.clickElement(`${element}-select-x-confirm-button`); - - } - }); + cy.elementExists(`${element}-select-x-confirm-button`).then((isExists) => { + if (isExists) { + cy.clickElement(`${element}-select-x-confirm-button`); + } + }); cy.clickBody(); }); @@ -91,20 +170,15 @@ Cypress.Commands.add("selectValue", (element, valueText) => { Cypress.Commands.add("selectColor", (element, colorCode) => { cy.clickElement(element); - cy.get('.vc-input__input') - .eq(0) + cy.get('.vc-input__input').eq(0) .invoke('val', colorCode) .trigger('input'); - cy.clickElement('.cly-vue-button.button-green-skin'); }); Cypress.Commands.add('dragAndDropFile', (element, filePath) => { cy.getElement(element) - .attachFile(filePath, { - encoding: 'utf-8', - subjectType: 'drag-n-drop' - }); + .attachFile(filePath, { encoding: 'utf-8', subjectType: 'drag-n-drop' }); }); Cypress.Commands.add('uploadFile', (filePath) => { @@ -115,9 +189,7 @@ Cypress.Commands.add("shouldTooltipContainText", (element, text) => { cy.getElement(element) .eq(0).invoke('show') .trigger('mouseenter'); - cy.shouldContainText('.tooltip-inner', text); - cy.getElement(element) .eq(0).invoke('show') .trigger('mouseleave'); @@ -131,14 +203,6 @@ Cypress.Commands.add("shouldBeDisabled", (element) => { cy.getElement(element).should("be.disabled"); }); -Cypress.Commands.add("shouldBeHasDisabledClass", (element) => { - cy.get(`[data-test-id="${element}"].is-disabled`).should("exist"); -}); - -Cypress.Commands.add("shouldNotBeHasDisabledClass", (element) => { - cy.get(`[data-test-id="${element}"].is-disabled`).should("not.exist"); -}); - Cypress.Commands.add("shouldNotBeDisabled", (element) => { cy.getElement(element).should("not.be.disabled"); }); @@ -147,28 +211,10 @@ Cypress.Commands.add("shouldContainText", (element, text) => { cy.getElement(element).should("contain.text", text); }); -Cypress.Commands.add("shouldNotContainText", (element, text) => { - cy.getElement(element).eq(0).should("not.contain.text", text); -}); - -Cypress.Commands.add("shouldBeEqual", (element, text) => { - cy.getElement(element).should("equal", text); -}); - -Cypress.Commands.add("shouldNotBeEqual", (element, text) => { - cy.getElement(element).invoke('text').then((actualText) => { - expect(actualText).not.to.equal(text); - }); -}); - Cypress.Commands.add("shouldPlaceholderContainText", (element, text) => { cy.getElement(element).invoke("attr", "placeholder").should("contain", text); }); -Cypress.Commands.add("shouldDataOriginalTitleContainText", (element, text) => { - cy.getElement(element).invoke("attr", "data-original-title").should("contain", text); -}); - Cypress.Commands.add("shouldHrefContainUrl", (element, url) => { cy.getElement(element).invoke("attr", "href").should("contain", url); }); @@ -182,79 +228,16 @@ Cypress.Commands.add("shouldUrlInclude", (url) => { }); Cypress.Commands.add('elementExists', (selector) => { - cy.wait(500); if (!selector.includes('[data-test-id=') && (!selector[0].includes('.') || !selector[0].includes('#'))) { selector = `[data-test-id="${selector}"]`; } - - cy - .get('body') - .then(($body) => { - return $body.find(selector).length > 0; - }); -}); - -Cypress.Commands.add('shouldBeExist', (element) => { - cy.getElement(element).should('exist'); -}); - -Cypress.Commands.add('shouldNotExist', (element) => { - cy.getElement(element).should('not.exist'); -}); - -Cypress.Commands.add('checkPaceRunning', () => { - cy - .elementExists('.pace-running') - .then((isExists) => { - if (isExists) { - cy.shouldNotExist('.pace-running'); - } - }); -}); - -Cypress.Commands.add('checkPaceActive', () => { - cy - .elementExists('.pace-active') - .then((isExists) => { - if (isExists) { - cy.shouldNotExist('.pace-active'); - } - }); -}); - -Cypress.Commands.add("scrollPageSlightly", (element = '.main-view', index = 0) => { - cy.get(element).eq(index).then(($el) => { - const currentScroll = $el[0].scrollTop; - const newScroll = currentScroll + 550; - - cy.wrap($el).scrollTo(0, newScroll, { - duration: 1000, - ensureScrollable: false, - }); - }); -}); - -Cypress.Commands.add("scrollPageToBottom", (element = '.main-view', index = 0) => { - cy.get(element).eq(index).scrollTo('bottom', { ensureScrollable: false }); -}); - -Cypress.Commands.add("scrollPageToTop", (element = '.main-view', index = 0) => { - cy.get(element).eq(index).scrollTo('top', { ensureScrollable: false }); -}); - -Cypress.Commands.add("scrollPageToCenter", (element = '.main-view', index = 0) => { - cy.get(element).eq(index).scrollTo('center', { ensureScrollable: false }); -}); - -Cypress.Commands.add("scrollDataTableToRight", (element = '.el-table__body-wrapper', index = 0) => { - cy.get(element).eq(index).scrollTo('right', { ensureScrollable: false }); -}); - -Cypress.Commands.add("scrollDataTableToLeft", (element = '.el-table__body-wrapper', index = 0) => { - cy.get(element).eq(index).scrollTo('left', { ensureScrollable: false }); + return cy.get('body').then(($body) => $body.find(selector).length > 0); }); +// ───────────────────────────────────────────── +// verifyElement with safeCheck support +// ───────────────────────────────────────────── Cypress.Commands.add('verifyElement', (options) => { const { labelElement, @@ -269,7 +252,6 @@ Cypress.Commands.add('verifyElement', (options) => { value, isChecked, isDisabled, - unVisibleElement, selectedIconColor, selectedMainColor, selectedFontColor, @@ -278,182 +260,25 @@ Cypress.Commands.add('verifyElement', (options) => { shouldNot = false } = options; - const safeCheck = (fn, description) => { - try { - fn(); - } - catch (err) { - cy.softFail(`${description} - ${err.message}`); - } - }; - if (!shouldNot) { - if (labelElement != null && isElementVisible === true) { - safeCheck(() => cy.getElement(labelElement, null, { soft: true }).should("be.visible"), `Element not visible: ${labelElement}`); + if (labelElement != null && isElementVisible) { + cy.safeCheck(() => cy.getElement(labelElement, null, { soft: true }).should("be.visible"), `Element not visible: ${labelElement}`); } if (labelText != null) { - safeCheck(() => cy.getElement(labelElement, null, { soft: true }).should("contain.text", labelText), `Label text mismatch: ${labelElement}`); - } - - if (tooltipElement != null) { - safeCheck(() => cy.getElement(tooltipElement, null, { soft: true }).should("be.visible"), `Tooltip not visible: ${tooltipElement}`); + cy.safeCheck(() => cy.getElement(labelElement, null, { soft: true }).should("contain.text", labelText), `Label text mismatch: ${labelElement}`); } - if (tooltipText != null) { - safeCheck(() => cy.shouldTooltipContainText(tooltipElement, tooltipText), `Tooltip text mismatch`); - } - - if (element != null && isElementVisible === true) { - safeCheck(() => cy.getElement(element, null, { soft: true }).should("be.visible"), `Element not visible: ${element}`); + if (element != null && isElementVisible) { + cy.safeCheck(() => cy.getElement(element, null, { soft: true }).should("be.visible"), `Element not visible: ${element}`); } if (elementText != null) { - safeCheck(() => cy.getElement(element, null, { soft: true }).should("contain.text", elementText), `Element text mismatch: ${element}`); + cy.safeCheck(() => cy.getElement(element, null, { soft: true }).should("contain.text", elementText), `Element text mismatch: ${element}`); } if (elementPlaceHolder != null) { - safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "placeholder").should("contain", elementPlaceHolder), `Placeholder mismatch: ${element}`); - } - - if (hrefContainUrl != null) { - safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "href").should("contain", hrefContainUrl), `Href mismatch: ${element}`); - } - - if (value != null) { - safeCheck(() => cy.getElement(element, null, { soft: true }).should("have.value", value), `Value mismatch: ${element}`); + cy.safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "placeholder").should("contain", elementPlaceHolder), `Placeholder mismatch: ${element}`); } - - if (isChecked != null) { - safeCheck(() => { - isChecked - ? cy.getElement(`${element}.is-checked`, null, { soft: true }).should('exist') - : cy.getElement(`${element}.is-checked`, null, { soft: true }).should('not.exist'); - }, `Checkbox state mismatch: ${element}`); - } - - if (isDisabled != null) { - safeCheck(() => { - isDisabled - ? cy.getElement(element, null, { soft: true }).should("be.disabled") - : cy.getElement(element, null, { soft: true }).should("not.be.disabled"); - }, `Disabled state mismatch: ${element}`); - } - - if (selectedIconColor != null) { - safeCheck(() => { - const selector = unVisibleElement ?? element; - cy.getElement(selector, null, { soft: true }) - .invoke('attr', 'style') - .should('contain', helper.hexToRgb(selectedIconColor)); - }, `Icon color mismatch: ${element}`); - } - - if (selectedFontColor != null) { - safeCheck(() => { - cy.getElement(element, null, { soft: true }) - .invoke('attr', 'style') - .should('contain', helper.hexToRgb(selectedFontColor)); - }, `Font color mismatch: ${element}`); - } - - if (selectedMainColor != null) { - safeCheck(() => { - cy.getElement(element, null, { soft: true }) - .invoke('attr', 'style') - .should('contain', helper.hexToRgb(selectedMainColor)); - }, `Main color mismatch: ${element}`); - } - - if (attr != null && attrText != null) { - safeCheck(() => { - cy.getElement(element, null, { soft: true }) - .invoke('attr', attr) - .should('contain', attrText); - }, `Attribute mismatch: ${element}`); - } - } - else { - if (element != null && isElementVisible === true) { - safeCheck(() => { - cy.getElement(element, null, { soft: true }).should("be.visible"); - cy.getElement(element, null, { soft: true }).should("not.contain", elementText); - }, `Negative assertion failed: ${element}`); - } - - if (labelElement != null && isElementVisible === true) { - safeCheck(() => { - cy.getElement(labelElement, null, { soft: true }).should("be.visible"); - cy.getElement(labelElement, null, { soft: true }).should("not.contain", labelText); - }, `Negative assertion failed: ${labelElement}`); - } - } -}); - -// Soft assert setup -// initializes the error list at the beginning of a new test. -Cypress.Commands.add('initSoftAssert', () => { - cy.wrap([]).as('softErrors'); -}); - -// collects soft assertion failures without failing the test immediately. -Cypress.Commands.add('softFail', (message) => { - // First, check if @softErrors alias exists - cy.state('aliases')?.softErrors - ? cy.get('@softErrors', { log: false }).then((errors) => { - errors.push(message); - cy.wrap(errors, { log: false }).as('softErrors'); - }) - : (() => { - // Soft assert not initialized — fail immediately - throw new Error(message); - })(); -}); - -// checks if there were any soft assertion failures and fails the test if there were. -Cypress.Commands.add('assertAll', () => { - cy.get('@softErrors').then((errors) => { - if (errors.length > 0) { - throw new Error(`Soft assertion errors:\n${errors.join('\n')}`); - } - }); -}); - -Cypress.Commands.add('dropMongoDatabase', () => { - cy.exec("mongosh mongodb/countly --eval 'db.dropDatabase()'"); -}); - -Cypress.Commands.add('getElement', (selector, parent = null, options = {}) => { - const { soft = false, timeout = 5000 } = options; - let finalSelector; - - if (!selector.includes('[data-test-id=')) { - if (selector.startsWith('.') || selector.startsWith('#')) { - finalSelector = selector; - } - else { - finalSelector = parent - ? `${parent} [data-test-id="${selector}"]` - : `[data-test-id="${selector}"]`; - } - } - else { - finalSelector = selector; - } - - if (soft) { - return cy.get('body', { timeout }).then(($body) => { - const found = $body.find(finalSelector); - if (found.length > 0) { - return cy.wrap(found); - } - else { - cy.softFail(`❌ Element not found: ${finalSelector}`); - return cy.wrap(Cypress.$([])); // Return empty set - } - }); - } - else { - return cy.get(finalSelector, { timeout }); } }); \ No newline at end of file From 128da1c63c84788c391874a635b5c26e25b24ecb Mon Sep 17 00:00:00 2001 From: can-angun Date: Fri, 7 Nov 2025 14:59:50 +0000 Subject: [PATCH 11/12] Updated assert commands --- ui-tests/cypress/support/commands.js | 277 +++++++++++++++++++-------- 1 file changed, 202 insertions(+), 75 deletions(-) diff --git a/ui-tests/cypress/support/commands.js b/ui-tests/cypress/support/commands.js index 8598811971d..b6e73de8dfe 100644 --- a/ui-tests/cypress/support/commands.js +++ b/ui-tests/cypress/support/commands.js @@ -4,15 +4,15 @@ const chai = require('chai'); const expect = chai.expect; // ───────────────────────────────────────────── -// Soft assert helpers +// Soft Assertion Commands // ───────────────────────────────────────────── -// initializes the error list at the beginning of a new test. +// Initializes the soft assertion error list Cypress.Commands.add('initSoftAssert', () => { cy.wrap([]).as('softErrors'); }); -// collects soft assertion failures without failing the test immediately. +// Collects soft assertion failures without stopping the test Cypress.Commands.add('softFail', (message) => { cy.get('@softErrors', { log: false }).then((errors) => { errors.push(message); @@ -23,18 +23,17 @@ Cypress.Commands.add('softFail', (message) => { }); }); -// safeCheck wraps Cypress assertions to continue even if they fail +// Safe wrapper to continue test after a failed Cypress command Cypress.Commands.add('safeCheck', (fn, description) => { cy.then(() => { - return fn() - .catch((err) => { - cy.softFail(`${description} - ${err.message}`); - return null; // allow flow to continue - }); + return fn().catch((err) => { + cy.softFail(`${description} - ${err.message}`); + return null; + }); }); }); -// checks if there were any soft assertion failures and fails the test if there were. +// Fails the test if there were any collected soft assertion errors Cypress.Commands.add('assertAll', () => { cy.get('@softErrors').then((errors) => { if (errors.length > 0) { @@ -72,7 +71,7 @@ Cypress.Commands.add('getElement', (selector, parent = null, options = {}) => { } else { cy.softFail(`❌ Element not found: ${finalSelector}`); - return cy.wrap(Cypress.$([])); // Return empty set + return cy.wrap(Cypress.$([])); // empty set } }); } @@ -82,9 +81,8 @@ Cypress.Commands.add('getElement', (selector, parent = null, options = {}) => { }); // ───────────────────────────────────────────── -// Core interaction commands +// Common Element Commands // ───────────────────────────────────────────── - Cypress.Commands.add("typeInput", (element, tag) => { cy.getElement(element).clear().type(tag); }); @@ -101,7 +99,7 @@ Cypress.Commands.add("clearInput", (element) => { }); Cypress.Commands.add("typeSelectInput", (element, ...tags) => { - for (let i = 0; i < tags.length; i++) { + for (var i = 0; i < tags.length; i++) { cy.getElement(element).type(tags[i] + '{enter}', { force: true }); } cy.clickBody(); @@ -113,7 +111,8 @@ Cypress.Commands.add('getText', { prevSubject: true }, (subject) => { Cypress.Commands.add("clickDataTableMoreButtonItem", (element, rowIndex = 0) => { cy.getElement("datatable-more-button-area") - .eq(rowIndex).invoke('show') + .eq(rowIndex) + .invoke('show') .trigger('mouseenter', { force: true }); cy.clickElement(element, true); @@ -129,6 +128,9 @@ Cypress.Commands.add("clickBody", () => { cy.checkPaceRunning(); }); +// ───────────────────────────────────────────── +// Selectors and Dropdowns +// ───────────────────────────────────────────── Cypress.Commands.add("selectOption", (element, option) => { cy.getElement(element).click(); cy.clickOption('.el-select-dropdown__item', option); @@ -141,15 +143,16 @@ Cypress.Commands.add("selectListBoxItem", (element, item) => { Cypress.Commands.add("selectCheckboxOption", (element, ...options) => { cy.getElement(element).click(); - for (let i = 0; i < options.length; i++) { + for (var i = 0; i < options.length; i++) { cy.clickOption('.el-checkbox__label', options[i]); } - cy.elementExists(`${element}-select-x-confirm-button`).then((isExists) => { - if (isExists) { - cy.clickElement(`${element}-select-x-confirm-button`); - } - }); + cy.elementExists(`${element}-select-x-confirm-button`) + .then((isExists) => { + if (isExists) { + cy.clickElement(`${element}-select-x-confirm-button`); + } + }); cy.clickBody(); }); @@ -170,29 +173,21 @@ Cypress.Commands.add("selectValue", (element, valueText) => { Cypress.Commands.add("selectColor", (element, colorCode) => { cy.clickElement(element); - cy.get('.vc-input__input').eq(0) + cy.get('.vc-input__input') + .eq(0) .invoke('val', colorCode) .trigger('input'); - cy.clickElement('.cly-vue-button.button-green-skin'); -}); - -Cypress.Commands.add('dragAndDropFile', (element, filePath) => { - cy.getElement(element) - .attachFile(filePath, { encoding: 'utf-8', subjectType: 'drag-n-drop' }); -}); -Cypress.Commands.add('uploadFile', (filePath) => { - cy.get('input[type="file"]').attachFile(filePath, { force: true }); + cy.clickElement('.cly-vue-button.button-green-skin'); }); +// ───────────────────────────────────────────── +// Assertions and Element Checks +// ───────────────────────────────────────────── Cypress.Commands.add("shouldTooltipContainText", (element, text) => { - cy.getElement(element) - .eq(0).invoke('show') - .trigger('mouseenter'); + cy.getElement(element).eq(0).invoke('show').trigger('mouseenter'); cy.shouldContainText('.tooltip-inner', text); - cy.getElement(element) - .eq(0).invoke('show') - .trigger('mouseleave'); + cy.getElement(element).eq(0).invoke('show').trigger('mouseleave'); }); Cypress.Commands.add("shouldBeVisible", (element) => { @@ -208,7 +203,21 @@ Cypress.Commands.add("shouldNotBeDisabled", (element) => { }); Cypress.Commands.add("shouldContainText", (element, text) => { - cy.getElement(element).should("contain.text", text); + cy.getElement(element).should("contain", text); +}); + +Cypress.Commands.add("shouldNotContainText", (element, text) => { + cy.getElement(element).eq(0).should("not.contain", text); +}); + +Cypress.Commands.add("shouldBeEqual", (element, text) => { + cy.getElement(element).should("equal", text); +}); + +Cypress.Commands.add("shouldNotBeEqual", (element, text) => { + cy.getElement(element).invoke('text').then((actualText) => { + expect(actualText).not.to.equal(text); + }); }); Cypress.Commands.add("shouldPlaceholderContainText", (element, text) => { @@ -223,62 +232,180 @@ Cypress.Commands.add("shouldHaveValue", (element, value) => { cy.getElement(element).should("have.value", value); }); -Cypress.Commands.add("shouldUrlInclude", (url) => { - cy.url().should('include', url); -}); - +// ───────────────────────────────────────────── +// Utility Checks +// ───────────────────────────────────────────── Cypress.Commands.add('elementExists', (selector) => { cy.wait(500); if (!selector.includes('[data-test-id=') && (!selector[0].includes('.') || !selector[0].includes('#'))) { selector = `[data-test-id="${selector}"]`; } - return cy.get('body').then(($body) => $body.find(selector).length > 0); + + cy.get('body').then(($body) => { + return $body.find(selector).length > 0; + }); +}); + +Cypress.Commands.add('checkPaceRunning', () => { + cy.elementExists('.pace-running').then((isExists) => { + if (isExists) { + cy.shouldNotExist('.pace-running'); + } + }); +}); + +Cypress.Commands.add('checkPaceActive', () => { + cy.elementExists('.pace-active').then((isExists) => { + if (isExists) { + cy.shouldNotExist('.pace-active'); + } + }); }); // ───────────────────────────────────────────── -// verifyElement with safeCheck support +// Scroll Helpers // ───────────────────────────────────────────── -Cypress.Commands.add('verifyElement', (options) => { - const { - labelElement, - labelText, - tooltipElement, - tooltipText, - element, - isElementVisible = true, - elementText, - elementPlaceHolder, - hrefContainUrl, - value, - isChecked, - isDisabled, - selectedIconColor, - selectedMainColor, - selectedFontColor, - attr, - attrText, - shouldNot = false - } = options; +Cypress.Commands.add("scrollPageToBottom", (element = '.main-view', index = 0) => { + cy.get(element).eq(index).scrollTo('bottom', { ensureScrollable: false }); +}); + +Cypress.Commands.add("scrollPageToTop", (element = '.main-view', index = 0) => { + cy.get(element).eq(index).scrollTo('top', { ensureScrollable: false }); +}); + +// ───────────────────────────────────────────── +// verifyElement — Soft Assertion Aware +// ───────────────────────────────────────────── +Cypress.Commands.add('verifyElement', ({ + labelElement, + labelText, + tooltipElement, + tooltipText, + element, + isElementVisible = true, + elementText, + elementPlaceHolder, + hrefContainUrl, + value, + isChecked, + isDisabled, + unVisibleElement, + selectedIconColor, + selectedMainColor, + selectedFontColor, + attr, + attrText, + shouldNot = false +}) => { if (!shouldNot) { - if (labelElement != null && isElementVisible) { - cy.safeCheck(() => cy.getElement(labelElement, null, { soft: true }).should("be.visible"), `Element not visible: ${labelElement}`); + + if (labelElement && isElementVisible) { + cy.safeCheck(() => cy.shouldBeVisible(labelElement), `Label element "${labelElement}" should be visible`); + } + + if (labelText) { + cy.safeCheck(() => cy.shouldContainText(labelElement, labelText), `Label text mismatch for "${labelElement}"`); + } + + if (tooltipElement) { + cy.safeCheck(() => cy.shouldBeVisible(tooltipElement), `Tooltip element "${tooltipElement}" should be visible`); + } + + if (tooltipText) { + cy.safeCheck(() => cy.shouldTooltipContainText(tooltipElement, tooltipText), `Tooltip text mismatch for "${tooltipElement}"`); + } + + if (element && isElementVisible) { + cy.safeCheck(() => cy.shouldBeVisible(element), `Element "${element}" should be visible`); } - if (labelText != null) { - cy.safeCheck(() => cy.getElement(labelElement, null, { soft: true }).should("contain.text", labelText), `Label text mismatch: ${labelElement}`); + if (elementText) { + cy.safeCheck(() => cy.shouldContainText(element, elementText), `Element text mismatch for "${element}"`); } - if (element != null && isElementVisible) { - cy.safeCheck(() => cy.getElement(element, null, { soft: true }).should("be.visible"), `Element not visible: ${element}`); + if (elementPlaceHolder) { + cy.safeCheck(() => cy.shouldPlaceholderContainText(element, elementPlaceHolder), `Placeholder mismatch for "${element}"`); } - if (elementText != null) { - cy.safeCheck(() => cy.getElement(element, null, { soft: true }).should("contain.text", elementText), `Element text mismatch: ${element}`); + if (hrefContainUrl) { + cy.safeCheck(() => cy.shouldHrefContainUrl(element, hrefContainUrl), `Href mismatch for "${element}"`); } - if (elementPlaceHolder != null) { - cy.safeCheck(() => cy.getElement(element, null, { soft: true }).invoke("attr", "placeholder").should("contain", elementPlaceHolder), `Placeholder mismatch: ${element}`); + if (value) { + cy.safeCheck(() => cy.shouldHaveValue(element, value), `Value mismatch for "${element}"`); } + + if (isChecked != null) { + const selector = `[data-test-id="${element}"]`; + if (isChecked) { + cy.safeCheck(() => cy.shouldBeVisible(selector + '.is-checked'), `Element "${selector}" should be checked`); + } + else { + cy.safeCheck(() => cy.shouldNotExist(selector + '.is-checked'), `Element "${selector}" should not be checked`); + } + } + + if (isDisabled != null) { + if (isDisabled) { + cy.safeCheck(() => cy.shouldBeDisabled(element), `Element "${element}" should be disabled`); + } + else { + cy.safeCheck(() => cy.shouldNotBeDisabled(element), `Element "${element}" should not be disabled`); + } + } + + if (selectedIconColor) { + const selector = unVisibleElement || element; + cy.safeCheck(() => { + cy.getElement(`[data-test-id="${selector}"]`) + .invoke('attr', 'style') + .should('contain', helper.hexToRgb(selectedIconColor)); + }, `Selected icon color mismatch for "${selector}"`); + } + + if (selectedFontColor) { + cy.safeCheck(() => { + cy.getElement(`[data-test-id="${element}"]`) + .invoke('attr', 'style') + .should('contain', helper.hexToRgb(selectedFontColor)); + }, `Selected font color mismatch for "${element}"`); + } + + if (selectedMainColor) { + cy.safeCheck(() => { + cy.getElement(`[data-test-id="${element}"]`) + .invoke('attr', 'style') + .should('contain', helper.hexToRgb(selectedMainColor)); + }, `Selected main color mismatch for "${element}"`); + } + + if (attr && attrText) { + cy.safeCheck(() => { + cy.getElement(`[data-test-id="${element}"]`) + .invoke('attr', attr) + .should('contain', attrText); + }, `Attribute "${attr}" mismatch for "${element}"`); + } + } + else { + + if (element && isElementVisible) { + cy.safeCheck(() => cy.shouldBeVisible(element), `Element "${element}" should be visible`); + cy.safeCheck(() => cy.shouldNotBeEqual(element, elementText), `Element "${element}" text should not equal "${elementText}"`); + } + + if (labelElement && isElementVisible) { + cy.safeCheck(() => cy.shouldBeVisible(labelElement), `Label element "${labelElement}" should be visible`); + cy.safeCheck(() => cy.shouldNotBeEqual(labelElement, labelText), `Label text should not equal "${labelText}"`); + } + } +}); + +// ───────────────────────────────────────────── +// DB helper +// ───────────────────────────────────────────── +Cypress.Commands.add('dropMongoDatabase', () => { + cy.exec("mongosh mongodb/countly --eval 'db.dropDatabase()'"); }); \ No newline at end of file From 9d43f87fe0a53e5a928fe6fd3e1ea341b8133317 Mon Sep 17 00:00:00 2001 From: can-angun Date: Fri, 7 Nov 2025 16:02:09 +0000 Subject: [PATCH 12/12] Added missing commands --- ui-tests/cypress/support/commands.js | 60 ++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/ui-tests/cypress/support/commands.js b/ui-tests/cypress/support/commands.js index b6e73de8dfe..c10ecb1891e 100644 --- a/ui-tests/cypress/support/commands.js +++ b/ui-tests/cypress/support/commands.js @@ -128,6 +128,18 @@ Cypress.Commands.add("clickBody", () => { cy.checkPaceRunning(); }); +Cypress.Commands.add('dragAndDropFile', (element, filePath) => { + cy.getElement(element) + .attachFile(filePath, { + encoding: 'utf-8', + subjectType: 'drag-n-drop' + }); +}); + +Cypress.Commands.add('uploadFile', (filePath) => { + cy.get('input[type="file"]').attachFile(filePath, { force: true }); +}); + // ───────────────────────────────────────────── // Selectors and Dropdowns // ───────────────────────────────────────────── @@ -202,6 +214,14 @@ Cypress.Commands.add("shouldNotBeDisabled", (element) => { cy.getElement(element).should("not.be.disabled"); }); +Cypress.Commands.add("shouldBeHasDisabledClass", (element) => { + cy.get(`[data-test-id="${element}"].is-disabled`).should("exist"); +}); + +Cypress.Commands.add("shouldNotBeHasDisabledClass", (element) => { + cy.get(`[data-test-id="${element}"].is-disabled`).should("not.exist"); +}); + Cypress.Commands.add("shouldContainText", (element, text) => { cy.getElement(element).should("contain", text); }); @@ -224,6 +244,10 @@ Cypress.Commands.add("shouldPlaceholderContainText", (element, text) => { cy.getElement(element).invoke("attr", "placeholder").should("contain", text); }); +Cypress.Commands.add("shouldDataOriginalTitleContainText", (element, text) => { + cy.getElement(element).invoke("attr", "data-original-title").should("contain", text); +}); + Cypress.Commands.add("shouldHrefContainUrl", (element, url) => { cy.getElement(element).invoke("attr", "href").should("contain", url); }); @@ -232,6 +256,10 @@ Cypress.Commands.add("shouldHaveValue", (element, value) => { cy.getElement(element).should("have.value", value); }); +Cypress.Commands.add("shouldUrlInclude", (url) => { + cy.url().should('include', url); +}); + // ───────────────────────────────────────────── // Utility Checks // ───────────────────────────────────────────── @@ -246,6 +274,14 @@ Cypress.Commands.add('elementExists', (selector) => { }); }); +Cypress.Commands.add('shouldBeExist', (element) => { + cy.getElement(element).should('exist'); +}); + +Cypress.Commands.add('shouldNotExist', (element) => { + cy.getElement(element).should('not.exist'); +}); + Cypress.Commands.add('checkPaceRunning', () => { cy.elementExists('.pace-running').then((isExists) => { if (isExists) { @@ -265,6 +301,18 @@ Cypress.Commands.add('checkPaceActive', () => { // ───────────────────────────────────────────── // Scroll Helpers // ───────────────────────────────────────────── +Cypress.Commands.add("scrollPageSlightly", (element = '.main-view', index = 0) => { + cy.get(element).eq(index).then(($el) => { + const currentScroll = $el[0].scrollTop; + const newScroll = currentScroll + 550; + + cy.wrap($el).scrollTo(0, newScroll, { + duration: 1000, + ensureScrollable: false, + }); + }); +}); + Cypress.Commands.add("scrollPageToBottom", (element = '.main-view', index = 0) => { cy.get(element).eq(index).scrollTo('bottom', { ensureScrollable: false }); }); @@ -273,6 +321,18 @@ Cypress.Commands.add("scrollPageToTop", (element = '.main-view', index = 0) => { cy.get(element).eq(index).scrollTo('top', { ensureScrollable: false }); }); +Cypress.Commands.add("scrollPageToCenter", (element = '.main-view', index = 0) => { + cy.get(element).eq(index).scrollTo('center', { ensureScrollable: false }); +}); + +Cypress.Commands.add("scrollDataTableToRight", (element = '.el-table__body-wrapper', index = 0) => { + cy.get(element).eq(index).scrollTo('right', { ensureScrollable: false }); +}); + +Cypress.Commands.add("scrollDataTableToLeft", (element = '.el-table__body-wrapper', index = 0) => { + cy.get(element).eq(index).scrollTo('left', { ensureScrollable: false }); +}); + // ───────────────────────────────────────────── // verifyElement — Soft Assertion Aware // ─────────────────────────────────────────────