From 68e27f71cde434589461d9e9c6cee1cb2752c776 Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Thu, 12 Dec 2024 23:50:13 -0500 Subject: [PATCH 01/21] wip --- .../components/snapshots/SnapshotDetails.jsx | 522 ++++++++++-------- 1 file changed, 290 insertions(+), 232 deletions(-) diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index cc30f34029..812d7b4c55 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -40,7 +40,8 @@ class SnapshotDetails extends Component { snapshotLogsErrMsg: "", loading: true, - snapshotDetails: {}, + currentSnapshotIndex: 0, + snapshotDetails: [], errorMessage: "", errorTitle: "", @@ -126,6 +127,46 @@ class SnapshotDetails extends Component { this.fetchSnapshotDetails(); } } + navigateSnapshot = (direction) => { + const { currentSnapshotIndex, snapshotDetails } = this.state; + const newIndex = + direction === "kots" + ? currentSnapshotIndex + 1 + : currentSnapshotIndex - 1; + + if (newIndex >= 0 && newIndex < snapshotDetails.length) { + const series = this.getSeriesDataForSnapshot(snapshotDetails[newIndex]); + this.setState({ + currentSnapshotIndex: newIndex, + series, + }); + } + }; + getSeriesDataForSnapshot = (snapshot) => { + let series = []; + if (!isEmpty(snapshot?.volumes)) { + if (snapshot?.hooks && !isEmpty(snapshot?.hooks)) { + series = this.getSeriesData( + [...snapshot?.volumes, ...snapshot?.hooks].sort( + (a, b) => new Date(a.started) - new Date(b.started) + ) + ); + } else { + series = this.getSeriesData( + (snapshot?.volumes).sort( + (a, b) => new Date(a.started) - new Date(b.started) + ) + ); + } + } else if (snapshot?.hooks && !isEmpty(snapshot?.hooks)) { + series = this.getSeriesData( + (snapshot?.hooks).sort( + (a, b) => new Date(a.started) - new Date(b.started) + ) + ); + } + return series; + }; fetchSnapshotDetails = async () => { const { params } = this.props; @@ -160,33 +201,11 @@ class SnapshotDetails extends Component { const snapshotDetails = response.backupDetails?.[0]; - let series = []; - if (!isEmpty(snapshotDetails?.volumes)) { - if (snapshotDetails?.hooks && !isEmpty(snapshotDetails?.hooks)) { - series = this.getSeriesData( - [...snapshotDetails?.volumes, ...snapshotDetails?.hooks].sort( - (a, b) => new Date(a.started) - new Date(b.started) - ) - ); - } else { - series = this.getSeriesData( - (snapshotDetails?.volumes).sort( - (a, b) => new Date(a.started) - new Date(b.started) - ) - ); - } - } else if (snapshotDetails?.hooks && !isEmpty(snapshotDetails?.hooks)) { - series = this.getSeriesData( - (snapshotDetails?.hooks).sort( - (a, b) => new Date(a.started) - new Date(b.started) - ) - ); - } - this.setState({ loading: false, - snapshotDetails: snapshotDetails, - series: series, + snapshotDetails, + currentSnapshotIndex: 0, + series, errorMessage: "", errorTitle: "", }); @@ -578,18 +597,19 @@ class SnapshotDetails extends Component { }; renderTimeInterval = () => { + const { currentSnapshotIndex, snapshotDetails } = this.state; let data; - if (!isEmpty(this.state.snapshotDetails?.volumes)) { - if (!isEmpty(this.state.snapshotDetails?.hooks)) { + if (!isEmpty(snapshotDetails[currentSnapshotIndex]?.volumes)) { + if (!isEmpty(snapshotDetails[currentSnapshotIndex]?.hooks)) { data = [ - ...this.state.snapshotDetails?.volumes, - ...this.state.snapshotDetails?.hooks, + ...snapshotDetails[currentSnapshotIndex]?.volumes, + ...snapshotDetails[currentSnapshotIndex]?.hooks, ]; } else { - data = this.state.snapshotDetails?.volumes; + data = snapshotDetails[currentSnapshotIndex]?.volumes; } - } else if (!isEmpty(this.state.snapshotDetails?.hooks)) { - data = this.state.snapshotDetails?.hooks; + } else if (!isEmpty(snapshotDetails[currentSnapshotIndex]?.hooks)) { + data = snapshotDetails[currentSnapshotIndex]?.hooks; } return (
@@ -649,202 +669,236 @@ class SnapshotDetails extends Component { ); }; - render() { + renderSnapshot = (snapshotDetails) => { + console.log(snapshotDetails, "snapshot"); + const { series, selectedScriptTab, selectedErrorsWarningTab } = this.state; const { isEmbeddedCluster } = this.props; - - const { - loading, - showScriptsOutput, - selectedTab, - selectedScriptTab, - scriptOutput, - showAllVolumes, - showAllPreSnapshotScripts, - showAllPostSnapshotScripts, - selectedErrorsWarningTab, - showAllErrors, - showAllWarnings, - snapshotDetails, - series, - errorMessage, - errorTitle, - } = this.state; - let featureName = "snapshot"; if (isEmbeddedCluster) { featureName = "backup"; } - - if (loading) { - return ( -
- -
- ); - } - return ( -
-

- this.props.navigate(-1)}> - {Utilities.toTitleCase(featureName)}s - - > - {snapshotDetails?.name} -

-
-
-
-

- {snapshotDetails?.name} -

-

- Total size:{" "} - - {snapshotDetails?.volumeSizeHuman} - -

-
-
-

- Status:{" "} - - {Utilities.snapshotStatusToDisplayName( - snapshotDetails?.status - )} +

+
+
+

+ {snapshotDetails?.name} {snapshotDetails?.type} +

+

+ Total size:{" "} + + {snapshotDetails?.volumeSizeHuman} + +

+
+
+

+ Status:{" "} + + {Utilities.snapshotStatusToDisplayName(snapshotDetails?.status)} + +

+
+ {snapshotDetails?.status !== "InProgress" && ( + this.viewLogs()}> + View logs -

-
- {snapshotDetails?.status !== "InProgress" && ( - this.viewLogs()}> - View logs - - )} -
+ )}
+
- {snapshotDetails?.status === "InProgress" ? ( -
- -

- {" "} - This {featureName} has not completed yet, check back soon{" "} -

-
- ) : ( -
- {!isEmpty(snapshotDetails?.volumes) || - !isEmpty(this.preSnapshotScripts()) || - !isEmpty(this.postSnapshotScripts()) ? ( -
-

- {Utilities.toTitleCase(featureName)} timeline -

-
- - {this.renderTimeInterval()} -
+ {snapshotDetails?.status === "InProgress" ? ( +
+ +

+ {" "} + This {featureName} has not completed yet, check back soon{" "} +

+
+ ) : ( +
+ {!isEmpty(snapshotDetails?.volumes) || + !isEmpty(this.preSnapshotScripts()) || + !isEmpty(this.postSnapshotScripts()) ? ( +
+

+ {Utilities.toTitleCase(featureName)} timeline +

+
+ + {this.renderTimeInterval()}
- ) : null} +
+ ) : null} -
-
-
-
-

- Volumes +

+
+
+
+

+ Volumes +

+ {snapshotDetails?.volumes?.length > 3 ? ( +
+ this.toggleShowAllVolumes()} + > + Show all {snapshotDetails?.volumes?.length} volumes + +
+ ) : null} +
+ {!isEmpty(snapshotDetails?.volumes) ? ( + this.renderShowAllVolumes( + snapshotDetails?.volumes?.slice(0, 3) + ) + ) : ( +
+

+ {" "} + No volumes to display{" "} +

+
+ )} +
+
+
+
+

+ Scripts

- {snapshotDetails?.volumes?.length > 3 ? ( + {this.preSnapshotScripts()?.length > 3 && + selectedScriptTab === "Pre-snapshot scripts" ? (
this.toggleShowAllVolumes()} + onClick={() => this.toggleShowAllPreScripts()} > - Show all {snapshotDetails?.volumes?.length} volumes + Show all {this.preSnapshotScripts()?.length}{" "} + pre-scripts
) : null} + {this.postSnapshotScripts()?.length > 3 && + selectedScriptTab === "Post-snapshot scripts" ? ( +
+ this.toggleShowAllPostScripts()} + > + Show all {this.postSnapshotScripts()?.length}{" "} + post-scripts + +
+ ) : null} +
+
+ {this.renderScriptsTabs()}
- {!isEmpty(snapshotDetails?.volumes) ? ( - this.renderShowAllVolumes( - snapshotDetails?.volumes?.slice(0, 3) +
+
+ {selectedScriptTab === "Pre-snapshot scripts" ? ( + !isEmpty(this.preSnapshotScripts()) ? ( + this.renderShowAllScripts( + this.preSnapshotScripts().slice(0, 3) + ) + ) : ( +
+

+ {" "} + No pre-{featureName} scripts to display{" "} +

+
+ ) + ) : selectedScriptTab === "Post-snapshot scripts" && + !isEmpty(this.postSnapshotScripts()) ? ( + this.renderShowAllScripts( + this.postSnapshotScripts().slice(0, 3) ) ) : (

{" "} - No volumes to display{" "} + No post-{featureName} scripts to display{" "}

)}
+
+
+
+ + {(!isEmpty(snapshotDetails?.errors) || + !isEmpty(snapshotDetails?.warnings)) && ( +
+

- Scripts + Errors and warnings

- {this.preSnapshotScripts()?.length > 3 && - selectedScriptTab === "Pre-snapshot scripts" ? ( + {snapshotDetails?.errors?.length > 3 && + selectedErrorsWarningTab === "Errors" ? (
this.toggleShowAllPreScripts()} + onClick={() => this.toggleShowAllErrors()} > - Show all {this.preSnapshotScripts()?.length}{" "} - pre-scripts + Show all {snapshotDetails?.errors?.length} errors{" "}
) : null} - {this.postSnapshotScripts()?.length > 3 && - selectedScriptTab === "Post-snapshot scripts" ? ( + {snapshotDetails?.warnings?.length > 3 && + selectedErrorsWarningTab === "Warnings" ? (
this.toggleShowAllPostScripts()} + onClick={() => this.toggleShowAllWarnings()} > - Show all {this.postSnapshotScripts()?.length}{" "} - post-scripts + Show all {snapshotDetails?.warnings?.length}{" "} + warnings{" "}
) : null}
- {this.renderScriptsTabs()} + {this.renderErrorsWarningsTabs()}
- {selectedScriptTab === "Pre-snapshot scripts" ? ( - !isEmpty(this.preSnapshotScripts()) ? ( - this.renderShowAllScripts( - this.preSnapshotScripts().slice(0, 3) + {selectedErrorsWarningTab === "Errors" ? ( + !isEmpty(snapshotDetails?.errors) ? ( + this.renderShowAllErrors( + snapshotDetails?.errors.slice(0, 3) ) ) : (

{" "} - No pre-{featureName} scripts to display{" "} + No errors to display{" "}

) - ) : selectedScriptTab === "Post-snapshot scripts" && - !isEmpty(this.postSnapshotScripts()) ? ( - this.renderShowAllScripts( - this.postSnapshotScripts().slice(0, 3) + ) : selectedErrorsWarningTab === "Warnings" && + !isEmpty(snapshotDetails?.warnings) ? ( + this.renderShowAllWarnings( + snapshotDetails?.warnings?.slice(0, 3) ) ) : (

{" "} - No post-{featureName} scripts to display{" "} + No warnings to display{" "}

)} @@ -852,81 +906,85 @@ class SnapshotDetails extends Component {
+ )} +
+ )} +
+ ); + }; - {(!isEmpty(snapshotDetails?.errors) || - !isEmpty(snapshotDetails?.warnings)) && ( -
-
-
-
-
-

- Errors and warnings -

- {snapshotDetails?.errors?.length > 3 && - selectedErrorsWarningTab === "Errors" ? ( -
- this.toggleShowAllErrors()} - > - Show all {snapshotDetails?.errors?.length}{" "} - errors{" "} - -
- ) : null} - {snapshotDetails?.warnings?.length > 3 && - selectedErrorsWarningTab === "Warnings" ? ( -
- this.toggleShowAllWarnings()} - > - Show all {snapshotDetails?.warnings?.length}{" "} - warnings{" "} - -
- ) : null} -
-
- {this.renderErrorsWarningsTabs()} -
-
-
- {selectedErrorsWarningTab === "Errors" ? ( - !isEmpty(snapshotDetails?.errors) ? ( - this.renderShowAllErrors( - snapshotDetails?.errors.slice(0, 3) - ) - ) : ( -
-

- {" "} - No errors to display{" "} -

-
- ) - ) : selectedErrorsWarningTab === "Warnings" && - !isEmpty(snapshotDetails?.warnings) ? ( - this.renderShowAllWarnings( - snapshotDetails?.warnings?.slice(0, 3) - ) - ) : ( -
-

- {" "} - No warnings to display{" "} -

-
- )} -
-
-
-
+ render() { + const { isEmbeddedCluster } = this.props; + + const { + loading, + showScriptsOutput, + selectedTab, + selectedScriptTab, + scriptOutput, + showAllVolumes, + showAllPreSnapshotScripts, + showAllPostSnapshotScripts, + selectedErrorsWarningTab, + showAllErrors, + showAllWarnings, + snapshotDetails, + currentSnapshotIndex, + series, + errorMessage, + errorTitle, + } = this.state; + + let featureName = "snapshot"; + if (isEmbeddedCluster) { + featureName = "backup"; + } + + if (loading) { + return ( +
+ +
+ ); + } + + console.log(snapshotDetails); + + return ( +
+
+

+ this.props.navigate(-1)}> + {Utilities.toTitleCase(featureName)} + + > + {snapshotDetails[currentSnapshotIndex]?.name} +

+ {/* only for EC???/ */} + {snapshotDetails.length > 1 && ( +
+ {currentSnapshotIndex === 0 && ( + + )} + {currentSnapshotIndex === 1 && ( + )}
)}
+ {this.renderSnapshot(snapshotDetails[currentSnapshotIndex])} {showScriptsOutput && scriptOutput && ( Date: Fri, 20 Dec 2024 15:24:59 -0500 Subject: [PATCH 02/21] f --- .../components/snapshots/SnapshotDetails.jsx | 48 +++++++++++++++++-- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index 812d7b4c55..e19fe8a291 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -13,6 +13,7 @@ import ShowAllModal from "../modals/ShowAllModal"; import ViewSnapshotLogsModal from "../modals/ViewSnapshotLogsModal"; import ErrorModal from "../modals/ErrorModal"; import { Utilities } from "../../utilities/utilities"; +import Toggle from "../shared/Toggle"; dayjs.extend(minMax); dayjs.extend(duration); @@ -129,13 +130,15 @@ class SnapshotDetails extends Component { } navigateSnapshot = (direction) => { const { currentSnapshotIndex, snapshotDetails } = this.state; + console.log(direction, "direction", currentSnapshotIndex); const newIndex = - direction === "kots" + direction === "infra" ? currentSnapshotIndex + 1 : currentSnapshotIndex - 1; if (newIndex >= 0 && newIndex < snapshotDetails.length) { const series = this.getSeriesDataForSnapshot(snapshotDetails[newIndex]); + console.log(newIndex, "ndew"); this.setState({ currentSnapshotIndex: newIndex, series, @@ -691,6 +694,43 @@ class SnapshotDetails extends Component {

+ {/* only for EC???/ */} + {snapshotDetails.length > 1 && ( +
+ { + // Ensure the next snapshot exists + this.navigateSnapshot("application"); + navigate( + `/snapshots/details/${snapshotDetails[currentSnapshotIndex].name}` + ); + // this.props.setState((prevState) => ({ currentSnapshotIndex: prevState.currentSnapshotIndex + 1 })); + }, + isActive: currentSnapshotIndex === 0, // + }, + { + title: "Infrastructure", + onClick: () => { + console.log("um"); + // Ensure the next snapshot exists + this.navigateSnapshot("infra"); + navigate( + `/snapshots/details/${ + snapshotDetails[currentSnapshotIndex + 1].name + }` + ); + console.log(currentSnapshotIndex); + // this.props.setState((prevState) => ({ currentSnapshotIndex: prevState.currentSnapshotIndex - 1 })); + }, + isActive: currentSnapshotIndex === 1, // + }, + ]} + /> +
+ )}

Status:{" "} @@ -914,7 +954,7 @@ class SnapshotDetails extends Component { }; render() { - const { isEmbeddedCluster } = this.props; + const { isEmbeddedCluster, navigate } = this.props; const { loading, @@ -947,9 +987,7 @@ class SnapshotDetails extends Component {

); } - - console.log(snapshotDetails); - + console.log(this.props, "props"); return (
From 8337a9a747cd2de7da7e9d27f820caa9d3ce005e Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Mon, 23 Dec 2024 12:59:02 -0500 Subject: [PATCH 03/21] add toggle --- web/src/components/shared/Toggle.tsx | 7 + .../components/snapshots/SnapshotDetails.jsx | 123 ++++++++---------- 2 files changed, 63 insertions(+), 67 deletions(-) diff --git a/web/src/components/shared/Toggle.tsx b/web/src/components/shared/Toggle.tsx index 4af0d915bf..e8cdeec4fe 100644 --- a/web/src/components/shared/Toggle.tsx +++ b/web/src/components/shared/Toggle.tsx @@ -5,6 +5,7 @@ interface Props { onClick: () => void; isActive: boolean; title: string; + status?: string; }[]; } @@ -19,6 +20,12 @@ export default function Tooltip(props: Props) { onClick={item.onClick} > {item.title} + + {item.status && ( + + )}
); })} diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index e19fe8a291..ad54468e76 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -202,7 +202,11 @@ class SnapshotDetails extends Component { } const response = await res.json(); - const snapshotDetails = response.backupDetails?.[0]; + const snapshotDetails = response.backupDetails; + const { currentSnapshotIndex } = this.state; + const series = this.getSeriesDataForSnapshot( + snapshotDetails[currentSnapshotIndex] + ); this.setState({ loading: false, @@ -672,59 +676,60 @@ class SnapshotDetails extends Component { ); }; - renderSnapshot = (snapshotDetails) => { - console.log(snapshotDetails, "snapshot"); - const { series, selectedScriptTab, selectedErrorsWarningTab } = this.state; - const { isEmbeddedCluster } = this.props; + renderSnapshot = (snapshotDetails, snapshotDetail) => { + const { + series, + selectedScriptTab, + selectedErrorsWarningTab, + currentSnapshotIndex, + } = this.state; + const { isEmbeddedCluster, navigate } = this.props; let featureName = "snapshot"; if (isEmbeddedCluster) { featureName = "backup"; } + const { params } = this.props; + const snapshotName = params.id; + return (

- {snapshotDetails?.name} {snapshotDetails?.type} + {snapshotName}

Total size:{" "} - {snapshotDetails?.volumeSizeHuman} + {snapshotDetail?.volumeSizeHuman}

{/* only for EC???/ */} - {snapshotDetails.length > 1 && ( + {snapshotDetails?.length > 1 && (
{ - // Ensure the next snapshot exists this.navigateSnapshot("application"); - navigate( - `/snapshots/details/${snapshotDetails[currentSnapshotIndex].name}` - ); - // this.props.setState((prevState) => ({ currentSnapshotIndex: prevState.currentSnapshotIndex + 1 })); }, + status: + snapshotDetails[0]?.status !== "Completed" + ? snapshotDetails[0]?.status + : null, isActive: currentSnapshotIndex === 0, // }, { title: "Infrastructure", onClick: () => { - console.log("um"); - // Ensure the next snapshot exists this.navigateSnapshot("infra"); - navigate( - `/snapshots/details/${ - snapshotDetails[currentSnapshotIndex + 1].name - }` - ); - console.log(currentSnapshotIndex); - // this.props.setState((prevState) => ({ currentSnapshotIndex: prevState.currentSnapshotIndex - 1 })); }, + status: + snapshotDetails[1]?.status !== "Completed" + ? snapshotDetails[1]?.status + : null, isActive: currentSnapshotIndex === 1, // }, ]} @@ -735,13 +740,13 @@ class SnapshotDetails extends Component {

Status:{" "} - {Utilities.snapshotStatusToDisplayName(snapshotDetails?.status)} + {Utilities.snapshotStatusToDisplayName(snapshotDetail?.status)}

- {snapshotDetails?.status !== "InProgress" && ( + {snapshotDetail?.status !== "InProgress" && ( this.viewLogs()}> View logs @@ -750,7 +755,7 @@ class SnapshotDetails extends Component {
- {snapshotDetails?.status === "InProgress" ? ( + {snapshotDetail?.status === "InProgress" ? (

@@ -760,7 +765,7 @@ class SnapshotDetails extends Component {

) : (
- {!isEmpty(snapshotDetails?.volumes) || + {!isEmpty(snapshotDetail?.volumes) || !isEmpty(this.preSnapshotScripts()) || !isEmpty(this.postSnapshotScripts()) ? (
@@ -786,20 +791,20 @@ class SnapshotDetails extends Component {

Volumes

- {snapshotDetails?.volumes?.length > 3 ? ( + {snapshotDetail?.volumes?.length > 3 ? (
this.toggleShowAllVolumes()} > - Show all {snapshotDetails?.volumes?.length} volumes + Show all {snapshotDetail?.volumes?.length} volumes
) : null}
- {!isEmpty(snapshotDetails?.volumes) ? ( + {!isEmpty(snapshotDetail?.volumes) ? ( this.renderShowAllVolumes( - snapshotDetails?.volumes?.slice(0, 3) + snapshotDetail?.volumes?.slice(0, 3) ) ) : (
@@ -877,8 +882,8 @@ class SnapshotDetails extends Component {
- {(!isEmpty(snapshotDetails?.errors) || - !isEmpty(snapshotDetails?.warnings)) && ( + {(!isEmpty(snapshotDetail?.errors) || + !isEmpty(snapshotDetail?.warnings)) && (
@@ -887,25 +892,25 @@ class SnapshotDetails extends Component {

Errors and warnings

- {snapshotDetails?.errors?.length > 3 && + {snapshotDetail?.errors?.length > 3 && selectedErrorsWarningTab === "Errors" ? (
this.toggleShowAllErrors()} > - Show all {snapshotDetails?.errors?.length} errors{" "} + Show all {snapshotDetail?.errors?.length} errors{" "}
) : null} - {snapshotDetails?.warnings?.length > 3 && + {snapshotDetail?.warnings?.length > 3 && selectedErrorsWarningTab === "Warnings" ? (
this.toggleShowAllWarnings()} > - Show all {snapshotDetails?.warnings?.length}{" "} + Show all {snapshotDetail?.warnings?.length}{" "} warnings{" "}
@@ -917,9 +922,9 @@ class SnapshotDetails extends Component {
{selectedErrorsWarningTab === "Errors" ? ( - !isEmpty(snapshotDetails?.errors) ? ( + !isEmpty(snapshotDetail?.errors) ? ( this.renderShowAllErrors( - snapshotDetails?.errors.slice(0, 3) + snapshotDetail?.errors.slice(0, 3) ) ) : (
@@ -930,9 +935,9 @@ class SnapshotDetails extends Component {
) ) : selectedErrorsWarningTab === "Warnings" && - !isEmpty(snapshotDetails?.warnings) ? ( + !isEmpty(snapshotDetail?.warnings) ? ( this.renderShowAllWarnings( - snapshotDetails?.warnings?.slice(0, 3) + snapshotDetail?.warnings?.slice(0, 3) ) ) : (
@@ -980,6 +985,9 @@ class SnapshotDetails extends Component { featureName = "backup"; } + const { params } = this.props; + + const snapshotName = params.id; if (loading) { return (
@@ -987,7 +995,7 @@ class SnapshotDetails extends Component {
); } - console.log(this.props, "props"); + return (
@@ -996,33 +1004,14 @@ class SnapshotDetails extends Component { {Utilities.toTitleCase(featureName)} > - {snapshotDetails[currentSnapshotIndex]?.name} + {snapshotName}

- {/* only for EC???/ */} - {snapshotDetails.length > 1 && ( -
- {currentSnapshotIndex === 0 && ( - - )} - {currentSnapshotIndex === 1 && ( - - )} -
- )}
- {this.renderSnapshot(snapshotDetails[currentSnapshotIndex])} + + {this.renderSnapshot( + snapshotDetails, + snapshotDetails[currentSnapshotIndex] + )} {showScriptsOutput && scriptOutput && ( Date: Fri, 27 Dec 2024 10:10:29 -0500 Subject: [PATCH 04/21] clean up --- web/src/components/snapshots/SnapshotDetails.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index ad54468e76..b7d096c265 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -130,7 +130,7 @@ class SnapshotDetails extends Component { } navigateSnapshot = (direction) => { const { currentSnapshotIndex, snapshotDetails } = this.state; - console.log(direction, "direction", currentSnapshotIndex); + const newIndex = direction === "infra" ? currentSnapshotIndex + 1 @@ -138,7 +138,7 @@ class SnapshotDetails extends Component { if (newIndex >= 0 && newIndex < snapshotDetails.length) { const series = this.getSeriesDataForSnapshot(snapshotDetails[newIndex]); - console.log(newIndex, "ndew"); + this.setState({ currentSnapshotIndex: newIndex, series, From bc8fab0c3e6cba5c54a6bdd6d25613df60926ce6 Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Fri, 27 Dec 2024 10:12:33 -0500 Subject: [PATCH 05/21] f --- web/src/components/snapshots/SnapshotDetails.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index b7d096c265..141cdbd5be 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -130,7 +130,7 @@ class SnapshotDetails extends Component { } navigateSnapshot = (direction) => { const { currentSnapshotIndex, snapshotDetails } = this.state; - + console.log("test", currentSnapshotIndex, snapshotDetails); const newIndex = direction === "infra" ? currentSnapshotIndex + 1 From 3986ecd5161a3a7e125bcd2ca9f75585bd6d9a9c Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Tue, 7 Jan 2025 15:18:41 -0500 Subject: [PATCH 06/21] accordian for DR --- .../components/snapshots/SnapshotDetails.jsx | 540 ++++++++++-------- 1 file changed, 303 insertions(+), 237 deletions(-) diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index 141cdbd5be..e2c69c5439 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -14,6 +14,7 @@ import ViewSnapshotLogsModal from "../modals/ViewSnapshotLogsModal"; import ErrorModal from "../modals/ErrorModal"; import { Utilities } from "../../utilities/utilities"; import Toggle from "../shared/Toggle"; +import Icon from "@components/Icon"; dayjs.extend(minMax); dayjs.extend(duration); @@ -130,7 +131,7 @@ class SnapshotDetails extends Component { } navigateSnapshot = (direction) => { const { currentSnapshotIndex, snapshotDetails } = this.state; - console.log("test", currentSnapshotIndex, snapshotDetails); + const newIndex = direction === "infra" ? currentSnapshotIndex + 1 @@ -691,269 +692,334 @@ class SnapshotDetails extends Component { const { params } = this.props; const snapshotName = params.id; + const activeId = "snapshot"; + + console.log(snapshotDetails, "detials"); return (
-
-
+
+

{snapshotName}

-

- Total size:{" "} - - {snapshotDetail?.volumeSizeHuman} - -

-
- {/* only for EC???/ */} - {snapshotDetails?.length > 1 && ( -
- { - this.navigateSnapshot("application"); - }, - status: - snapshotDetails[0]?.status !== "Completed" - ? snapshotDetails[0]?.status - : null, - isActive: currentSnapshotIndex === 0, // - }, - { - title: "Infrastructure", - onClick: () => { - this.navigateSnapshot("infra"); - }, - status: - snapshotDetails[1]?.status !== "Completed" - ? snapshotDetails[1]?.status - : null, - isActive: currentSnapshotIndex === 1, // - }, - ]} - /> -
- )} -
-

- Status:{" "} - - {Utilities.snapshotStatusToDisplayName(snapshotDetail?.status)} - -

-
- {snapshotDetail?.status !== "InProgress" && ( - this.viewLogs()}> - View logs +
+

+ Total size:{" "} + + {snapshotDetail?.volumeSizeHuman} - )} +

+

+ Status:{" "} + + {Utilities.snapshotStatusToDisplayName( + snapshotDetail?.status + )} + +

-
- - {snapshotDetail?.status === "InProgress" ? ( -
- -

- {" "} - This {featureName} has not completed yet, check back soon{" "} -

-
- ) : ( -
- {!isEmpty(snapshotDetail?.volumes) || - !isEmpty(this.preSnapshotScripts()) || - !isEmpty(this.postSnapshotScripts()) ? ( -
-

- {Utilities.toTitleCase(featureName)} timeline -

-
- - {this.renderTimeInterval()} -
-
- ) : null} + {/* only render accordian if there are more than one snapshot for EC */} + {snapshotDetails.length > 1 && + snapshotDetails.map((snapshotDetail) => ( +
+
+
+ + +
+
+

+ Status:{" "} this.toggleShowAllPostScripts()} + className={`status-indicator ${snapshotDetail?.status?.toLowerCase()} u-marginLeft--5`} > - Show all {this.postSnapshotScripts()?.length}{" "} - post-scripts + {Utilities.snapshotStatusToDisplayName( + snapshotDetail?.status + )} -

- ) : null} -
-
- {this.renderScriptsTabs()} -
-
-
- {selectedScriptTab === "Pre-snapshot scripts" ? ( - !isEmpty(this.preSnapshotScripts()) ? ( - this.renderShowAllScripts( - this.preSnapshotScripts().slice(0, 3) - ) - ) : ( -
-

- {" "} - No pre-{featureName} scripts to display{" "} -

-
- ) - ) : selectedScriptTab === "Post-snapshot scripts" && - !isEmpty(this.postSnapshotScripts()) ? ( - this.renderShowAllScripts( - this.postSnapshotScripts().slice(0, 3) - ) - ) : ( -
-

- {" "} - No post-{featureName} scripts to display{" "}

-
- )} -
-
-
-
- - {(!isEmpty(snapshotDetail?.errors) || - !isEmpty(snapshotDetail?.warnings)) && ( -
-
-
-
-
-

- Errors and warnings -

- {snapshotDetail?.errors?.length > 3 && - selectedErrorsWarningTab === "Errors" ? ( -
+
+ {snapshotDetail?.status !== "InProgress" && ( this.toggleShowAllErrors()} + className="link" + onClick={() => this.viewLogs()} > - Show all {snapshotDetail?.errors?.length} errors{" "} + View logs -
- ) : null} - {snapshotDetail?.warnings?.length > 3 && - selectedErrorsWarningTab === "Warnings" ? ( -
- this.toggleShowAllWarnings()} - > - Show all {snapshotDetail?.warnings?.length}{" "} - warnings{" "} - -
- ) : null} -
-
- {this.renderErrorsWarningsTabs()} + )} +
-
-
- {selectedErrorsWarningTab === "Errors" ? ( - !isEmpty(snapshotDetail?.errors) ? ( - this.renderShowAllErrors( - snapshotDetail?.errors.slice(0, 3) - ) - ) : ( -
-

- {" "} - No errors to display{" "} -

-
- ) - ) : selectedErrorsWarningTab === "Warnings" && - !isEmpty(snapshotDetail?.warnings) ? ( - this.renderShowAllWarnings( - snapshotDetail?.warnings?.slice(0, 3) - ) - ) : ( -
-

+ + {snapshotDetail?.status === "InProgress" ? ( +

+ +

{" "} - No warnings to display{" "} + This {featureName} has not completed yet, check back + soon{" "}

+ ) : ( +
+ {!isEmpty(snapshotDetail?.volumes) || + !isEmpty(this.preSnapshotScripts()) || + !isEmpty(this.postSnapshotScripts()) ? ( +
+

+ {Utilities.toTitleCase(featureName)} timeline +

+
+ + {this.renderTimeInterval()} +
+
+ ) : null} + +
+
+
+
+

+ Volumes +

+ {snapshotDetail?.volumes?.length > 3 ? ( +
+ + this.toggleShowAllVolumes() + } + > + Show all{" "} + {snapshotDetail?.volumes?.length}{" "} + volumes + +
+ ) : null} +
+ {!isEmpty(snapshotDetail?.volumes) ? ( + this.renderShowAllVolumes( + snapshotDetail?.volumes?.slice(0, 3) + ) + ) : ( +
+

+ {" "} + No volumes to display{" "} +

+
+ )} +
+
+
+
+

+ Scripts +

+ {this.preSnapshotScripts()?.length > 3 && + selectedScriptTab === + "Pre-snapshot scripts" ? ( +
+ + this.toggleShowAllPreScripts() + } + > + Show all{" "} + {this.preSnapshotScripts()?.length}{" "} + pre-scripts + +
+ ) : null} + {this.postSnapshotScripts()?.length > 3 && + selectedScriptTab === + "Post-snapshot scripts" ? ( +
+ + this.toggleShowAllPostScripts() + } + > + Show all{" "} + {this.postSnapshotScripts()?.length}{" "} + post-scripts + +
+ ) : null} +
+
+ {this.renderScriptsTabs()} +
+
+
+ {selectedScriptTab === + "Pre-snapshot scripts" ? ( + !isEmpty(this.preSnapshotScripts()) ? ( + this.renderShowAllScripts( + this.preSnapshotScripts().slice(0, 3) + ) + ) : ( +
+

+ {" "} + No pre-{featureName} scripts to + display{" "} +

+
+ ) + ) : selectedScriptTab === + "Post-snapshot scripts" && + !isEmpty(this.postSnapshotScripts()) ? ( + this.renderShowAllScripts( + this.postSnapshotScripts().slice(0, 3) + ) + ) : ( +
+

+ {" "} + No post-{featureName} scripts to display{" "} +

+
+ )} +
+
+
+
+ + {(!isEmpty(snapshotDetail?.errors) || + !isEmpty(snapshotDetail?.warnings)) && ( +
+
+
+
+
+

+ Errors and warnings +

+ {snapshotDetail?.errors?.length > 3 && + selectedErrorsWarningTab === "Errors" ? ( +
+ + this.toggleShowAllErrors() + } + > + Show all{" "} + {snapshotDetail?.errors?.length}{" "} + errors{" "} + +
+ ) : null} + {snapshotDetail?.warnings?.length > 3 && + selectedErrorsWarningTab === + "Warnings" ? ( +
+ + this.toggleShowAllWarnings() + } + > + Show all{" "} + {snapshotDetail?.warnings?.length}{" "} + warnings{" "} + +
+ ) : null} +
+
+ {this.renderErrorsWarningsTabs()} +
+
+
+ {selectedErrorsWarningTab === "Errors" ? ( + !isEmpty(snapshotDetail?.errors) ? ( + this.renderShowAllErrors( + snapshotDetail?.errors.slice(0, 3) + ) + ) : ( +
+

+ {" "} + No errors to display{" "} +

+
+ ) + ) : selectedErrorsWarningTab === + "Warnings" && + !isEmpty(snapshotDetail?.warnings) ? ( + this.renderShowAllWarnings( + snapshotDetail?.warnings?.slice(0, 3) + ) + ) : ( +
+

+ {" "} + No warnings to display{" "} +

+
+ )} +
+
+
+
+ )} +
)}
- )} -
- )} + ))} +
); }; From 308e4506dbe24e3125142b6eb3267fd5d4bfac20 Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Tue, 7 Jan 2025 16:28:52 -0500 Subject: [PATCH 07/21] set default expanded accordion if there's a failure --- .../components/snapshots/SnapshotDetails.jsx | 470 ++++++++++-------- web/src/scss/utilities/base.scss | 2 +- 2 files changed, 254 insertions(+), 218 deletions(-) diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index e2c69c5439..d79214fc22 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -1,4 +1,4 @@ -import { Component } from "react"; +import { Component, useState } from "react"; import { withRouter } from "@src/utilities/react-router-utilities"; import MonacoEditor from "@monaco-editor/react"; import Modal from "react-modal"; @@ -24,6 +24,7 @@ let mapColors = {}; class SnapshotDetails extends Component { state = { + activeIds: [], showScriptsOutput: false, scriptOutput: "", selectedTab: "stdout", @@ -677,12 +678,31 @@ class SnapshotDetails extends Component { ); }; + componentDidUpdate = (lastProps, lastState) => { + console.log("hello"); + const { snapshotDetails } = this.state; + + if ( + lastState.snapshotDetails !== snapshotDetails && + snapshotDetails.length > 0 + ) { + // Filter snapshots with status not equal to "Completed" + const activeIds = snapshotDetails + .filter((snapshotDetail) => snapshotDetail.status !== "Completed") + .map((snapshotDetail) => snapshotDetail.name); + + // Set the filtered snapshot names in state + this.setState({ activeIds }); + } + }; + renderSnapshot = (snapshotDetails, snapshotDetail) => { const { series, selectedScriptTab, selectedErrorsWarningTab, currentSnapshotIndex, + activeIds, } = this.state; const { isEmbeddedCluster, navigate } = this.props; let featureName = "snapshot"; @@ -692,9 +712,14 @@ class SnapshotDetails extends Component { const { params } = this.props; const snapshotName = params.id; - const activeId = "snapshot"; + const toggleAccordion = (name) => { + this.setState({ + activeIds: this.state.activeIds.includes(name) + ? this.state.activeIds.filter((id) => id !== name) + : [...this.state.activeIds, name], + }); + }; - console.log(snapshotDetails, "detials"); return (
@@ -723,287 +748,217 @@ class SnapshotDetails extends Component {
{/* only render accordian if there are more than one snapshot for EC */} {snapshotDetails.length > 1 && - snapshotDetails.map((snapshotDetail) => ( -
-
-
- - -
-
-

- Status:{" "} - - {Utilities.snapshotStatusToDisplayName( - snapshotDetail?.status - )} + snapshotDetails.map((snapshotDetail) => { + const isActive = activeIds.includes(snapshotDetail.name); + return ( +

+
+
+ - {snapshotDetail?.status === "InProgress" ? ( -
- -

- {" "} - This {featureName} has not completed yet, check back - soon{" "} -

-
- ) : ( +
- {!isEmpty(snapshotDetail?.volumes) || - !isEmpty(this.preSnapshotScripts()) || - !isEmpty(this.postSnapshotScripts()) ? ( -
-

- {Utilities.toTitleCase(featureName)} timeline -

-
- - {this.renderTimeInterval()} -
-
- ) : null} +
+

+ Size:{" "} + + {snapshotDetail?.volumeSizeHuman} + +

+

+ Status:{" "} + + {Utilities.snapshotStatusToDisplayName( + snapshotDetail?.status + )} + +

+
+
+ {snapshotDetail?.status !== "InProgress" && ( + this.viewLogs()} + > + View logs + + )} +
+
-
-
-
-
-

- Volumes -

- {snapshotDetail?.volumes?.length > 3 ? ( -
- - this.toggleShowAllVolumes() - } - > - Show all{" "} - {snapshotDetail?.volumes?.length}{" "} - volumes - -
- ) : null} + {snapshotDetail?.status === "InProgress" ? ( +
+ +

+ {" "} + This {featureName} has not completed yet, check + back soon{" "} +

+
+ ) : ( +
+ {!isEmpty(snapshotDetail?.volumes) || + !isEmpty(this.preSnapshotScripts()) || + !isEmpty(this.postSnapshotScripts()) ? ( +
+

+ {Utilities.toTitleCase(featureName)} timeline +

+
+ + {this.renderTimeInterval()}
- {!isEmpty(snapshotDetail?.volumes) ? ( - this.renderShowAllVolumes( - snapshotDetail?.volumes?.slice(0, 3) - ) - ) : ( -
-

- {" "} - No volumes to display{" "} -

-
- )}
-
-
-
-

- Scripts + ) : null} + +

+
+
+
+

+ Volumes

- {this.preSnapshotScripts()?.length > 3 && - selectedScriptTab === - "Pre-snapshot scripts" ? ( -
- - this.toggleShowAllPreScripts() - } - > - Show all{" "} - {this.preSnapshotScripts()?.length}{" "} - pre-scripts - -
- ) : null} - {this.postSnapshotScripts()?.length > 3 && - selectedScriptTab === - "Post-snapshot scripts" ? ( + {snapshotDetail?.volumes?.length > 3 ? (
- this.toggleShowAllPostScripts() + this.toggleShowAllVolumes() } > Show all{" "} - {this.postSnapshotScripts()?.length}{" "} - post-scripts + {snapshotDetail?.volumes?.length}{" "} + volumes
) : null}
-
- {this.renderScriptsTabs()} -
-
-
- {selectedScriptTab === - "Pre-snapshot scripts" ? ( - !isEmpty(this.preSnapshotScripts()) ? ( - this.renderShowAllScripts( - this.preSnapshotScripts().slice(0, 3) - ) - ) : ( -
-

- {" "} - No pre-{featureName} scripts to - display{" "} -

-
- ) - ) : selectedScriptTab === - "Post-snapshot scripts" && - !isEmpty(this.postSnapshotScripts()) ? ( - this.renderShowAllScripts( - this.postSnapshotScripts().slice(0, 3) + {!isEmpty(snapshotDetail?.volumes) ? ( + this.renderShowAllVolumes( + snapshotDetail?.volumes?.slice(0, 3) ) ) : (

{" "} - No post-{featureName} scripts to display{" "} + No volumes to display{" "}

)}
-
-
-
- - {(!isEmpty(snapshotDetail?.errors) || - !isEmpty(snapshotDetail?.warnings)) && ( -
-

- Errors and warnings + Scripts

- {snapshotDetail?.errors?.length > 3 && - selectedErrorsWarningTab === "Errors" ? ( + {this.preSnapshotScripts()?.length > 3 && + selectedScriptTab === + "Pre-snapshot scripts" ? (
- this.toggleShowAllErrors() + this.toggleShowAllPreScripts() } > Show all{" "} - {snapshotDetail?.errors?.length}{" "} - errors{" "} + {this.preSnapshotScripts()?.length}{" "} + pre-scripts
) : null} - {snapshotDetail?.warnings?.length > 3 && - selectedErrorsWarningTab === - "Warnings" ? ( + {this.postSnapshotScripts()?.length > 3 && + selectedScriptTab === + "Post-snapshot scripts" ? (
- this.toggleShowAllWarnings() + this.toggleShowAllPostScripts() } > Show all{" "} - {snapshotDetail?.warnings?.length}{" "} - warnings{" "} + {this.postSnapshotScripts()?.length}{" "} + post-scripts
) : null}
- {this.renderErrorsWarningsTabs()} + {this.renderScriptsTabs()}
- {selectedErrorsWarningTab === "Errors" ? ( - !isEmpty(snapshotDetail?.errors) ? ( - this.renderShowAllErrors( - snapshotDetail?.errors.slice(0, 3) + {selectedScriptTab === + "Pre-snapshot scripts" ? ( + !isEmpty(this.preSnapshotScripts()) ? ( + this.renderShowAllScripts( + this.preSnapshotScripts().slice(0, 3) ) ) : (

{" "} - No errors to display{" "} + No pre-{featureName} scripts to + display{" "}

) - ) : selectedErrorsWarningTab === - "Warnings" && - !isEmpty(snapshotDetail?.warnings) ? ( - this.renderShowAllWarnings( - snapshotDetail?.warnings?.slice(0, 3) + ) : selectedScriptTab === + "Post-snapshot scripts" && + !isEmpty(this.postSnapshotScripts()) ? ( + this.renderShowAllScripts( + this.postSnapshotScripts().slice(0, 3) ) ) : (

{" "} - No warnings to display{" "} + No post-{featureName} scripts to + display{" "}

)} @@ -1011,14 +966,95 @@ class SnapshotDetails extends Component {
- )} -
- )} + + {(!isEmpty(snapshotDetail?.errors) || + !isEmpty(snapshotDetail?.warnings)) && ( +
+
+
+
+
+

+ Errors and warnings +

+ {snapshotDetail?.errors?.length > 3 && + selectedErrorsWarningTab === + "Errors" ? ( +
+ + this.toggleShowAllErrors() + } + > + Show all{" "} + {snapshotDetail?.errors?.length}{" "} + errors{" "} + +
+ ) : null} + {snapshotDetail?.warnings?.length > 3 && + selectedErrorsWarningTab === + "Warnings" ? ( +
+ + this.toggleShowAllWarnings() + } + > + Show all{" "} + {snapshotDetail?.warnings?.length}{" "} + warnings{" "} + +
+ ) : null} +
+
+ {this.renderErrorsWarningsTabs()} +
+
+
+ {selectedErrorsWarningTab === "Errors" ? ( + !isEmpty(snapshotDetail?.errors) ? ( + this.renderShowAllErrors( + snapshotDetail?.errors.slice(0, 3) + ) + ) : ( +
+

+ {" "} + No errors to display{" "} +

+
+ ) + ) : selectedErrorsWarningTab === + "Warnings" && + !isEmpty(snapshotDetail?.warnings) ? ( + this.renderShowAllWarnings( + snapshotDetail?.warnings?.slice(0, 3) + ) + ) : ( +
+

+ {" "} + No warnings to display{" "} +

+
+ )} +
+
+
+
+ )} +
+ )} +
-
- ))} + ); + })}
); diff --git a/web/src/scss/utilities/base.scss b/web/src/scss/utilities/base.scss index 480333a6a2..863e1a4432 100644 --- a/web/src/scss/utilities/base.scss +++ b/web/src/scss/utilities/base.scss @@ -247,7 +247,7 @@ body a { background-color: #4999ad; } &.partiallyfailed::before { - background-color: #f1843c; + background-color: #bc4752; } &.deleting::before { background-color: #f7b500; From d154f28c67aa3d52dda97e9b447fb00204b03da6 Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Wed, 8 Jan 2025 12:33:50 -0500 Subject: [PATCH 08/21] move size and status to left align --- .../components/snapshots/SnapshotDetails.jsx | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index d79214fc22..abd88eb255 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -679,7 +679,6 @@ class SnapshotDetails extends Component { }; componentDidUpdate = (lastProps, lastState) => { - console.log("hello"); const { snapshotDetails } = this.state; if ( @@ -727,22 +726,19 @@ class SnapshotDetails extends Component {

{snapshotName}

-
+

Total size:{" "} {snapshotDetail?.volumeSizeHuman}

-

+

Status:{" "} - {Utilities.snapshotStatusToDisplayName( - snapshotDetail?.status - )} - + className={`tw-mb-4 status-indicator ${snapshotDetail?.status?.toLowerCase()} u-marginLeft--5`} + > + {Utilities.snapshotStatusToDisplayName(snapshotDetail?.status)}

@@ -790,22 +786,21 @@ class SnapshotDetails extends Component { } tw-overflow-hidden`} >
-
-

+

+

Size:{" "} {snapshotDetail?.volumeSizeHuman}

-

+

Status:{" "} - {Utilities.snapshotStatusToDisplayName( - snapshotDetail?.status - )} - + className={`tw-mb-4 status-indicator ${snapshotDetail?.status?.toLowerCase()} u-marginLeft--5`} + > + {Utilities.snapshotStatusToDisplayName( + snapshotDetail?.status + )}

From d294a6c605e724f4149b1177cd383c213dd68ba3 Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Thu, 9 Jan 2025 12:15:21 -0500 Subject: [PATCH 09/21] remove volume count --- web/src/components/snapshots/SnapshotRow.jsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/src/components/snapshots/SnapshotRow.jsx b/web/src/components/snapshots/SnapshotRow.jsx index b7d204fed3..9052c330bc 100644 --- a/web/src/components/snapshots/SnapshotRow.jsx +++ b/web/src/components/snapshots/SnapshotRow.jsx @@ -80,10 +80,6 @@ class SnapshotRow extends Component { {snapshot?.volumeSizeHuman}{" "}

)} -

- {" "} - {snapshot?.volumeSuccessCount}/{snapshot?.volumeCount} -

{snapshot?.status === "Completed" ? (

From 0abc12860372c9cddc2eefe62d3691bfa77d9a16 Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Thu, 9 Jan 2025 14:17:26 -0500 Subject: [PATCH 10/21] remove changes to toggle --- web/src/components/shared/Toggle.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/web/src/components/shared/Toggle.tsx b/web/src/components/shared/Toggle.tsx index e8cdeec4fe..4af0d915bf 100644 --- a/web/src/components/shared/Toggle.tsx +++ b/web/src/components/shared/Toggle.tsx @@ -5,7 +5,6 @@ interface Props { onClick: () => void; isActive: boolean; title: string; - status?: string; }[]; } @@ -20,12 +19,6 @@ export default function Tooltip(props: Props) { onClick={item.onClick} > {item.title} - - {item.status && ( - - )}

); })} From 7cfcf87aac7fb98881f3108864c0080e928a1a90 Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Thu, 9 Jan 2025 14:26:07 -0500 Subject: [PATCH 11/21] reorder imports --- .../components/snapshots/SnapshotDetails.jsx | 38 ++++++------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index abd88eb255..c73c31c6a0 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -1,20 +1,20 @@ -import { Component, useState } from "react"; -import { withRouter } from "@src/utilities/react-router-utilities"; import MonacoEditor from "@monaco-editor/react"; -import Modal from "react-modal"; +import dayjs from "dayjs"; +import duration from "dayjs/plugin/duration"; +import minMax from "dayjs/plugin/minMax"; import filter from "lodash/filter"; import isEmpty from "lodash/isEmpty"; +import { Component, useState } from "react"; import ReactApexChart from "react-apexcharts"; -import dayjs from "dayjs"; -import minMax from "dayjs/plugin/minMax"; -import duration from "dayjs/plugin/duration"; -import Loader from "../shared/Loader"; +import Modal from "react-modal"; + +import Icon from "@components/Icon"; +import { withRouter } from "@src/utilities/react-router-utilities"; +import { Utilities } from "../../utilities/utilities"; +import ErrorModal from "../modals/ErrorModal"; import ShowAllModal from "../modals/ShowAllModal"; import ViewSnapshotLogsModal from "../modals/ViewSnapshotLogsModal"; -import ErrorModal from "../modals/ErrorModal"; -import { Utilities } from "../../utilities/utilities"; -import Toggle from "../shared/Toggle"; -import Icon from "@components/Icon"; +import Loader from "../shared/Loader"; dayjs.extend(minMax); dayjs.extend(duration); @@ -130,23 +130,7 @@ class SnapshotDetails extends Component { this.fetchSnapshotDetails(); } } - navigateSnapshot = (direction) => { - const { currentSnapshotIndex, snapshotDetails } = this.state; - const newIndex = - direction === "infra" - ? currentSnapshotIndex + 1 - : currentSnapshotIndex - 1; - - if (newIndex >= 0 && newIndex < snapshotDetails.length) { - const series = this.getSeriesDataForSnapshot(snapshotDetails[newIndex]); - - this.setState({ - currentSnapshotIndex: newIndex, - series, - }); - } - }; getSeriesDataForSnapshot = (snapshot) => { let series = []; if (!isEmpty(snapshot?.volumes)) { From e296ca4e2a1db76acd906b1b08799edace3065f9 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Thu, 9 Jan 2025 16:57:29 +0100 Subject: [PATCH 12/21] undo changes to ListBackupsForApp --- pkg/kotsadmsnapshot/backup.go | 62 +++-------------------------------- 1 file changed, 4 insertions(+), 58 deletions(-) diff --git a/pkg/kotsadmsnapshot/backup.go b/pkg/kotsadmsnapshot/backup.go index 1a485cef69..e1d2752e17 100644 --- a/pkg/kotsadmsnapshot/backup.go +++ b/pkg/kotsadmsnapshot/backup.go @@ -743,9 +743,10 @@ func ListBackupsForApp(ctx context.Context, kotsadmNamespace string, appID strin continue } - back, err := ParseVeleroBackup(ctx, veleroBackup) - if err != nil { - return nil, errors.Wrap(err, "failed to parse velero backup") + backup := types.Backup{ + Name: veleroBackup.Name, + Status: types.GetStatusFromBackupPhase(veleroBackup.Status.Phase), + AppID: appID, } back.AppID = appID @@ -755,61 +756,6 @@ func ListBackupsForApp(ctx context.Context, kotsadmNamespace string, appID strin return backups, nil } -func ParseVeleroBackup(ctx context.Context, veleroBackup velerov1.Backup) (*types.Backup, error) { - backup := types.Backup{ - Name: veleroBackup.Name, - Status: types.GetStatusFromBackupPhase(veleroBackup.Status.Phase), - } - - if veleroBackup.Status.StartTimestamp != nil { - startedAt := veleroBackup.Status.StartTimestamp.Time.UTC() - backup.StartedAt = &startedAt - } - if veleroBackup.Status.CompletionTimestamp != nil { - finishedAt := veleroBackup.Status.CompletionTimestamp.Time.UTC() - backup.FinishedAt = &finishedAt - } - if veleroBackup.Status.Expiration != nil { - expiresAt := veleroBackup.Status.Expiration.Time.UTC() - backup.ExpiresAt = &expiresAt - } - sequence, ok := veleroBackup.Annotations["kots.io/app-sequence"] - if ok { - s, err := strconv.ParseInt(sequence, 10, 64) - if err != nil { - return nil, errors.Wrap(err, "failed to parse app sequence") - } - - backup.Sequence = s - } - if backup.Status == "" { - backup.Status = types.BackupStatusInProgress - } - - trigger, ok := veleroBackup.Annotations[types.BackupTriggerAnnotation] - if ok { - backup.Trigger = trigger - } - - supportBundleID, ok := veleroBackup.Annotations["kots.io/support-bundle-id"] - if ok { - backup.SupportBundleID = supportBundleID - } - - if backup.Status != types.BackupStatusInProgress { - volumeSummary, err := getSnapshotVolumeSummary(ctx, &veleroBackup) - if err != nil { - return nil, errors.Wrap(err, "failed to get volume summary") - } - - backup.VolumeCount = volumeSummary.VolumeCount - backup.VolumeSuccessCount = volumeSummary.VolumeSuccessCount - backup.VolumeBytes = volumeSummary.VolumeBytes - backup.VolumeSizeHuman = volumeSummary.VolumeSizeHuman - } - return &backup, nil -} - func ListInstanceBackups(ctx context.Context, kotsadmNamespace string) ([]*types.Backup, error) { cfg, err := k8sutil.GetClusterConfig() if err != nil { From ae6bd190f02c0bcfbfdf7f1b034191827c7955ea Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Thu, 9 Jan 2025 16:02:58 -0500 Subject: [PATCH 13/21] update with new aggregated endpoint --- .../components/snapshots/SnapshotDetails.jsx | 112 +++++++++--------- 1 file changed, 59 insertions(+), 53 deletions(-) diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index c73c31c6a0..357ed92768 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -44,7 +44,7 @@ class SnapshotDetails extends Component { loading: true, currentSnapshotIndex: 0, - snapshotDetails: [], + snapshotDetails: {}, errorMessage: "", errorTitle: "", @@ -188,17 +188,10 @@ class SnapshotDetails extends Component { } const response = await res.json(); - const snapshotDetails = response.backupDetails; - const { currentSnapshotIndex } = this.state; - const series = this.getSeriesDataForSnapshot( - snapshotDetails[currentSnapshotIndex] - ); - this.setState({ loading: false, - snapshotDetails, + snapshotDetails: response, currentSnapshotIndex: 0, - series, errorMessage: "", errorTitle: "", }); @@ -214,14 +207,14 @@ class SnapshotDetails extends Component { } }; - preSnapshotScripts = () => { - return filter(this.state.snapshotDetails?.hooks, (hook) => { + preSnapshotScripts = (snapshotDetail) => { + return filter(snapshotDetail?.hooks, (hook) => { return hook.phase === "pre"; }); }; - postSnapshotScripts = () => { - return filter(this.state.snapshotDetails?.hooks, (hook) => { + postSnapshotScripts = (snapshotDetail) => { + return filter(snapshotDetail?.hooks, (hook) => { return hook.phase === "post"; }); }; @@ -388,8 +381,8 @@ class SnapshotDetails extends Component { ); }; - renderErrorsWarningsTabs = () => { - const { snapshotDetails, selectedErrorsWarningTab } = this.state; + renderErrorsWarningsTabs = (snapshotDetail) => { + const { selectedErrorsWarningTab } = this.state; const tabs = ["Errors", "Warnings"]; return (
@@ -405,14 +398,14 @@ class SnapshotDetails extends Component { {tab === "Errors" ? ( {" "} - {snapshotDetails?.errors?.length}{" "} + {snapshotDetail?.errors?.length}{" "} ) : ( {" "} - {!snapshotDetails?.warnings + {!snapshotDetail?.warnings ? "0" - : snapshotDetails?.warnings?.length}{" "} + : snapshotDetail?.warnings?.length}{" "} )}
@@ -589,20 +582,16 @@ class SnapshotDetails extends Component { return series; }; - renderTimeInterval = () => { - const { currentSnapshotIndex, snapshotDetails } = this.state; + renderTimeInterval = (snapshotDetail) => { let data; - if (!isEmpty(snapshotDetails[currentSnapshotIndex]?.volumes)) { - if (!isEmpty(snapshotDetails[currentSnapshotIndex]?.hooks)) { - data = [ - ...snapshotDetails[currentSnapshotIndex]?.volumes, - ...snapshotDetails[currentSnapshotIndex]?.hooks, - ]; + if (!isEmpty(snapshotDetail?.volumes)) { + if (!isEmpty(snapshotDetail?.hooks)) { + data = [...snapshotDetail?.volumes, ...snapshotDetail?.hooks]; } else { - data = snapshotDetails[currentSnapshotIndex]?.volumes; + data = snapshotDetail?.volumes; } - } else if (!isEmpty(snapshotDetails[currentSnapshotIndex]?.hooks)) { - data = snapshotDetails[currentSnapshotIndex]?.hooks; + } else if (!isEmpty(snapshotDetail?.hooks)) { + data = snapshotDetail?.hooks; } return (
@@ -664,13 +653,13 @@ class SnapshotDetails extends Component { componentDidUpdate = (lastProps, lastState) => { const { snapshotDetails } = this.state; - + const { backupDetails } = snapshotDetails; if ( lastState.snapshotDetails !== snapshotDetails && - snapshotDetails.length > 0 + backupDetails.length > 0 ) { // Filter snapshots with status not equal to "Completed" - const activeIds = snapshotDetails + const activeIds = backupDetails .filter((snapshotDetail) => snapshotDetail.status !== "Completed") .map((snapshotDetail) => snapshotDetail.name); @@ -679,7 +668,8 @@ class SnapshotDetails extends Component { } }; - renderSnapshot = (snapshotDetails, snapshotDetail) => { + renderSnapshot = () => { + const { backupDetails, backup } = this.state.snapshotDetails; const { series, selectedScriptTab, @@ -714,21 +704,21 @@ class SnapshotDetails extends Component {

Total size:{" "} - {snapshotDetail?.volumeSizeHuman} + {backup?.volumeSizeHuman}

Status:{" "} - {Utilities.snapshotStatusToDisplayName(snapshotDetail?.status)} + {Utilities.snapshotStatusToDisplayName(backup?.status)}

{/* only render accordian if there are more than one snapshot for EC */} - {snapshotDetails.length > 1 && - snapshotDetails.map((snapshotDetail) => { + {backupDetails && + backupDetails?.map((snapshotDetail) => { const isActive = activeIds.includes(snapshotDetail.name); return (
@@ -811,8 +801,10 @@ class SnapshotDetails extends Component { ) : (
{!isEmpty(snapshotDetail?.volumes) || - !isEmpty(this.preSnapshotScripts()) || - !isEmpty(this.postSnapshotScripts()) ? ( + !isEmpty(this.preSnapshotScripts(snapshotDetail)) || + !isEmpty( + this.postSnapshotScripts(snapshotDetail) + ) ? (

{Utilities.toTitleCase(featureName)} timeline @@ -820,11 +812,13 @@ class SnapshotDetails extends Component {

- {this.renderTimeInterval()} + {/* {this.renderTimeInterval(snapshotDetail)} */}
) : null} @@ -889,7 +883,8 @@ class SnapshotDetails extends Component {
) : null} - {this.postSnapshotScripts()?.length > 3 && + {this.postSnapshotScripts(snapshotDetail) + ?.length > 3 && selectedScriptTab === "Post-snapshot scripts" ? (
@@ -900,7 +895,11 @@ class SnapshotDetails extends Component { } > Show all{" "} - {this.postSnapshotScripts()?.length}{" "} + { + this.postSnapshotScripts( + snapshotDetail + )?.length + }{" "} post-scripts
@@ -913,9 +912,13 @@ class SnapshotDetails extends Component {
{selectedScriptTab === "Pre-snapshot scripts" ? ( - !isEmpty(this.preSnapshotScripts()) ? ( + !isEmpty( + this.preSnapshotScripts(snapshotDetail) + ) ? ( this.renderShowAllScripts( - this.preSnapshotScripts().slice(0, 3) + this.preSnapshotScripts( + snapshotDetail + ).slice(0, 3) ) ) : (
@@ -928,9 +931,13 @@ class SnapshotDetails extends Component { ) ) : selectedScriptTab === "Post-snapshot scripts" && - !isEmpty(this.postSnapshotScripts()) ? ( + !isEmpty( + this.postSnapshotScripts(snapshotDetail) + ) ? ( this.renderShowAllScripts( - this.postSnapshotScripts().slice(0, 3) + this.postSnapshotScripts( + snapshotDetail + ).slice(0, 3) ) ) : (
@@ -990,7 +997,9 @@ class SnapshotDetails extends Component { ) : null}
- {this.renderErrorsWarningsTabs()} + {this.renderErrorsWarningsTabs( + snapshotDetail + )}
@@ -1089,10 +1098,7 @@ class SnapshotDetails extends Component {

- {this.renderSnapshot( - snapshotDetails, - snapshotDetails[currentSnapshotIndex] - )} + {this.renderSnapshot()} {showScriptsOutput && scriptOutput && ( Date: Mon, 13 Jan 2025 15:37:40 -0500 Subject: [PATCH 14/21] fix bad merge --- pkg/kotsadmsnapshot/backup.go | 63 ++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/pkg/kotsadmsnapshot/backup.go b/pkg/kotsadmsnapshot/backup.go index e1d2752e17..f2283f4a63 100644 --- a/pkg/kotsadmsnapshot/backup.go +++ b/pkg/kotsadmsnapshot/backup.go @@ -743,10 +743,9 @@ func ListBackupsForApp(ctx context.Context, kotsadmNamespace string, appID strin continue } - backup := types.Backup{ - Name: veleroBackup.Name, - Status: types.GetStatusFromBackupPhase(veleroBackup.Status.Phase), - AppID: appID, + back, err := ParseVeleroBackup(ctx, veleroBackup) + if err != nil { + return nil, errors.Wrap(err, "failed to parse velero backup") } back.AppID = appID @@ -756,6 +755,62 @@ func ListBackupsForApp(ctx context.Context, kotsadmNamespace string, appID strin return backups, nil } +func ParseVeleroBackup(ctx context.Context, veleroBackup velerov1.Backup) (*types.Backup, error) { + backup := types.Backup{ + Name: veleroBackup.Name, + Status: types.GetStatusFromBackupPhase(veleroBackup.Status.Phase), + } + + if veleroBackup.Status.StartTimestamp != nil { + startedAt := veleroBackup.Status.StartTimestamp.Time.UTC() + backup.StartedAt = &startedAt + } + if veleroBackup.Status.CompletionTimestamp != nil { + finishedAt := veleroBackup.Status.CompletionTimestamp.Time.UTC() + backup.FinishedAt = &finishedAt + } + if veleroBackup.Status.Expiration != nil { + expiresAt := veleroBackup.Status.Expiration.Time.UTC() + backup.ExpiresAt = &expiresAt + } + sequence, ok := veleroBackup.Annotations["kots.io/app-sequence"] + if ok { + s, err := strconv.ParseInt(sequence, 10, 64) + if err != nil { + return nil, errors.Wrap(err, "failed to parse app sequence") + } + + backup.Sequence = s + } + if backup.Status == "" { + backup.Status = types.BackupStatusInProgress + } + + trigger, ok := veleroBackup.Annotations[types.BackupTriggerAnnotation] + if ok { + backup.Trigger = trigger + } + + supportBundleID, ok := veleroBackup.Annotations["kots.io/support-bundle-id"] + if ok { + backup.SupportBundleID = supportBundleID + } + + if backup.Status != types.BackupStatusInProgress { + volumeSummary, err := getSnapshotVolumeSummary(ctx, &veleroBackup) + if err != nil { + return nil, errors.Wrap(err, "failed to get volume summary") + } + + backup.VolumeCount = volumeSummary.VolumeCount + backup.VolumeSuccessCount = volumeSummary.VolumeSuccessCount + backup.VolumeBytes = volumeSummary.VolumeBytes + backup.VolumeSizeHuman = volumeSummary.VolumeSizeHuman + } + return &backup, nil +} + + func ListInstanceBackups(ctx context.Context, kotsadmNamespace string) ([]*types.Backup, error) { cfg, err := k8sutil.GetClusterConfig() if err != nil { From 714d501d2c485185b0e56092bacb61104881f4c2 Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Tue, 14 Jan 2025 14:41:41 -0500 Subject: [PATCH 15/21] update test to click on infrastructure --- e2e/playwright/tests/@smoke-test/test.spec.ts | 445 +++++++++--------- 1 file changed, 223 insertions(+), 222 deletions(-) diff --git a/e2e/playwright/tests/@smoke-test/test.spec.ts b/e2e/playwright/tests/@smoke-test/test.spec.ts index cc7ea645e0..2919313137 100644 --- a/e2e/playwright/tests/@smoke-test/test.spec.ts +++ b/e2e/playwright/tests/@smoke-test/test.spec.ts @@ -6,231 +6,231 @@ const { execSync } = require("child_process"); test("smoke test", async ({ page }) => { test.setTimeout(5 * 60 * 1000); // 5 minutes await login(page); - await uploadLicense(page, expect); - await expect(page.locator("#app")).toContainText( - "Install in airgapped environment", - { timeout: 15000 }, - ); - await page.getByText("download App Name from the Internet").click(); - await expect(page.locator("#app")).toContainText("Installing your license"); - await expect(page.locator("h3")).toContainText("My Example Config", { - timeout: 30000, - }); - await page.locator("#a_bool-group").getByText("a bool field").click(); - await page.locator("#a_required_text-group").getByRole("textbox").click(); - await page - .locator("#a_required_text-group") - .getByRole("textbox") - .fill("my required text field"); - await expect(page.locator("#version_sequence-group")).toContainText( - "This version is 0", - ); - await page.getByRole("button", { name: "Continue" }).click(); - await expect(page.locator("#app")).toContainText("Results", { - timeout: 60 * 1000, - }); - await expect(page.locator("#app")).toContainText("Sequence is 0"); - await page.getByRole("button", { name: "Deploy" }).click(); - await page.getByRole("button", { name: "Deploy anyway" }).click(); - await expect(page.locator("#app")).toContainText("Ready", { timeout: 30000 }); - await expect(page.locator("#app")).toContainText( - "Currently deployed version", - { timeout: 30000 }, - ); - await expect(page.locator("#app")).toContainText("Check for update"); - await expect(page.locator("#app")).toContainText("Redeploy", { - timeout: 15000, - }); - await expect(page.getByText("App Name")).toBeVisible(); - await expect(page.locator(".Dashboard--appIcon")).toBeVisible(); - await expect(page.locator("p").filter({ hasText: "License" })).toBeVisible(); - await page.getByText("Configure automatic updates").click(); - await expect(page.locator(".ConfigureUpdatesModal")).toContainText("Default"); - await expect(page.locator(".ConfigureUpdatesModal")).toContainText( - "Every 4 hours", - ); - await expect(page.locator("label")).toContainText( - "Enable automatic deployment", - ); - await page.locator(".replicated-select__control").click(); - await page.waitForTimeout(1000); - await page - .locator(".replicated-select__option") - .getByText("Weekly", { exact: true }) - .click(); - await page.waitForTimeout(1000); - await expect(page.locator(".ConfigureUpdatesModal")).toContainText("Weekly"); - await expect(page.locator(".ConfigureUpdatesModal")).toContainText( - "At 12:00 AM, only on Sunday", - ); - await page.getByRole("button", { name: "Update", exact: true }).click(); - await expect( - page.getByText("Automatically check for updates", { exact: true }), - ).not.toBeVisible(); - await page - .locator('svg.icons.clickable[data-tip="View release notes"]') - .click(); - await expect( - page.getByLabel("Release Notes").getByRole("paragraph"), - ).toContainText("release notes - updates"); - await page.getByRole("button", { name: "Close" }).click(); - await page.locator('span[data-tip="View deploy logs"]').click(); - await validateDeployLogs(page, expect); - await page.getByRole("link", { name: "Version history" }).click(); - await expect(page.locator(".currentVersion--wrapper")).toContainText( - "Sequence 0", - ); - await expect(page.locator("#app")).toContainText( - "Currently deployed version", - ); - await expect(page.locator("#app")).toContainText("Check for update"); - await expect(page.locator("#app")).toContainText( - "Configure automatic updates", - ); - await expect(page.getByRole("button")).toContainText("Redeploy"); - await page.getByText("Configure automatic updates").click(); - await expect(page.locator(".ConfigureUpdatesModal")).toContainText("Weekly"); - await expect(page.locator(".ConfigureUpdatesModal")).toContainText( - "At 12:00 AM, only on Sunday", - ); - await expect(page.locator("label")).toContainText( - "Enable automatic deployment", - ); - await page.getByRole("button", { name: "Cancel" }).click(); - await expect( - page.getByText("Automatically check for updates", { exact: true }), - ).not.toBeVisible(); - await page.locator('span[data-tip="View deploy logs"]').first().click(); - await validateDeployLogs(page, expect); - await page.getByRole("link", { name: "Config", exact: true }).click(); - await expect(page.locator("h3")).toContainText("My Example Config"); - await expect(page.locator("#version_sequence-group")).toContainText( - "This version is 1", - ); - await expect(page.getByRole("combobox")).toHaveValue("option_1"); - await page.getByRole("combobox").selectOption("option_2"); - await expect(page.getByRole("combobox")).toHaveValue("option_2"); - await expect(page.getByLabel("radio_1")).toBeChecked(); - await page.getByLabel("radio_2").click(); - await expect(page.getByLabel("radio_2")).toBeChecked(); - await expect(page.getByRole("button", { name: "Save config" })).toBeVisible(); - await page.getByRole("link", { name: "Troubleshoot" }).click(); - await expect( - page.getByRole("button", { name: "Analyze App Name" }), - ).toBeVisible(); - await page.getByRole("link", { name: "License" }).click(); - await expect(page.locator("#app")).toContainText("Airgap enabled"); - await expect(page.locator("#app")).toContainText("Snapshots enabled"); - await expect( - page.getByRole("button", { name: "Sync license" }), - ).toBeVisible(); - await page.getByRole("link", { name: "View files" }).click(); - await page.getByText("upstream", { exact: true }).click(); - await page - .getByRole("listitem", { name: "config.yaml" }) - .locator("div") - .click(); - await expect(page.locator(".view-lines")).toContainText("apiVersion"); - await page.getByText("Click here", { exact: true }).click(); - await expect(page.getByRole("heading")).toContainText( - "Edit patches for your kots application", - ); - await expect(page.getByText("Copy command").first()).toBeVisible(); + // await uploadLicense(page, expect); + // await expect(page.locator("#app")).toContainText( + // "Install in airgapped environment", + // { timeout: 15000 }, + // ); + // await page.getByText("download App Name from the Internet").click(); + // await expect(page.locator("#app")).toContainText("Installing your license"); + // await expect(page.locator("h3")).toContainText("My Example Config", { + // timeout: 30000, + // }); + // await page.locator("#a_bool-group").getByText("a bool field").click(); + // await page.locator("#a_required_text-group").getByRole("textbox").click(); + // await page + // .locator("#a_required_text-group") + // .getByRole("textbox") + // .fill("my required text field"); + // await expect(page.locator("#version_sequence-group")).toContainText( + // "This version is 0", + // ); + // await page.getByRole("button", { name: "Continue" }).click(); + // await expect(page.locator("#app")).toContainText("Results", { + // timeout: 60 * 1000, + // }); + // await expect(page.locator("#app")).toContainText("Sequence is 0"); + // await page.getByRole("button", { name: "Deploy" }).click(); + // await page.getByRole("button", { name: "Deploy anyway" }).click(); + // await expect(page.locator("#app")).toContainText("Ready", { timeout: 30000 }); + // await expect(page.locator("#app")).toContainText( + // "Currently deployed version", + // { timeout: 30000 }, + // ); + // await expect(page.locator("#app")).toContainText("Check for update"); + // await expect(page.locator("#app")).toContainText("Redeploy", { + // timeout: 15000, + // }); + // await expect(page.getByText("App Name")).toBeVisible(); + // await expect(page.locator(".Dashboard--appIcon")).toBeVisible(); + // await expect(page.locator("p").filter({ hasText: "License" })).toBeVisible(); + // await page.getByText("Configure automatic updates").click(); + // await expect(page.locator(".ConfigureUpdatesModal")).toContainText("Default"); + // await expect(page.locator(".ConfigureUpdatesModal")).toContainText( + // "Every 4 hours", + // ); + // await expect(page.locator("label")).toContainText( + // "Enable automatic deployment", + // ); + // await page.locator(".replicated-select__control").click(); + // await page.waitForTimeout(1000); + // await page + // .locator(".replicated-select__option") + // .getByText("Weekly", { exact: true }) + // .click(); + // await page.waitForTimeout(1000); + // await expect(page.locator(".ConfigureUpdatesModal")).toContainText("Weekly"); + // await expect(page.locator(".ConfigureUpdatesModal")).toContainText( + // "At 12:00 AM, only on Sunday", + // ); + // await page.getByRole("button", { name: "Update", exact: true }).click(); + // await expect( + // page.getByText("Automatically check for updates", { exact: true }), + // ).not.toBeVisible(); + // await page + // .locator('svg.icons.clickable[data-tip="View release notes"]') + // .click(); + // await expect( + // page.getByLabel("Release Notes").getByRole("paragraph"), + // ).toContainText("release notes - updates"); + // await page.getByRole("button", { name: "Close" }).click(); + // await page.locator('span[data-tip="View deploy logs"]').click(); + // await validateDeployLogs(page, expect); + // await page.getByRole("link", { name: "Version history" }).click(); + // await expect(page.locator(".currentVersion--wrapper")).toContainText( + // "Sequence 0", + // ); + // await expect(page.locator("#app")).toContainText( + // "Currently deployed version", + // ); + // await expect(page.locator("#app")).toContainText("Check for update"); + // await expect(page.locator("#app")).toContainText( + // "Configure automatic updates", + // ); + // await expect(page.getByRole("button")).toContainText("Redeploy"); + // await page.getByText("Configure automatic updates").click(); + // await expect(page.locator(".ConfigureUpdatesModal")).toContainText("Weekly"); + // await expect(page.locator(".ConfigureUpdatesModal")).toContainText( + // "At 12:00 AM, only on Sunday", + // ); + // await expect(page.locator("label")).toContainText( + // "Enable automatic deployment", + // ); + // await page.getByRole("button", { name: "Cancel" }).click(); + // await expect( + // page.getByText("Automatically check for updates", { exact: true }), + // ).not.toBeVisible(); + // await page.locator('span[data-tip="View deploy logs"]').first().click(); + // await validateDeployLogs(page, expect); + // await page.getByRole("link", { name: "Config", exact: true }).click(); + // await expect(page.locator("h3")).toContainText("My Example Config"); + // await expect(page.locator("#version_sequence-group")).toContainText( + // "This version is 1", + // ); + // await expect(page.getByRole("combobox")).toHaveValue("option_1"); + // await page.getByRole("combobox").selectOption("option_2"); + // await expect(page.getByRole("combobox")).toHaveValue("option_2"); + // await expect(page.getByLabel("radio_1")).toBeChecked(); + // await page.getByLabel("radio_2").click(); + // await expect(page.getByLabel("radio_2")).toBeChecked(); + // await expect(page.getByRole("button", { name: "Save config" })).toBeVisible(); + // await page.getByRole("link", { name: "Troubleshoot" }).click(); + // await expect( + // page.getByRole("button", { name: "Analyze App Name" }), + // ).toBeVisible(); + // await page.getByRole("link", { name: "License" }).click(); + // await expect(page.locator("#app")).toContainText("Airgap enabled"); + // await expect(page.locator("#app")).toContainText("Snapshots enabled"); + // await expect( + // page.getByRole("button", { name: "Sync license" }), + // ).toBeVisible(); + // await page.getByRole("link", { name: "View files" }).click(); + // await page.getByText("upstream", { exact: true }).click(); + // await page + // .getByRole("listitem", { name: "config.yaml" }) + // .locator("div") + // .click(); + // await expect(page.locator(".view-lines")).toContainText("apiVersion"); + // await page.getByText("Click here", { exact: true }).click(); + // await expect(page.getByRole("heading")).toContainText( + // "Edit patches for your kots application", + // ); + // await expect(page.getByText("Copy command").first()).toBeVisible(); - let downloadCommand = await page - .locator(".react-prism.language-bash") - .first() - .textContent(); - if (!downloadCommand!.includes("download")) { - throw new Error( - "Expected the download command to contain the word 'download'", - ); - } - downloadCommand = `${downloadCommand} --overwrite`; - console.log(downloadCommand, "\n"); - execSync(downloadCommand, { stdio: "inherit" }); + // let downloadCommand = await page + // .locator(".react-prism.language-bash") + // .first() + // .textContent(); + // if (!downloadCommand!.includes("download")) { + // throw new Error( + // "Expected the download command to contain the word 'download'", + // ); + // } + // downloadCommand = `${downloadCommand} --overwrite`; + // console.log(downloadCommand, "\n"); + // execSync(downloadCommand, { stdio: "inherit" }); - await expect(page.getByText("Copy command").last()).toBeVisible(); - let uploadCommand = await page - .locator(".react-prism.language-bash") - .last() - .textContent(); - if (!uploadCommand!.includes("upload")) { - throw new Error("Expected the upload command to contain the word 'upload'"); - } - console.log(uploadCommand, "\n"); - execSync(uploadCommand, { stdio: "inherit" }); + // await expect(page.getByText("Copy command").last()).toBeVisible(); + // let uploadCommand = await page + // .locator(".react-prism.language-bash") + // .last() + // .textContent(); + // if (!uploadCommand!.includes("upload")) { + // throw new Error("Expected the upload command to contain the word 'upload'"); + // } + // console.log(uploadCommand, "\n"); + // execSync(uploadCommand, { stdio: "inherit" }); - await page.getByRole("button", { name: "Ok, got it!" }).click(); - await page.getByRole("link", { name: "Version history" }).click(); - await expect(page.locator("#app")).toContainText("KOTS Upload"); - await expect( - page.getByText("Running checks", { exact: true }).first(), - ).not.toBeVisible({ timeout: 30000 }); - await page.getByRole("button", { name: "Deploy" }).first().click(); - await page.getByRole("button", { name: "Deploy this version" }).click(); - await expect(page.locator("#app")).toContainText("Deploying"); - await expect(page.locator("#app")).toContainText( - "Currently deployed version", - ); - await expect(page.locator("#app")).toContainText("Application up to date."); - await expect(page.locator(".currentVersion--wrapper")).toContainText( - "Sequence 1", - ); - await page.getByRole("link", { name: "Registry settings" }).click(); - await page.getByPlaceholder("artifactory.some-big-bank.com").click(); - await page.getByPlaceholder("artifactory.some-big-bank.com").fill("ttl.sh"); - await page.getByPlaceholder("username").click(); - await page.getByPlaceholder("username").fill("admin"); - await page.getByPlaceholder("password").click(); - await page.getByPlaceholder("password").fill("admin"); - await page.getByRole("button", { name: "Test connection" }).click(); - await expect(page.locator("form")).toContainText("Success!"); - await page.getByRole("button", { name: "Save changes" }).click(); - await expect( - page.getByRole("button", { name: "Save changes" }), - ).toBeDisabled(); - await expect(page.locator(".Loader")).toBeVisible(); - await expect(page.locator("#app")).toContainText( - "Writing manifest to image destination", - { timeout: 30000 }, - ); - await expect(page.getByRole("button", { name: "Save changes" })).toBeEnabled({ - timeout: 60000, - }); - await expect(page.locator(".Loader")).not.toBeVisible(); - await page.getByRole("link", { name: "Version history" }).click(); - await expect(page.locator("#app")).toContainText("Registry Change"); - await expect(page.locator("#app")).toContainText("Sequence 2"); - await page.getByRole("link", { name: "Registry settings" }).click(); - await page.getByRole("button", { name: "Stop using registry" }).click(); - await page.getByRole("button", { name: "OK" }).click(); - await expect(page.locator(".Loader")).toBeVisible(); - await expect( - page.getByRole("button", { name: "Stop using registry" }), - ).toBeDisabled(); - await expect(page.getByRole("button", { name: "Save changes" })).toBeEnabled({ - timeout: 30000, - }); - await expect(page.locator(".Loader")).not.toBeVisible(); - await expect( - page.getByPlaceholder("artifactory.some-big-bank.com"), - ).toBeEmpty(); - await expect(page.getByPlaceholder("username")).toBeEmpty(); - await expect(page.getByPlaceholder("password")).toBeEmpty(); - await expect(page.getByPlaceholder("namespace")).toBeEmpty(); - await page.waitForTimeout(2000); - await page.getByRole("link", { name: "Version history" }).click(); - await expect(page.locator("#app")).toContainText("Sequence 3", { - timeout: 10000, - }); - await expect(page.locator("#app")).toContainText("Registry Change"); - await expect( - page.locator(".NavItem").getByText("Application", { exact: true }), - ).toBeVisible(); - await expect( - page.locator(".NavItem").getByText("GitOps", { exact: true }), - ).toBeVisible(); + // await page.getByRole("button", { name: "Ok, got it!" }).click(); + // await page.getByRole("link", { name: "Version history" }).click(); + // await expect(page.locator("#app")).toContainText("KOTS Upload"); + // await expect( + // page.getByText("Running checks", { exact: true }).first(), + // ).not.toBeVisible({ timeout: 30000 }); + // await page.getByRole("button", { name: "Deploy" }).first().click(); + // await page.getByRole("button", { name: "Deploy this version" }).click(); + // await expect(page.locator("#app")).toContainText("Deploying"); + // await expect(page.locator("#app")).toContainText( + // "Currently deployed version", + // ); + // await expect(page.locator("#app")).toContainText("Application up to date."); + // await expect(page.locator(".currentVersion--wrapper")).toContainText( + // "Sequence 1", + // ); + // await page.getByRole("link", { name: "Registry settings" }).click(); + // await page.getByPlaceholder("artifactory.some-big-bank.com").click(); + // await page.getByPlaceholder("artifactory.some-big-bank.com").fill("ttl.sh"); + // await page.getByPlaceholder("username").click(); + // await page.getByPlaceholder("username").fill("admin"); + // await page.getByPlaceholder("password").click(); + // await page.getByPlaceholder("password").fill("admin"); + // await page.getByRole("button", { name: "Test connection" }).click(); + // await expect(page.locator("form")).toContainText("Success!"); + // await page.getByRole("button", { name: "Save changes" }).click(); + // await expect( + // page.getByRole("button", { name: "Save changes" }), + // ).toBeDisabled(); + // await expect(page.locator(".Loader")).toBeVisible(); + // await expect(page.locator("#app")).toContainText( + // "Writing manifest to image destination", + // { timeout: 30000 }, + // ); + // await expect(page.getByRole("button", { name: "Save changes" })).toBeEnabled({ + // timeout: 60000, + // }); + // await expect(page.locator(".Loader")).not.toBeVisible(); + // await page.getByRole("link", { name: "Version history" }).click(); + // await expect(page.locator("#app")).toContainText("Registry Change"); + // await expect(page.locator("#app")).toContainText("Sequence 2"); + // await page.getByRole("link", { name: "Registry settings" }).click(); + // await page.getByRole("button", { name: "Stop using registry" }).click(); + // await page.getByRole("button", { name: "OK" }).click(); + // await expect(page.locator(".Loader")).toBeVisible(); + // await expect( + // page.getByRole("button", { name: "Stop using registry" }), + // ).toBeDisabled(); + // await expect(page.getByRole("button", { name: "Save changes" })).toBeEnabled({ + // timeout: 30000, + // }); + // await expect(page.locator(".Loader")).not.toBeVisible(); + // await expect( + // page.getByPlaceholder("artifactory.some-big-bank.com"), + // ).toBeEmpty(); + // await expect(page.getByPlaceholder("username")).toBeEmpty(); + // await expect(page.getByPlaceholder("password")).toBeEmpty(); + // await expect(page.getByPlaceholder("namespace")).toBeEmpty(); + // await page.waitForTimeout(2000); + // await page.getByRole("link", { name: "Version history" }).click(); + // await expect(page.locator("#app")).toContainText("Sequence 3", { + // timeout: 10000, + // }); + // await expect(page.locator("#app")).toContainText("Registry Change"); + // await expect( + // page.locator(".NavItem").getByText("Application", { exact: true }), + // ).toBeVisible(); + // await expect( + // page.locator(".NavItem").getByText("GitOps", { exact: true }), + // ).toBeVisible(); await expect( page.locator(".NavItem").getByText("Snapshots", { exact: true }), ).toBeVisible(); @@ -283,6 +283,7 @@ test("smoke test", async ({ page }) => { .click(); await page.locator(".SnapshotRow--wrapper").click(); await expect(page.locator("#app")).toContainText("Snapshot timeline"); + await page.getByText("Infrastructure", { exact: true }).click(); await page.getByText("View logs").click(); await expect(page.locator(".view-lines")).toContainText("level=info"); await page.getByRole("button", { name: "Ok, got it!" }).click(); From abb6096e24df7cd08630a677339b4cac02d84fc3 Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Tue, 14 Jan 2025 15:03:11 -0500 Subject: [PATCH 16/21] uncomment --- e2e/playwright/tests/@smoke-test/test.spec.ts | 446 +++++++++--------- 1 file changed, 223 insertions(+), 223 deletions(-) diff --git a/e2e/playwright/tests/@smoke-test/test.spec.ts b/e2e/playwright/tests/@smoke-test/test.spec.ts index 2919313137..5b0973de6b 100644 --- a/e2e/playwright/tests/@smoke-test/test.spec.ts +++ b/e2e/playwright/tests/@smoke-test/test.spec.ts @@ -6,231 +6,231 @@ const { execSync } = require("child_process"); test("smoke test", async ({ page }) => { test.setTimeout(5 * 60 * 1000); // 5 minutes await login(page); - // await uploadLicense(page, expect); - // await expect(page.locator("#app")).toContainText( - // "Install in airgapped environment", - // { timeout: 15000 }, - // ); - // await page.getByText("download App Name from the Internet").click(); - // await expect(page.locator("#app")).toContainText("Installing your license"); - // await expect(page.locator("h3")).toContainText("My Example Config", { - // timeout: 30000, - // }); - // await page.locator("#a_bool-group").getByText("a bool field").click(); - // await page.locator("#a_required_text-group").getByRole("textbox").click(); - // await page - // .locator("#a_required_text-group") - // .getByRole("textbox") - // .fill("my required text field"); - // await expect(page.locator("#version_sequence-group")).toContainText( - // "This version is 0", - // ); - // await page.getByRole("button", { name: "Continue" }).click(); - // await expect(page.locator("#app")).toContainText("Results", { - // timeout: 60 * 1000, - // }); - // await expect(page.locator("#app")).toContainText("Sequence is 0"); - // await page.getByRole("button", { name: "Deploy" }).click(); - // await page.getByRole("button", { name: "Deploy anyway" }).click(); - // await expect(page.locator("#app")).toContainText("Ready", { timeout: 30000 }); - // await expect(page.locator("#app")).toContainText( - // "Currently deployed version", - // { timeout: 30000 }, - // ); - // await expect(page.locator("#app")).toContainText("Check for update"); - // await expect(page.locator("#app")).toContainText("Redeploy", { - // timeout: 15000, - // }); - // await expect(page.getByText("App Name")).toBeVisible(); - // await expect(page.locator(".Dashboard--appIcon")).toBeVisible(); - // await expect(page.locator("p").filter({ hasText: "License" })).toBeVisible(); - // await page.getByText("Configure automatic updates").click(); - // await expect(page.locator(".ConfigureUpdatesModal")).toContainText("Default"); - // await expect(page.locator(".ConfigureUpdatesModal")).toContainText( - // "Every 4 hours", - // ); - // await expect(page.locator("label")).toContainText( - // "Enable automatic deployment", - // ); - // await page.locator(".replicated-select__control").click(); - // await page.waitForTimeout(1000); - // await page - // .locator(".replicated-select__option") - // .getByText("Weekly", { exact: true }) - // .click(); - // await page.waitForTimeout(1000); - // await expect(page.locator(".ConfigureUpdatesModal")).toContainText("Weekly"); - // await expect(page.locator(".ConfigureUpdatesModal")).toContainText( - // "At 12:00 AM, only on Sunday", - // ); - // await page.getByRole("button", { name: "Update", exact: true }).click(); - // await expect( - // page.getByText("Automatically check for updates", { exact: true }), - // ).not.toBeVisible(); - // await page - // .locator('svg.icons.clickable[data-tip="View release notes"]') - // .click(); - // await expect( - // page.getByLabel("Release Notes").getByRole("paragraph"), - // ).toContainText("release notes - updates"); - // await page.getByRole("button", { name: "Close" }).click(); - // await page.locator('span[data-tip="View deploy logs"]').click(); - // await validateDeployLogs(page, expect); - // await page.getByRole("link", { name: "Version history" }).click(); - // await expect(page.locator(".currentVersion--wrapper")).toContainText( - // "Sequence 0", - // ); - // await expect(page.locator("#app")).toContainText( - // "Currently deployed version", - // ); - // await expect(page.locator("#app")).toContainText("Check for update"); - // await expect(page.locator("#app")).toContainText( - // "Configure automatic updates", - // ); - // await expect(page.getByRole("button")).toContainText("Redeploy"); - // await page.getByText("Configure automatic updates").click(); - // await expect(page.locator(".ConfigureUpdatesModal")).toContainText("Weekly"); - // await expect(page.locator(".ConfigureUpdatesModal")).toContainText( - // "At 12:00 AM, only on Sunday", - // ); - // await expect(page.locator("label")).toContainText( - // "Enable automatic deployment", - // ); - // await page.getByRole("button", { name: "Cancel" }).click(); - // await expect( - // page.getByText("Automatically check for updates", { exact: true }), - // ).not.toBeVisible(); - // await page.locator('span[data-tip="View deploy logs"]').first().click(); - // await validateDeployLogs(page, expect); - // await page.getByRole("link", { name: "Config", exact: true }).click(); - // await expect(page.locator("h3")).toContainText("My Example Config"); - // await expect(page.locator("#version_sequence-group")).toContainText( - // "This version is 1", - // ); - // await expect(page.getByRole("combobox")).toHaveValue("option_1"); - // await page.getByRole("combobox").selectOption("option_2"); - // await expect(page.getByRole("combobox")).toHaveValue("option_2"); - // await expect(page.getByLabel("radio_1")).toBeChecked(); - // await page.getByLabel("radio_2").click(); - // await expect(page.getByLabel("radio_2")).toBeChecked(); - // await expect(page.getByRole("button", { name: "Save config" })).toBeVisible(); - // await page.getByRole("link", { name: "Troubleshoot" }).click(); - // await expect( - // page.getByRole("button", { name: "Analyze App Name" }), - // ).toBeVisible(); - // await page.getByRole("link", { name: "License" }).click(); - // await expect(page.locator("#app")).toContainText("Airgap enabled"); - // await expect(page.locator("#app")).toContainText("Snapshots enabled"); - // await expect( - // page.getByRole("button", { name: "Sync license" }), - // ).toBeVisible(); - // await page.getByRole("link", { name: "View files" }).click(); - // await page.getByText("upstream", { exact: true }).click(); - // await page - // .getByRole("listitem", { name: "config.yaml" }) - // .locator("div") - // .click(); - // await expect(page.locator(".view-lines")).toContainText("apiVersion"); - // await page.getByText("Click here", { exact: true }).click(); - // await expect(page.getByRole("heading")).toContainText( - // "Edit patches for your kots application", - // ); - // await expect(page.getByText("Copy command").first()).toBeVisible(); + await uploadLicense(page, expect); + await expect(page.locator("#app")).toContainText( + "Install in airgapped environment", + { timeout: 15000 }, + ); + await page.getByText("download App Name from the Internet").click(); + await expect(page.locator("#app")).toContainText("Installing your license"); + await expect(page.locator("h3")).toContainText("My Example Config", { + timeout: 30000, + }); + await page.locator("#a_bool-group").getByText("a bool field").click(); + await page.locator("#a_required_text-group").getByRole("textbox").click(); + await page + .locator("#a_required_text-group") + .getByRole("textbox") + .fill("my required text field"); + await expect(page.locator("#version_sequence-group")).toContainText( + "This version is 0", + ); + await page.getByRole("button", { name: "Continue" }).click(); + await expect(page.locator("#app")).toContainText("Results", { + timeout: 60 * 1000, + }); + await expect(page.locator("#app")).toContainText("Sequence is 0"); + await page.getByRole("button", { name: "Deploy" }).click(); + await page.getByRole("button", { name: "Deploy anyway" }).click(); + await expect(page.locator("#app")).toContainText("Ready", { timeout: 30000 }); + await expect(page.locator("#app")).toContainText( + "Currently deployed version", + { timeout: 30000 }, + ); + await expect(page.locator("#app")).toContainText("Check for update"); + await expect(page.locator("#app")).toContainText("Redeploy", { + timeout: 15000, + }); + await expect(page.getByText("App Name")).toBeVisible(); + await expect(page.locator(".Dashboard--appIcon")).toBeVisible(); + await expect(page.locator("p").filter({ hasText: "License" })).toBeVisible(); + await page.getByText("Configure automatic updates").click(); + await expect(page.locator(".ConfigureUpdatesModal")).toContainText("Default"); + await expect(page.locator(".ConfigureUpdatesModal")).toContainText( + "Every 4 hours", + ); + await expect(page.locator("label")).toContainText( + "Enable automatic deployment", + ); + await page.locator(".replicated-select__control").click(); + await page.waitForTimeout(1000); + await page + .locator(".replicated-select__option") + .getByText("Weekly", { exact: true }) + .click(); + await page.waitForTimeout(1000); + await expect(page.locator(".ConfigureUpdatesModal")).toContainText("Weekly"); + await expect(page.locator(".ConfigureUpdatesModal")).toContainText( + "At 12:00 AM, only on Sunday", + ); + await page.getByRole("button", { name: "Update", exact: true }).click(); + await expect( + page.getByText("Automatically check for updates", { exact: true }), + ).not.toBeVisible(); + await page + .locator('svg.icons.clickable[data-tip="View release notes"]') + .click(); + await expect( + page.getByLabel("Release Notes").getByRole("paragraph"), + ).toContainText("release notes - updates"); + await page.getByRole("button", { name: "Close" }).click(); + await page.locator('span[data-tip="View deploy logs"]').click(); + await validateDeployLogs(page, expect); + await page.getByRole("link", { name: "Version history" }).click(); + await expect(page.locator(".currentVersion--wrapper")).toContainText( + "Sequence 0", + ); + await expect(page.locator("#app")).toContainText( + "Currently deployed version", + ); + await expect(page.locator("#app")).toContainText("Check for update"); + await expect(page.locator("#app")).toContainText( + "Configure automatic updates", + ); + await expect(page.getByRole("button")).toContainText("Redeploy"); + await page.getByText("Configure automatic updates").click(); + await expect(page.locator(".ConfigureUpdatesModal")).toContainText("Weekly"); + await expect(page.locator(".ConfigureUpdatesModal")).toContainText( + "At 12:00 AM, only on Sunday", + ); + await expect(page.locator("label")).toContainText( + "Enable automatic deployment", + ); + await page.getByRole("button", { name: "Cancel" }).click(); + await expect( + page.getByText("Automatically check for updates", { exact: true }), + ).not.toBeVisible(); + await page.locator('span[data-tip="View deploy logs"]').first().click(); + await validateDeployLogs(page, expect); + await page.getByRole("link", { name: "Config", exact: true }).click(); + await expect(page.locator("h3")).toContainText("My Example Config"); + await expect(page.locator("#version_sequence-group")).toContainText( + "This version is 1", + ); + await expect(page.getByRole("combobox")).toHaveValue("option_1"); + await page.getByRole("combobox").selectOption("option_2"); + await expect(page.getByRole("combobox")).toHaveValue("option_2"); + await expect(page.getByLabel("radio_1")).toBeChecked(); + await page.getByLabel("radio_2").click(); + await expect(page.getByLabel("radio_2")).toBeChecked(); + await expect(page.getByRole("button", { name: "Save config" })).toBeVisible(); + await page.getByRole("link", { name: "Troubleshoot" }).click(); + await expect( + page.getByRole("button", { name: "Analyze App Name" }), + ).toBeVisible(); + await page.getByRole("link", { name: "License" }).click(); + await expect(page.locator("#app")).toContainText("Airgap enabled"); + await expect(page.locator("#app")).toContainText("Snapshots enabled"); + await expect( + page.getByRole("button", { name: "Sync license" }), + ).toBeVisible(); + await page.getByRole("link", { name: "View files" }).click(); + await page.getByText("upstream", { exact: true }).click(); + await page + .getByRole("listitem", { name: "config.yaml" }) + .locator("div") + .click(); + await expect(page.locator(".view-lines")).toContainText("apiVersion"); + await page.getByText("Click here", { exact: true }).click(); + await expect(page.getByRole("heading")).toContainText( + "Edit patches for your kots application", + ); + await expect(page.getByText("Copy command").first()).toBeVisible(); - // let downloadCommand = await page - // .locator(".react-prism.language-bash") - // .first() - // .textContent(); - // if (!downloadCommand!.includes("download")) { - // throw new Error( - // "Expected the download command to contain the word 'download'", - // ); - // } - // downloadCommand = `${downloadCommand} --overwrite`; - // console.log(downloadCommand, "\n"); - // execSync(downloadCommand, { stdio: "inherit" }); + let downloadCommand = await page + .locator(".react-prism.language-bash") + .first() + .textContent(); + if (!downloadCommand!.includes("download")) { + throw new Error( + "Expected the download command to contain the word 'download'", + ); + } + downloadCommand = `${downloadCommand} --overwrite`; + console.log(downloadCommand, "\n"); + execSync(downloadCommand, { stdio: "inherit" }); - // await expect(page.getByText("Copy command").last()).toBeVisible(); - // let uploadCommand = await page - // .locator(".react-prism.language-bash") - // .last() - // .textContent(); - // if (!uploadCommand!.includes("upload")) { - // throw new Error("Expected the upload command to contain the word 'upload'"); - // } - // console.log(uploadCommand, "\n"); - // execSync(uploadCommand, { stdio: "inherit" }); + await expect(page.getByText("Copy command").last()).toBeVisible(); + let uploadCommand = await page + .locator(".react-prism.language-bash") + .last() + .textContent(); + if (!uploadCommand!.includes("upload")) { + throw new Error("Expected the upload command to contain the word 'upload'"); + } + console.log(uploadCommand, "\n"); + execSync(uploadCommand, { stdio: "inherit" }); - // await page.getByRole("button", { name: "Ok, got it!" }).click(); - // await page.getByRole("link", { name: "Version history" }).click(); - // await expect(page.locator("#app")).toContainText("KOTS Upload"); - // await expect( - // page.getByText("Running checks", { exact: true }).first(), - // ).not.toBeVisible({ timeout: 30000 }); - // await page.getByRole("button", { name: "Deploy" }).first().click(); - // await page.getByRole("button", { name: "Deploy this version" }).click(); - // await expect(page.locator("#app")).toContainText("Deploying"); - // await expect(page.locator("#app")).toContainText( - // "Currently deployed version", - // ); - // await expect(page.locator("#app")).toContainText("Application up to date."); - // await expect(page.locator(".currentVersion--wrapper")).toContainText( - // "Sequence 1", - // ); - // await page.getByRole("link", { name: "Registry settings" }).click(); - // await page.getByPlaceholder("artifactory.some-big-bank.com").click(); - // await page.getByPlaceholder("artifactory.some-big-bank.com").fill("ttl.sh"); - // await page.getByPlaceholder("username").click(); - // await page.getByPlaceholder("username").fill("admin"); - // await page.getByPlaceholder("password").click(); - // await page.getByPlaceholder("password").fill("admin"); - // await page.getByRole("button", { name: "Test connection" }).click(); - // await expect(page.locator("form")).toContainText("Success!"); - // await page.getByRole("button", { name: "Save changes" }).click(); - // await expect( - // page.getByRole("button", { name: "Save changes" }), - // ).toBeDisabled(); - // await expect(page.locator(".Loader")).toBeVisible(); - // await expect(page.locator("#app")).toContainText( - // "Writing manifest to image destination", - // { timeout: 30000 }, - // ); - // await expect(page.getByRole("button", { name: "Save changes" })).toBeEnabled({ - // timeout: 60000, - // }); - // await expect(page.locator(".Loader")).not.toBeVisible(); - // await page.getByRole("link", { name: "Version history" }).click(); - // await expect(page.locator("#app")).toContainText("Registry Change"); - // await expect(page.locator("#app")).toContainText("Sequence 2"); - // await page.getByRole("link", { name: "Registry settings" }).click(); - // await page.getByRole("button", { name: "Stop using registry" }).click(); - // await page.getByRole("button", { name: "OK" }).click(); - // await expect(page.locator(".Loader")).toBeVisible(); - // await expect( - // page.getByRole("button", { name: "Stop using registry" }), - // ).toBeDisabled(); - // await expect(page.getByRole("button", { name: "Save changes" })).toBeEnabled({ - // timeout: 30000, - // }); - // await expect(page.locator(".Loader")).not.toBeVisible(); - // await expect( - // page.getByPlaceholder("artifactory.some-big-bank.com"), - // ).toBeEmpty(); - // await expect(page.getByPlaceholder("username")).toBeEmpty(); - // await expect(page.getByPlaceholder("password")).toBeEmpty(); - // await expect(page.getByPlaceholder("namespace")).toBeEmpty(); - // await page.waitForTimeout(2000); - // await page.getByRole("link", { name: "Version history" }).click(); - // await expect(page.locator("#app")).toContainText("Sequence 3", { - // timeout: 10000, - // }); - // await expect(page.locator("#app")).toContainText("Registry Change"); - // await expect( - // page.locator(".NavItem").getByText("Application", { exact: true }), - // ).toBeVisible(); - // await expect( - // page.locator(".NavItem").getByText("GitOps", { exact: true }), - // ).toBeVisible(); + await page.getByRole("button", { name: "Ok, got it!" }).click(); + await page.getByRole("link", { name: "Version history" }).click(); + await expect(page.locator("#app")).toContainText("KOTS Upload"); + await expect( + page.getByText("Running checks", { exact: true }).first(), + ).not.toBeVisible({ timeout: 30000 }); + await page.getByRole("button", { name: "Deploy" }).first().click(); + await page.getByRole("button", { name: "Deploy this version" }).click(); + await expect(page.locator("#app")).toContainText("Deploying"); + await expect(page.locator("#app")).toContainText( + "Currently deployed version", + ); + await expect(page.locator("#app")).toContainText("Application up to date."); + await expect(page.locator(".currentVersion--wrapper")).toContainText( + "Sequence 1", + ); + await page.getByRole("link", { name: "Registry settings" }).click(); + await page.getByPlaceholder("artifactory.some-big-bank.com").click(); + await page.getByPlaceholder("artifactory.some-big-bank.com").fill("ttl.sh"); + await page.getByPlaceholder("username").click(); + await page.getByPlaceholder("username").fill("admin"); + await page.getByPlaceholder("password").click(); + await page.getByPlaceholder("password").fill("admin"); + await page.getByRole("button", { name: "Test connection" }).click(); + await expect(page.locator("form")).toContainText("Success!"); + await page.getByRole("button", { name: "Save changes" }).click(); + await expect( + page.getByRole("button", { name: "Save changes" }), + ).toBeDisabled(); + await expect(page.locator(".Loader")).toBeVisible(); + await expect(page.locator("#app")).toContainText( + "Writing manifest to image destination", + { timeout: 30000 }, + ); + await expect(page.getByRole("button", { name: "Save changes" })).toBeEnabled({ + timeout: 60000, + }); + await expect(page.locator(".Loader")).not.toBeVisible(); + await page.getByRole("link", { name: "Version history" }).click(); + await expect(page.locator("#app")).toContainText("Registry Change"); + await expect(page.locator("#app")).toContainText("Sequence 2"); + await page.getByRole("link", { name: "Registry settings" }).click(); + await page.getByRole("button", { name: "Stop using registry" }).click(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.locator(".Loader")).toBeVisible(); + await expect( + page.getByRole("button", { name: "Stop using registry" }), + ).toBeDisabled(); + await expect(page.getByRole("button", { name: "Save changes" })).toBeEnabled({ + timeout: 30000, + }); + await expect(page.locator(".Loader")).not.toBeVisible(); + await expect( + page.getByPlaceholder("artifactory.some-big-bank.com"), + ).toBeEmpty(); + await expect(page.getByPlaceholder("username")).toBeEmpty(); + await expect(page.getByPlaceholder("password")).toBeEmpty(); + await expect(page.getByPlaceholder("namespace")).toBeEmpty(); + await page.waitForTimeout(2000); + await page.getByRole("link", { name: "Version history" }).click(); + await expect(page.locator("#app")).toContainText("Sequence 3", { + timeout: 10000, + }); + await expect(page.locator("#app")).toContainText("Registry Change"); + await expect( + page.locator(".NavItem").getByText("Application", { exact: true }), + ).toBeVisible(); + await expect( + page.locator(".NavItem").getByText("GitOps", { exact: true }), + ).toBeVisible(); await expect( page.locator(".NavItem").getByText("Snapshots", { exact: true }), ).toBeVisible(); @@ -345,4 +345,4 @@ const validateDeployLogs = async (page, expect) => { await page.getByText("applyStdout").click(); await expect(page.locator(".view-lines")).toContainText("created"); await page.getByRole("button", { name: "Ok, got it!" }).click(); -}; +}; \ No newline at end of file From 47e2d40088e93cbf1f19963b8f7dc041faaad770 Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Tue, 14 Jan 2025 15:31:45 -0500 Subject: [PATCH 17/21] pass snapshot name to fetch logs --- web/src/components/snapshots/SnapshotDetails.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index 357ed92768..8b36037392 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -251,14 +251,13 @@ class SnapshotDetails extends Component { this.setState({ showAllErrors: !this.state.showAllErrors }); }; - viewLogs = () => { + viewLogs = (name) => { this.setState( { toggleViewLogsModal: !this.state.toggleViewLogsModal, }, () => { this.setState({ loadingSnapshotLogs: true }); - const name = this.state.snapshotDetails?.name; const url = `${process.env.API_ENDPOINT}/snapshot/${name}/logs`; fetch(url, { credentials: "include", @@ -781,7 +780,7 @@ class SnapshotDetails extends Component { {snapshotDetail?.status !== "InProgress" && ( this.viewLogs()} + onClick={() => this.viewLogs(snapshotDetail?.name)} > View logs From 6504c613d9045c27831dbc6888a335c20c324663 Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Tue, 14 Jan 2025 15:56:26 -0500 Subject: [PATCH 18/21] run prettier --- web/src/components/snapshots/SnapshotDetails.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index 8b36037392..37425a34a2 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -780,7 +780,9 @@ class SnapshotDetails extends Component { {snapshotDetail?.status !== "InProgress" && ( this.viewLogs(snapshotDetail?.name)} + onClick={() => + this.viewLogs(snapshotDetail?.name) + } > View logs From 52557ecd96fb05235ef8b83928d7027c26a0bde1 Mon Sep 17 00:00:00 2001 From: Mia Wong Date: Wed, 15 Jan 2025 14:06:40 -0500 Subject: [PATCH 19/21] fix modals --- .../components/snapshots/SnapshotDetails.jsx | 97 +++++++++++-------- 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/web/src/components/snapshots/SnapshotDetails.jsx b/web/src/components/snapshots/SnapshotDetails.jsx index 37425a34a2..4812725cf4 100644 --- a/web/src/components/snapshots/SnapshotDetails.jsx +++ b/web/src/components/snapshots/SnapshotDetails.jsx @@ -675,6 +675,12 @@ class SnapshotDetails extends Component { selectedErrorsWarningTab, currentSnapshotIndex, activeIds, + showAllVolumes, + showAllPreSnapshotScripts, + showAllPostSnapshotScripts, + showAllWarnings, + showAllErrors, + showAllScriptsOutput, } = this.state; const { isEmbeddedCluster, navigate } = this.props; let featureName = "snapshot"; @@ -721,6 +727,56 @@ class SnapshotDetails extends Component { const isActive = activeIds.includes(snapshotDetail.name); return (
+ {showAllVolumes && ( + + )} + {showAllPreSnapshotScripts && ( + + )} + {showAllPostSnapshotScripts && ( + + )} + {showAllWarnings && ( + + )} + {showAllErrors && ( + + )}
- {/* only render accordian if there are more than one snapshot for EC */} + {/* only render accordion if there are more than one snapshot for EC */} {backupDetails && backupDetails?.map((snapshotDetail) => { const isActive = activeIds.includes(snapshotDetail.name);