From 4ddc2572ad7edfe78247029097b78989c57d67d9 Mon Sep 17 00:00:00 2001
From: Fanna Lautenbach <lautenbach@astron.nl>
Date: Thu, 27 Jul 2023 11:27:02 +0200
Subject: [PATCH] extract timelineitempopover + tests

---
 .../components/TimelineItemPopover.js         | 88 +++++++++++++++++
 .../components/TimelineItemPopover.test.js    | 94 +++++++++++++++++++
 .../src/routes/Timeline/week.view.js          | 81 +---------------
 3 files changed, 184 insertions(+), 79 deletions(-)
 create mode 100644 SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/TimelineItemPopover.js
 create mode 100644 SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/TimelineItemPopover.test.js

diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/TimelineItemPopover.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/TimelineItemPopover.js
new file mode 100644
index 00000000000..ebbd178067a
--- /dev/null
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/TimelineItemPopover.js
@@ -0,0 +1,88 @@
+import UIConstants from "../../../utils/ui.constants";
+import React from "react";
+
+export default function TimelineItemPopover(props) {
+    const {mouseOverItem} = props;
+    if (!mouseOverItem) {
+        return;
+    }
+    return <div className="p-overlaypanel-content">
+        {mouseOverItem && mouseOverItem.type === "SCHEDULE" &&
+            <div className={`p-grid timeline-item su-${mouseOverItem.status}`} style={{width: '350px'}}>
+                <h3 className={`col-12 su-${mouseOverItem.status}-icon`}>
+                    Scheduling Unit ({mouseOverItem.suId}) Overview
+                </h3>
+                <hr></hr>
+                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Project:</label>
+                <div className="col-7">{mouseOverItem.project}</div>
+                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Scheduling Unit:</label>
+                <div className="col-7">{mouseOverItem.name}</div>
+                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Scheduler:</label>
+                <div className="col-7">{mouseOverItem.scheduleMethod}</div>
+                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Friends:</label>
+                <div className="col-7">{mouseOverItem.friends ? mouseOverItem.friends : "-"}</div>
+                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Start Time:</label>
+                <div
+                    className="col-7">{mouseOverItem.suStartTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
+                <label className={`col-5 su-${mouseOverItem.status}-icon`}>End Time:</label>
+                <div
+                    className="col-7">{mouseOverItem.suStopTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
+                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Antenna Set:</label>
+                <div className="col-7">{mouseOverItem.antennaSet}</div>
+                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Stations:</label>
+                <div
+                    className="col-7">{mouseOverItem.stations.groups}:{mouseOverItem.stations.counts}</div>
+                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Status:</label>
+                <div className="col-7">{mouseOverItem.status}</div>
+                {mouseOverItem.status === 'unschedulable' &&
+                    <>
+                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Unschedule
+                            Reason:</label>
+                        <div className="col-7">{mouseOverItem.unschedulable_reason}</div>
+                    </>
+                }
+                {mouseOverItem.type === 'SCHEDULE' &&
+                    <>
+                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Exposure
+                            Time:</label>
+                        <div className="col-7">{mouseOverItem.on_sky_duration}</div>
+                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>SU Duration:</label>
+                        <div className="col-7">{mouseOverItem.duration}</div>
+                    </>}
+            </div>
+        }
+        {(mouseOverItem && mouseOverItem.type === "RESERVATION") &&
+            <div className={`p-grid`} style={{
+                width: '350px',
+                backgroundColor: mouseOverItem.bgColor,
+                color: mouseOverItem.color
+            }}>
+                <h3 className={`col-12`}>Reservation Overview</h3>
+                <hr></hr>
+                <label className={`col-5`} style={{color: mouseOverItem.color}}>Name:</label>
+                <div className="col-7">{mouseOverItem.name}</div>
+                <label className={`col-5`} style={{color: mouseOverItem.color}}>Description:</label>
+                <div className="col-7">{mouseOverItem.desc}</div>
+                <label className={`col-5`} style={{color: mouseOverItem.color}}>Type:</label>
+                <div className="col-7">{mouseOverItem.activity_type}</div>
+                <label className={`col-5`} style={{color: mouseOverItem.color}}>Stations:</label>
+                <div
+                    className="col-7">{mouseOverItem.stations.groups ? `${mouseOverItem.stations.groups} : ${mouseOverItem.stations.counts}` : mouseOverItem.stations}</div>
+                <label className={`col-5`} style={{color: mouseOverItem.color}}>Project:</label>
+                <div className="col-7">{mouseOverItem.project ? mouseOverItem.project : "-"}</div>
+                <label className={`col-5`} style={{color: mouseOverItem.color}}>Start Time:</label>
+                <div
+                    className="col-7">{mouseOverItem.displayStartTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
+                <label className={`col-5`} style={{color: mouseOverItem.color}}>End Time:</label>
+                <div
+                    className="col-7">{mouseOverItem.displayEndTime ? mouseOverItem.displayEndTime.format(UIConstants.CALENDAR_DATETIME_FORMAT) : 'Unknown'}</div>
+                {/* <label className={`col-5`} style={{color: mouseOverItem.color}}>Stations:</label>
+                        <div className="col-7">{mouseOverItem.stations.groups}:{mouseOverItem.stations.counts}</div> */}
+                <label className={`col-5`} style={{color: mouseOverItem.color}}>Duration:</label>
+                <div className="col-7">{mouseOverItem.duration}</div>
+                <label className={`col-5`} style={{color: mouseOverItem.color}}>Planned:</label>
+                <div className="col-7">{mouseOverItem.planned ? 'Yes' : 'No'}</div>
+            </div>
+        }
+    </div>
+}
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/TimelineItemPopover.test.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/TimelineItemPopover.test.js
new file mode 100644
index 00000000000..b9eb3f13bbf
--- /dev/null
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/TimelineItemPopover.test.js
@@ -0,0 +1,94 @@
+import {render} from "@testing-library/react";
+import TimelineItemPopover from "./TimelineItemPopover";
+import moment from "moment";
+import {removeReact18ConsoleErrors} from "../../../utils/test.helper";
+
+removeReact18ConsoleErrors()
+
+describe("TimelineItemPopover", () => {
+    let mouseOverItemSchedule = {
+        type: "SCHEDULE",
+        status: "scheduled",
+        suId: "123",
+        project: "X-men",
+        name: "Wolverine",
+        scheduleMethod: "dynamic",
+        suStartTime: moment("2023-07-27T08:00:00Z"),
+        suStopTime: moment("2023-07-27T10:00:00Z"),
+        antennaSet: "LBA",
+        stations: {
+            groups: 2,
+            counts: 8,
+        },
+        on_sky_duration: 2.5,
+        duration: 3.5,
+    };
+    test("Render type SCHEDULE for a schedulable item", () => {
+        const content = render(<TimelineItemPopover mouseOverItem={mouseOverItemSchedule}/>);
+
+        console.log("container")
+        expect(content.getByText(`Scheduling Unit (${mouseOverItemSchedule.suId}) Overview`)).toBeInTheDocument();
+        expect(content.getByText(`Project: ${mouseOverItemSchedule.project}`)).toBeInTheDocument();
+        expect(content.getByText(`Scheduling Unit: ${mouseOverItemSchedule.name}`)).toBeInTheDocument();
+        expect(content.getByText(`Scheduler: ${mouseOverItemSchedule.scheduleMethod}`)).toBeInTheDocument();
+        expect(content.getByText(`Friends: ${mouseOverItemSchedule.friends ? mouseOverItemSchedule.friends : "-"}`)).toBeInTheDocument();
+        expect(content.getByText(`Start Time: ${mouseOverItemSchedule.suStartTime.toISOString()}`)).toBeInTheDocument();
+        expect(content.getByText(`End Time: ${mouseOverItemSchedule.suStopTime.toISOString()}`)).toBeInTheDocument();
+        expect(content.getByText(`Antenna Set: ${mouseOverItemSchedule.antennaSet}`)).toBeInTheDocument();
+        expect(content.getByText(`Stations: ${mouseOverItemSchedule.stations.groups}:${mouseOverItemSchedule.stations.counts}`)).toBeInTheDocument();
+        expect(content.getByText(`Status: ${mouseOverItemSchedule.status}`)).toBeInTheDocument();
+        expect(content.getByText("Exposure Time:")).toBeInTheDocument();
+        expect(content.getByText(mouseOverItemSchedule.on_sky_duration.toString())).toBeInTheDocument();
+        expect(content.getByText("SU Duration:")).toBeInTheDocument();
+        expect(content.getByText(mouseOverItemSchedule.duration.toString())).toBeInTheDocument();
+    });
+
+    test("Render type SCHEDULE for an unschedulable item", () => {
+        const {getByText} = render(<TimelineItemPopover mouseOverItem={mouseOverItemSchedule}/>);
+
+        expect(getByText(`Scheduling Unit (${mouseOverItemSchedule.suId}) Overview`)).toBeInTheDocument();
+        expect(getByText(`Project: ${mouseOverItemSchedule.project}`)).toBeInTheDocument();
+        expect(getByText(`Scheduling Unit: ${mouseOverItemSchedule.name}`)).toBeInTheDocument();
+        expect(getByText(`Scheduler: ${mouseOverItemSchedule.scheduleMethod}`)).toBeInTheDocument();
+        expect(getByText(`Friends: ${mouseOverItemSchedule.friends ? mouseOverItemSchedule.friends : "-"}`)).toBeInTheDocument();
+        expect(getByText(`Start Time: ${mouseOverItemSchedule.suStartTime.toISOString()}`)).toBeInTheDocument();
+        expect(getByText(`End Time: ${mouseOverItemSchedule.suStopTime.toISOString()}`)).toBeInTheDocument();
+        expect(getByText(`Antenna Set: ${mouseOverItemSchedule.antennaSet}`)).toBeInTheDocument();
+        expect(getByText(`Stations: ${mouseOverItemSchedule.stations.groups}:${mouseOverItemSchedule.stations.counts}`)).toBeInTheDocument();
+        expect(getByText(`Status: ${mouseOverItemSchedule.status}`)).toBeInTheDocument();
+        expect(getByText("Unschedule Reason:")).toBeInTheDocument();
+        expect(getByText(mouseOverItemSchedule.unschedulable_reason)).toBeInTheDocument();
+
+    });
+
+    test("Render a RESERVATION", () => {
+        const mouseOverItem = {
+            type: "RESERVATION",
+            bgColor: "#ffffff",
+            color: "#000000",
+            name: "Reservation Name",
+            desc: "Reservation Description",
+            activity_type: "Type",
+            stations: "Station Name",
+            project: "Project Name",
+            displayStartTime: moment("2023-07-27T12:00:00Z"),
+            displayEndTime: moment("2023-07-27T14:00:00Z"),
+            duration: 2,
+            planned: true,
+        };
+
+        const {getByText} = render(<TimelineItemPopover mouseOverItem={mouseOverItem}/>);
+
+        expect(getByText("Reservation Overview")).toBeInTheDocument();
+        expect(getByText(`Name: ${mouseOverItem.name}`)).toBeInTheDocument();
+        expect(getByText(`Description: ${mouseOverItem.desc}`)).toBeInTheDocument();
+
+    });
+
+    test.each([null, undefined, {}])('Render nothing on invalid input: %s', (mouseOverItem) => {
+        const {queryByText} = render(<TimelineItemPopover mouseOverItem={mouseOverItem}/>);
+
+        expect(queryByText("Reservation Overview")).not.toBeInTheDocument();
+        expect(queryByText("Scheduling Unit Overview")).not.toBeInTheDocument();
+    });
+});
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js
index 09dd8832d62..9fb42ede8f4 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js
@@ -34,6 +34,7 @@ import AuthUtil from '../../utils/auth.util';
 import AuthStore from '../../authenticate/auth.store';
 import TopProgressBar from '../../layout/components/TopProgressBar';
 import {getTimelineItem} from "./week.view.helper";
+import TimelineItemPopover from "./components/TimelineItemPopover";
 
 // Color constant for status
 export const STATUS_COLORS = {
@@ -1648,85 +1649,7 @@ export class WeekTimelineView extends Component {
                 {/* SU Item Tooltip popover with SU status color */}
                 <div className="p-overlaypanel p-component timeline-popover"
                      style={{...this.state.popPosition, zIndex: 1324, opacity: 1.944}}>
-                    <div className="p-overlaypanel-content">
-                        {mouseOverItem && mouseOverItem.type === "SCHEDULE" &&
-                            <div className={`p-grid su-${mouseOverItem.status}`} style={{width: '350px'}}>
-                                <h3 className={`col-12 su-${mouseOverItem.status}-icon`}>
-                                    Scheduling Unit ({mouseOverItem.suId}) Overview
-                                </h3>
-                                <hr></hr>
-                                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Project:</label>
-                                <div className="col-7">{mouseOverItem.project}</div>
-                                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Scheduling Unit:</label>
-                                <div className="col-7">{mouseOverItem.name}</div>
-                                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Scheduler:</label>
-                                <div className="col-7">{mouseOverItem.scheduleMethod}</div>
-                                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Friends:</label>
-                                <div className="col-7">{mouseOverItem.friends ? mouseOverItem.friends : "-"}</div>
-                                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Start Time:</label>
-                                <div
-                                    className="col-7">{mouseOverItem.suStartTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
-                                <label className={`col-5 su-${mouseOverItem.status}-icon`}>End Time:</label>
-                                <div
-                                    className="col-7">{mouseOverItem.suStopTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
-                                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Antenna Set:</label>
-                                <div className="col-7">{mouseOverItem.antennaSet}</div>
-                                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Stations:</label>
-                                <div
-                                    className="col-7">{mouseOverItem.stations.groups}:{mouseOverItem.stations.counts}</div>
-                                <label className={`col-5 su-${mouseOverItem.status}-icon`}>Status:</label>
-                                <div className="col-7">{mouseOverItem.status}</div>
-                                {mouseOverItem.status === 'unschedulable' &&
-                                    <>
-                                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Unschedule
-                                            Reason:</label>
-                                        <div className="col-7">{mouseOverItem.unschedulable_reason}</div>
-                                    </>
-                                }
-                                {mouseOverItem.type === 'SCHEDULE' &&
-                                    <>
-                                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>Exposure
-                                            Time:</label>
-                                        <div className="col-7">{mouseOverItem.on_sky_duration}</div>
-                                        <label className={`col-5 su-${mouseOverItem.status}-icon`}>SU Duration:</label>
-                                        <div className="col-7">{mouseOverItem.duration}</div>
-                                    </>}
-                            </div>
-                        }
-                        {(mouseOverItem && mouseOverItem.type === "RESERVATION") &&
-                            <div className={`p-grid`} style={{
-                                width: '350px',
-                                backgroundColor: mouseOverItem.bgColor,
-                                color: mouseOverItem.color
-                            }}>
-                                <h3 className={`col-12`}>Reservation Overview</h3>
-                                <hr></hr>
-                                <label className={`col-5`} style={{color: mouseOverItem.color}}>Name:</label>
-                                <div className="col-7">{mouseOverItem.name}</div>
-                                <label className={`col-5`} style={{color: mouseOverItem.color}}>Description:</label>
-                                <div className="col-7">{mouseOverItem.desc}</div>
-                                <label className={`col-5`} style={{color: mouseOverItem.color}}>Type:</label>
-                                <div className="col-7">{mouseOverItem.activity_type}</div>
-                                <label className={`col-5`} style={{color: mouseOverItem.color}}>Stations:</label>
-                                <div
-                                    className="col-7">{mouseOverItem.stations.groups ? `${mouseOverItem.stations.groups} : ${mouseOverItem.stations.counts}` : mouseOverItem.stations}</div>
-                                <label className={`col-5`} style={{color: mouseOverItem.color}}>Project:</label>
-                                <div className="col-7">{mouseOverItem.project ? mouseOverItem.project : "-"}</div>
-                                <label className={`col-5`} style={{color: mouseOverItem.color}}>Start Time:</label>
-                                <div
-                                    className="col-7">{mouseOverItem.displayStartTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)}</div>
-                                <label className={`col-5`} style={{color: mouseOverItem.color}}>End Time:</label>
-                                <div
-                                    className="col-7">{mouseOverItem.displayEndTime ? mouseOverItem.displayEndTime.format(UIConstants.CALENDAR_DATETIME_FORMAT) : 'Unknown'}</div>
-                                {/* <label className={`col-5`} style={{color: mouseOverItem.color}}>Stations:</label>
-                        <div className="col-7">{mouseOverItem.stations.groups}:{mouseOverItem.stations.counts}</div> */}
-                                <label className={`col-5`} style={{color: mouseOverItem.color}}>Duration:</label>
-                                <div className="col-7">{mouseOverItem.duration}</div>
-                                <label className={`col-5`} style={{color: mouseOverItem.color}}>Planned:</label>
-                                <div className="col-7">{mouseOverItem.planned ? 'Yes' : 'No'}</div>
-                            </div>
-                        }
-                    </div>
+                    <TimelineItemPopover mouseOverItem={mouseOverItem}/>
                 </div>
                 {/* Open Websocket after loading all initial data */}
                 {!this.state.isLoading &&
-- 
GitLab