Skip to content

Commit 5397438

Browse files
kaituoyubonluo
andauthored
extract tenant-dialog helper & improve forecaster list test robustness (#1823)
* extract tenant-dialog helper & improve forecaster list test robustness This PR: * Create `cy.handleTenantDialog` custom command to handle the optional **"Select your tenant"** pop-up. * Refactor `forecaster_list_spec.js` and `createForecaster` helper to leverage the new command, reducing duplicate code. * Introduce explicit waits (`cy.contains('Loading OpenSearch Dashboards').should('not.exist')`, `cy.wait(500)`) and additional `cy.log` statements to handle cases where the home page loads slowly, improving stability and aiding debugging. * Replace thrown error during action-menu retries with a `cy.log` to enable fallback logic in future enhancements. * Implement fallback logic to use **"Stop forecasting"** when the **"Cancel forecast"** modal does not appear, covering both *Initializing* and *Running* forecast states. Signed-off-by: Kaituo Li <[email protected]> * check if deleted detector matches of of known existing one Signed-off-by: Kaituo Li <[email protected]> --------- Signed-off-by: Kaituo Li <[email protected]> Co-authored-by: yuboluo <[email protected]>
1 parent ec9f7cd commit 5397438

File tree

2 files changed

+102
-65
lines changed

2 files changed

+102
-65
lines changed

cypress/integration/plugins/anomaly-detection-dashboards-plugin/forecaster_list_spec.js

Lines changed: 82 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -135,16 +135,11 @@ context('list forecaster workflow', () => {
135135
});
136136

137137
// Handles the optional "Select your tenant" pop-up
138-
cy.get('body').then(($body) => {
139-
// We look for an element containing "Select your tenant" to avoid being
140-
// specific about which tag (e.g. h1, h2) is used for the title.
141-
if ($body.find(':contains("Select your tenant")').length > 0) {
142-
const confirmButton = $body.find('button:contains("Confirm")');
143-
if (confirmButton.length) {
144-
cy.wrap(confirmButton.first()).click();
145-
}
146-
}
147-
});
138+
cy.handleTenantDialog();
139+
140+
cy.contains('Loading OpenSearch Dashboards', { timeout: 120000 }).should(
141+
'not.exist'
142+
);
148143

149144
// The application can sometimes re-render the header after loading or
150145
// dismissing initial pop-ups. To prevent a "detached from the DOM" error,
@@ -163,16 +158,8 @@ context('list forecaster workflow', () => {
163158
cy.contains('Forecasting').click();
164159

165160
// Handles the optional "Select your tenant" pop-up
166-
cy.get('body').then(($body) => {
167-
// We look for an element containing "Select your tenant" to avoid being
168-
// specific about which tag (e.g. h1, h2) is used for the title.
169-
if ($body.find(':contains("Select your tenant")').length > 0) {
170-
const confirmButton = $body.find('button:contains("Confirm")');
171-
if (confirmButton.length) {
172-
cy.wrap(confirmButton.first()).click();
173-
}
174-
}
175-
});
161+
// tenant dialog can appear at any time, so we need to handle it between each clicks
162+
cy.handleTenantDialog();
176163

177164
// Verify we're redirected to the forecasters list page
178165
// match ensures that the url ends with /forecasters
@@ -181,8 +168,10 @@ context('list forecaster workflow', () => {
181168
// ========== Scenario: CREATE FORECASTER ==========
182169
// Verify the title contains the word "Forecasters"
183170
cy.contains('Forecasters (0)').should('exist');
171+
cy.handleTenantDialog();
184172
// we have two create forecaster buttons, click any one of them
185173
cy.getElementByTestId('createForecasterButton').first().click();
174+
cy.handleTenantDialog();
186175

187176
// Call the new reusable command to create the forecaster
188177
cy.createForecaster({
@@ -433,7 +422,7 @@ context('list forecaster workflow', () => {
433422
buttonCount
434423
) => {
435424
if (buttonIndex >= buttonCount) {
436-
throw new Error(
425+
cy.log(
437426
`Could not open the "${modalTestSubj}" modal after trying all buttons.`
438427
);
439428
}
@@ -473,42 +462,51 @@ context('list forecaster workflow', () => {
473462
.click({ force: true });
474463

475464
// Poll for the menu to appear
465+
cy.wait(500);
476466
cy.get('body').then(($body) => {
477467
const $visibleMenu = $body.find('.euiContextMenuPanel:visible');
478468
if (
479469
$visibleMenu.length > 0 &&
480470
$visibleMenu.text().includes(menuItemText)
481471
) {
482-
// Correct menu found, attempt to click the item
483-
cy.log(` Correct menu is visible. Clicking "${menuItemText}".`);
484-
cy.get('.euiContextMenuPanel:visible')
485-
.contains('.euiContextMenuItem', menuItemText)
486-
.click({ force: true });
487-
488-
// After clicking, wait a moment and check if the modal appeared.
489-
cy.wait(500); // Wait for modal to render
490-
cy.get('body').then(($bodyAfterClick) => {
491-
if (
492-
$bodyAfterClick.find(`[data-test-subj="${modalTestSubj}"]`)
493-
.length > 0
494-
) {
495-
// SUCCESS! Modal is open. End all loops.
496-
cy.log(` Success! ${modalTestSubj} is visible.`);
497-
return;
498-
} else {
499-
// Modal did not appear. Retry the sequence.
500-
cy.log(
501-
' Modal did not appear after click. Closing menu and retrying.'
502-
);
503-
cy.get('body').click('topLeft', { force: true });
504-
cy.wait(500);
505-
attemptClickSequence(retryCount - 1);
506-
}
507-
});
472+
// The menu was visible. Re-check just before clicking to be safe.
473+
if (Cypress.$('.euiContextMenuPanel:visible').length > 0) {
474+
cy.log(` Correct menu is visible. Clicking "${menuItemText}".`);
475+
cy.get('.euiContextMenuPanel:visible')
476+
.contains('.euiContextMenuItem', menuItemText)
477+
.click({ force: true });
478+
479+
// After clicking, wait a moment and check if the modal appeared.
480+
cy.wait(500); // Wait for modal to render
481+
cy.get('body').then(($bodyAfterClick) => {
482+
if (
483+
$bodyAfterClick.find(`[data-test-subj="${modalTestSubj}"]`)
484+
.length > 0
485+
) {
486+
// SUCCESS! Modal is open. End all loops.
487+
cy.log(` Success! ${modalTestSubj} is visible.`);
488+
return;
489+
} else {
490+
// Modal did not appear. Retry the sequence.
491+
cy.log(
492+
' Modal did not appear after click. Closing menu and retrying.'
493+
);
494+
cy.get('body').click('topLeft', { force: true });
495+
cy.wait(500);
496+
attemptClickSequence(retryCount - 1);
497+
}
498+
});
499+
} else {
500+
// The menu disappeared between the initial check and this one.
501+
cy.log(' Menu disappeared before click. Retrying.');
502+
cy.wait(500);
503+
attemptClickSequence(retryCount - 1);
504+
}
508505
} else {
509506
// Menu was not visible or was incorrect. Retry.
510507
cy.log(' Menu not visible or incorrect. Retrying.');
511508
if ($visibleMenu.length > 0) {
509+
cy.log(` Visible menu text: "${$visibleMenu.text()}"`);
512510
cy.get('body').click('topLeft', { force: true });
513511
}
514512
cy.wait(500);
@@ -518,7 +516,7 @@ context('list forecaster workflow', () => {
518516
};
519517

520518
// Start the inner retry loop for the current button. Allow 3 retries.
521-
attemptClickSequence(3);
519+
attemptClickSequence(6);
522520
};
523521

524522
// Define a helper to retry clicking a confirm button until its modal disappears.
@@ -561,6 +559,34 @@ context('list forecaster workflow', () => {
561559
}
562560
});
563561

562+
// After attempting with "Cancel forecast", check if the modal appeared.
563+
// If not, try again with "Stop forecasting". "Cancel forecast" is present
564+
// when state is "Initializing". "Stop forecasting" is present when state
565+
// is "Running". We are not sure which state the forecaster is in when the
566+
// real time run is triggered, so we try both.
567+
cy.get('body').then(($body) => {
568+
if (
569+
$body.find('[data-test-subj="stopForecastersModal"]:visible').length ===
570+
0
571+
) {
572+
cy.log(
573+
'Modal not found with "Cancel forecast", trying "Stop forecasting".'
574+
);
575+
cy.get('button[aria-label="Show actions"]')
576+
.its('length')
577+
.then((count) => {
578+
if (count > 0) {
579+
openActionModalWithRetries(
580+
'Stop forecasting',
581+
'stopForecastersModal',
582+
0,
583+
count
584+
);
585+
}
586+
});
587+
}
588+
});
589+
564590
// =====================================================================
565591
// Scenario – STOP FORECASTER VIA ROW ACTIONS
566592
// =====================================================================
@@ -652,10 +678,14 @@ context('list forecaster workflow', () => {
652678
// Verify the 'Delete forecaster' confirmation modal appears.
653679
cy.get('[data-test-subj="deleteForecastersModal"]')
654680
.should('be.visible')
655-
.and(
656-
'contain',
657-
`Are you sure you want to delete "${TEST_FORECASTER_NAME}"?`
658-
);
681+
.and(($element) => {
682+
const text = $element.text();
683+
const includesFirstName = text.includes(TEST_FORECASTER_NAME);
684+
const includesSecondName = text.includes(TEST_FORECASTER_NAME_2);
685+
686+
// Assert that at least one of the names is in the text
687+
expect(includesFirstName || includesSecondName).to.be.true;
688+
});
659689

660690
// Type 'delete' in the confirmation field to enable the button.
661691
cy.get('[data-test-subj="deleteForecastersModal"]')

cypress/utils/plugins/anomaly-detection-dashboards-plugin/commands.js

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,26 @@ import {
1515
} from '../../constants';
1616
import { selectTopItemFromFilter } from './helpers';
1717

18+
Cypress.Commands.add(
19+
'handleTenantDialog',
20+
{
21+
prevSubject: 'optional',
22+
},
23+
() => {
24+
// Handles the optional "Select your tenant" pop-up
25+
cy.get('body').then(($body) => {
26+
// We look for an element containing "Select your tenant" to avoid being
27+
// specific about which tag (e.g. h1, h2) is used for the title.
28+
if ($body.find(':contains("Select your tenant")').length > 0) {
29+
const confirmButton = $body.find('button:contains("Confirm")');
30+
if (confirmButton.length) {
31+
cy.wrap(confirmButton.first()).click();
32+
}
33+
}
34+
});
35+
}
36+
);
37+
1838
Cypress.Commands.add(
1939
'mockGetDetectorOnAction',
2040
function (fixtureFileName, funcMockedOn) {
@@ -135,19 +155,6 @@ Cypress.Commands.add('createForecaster', (forecasterDetails) => {
135155
interval = 0,
136156
} = forecasterDetails;
137157

138-
// pop up for tenant selection
139-
// Handles the optional "Select your tenant" pop-up
140-
cy.get('body').then(($body) => {
141-
// We look for an element containing "Select your tenant" to avoid being
142-
// specific about which tag (e.g. h1, h2) is used for the title.
143-
if ($body.find(':contains("Select your tenant")').length > 0) {
144-
const confirmButton = $body.find('button:contains("Confirm")');
145-
if (confirmButton.length) {
146-
cy.wrap(confirmButton.first()).click();
147-
}
148-
}
149-
});
150-
151158
cy.getElementByTestId('defineOrEditForecasterTitle').should('exist');
152159
cy.getElementByTestId('forecasterNameTextInput').type(name);
153160
cy.getElementByTestId('indicesFilter').type(`${index}{enter}`);

0 commit comments

Comments
 (0)