From 4f9899974074c32959d1963f3d544805cc45435e Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Mon, 29 Sep 2025 13:08:09 +0530 Subject: [PATCH 1/5] Added command to validate form labels --- .../commands/constants/command_constants.js | 35 +++++++++ .../form_elements_validation_commands.js | 73 +++++++++++++++++++ cypress/support/e2e.js | 1 + 3 files changed, 109 insertions(+) create mode 100644 cypress/support/commands/constants/command_constants.js create mode 100644 cypress/support/commands/form_elements_validation_commands.js diff --git a/cypress/support/commands/constants/command_constants.js b/cypress/support/commands/constants/command_constants.js new file mode 100644 index 00000000000..804a316ce41 --- /dev/null +++ b/cypress/support/commands/constants/command_constants.js @@ -0,0 +1,35 @@ +/* + * Form elements validation constants + * ========================================================== + */ + +// Form label configuration keys +export const LABEL_CONFIG_KEYS = { + FOR_VALUE: 'forValue', + EXPECTED_TEXT: 'expectedText', +}; + +// Form user input field configuration keys +export const FIELD_CONFIG_KEYS = { + ID: 'id', + FIELD_TYPE: 'fieldType', + INPUT_FIELD_TYPE: 'inputFieldType', + SHOULD_BE_DISABLED: 'shouldBeDisabled', + EXPECTED_VALUE: 'expectedValue', +}; + +// Form field types +export const FIELD_TYPES = { + INPUT: 'input', + SELECT: 'select', + TEXTAREA: 'textarea', +}; + +// Form button configuration keys +export const BUTTON_CONFIG_KEYS = { + BUTTON_TEXT: 'buttonText', + BUTTON_TYPE: 'buttonType', + SHOULD_BE_DISABLED: 'shouldBeDisabled', +}; + +/* ========================================================== */ diff --git a/cypress/support/commands/form_elements_validation_commands.js b/cypress/support/commands/form_elements_validation_commands.js new file mode 100644 index 00000000000..e361598891b --- /dev/null +++ b/cypress/support/commands/form_elements_validation_commands.js @@ -0,0 +1,73 @@ +// TODO: Use aliased import(@cypress-dir) once #9631 is merged +import { LABEL_CONFIG_KEYS } from './constants/command_constants.js'; + +/** + * Helper function to validate that config objects only contain valid keys + * + * @param {Object} config - The configuration object to validate + * @param {Object} validKeysObject - The object containing valid keys (e.g., LABEL_CONFIG_KEYS) + * @param {string} configType - The type of configuration being validated (for error messages) + */ +const validateConfigKeys = (config, validKeysObject, configType) => { + const validKeys = Object.values(validKeysObject); + + Object.keys(config).forEach(key => { + if (!validKeys.includes(key)) { + cy.logAndThrowError( + `Unknown key "${key}" in ${configType} config. Valid keys are: ${validKeys.join(', ')}` + ); + } + }); +}; + +/** + * Validates form field labels based on provided configurations + * + * @param {Array} labelConfigs - Array of label configuration objects + * @param {string} labelConfigs[].forValue - The 'for' attribute value of the label + * @param {string} [labelConfigs[].expectedText] - The expected text content of the label + * + * Example: + * cy.validateFormLabels([ + * { [LABEL_CONFIG_KEYS.FOR_VALUE]: 'name', [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: 'Name' }, + * { [LABEL_CONFIG_KEYS.FOR_VALUE]: 'email', [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: 'Email Address' } + * ]); + * + * Or using regular object keys: + * cy.validateFormLabels([ + * { forValue: 'name', expectedText: 'Name' }, + * { forValue: 'email', expectedText: 'Email Address' } + * ]); + * + * Both approaches work but using config-keys object(LABEL_CONFIG_KEYS) is recommended to avoid typos and unknown keys + */ +Cypress.Commands.add('validateFormLabels', (labelConfigs) => { + if (!Array.isArray(labelConfigs)) { + cy.logAndThrowError('labelConfigs must be an array'); + } + + if (!labelConfigs.length) { + cy.logAndThrowError('labelConfigs array cannot be empty'); + } + + labelConfigs.forEach((config) => { + validateConfigKeys(config, LABEL_CONFIG_KEYS, 'label'); + + const forValue = config[LABEL_CONFIG_KEYS.FOR_VALUE]; + const expectedText = config[LABEL_CONFIG_KEYS.EXPECTED_TEXT]; + + if (!forValue) { + cy.logAndThrowError( + `${LABEL_CONFIG_KEYS.FOR_VALUE} is required for each label config` + ); + } + + const labelCheck = cy + .getFormLabelByForAttribute({ forValue }) + .should('be.visible'); + + if (expectedText) { + labelCheck.and('contain.text', expectedText); + } + }); +}); diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js index 618102223c9..a5a8e14d151 100644 --- a/cypress/support/e2e.js +++ b/cypress/support/e2e.js @@ -44,6 +44,7 @@ import './commands/api_commands.js'; import './commands/custom_logging_commands.js'; import './commands/element_selectors.js'; import './commands/explorer.js'; +import './commands/form_elements_validation_commands.js'; import './commands/gtl.js'; import './commands/login.js'; import './commands/menu.js'; From 948fb0f91d01c8c251f6071845b3f66002e91858 Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Mon, 29 Sep 2025 13:09:58 +0530 Subject: [PATCH 2/5] Added command to validate form user-input fields --- .../form_elements_validation_commands.js | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/cypress/support/commands/form_elements_validation_commands.js b/cypress/support/commands/form_elements_validation_commands.js index e361598891b..c0d17606bb7 100644 --- a/cypress/support/commands/form_elements_validation_commands.js +++ b/cypress/support/commands/form_elements_validation_commands.js @@ -71,3 +71,115 @@ Cypress.Commands.add('validateFormLabels', (labelConfigs) => { } }); }); + +/** + * Validates form input fields based on provided configurations + * + * @param {Array} fieldConfigs - Array of field configuration objects + * @param {string} fieldConfigs[].id - The ID of the form field + * @param {string} [fieldConfigs[].fieldType='input'] - The type of field ('input', 'select', 'textarea') + * @param {string} [fieldConfigs[].inputFieldType='text'] - The type of input field ('text', 'password', 'number') + * @param {boolean} [fieldConfigs[].shouldBeDisabled=false] - Whether the field should be disabled + * @param {string} [fieldConfigs[].expectedValue] - The expected value of the field + * + * Example: + * cy.validateFormFields([ + * { [FIELD_CONFIG_KEYS.ID]: 'name', [FIELD_CONFIG_KEYS.SHOULD_BE_DISABLED]: true }, + * { [FIELD_CONFIG_KEYS.ID]: 'email', [FIELD_CONFIG_KEYS.INPUT_FIELD_TYPE]: 'email' }, + * { + * [FIELD_CONFIG_KEYS.ID]: 'role', + * [FIELD_CONFIG_KEYS.FIELD_TYPE]: FIELD_TYPES.SELECT, + * [FIELD_CONFIG_KEYS.EXPECTED_VALUE]: 'admin' + * } + * ]); + * + * Or using regular object keys: + * cy.validateFormFields([ + * { id: 'name', shouldBeDisabled: true }, + * { id: 'email' }, + * { id: 'role', fieldType: 'select', expectedValue: 'admin' } + * ]); + * + * Both approaches work but using config-keys object(FIELD_CONFIG_KEYS) is recommended to avoid typos and unknown keys + */ +Cypress.Commands.add('validateFormFields', (fieldConfigs) => { + if (!Array.isArray(fieldConfigs)) { + cy.logAndThrowError('fieldConfigs must be an array'); + } + + if (!fieldConfigs.length) { + cy.logAndThrowError('fieldConfigs array cannot be empty'); + } + + fieldConfigs.forEach((config) => { + validateConfigKeys(config, FIELD_CONFIG_KEYS, 'field'); + + const id = config[FIELD_CONFIG_KEYS.ID]; + const fieldType = config[FIELD_CONFIG_KEYS.FIELD_TYPE] || FIELD_TYPES.INPUT; + const inputFieldType = config[FIELD_CONFIG_KEYS.INPUT_FIELD_TYPE] || 'text'; + const shouldBeDisabled = + config[FIELD_CONFIG_KEYS.SHOULD_BE_DISABLED] || false; + const expectedValue = config[FIELD_CONFIG_KEYS.EXPECTED_VALUE]; + + if (!id) { + cy.logAndThrowError( + `${FIELD_CONFIG_KEYS.ID} is required for each field config` + ); + } + + // Check field based on type + switch (fieldType) { + case FIELD_TYPES.INPUT: + cy.getFormInputFieldByIdAndType({ + inputId: id, + inputType: inputFieldType, + }) + .should('be.visible') + .then((field) => { + if (shouldBeDisabled) { + expect(field).to.be.disabled; + } else { + expect(field).to.not.be.disabled; + } + + if (expectedValue) { + cy.wrap(field).should('have.value', expectedValue); + } + }); + break; + case FIELD_TYPES.SELECT: + cy.getFormSelectFieldById({ selectId: id }) + .should('be.visible') + .then((field) => { + if (shouldBeDisabled) { + expect(field).to.be.disabled; + } else { + expect(field).to.not.be.disabled; + } + + if (expectedValue) { + cy.wrap(field).should('have.value', expectedValue); + } + }); + break; + case FIELD_TYPES.TEXTAREA: + cy.getFormTextareaById({ textareaId: id }) + .should('be.visible') + .then((field) => { + if (shouldBeDisabled) { + expect(field).to.be.disabled; + } else { + expect(field).to.not.be.disabled; + } + + if (expectedValue) { + cy.wrap(field).should('have.value', expectedValue); + } + }); + break; + + default: + cy.logAndThrowError(`Unsupported field type: ${fieldType}`); + } + }); +}); From 63ca416395b3cdca5af1e4810076f76e53b4f28b Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Mon, 29 Sep 2025 13:12:40 +0530 Subject: [PATCH 3/5] Added command to validate form footer buttons --- .../form_elements_validation_commands.js | 77 ++++++++++++++++++- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/cypress/support/commands/form_elements_validation_commands.js b/cypress/support/commands/form_elements_validation_commands.js index c0d17606bb7..db71ae4b75d 100644 --- a/cypress/support/commands/form_elements_validation_commands.js +++ b/cypress/support/commands/form_elements_validation_commands.js @@ -1,5 +1,10 @@ // TODO: Use aliased import(@cypress-dir) once #9631 is merged -import { LABEL_CONFIG_KEYS } from './constants/command_constants.js'; +import { + LABEL_CONFIG_KEYS, + FIELD_CONFIG_KEYS, + FIELD_TYPES, + BUTTON_CONFIG_KEYS, +} from './constants/command_constants.js'; /** * Helper function to validate that config objects only contain valid keys @@ -10,11 +15,13 @@ import { LABEL_CONFIG_KEYS } from './constants/command_constants.js'; */ const validateConfigKeys = (config, validKeysObject, configType) => { const validKeys = Object.values(validKeysObject); - - Object.keys(config).forEach(key => { + + Object.keys(config).forEach((key) => { if (!validKeys.includes(key)) { cy.logAndThrowError( - `Unknown key "${key}" in ${configType} config. Valid keys are: ${validKeys.join(', ')}` + `Unknown key "${key}" in ${configType} config. Valid keys are: ${validKeys.join( + ', ' + )}` ); } }); @@ -183,3 +190,65 @@ Cypress.Commands.add('validateFormFields', (fieldConfigs) => { } }); }); + +/** + * Validates form buttons based on provided configurations + * + * @param {Array} buttonConfigs - Array of button configuration objects + * @param {string} buttonConfigs[].buttonText - The text of the button + * @param {string} [buttonConfigs[].buttonType='button'] - The type of button (e.g., 'submit', 'reset') + * @param {boolean} [buttonConfigs[].shouldBeDisabled=false] - Whether the button should be disabled + * + * Example: + * cy.validateFormFooterButtons([ + * { [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: 'Cancel' }, + * { [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: 'Reset', [BUTTON_CONFIG_KEYS.SHOULD_BE_DISABLED]: true }, + * { [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: 'Submit', [BUTTON_CONFIG_KEYS.BUTTON_TYPE]: 'submit' } + * ]); + * + * Or using regular object keys: + * cy.validateFormFooterButtons([ + * { buttonText: 'Cancel' }, + * { buttonText: 'Reset', shouldBeDisabled: true }, + * { buttonText: 'Submit', buttonType: 'submit' } + * ]); + * + * Both approaches work but using config-keys object(BUTTON_CONFIG_KEYS) is recommended to avoid typos and unknown keys + */ +Cypress.Commands.add('validateFormFooterButtons', (buttonConfigs) => { + if (!Array.isArray(buttonConfigs)) { + cy.logAndThrowError('buttonConfigs must be an array'); + } + + if (!buttonConfigs.length) { + cy.logAndThrowError('buttonConfigs array cannot be empty'); + } + + buttonConfigs.forEach((config) => { + validateConfigKeys(config, BUTTON_CONFIG_KEYS, 'button'); + + const buttonText = config[BUTTON_CONFIG_KEYS.BUTTON_TEXT]; + const buttonType = config[BUTTON_CONFIG_KEYS.BUTTON_TYPE] || 'button'; + const shouldBeDisabled = + config[BUTTON_CONFIG_KEYS.SHOULD_BE_DISABLED] || false; + + if (!buttonText) { + cy.logAndThrowError( + `${BUTTON_CONFIG_KEYS.BUTTON_TEXT} is required for each button config` + ); + } + + const buttonCheck = cy + .getFormFooterButtonByTypeWithText({ + buttonText, + buttonType, + }) + .should('be.visible'); + + if (shouldBeDisabled) { + buttonCheck.and('be.disabled'); + } else { + buttonCheck.and('be.enabled'); + } + }); +}); From 594d4907345daa0f201bc4425ea52280c0617b03 Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Mon, 29 Sep 2025 13:25:12 +0530 Subject: [PATCH 4/5] Updated readme with form_elements_validation_commands info --- cypress/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cypress/README.md b/cypress/README.md index 1586ea53f3b..d23a9bda0b0 100644 --- a/cypress/README.md +++ b/cypress/README.md @@ -80,6 +80,12 @@ ManageIQ implements the following cypress extensions: * `cy.getFormSelectFieldById({ selectId })` - retrieves a form select field by its ID. `selectId` is the ID of the select field. e.g. `cy.getFormSelectFieldById({selectId: 'select-scan-limit'});` * `cy.getFormTextareaById({ textareaId })` - retrieves a form textarea field by its ID. `textareaId` is the ID of the textarea field. e.g. `cy.getFormTextareaById({textareaId: 'default.auth_key'});` +##### form_elements_validation_commands + +* `cy.validateFormLabels(labelConfigs)` - validates form field labels based on provided configurations. `labelConfigs` is an array of label configuration objects with properties: `forValue` (required) - the 'for' attribute value of the label, `expectedText` (optional) - the expected text content of the label. e.g. `cy.validateFormLabels([{ forValue: 'name', expectedText: 'Name' }, { forValue: 'email', expectedText: 'Email Address' }]);` or using constants: `cy.validateFormLabels([{ [LABEL_CONFIG_KEYS.FOR_VALUE]: 'name', [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: 'Name' }]);` +* `cy.validateFormFields(fieldConfigs)` - validates form input fields based on provided configurations. `fieldConfigs` is an array of field configuration objects with properties: `id` (required) - the ID of the form field, `fieldType` (optional, default: 'input') - the type of field ('input', 'select', 'textarea'), `inputFieldType` (optional, default: 'text') - the type of input field ('text', 'password', 'number'), `shouldBeDisabled` (optional, default: false) - whether the field should be disabled, `expectedValue` (optional) - the expected value of the field. e.g. `cy.validateFormFields([{ id: 'name', shouldBeDisabled: true }, { id: 'role', fieldType: 'select', expectedValue: 'admin' }]);` or using constants: `cy.validateFormFields([{ [FIELD_CONFIG_KEYS.ID]: 'email', [FIELD_CONFIG_KEYS.INPUT_FIELD_TYPE]: 'email' }, { [FIELD_CONFIG_KEYS.ID]: 'name', [FIELD_CONFIG_KEYS.SHOULD_BE_DISABLED]: true }]);` +* `cy.validateFormFooterButtons(buttonConfigs)` - validates form buttons based on provided configurations. `buttonConfigs` is an array of button configuration objects with properties: `buttonText` (required) - the text of the button, `buttonType` (optional, default: 'button') - the type of button (e.g., 'submit', 'reset'), `shouldBeDisabled` (optional, default: false) - whether the button should be disabled. e.g. `cy.validateFormFooterButtons([{ buttonText: 'Cancel' }, { buttonText: 'Submit', buttonType: 'submit', shouldBeDisabled: true }]);` or using constants: `cy.validateFormFooterButtons([{ [BUTTON_CONFIG_KEYS.TEXT]: 'Cancel' }]);` + #### Assertions * `cy.expect_explorer_title(title)` - check that the title on an explorer screen matches the provided title. `title`: String for the title. From 08b91c0ee658c272ce6a86205b6602b77698aae7 Mon Sep 17 00:00:00 2001 From: asirvadAbrahamVarghese Date: Mon, 29 Sep 2025 13:26:35 +0530 Subject: [PATCH 5/5] C&U gap collection form elements validation via commands --- .../c_and_u_gap_collection.cy.js | 67 +++++++++++-------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/cypress/e2e/ui/Settings/Application-Settings/c_and_u_gap_collection.cy.js b/cypress/e2e/ui/Settings/Application-Settings/c_and_u_gap_collection.cy.js index 4c102d23af5..89d67d7fc20 100644 --- a/cypress/e2e/ui/Settings/Application-Settings/c_and_u_gap_collection.cy.js +++ b/cypress/e2e/ui/Settings/Application-Settings/c_and_u_gap_collection.cy.js @@ -1,5 +1,11 @@ /* eslint-disable no-undef */ +// TODO: Use aliased import(@cypress-dir) once #9631 is merged import { flashClassMap } from '../../../../support/assertions/assertion_constants'; +import { + LABEL_CONFIG_KEYS, + FIELD_CONFIG_KEYS, + BUTTON_CONFIG_KEYS, +} from '../../../../support/commands/constants/command_constants'; // Menu options const SETTINGS_MENU_OPTION = 'Settings'; @@ -74,34 +80,39 @@ describe('Automate C & U Gap Collection form operations: Settings > Application cy.contains('#main-content .bx--form h3', FORM_SUBHEADER_SNIPPET).should( 'be.visible' ); - // Assert timezone label & field is visible and enabled - cy.getFormLabelByForAttribute({ forValue: 'timezone' }) - .should('be.visible') - .and('contain.text', TIMEZONE_FIELD_LABEL); - cy.getFormInputFieldByIdAndType({ inputId: 'timezone' }) - .should('be.visible') - .and('be.enabled'); - // Assert start date label & field is visible and enabled - cy.getFormLabelByForAttribute({ forValue: 'startDate' }) - .should('be.visible') - .and('contain.text', START_DATE_FIELD_LABEL); - cy.getFormInputFieldByIdAndType({ inputId: 'startDate' }) - .should('be.visible') - .and('be.enabled'); - // Assert end date label & field is visible and enabled - cy.getFormLabelByForAttribute({ forValue: 'endDate' }) - .should('be.visible') - .and('contain.text', END_DATE_FIELD_LABEL); - cy.getFormInputFieldByIdAndType({ inputId: 'endDate' }) - .should('be.visible') - .and('be.enabled'); - // Assert save button is visible and disabled - cy.getFormFooterButtonByTypeWithText({ - buttonText: 'Save', - buttonType: 'submit', - }) - .should('be.visible') - .and('be.disabled'); + + // Validate form labels + cy.validateFormLabels([ + { + [LABEL_CONFIG_KEYS.FOR_VALUE]: 'timezone', + [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: TIMEZONE_FIELD_LABEL, + }, + { + [LABEL_CONFIG_KEYS.FOR_VALUE]: 'startDate', + [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: START_DATE_FIELD_LABEL, + }, + { + [LABEL_CONFIG_KEYS.FOR_VALUE]: 'endDate', + [LABEL_CONFIG_KEYS.EXPECTED_TEXT]: END_DATE_FIELD_LABEL, + }, + ]); + // Validate form fields + cy.validateFormFields([ + { + [FIELD_CONFIG_KEYS.ID]: 'timezone', + [FIELD_CONFIG_KEYS.EXPECTED_VALUE]: '(GMT+00:00) UTC', + }, + { [FIELD_CONFIG_KEYS.ID]: 'startDate' }, + { [FIELD_CONFIG_KEYS.ID]: 'endDate' }, + ]); + // Validate form footer buttons + cy.validateFormFooterButtons([ + { + [BUTTON_CONFIG_KEYS.BUTTON_TEXT]: 'Save', + [BUTTON_CONFIG_KEYS.BUTTON_TYPE]: 'submit', + [BUTTON_CONFIG_KEYS.SHOULD_BE_DISABLED]: true, + }, + ]); }); it('Should fail if start date is greater than end date', () => {