Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cypress/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ ManageIQ implements the following cypress extensions:
* `cy.toolbarItems(toolbarButton)` - returns an array of objects {text: String, disabled: Boolean} for the toolbar dropdown buttons for when a toolbar button is clicked. `toolbarButton` is the string for the text of the toolbar button that you want to click on.
* `cy.toolbar(toolbarButton, toolbarOption)` - click on the toolbar button specified by the user. Can also then click on a specified dropdown option as well. `toolbarButton` is the string for the text of the toolbar button that you want to click on. `toolbarOption` is the string for the text of the toolbar dropdown option that you want to click on.

##### api_commands

* `cy.interceptApi({ alias, method = 'POST', urlPattern, triggerFn, onApiResponse })` - intercepts API calls and waits for them to complete. This command will: 1) Register an intercept for the given alias and URL pattern if not already registered, 2) Execute the trigger function that makes the API call, 3) Wait for the intercepted request to complete. `alias` is the string for a unique alias for this interception. `method` is the string for the HTTP method (default: 'POST'). `urlPattern` is the string or RegExp for the URL pattern to intercept. `triggerFn` is the function that triggers the API call. `onApiResponse` is an optional callback function that receives the interception object after the API call completes. Use this to perform assertions on the response, extract data, or perform additional actions based on the API result. Default is a no-op function. e.g. `cy.interceptApi({ alias: 'getUsers', method: 'GET', urlPattern: '/api/users', triggerFn: () => cy.get('#load-users').click(), onApiResponse: (interception) => { expect(interception.response.statusCode).to.equal(200); } });`
* `cy.getInterceptedApiAliases()` - returns the intercepted API aliases stored in Cypress environment variables.
* `cy.setInterceptedApiAlias(aliasKey, aliasValue)` - sets an intercepted API alias in the Cypress environment variables. `aliasKey` is the string for the key/name of the alias to set. `aliasValue` is an optional string for the value to store for the alias (defaults to the same as the key). e.g. `cy.setInterceptedApiAlias('getUsersApi');`, `cy.setInterceptedApiAlias('getUsersApi', 'customValue');`
* `cy.resetInterceptedApiAliases()` - resets the intercepted API aliases stored in Cypress environment variables.

##### custom_logging_commands

* `cy.logAndThrowError(messageToLog, messageToThrow)` - Logs a custom error message to Cypress log and then throws an error. `messageToLog` is the message to display in the Cypress command log. `messageToThrow` is the optional error message to throw, defaults to `messageToLog`. e.g. `cy.logAndThrowError('This is the logged message', 'This is the thrown error message');`, `cy.logAndThrowError('This is the message that gets logged and thrown');`
Expand Down
33 changes: 22 additions & 11 deletions cypress/e2e/ui/validate-intercept-api-command.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@ describe('Validate intercept command', () => {
cy.menu('Settings', 'Application Settings');
});

it('Should register the alias, intercept & wait when API fired', () => {
it('Should register the alias, intercept, wait & validate response status code when an API is fired', () => {
cy.accordion('Diagnostics');
cy.interceptApi({
alias: 'treeSelectApi',
urlPattern: /\/ops\/tree_select\?id=.*&text=.*/,
triggerFn: () => cy.selectAccordionItem([/^ManageIQ Region:/, /^Zone:/]),
onApiResponse: (interception) => {
expect(interception.response.statusCode).to.equal(200);
},
}).then(() => {
// verifies that the alias is set and the request is intercepted & awaited
expect(Cypress.env('interceptedAliases')).to.have.property(
'treeSelectApi'
);
cy.getInterceptedApiAliases().then((interceptedAliasesObject) => {
expect(interceptedAliasesObject).to.have.property('treeSelectApi');
});
});
});

Expand All @@ -36,10 +39,12 @@ describe('Validate intercept command', () => {
triggerFn: () => cy.selectAccordionItem([/^ManageIQ Region:/, /^Zone:/]),
}).then(() => {
// verifies that both the aliases are set and the request is intercepted & awaited
expect(Cypress.env('interceptedAliases')).to.include.all.keys(
'accordionSelectApi',
'treeSelectApi'
);
cy.getInterceptedApiAliases().then((interceptedAliasesObject) => {
expect(interceptedAliasesObject).to.include.all.keys(
'accordionSelectApi',
'treeSelectApi'
);
});
});
});

Expand All @@ -50,15 +55,19 @@ describe('Validate intercept command', () => {
urlPattern: /\/ops\/accordion_select\?id=.*/,
triggerFn: () => cy.accordion('Diagnostics'),
}).then(() => {
expect(Object.keys(Cypress.env('interceptedAliases')).length).to.equal(1);
cy.getInterceptedApiAliases().then((interceptedAliasesObject) => {
expect(Object.keys(interceptedAliasesObject).length).to.equal(1);
});
});
// second first api with alias 'treeSelectApi'
cy.interceptApi({
alias: 'treeSelectApi',
urlPattern: /\/ops\/tree_select\?id=.*&text=.*/,
triggerFn: () => cy.selectAccordionItem([/^ManageIQ Region:/, /^Zone:/]),
}).then(() => {
expect(Object.keys(Cypress.env('interceptedAliases')).length).to.equal(2);
cy.getInterceptedApiAliases().then((interceptedAliasesObject) => {
expect(Object.keys(interceptedAliasesObject).length).to.equal(2);
});
});
// third api with a duplicate alias as above 'accordionSelectApi'
cy.interceptApi({
Expand All @@ -67,7 +76,9 @@ describe('Validate intercept command', () => {
triggerFn: () => cy.accordion('Access Control'),
}).then(() => {
// assert that the alias is not overwritten
expect(Object.keys(Cypress.env('interceptedAliases')).length).to.equal(2);
cy.getInterceptedApiAliases().then((interceptedAliasesObject) => {
expect(Object.keys(interceptedAliasesObject).length).to.equal(2);
});
});
});
});
95 changes: 80 additions & 15 deletions cypress/support/commands/api_commands.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,58 @@
/* eslint-disable no-undef */

/**
* Custom command to get the intercepted API aliases stored in Cypress environment variables.
* This command returns the object containing all registered API interception aliases.
*
* @returns {Object} An object where keys are the alias names and values are typically the same alias names
* @example
* cy.getInterceptedApiAliases().then((aliases) => {
* Check if a specific alias exists
* expect(aliases).to.have.property('myApiAlias');
*
* Get the number of registered aliases
* const aliasCount = Object.keys(aliases).length;
* });
*/
Cypress.Commands.add('getInterceptedApiAliases', () =>
Cypress.env('interceptedAliases')
);

/**
* Custom command to set an intercepted API alias in the Cypress environment variables.
* This command adds an alias in the intercepted aliases tracking object.
*
* @param {string} aliasKey - The key/name of the alias to set
* @param {string} [aliasValue=aliasKey] - The value to store for the alias (defaults to the same as the key)
* @example
* Set a new alias
* cy.setInterceptedApiAlias('getUsersApi');
*
* Set an alias with a custom value
* cy.setInterceptedApiAlias('getUsersApi', 'customValue');
*/
Cypress.Commands.add(
'setInterceptedApiAlias',
(aliasKey, aliasValue = aliasKey) => {
cy.getInterceptedApiAliases().then((interceptedAliasesMap) => {
interceptedAliasesMap[aliasKey] = aliasValue;
Cypress.env('interceptedAliases', interceptedAliasesMap);
});
}
);

/**
* Custom command to reset all intercepted API aliases stored in Cypress environment variables.
* This command clears the tracking object by setting it to an empty object.
* Useful for cleaning up between tests or test suites.
* @example
* Reset all intercepted API aliases
* cy.resetInterceptedApiAliases();
*/
Cypress.Commands.add('resetInterceptedApiAliases', () =>
Cypress.env('interceptedAliases', {})
);

/**
* Custom command to intercept API calls and wait for them to complete.
* This command will:
Expand All @@ -12,33 +65,45 @@
* @param {string} options.method - HTTP method (default: 'POST')
* @param {string|RegExp} options.urlPattern - URL pattern to intercept
* @param {Function} options.triggerFn - Function that triggers the API call
* @param {Function} [options.onApiResponse] - Optional callback function that receives the interception object after the API call completes.
* Use this to perform assertions on the response, extract data, or perform additional actions based on the API result.
* Default is a no-op function. e.g. { onApiResponse: (interception) => { expect(interception.response.statusCode).to.equal(200); } }
*/
Cypress.Commands.add(
'interceptApi',
({ alias, method = 'POST', urlPattern, triggerFn }) => {
({
alias,
method = 'POST',
urlPattern,
triggerFn,
onApiResponse = () => {
/* default implementation */
},
}) => {
/* ===== TODO: Remove this block once interceptApi command becomes stable ===== */
const envVars = Cypress.env();
cy.log('Cypress Environment Variables:');
cy.log(JSON.stringify(envVars, null, 2));
/* ======================================================= */

// Check if this request is already registered
const isAlreadyRegistered = !!Cypress.env('interceptedAliases')[alias];

// Register the intercept if not already done
if (!isAlreadyRegistered) {
cy.intercept(method, urlPattern).as(alias);
cy.getInterceptedApiAliases().then((interceptedAliasesMap) => {
// Check if this request is already registered
const isAlreadyRegistered = !!interceptedAliasesMap[alias];

// Store the alias in the tracking object
const interceptedAliases = Cypress.env('interceptedAliases');
interceptedAliases[alias] = alias;
Cypress.env('interceptedAliases', interceptedAliases);
}
// Register the intercept if not already done
if (!isAlreadyRegistered) {
cy.intercept(method, urlPattern).as(alias);
cy.setInterceptedApiAlias(alias);
}

// Execute the function that triggers the API call
triggerFn();
// Execute the function that triggers the API call
triggerFn();

// Wait for the intercepted request to complete
cy.wait(`@${alias}`);
// Wait for the intercepted request to complete
cy.wait(`@${alias}`).then((interception) => {
onApiResponse(interception);
});
});
}
);
2 changes: 1 addition & 1 deletion cypress/support/e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,5 @@ beforeEach(() => {
// cy.stub_notifications();

// Reset the intercepted aliases tracking object
Cypress.env('interceptedAliases', {});
cy.resetInterceptedApiAliases();
})