qualiteer/src/views/failing/FailingBox.jsx
2022-08-09 04:29:10 +00:00

255 lines
7.3 KiB
JavaScript

import React, { useState, useContext } from "react";
import { useNavigate } from "react-router-dom";
import StoreContext from "../../ctx/StoreContext.jsx";
import JobContext, { jobStatus } from "../../ctx/JobContext.jsx";
import Accordion from "@mui/material/Accordion";
import AccordionDetails from "@mui/material/AccordionDetails";
import AccordionSummary from "@mui/material/AccordionSummary";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";
import IconButton from "@mui/material/IconButton";
import DeleteIcon from "@mui/icons-material/Delete";
import NotificationsIcon from "@mui/icons-material/Notifications";
import ReplayIcon from "@mui/icons-material/Replay";
import PhotoCameraIcon from "@mui/icons-material/PhotoCamera";
import CheckIcon from "@mui/icons-material/Check";
import ClearIcon from "@mui/icons-material/Clear";
import ViewColumnIcon from "@mui/icons-material/ViewColumn";
import PendingIcon from "@mui/icons-material/Pending";
import VisibilityIcon from "@mui/icons-material/Visibility";
import DoNotDisturbIcon from "@mui/icons-material/DoNotDisturb";
import Badge from "@mui/material/Badge";
import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
const stopPropagation = (e) => e.stopPropagation() && e.preventDefault();
export default function FailingBox(props) {
const { failingTest, silenceClick } = props;
const {
class: testClass,
name: testName,
timestamp,
silencedUntil,
type,
dailyFails,
screenshot: screenshotUrl,
recentResults,
failedMessage,
isCompound,
jobStatus: testJobStatus,
job,
} = failingTest;
const navigate = useNavigate();
const { state: jobState, jobFactory } = useContext(JobContext);
const { state: store, updateStore, removeFailure } = useContext(StoreContext);
const [open, setOpen] = useState(false);
const toggleOpen = () => setOpen(!open);
const [removeOpen, setRemoveOpen] = useState(false);
const removeClick = () => setRemoveOpen(!removeOpen);
const handleRemoveClose = (confirmed) => (e) => {
stopPropagation(e);
setRemoveOpen(false);
if (!confirmed) return;
removeFailure(failingTest);
};
function badgeColor() {
if (dailyFails === 1) return "primary";
else if (dailyFails === 2) return "secondary";
else if (dailyFails < 6) return "warning";
return "error";
}
const retryTest = () => {
const jobId = jobFactory({ testNames: [testName], isTriage: true });
if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`);
};
const jobOnClick = () => {
if (!job) return retryTest;
switch (job.status) {
case jobStatus.OK:
return null;
case jobStatus.ERROR:
return retryTest;
case jobStatus.PENDING:
return null;
case jobStatus.ACTIVE:
return null;
case jobStatus.CANCELED:
return retryTest;
case jobStatus.QUEUED:
return null;
default:
return retryTest;
}
};
function jobIcon() {
if (!job) return <ReplayIcon />;
switch (job.status) {
case jobStatus.OK:
return <CheckIcon color="success" />;
case jobStatus.ERROR:
return <ClearIcon color="error" />;
case jobStatus.PENDING:
return <PendingIcon color="info" />;
case jobStatus.ACTIVE:
return <VisibilityIcon color="primary" />;
case jobStatus.CANCELED:
return <DoNotDisturbIcon color="warning" />;
case jobStatus.QUEUED:
return <ViewColumnIcon color="secondary" />;
default:
return <ReplayIcon />;
}
}
function Actions() {
return (
<React.Fragment>
<a href={screenshotUrl}>
<IconButton aria-label="photo" component="span">
<PhotoCameraIcon />
</IconButton>
</a>
<IconButton aria-label="retry" component="span" onClick={jobOnClick()}>
{jobIcon()}
</IconButton>
<IconButton
aria-label="silence"
component="span"
color={silencedUntil ? "primary" : "default"}
onClick={silenceClick}
>
<NotificationsIcon />
</IconButton>
<IconButton
color="error"
aria-label="delete"
component="span"
onClick={removeClick}
>
<DeleteIcon />
</IconButton>
</React.Fragment>
);
}
return (
<Accordion
expanded={open}
onChange={toggleOpen}
disableGutters={true}
square
>
<AccordionSummary
style={{
backgroundColor: "rgba(0, 0, 0, .03)",
flexWrap: "wrap",
}}
>
<Badge
sx={{ mr: 2 }}
color={badgeColor()}
badgeContent={dailyFails}
anchorOrigin={{
vertical: "top",
horizontal: "left",
}}
></Badge>
<Badge
sx={{ mr: 2 }}
style={{ whiteSpace: "nowrap", left: "3.125rem" }}
badgeContent={new Date(timestamp).toLocaleTimeString("en-US")}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
></Badge>
<Dialog
open={removeOpen}
onClose={handleRemoveClose()}
sx={{ "& .MuiDialog-paper": { width: "80%", maxHeight: 435 } }}
maxWidth="xs"
>
<DialogTitle>Remove failure?</DialogTitle>
<DialogContent>
<DialogContentText>
This will remove 1 test from the database
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleRemoveClose()}>Cancel</Button>
<Button onClick={handleRemoveClose(true)} autoFocus>
Yes
</Button>
</DialogActions>
</Dialog>
<Typography component={"span"} style={{ wordBreak: "break-word" }}>
{`${testClass}#`}
<Box fontWeight="bold" display="inline">
{testName}{" "}
</Box>
<br />
<div>
<span className="recent-results">
{recentResults.map(
(v, i) =>
(v && <CheckIcon key={i} color="success" />) || (
<ClearIcon key={i} color="error" />
)
)}
</span>
{isCompound && <ViewColumnIcon />}
</div>
</Typography>
<Stack
onClick={stopPropagation}
sx={{ ml: "auto", display: { md: "none", lg: "none", xl: "none" } }}
>
<Actions />
</Stack>
<Stack
onClick={stopPropagation}
direction="row"
sx={{
ml: "auto",
mb: "auto",
mt: "auto",
whiteSpace: "nowrap",
display: { xs: "none", sm: "none", md: "block", lg: "block" },
}}
>
<Actions />
</Stack>
</AccordionSummary>
<AccordionDetails>
<Typography component={"span"} style={{ wordBreak: "break-word" }}>
{failedMessage}
</Typography>
</AccordionDetails>
</Accordion>
);
}