diff --git a/cypress/integration/plugins/index-management-dashboards-plugin/snapshots_spec.js b/cypress/integration/plugins/index-management-dashboards-plugin/snapshots_spec.js index 2d1bc1afc..b8cb8048b 100644 --- a/cypress/integration/plugins/index-management-dashboards-plugin/snapshots_spec.js +++ b/cypress/integration/plugins/index-management-dashboards-plugin/snapshots_spec.js @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +/* eslint-disable cypress/no-unnecessary-waiting */ + import { IM_PLUGIN_NAME, BASE_PATH } from "../../../utils/constants"; describe("Snapshots", () => { @@ -13,8 +15,8 @@ describe("Snapshots", () => { // Visit ISM Snapshots Dashboard cy.visit(`${BASE_PATH}/app/${IM_PLUGIN_NAME}#/snapshots`); - // Common text to wait for to confirm page loaded, give up to 60 seconds for initial load - cy.contains("Restore", { timeout: 60000 }); + // Common text to wait for to confirm page loaded, give up to 120 seconds for initial load + cy.contains("Restore", { timeout: 120000 }); }); describe("Repository can be created", () => { @@ -31,6 +33,9 @@ describe("Snapshots", () => { // Type in repository location cy.get(`input[placeholder="e.g., /mnt/snapshots"]`).focus().type("~/Desktop"); + // if a toast message pops up then dismiss it + cy.dismissToast(); + // Click Add button cy.get("button").contains("Add").click({ force: true }); @@ -51,12 +56,12 @@ describe("Snapshots", () => { cy.createIndex("test_index_2"); cy.createIndex("test_index_3"); - // wait needed here to enable cypress to find "Take snapshot" button. Timeout - // cannot be used with cy.createIndex - cy.wait(5000); - - // Click Take snapshot button - cy.get("button").contains("Take snapshot").click({ force: true }); + // Click Take snapshot button with a 12sec timeout + cy.get(`[data-test-subj="takeSnapshotButton"]`, { timeout: 12000 }) + .should("be.visible") + .should("not.be.disabled") + .contains("Take snapshot") + .click({ force: true }); // Confirm test_repo exists and is in the Select repo field cy.contains("test_repo"); @@ -69,8 +74,11 @@ describe("Snapshots", () => { cy.get(`[data-test-subj="indicesComboBoxInput"]`).type("test_index_2{enter}"); cy.get(`[data-test-subj="indicesComboBoxInput"]`).type("test_index_3{enter}"); + // if a toast message pops up then dismiss it + cy.dismissToast(); + // Click 'Add' button to create snapshot - cy.get("button").contains("Add", { timeout: 3000 }).click({ force: true }); + cy.get(`[data-test-subj="flyout-footer-action-button"]`).contains("Add", { timeout: 3000 }).click({ force: true }); // check for success status and snapshot name cy.get("button").contains("Refresh").click({ force: true }); @@ -84,17 +92,21 @@ describe("Snapshots", () => { describe("Snapshot can be restored", () => { it("Successfully restores indexes from snapshot", () => { - // Must wait here before refreshing so snapshot status becomes 'success' - cy.wait(5000); - - // Wait for snapshot to be created successfully - cy.get("button").contains("Refresh").click({ force: true }); + // Wait for snapshot to be created successfully with a 12sec timeout + cy.get(`[data-test-subj="refreshButton"]`, { timeout: 12000 }) + .should("be.visible") + .should("not.be.disabled") + .contains("Refresh") + .click({ force: true }); - // Select test snapshot - cy.get(`[data-test-subj="checkboxSelectRow-test_repo:test_snapshot"]`).check({ force: true }); + // Select test snapshot with a 2sec timeout + cy.get(`[data-test-subj="checkboxSelectRow-test_repo:test_snapshot"]`, { timeout: 2000 }).check({ force: true }); // click "Restore" button - cy.get(`[data-test-subj="restoreButton"]`).click({ force: true }); + cy.get(`[data-test-subj="restoreButton"]`).should("be.visible").should("not.be.disabled").click({ force: true }); + + // if a toast message pops up then dismiss it + cy.dismissToast(); // Check for restore flyout cy.contains("Restore snapshot"); @@ -103,7 +115,7 @@ describe("Snapshots", () => { cy.get(`input[data-test-subj="prefixInput"]`).type("restored_"); // Click restore snapshot button - cy.get("button").contains("Restore snapshot").click({ force: true }); + cy.get(`[data-test-subj="flyout-footer-action-button"]`).contains("Restore snapshot").click({ force: true }); // Check for success toast cy.contains(`Restore from snapshot "test_snapshot" is in progress.`); @@ -111,15 +123,20 @@ describe("Snapshots", () => { }); describe("Snapshot can be deleted", () => { - it("deletes snapshot successfully", async () => { + it("deletes snapshot successfully", () => { // Select test snapshot cy.get(`[data-test-subj="checkboxSelectRow-test_repo:test_snapshot"]`).check({ force: true }); // click "Delete" button - cy.get("button").contains("Delete", { timeout: 3000 }).click({ force: true }); - - // click "Delete snapshot" button on modal - cy.get("button").contains("Delete snapshot").click({ force: true }); + cy.get(`[data-test-subj="deleteButton"]`) + .should("be.visible") + .should("not.be.disabled") + .get("button") + .contains("Delete", { timeout: 3000 }) + .click({ force: true }); + + // click "Delete snapshot" button on modal with a 2sec timeout + cy.get("button", { timeout: 2000 }).contains("Delete snapshot").click({ force: true }); cy.contains("Deleted snapshot"); cy.contains("No items found"); diff --git a/cypress/utils/plugins/index-management-dashboards-plugin/commands.js b/cypress/utils/plugins/index-management-dashboards-plugin/commands.js index 71bb3370a..3dba766f2 100644 --- a/cypress/utils/plugins/index-management-dashboards-plugin/commands.js +++ b/cypress/utils/plugins/index-management-dashboards-plugin/commands.js @@ -125,3 +125,11 @@ Cypress.Commands.add("removeIndexAlias", (alias) => { failOnStatusCode: false, }); }); + +Cypress.Commands.add("dismissToast", () => { + cy.get("body").then(($body) => { + if ($body.find(`[data-test-subj="toastCloseButton"]`).length) { + cy.get(`[data-test-subj="toastCloseButton"]`).click({ force: true }); + } + }); +}); diff --git a/public/pages/Snapshots/containers/Snapshots/Snapshots.tsx b/public/pages/Snapshots/containers/Snapshots/Snapshots.tsx index 2b4ac530e..11855028c 100644 --- a/public/pages/Snapshots/containers/Snapshots/Snapshots.tsx +++ b/public/pages/Snapshots/containers/Snapshots/Snapshots.tsx @@ -22,7 +22,7 @@ import { FieldValueSelectionFilterConfigType } from "@elastic/eui/src/components import { CoreServicesContext } from "../../../../components/core_services"; import { SnapshotManagementService, IndexService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; -import { Toast, RestoreError } from "../../../../models/interfaces" +import { Toast, RestoreError } from "../../../../models/interfaces"; import { CatSnapshotWithRepoAndPolicy as SnapshotsWithRepoAndPolicy } from "../../../../../server/models/interfaces"; import { ContentPanel } from "../../../../components/ContentPanel"; import SnapshotFlyout from "../../components/SnapshotFlyout/SnapshotFlyout"; @@ -32,9 +32,9 @@ import RestoreActivitiesPanel from "../../components/RestoreActivitiesPanel"; import { Snapshot } from "../../../../../models/interfaces"; import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import { renderTimestampMillis } from "../../../SnapshotPolicies/helpers"; -import ErrorModal from "../../../Snapshots/components/ErrorModal/ErrorModal" +import ErrorModal from "../../../Snapshots/components/ErrorModal/ErrorModal"; import DeleteModal from "../../../Repositories/components/DeleteModal/DeleteModal"; -import { getToasts } from "../../helper" +import { getToasts } from "../../helper"; import { snapshotStatusRender, truncateSpan } from "../../helper"; interface SnapshotsProps extends RouteComponentProps { @@ -174,11 +174,11 @@ export default class Snapshots extends Component ] as string[]; this.setState({ snapshots, existingPolicyNames }); } else { - const message = JSON.parse(response.error).error.root_cause[0].reason + const message = JSON.parse(response.error).error.root_cause[0].reason; const trimmedMessage = message.slice(message.indexOf("]") + 1, message.indexOf(".") + 1); this.context.notifications.toasts.addError(response.error, { title: `There was a problem getting the snapshots.`, - toastMessage: `${trimmedMessage} Open browser console & click below for details.` + toastMessage: `${trimmedMessage} Open browser console & click below for details.`, }); } } catch (err) { @@ -212,11 +212,11 @@ export default class Snapshots extends Component if (response.ok) { this.context.notifications.toasts.addSuccess(`Deleted snapshot ${snapshotId} from repository ${repository}.`); } else { - const message = JSON.parse(response.error).error.root_cause[0].reason + const message = JSON.parse(response.error).error.root_cause[0].reason; const trimmedMessage = message.slice(message.indexOf("]") + 1, message.indexOf(".") + 1); this.context.notifications.toasts.addError(response.error, { title: `There was a problem deleting the snapshot.`, - toastMessage: `${trimmedMessage} Open browser console & click below for details.` + toastMessage: `${trimmedMessage} Open browser console & click below for details.`, }); } } catch (err) { @@ -241,12 +241,12 @@ export default class Snapshots extends Component this.context.notifications.toasts.addSuccess(`Created snapshot ${snapshotId} in repository ${repository}.`); await this.getSnapshots(); } else { - const message = JSON.parse(response.error).error.root_cause[0].reason + const message = JSON.parse(response.error).error.root_cause[0].reason; const trimmedMessage = message.slice(message.indexOf("]") + 1, message.indexOf(".") + 1); this.context.notifications.toasts.addError(response.error, { title: `There was a problem creating the snapshot.`, - toastMessage: `${trimmedMessage} Open browser console & click below for details.` + toastMessage: `${trimmedMessage} Open browser console & click below for details.`, }); } } catch (err) { @@ -256,7 +256,7 @@ export default class Snapshots extends Component restoreSnapshot = async (snapshotId: string, repository: string, options: object) => { try { - await this.setState({ toasts: [], indicesToRestore: options.indices }) + await this.setState({ toasts: [], indicesToRestore: options.indices }); const { snapshotManagementService } = this.props; const response = await snapshotManagementService.restoreSnapshot(snapshotId, repository, options); @@ -277,43 +277,43 @@ export default class Snapshots extends Component let optionalMessage = ""; if (error.reason.indexOf("open index with same name") >= 0) { - optionalMessage = "You have an index with the same name. Try a different prefix." + optionalMessage = "You have an index with the same name. Try a different prefix."; } - errorMessage = `${optionalMessage}` + errorMessage = `${optionalMessage}`; } - const toasts = success ? - getToasts("success_restore_toast", errorMessage, selectedItems[0].id, this.onClickTab) : - getToasts("error_restore_toast", errorMessage, selectedItems[0].id, this.onOpenError); + const toasts = success + ? getToasts("success_restore_toast", errorMessage, selectedItems[0].id, this.onClickTab) + : getToasts("error_restore_toast", errorMessage, selectedItems[0].id, this.onOpenError); this.setState({ toasts, error: error }); - } + }; onOpenError = () => { this.setState({ viewError: true }); - } + }; collectError = (error: object) => { this.setState(error); - } + }; collectToast = (toasts: Toast[]) => { - this.setState({ toasts: toasts }) - } + this.setState({ toasts: toasts }); + }; onCloseModal = () => { this.setState({ viewError: false, error: {} }); - } + }; getRestoreTime = (time: number) => { - this.setState({ restoreStart: time }) - } + this.setState({ restoreStart: time }); + }; onClickRestore = async () => { const { selectedItems } = this.state; const snapshot = selectedItems[0]; const errorMessage = `${snapshot.id} is not available for restore`; - const failedText = "This shapshot is incomplete. Pick another snapshot to restore." - const inProgressText = "This snapshot is still being created. Try again once the snapshot has been completed." + const failedText = "This shapshot is incomplete. Pick another snapshot to restore."; + const inProgressText = "This snapshot is still being created. Try again once the snapshot has been completed."; if (snapshot.status === "FAILED" || snapshot.status === "IN_PROGRESS") { const errorText = snapshot.status === "IN_PROGRESS" ? inProgressText : failedText; @@ -325,7 +325,7 @@ export default class Snapshots extends Component onToastEnd = () => { this.setState({ toasts: [] }); - } + }; onCloseRestoreFlyout = () => { this.setState({ showRestoreFlyout: false }); @@ -366,7 +366,7 @@ export default class Snapshots extends Component secondTab!.ariaSelected = "true"; secondTab!.classList.add("euiTab-isSelected"); } - let newState = { snapshotPanel: snapshotPanel, selectedItems } + let newState = { snapshotPanel: snapshotPanel, selectedItems }; if (snapshotPanel) newState.selectedItems = []; @@ -434,7 +434,7 @@ export default class Snapshots extends Component Restore , - + Take snapshot , ]; @@ -442,7 +442,9 @@ export default class Snapshots extends Component const subTitleText = (

- Snapshots of indices are taken automatically from snapshot policies,
or you can initiate manual snapshots to save to a repository.
+ Snapshots of indices are taken automatically from snapshot policies,
+ or you can initiate manual snapshots to save to a repository. +
You can restore indices by selecting a snapshot.

@@ -452,8 +454,10 @@ export default class Snapshots extends Component <> - Snapshots - Restore activities in progress + + Snapshots + + Restore activities in progress {snapshotPanel || ( @@ -525,13 +529,7 @@ export default class Snapshots extends Component - {viewError && ( - - )} + {viewError && } {isDeleteModalVisible && (