Repository: openshift/sippy
Pull request #2649: TRT-2094: add a link to the test_details report for each triage record
Author: login: smg247
Labels: name: approved
description: Indicates a PR has been approved by an approver from all required OWNERS files.
name: lgtm
description: Indicates that a PR is ready to be merged.
name: tide/merge-method-squash
description: Denotes a PR that should be squashed by tide when it merges.
name: jira/valid-reference
description: Indicates that this PR references a valid Jira ticket of any type.
Created: 2025-05-28T20:12:17Z | Merged: 2025-05-29T11:36:27Z

This adapts functionality from the `RegressedTestsPanel` to utilize in the `TriagedRegressionTestList` on the modal as well. The adapted functionality now resides in `CompReadyUtils`.

This is not shown on the triage details page as we don't have the necessary info available there to determine the URL. This could be enhanced in the future if desired, but I don't think it is necessary.


Diff:
diff --git a/sippy-ng/src/component_readiness/CompReadyUtils.js b/sippy-ng/src/component_readiness/CompReadyUtils.js
index 1c2928cd58..1fe5a91778 100644
--- a/sippy-ng/src/component_readiness/CompReadyUtils.js
+++ b/sippy-ng/src/component_readiness/CompReadyUtils.js
@@ -741,20 +741,40 @@ export function generateTestReport(
   return sortQueryParams(retUrl)
 }
 
+// Construct a URL with all existing filters utilizing the necessary info from the regressed test.
+// We pass these arguments to the component that generates the test details report.
+export function generateTestReportForRegressedTest(
+  regressedTest,
+  filterVals,
+  expandEnvironment
+) {
+  const environmentVal = formColumnName({ variants: regressedTest.variants })
+  const safeComponentName = safeEncodeURIComponent(regressedTest.component)
+  const safeTestId = safeEncodeURIComponent(regressedTest.test_id)
+  const safeTestName = safeEncodeURIComponent(regressedTest.test_name)
+  const safeTestBasisRelease = safeEncodeURIComponent(
+    regressedTest.base_stats?.release
+  )
+  let variantsUrl = ''
+  Object.entries(regressedTest.variants).forEach(([key, value]) => {
+    variantsUrl += '&' + key + '=' + safeEncodeURIComponent(value)
+  })
+  const retUrl =
+    '/component_readiness/test_details' +
+    filterVals +
+    `&testBasisRelease=${safeTestBasisRelease}` +
+    `&testId=${safeTestId}` +
+    expandEnvironment(environmentVal) +
+    `&component=${safeComponentName}` +
+    `&capability=${regressedTest.capability}` +
+    variantsUrl +
+    `&testName=${safeTestName}`
+
+  return sortQueryParams(retUrl)
+}
+
 export function generateRegressionCount(regressed_tests, triaged_incidents) {
   let regressedCount = regressed_tests ? regressed_tests.length : 0
   let triagedCount = triaged_incidents ? triaged_incidents.length : 0
   return regressedCount + triagedCount
 }
-
-export function safeExternalHref(url) {
-  try {
-    const parsed = new URL(url)
-    if (['http:', 'https:'].includes(parsed.protocol)) {
-      return parsed.href
-    }
-  } catch (e) {
-    // Invalid URL
-  }
-  return '#'
-}
diff --git a/sippy-ng/src/component_readiness/RegressedTestsModal.js b/sippy-ng/src/component_readiness/RegressedTestsModal.js
index 5329a8ba59..db9fb0d4a1 100644
--- a/sippy-ng/src/component_readiness/RegressedTestsModal.js
+++ b/sippy-ng/src/component_readiness/RegressedTestsModal.js
@@ -85,7 +85,11 @@ export default function RegressedTestsModal(props) {
           )}
           {triageEntriesExist && (
             <RegressedTestsTabPanel activeIndex={activeTabIndex} index={1}>
-              <TriagedTestsPanel triageEntries={props.triageEntries} />
+              <TriagedTestsPanel
+                triageEntries={props.triageEntries}
+                allRegressedTests={props.allRegressedTests}
+                filterVals={props.filterVals}
+              />
             </RegressedTestsTabPanel>
           )}
           <RegressedTestsTabPanel activeIndex={activeTabIndex} index={2}>
diff --git a/sippy-ng/src/component_readiness/RegressedTestsPanel.js b/sippy-ng/src/component_readiness/RegressedTestsPanel.js
index e7271edcbc..7e1a8c6afe 100644
--- a/sippy-ng/src/component_readiness/RegressedTestsPanel.js
+++ b/sippy-ng/src/component_readiness/RegressedTestsPanel.js
@@ -2,10 +2,13 @@ import { CapabilitiesContext } from '../App'
 import { CompReadyVarsContext } from './CompReadyVars'
 import { DataGrid, GridToolbar } from '@mui/x-data-grid'
 import { FileCopy } from '@mui/icons-material'
-import { formColumnName, sortQueryParams } from './CompReadyUtils'
+import {
+  formColumnName,
+  generateTestReportForRegressedTest,
+} from './CompReadyUtils'
 import { Link } from 'react-router-dom'
 import { Popover, Snackbar, Tooltip } from '@mui/material'
-import { relativeTime, safeEncodeURIComponent } from '../helpers'
+import { relativeTime } from '../helpers'
 import Alert from '@mui/material/Alert'
 import Button from '@mui/material/Button'
 import CompSeverityIcon from './CompSeverityIcon'
@@ -14,43 +17,8 @@ import PropTypes from 'prop-types'
 import React, { Fragment, useContext } from 'react'
 import TriageFields from './TriageFields'
 
-// Construct a URL with all existing filters plus testId, environment, and testName.
-// This is the url used when you click inside a TableCell on page4 on the right.
-// We pass these arguments to the component that generates the test details report.
-function generateTestReport(
-  testId,
-  variants,
-  filterVals,
-  componentName,
-  capabilityName,
-  testName,
-  testBasisRelease
-) {
-  const environmentVal = formColumnName({ variants: variants })
-  const { expandEnvironment } = useContext(CompReadyVarsContext)
-  const safeComponentName = safeEncodeURIComponent(componentName)
-  const safeTestId = safeEncodeURIComponent(testId)
-  const safeTestName = safeEncodeURIComponent(testName)
-  const safeTestBasisRelease = safeEncodeURIComponent(testBasisRelease)
-  let variantsUrl = ''
-  Object.entries(variants).forEach(([key, value]) => {
-    variantsUrl += '&' + key + '=' + safeEncodeURIComponent(value)
-  })
-  const retUrl =
-    '/component_readiness/test_details' +
-    filterVals +
-    `&testBasisRelease=${safeTestBasisRelease}` +
-    `&testId=${safeTestId}` +
-    expandEnvironment(environmentVal) +
-    `&component=${safeComponentName}` +
-    `&capability=${capabilityName}` +
-    variantsUrl +
-    `&testName=${safeTestName}`
-
-  return sortQueryParams(retUrl)
-}
-
 export default function RegressedTestsPanel(props) {
+  const { expandEnvironment } = useContext(CompReadyVarsContext)
   const { filterVals, regressedTests, setTriageEntryCreated } = props
   const [sortModel, setSortModel] = React.useState([
     { field: 'component', sort: 'asc' },
@@ -253,14 +221,10 @@ export default function RegressedTestsPanel(props) {
           className="status"
         >
           <Link
-            to={generateTestReport(
-              params.row.test_id,
-              params.row.variants,
+            to={generateTestReportForRegressedTest(
+              params.row,
               filterVals,
-              params.row.component,
-              params.row.capability,
-              params.row.test_name,
-              params.row.base_stats ? params.row.base_stats.release : ''
+              expandEnvironment
             )}
           >
             <CompSeverityIcon
diff --git a/sippy-ng/src/component_readiness/TriagedRegressionTestList.js b/sippy-ng/src/component_readiness/TriagedRegressionTestList.js
index 9e24f9487b..e371ad4c84 100644
--- a/sippy-ng/src/component_readiness/TriagedRegressionTestList.js
+++ b/sippy-ng/src/component_readiness/TriagedRegressionTestList.js
@@ -1,11 +1,19 @@
+import { CompReadyVarsContext } from './CompReadyVars'
 import { DataGrid, GridToolbar } from '@mui/x-data-grid'
-import { formColumnName } from './CompReadyUtils'
+import {
+  formColumnName,
+  generateTestReportForRegressedTest,
+} from './CompReadyUtils'
+import { Link } from 'react-router-dom'
 import { relativeTime } from '../helpers'
 import { Tooltip, Typography } from '@mui/material'
+import CompSeverityIcon from './CompSeverityIcon'
 import PropTypes from 'prop-types'
-import React, { Fragment } from 'react'
+import React, { Fragment, useContext } from 'react'
 
 export default function TriagedRegressionTestList(props) {
+  const { expandEnvironment } = useContext(CompReadyVarsContext)
+
   const [sortModel, setSortModel] = React.useState([
     { field: 'component', sort: 'asc' },
   ])
@@ -32,6 +40,9 @@ export default function TriagedRegressionTestList(props) {
     )
   }
 
+  const showStatus =
+    props.allRegressedTests !== undefined && props.allRegressedTests.length > 0
+
   const columns = [
     {
       field: 'test_name',
@@ -98,6 +109,51 @@ export default function TriagedRegressionTestList(props) {
         )
       },
     },
+    ...(showStatus
+      ? [
+          {
+            field: 'status',
+            headerName: 'Status',
+            valueGetter: (params) => {
+              const value = {
+                status: '',
+                explanations: '',
+                url: '',
+              }
+              const regressionId = params.row.id
+              const matchingRegression = props.allRegressedTests.find(
+                (rt) => rt.regression?.id === regressionId
+              )
+              if (matchingRegression) {
+                value.status = matchingRegression.status
+                value.explanations = matchingRegression.explanations
+                value.url = generateTestReportForRegressedTest(
+                  matchingRegression,
+                  props.filterVals,
+                  expandEnvironment
+                )
+              }
+              return value
+            },
+            renderCell: (params) => (
+              <div
+                style={{
+                  textAlign: 'center',
+                }}
+                className="status"
+              >
+                <Link to={params.value.url}>
+                  <CompSeverityIcon
+                    status={params.value.status}
+                    explanations={params.value.explanations}
+                  />
+                </Link>
+              </div>
+            ),
+            flex: 6,
+          },
+        ]
+      : []),
   ]
 
   return (
@@ -129,5 +185,7 @@ export default function TriagedRegressionTestList(props) {
 TriagedRegressionTestList.propTypes = {
   eventEmitter: PropTypes.object,
   regressions: PropTypes.array,
+  allRegressedTests: PropTypes.array,
+  filterVals: PropTypes.string,
   showOnLoad: PropTypes.bool,
 }
diff --git a/sippy-ng/src/component_readiness/TriagedRegressions.js b/sippy-ng/src/component_readiness/TriagedRegressions.js
index 474ad0683b..d8888e2307 100644
--- a/sippy-ng/src/component_readiness/TriagedRegressions.js
+++ b/sippy-ng/src/component_readiness/TriagedRegressions.js
@@ -17,7 +17,7 @@ export default function TriagedRegressions(props) {
     })
 
     if (
-      selectedTriagedEntry.regressions !== 'undefined' &&
+      selectedTriagedEntry.regressions !== null &&
       selectedTriagedEntry.regressions.length > 0
     ) {
       props.eventEmitter.emit(
diff --git a/sippy-ng/src/component_readiness/TriagedTestsPanel.js b/sippy-ng/src/component_readiness/TriagedTestsPanel.js
index 94a5f67919..678cae1f02 100644
--- a/sippy-ng/src/component_readiness/TriagedTestsPanel.js
+++ b/sippy-ng/src/component_readiness/TriagedTestsPanel.js
@@ -15,12 +15,18 @@ export default function TriagedTestsPanel(props) {
           eventEmitter={eventEmitter}
           triageEntries={props.triageEntries}
         />
-        <TriagedRegressionTestList eventEmitter={eventEmitter} />
+        <TriagedRegressionTestList
+          eventEmitter={eventEmitter}
+          allRegressedTests={props.allRegressedTests}
+          filterVals={props.filterVals}
+        />
       </Grid>
     </Fragment>
   )
 }
 
 TriagedTestsPanel.propTypes = {
-  triageEntries: PropTypes.array,
+  triageEntries: PropTypes.array.isRequired,
+  allRegressedTests: PropTypes.array,
+  filterVals: PropTypes.string,
 }


Reviews:
user: login: smg247
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-29T10:59:14Z
user: login: smg247
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-29T11:03:08Z

Review comments:
diff_hunk: @@ -741,20 +741,40 @@ export function generateTestReport(
   return sortQueryParams(retUrl)
 }
 
+// Construct a URL with all existing filters utilizing the necessary info from the regressed test.
+// We pass these arguments to the component that generates the test details report.
+export function generateTestReportForRegressedTest(
+  regressedTest,
+  filterVals,
+  expandEnvironment
+) {
+  const environmentVal = formColumnName({ variants: regressedTest.variants })
+  const safeComponentName = safeEncodeURIComponent(regressedTest.component)
+  const safeTestId = safeEncodeURIComponent(regressedTest.test_id)
+  const safeTestName = safeEncodeURIComponent(regressedTest.test_name)
+  const safeTestBasisRelease = safeEncodeURIComponent(
+    regressedTest.base_stats?.release
+  )
+  let variantsUrl = ''
+  Object.entries(regressedTest.variants).forEach(([key, value]) => {
+    variantsUrl += '&' + key + '=' + safeEncodeURIComponent(value)
+  })
+  const retUrl =
+    '/component_readiness/test_details' +
+    filterVals +
+    `&testBasisRelease=${safeTestBasisRelease}` +
+    `&testId=${safeTestId}` +
+    expandEnvironment(environmentVal) +
+    `&component=${safeComponentName}` +
+    `&capability=${regressedTest.capability}` +
+    variantsUrl +
+    `&testName=${safeTestName}`
+
+  return sortQueryParams(retUrl)
+}
+
 export function generateRegressionCount(regressed_tests, triaged_incidents) {
   let regressedCount = regressed_tests ? regressed_tests.length : 0
   let triagedCount = triaged_incidents ? triaged_incidents.length : 0
   return regressedCount + triagedCount
 }
-
-export function safeExternalHref(url) {
path: sippy-ng/src/component_readiness/CompReadyUtils.js
user: login: smg247
body: This was a half-baked idea that I forgot to remove until now
created_at: 2025-05-29T10:59:14Z
author_association: MEMBER
diff_hunk: @@ -17,7 +17,7 @@ export default function TriagedRegressions(props) {
     })
 
     if (
-      selectedTriagedEntry.regressions !== 'undefined' &&
path: sippy-ng/src/component_readiness/TriagedRegressions.js
user: login: smg247
body: this was actually throwing an error in the console that I hadn't noticed, this fixes it
created_at: 2025-05-29T11:03:07Z
author_association: MEMBER

PR comments:
user: login: openshift-ci-robot
created_at: 2025-05-28T20:12:21Z
body: @smg247: This pull request references [TRT-2094](https://issues.redhat.com//browse/TRT-2094) which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.20.0" version, but no target version was set.

<details>

In response to [this](https://github.com/openshift/sippy/pull/2649):

>This adapts functionality from the `RegressedTestsPanel` to utilize in the `TriagedRegressionTestList` as well. The adapted functionality now resides in `CompReadyUtils`.


Instructions for interacting with me using PR comments are available [here](https://prow.ci.openshift.org/command-help?repo=openshift%2Fsippy).  If you have questions or suggestions related to my behavior, please file an issue against the [openshift-eng/jira-lifecycle-plugin](https://github.com/openshift-eng/jira-lifecycle-plugin/issues/new) repository.
</details>
author_association: NONE
user: login: smg247
created_at: 2025-05-28T20:12:53Z
body: /hold for additional usages of the component that need to be addressed
author_association: MEMBER
user: login: smg247
created_at: 2025-05-28T20:36:25Z
body: /hold cancel
/label tide/merge-method-squash
author_association: MEMBER
user: login: openshift-ci-robot
created_at: 2025-05-28T20:49:36Z
body: @smg247: This pull request references [TRT-2094](https://issues.redhat.com//browse/TRT-2094) which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.20.0" version, but no target version was set.

<details>

In response to [this](https://github.com/openshift/sippy/pull/2649):

>This adapts functionality from the `RegressedTestsPanel` to utilize in the `TriagedRegressionTestList` on the modal as well. The adapted functionality now resides in `CompReadyUtils`.
>
>This is not shown on the triage details page as we don't have the necessary info available there to determine the URL. This could be enhanced in the future if desired, but I don't think it is necessary.


Instructions for interacting with me using PR comments are available [here](https://prow.ci.openshift.org/command-help?repo=openshift%2Fsippy).  If you have questions or suggestions related to my behavior, please file an issue against the [openshift-eng/jira-lifecycle-plugin](https://github.com/openshift-eng/jira-lifecycle-plugin/issues/new) repository.
</details>
author_association: NONE
user: login: dgoodwin
created_at: 2025-05-29T11:20:33Z
body: /lgtm
author_association: CONTRIBUTOR
user: login: openshift-ci[bot]
created_at: 2025-05-29T11:21:05Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/openshift/sippy/pull/2649#issuecomment-2919096872" title="LGTM">dgoodwin</a>*, *<a href="https://github.com/openshift/sippy/pull/2649#" title="Author self-approved">smg247</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=openshift%2Fsippy).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/openshift/sippy/blob/main/OWNERS)~~ [dgoodwin,smg247]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR
user: login: openshift-ci[bot]
created_at: 2025-05-29T11:32:34Z
body: @smg247: all tests **passed!**


[Full PR test history](https://prow.ci.openshift.org/pr-history?org=openshift&repo=sippy&pr=2649). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:smg247).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
author_association: CONTRIBUTOR