From 05cfa2b5406e8f5c093da0b840364220ef5e3bbe Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Tue, 28 Oct 2025 16:10:33 +0530 Subject: [PATCH 1/8] Added automated tests with cypress for Service Requests form --- .../Services/Requests/service_requests.cy.js | 519 ++++++++++++++++++ 1 file changed, 519 insertions(+) create mode 100644 cypress/e2e/ui/Services/Requests/service_requests.cy.js diff --git a/cypress/e2e/ui/Services/Requests/service_requests.cy.js b/cypress/e2e/ui/Services/Requests/service_requests.cy.js new file mode 100644 index 00000000000..eab30b68fe4 --- /dev/null +++ b/cypress/e2e/ui/Services/Requests/service_requests.cy.js @@ -0,0 +1,519 @@ +/* eslint-disable no-undef */ +import { flashClassMap } from '../../../../support/assertions/assertion_constants'; +import { + LABEL_CONFIG_KEYS, + FIELD_CONFIG_KEYS, + FIELD_TYPES, + BUTTON_CONFIG_KEYS, +} from '../../../../support/commands/constants/command_constants'; + +// Component route url +const COMPONENT_ROUTE_URL = '/miq_request/show_list'; + +// Menu options +const SERVICES_MENU_OPTION = 'Services'; +const REQUESTS_MENU_OPTION = 'Requests'; +const CATALOGS_MENU_OPTION = 'Catalogs'; +const AUTOMATION_MENU_OPTION = 'Automation'; +const EMBEDDED_AUTOMATION_MENU_OPTION = 'Embedded Automate'; +const CUSTOMIZATION_MENU_OPTION = 'Customization'; + +// Accordion items +const SERVICE_DIALOGS_ACCORDION = 'Service Dialogs'; +const ALL_DIALOGS_ACCORDION_ITEM = 'All Dialogs'; +const SERVICE_CATALOGS_ACCORDION = 'Service Catalogs'; +const ALL_SERVICES_ACCORDION_ITEM = 'All Services'; +const CATALOG_ITEMS_ACCORDION_ITEM = 'Catalog Items'; +const ALL_CATALOG_ITEMS_ACCORDION_ITEM = 'All Catalog Items'; + +// Toolbar options +const TOOLBAR_CONFIGURATION = 'Configuration'; +const TOOLBAR_ADD_NEW_DIALOG = 'Add a new Dialog'; +const TOOLBAR_ADD_CATALOG_ITEM = 'Add a New Catalog Item'; +const TOOLBAR_REMOVE_SELECTED_DIALOGS = 'Remove selected Dialogs'; +const TOOLBAR_DELETE_CATALOG_ITEMS = 'Delete Catalog Items'; + +// Field labels +const FORM_HEADER = 'Requests'; +const REQUESTER_LABEL = 'Requester'; +const APPROVAL_STATE_HEADER = 'Approval State'; +const TYPE_LABEL = 'Type'; +const REQUEST_DATE_LABEL = 'Request Date'; +const REASON_LABEL = 'Reason'; + +// Field values +const TEST_DIALOG_NAME = 'Test-Dialog-Name'; +const CATALOG_ITEM_NAME = 'Test-Catalog-Item'; +const SELECT_OPTION_ALL = 'all'; +const TYPE_VM_PROVISION = 'VM Provision'; +const REQUEST_DATE_LAST_7_DAYS = '7'; + +// Checkbox labels +const PENDING_APPROVAL_LABEL = 'Pending Approval'; +const APPROVED_LABEL = 'Approved'; +const DENIED_LABEL = 'Denied'; + +// Button texts +const APPLY_BUTTON_TEXT = 'Apply'; +const RESET_BUTTON_TEXT = 'Reset'; +const SAVE_BUTTON_TEXT = 'Save'; +const ADD_BUTTON_TEXT = 'Add'; +const ORDER_BUTTON_TEXT = 'Order'; +const SUBMIT_BUTTON_TEXT = 'Submit'; +const DELETE_BUTTON_TEXT = 'Delete'; + +// Flash message text snippets +const FLASH_MESSAGE_ADD_SUCCESS = 'added'; +const FLASH_MESSAGE_SAVE_SUCCESS = 'saved'; +const FLASH_MESSAGE_SUBMITTED = 'submitted'; +const ALERT_MESSAGE_DELETE = 'delete'; +const BROWSER_CONFIRM_REMOVE_MESSAGE = 'remove'; + +function dataSetup() { + // Adding a dialog + cy.menu( + AUTOMATION_MENU_OPTION, + EMBEDDED_AUTOMATION_MENU_OPTION, + CUSTOMIZATION_MENU_OPTION + ); + cy.accordion(SERVICE_DIALOGS_ACCORDION); + cy.selectAccordionItem([ALL_DIALOGS_ACCORDION_ITEM]); + cy.toolbar(TOOLBAR_CONFIGURATION, TOOLBAR_ADD_NEW_DIALOG); + cy.contains('ul.static-field-list li.static-field-item', 'Text Box').trigger( + 'mousedown', + { which: 1 } + ); + cy.get('.well-lg').trigger('mousemove').trigger('mouseup', { force: true }); + cy.getFormInputFieldByIdAndType({ inputId: 'name' }).type(TEST_DIALOG_NAME); + cy.interceptApi({ + urlPattern: '/api/service_dialogs', + alias: 'addDialogApi', + triggerFn: () => + cy + .contains('.pull-right button', SAVE_BUTTON_TEXT) + .should('be.enabled') + .click(), + onApiResponse: (interception) => + expect(interception.response.statusCode).to.equal(200), + }); + cy.expect_flash(flashClassMap.success, FLASH_MESSAGE_SAVE_SUCCESS); + // Adding a catalog item + cy.menu(SERVICES_MENU_OPTION, CATALOGS_MENU_OPTION); + cy.accordion(CATALOG_ITEMS_ACCORDION_ITEM); + cy.selectAccordionItem([ALL_CATALOG_ITEMS_ACCORDION_ITEM]); + cy.toolbar(TOOLBAR_CONFIGURATION, TOOLBAR_ADD_CATALOG_ITEM); + cy.contains('.form-group button[data-id="st_prov_type"]', 'Choose').click(); + cy.interceptApi({ + urlPattern: '/catalog/atomic_form_field_changed/new?st_prov_type=generic', + alias: 'getGenericCatalogItemTypeDetailsApi', + triggerFn: () => + cy.contains('.form-group ul.dropdown-menu li a', 'Generic').click(), + onApiResponse: (interception) => + expect(interception.response.statusCode).to.equal(200), + }); + cy.get('input#name').type(CATALOG_ITEM_NAME); + cy.contains('.form-group button[data-id="catalog_id"]', 'Unassigned').click(); + cy.contains( + '.form-group ul.dropdown-menu li a', + 'My Company/My Catalog' + ).click(); + cy.contains('.form-group button[data-id="dialog_id"]', 'No Dialog').click(); + cy.contains(' .form-group ul.dropdown-menu li a', TEST_DIALOG_NAME).click(); + cy.get('input#display').check(); + cy.interceptApi({ + urlPattern: '/catalog/servicetemplate_edit?button=add', + alias: 'addCatalogItemApi', + triggerFn: () => + cy.contains('#form_buttons_div button', ADD_BUTTON_TEXT).click(), + onApiResponse: (interception) => + expect(interception.response.statusCode).to.equal(200), + }); + cy.expect_flash(flashClassMap.success, FLASH_MESSAGE_ADD_SUCCESS); + // Order + cy.accordion(SERVICE_CATALOGS_ACCORDION); + cy.selectAccordionItem([ALL_SERVICES_ACCORDION_ITEM]); + // TODO: Replace with clickTableRowByText once #9691 is merged + cy.contains('.miq-data-table table tbody tr td', CATALOG_ITEM_NAME).click(); + // cy.clickTableRowByText({ text: CATALOG_ITEM_NAME, columnIndex: 1 }); + cy.interceptApi({ + urlPattern: /\/catalog\/x_button\/\d+\?pressed=svc_catalog_provision/, + alias: 'orderApi', + triggerFn: () => + cy + .contains(`#main-content button[type="button"]`, ORDER_BUTTON_TEXT) + .click(), + onApiResponse: (interception) => + expect(interception.response.statusCode).to.equal(200), + }); + cy.interceptApi({ + urlPattern: /\/api\/service_catalogs\/\d+\/service_templates\/\d+$/, + alias: 'submitOrderApi', + triggerFn: () => + cy.contains('.pull-right button', SUBMIT_BUTTON_TEXT).click(), + onApiResponse: (interception) => + expect(interception.response.statusCode).to.equal(200), + }); + cy.expect_flash(flashClassMap.success, FLASH_MESSAGE_SUBMITTED); +} + +function cleanUp() { + cy.url() + .then((url) => { + // Ensures navigation to Services -> Catalogs in the UI + if (!url.endsWith(COMPONENT_ROUTE_URL)) { + cy.visit(COMPONENT_ROUTE_URL); + } + }) + .then(() => { + // Delete the request + // TODO: Replace with clickTableRowByText once #9691 is merged + cy.contains( + '.miq-data-table table tbody tr td', + CATALOG_ITEM_NAME + ).click(); + // cy.clickTableRowByText({ text: CATALOG_ITEM_NAME, columnIndex: 6 }); + cy.interceptApi({ + urlPattern: /\/miq_request\/button\/\d+\?pressed=miq_request_delete/, + alias: 'deleteRequestApi', + triggerFn: () => + cy.expect_browser_confirm_with_text({ + confirmTriggerFn: () => cy.get('button#miq_request_delete').click(), + containsText: ALERT_MESSAGE_DELETE, + }), + onApiResponse: (interception) => + expect(interception.response.statusCode).to.equal(200), + }); + // Delete catalog item + cy.menu(SERVICES_MENU_OPTION, CATALOGS_MENU_OPTION); + cy.accordion(CATALOG_ITEMS_ACCORDION_ITEM); + cy.selectAccordionItem([ALL_CATALOG_ITEMS_ACCORDION_ITEM]); + cy.selectTableRowsByText({ + textArray: [CATALOG_ITEM_NAME], + }); + cy.toolbar(TOOLBAR_CONFIGURATION, TOOLBAR_DELETE_CATALOG_ITEMS); + cy.interceptApi({ + urlPattern: /\/api\/service_templates\/\d+$/, + alias: 'deleteCatalogItemApi', + triggerFn: () => + cy.expect_modal({ + modalContentExpectedTexts: ['delete', CATALOG_ITEM_NAME], + targetFooterButtonText: DELETE_BUTTON_TEXT, + }), + onApiResponse: (interception) => + expect(interception.response.statusCode).to.equal(200), + }); + // Delete service dialog + cy.menu( + AUTOMATION_MENU_OPTION, + EMBEDDED_AUTOMATION_MENU_OPTION, + CUSTOMIZATION_MENU_OPTION + ); + cy.accordion(SERVICE_DIALOGS_ACCORDION); + cy.selectAccordionItem([ALL_DIALOGS_ACCORDION_ITEM]); + cy.selectTableRowsByText({ + textArray: [TEST_DIALOG_NAME], + }); + cy.interceptApi({ + urlPattern: '/miq_ae_customization/x_button?pressed=dialog_delete', + alias: 'deleteDialogApi', + triggerFn: () => + cy.expect_browser_confirm_with_text({ + confirmTriggerFn: () => + cy.toolbar( + TOOLBAR_CONFIGURATION, + TOOLBAR_REMOVE_SELECTED_DIALOGS + ), + containsText: BROWSER_CONFIRM_REMOVE_MESSAGE, + }), + onApiResponse: (interception) => + expect(interception.response.statusCode).to.equal(200), + }); + cy.expect_flash(flashClassMap.success, ALERT_MESSAGE_DELETE); + }); +} + +describe('Automate Service Requests form operations: Services > Requests', () => { + beforeEach(() => { + cy.login(); + }); + + describe('Verify form fields', () => { + beforeEach(() => { + cy.menu(SERVICES_MENU_OPTION, REQUESTS_MENU_OPTION); + }); + + it('Verify form’s initial UI state', () => { + cy.contains('#main-content h1', FORM_HEADER); + cy.getFormLegendByText({ legendText: APPROVAL_STATE_HEADER }); + cy.validateFormLabels([ + { + [LABEL_CONFIG_KEYS.FOR_VALUE]: 'selectedUser', + [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: REQUESTER_LABEL, + }, + { + [LABEL_CONFIG_KEYS.FOR_VALUE]: 'types', + [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: TYPE_LABEL, + }, + { + [LABEL_CONFIG_KEYS.FOR_VALUE]: 'selectedPeriod', + [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: REQUEST_DATE_LABEL, + }, + { + [LABEL_CONFIG_KEYS.FOR_VALUE]: 'reasonText', + [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: REASON_LABEL, + }, + { + [LABEL_CONFIG_KEYS.FOR_VALUE]: 'approvalStates-pending_approval', + [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: PENDING_APPROVAL_LABEL, + }, + { + [LABEL_CONFIG_KEYS.FOR_VALUE]: 'approvalStates-approved', + [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: APPROVED_LABEL, + }, + { + [LABEL_CONFIG_KEYS.FOR_VALUE]: 'approvalStates-denied', + [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: DENIED_LABEL, + }, + ]); + cy.validateFormFields([ + { + [FIELD_CONFIG_KEYS.ID]: 'selectedUser', + [FIELD_CONFIG_KEYS.FIELD_TYPE]: FIELD_TYPES.SELECT, + [FIELD_CONFIG_KEYS.EXPECTED_VALUE]: SELECT_OPTION_ALL, + }, + { + [FIELD_CONFIG_KEYS.ID]: 'approvalStates-pending_approval', + [FIELD_CONFIG_KEYS.INPUT_FIELD_TYPE]: 'checkbox', + [FIELD_CONFIG_KEYS.SHOULD_BE_CHECKED]: true, + }, + { + [FIELD_CONFIG_KEYS.ID]: 'approvalStates-approved', + [FIELD_CONFIG_KEYS.INPUT_FIELD_TYPE]: 'checkbox', + [FIELD_CONFIG_KEYS.SHOULD_BE_CHECKED]: true, + }, + { + [FIELD_CONFIG_KEYS.ID]: 'approvalStates-denied', + [FIELD_CONFIG_KEYS.INPUT_FIELD_TYPE]: 'checkbox', + [FIELD_CONFIG_KEYS.SHOULD_BE_CHECKED]: true, + }, + { + [FIELD_CONFIG_KEYS.ID]: 'types', + [FIELD_CONFIG_KEYS.FIELD_TYPE]: FIELD_TYPES.SELECT, + [FIELD_CONFIG_KEYS.EXPECTED_VALUE]: SELECT_OPTION_ALL, + }, + { + [FIELD_CONFIG_KEYS.ID]: 'selectedPeriod', + [FIELD_CONFIG_KEYS.FIELD_TYPE]: FIELD_TYPES.SELECT, + [FIELD_CONFIG_KEYS.EXPECTED_VALUE]: REQUEST_DATE_LAST_7_DAYS, + }, + { + [FIELD_CONFIG_KEYS.ID]: 'reasonText', + }, + ]); + // TODO: Replace with validateFormFooterButtons once #9689 is merged + cy.contains(`#main-content button[type="submit"]`, APPLY_BUTTON_TEXT) + .should('be.visible') + .and('be.enabled'); + cy.contains(`#main-content button[type="button"]`, RESET_BUTTON_TEXT) + .should('be.visible') + .and('be.enabled'); + // cy.validateFormFooterButtons([ + // { + // [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: APPLY_BUTTON_TEXT, + // [BUTTON_CONFIG_KEYS.BUTTON_TYPE]: 'submit', + // [BUTTON_CONFIG_KEYS.BUTTON_WRAPPER_CLASS]: 'custom-button-wrapper', + // }, + // { + // [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: RESET_BUTTON_TEXT, + // [BUTTON_CONFIG_KEYS.BUTTON_WRAPPER_CLASS]: 'custom-button-wrapper', + // }, + // ]); + // TODO: Replace with verify_gtl_no_records_text once #9691 is merged + cy.contains('#miq-gtl-view .no-records-found', 'No records found'); + // cy.verify_gtl_no_records_text(); + }); + }); + + describe('Validate button click actions', () => { + beforeEach(() => { + // TODO: Replace with better setup approach + dataSetup(); + }); + + it('Validate reset & apply buttons', () => { + /* Reset */ + cy.getFormSelectFieldById({ selectId: 'selectedUser' }).select( + 'Administrator' + ); + cy.getFormLabelByForAttribute({ + forValue: 'approvalStates-pending_approval', + }).click(); + cy.getFormInputFieldByIdAndType({ + inputId: 'approvalStates-pending_approval', + inputType: 'checkbox', + }).should('not.be.checked'); + cy.getFormLabelByForAttribute({ + forValue: 'approvalStates-approved', + }).click(); + cy.getFormInputFieldByIdAndType({ + inputId: 'approvalStates-approved', + inputType: 'checkbox', + }).should('not.be.checked'); + cy.getFormLabelByForAttribute({ + forValue: 'approvalStates-denied', + }).click(); + cy.getFormInputFieldByIdAndType({ + inputId: 'approvalStates-denied', + inputType: 'checkbox', + }).should('not.be.checked'); + cy.getFormSelectFieldById({ selectId: 'types' }).select( + TYPE_VM_PROVISION + ); + cy.getFormSelectFieldById({ selectId: 'selectedPeriod' }).select( + 'Last 30 Days' + ); + cy.getFormInputFieldByIdAndType({ inputId: 'reasonText' }).type( + 'Testing' + ); + // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged + cy.contains( + `#main-content button[type="button"]`, + RESET_BUTTON_TEXT + ).click(); + // cy.getFormFooterButtonByTypeWithText({ + // buttonText: RESET_BUTTON_TEXT, + // buttonWrapperClass: 'custom-button-wrapper', + // }).click(); + cy.getFormSelectFieldById({ selectId: 'selectedUser' }).should( + 'have.value', + SELECT_OPTION_ALL + ); + cy.getFormInputFieldByIdAndType({ + inputId: 'approvalStates-pending_approval', + inputType: 'checkbox', + }).should('be.checked'); + cy.getFormInputFieldByIdAndType({ + inputId: 'approvalStates-approved', + inputType: 'checkbox', + }).should('be.checked'); + cy.getFormInputFieldByIdAndType({ + inputId: 'approvalStates-denied', + inputType: 'checkbox', + }).should('be.checked'); + cy.getFormSelectFieldById({ selectId: 'types' }).should( + 'have.value', + SELECT_OPTION_ALL + ); + cy.getFormSelectFieldById({ selectId: 'selectedPeriod' }).should( + 'have.value', + REQUEST_DATE_LAST_7_DAYS + ); + cy.getFormInputFieldByIdAndType({ inputId: 'reasonText' }).should( + 'have.value', + '' + ); + /* Apply */ + // Filter data with approval state + cy.getFormLabelByForAttribute({ + forValue: 'approvalStates-pending_approval', + }).click(); + cy.getFormInputFieldByIdAndType({ + inputId: 'approvalStates-pending_approval', + inputType: 'checkbox', + }).should('not.be.checked'); + cy.getFormLabelByForAttribute({ + forValue: 'approvalStates-approved', + }).click(); + cy.getFormInputFieldByIdAndType({ + inputId: 'approvalStates-approved', + inputType: 'checkbox', + }).should('not.be.checked'); + // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged + cy.contains( + `#main-content button[type="submit"]`, + APPLY_BUTTON_TEXT + ).click(); + // cy.getFormFooterButtonByTypeWithText({ + // buttonText: APPLY_BUTTON_TEXT, + // buttonWrapperClass: 'custom-button-wrapper', + // buttonType: 'submit', + // }).click(); + // TODO: Replace with verify_gtl_no_records_text once #9691 is merged + cy.contains('#miq-gtl-view .no-records-found', 'No records found'); + // cy.verify_gtl_no_records_text(); + // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged + cy.contains( + `#main-content button[type="button"]`, + RESET_BUTTON_TEXT + ).click(); + // cy.getFormFooterButtonByTypeWithText({ + // buttonText: RESET_BUTTON_TEXT, + // buttonWrapperClass: 'custom-button-wrapper', + // }).click(); + cy.gtlGetRows([0]).then((data) => { + expect(data.length).to.equal(1); + }); + // Filter data with type + cy.getFormSelectFieldById({ selectId: 'types' }).select( + TYPE_VM_PROVISION + ); + // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged + cy.contains( + `#main-content button[type="submit"]`, + APPLY_BUTTON_TEXT + ).click(); + // cy.getFormFooterButtonByTypeWithText({ + // buttonText: APPLY_BUTTON_TEXT, + // buttonWrapperClass: 'custom-button-wrapper', + // buttonType: 'submit', + // }).click(); + // TODO: Replace with verify_gtl_no_records_text once #9691 is merged + cy.contains('#miq-gtl-view .no-records-found', 'No records found'); + // cy.verify_gtl_no_records_text(); + // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged + cy.contains( + `#main-content button[type="button"]`, + RESET_BUTTON_TEXT + ).click(); + // cy.getFormFooterButtonByTypeWithText({ + // buttonText: RESET_BUTTON_TEXT, + // buttonWrapperClass: 'custom-button-wrapper', + // }).click(); + cy.gtlGetRows([0]).then((data) => { + expect(data.length).to.equal(1); + }); + // Filter data with + cy.getFormInputFieldByIdAndType({ inputId: 'reasonText' }).type('r@ndOm'); + // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged + cy.contains( + `#main-content button[type="submit"]`, + APPLY_BUTTON_TEXT + ).click(); + // cy.getFormFooterButtonByTypeWithText({ + // buttonText: APPLY_BUTTON_TEXT, + // buttonWrapperClass: 'custom-button-wrapper', + // buttonType: 'submit', + // }).click(); + // TODO: Replace with verify_gtl_no_records_text once #9691 is merged + cy.contains('#miq-gtl-view .no-records-found', 'No records found'); + // cy.verify_gtl_no_records_text(); + // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged + cy.contains( + `#main-content button[type="button"]`, + RESET_BUTTON_TEXT + ).click(); + // cy.getFormFooterButtonByTypeWithText({ + // buttonText: RESET_BUTTON_TEXT, + // buttonWrapperClass: 'custom-button-wrapper', + // }).click(); + cy.gtlGetRows([0]).then((data) => { + expect(data.length).to.equal(1); + }); + }); + + afterEach(() => { + // TODO: Replace with better cleanup approach + cleanUp(); + }); + }); +}); From edfa11566259550626c26de1f88ed59544eabd32 Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Wed, 29 Oct 2025 19:48:00 +0530 Subject: [PATCH 2/8] Switching to database restore instead of UI-driven cleanup --- .../Services/Requests/service_requests.cy.js | 87 +------------------ 1 file changed, 1 insertion(+), 86 deletions(-) diff --git a/cypress/e2e/ui/Services/Requests/service_requests.cy.js b/cypress/e2e/ui/Services/Requests/service_requests.cy.js index eab30b68fe4..eb26c39014c 100644 --- a/cypress/e2e/ui/Services/Requests/service_requests.cy.js +++ b/cypress/e2e/ui/Services/Requests/service_requests.cy.js @@ -7,9 +7,6 @@ import { BUTTON_CONFIG_KEYS, } from '../../../../support/commands/constants/command_constants'; -// Component route url -const COMPONENT_ROUTE_URL = '/miq_request/show_list'; - // Menu options const SERVICES_MENU_OPTION = 'Services'; const REQUESTS_MENU_OPTION = 'Requests'; @@ -30,8 +27,6 @@ const ALL_CATALOG_ITEMS_ACCORDION_ITEM = 'All Catalog Items'; const TOOLBAR_CONFIGURATION = 'Configuration'; const TOOLBAR_ADD_NEW_DIALOG = 'Add a new Dialog'; const TOOLBAR_ADD_CATALOG_ITEM = 'Add a New Catalog Item'; -const TOOLBAR_REMOVE_SELECTED_DIALOGS = 'Remove selected Dialogs'; -const TOOLBAR_DELETE_CATALOG_ITEMS = 'Delete Catalog Items'; // Field labels const FORM_HEADER = 'Requests'; @@ -60,14 +55,11 @@ const SAVE_BUTTON_TEXT = 'Save'; const ADD_BUTTON_TEXT = 'Add'; const ORDER_BUTTON_TEXT = 'Order'; const SUBMIT_BUTTON_TEXT = 'Submit'; -const DELETE_BUTTON_TEXT = 'Delete'; // Flash message text snippets const FLASH_MESSAGE_ADD_SUCCESS = 'added'; const FLASH_MESSAGE_SAVE_SUCCESS = 'saved'; const FLASH_MESSAGE_SUBMITTED = 'submitted'; -const ALERT_MESSAGE_DELETE = 'delete'; -const BROWSER_CONFIRM_REMOVE_MESSAGE = 'remove'; function dataSetup() { // Adding a dialog @@ -156,82 +148,6 @@ function dataSetup() { cy.expect_flash(flashClassMap.success, FLASH_MESSAGE_SUBMITTED); } -function cleanUp() { - cy.url() - .then((url) => { - // Ensures navigation to Services -> Catalogs in the UI - if (!url.endsWith(COMPONENT_ROUTE_URL)) { - cy.visit(COMPONENT_ROUTE_URL); - } - }) - .then(() => { - // Delete the request - // TODO: Replace with clickTableRowByText once #9691 is merged - cy.contains( - '.miq-data-table table tbody tr td', - CATALOG_ITEM_NAME - ).click(); - // cy.clickTableRowByText({ text: CATALOG_ITEM_NAME, columnIndex: 6 }); - cy.interceptApi({ - urlPattern: /\/miq_request\/button\/\d+\?pressed=miq_request_delete/, - alias: 'deleteRequestApi', - triggerFn: () => - cy.expect_browser_confirm_with_text({ - confirmTriggerFn: () => cy.get('button#miq_request_delete').click(), - containsText: ALERT_MESSAGE_DELETE, - }), - onApiResponse: (interception) => - expect(interception.response.statusCode).to.equal(200), - }); - // Delete catalog item - cy.menu(SERVICES_MENU_OPTION, CATALOGS_MENU_OPTION); - cy.accordion(CATALOG_ITEMS_ACCORDION_ITEM); - cy.selectAccordionItem([ALL_CATALOG_ITEMS_ACCORDION_ITEM]); - cy.selectTableRowsByText({ - textArray: [CATALOG_ITEM_NAME], - }); - cy.toolbar(TOOLBAR_CONFIGURATION, TOOLBAR_DELETE_CATALOG_ITEMS); - cy.interceptApi({ - urlPattern: /\/api\/service_templates\/\d+$/, - alias: 'deleteCatalogItemApi', - triggerFn: () => - cy.expect_modal({ - modalContentExpectedTexts: ['delete', CATALOG_ITEM_NAME], - targetFooterButtonText: DELETE_BUTTON_TEXT, - }), - onApiResponse: (interception) => - expect(interception.response.statusCode).to.equal(200), - }); - // Delete service dialog - cy.menu( - AUTOMATION_MENU_OPTION, - EMBEDDED_AUTOMATION_MENU_OPTION, - CUSTOMIZATION_MENU_OPTION - ); - cy.accordion(SERVICE_DIALOGS_ACCORDION); - cy.selectAccordionItem([ALL_DIALOGS_ACCORDION_ITEM]); - cy.selectTableRowsByText({ - textArray: [TEST_DIALOG_NAME], - }); - cy.interceptApi({ - urlPattern: '/miq_ae_customization/x_button?pressed=dialog_delete', - alias: 'deleteDialogApi', - triggerFn: () => - cy.expect_browser_confirm_with_text({ - confirmTriggerFn: () => - cy.toolbar( - TOOLBAR_CONFIGURATION, - TOOLBAR_REMOVE_SELECTED_DIALOGS - ), - containsText: BROWSER_CONFIRM_REMOVE_MESSAGE, - }), - onApiResponse: (interception) => - expect(interception.response.statusCode).to.equal(200), - }); - cy.expect_flash(flashClassMap.success, ALERT_MESSAGE_DELETE); - }); -} - describe('Automate Service Requests form operations: Services > Requests', () => { beforeEach(() => { cy.login(); @@ -512,8 +428,7 @@ describe('Automate Service Requests form operations: Services > Requests', () => }); afterEach(() => { - // TODO: Replace with better cleanup approach - cleanUp(); + cy.appDbState('restore'); }); }); }); From f5f4ec348a354853d0c44198bbfcd691ff51bfaa Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Thu, 30 Oct 2025 10:25:56 +0530 Subject: [PATCH 3/8] Incorporating changes from 9689 --- .../Services/Requests/service_requests.cy.js | 126 ++++++------------ 1 file changed, 42 insertions(+), 84 deletions(-) diff --git a/cypress/e2e/ui/Services/Requests/service_requests.cy.js b/cypress/e2e/ui/Services/Requests/service_requests.cy.js index eb26c39014c..e7d2df2b98f 100644 --- a/cypress/e2e/ui/Services/Requests/service_requests.cy.js +++ b/cypress/e2e/ui/Services/Requests/service_requests.cy.js @@ -226,24 +226,17 @@ describe('Automate Service Requests form operations: Services > Requests', () => [FIELD_CONFIG_KEYS.ID]: 'reasonText', }, ]); - // TODO: Replace with validateFormFooterButtons once #9689 is merged - cy.contains(`#main-content button[type="submit"]`, APPLY_BUTTON_TEXT) - .should('be.visible') - .and('be.enabled'); - cy.contains(`#main-content button[type="button"]`, RESET_BUTTON_TEXT) - .should('be.visible') - .and('be.enabled'); - // cy.validateFormFooterButtons([ - // { - // [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: APPLY_BUTTON_TEXT, - // [BUTTON_CONFIG_KEYS.BUTTON_TYPE]: 'submit', - // [BUTTON_CONFIG_KEYS.BUTTON_WRAPPER_CLASS]: 'custom-button-wrapper', - // }, - // { - // [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: RESET_BUTTON_TEXT, - // [BUTTON_CONFIG_KEYS.BUTTON_WRAPPER_CLASS]: 'custom-button-wrapper', - // }, - // ]); + cy.validateFormFooterButtons([ + { + [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: APPLY_BUTTON_TEXT, + [BUTTON_CONFIG_KEYS.BUTTON_TYPE]: 'submit', + [BUTTON_CONFIG_KEYS.BUTTON_WRAPPER_CLASS]: 'custom-button-wrapper', + }, + { + [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: RESET_BUTTON_TEXT, + [BUTTON_CONFIG_KEYS.BUTTON_WRAPPER_CLASS]: 'custom-button-wrapper', + }, + ]); // TODO: Replace with verify_gtl_no_records_text once #9691 is merged cy.contains('#miq-gtl-view .no-records-found', 'No records found'); // cy.verify_gtl_no_records_text(); @@ -291,15 +284,10 @@ describe('Automate Service Requests form operations: Services > Requests', () => cy.getFormInputFieldByIdAndType({ inputId: 'reasonText' }).type( 'Testing' ); - // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged - cy.contains( - `#main-content button[type="button"]`, - RESET_BUTTON_TEXT - ).click(); - // cy.getFormFooterButtonByTypeWithText({ - // buttonText: RESET_BUTTON_TEXT, - // buttonWrapperClass: 'custom-button-wrapper', - // }).click(); + cy.getFormFooterButtonByTypeWithText({ + buttonText: RESET_BUTTON_TEXT, + buttonWrapperClass: 'custom-button-wrapper', + }).click(); cy.getFormSelectFieldById({ selectId: 'selectedUser' }).should( 'have.value', SELECT_OPTION_ALL @@ -344,28 +332,18 @@ describe('Automate Service Requests form operations: Services > Requests', () => inputId: 'approvalStates-approved', inputType: 'checkbox', }).should('not.be.checked'); - // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged - cy.contains( - `#main-content button[type="submit"]`, - APPLY_BUTTON_TEXT - ).click(); - // cy.getFormFooterButtonByTypeWithText({ - // buttonText: APPLY_BUTTON_TEXT, - // buttonWrapperClass: 'custom-button-wrapper', - // buttonType: 'submit', - // }).click(); + cy.getFormFooterButtonByTypeWithText({ + buttonText: APPLY_BUTTON_TEXT, + buttonWrapperClass: 'custom-button-wrapper', + buttonType: 'submit', + }).click(); // TODO: Replace with verify_gtl_no_records_text once #9691 is merged cy.contains('#miq-gtl-view .no-records-found', 'No records found'); // cy.verify_gtl_no_records_text(); - // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged - cy.contains( - `#main-content button[type="button"]`, - RESET_BUTTON_TEXT - ).click(); - // cy.getFormFooterButtonByTypeWithText({ - // buttonText: RESET_BUTTON_TEXT, - // buttonWrapperClass: 'custom-button-wrapper', - // }).click(); + cy.getFormFooterButtonByTypeWithText({ + buttonText: RESET_BUTTON_TEXT, + buttonWrapperClass: 'custom-button-wrapper', + }).click(); cy.gtlGetRows([0]).then((data) => { expect(data.length).to.equal(1); }); @@ -373,55 +351,35 @@ describe('Automate Service Requests form operations: Services > Requests', () => cy.getFormSelectFieldById({ selectId: 'types' }).select( TYPE_VM_PROVISION ); - // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged - cy.contains( - `#main-content button[type="submit"]`, - APPLY_BUTTON_TEXT - ).click(); - // cy.getFormFooterButtonByTypeWithText({ - // buttonText: APPLY_BUTTON_TEXT, - // buttonWrapperClass: 'custom-button-wrapper', - // buttonType: 'submit', - // }).click(); + cy.getFormFooterButtonByTypeWithText({ + buttonText: APPLY_BUTTON_TEXT, + buttonWrapperClass: 'custom-button-wrapper', + buttonType: 'submit', + }).click(); // TODO: Replace with verify_gtl_no_records_text once #9691 is merged cy.contains('#miq-gtl-view .no-records-found', 'No records found'); // cy.verify_gtl_no_records_text(); - // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged - cy.contains( - `#main-content button[type="button"]`, - RESET_BUTTON_TEXT - ).click(); - // cy.getFormFooterButtonByTypeWithText({ - // buttonText: RESET_BUTTON_TEXT, - // buttonWrapperClass: 'custom-button-wrapper', - // }).click(); + cy.getFormFooterButtonByTypeWithText({ + buttonText: RESET_BUTTON_TEXT, + buttonWrapperClass: 'custom-button-wrapper', + }).click(); cy.gtlGetRows([0]).then((data) => { expect(data.length).to.equal(1); }); // Filter data with cy.getFormInputFieldByIdAndType({ inputId: 'reasonText' }).type('r@ndOm'); - // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged - cy.contains( - `#main-content button[type="submit"]`, - APPLY_BUTTON_TEXT - ).click(); - // cy.getFormFooterButtonByTypeWithText({ - // buttonText: APPLY_BUTTON_TEXT, - // buttonWrapperClass: 'custom-button-wrapper', - // buttonType: 'submit', - // }).click(); + cy.getFormFooterButtonByTypeWithText({ + buttonText: APPLY_BUTTON_TEXT, + buttonWrapperClass: 'custom-button-wrapper', + buttonType: 'submit', + }).click(); // TODO: Replace with verify_gtl_no_records_text once #9691 is merged cy.contains('#miq-gtl-view .no-records-found', 'No records found'); // cy.verify_gtl_no_records_text(); - // TODO: Replace with getFormFooterButtonByTypeWithText once #9689 is merged - cy.contains( - `#main-content button[type="button"]`, - RESET_BUTTON_TEXT - ).click(); - // cy.getFormFooterButtonByTypeWithText({ - // buttonText: RESET_BUTTON_TEXT, - // buttonWrapperClass: 'custom-button-wrapper', - // }).click(); + cy.getFormFooterButtonByTypeWithText({ + buttonText: RESET_BUTTON_TEXT, + buttonWrapperClass: 'custom-button-wrapper', + }).click(); cy.gtlGetRows([0]).then((data) => { expect(data.length).to.equal(1); }); From fc0c901ca825f94110315205440a342b53f8d4f1 Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Tue, 4 Nov 2025 17:01:40 +0530 Subject: [PATCH 4/8] Incorporating changes from #9701 --- .../Services/Requests/service_requests.cy.js | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/cypress/e2e/ui/Services/Requests/service_requests.cy.js b/cypress/e2e/ui/Services/Requests/service_requests.cy.js index e7d2df2b98f..7bc86ca44fd 100644 --- a/cypress/e2e/ui/Services/Requests/service_requests.cy.js +++ b/cypress/e2e/ui/Services/Requests/service_requests.cy.js @@ -226,7 +226,7 @@ describe('Automate Service Requests form operations: Services > Requests', () => [FIELD_CONFIG_KEYS.ID]: 'reasonText', }, ]); - cy.validateFormFooterButtons([ + cy.validateFormButtons([ { [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: APPLY_BUTTON_TEXT, [BUTTON_CONFIG_KEYS.BUTTON_TYPE]: 'submit', @@ -284,9 +284,8 @@ describe('Automate Service Requests form operations: Services > Requests', () => cy.getFormInputFieldByIdAndType({ inputId: 'reasonText' }).type( 'Testing' ); - cy.getFormFooterButtonByTypeWithText({ + cy.getFormButtonByTypeWithText({ buttonText: RESET_BUTTON_TEXT, - buttonWrapperClass: 'custom-button-wrapper', }).click(); cy.getFormSelectFieldById({ selectId: 'selectedUser' }).should( 'have.value', @@ -332,17 +331,15 @@ describe('Automate Service Requests form operations: Services > Requests', () => inputId: 'approvalStates-approved', inputType: 'checkbox', }).should('not.be.checked'); - cy.getFormFooterButtonByTypeWithText({ + cy.getFormButtonByTypeWithText({ buttonText: APPLY_BUTTON_TEXT, - buttonWrapperClass: 'custom-button-wrapper', buttonType: 'submit', }).click(); // TODO: Replace with verify_gtl_no_records_text once #9691 is merged cy.contains('#miq-gtl-view .no-records-found', 'No records found'); // cy.verify_gtl_no_records_text(); - cy.getFormFooterButtonByTypeWithText({ + cy.getFormButtonByTypeWithText({ buttonText: RESET_BUTTON_TEXT, - buttonWrapperClass: 'custom-button-wrapper', }).click(); cy.gtlGetRows([0]).then((data) => { expect(data.length).to.equal(1); @@ -351,34 +348,30 @@ describe('Automate Service Requests form operations: Services > Requests', () => cy.getFormSelectFieldById({ selectId: 'types' }).select( TYPE_VM_PROVISION ); - cy.getFormFooterButtonByTypeWithText({ + cy.getFormButtonByTypeWithText({ buttonText: APPLY_BUTTON_TEXT, - buttonWrapperClass: 'custom-button-wrapper', buttonType: 'submit', }).click(); // TODO: Replace with verify_gtl_no_records_text once #9691 is merged cy.contains('#miq-gtl-view .no-records-found', 'No records found'); // cy.verify_gtl_no_records_text(); - cy.getFormFooterButtonByTypeWithText({ + cy.getFormButtonByTypeWithText({ buttonText: RESET_BUTTON_TEXT, - buttonWrapperClass: 'custom-button-wrapper', }).click(); cy.gtlGetRows([0]).then((data) => { expect(data.length).to.equal(1); }); // Filter data with cy.getFormInputFieldByIdAndType({ inputId: 'reasonText' }).type('r@ndOm'); - cy.getFormFooterButtonByTypeWithText({ + cy.getFormButtonByTypeWithText({ buttonText: APPLY_BUTTON_TEXT, - buttonWrapperClass: 'custom-button-wrapper', buttonType: 'submit', }).click(); // TODO: Replace with verify_gtl_no_records_text once #9691 is merged cy.contains('#miq-gtl-view .no-records-found', 'No records found'); // cy.verify_gtl_no_records_text(); - cy.getFormFooterButtonByTypeWithText({ + cy.getFormButtonByTypeWithText({ buttonText: RESET_BUTTON_TEXT, - buttonWrapperClass: 'custom-button-wrapper', }).click(); cy.gtlGetRows([0]).then((data) => { expect(data.length).to.equal(1); From 1d8282bcfd36830762b14b64c5d0b6f534554459 Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Tue, 4 Nov 2025 17:38:02 +0530 Subject: [PATCH 5/8] Incorporating changes from #9691 --- .../Services/Requests/service_requests.cy.js | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/cypress/e2e/ui/Services/Requests/service_requests.cy.js b/cypress/e2e/ui/Services/Requests/service_requests.cy.js index 7bc86ca44fd..bf13ebb7d56 100644 --- a/cypress/e2e/ui/Services/Requests/service_requests.cy.js +++ b/cypress/e2e/ui/Services/Requests/service_requests.cy.js @@ -124,9 +124,7 @@ function dataSetup() { // Order cy.accordion(SERVICE_CATALOGS_ACCORDION); cy.selectAccordionItem([ALL_SERVICES_ACCORDION_ITEM]); - // TODO: Replace with clickTableRowByText once #9691 is merged - cy.contains('.miq-data-table table tbody tr td', CATALOG_ITEM_NAME).click(); - // cy.clickTableRowByText({ text: CATALOG_ITEM_NAME, columnIndex: 1 }); + cy.clickTableRowByText({ text: CATALOG_ITEM_NAME, columnIndex: 0 }); cy.interceptApi({ urlPattern: /\/catalog\/x_button\/\d+\?pressed=svc_catalog_provision/, alias: 'orderApi', @@ -230,16 +228,12 @@ describe('Automate Service Requests form operations: Services > Requests', () => { [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: APPLY_BUTTON_TEXT, [BUTTON_CONFIG_KEYS.BUTTON_TYPE]: 'submit', - [BUTTON_CONFIG_KEYS.BUTTON_WRAPPER_CLASS]: 'custom-button-wrapper', }, { [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: RESET_BUTTON_TEXT, - [BUTTON_CONFIG_KEYS.BUTTON_WRAPPER_CLASS]: 'custom-button-wrapper', }, ]); - // TODO: Replace with verify_gtl_no_records_text once #9691 is merged - cy.contains('#miq-gtl-view .no-records-found', 'No records found'); - // cy.verify_gtl_no_records_text(); + cy.expect_gtl_no_records_with_text(); }); }); @@ -335,9 +329,7 @@ describe('Automate Service Requests form operations: Services > Requests', () => buttonText: APPLY_BUTTON_TEXT, buttonType: 'submit', }).click(); - // TODO: Replace with verify_gtl_no_records_text once #9691 is merged - cy.contains('#miq-gtl-view .no-records-found', 'No records found'); - // cy.verify_gtl_no_records_text(); + cy.expect_gtl_no_records_with_text(); cy.getFormButtonByTypeWithText({ buttonText: RESET_BUTTON_TEXT, }).click(); @@ -352,9 +344,7 @@ describe('Automate Service Requests form operations: Services > Requests', () => buttonText: APPLY_BUTTON_TEXT, buttonType: 'submit', }).click(); - // TODO: Replace with verify_gtl_no_records_text once #9691 is merged - cy.contains('#miq-gtl-view .no-records-found', 'No records found'); - // cy.verify_gtl_no_records_text(); + cy.expect_gtl_no_records_with_text(); cy.getFormButtonByTypeWithText({ buttonText: RESET_BUTTON_TEXT, }).click(); @@ -367,9 +357,7 @@ describe('Automate Service Requests form operations: Services > Requests', () => buttonText: APPLY_BUTTON_TEXT, buttonType: 'submit', }).click(); - // TODO: Replace with verify_gtl_no_records_text once #9691 is merged - cy.contains('#miq-gtl-view .no-records-found', 'No records found'); - // cy.verify_gtl_no_records_text(); + cy.expect_gtl_no_records_with_text(); cy.getFormButtonByTypeWithText({ buttonText: RESET_BUTTON_TEXT, }).click(); From 5e78f1e9c555692b87eabfc19b0da268c89d91bc Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Thu, 6 Nov 2025 11:41:40 +0530 Subject: [PATCH 6/8] Switching to data setup using factories instead of UI-driven setup --- .../Services/Requests/service_requests.cy.js | 196 ++++++++---------- 1 file changed, 82 insertions(+), 114 deletions(-) diff --git a/cypress/e2e/ui/Services/Requests/service_requests.cy.js b/cypress/e2e/ui/Services/Requests/service_requests.cy.js index bf13ebb7d56..6ba1d7d24af 100644 --- a/cypress/e2e/ui/Services/Requests/service_requests.cy.js +++ b/cypress/e2e/ui/Services/Requests/service_requests.cy.js @@ -1,5 +1,4 @@ /* eslint-disable no-undef */ -import { flashClassMap } from '../../../../support/assertions/assertion_constants'; import { LABEL_CONFIG_KEYS, FIELD_CONFIG_KEYS, @@ -10,23 +9,6 @@ import { // Menu options const SERVICES_MENU_OPTION = 'Services'; const REQUESTS_MENU_OPTION = 'Requests'; -const CATALOGS_MENU_OPTION = 'Catalogs'; -const AUTOMATION_MENU_OPTION = 'Automation'; -const EMBEDDED_AUTOMATION_MENU_OPTION = 'Embedded Automate'; -const CUSTOMIZATION_MENU_OPTION = 'Customization'; - -// Accordion items -const SERVICE_DIALOGS_ACCORDION = 'Service Dialogs'; -const ALL_DIALOGS_ACCORDION_ITEM = 'All Dialogs'; -const SERVICE_CATALOGS_ACCORDION = 'Service Catalogs'; -const ALL_SERVICES_ACCORDION_ITEM = 'All Services'; -const CATALOG_ITEMS_ACCORDION_ITEM = 'Catalog Items'; -const ALL_CATALOG_ITEMS_ACCORDION_ITEM = 'All Catalog Items'; - -// Toolbar options -const TOOLBAR_CONFIGURATION = 'Configuration'; -const TOOLBAR_ADD_NEW_DIALOG = 'Add a new Dialog'; -const TOOLBAR_ADD_CATALOG_ITEM = 'Add a New Catalog Item'; // Field labels const FORM_HEADER = 'Requests'; @@ -37,10 +19,8 @@ const REQUEST_DATE_LABEL = 'Request Date'; const REASON_LABEL = 'Reason'; // Field values -const TEST_DIALOG_NAME = 'Test-Dialog-Name'; -const CATALOG_ITEM_NAME = 'Test-Catalog-Item'; const SELECT_OPTION_ALL = 'all'; -const TYPE_VM_PROVISION = 'VM Provision'; +const TYPE_VM_RECONFIGURE = 'vm_reconfigure'; const REQUEST_DATE_LAST_7_DAYS = '7'; // Checkbox labels @@ -51,99 +31,24 @@ const DENIED_LABEL = 'Denied'; // Button texts const APPLY_BUTTON_TEXT = 'Apply'; const RESET_BUTTON_TEXT = 'Reset'; -const SAVE_BUTTON_TEXT = 'Save'; -const ADD_BUTTON_TEXT = 'Add'; -const ORDER_BUTTON_TEXT = 'Order'; -const SUBMIT_BUTTON_TEXT = 'Submit'; -// Flash message text snippets -const FLASH_MESSAGE_ADD_SUCCESS = 'added'; -const FLASH_MESSAGE_SAVE_SUCCESS = 'saved'; -const FLASH_MESSAGE_SUBMITTED = 'submitted'; +/** + * Converts a JavaScript Date object to a database-compatible timestamp string. + * @param {Date} [dateObject=new Date()] - The date to convert. Defaults to current date/time. + * @returns {string} Formatted timestamp string in format: "YYYY-MM-DD HH:MM:SS.mmmmmm" like "2025-11-06 05:30:45.123000" + */ +function getDateStringInDbFormat(dateObject = new Date()) { + const year = dateObject.getFullYear(); + const month = String(dateObject.getMonth() + 1).padStart(2, '0'); + const day = String(dateObject.getDate()).padStart(2, '0'); + const hours = String(dateObject.getHours()).padStart(2, '0'); + const minutes = String(dateObject.getMinutes()).padStart(2, '0'); + const seconds = String(dateObject.getSeconds()).padStart(2, '0'); + const millis = String(dateObject.getMilliseconds()).padStart(3, '0'); + // JS only gives milliseconds (3 digits) so converting it to 6 digits like ".812169" + const micros = millis + '000'; -function dataSetup() { - // Adding a dialog - cy.menu( - AUTOMATION_MENU_OPTION, - EMBEDDED_AUTOMATION_MENU_OPTION, - CUSTOMIZATION_MENU_OPTION - ); - cy.accordion(SERVICE_DIALOGS_ACCORDION); - cy.selectAccordionItem([ALL_DIALOGS_ACCORDION_ITEM]); - cy.toolbar(TOOLBAR_CONFIGURATION, TOOLBAR_ADD_NEW_DIALOG); - cy.contains('ul.static-field-list li.static-field-item', 'Text Box').trigger( - 'mousedown', - { which: 1 } - ); - cy.get('.well-lg').trigger('mousemove').trigger('mouseup', { force: true }); - cy.getFormInputFieldByIdAndType({ inputId: 'name' }).type(TEST_DIALOG_NAME); - cy.interceptApi({ - urlPattern: '/api/service_dialogs', - alias: 'addDialogApi', - triggerFn: () => - cy - .contains('.pull-right button', SAVE_BUTTON_TEXT) - .should('be.enabled') - .click(), - onApiResponse: (interception) => - expect(interception.response.statusCode).to.equal(200), - }); - cy.expect_flash(flashClassMap.success, FLASH_MESSAGE_SAVE_SUCCESS); - // Adding a catalog item - cy.menu(SERVICES_MENU_OPTION, CATALOGS_MENU_OPTION); - cy.accordion(CATALOG_ITEMS_ACCORDION_ITEM); - cy.selectAccordionItem([ALL_CATALOG_ITEMS_ACCORDION_ITEM]); - cy.toolbar(TOOLBAR_CONFIGURATION, TOOLBAR_ADD_CATALOG_ITEM); - cy.contains('.form-group button[data-id="st_prov_type"]', 'Choose').click(); - cy.interceptApi({ - urlPattern: '/catalog/atomic_form_field_changed/new?st_prov_type=generic', - alias: 'getGenericCatalogItemTypeDetailsApi', - triggerFn: () => - cy.contains('.form-group ul.dropdown-menu li a', 'Generic').click(), - onApiResponse: (interception) => - expect(interception.response.statusCode).to.equal(200), - }); - cy.get('input#name').type(CATALOG_ITEM_NAME); - cy.contains('.form-group button[data-id="catalog_id"]', 'Unassigned').click(); - cy.contains( - '.form-group ul.dropdown-menu li a', - 'My Company/My Catalog' - ).click(); - cy.contains('.form-group button[data-id="dialog_id"]', 'No Dialog').click(); - cy.contains(' .form-group ul.dropdown-menu li a', TEST_DIALOG_NAME).click(); - cy.get('input#display').check(); - cy.interceptApi({ - urlPattern: '/catalog/servicetemplate_edit?button=add', - alias: 'addCatalogItemApi', - triggerFn: () => - cy.contains('#form_buttons_div button', ADD_BUTTON_TEXT).click(), - onApiResponse: (interception) => - expect(interception.response.statusCode).to.equal(200), - }); - cy.expect_flash(flashClassMap.success, FLASH_MESSAGE_ADD_SUCCESS); - // Order - cy.accordion(SERVICE_CATALOGS_ACCORDION); - cy.selectAccordionItem([ALL_SERVICES_ACCORDION_ITEM]); - cy.clickTableRowByText({ text: CATALOG_ITEM_NAME, columnIndex: 0 }); - cy.interceptApi({ - urlPattern: /\/catalog\/x_button\/\d+\?pressed=svc_catalog_provision/, - alias: 'orderApi', - triggerFn: () => - cy - .contains(`#main-content button[type="button"]`, ORDER_BUTTON_TEXT) - .click(), - onApiResponse: (interception) => - expect(interception.response.statusCode).to.equal(200), - }); - cy.interceptApi({ - urlPattern: /\/api\/service_catalogs\/\d+\/service_templates\/\d+$/, - alias: 'submitOrderApi', - triggerFn: () => - cy.contains('.pull-right button', SUBMIT_BUTTON_TEXT).click(), - onApiResponse: (interception) => - expect(interception.response.statusCode).to.equal(200), - }); - cy.expect_flash(flashClassMap.success, FLASH_MESSAGE_SUBMITTED); + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${micros}`; } describe('Automate Service Requests form operations: Services > Requests', () => { @@ -239,8 +144,71 @@ describe('Automate Service Requests form operations: Services > Requests', () => describe('Validate button click actions', () => { beforeEach(() => { - // TODO: Replace with better setup approach - dataSetup(); + const tenDaysAgo = new Date( + new Date().getTime() - 10 * 24 * 60 * 60 * 1000 + ); + + cy.appFactories([ + [ + 'create', + 'service_template_provision_request', + { + description: 'Cypress mock data for approval state: Approved', + approval_state: 'approved', + type: 'ServiceTemplateProvisionRequest', + fulfilled_on: getDateStringInDbFormat(), + request_type: 'clone_to_service', + request_state: 'finished', + message: 'Cypress mock data for approval state: Approved', + status: 'Ok', + }, + ], + [ + 'create', + 'service_template_provision_request', + { + description: 'Cypress mock data for approval state: Denied', + approval_state: 'denied', + type: 'ServiceTemplateProvisionRequest', + fulfilled_on: getDateStringInDbFormat(), + request_type: 'clone_to_service', + request_state: 'finished', + message: 'Cypress mock data for approval state: Denied', + status: 'Ok', + }, + ], + [ + 'create', + 'vm_reconfigure_request', + { + description: + 'Cypress mock data for approval state: Pending-Approval', + approval_state: 'pending_approval', + type: 'VmReconfigureRequest', + request_type: TYPE_VM_RECONFIGURE, + request_state: 'pending', + message: 'Cypress mock data for approval state: Pending-Approval', + status: 'Ok', + }, + ], + // This record is used to filter data for requests made in the last 30 days + [ + 'create', + 'service_template_provision_request', + { + description: + 'Cypress mock data for request made in the last 30 days', + approval_state: 'approved', + type: 'ServiceTemplateProvisionRequest', + created_on: getDateStringInDbFormat(tenDaysAgo), + fulfilled_on: getDateStringInDbFormat(), + request_type: 'clone_to_service', + request_state: 'finished', + message: 'Cypress mock data for request made in the last 30 days', + status: 'Ok', + }, + ], + ]); }); it('Validate reset & apply buttons', () => { From b8fc5d5ff57ad27e424b3634ed5e8899281bcb6d Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Thu, 6 Nov 2025 11:49:43 +0530 Subject: [PATCH 7/8] Enhanced test with additional mock data --- .../Services/Requests/service_requests.cy.js | 131 +++++++++++++++--- 1 file changed, 110 insertions(+), 21 deletions(-) diff --git a/cypress/e2e/ui/Services/Requests/service_requests.cy.js b/cypress/e2e/ui/Services/Requests/service_requests.cy.js index 6ba1d7d24af..23e22936d0d 100644 --- a/cypress/e2e/ui/Services/Requests/service_requests.cy.js +++ b/cypress/e2e/ui/Services/Requests/service_requests.cy.js @@ -22,6 +22,7 @@ const REASON_LABEL = 'Reason'; const SELECT_OPTION_ALL = 'all'; const TYPE_VM_RECONFIGURE = 'vm_reconfigure'; const REQUEST_DATE_LAST_7_DAYS = '7'; +const REQUEST_DATE_LAST_30_DAYS = '30'; // Checkbox labels const PENDING_APPROVAL_LABEL = 'Pending Approval'; @@ -51,17 +52,26 @@ function getDateStringInDbFormat(dateObject = new Date()) { return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${micros}`; } +/** + * Function to do assertions on table data + */ +function assertGtlData({ columnIndex, expectedRowCount, rowContainsText }) { + cy.gtlGetRows([columnIndex]).then((data) => { + expect(data.length).to.equal(expectedRowCount); + if (rowContainsText) { + expect(data[0][0]).to.include(rowContainsText); + } + }); +} + describe('Automate Service Requests form operations: Services > Requests', () => { beforeEach(() => { cy.login(); }); describe('Verify form fields', () => { - beforeEach(() => { - cy.menu(SERVICES_MENU_OPTION, REQUESTS_MENU_OPTION); - }); - it('Verify form’s initial UI state', () => { + cy.menu(SERVICES_MENU_OPTION, REQUESTS_MENU_OPTION); cy.contains('#main-content h1', FORM_HEADER); cy.getFormLegendByText({ legendText: APPROVAL_STATE_HEADER }); cy.validateFormLabels([ @@ -208,13 +218,21 @@ describe('Automate Service Requests form operations: Services > Requests', () => status: 'Ok', }, ], - ]); + ]).then((createdRequestsData) => { + expect(createdRequestsData.length).to.equal(4); + cy.menu(SERVICES_MENU_OPTION, REQUESTS_MENU_OPTION); + }); }); it('Validate reset & apply buttons', () => { /* Reset */ - cy.getFormSelectFieldById({ selectId: 'selectedUser' }).select( - 'Administrator' + cy.getFormSelectFieldById({ selectId: 'selectedUser' }).then( + (selectElement) => { + const value = [...selectElement[0].options].find( + (option) => option.value !== SELECT_OPTION_ALL + ).value; + cy.wrap(selectElement).select(value); + } ); cy.getFormLabelByForAttribute({ forValue: 'approvalStates-pending_approval', @@ -238,7 +256,7 @@ describe('Automate Service Requests form operations: Services > Requests', () => inputType: 'checkbox', }).should('not.be.checked'); cy.getFormSelectFieldById({ selectId: 'types' }).select( - TYPE_VM_PROVISION + TYPE_VM_RECONFIGURE ); cy.getFormSelectFieldById({ selectId: 'selectedPeriod' }).select( 'Last 30 Days' @@ -278,7 +296,7 @@ describe('Automate Service Requests form operations: Services > Requests', () => '' ); /* Apply */ - // Filter data with approval state + // Filter data with approval state: Denied cy.getFormLabelByForAttribute({ forValue: 'approvalStates-pending_approval', }).click(); @@ -297,29 +315,102 @@ describe('Automate Service Requests form operations: Services > Requests', () => buttonText: APPLY_BUTTON_TEXT, buttonType: 'submit', }).click(); - cy.expect_gtl_no_records_with_text(); + assertGtlData({ + columnIndex: 7, + expectedRowCount: 1, + rowContainsText: DENIED_LABEL, + }); + cy.getFormButtonByTypeWithText({ + buttonText: RESET_BUTTON_TEXT, + }).click(); + assertGtlData({ columnIndex: 0, expectedRowCount: 3 }); + // Filter data with approval state: Approved + cy.getFormLabelByForAttribute({ + forValue: 'approvalStates-pending_approval', + }).click(); + cy.getFormInputFieldByIdAndType({ + inputId: 'approvalStates-pending_approval', + inputType: 'checkbox', + }).should('not.be.checked'); + cy.getFormLabelByForAttribute({ + forValue: 'approvalStates-denied', + }).click(); + cy.getFormInputFieldByIdAndType({ + inputId: 'approvalStates-denied', + inputType: 'checkbox', + }).should('not.be.checked'); + cy.getFormButtonByTypeWithText({ + buttonText: APPLY_BUTTON_TEXT, + buttonType: 'submit', + }).click(); + assertGtlData({ + columnIndex: 7, + expectedRowCount: 1, + rowContainsText: APPROVED_LABEL, + }); cy.getFormButtonByTypeWithText({ buttonText: RESET_BUTTON_TEXT, }).click(); - cy.gtlGetRows([0]).then((data) => { - expect(data.length).to.equal(1); + assertGtlData({ columnIndex: 0, expectedRowCount: 3 }); + // Filter data with approval state: Pending approval + cy.getFormLabelByForAttribute({ + forValue: 'approvalStates-approved', + }).click(); + cy.getFormInputFieldByIdAndType({ + inputId: 'approvalStates-approved', + inputType: 'checkbox', + }).should('not.be.checked'); + cy.getFormLabelByForAttribute({ + forValue: 'approvalStates-denied', + }).click(); + cy.getFormInputFieldByIdAndType({ + inputId: 'approvalStates-denied', + inputType: 'checkbox', + }).should('not.be.checked'); + cy.getFormButtonByTypeWithText({ + buttonText: APPLY_BUTTON_TEXT, + buttonType: 'submit', + }).click(); + assertGtlData({ + columnIndex: 7, + expectedRowCount: 1, + rowContainsText: PENDING_APPROVAL_LABEL, }); - // Filter data with type + cy.getFormButtonByTypeWithText({ + buttonText: RESET_BUTTON_TEXT, + }).click(); + assertGtlData({ columnIndex: 0, expectedRowCount: 3 }); + // Filter data with type: VM Reconfigure cy.getFormSelectFieldById({ selectId: 'types' }).select( - TYPE_VM_PROVISION + TYPE_VM_RECONFIGURE ); cy.getFormButtonByTypeWithText({ buttonText: APPLY_BUTTON_TEXT, buttonType: 'submit', }).click(); - cy.expect_gtl_no_records_with_text(); + assertGtlData({ + columnIndex: 4, + expectedRowCount: 1, + rowContainsText: 'VM Reconfigure', + }); cy.getFormButtonByTypeWithText({ buttonText: RESET_BUTTON_TEXT, }).click(); - cy.gtlGetRows([0]).then((data) => { - expect(data.length).to.equal(1); + assertGtlData({ columnIndex: 0, expectedRowCount: 3 }); + // Filter data with request date: last 30 days + cy.getFormSelectFieldById({ selectId: 'selectedPeriod' }).select( + REQUEST_DATE_LAST_30_DAYS + ); + cy.getFormButtonByTypeWithText({ + buttonText: APPLY_BUTTON_TEXT, + buttonType: 'submit', + }).click(); + assertGtlData({ + columnIndex: 6, + expectedRowCount: 4, + rowContainsText: 'request made in the last 30 days', }); - // Filter data with + // Filter data with reason text cy.getFormInputFieldByIdAndType({ inputId: 'reasonText' }).type('r@ndOm'); cy.getFormButtonByTypeWithText({ buttonText: APPLY_BUTTON_TEXT, @@ -329,9 +420,7 @@ describe('Automate Service Requests form operations: Services > Requests', () => cy.getFormButtonByTypeWithText({ buttonText: RESET_BUTTON_TEXT, }).click(); - cy.gtlGetRows([0]).then((data) => { - expect(data.length).to.equal(1); - }); + assertGtlData({ columnIndex: 0, expectedRowCount: 3 }); }); afterEach(() => { From 260fb76e25638e62e1a1d8bbcc47969a9110aa69 Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Wed, 19 Nov 2025 10:48:15 +0530 Subject: [PATCH 8/8] Using moment for creating date-string --- .../Services/Requests/service_requests.cy.js | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/cypress/e2e/ui/Services/Requests/service_requests.cy.js b/cypress/e2e/ui/Services/Requests/service_requests.cy.js index 23e22936d0d..655e55bfa43 100644 --- a/cypress/e2e/ui/Services/Requests/service_requests.cy.js +++ b/cypress/e2e/ui/Services/Requests/service_requests.cy.js @@ -1,4 +1,5 @@ /* eslint-disable no-undef */ +import moment from 'moment'; import { LABEL_CONFIG_KEYS, FIELD_CONFIG_KEYS, @@ -34,26 +35,7 @@ const APPLY_BUTTON_TEXT = 'Apply'; const RESET_BUTTON_TEXT = 'Reset'; /** - * Converts a JavaScript Date object to a database-compatible timestamp string. - * @param {Date} [dateObject=new Date()] - The date to convert. Defaults to current date/time. - * @returns {string} Formatted timestamp string in format: "YYYY-MM-DD HH:MM:SS.mmmmmm" like "2025-11-06 05:30:45.123000" - */ -function getDateStringInDbFormat(dateObject = new Date()) { - const year = dateObject.getFullYear(); - const month = String(dateObject.getMonth() + 1).padStart(2, '0'); - const day = String(dateObject.getDate()).padStart(2, '0'); - const hours = String(dateObject.getHours()).padStart(2, '0'); - const minutes = String(dateObject.getMinutes()).padStart(2, '0'); - const seconds = String(dateObject.getSeconds()).padStart(2, '0'); - const millis = String(dateObject.getMilliseconds()).padStart(3, '0'); - // JS only gives milliseconds (3 digits) so converting it to 6 digits like ".812169" - const micros = millis + '000'; - - return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${micros}`; -} - -/** - * Function to do assertions on table data + * Function to do assertions on table data */ function assertGtlData({ columnIndex, expectedRowCount, rowContainsText }) { cy.gtlGetRows([columnIndex]).then((data) => { @@ -154,9 +136,7 @@ describe('Automate Service Requests form operations: Services > Requests', () => describe('Validate button click actions', () => { beforeEach(() => { - const tenDaysAgo = new Date( - new Date().getTime() - 10 * 24 * 60 * 60 * 1000 - ); + const today = moment().format(); cy.appFactories([ [ @@ -166,7 +146,7 @@ describe('Automate Service Requests form operations: Services > Requests', () => description: 'Cypress mock data for approval state: Approved', approval_state: 'approved', type: 'ServiceTemplateProvisionRequest', - fulfilled_on: getDateStringInDbFormat(), + fulfilled_on: today, request_type: 'clone_to_service', request_state: 'finished', message: 'Cypress mock data for approval state: Approved', @@ -180,7 +160,7 @@ describe('Automate Service Requests form operations: Services > Requests', () => description: 'Cypress mock data for approval state: Denied', approval_state: 'denied', type: 'ServiceTemplateProvisionRequest', - fulfilled_on: getDateStringInDbFormat(), + fulfilled_on: today, request_type: 'clone_to_service', request_state: 'finished', message: 'Cypress mock data for approval state: Denied', @@ -210,8 +190,8 @@ describe('Automate Service Requests form operations: Services > Requests', () => 'Cypress mock data for request made in the last 30 days', approval_state: 'approved', type: 'ServiceTemplateProvisionRequest', - created_on: getDateStringInDbFormat(tenDaysAgo), - fulfilled_on: getDateStringInDbFormat(), + created_on: moment().subtract(10, 'days'), + fulfilled_on: today, request_type: 'clone_to_service', request_state: 'finished', message: 'Cypress mock data for request made in the last 30 days',