From 6d324bbe64ba06cc5e00500ceaba892560f4a66e Mon Sep 17 00:00:00 2001 From: amnambiar Date: Fri, 22 Sep 2023 18:30:08 +0530 Subject: [PATCH 1/9] PLT-7646 Fixed calculation of progress% of last property in TimelineView grid when Finished --- src/components/StatusIcon/StatusIcon.tsx | 18 +++++++++ .../TimelineItem/timeline.helper.tsx | 13 ------- .../certification/Certification.helper.tsx | 21 +++++++++- .../certification-result/FullReportTable.tsx | 13 ++----- .../components/FileCoverageContainer.tsx | 2 +- .../components/TimelineView/TimelineView.tsx | 38 ++++++++++++------- 6 files changed, 67 insertions(+), 38 deletions(-) create mode 100644 src/components/StatusIcon/StatusIcon.tsx diff --git a/src/components/StatusIcon/StatusIcon.tsx b/src/components/StatusIcon/StatusIcon.tsx new file mode 100644 index 00000000..657d4c3c --- /dev/null +++ b/src/components/StatusIcon/StatusIcon.tsx @@ -0,0 +1,18 @@ +const StatusIcon: React.FC<{ + iconName: string; + altText?: string; + }> = ({ + iconName, + altText, + ...props + }) => { + + return {altText +} + +export default StatusIcon \ No newline at end of file diff --git a/src/components/TimelineItem/timeline.helper.tsx b/src/components/TimelineItem/timeline.helper.tsx index 0f4646d3..e9a1b9bb 100644 --- a/src/components/TimelineItem/timeline.helper.tsx +++ b/src/components/TimelineItem/timeline.helper.tsx @@ -58,19 +58,6 @@ export const processTimeLineConfig = ( const currentState = status === "finished" ? "passed" : state || "running"; let returnObj: any = { ...item, state: currentState }; - // if ( - // status === "certifying" && - // currentState === "running" && - // res.data.progress && - // res.data.plan - // ) { - // const plannedTasksCount = getPlannedCertificationTaskCount(res.data.plan); - // if (plannedTasksCount > 0) { - // returnObj["progress"] = Math.trunc((res.data.progress["finished-tasks"].length / plannedTasksCount) * 100); - // } else { - // returnObj["progress"] = 0; - // } - // } return returnObj; } // Set the previously executed states as passed diff --git a/src/pages/certification/Certification.helper.tsx b/src/pages/certification/Certification.helper.tsx index 1fb37b00..ca9ba938 100644 --- a/src/pages/certification/Certification.helper.tsx +++ b/src/pages/certification/Certification.helper.tsx @@ -1,6 +1,13 @@ import { formatToTitleCase } from "utils/utils"; -export const CertificationTasks = [{ +export interface ICertificationTask { + label: string; + key: string; + type: string; + name: string; + runTimeTaken?: any; +} +export const CertificationTasks: ICertificationTask[] = [{ label: 'UnitTests', key: '_certRes_unitTestResults', type: 'array', @@ -53,6 +60,18 @@ export const isAnyTaskFailure = (result: any) => { return flag ? true : false; } +export const isTaskSuccess = (result: any, taskName: string) => { + let failed = false; + if (taskName === '_certRes_unitTestResults') { + failed = result[taskName].filter((item: any) => (typeof item !== 'string' && item.resultOutcome.tag === 'Failure'))?.length ? true : false + } else if (taskName === '_certRes_DLTests') { + failed = result[taskName].filter((item: any) => item[1].tag === 'Failure')?.length ? true : false + } else { + failed = result[taskName].tag === "Failure" + } + return !failed +} + export const processTablesDataForChart = (resultObj: any, tableAttr: string) => { let totalCount = 0 try { diff --git a/src/pages/certification/certification-result/FullReportTable.tsx b/src/pages/certification/certification-result/FullReportTable.tsx index 1cc342de..cfa94cdd 100644 --- a/src/pages/certification/certification-result/FullReportTable.tsx +++ b/src/pages/certification/certification-result/FullReportTable.tsx @@ -4,6 +4,7 @@ import TableComponent from "components/Table/Table" import { generateCollapsibleContent, processData } from "./fullReportTable.helper"; import './fullReportTable.css'; +import StatusIcon from "components/StatusIcon/StatusIcon"; const columns = [ { @@ -17,17 +18,9 @@ const columns = [ disableSortBy: true, Cell: (props: any) => { if (props.row.original.status === 'success') { - return success + return } else if (props.row.original.status === 'failure') { - return failure + return } else { return null; } } } diff --git a/src/pages/certification/components/FileCoverageContainer.tsx b/src/pages/certification/components/FileCoverageContainer.tsx index 237b35d3..8cd66c9a 100644 --- a/src/pages/certification/components/FileCoverageContainer.tsx +++ b/src/pages/certification/components/FileCoverageContainer.tsx @@ -97,7 +97,7 @@ const FileCoverageContainer: React.FC<{ return (<> {coverageIndexFiles ? (
-
    {renderRows()}
+ {renderRows()}
) : null} ); diff --git a/src/pages/certification/components/TimelineView/TimelineView.tsx b/src/pages/certification/components/TimelineView/TimelineView.tsx index d73aed2b..976cf139 100644 --- a/src/pages/certification/components/TimelineView/TimelineView.tsx +++ b/src/pages/certification/components/TimelineView/TimelineView.tsx @@ -15,7 +15,9 @@ import { TIMELINE_CONFIG } from "compositions/Timeline/timeline.config"; import { processFinishedJson, processTimeLineConfig } from "components/TimelineItem/timeline.helper"; import { CertificationTasks, + ICertificationTask, isAnyTaskFailure, + isTaskSuccess, } from "./../../Certification.helper"; import LogsView from "components/LogsView/LogsView"; import ProgressCard from "components/ProgressCard/ProgressCard"; @@ -24,11 +26,13 @@ import DownloadResult from "../DownloadResult/DownloadResult"; import FileCoverageContainer from "../FileCoverageContainer"; import { clearPersistentStates } from "../AuditorRunTestForm/utils"; import Loader from "components/Loader/Loader"; +import StatusIcon from "components/StatusIcon/StatusIcon"; const TIMEOFFSET = 1000; interface PlanObj { + key: string; name: string; label: string; discarded: number; @@ -85,10 +89,14 @@ const TimelineView: React.FC<{ if (!plannedTestingTasks.length && resPlan.length) { setPlannedTestingTasks( resPlan.map((item: { index: number; name: string }) => { + const TaskConfig: ICertificationTask | undefined = CertificationTasks.find((task) => task.name === item.name) + if (!TaskConfig) { + return null; + } return { + key: TaskConfig.key, name: item.name, - label: CertificationTasks.find((task) => task.name === item.name) - ?.label, + label: TaskConfig.label, discarded: 0, progress: 0, }; @@ -251,7 +259,7 @@ const TimelineView: React.FC<{ {runStatus === "certifying" || runStatus === "finished" ? ( <> -
+
{runStatus === "finished" && - {plannedTestingTasks.map((task: PlanObj) => { + {plannedTestingTasks.map((task: PlanObj, index: number) => { return ( - {task.progress === 100 ? ( - complete - ) : ( - {task.progress}% - )} + { + (runStatus === "finished" && (plannedTestingTasks.length - 1 === index)) + ? (isTaskSuccess(resultData, task.key) ? + + : + ) + : (task.progress === 100 ? + + : {task.progress}% + ) + + } + ); From a2286c6ed0ecb86ac0ab74ca05246954f6db9a20 Mon Sep 17 00:00:00 2001 From: amnambiar Date: Sun, 24 Sep 2023 10:16:33 +0530 Subject: [PATCH 2/9] PLT-7646 Fixes to calcultion of Property Based Testing progress card --- .../certification/Certification.helper.tsx | 6 +++--- .../CertificationResult.tsx | 19 +++++++++++++++--- .../components/TimelineView/TimelineView.tsx | 20 +++++++++++++++---- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/pages/certification/Certification.helper.tsx b/src/pages/certification/Certification.helper.tsx index ca9ba938..0dffc84a 100644 --- a/src/pages/certification/Certification.helper.tsx +++ b/src/pages/certification/Certification.helper.tsx @@ -63,11 +63,11 @@ export const isAnyTaskFailure = (result: any) => { export const isTaskSuccess = (result: any, taskName: string) => { let failed = false; if (taskName === '_certRes_unitTestResults') { - failed = result[taskName].filter((item: any) => (typeof item !== 'string' && item.resultOutcome.tag === 'Failure'))?.length ? true : false + failed = result.filter((item: any) => (typeof item !== 'string' && item.resultOutcome.tag === 'Failure'))?.length ? true : false } else if (taskName === '_certRes_DLTests') { - failed = result[taskName].filter((item: any) => item[1].tag === 'Failure')?.length ? true : false + failed = result.filter((item: any) => item[1].tag === 'Failure')?.length ? true : false } else { - failed = result[taskName].tag === "Failure" + failed = result.tag === "Failure" } return !failed } diff --git a/src/pages/certification/certification-result/CertificationResult.tsx b/src/pages/certification/certification-result/CertificationResult.tsx index a7911e1a..99395922 100644 --- a/src/pages/certification/certification-result/CertificationResult.tsx +++ b/src/pages/certification/certification-result/CertificationResult.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState, useCallback } from "react"; +import { useEffect, useState, useCallback, useRef } from "react"; import { useParams } from "react-router"; import { useLocation } from "react-router-dom"; @@ -21,7 +21,7 @@ import "../Certification.scss"; import DownloadResult from "../components/DownloadResult/DownloadResult"; import ProgressCard from "components/ProgressCard/ProgressCard"; import FullReportTable from "./FullReportTable"; -import { isAnyTaskFailure } from "../Certification.helper"; +import { isAnyTaskFailure, isTaskSuccess, taskKeys } from "../Certification.helper"; const CertificationResult = () => { const param = useParams<{ uuid: string }>(); @@ -32,6 +32,9 @@ const CertificationResult = () => { const [errorToast, setErrorToast] = useState(false); const [timelineConfig, setTimelineConfig] = useState(TIMELINE_CONFIG); + const propertyBasedTestProgressTotal = useRef(0) + const currentPropertyBasedTestProgress = useRef(0) + useEffect(() => { (async () => { try { @@ -52,6 +55,16 @@ const CertificationResult = () => { const unitTestResult = processFinishedJson(resultJson); setUnitTestSuccess(unitTestResult); setResultData(resultJson); + const resultTaskKeys = Object.keys(resultJson) + const certificationTaskKeys = taskKeys(); + let resultTaskKeysLength = 0; + resultTaskKeys.forEach((key: any) => { + if (certificationTaskKeys.indexOf(key) !== -1 && resultJson[key]) { + currentPropertyBasedTestProgress.current += isTaskSuccess(resultJson[key], key) ? 100 : 0 + resultTaskKeysLength++; + } + }) + propertyBasedTestProgressTotal.current = resultTaskKeysLength * 100 } } catch (error) { handleErrorScenario(); @@ -101,7 +114,7 @@ const CertificationResult = () => { result={resultData} coverageFile={coverageFile} />} - {/* */} +
) : null} diff --git a/src/pages/certification/components/TimelineView/TimelineView.tsx b/src/pages/certification/components/TimelineView/TimelineView.tsx index 976cf139..89f9266c 100644 --- a/src/pages/certification/components/TimelineView/TimelineView.tsx +++ b/src/pages/certification/components/TimelineView/TimelineView.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { useNavigate } from "react-router-dom"; import { useConfirm } from "material-ui-confirm"; @@ -63,6 +63,8 @@ const TimelineView: React.FC<{ const [hasFailedTasks, setHasFailedTasks] = useState(false); const [plannedTestingTasks, setPlannedTestingTasks] = useState([]); + const currentPropertyBasedTestProgress = useRef([]); + const abortTestRun = () => { confirm({ title: "", description: "Are sure you want to abort this run!" }) .then(async () => { @@ -121,6 +123,7 @@ const TimelineView: React.FC<{ resProgress["finished-tasks"].forEach((entry: any) => { if (item.name === entry["task"]["name"] && entry.succeeded) { item.progress = 100; + recalculateTestTaskProgress(entry['task']) } }); } @@ -131,6 +134,12 @@ const TimelineView: React.FC<{ } }; + const recalculateTestTaskProgress = (task: any) => { + if (currentPropertyBasedTestProgress.current.indexOf(task['name']) === -1) { + currentPropertyBasedTestProgress.current.push(task['name']) + } + } + const triggerFetchRunStatus = async () => { let config = timelineConfig; try { @@ -266,7 +275,7 @@ const TimelineView: React.FC<{ coverageFile={coverageFile} /> } - {/* */} +
@@ -300,8 +309,11 @@ const TimelineView: React.FC<{ { (runStatus === "finished" && (plannedTestingTasks.length - 1 === index)) - ? (isTaskSuccess(resultData, task.key) ? - + ? (isTaskSuccess(resultData[task.key], task.key) ? + (<> + + {recalculateTestTaskProgress(task)} + ) : ) : (task.progress === 100 ? From e43ade526126b70eabfd8371d8668394fd2d7df7 Mon Sep 17 00:00:00 2001 From: amnambiar Date: Sun, 24 Sep 2023 22:32:20 +0530 Subject: [PATCH 3/9] PLT-7646 - PLT-7656 Fix to multiple horizontal scrollbars above 4k --- .../certification/certification-result/fullReportTable.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/certification/certification-result/fullReportTable.css b/src/pages/certification/certification-result/fullReportTable.css index 97606be0..89a83873 100644 --- a/src/pages/certification/certification-result/fullReportTable.css +++ b/src/pages/certification/certification-result/fullReportTable.css @@ -1,6 +1,6 @@ .task-details { padding: 20px ; overflow: auto; - max-width: calc(100vw - 400px); + max-width: calc(100vw - 457px); max-height: 500px; } \ No newline at end of file From 40a999aefee93c83d7e1903aad64d35ee9af3259 Mon Sep 17 00:00:00 2001 From: amnambiar Date: Tue, 26 Sep 2023 17:09:01 +0530 Subject: [PATCH 4/9] Cleanup of commented lines --- src/hooks/useLocalStorage.ts | 9 --------- src/hooks/useLogs.ts | 4 ---- 2 files changed, 13 deletions(-) diff --git a/src/hooks/useLocalStorage.ts b/src/hooks/useLocalStorage.ts index de3158b4..9ff5de6c 100644 --- a/src/hooks/useLocalStorage.ts +++ b/src/hooks/useLocalStorage.ts @@ -1,15 +1,6 @@ /* eslint-disable no-useless-escape */ import { Dispatch, SetStateAction, useEffect, useState } from "react"; -function isValidJSON(text: string) { - try { - const result = JSON.parse(text); - return typeof result === "object" && result !== null; - } catch (e) { - return false; - } -} - // - string | null | boolean + to fix type errors while using the value function useLocalStorage( key: string, diff --git a/src/hooks/useLogs.ts b/src/hooks/useLogs.ts index fa72bbfc..444ea3c3 100644 --- a/src/hooks/useLogs.ts +++ b/src/hooks/useLogs.ts @@ -6,8 +6,6 @@ import { useDelayedApi } from "hooks/useDelayedApi"; import { fetchData } from "api/api"; import { Log } from '../pages/certification/Certification.helper' import { setStates, setEnded, setBuildInfo } from "pages/certification/slices/logRunTime.slice"; -// import { LocalStorageKeys } from "constants/constants"; -// import useLocalStorage from "./useLocalStorage"; const TIME_OFFSET = 1000; @@ -22,8 +20,6 @@ export const useLogs = ( const [fetchingLogs, setFetchingLogs] = useState(false); const [refetchLogsOffset] = useState(1); - // const [, setCertificationRunTime] = useLocalStorage(LocalStorageKeys.certificationRunTime, null) - const { startTime, endTime, runState, ended } = useAppSelector((state) => state.runTime) const enabled = !fetchingLogs && !(ended > 1) && !!uuid From e98172dee294d9b83f4111261ac63e8d7ab0ec72 Mon Sep 17 00:00:00 2001 From: amnambiar Date: Wed, 27 Sep 2023 13:03:11 +0530 Subject: [PATCH 5/9] PLT-7646 Fix to incorrect data in progress% column when all at once executed --- src/components/LogsView/LogsView.tsx | 166 ++++++++++-------- .../components/TimelineView/TimelineView.tsx | 66 ++++--- 2 files changed, 134 insertions(+), 98 deletions(-) diff --git a/src/components/LogsView/LogsView.tsx b/src/components/LogsView/LogsView.tsx index 7a11e9f0..b1559988 100644 --- a/src/components/LogsView/LogsView.tsx +++ b/src/components/LogsView/LogsView.tsx @@ -4,86 +4,106 @@ import React, { FC, useState, useEffect, useRef } from "react"; import "./LogsView.scss"; import LogsViewEntry from "./LogsViewEntry"; -const LogsView: FC<{ runId: string, endPolling?: boolean, oneTime?: boolean, open?: boolean }> = ({ runId, endPolling = false, oneTime = false, open = false }) => { - const [showLogs, setShowLogs] = useState(false); - const bottomRef = useRef(null); - const logContentRef = useRef(null); +const LogsView: FC<{ + runId: string; + endPolling?: boolean; + oneTime?: boolean; + open?: boolean; + latestTestingProgress?: (progress: any) => any; +}> = ({ + runId, + endPolling = false, + oneTime = false, + open = false, + latestTestingProgress, +}) => { + const [showLogs, setShowLogs] = useState(false); + const bottomRef = useRef(null); + const logContentRef = useRef(null); + const lastLog = useRef(null); - const showLogView = () => { - setShowLogs(true); - const timeout = setTimeout(() => { - clearTimeout(timeout) - bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); // scroll to bottom - }, 2); - } + const showLogView = () => { + setShowLogs(true); + const timeout = setTimeout(() => { + clearTimeout(timeout); + bottomRef.current?.scrollIntoView({ behavior: "smooth" }); // scroll to bottom + }, 2); + }; - const hideLogView = () => { - setShowLogs(false) - } + const hideLogView = () => { + setShowLogs(false); + }; + const { logInfo: logs } = useLogs(runId, oneTime || endPolling, !oneTime); - const { logInfo: logs } = useLogs( - runId, - oneTime || endPolling, - !oneTime - ) + useEffect(() => { + lastLog.current = logs.length ? logs.length - 1 : null; + bottomRef.current?.scrollIntoView({ behavior: "smooth" }); // scroll to bottom + }, [logs]); + useEffect(() => { + open && showLogView(); + }, [open]); - useEffect(() => { - bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); // scroll to bottom - }, [logs]) + return ( + <> +
+ + View logs + +
+
+
Logs
+ + - + +
+
+ {logs.map((item: any, index: number) => { + if ( + latestTestingProgress && + lastLog.current && + index === lastLog.current - 1 && + item.Source.indexOf("run-certify") !== -1 + ) { + // is 2nd last entry in logs when Source has run-certify + latestTestingProgress(JSON.parse(item.Text)); + } - useEffect(() => { - open && showLogView() - }, [open]) - - return ( - <> -
- - View logs - -
-
-
Logs
- - - - -
-
- {logs.map((item: any, index: number) => { - let logData = '' - try { - const data = JSON.parse(item.Text) - const attr = data[Object.keys(data)[0]] - if (attr?.fields?.hasOwnProperty('launch-config')) { - logData = attr['fields']['launch-config'] - } - if (attr?.fields?.hasOwnProperty('chunk-data')) { - logData = attr['fields']['chunk-data'] - } - } catch (e) { - // do nothing - if (typeof item.Text == 'string' && item.Text.length) { - logData = item.Text - } - } - logData = !logData.length ? item.Text : logData - return logData.length ? ( - - ) : null - })} -
-
-
-
- - ); + let logData = ""; + try { + const data = JSON.parse(item.Text); + const attr = data[Object.keys(data)[0]]; + if (attr?.fields?.hasOwnProperty("launch-config")) { + logData = attr["fields"]["launch-config"]; + } + if (attr?.fields?.hasOwnProperty("chunk-data")) { + logData = attr["fields"]["chunk-data"]; + } + } catch (e) { + // do nothing + if (typeof item.Text == "string" && item.Text.length) { + logData = item.Text; + } + } + logData = !logData.length ? item.Text : logData; + return logData.length ? ( + + ) : null; + })} +
+
+
+
+ + ); }; export default LogsView; diff --git a/src/pages/certification/components/TimelineView/TimelineView.tsx b/src/pages/certification/components/TimelineView/TimelineView.tsx index 89f9266c..807a5f0d 100644 --- a/src/pages/certification/components/TimelineView/TimelineView.tsx +++ b/src/pages/certification/components/TimelineView/TimelineView.tsx @@ -80,6 +80,37 @@ const TimelineView: React.FC<{ navigate("/report/" + uuid, { state: { certifiable: true, repo: repo, commitHash: commitHash } }); }; + const assignProgressToTasks = (progress: any) => { + setPlannedTestingTasks( + plannedTestingTasks.map((item: PlanObj) => { + const currentTask = progress["current-task"]; + if (currentTask && item.name === currentTask["name"]) { + const currentProgressStats = progress["qc-progress"]; + item.discarded = currentProgressStats["discarded"]; + item.progress = Math.trunc( + ((currentProgressStats["successes"] + + currentProgressStats["failures"]) / + currentProgressStats["expected"]) * + 100 + ); + } + if (progress["finished-tasks"].length) { + progress["finished-tasks"].forEach((entry: any) => { + if (item.name === entry["task"]["name"]) { + if (entry.succeeded) { + item.progress = 100; + } else { + item.progress = -1; + } + recalculateTestTaskProgress(entry['task']) + } + }); + } + return item; + }) + ); + } + const handleTestingTimelineDetails = ( status: string, state: string, @@ -106,30 +137,7 @@ const TimelineView: React.FC<{ ); } else if (plannedTestingTasks.length && resProgress) { // planned tasks are already defined - setPlannedTestingTasks( - plannedTestingTasks.map((item: PlanObj) => { - const currentTask = resProgress["current-task"]; - if (currentTask && item.name === currentTask["name"]) { - const currentProgressStats = resProgress["qc-progress"]; - item.discarded = currentProgressStats["discarded"]; - item.progress = Math.trunc( - ((currentProgressStats["successes"] + - currentProgressStats["failures"]) / - currentProgressStats["expected"]) * - 100 - ); - } - if (resProgress["finished-tasks"].length) { - resProgress["finished-tasks"].forEach((entry: any) => { - if (item.name === entry["task"]["name"] && entry.succeeded) { - item.progress = 100; - recalculateTestTaskProgress(entry['task']) - } - }); - } - return item; - }) - ); + assignProgressToTasks(resProgress) } } }; @@ -181,6 +189,11 @@ const TimelineView: React.FC<{ } }; + const processLatestTestingProgressFromLogs = (response: {status: any}) => { + if (plannedTestingTasks && response.status) + assignProgressToTasks(response.status); + } + useEffect(() => { if (uuid.length) { triggerFetchRunStatus(); @@ -264,6 +277,7 @@ const TimelineView: React.FC<{ runId={uuid} endPolling={runStatus === "finished" || runState === "failed"} open={runState === "failed"} + latestTestingProgress={processLatestTestingProgressFromLogs} /> {runStatus === "certifying" || runStatus === "finished" ? ( @@ -318,7 +332,9 @@ const TimelineView: React.FC<{ ) : (task.progress === 100 ? - : {task.progress}% + : task.progress === -1 ? + + : {task.progress}% ) } From 7b2c77aa1d388aa2881506cb702559ed0c25b3e8 Mon Sep 17 00:00:00 2001 From: amnambiar Date: Fri, 29 Sep 2023 18:34:34 +0530 Subject: [PATCH 6/9] PLT-7047 HOTFIX to private repo access check --- .../components/AuditorRunTestForm/AuditorRunTestForm.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pages/certification/components/AuditorRunTestForm/AuditorRunTestForm.tsx b/src/pages/certification/components/AuditorRunTestForm/AuditorRunTestForm.tsx index e2bbf1e7..10904e95 100644 --- a/src/pages/certification/components/AuditorRunTestForm/AuditorRunTestForm.tsx +++ b/src/pages/certification/components/AuditorRunTestForm/AuditorRunTestForm.tsx @@ -93,6 +93,7 @@ const AuditorRunTestForm: React.FC = ({ // fetch CLIENT_ID from api const clientId = (await fetchData.get("/github/client-id").catch(error => { throw new Error(error) })) .data as string; + localStorage.setItem('testingForm', JSON.stringify(form.getValues())) clientId && window.location.assign( `https://github.com/login/oauth/authorize?client_id=${clientId}&scope=repo` ); @@ -148,6 +149,7 @@ const AuditorRunTestForm: React.FC = ({ return; } + const [, , , username, repoName, , commitHash] = formData.repoURL.split("/"); dispatch(setRepoUrl(`https://github.com/${username}/${repoName}`)); @@ -217,6 +219,13 @@ const AuditorRunTestForm: React.FC = ({ } // the enclosed snippet is to be triggered only once right when the component is rendered to check if the url contains code (to validate if it is a redirect from github) + const formDataInLS = localStorage.getItem('testingForm') + if (formDataInLS && formDataInLS !== 'undefined') { + const profileFormData = JSON.parse(formDataInLS); + form.reset(profileFormData) + localStorage.removeItem('testingForm') + } + // Run on unmount return () => { dispatch(clearAccessStatus()) From ca7c0a8c971502ba00b860fecc7d19cd926700f7 Mon Sep 17 00:00:00 2001 From: amnambiar Date: Fri, 29 Sep 2023 18:36:47 +0530 Subject: [PATCH 7/9] PLT-7047 Fixed image url --- src/components/RepoAccessStatus/repoAccessStatus.config.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/RepoAccessStatus/repoAccessStatus.config.ts b/src/components/RepoAccessStatus/repoAccessStatus.config.ts index d538d132..1a362360 100644 --- a/src/components/RepoAccessStatus/repoAccessStatus.config.ts +++ b/src/components/RepoAccessStatus/repoAccessStatus.config.ts @@ -2,21 +2,21 @@ export const ACCESS_STATUS: { [index: string]: any } = { verifying: { className: "image anim-rotate", "data-testid": "verifying", - src: "images/running.svg", + src: "/images/running.svg", alt: "verifying", color: "#DBAB0A", }, accessible: { className: "image", "data-testid": "accessible", - src: "images/passed.svg", + src: "/images/passed.svg", alt: "accessible", color: "#009168", }, notAccessible: { className: "image", "data-testid": "notAccessible", - src: "images/failed.svg", + src: "/images/failed.svg", alt: "not accessible", color: "#FF493D", }, From b1ea3ad6aa84972a6cb952f7e2d9860ebf5a0a59 Mon Sep 17 00:00:00 2001 From: amnambiar Date: Fri, 29 Sep 2023 19:11:27 +0530 Subject: [PATCH 8/9] PLT-7047 fix save githubToken to profile along with dapp --- .../components/AuditorRunTestForm/AuditorRunTestForm.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/certification/components/AuditorRunTestForm/AuditorRunTestForm.tsx b/src/pages/certification/components/AuditorRunTestForm/AuditorRunTestForm.tsx index 10904e95..90c8bd22 100644 --- a/src/pages/certification/components/AuditorRunTestForm/AuditorRunTestForm.tsx +++ b/src/pages/certification/components/AuditorRunTestForm/AuditorRunTestForm.tsx @@ -19,6 +19,7 @@ import { auditorRunTestFormSchema } from "./auditorRunTestForm.schema"; import { IAuditorRunTestFormFields } from "./auditorRunTestForm.interface"; import { clearAccessStatus, + clearAccessToken, getUserAccessToken, verifyRepoAccess, } from "store/slices/repositoryAccess.slice"; @@ -76,7 +77,7 @@ const AuditorRunTestForm: React.FC = ({ const { repoUrl } = useAppSelector((state) => state.certification); const { profile } = useAppSelector((state) => state.auth); - const { showConfirmConnection, accessStatus } = useAppSelector((state) => state.repoAccess); + const { showConfirmConnection, accessStatus, accessToken } = useAppSelector((state) => state.repoAccess); const confirm = useConfirm(); const [submitting, setSubmitting] = useState(false); const [showError, setShowError] = useState(""); @@ -168,10 +169,12 @@ const AuditorRunTestForm: React.FC = ({ repo: repoName, name: name, version: version, - subject: subject + subject: subject, + githubToken: accessToken || null, }, })); if (response.payload && response.payload?.dapp?.owner) { + dispatch(clearAccessToken()) const runResponse = await postData.post("/run", checkout); if (runResponse.data) { // store data into LS From 4dcebd8ca688119eb85bd146497b552607828e43 Mon Sep 17 00:00:00 2001 From: amnambiar Date: Fri, 29 Sep 2023 19:46:14 +0530 Subject: [PATCH 9/9] PLT-7646 Added ProgressCard; renewed calculations and fixed values in progress% in timelineView grid --- src/components/LogsView/LogsView.tsx | 24 ++- .../certification/Certification.helper.tsx | 110 ++++++++++++ .../CertificationResult.tsx | 35 ++-- .../components/TimelineView/TimelineView.tsx | 157 ++++++++++-------- 4 files changed, 229 insertions(+), 97 deletions(-) diff --git a/src/components/LogsView/LogsView.tsx b/src/components/LogsView/LogsView.tsx index b1559988..44efc991 100644 --- a/src/components/LogsView/LogsView.tsx +++ b/src/components/LogsView/LogsView.tsx @@ -20,7 +20,7 @@ const LogsView: FC<{ const [showLogs, setShowLogs] = useState(false); const bottomRef = useRef(null); const logContentRef = useRef(null); - const lastLog = useRef(null); + const lastLog = useRef(null); const showLogView = () => { setShowLogs(true); @@ -37,9 +37,15 @@ const LogsView: FC<{ const { logInfo: logs } = useLogs(runId, oneTime || endPolling, !oneTime); useEffect(() => { - lastLog.current = logs.length ? logs.length - 1 : null; + if (endPolling && latestTestingProgress) { + lastLog.current = logs[logs.length - 2] + if (lastLog.current?.Source.indexOf('run-certify') !== -1) { + // is 2nd last entry in logs when Source has run-certify + latestTestingProgress(JSON.parse(lastLog.current.Text)); + } + } bottomRef.current?.scrollIntoView({ behavior: "smooth" }); // scroll to bottom - }, [logs]); + }, [logs, endPolling, latestTestingProgress]); useEffect(() => { open && showLogView(); @@ -66,17 +72,7 @@ const LogsView: FC<{
- {logs.map((item: any, index: number) => { - if ( - latestTestingProgress && - lastLog.current && - index === lastLog.current - 1 && - item.Source.indexOf("run-certify") !== -1 - ) { - // is 2nd last entry in logs when Source has run-certify - latestTestingProgress(JSON.parse(item.Text)); - } - + {logs.map((item: any, index: number) => { let logData = ""; try { const data = JSON.parse(item.Text); diff --git a/src/pages/certification/Certification.helper.tsx b/src/pages/certification/Certification.helper.tsx index 0dffc84a..a3d9bad7 100644 --- a/src/pages/certification/Certification.helper.tsx +++ b/src/pages/certification/Certification.helper.tsx @@ -7,6 +7,46 @@ export interface ICertificationTask { name: string; runTimeTaken?: any; } + +export interface IFinishedTestingTaskDetails { + "qc-result": { + discarded: number; + expected: number; + failures: number; + succeeded: number; + }, + succeeded: boolean, + task: { + name: string; + index: number; + } +} + +export interface ITestingProgress { + "current-task": { + name: string; + index: number; + }, + "finished-tasks": Array, + "progress-index": number, + "qc-progress": { + discarded: number; + failures: number; + succeeded: number; + expected?: number; + } +} + +export interface PlanObj { + key: string; + name: string; + label: string; + discarded: number; + progress: number | 'On Going'; + expected?: number; + completed?: number; +} + export const CertificationTasks: ICertificationTask[] = [{ label: 'UnitTests', key: '_certRes_unitTestResults', @@ -112,4 +152,74 @@ export const getCertificationTaskName = (key: string) => { export const taskKeys = () => { return CertificationTasks.map(item => item.key) +} + +export const parseTestCount = (resultText: string) => { + if (resultText.indexOf("+++ OK, passed ") !== -1) { + // has the num of tests ran + return parseInt(resultText.split("+++ OK, passed ")[1].split(" tests")[0], 10) + } else { + return 1; + } +} + +export const calculateCurrentProgress = (plannedTasks: PlanObj[]) => { + return plannedTasks.reduce((accumulator, task) => { + return accumulator + (task.completed || 0) + }, 0) +} + +export const calculateExpectedProgress = (plannedTasks: PlanObj[]) => { + return plannedTasks.reduce((accumulator, task) => { + return accumulator + (task.expected || 0) + }, 0) +} + +const DEFAULT_TESTS_COUNT: number = 100; +// calculate the expected and completed to populate the Progress Card +export const getProgressCardInfo = (keyResult: any, currentTask: PlanObj) => { + const item = {...currentTask} + if (isTaskSuccess(keyResult, item.key)) { + if (item.name === 'dl-tests') { + keyResult.forEach((dlTest: any) => { + item.expected = (item.expected || 0) + (dlTest[1].numTests - dlTest[1].numDiscarded) + }) + } else if (item.name === 'unit-tests') { + keyResult.forEach((unitTest: any) => { + item.expected = (item.expected || 0) + parseTestCount(unitTest.resultDescription) + }) + } else { + item.expected = keyResult.numTests - keyResult.numDiscarded + } + item.completed = item.expected + } else { + if (!item.hasOwnProperty('expected')) { + // expected not already calculated from 'finished-tasks' + if (item.name === 'unit-tests') { + keyResult.forEach((unitTest: any) => { + if (unitTest.resultOutcome.tag === 'Failure') { + item.expected = (item.expected || 0) + 1 + item.completed = 0 + } else if (unitTest.resultOutcome.tag === 'Success') { + item.expected = (item.expected || 0) + parseTestCount(unitTest.resultDescription) + item.completed = (item.completed || 0) + parseTestCount(unitTest.resultDescription) + } + }) + } else if (item.name === 'dl-tests') { + keyResult.forEach((dlTest: any) => { + if (dlTest[1].tag === 'Success') { + item.expected = (item.expected || 0) + (dlTest[1].numTests - dlTest[1].numDiscarded) + item.completed = (item.completed || 0) + (dlTest[1].numTests - dlTest[1].numDiscarded) + } else if (dlTest[1].tag === 'Failure') { + item.expected = (item.expected || 0) + DEFAULT_TESTS_COUNT + item.completed = (item.completed || 0) + (dlTest[1].numTests || 0) + } + }) + } else { + item.expected = DEFAULT_TESTS_COUNT + item.completed = keyResult.numTests || 0 + } + } + } + return item } \ No newline at end of file diff --git a/src/pages/certification/certification-result/CertificationResult.tsx b/src/pages/certification/certification-result/CertificationResult.tsx index 99395922..65d90ab7 100644 --- a/src/pages/certification/certification-result/CertificationResult.tsx +++ b/src/pages/certification/certification-result/CertificationResult.tsx @@ -21,7 +21,7 @@ import "../Certification.scss"; import DownloadResult from "../components/DownloadResult/DownloadResult"; import ProgressCard from "components/ProgressCard/ProgressCard"; import FullReportTable from "./FullReportTable"; -import { isAnyTaskFailure, isTaskSuccess, taskKeys } from "../Certification.helper"; +import { calculateCurrentProgress, calculateExpectedProgress, CertificationTasks, getProgressCardInfo, ICertificationTask, isAnyTaskFailure, PlanObj } from "../Certification.helper"; const CertificationResult = () => { const param = useParams<{ uuid: string }>(); @@ -32,8 +32,7 @@ const CertificationResult = () => { const [errorToast, setErrorToast] = useState(false); const [timelineConfig, setTimelineConfig] = useState(TIMELINE_CONFIG); - const propertyBasedTestProgressTotal = useRef(0) - const currentPropertyBasedTestProgress = useRef(0) + const testTaskProgress = useRef([]) useEffect(() => { (async () => { @@ -56,15 +55,26 @@ const CertificationResult = () => { setUnitTestSuccess(unitTestResult); setResultData(resultJson); const resultTaskKeys = Object.keys(resultJson) - const certificationTaskKeys = taskKeys(); - let resultTaskKeysLength = 0; resultTaskKeys.forEach((key: any) => { - if (certificationTaskKeys.indexOf(key) !== -1 && resultJson[key]) { - currentPropertyBasedTestProgress.current += isTaskSuccess(resultJson[key], key) ? 100 : 0 - resultTaskKeysLength++; + + const TaskConfig: ICertificationTask | undefined = CertificationTasks.find((task) => task.key === key) + if (!TaskConfig || !resultJson[key]) { + // do nothing + } else { + const taskEntry = { + key: TaskConfig.key, + name: TaskConfig.name, + label: TaskConfig.label, + discarded: 0, + progress: 0 + } + + testTaskProgress.current.push({ + ...taskEntry, + ...getProgressCardInfo(resultJson[key], taskEntry) + }) } }) - propertyBasedTestProgressTotal.current = resultTaskKeysLength * 100 } } catch (error) { handleErrorScenario(); @@ -114,8 +124,11 @@ const CertificationResult = () => { result={resultData} coverageFile={coverageFile} />} - - +
) : null} diff --git a/src/pages/certification/components/TimelineView/TimelineView.tsx b/src/pages/certification/components/TimelineView/TimelineView.tsx index 807a5f0d..03c8608c 100644 --- a/src/pages/certification/components/TimelineView/TimelineView.tsx +++ b/src/pages/certification/components/TimelineView/TimelineView.tsx @@ -14,10 +14,16 @@ import Timeline from "compositions/Timeline/Timeline"; import { TIMELINE_CONFIG } from "compositions/Timeline/timeline.config"; import { processFinishedJson, processTimeLineConfig } from "components/TimelineItem/timeline.helper"; import { + calculateCurrentProgress, + calculateExpectedProgress, CertificationTasks, + getProgressCardInfo, ICertificationTask, + IFinishedTestingTaskDetails, isAnyTaskFailure, isTaskSuccess, + ITestingProgress, + PlanObj } from "./../../Certification.helper"; import LogsView from "components/LogsView/LogsView"; import ProgressCard from "components/ProgressCard/ProgressCard"; @@ -30,14 +36,8 @@ import StatusIcon from "components/StatusIcon/StatusIcon"; const TIMEOFFSET = 1000; - -interface PlanObj { - key: string; - name: string; - label: string; - discarded: number; - progress: number; -} +const PROGRESS_PASS = 100; +const PROGRESS_FAIL = -1; const TimelineView: React.FC<{ uuid: string; @@ -63,8 +63,6 @@ const TimelineView: React.FC<{ const [hasFailedTasks, setHasFailedTasks] = useState(false); const [plannedTestingTasks, setPlannedTestingTasks] = useState([]); - const currentPropertyBasedTestProgress = useRef([]); - const abortTestRun = () => { confirm({ title: "", description: "Are sure you want to abort this run!" }) .then(async () => { @@ -80,37 +78,6 @@ const TimelineView: React.FC<{ navigate("/report/" + uuid, { state: { certifiable: true, repo: repo, commitHash: commitHash } }); }; - const assignProgressToTasks = (progress: any) => { - setPlannedTestingTasks( - plannedTestingTasks.map((item: PlanObj) => { - const currentTask = progress["current-task"]; - if (currentTask && item.name === currentTask["name"]) { - const currentProgressStats = progress["qc-progress"]; - item.discarded = currentProgressStats["discarded"]; - item.progress = Math.trunc( - ((currentProgressStats["successes"] + - currentProgressStats["failures"]) / - currentProgressStats["expected"]) * - 100 - ); - } - if (progress["finished-tasks"].length) { - progress["finished-tasks"].forEach((entry: any) => { - if (item.name === entry["task"]["name"]) { - if (entry.succeeded) { - item.progress = 100; - } else { - item.progress = -1; - } - recalculateTestTaskProgress(entry['task']) - } - }); - } - return item; - }) - ); - } - const handleTestingTimelineDetails = ( status: string, state: string, @@ -136,18 +103,45 @@ const TimelineView: React.FC<{ }) ); } else if (plannedTestingTasks.length && resProgress) { - // planned tasks are already defined - assignProgressToTasks(resProgress) + setPlannedTestingTasks( + plannedTestingTasks.map((item: PlanObj) => { + const currentTask = resProgress["current-task"]; + if (currentTask && item.name === currentTask["name"]) { + const currentProgressStats = resProgress["qc-progress"]; + item.discarded = currentProgressStats["discarded"]; + item.progress = currentProgressStats["expected"] ? Math.trunc( + ((currentProgressStats["successes"] + currentProgressStats["failures"]) / + // currentProgressStats["expected"]) * 100 + (currentProgressStats["expected"] - currentProgressStats["discarded"])) * 100 + ) : "On Going"; + } + const isInFinished = resProgress["finished-tasks"].find((task: IFinishedTestingTaskDetails) => task.task.name === item.name) + if (isInFinished) { + item.discarded = isInFinished["qc-result"].discarded + item.progress = isInFinished.succeeded ? PROGRESS_PASS : PROGRESS_FAIL + } + return item + }) + ) } + } else if (status === 'finished') { + const isArrayResult = Array.isArray(res.data.result); + const resultJson = isArrayResult + ? res.data.result[0] + : res.data.result; + setPlannedTestingTasks( + plannedTestingTasks.map((item: PlanObj) => { + if (isTaskSuccess(resultJson[item.key], item.key)) { + item.progress = PROGRESS_PASS + } else { + item.progress = PROGRESS_FAIL; + } + return {...item, ...getProgressCardInfo(resultJson[item.key], item)} + }) + ) } }; - const recalculateTestTaskProgress = (task: any) => { - if (currentPropertyBasedTestProgress.current.indexOf(task['name']) === -1) { - currentPropertyBasedTestProgress.current.push(task['name']) - } - } - const triggerFetchRunStatus = async () => { let config = timelineConfig; try { @@ -190,8 +184,39 @@ const TimelineView: React.FC<{ }; const processLatestTestingProgressFromLogs = (response: {status: any}) => { - if (plannedTestingTasks && response.status) - assignProgressToTasks(response.status); + if (plannedTestingTasks && response.status) { + const progress: ITestingProgress = response.status; + + plannedTestingTasks.map((task:PlanObj) => { + const finishedTask = progress["finished-tasks"].find(entry => task.name === entry.task.name) + if (finishedTask) { + if (!task.expected) { + task.expected = finishedTask["qc-result"].expected; + } + task.progress = finishedTask.succeeded ? PROGRESS_PASS : PROGRESS_FAIL; + } else if (progress['current-task'].name === task.name) { + if (!task.expected && progress['qc-progress'].hasOwnProperty('expected')) { + task.expected = progress['qc-progress'].expected + } + } + return task; + }) + } + } + + const getTaskProgress = (task: PlanObj, index: number) => { + return (runStatus === "finished" && (plannedTestingTasks.length - 1 === index)) + ? (isTaskSuccess(resultData[task.key], task.key) ? + + : + ) + : (task.progress === PROGRESS_PASS ? + + : task.progress === PROGRESS_FAIL ? + + : {task.progress}{typeof task.progress === "number" ? "%" : ""} + ) + } useEffect(() => { @@ -283,13 +308,18 @@ const TimelineView: React.FC<{ {runStatus === "certifying" || runStatus === "finished" ? ( <>
- {runStatus === "finished" && + - } - + + )}
@@ -321,24 +351,7 @@ const TimelineView: React.FC<{ {task.discarded} - { - (runStatus === "finished" && (plannedTestingTasks.length - 1 === index)) - ? (isTaskSuccess(resultData[task.key], task.key) ? - (<> - - {recalculateTestTaskProgress(task)} - ) - : - ) - : (task.progress === 100 ? - - : task.progress === -1 ? - - : {task.progress}% - ) - - } - + {getTaskProgress(task, index)} );