diff --git a/dashboard/src/components/TestDetails/TestDetails.tsx b/dashboard/src/components/TestDetails/TestDetails.tsx
index f2a7089d0..5bcacb689 100644
--- a/dashboard/src/components/TestDetails/TestDetails.tsx
+++ b/dashboard/src/components/TestDetails/TestDetails.tsx
@@ -39,6 +39,9 @@ import { StatusIcon } from '@/components/Icons/StatusIcons';
import PageWithTitle from '@/components/PageWithTitle';
import { getTitle } from '@/utils/utils';
+import { getTestHardware } from '@/lib/test';
+
+import { MemoizedTestDetailsOGTags } from '@/components/OpenGraphTags/TestDetailsOGTags';
const LinkItem = ({ children, ...props }: LinkProps): JSX.Element => {
return (
@@ -55,27 +58,6 @@ const LinkItem = ({ children, ...props }: LinkProps): JSX.Element => {
const MemoizedLinkItem = memo(LinkItem);
-const getTestHardware = ({
- misc,
- compatibles,
- defaultValue,
-}: {
- misc?: Record
;
- compatibles?: string[];
- defaultValue?: string;
-}): string => {
- const platform = misc?.['platform'];
- if (typeof platform === 'string' && platform !== '') {
- return platform;
- }
-
- if (compatibles && compatibles.length > 0) {
- return compatibles[0];
- }
-
- return defaultValue ?? '-';
-};
-
const TestDetailsSections = ({
test,
setSheetType,
@@ -336,13 +318,16 @@ const TestDetails = ({ breadcrumb }: TestsDetailsProps): JSX.Element => {
const [sheetType, setSheetType] = useState('log');
const [jsonContent, setJsonContent] = useState();
+ const testDetailsTabTitle: string = useMemo(() => {
+ return formatMessage(
+ { id: 'title.testDetails' },
+ { testName: getTitle(data?.path, isLoading) },
+ );
+ }, [data?.path, formatMessage, isLoading]);
+
return (
-
+
+
{
+ const result: string[] = [];
+ if (culprit_code) {
+ result.push(formatMessage({ id: 'issueDetails.culpritCode' }));
+ }
+ if (culprit_harness) {
+ result.push(formatMessage({ id: 'issueDetails.culpritHarness' }));
+ }
+ if (culprit_tool) {
+ result.push(formatMessage({ id: 'issueDetails.culpritTool' }));
+ }
+
+ return valueOrEmpty(result.join(', '));
+};
diff --git a/dashboard/src/lib/test.ts b/dashboard/src/lib/test.ts
new file mode 100644
index 000000000..7ec7655b4
--- /dev/null
+++ b/dashboard/src/lib/test.ts
@@ -0,0 +1,20 @@
+export const getTestHardware = ({
+ misc,
+ compatibles,
+ defaultValue,
+}: {
+ misc?: Record;
+ compatibles?: string[];
+ defaultValue?: string;
+}): string => {
+ const platform = misc?.['platform'];
+ if (typeof platform === 'string' && platform !== '') {
+ return platform;
+ }
+
+ if (compatibles && compatibles.length > 0) {
+ return compatibles[0];
+ }
+
+ return defaultValue ?? '-';
+};
diff --git a/dashboard/src/locales/messages/index.ts b/dashboard/src/locales/messages/index.ts
index fd7391e27..f22b2d4b6 100644
--- a/dashboard/src/locales/messages/index.ts
+++ b/dashboard/src/locales/messages/index.ts
@@ -201,6 +201,8 @@ export const messages = {
'hardwareDetails.platforms': 'Platforms',
'hardwareDetails.timeFrame':
'Results from {startDate} and {startTime} to {endDate} {endTime}',
+ 'hardwareListing.description': 'List of hardware from kernel tests',
+ 'hardwareListing.title': 'Hardware Listing ― KCI Dashboard',
'issue.alsoPresentTooltip': 'Issue also present in {tree}',
'issue.firstSeen': 'First seen',
'issue.newIssue': 'New issue: This is the first time this issue was seen',
@@ -224,6 +226,8 @@ export const messages = {
'issueDetails.reportSubject': 'Report Subject',
'issueDetails.reportUrl': 'Report URL',
'issueDetails.version': 'Version',
+ 'issueListing.description': 'List of issues from builds and tests',
+ 'issueListing.title': 'Issue Listing ― KCI Dashboard',
'issueListing.treeBranchTooltip':
'The tree name and git repository branch of the first incident\nClick a cell to see details of that checkout',
'jsonSheet.title': 'JSON Viewer',
@@ -250,6 +254,10 @@ export const messages = {
'table.itemsPerPage': 'Items per page:',
'table.of': 'of',
'table.showing': 'Showing:',
+ 'tag.failCount': '{count} Fail',
+ 'tag.inconclusiveCount': '{count} Inconclusive',
+ 'tag.noBuildsOrTestsData': 'No builds or tests data.',
+ 'tag.passCount': '{count} Pass',
'test.details': 'Test Details',
'test.statusTooltip':
'Success - tests with PASS status{br}' +
@@ -294,6 +302,8 @@ export const messages = {
'treeDetails.testsInconclusive': 'Inconclusive tests',
'treeDetails.testsSuccess': 'Success tests',
'treeDetails.validBuilds': 'Success builds',
+ 'treeListing.description': 'List of trees for kernel builds and tests',
+ 'treeListing.title': 'Tree Listing ― KCI Dashboard',
},
};
diff --git a/dashboard/src/pages/Hardware/Hardware.tsx b/dashboard/src/pages/Hardware/Hardware.tsx
index 6c2a5926e..e61e419df 100644
--- a/dashboard/src/pages/Hardware/Hardware.tsx
+++ b/dashboard/src/pages/Hardware/Hardware.tsx
@@ -8,6 +8,7 @@ import { useNavigate, useSearch } from '@tanstack/react-router';
import HardwareListingPage from '@/pages/Hardware/HardwareListingPage';
import DebounceInput from '@/components/DebounceInput/DebounceInput';
+import { MemoizedListingOGTags } from '@/components/OpenGraphTags/ListingOGTags';
const Hardware = (): JSX.Element => {
const { hardwareSearch } = useSearch({
@@ -29,10 +30,11 @@ const Hardware = (): JSX.Element => {
[navigate],
);
- const intl = useIntl();
+ const { formatMessage } = useIntl();
return (
<>
+
{
className="w-2/3"
type="text"
startingValue={hardwareSearch}
- placeholder={intl.formatMessage({
+ placeholder={formatMessage({
id: 'hardware.searchPlaceholder',
})}
/>
diff --git a/dashboard/src/pages/IssueListing/IssueListing.tsx b/dashboard/src/pages/IssueListing/IssueListing.tsx
index f66315444..1cf33e88e 100644
--- a/dashboard/src/pages/IssueListing/IssueListing.tsx
+++ b/dashboard/src/pages/IssueListing/IssueListing.tsx
@@ -9,6 +9,8 @@ import { z } from 'zod';
import DebounceInput from '@/components/DebounceInput/DebounceInput';
+import { MemoizedListingOGTags } from '@/components/OpenGraphTags/ListingOGTags';
+
import { IssueListingPage } from './IssueListingPage';
const IssueListing = (): JSX.Element => {
@@ -36,6 +38,7 @@ const IssueListing = (): JSX.Element => {
return (
<>
+
{
- const { valid, invalid } = data?.summary.builds.status ?? {};
+ const [buildStatusCount, bootStatusCount, testStatusCount]: [
+ GroupedStatus,
+ GroupedStatus,
+ GroupedStatus,
+ ] = useMemo(() => {
+ const { status: buildStatusSummary } = data?.summary.builds ?? {};
const { status: testStatusSummary } = data?.summary.tests ?? {};
-
const { status: bootStatusSummary } = data?.summary.boots ?? {};
+ const buildCount = groupStatus({
+ passCount: buildStatusSummary?.valid,
+ failCount: buildStatusSummary?.invalid,
+ nullCount: buildStatusSummary?.null,
+ });
+
+ const bootCount = groupStatus({
+ passCount: bootStatusSummary?.PASS,
+ failCount: bootStatusSummary?.FAIL,
+ doneCount: bootStatusSummary?.DONE,
+ errorCount: bootStatusSummary?.ERROR,
+ missCount: bootStatusSummary?.MISS,
+ skipCount: bootStatusSummary?.SKIP,
+ nullCount: bootStatusSummary?.NULL,
+ });
+
+ const testCount = groupStatus({
+ passCount: testStatusSummary?.PASS,
+ failCount: testStatusSummary?.FAIL,
+ doneCount: testStatusSummary?.DONE,
+ errorCount: testStatusSummary?.ERROR,
+ missCount: testStatusSummary?.MISS,
+ skipCount: testStatusSummary?.SKIP,
+ nullCount: testStatusSummary?.NULL,
+ });
+
+ return [buildCount, bootCount, testCount];
+ }, [data?.summary.boots, data?.summary.builds, data?.summary.tests]);
+
+ const tabsCounts: TreeDetailsTabRightElement = useMemo(() => {
return {
- 'global.tests': testStatusSummary ? (
+ 'global.tests': (
- ) : (
- <>>
),
- 'global.boots': bootStatusSummary ? (
+ 'global.boots': (
- ) : (
- <>>
),
- 'global.builds': data ? (
-
- ) : (
- <>>
),
};
- }, [data]);
+ }, [bootStatusCount, buildStatusCount, testStatusCount]);
+
+ const treeDetailsTitle = formatMessage(
+ { id: 'title.treeDetails' },
+ {
+ treeName: getTreeName(treeId, treeInfo.treeName, treeInfo.gitBranch),
+ },
+ );
return (
-
+
+
{
const { treeSearch: unsafeTreeSearch } = useSearch({
@@ -32,10 +33,11 @@ const Trees = (): JSX.Element => {
[navigate],
);
- const intl = useIntl();
+ const { formatMessage } = useIntl();
return (
<>
+
{
className="w-2/3"
type="text"
startingValue={treeSearch}
- placeholder={intl.formatMessage({ id: 'tree.searchPlaceholder' })}
+ placeholder={formatMessage({ id: 'tree.searchPlaceholder' })}
/>
diff --git a/dashboard/src/pages/hardwareDetails/HardwareDetails.tsx b/dashboard/src/pages/hardwareDetails/HardwareDetails.tsx
index 787977c6b..ce54dd217 100644
--- a/dashboard/src/pages/hardwareDetails/HardwareDetails.tsx
+++ b/dashboard/src/pages/hardwareDetails/HardwareDetails.tsx
@@ -29,10 +29,7 @@ import type {
import MemoizedCompatibleHardware from '@/components/Cards/CompatibleHardware';
-import {
- GroupedTestStatus,
- BuildStatus as BuildStatusComponent,
-} from '@/components/Status/Status';
+import { GroupedTestStatus } from '@/components/Status/Status';
import { mapFilterToReq } from '@/components/Tabs/Filters';
@@ -52,10 +49,13 @@ import { useHardwareDetailsLazyLoadQuery } from '@/hooks/useHardwareDetailsLazyL
import { useQueryInconsistencyInvalidator } from '@/hooks/useQueryInconsistencyInvalidator';
-import { statusCountToRequiredStatusCount } from '@/utils/status';
+import type { GroupedStatus } from '@/utils/status';
+import { groupStatus, statusCountToRequiredStatusCount } from '@/utils/status';
import PageWithTitle from '@/components/PageWithTitle';
+import { MemoizedTreeHardwareDetailsOGTags } from '@/components/OpenGraphTags/TreeHardwareDetailsOGTags';
+
import { HardwareHeader } from './HardwareDetailsHeaderTable';
import type { TreeDetailsTabRightElement } from './Tabs/HardwareDetailsTabs';
import HardwareDetailsTabs from './Tabs/HardwareDetailsTabs';
@@ -240,7 +240,11 @@ function HardwareDetails(): JSX.Element {
const startDate = getFormattedDate(startTimestampInSeconds);
const endDate = getFormattedDate(endTimestampInSeconds);
- const tabsCounts: TreeDetailsTabRightElement = useMemo(() => {
+ const [buildStatusCount, bootStatusCount, testStatusCount]: [
+ GroupedStatus,
+ GroupedStatus,
+ GroupedStatus,
+ ] = useMemo(() => {
const { status: buildStatusSummary } =
summaryResponse.data?.summary.builds ?? {};
const { status: testStatusSummary } =
@@ -248,40 +252,63 @@ function HardwareDetails(): JSX.Element {
const { status: bootStatusSummary } =
summaryResponse.data?.summary.boots ?? {};
+ const buildCount = groupStatus({
+ passCount: buildStatusSummary?.valid,
+ failCount: buildStatusSummary?.invalid,
+ nullCount: buildStatusSummary?.null,
+ });
+
+ const bootCount = groupStatus({
+ passCount: bootStatusSummary?.PASS,
+ failCount: bootStatusSummary?.FAIL,
+ doneCount: bootStatusSummary?.DONE,
+ errorCount: bootStatusSummary?.ERROR,
+ missCount: bootStatusSummary?.MISS,
+ skipCount: bootStatusSummary?.SKIP,
+ nullCount: bootStatusSummary?.NULL,
+ });
+
+ const testCount = groupStatus({
+ passCount: testStatusSummary?.PASS,
+ failCount: testStatusSummary?.FAIL,
+ doneCount: testStatusSummary?.DONE,
+ errorCount: testStatusSummary?.ERROR,
+ missCount: testStatusSummary?.MISS,
+ skipCount: testStatusSummary?.SKIP,
+ nullCount: testStatusSummary?.NULL,
+ });
+
+ return [buildCount, bootCount, testCount];
+ }, [
+ summaryResponse.data?.summary.boots,
+ summaryResponse.data?.summary.builds,
+ summaryResponse.data?.summary.tests,
+ ]);
+
+ const tabsCounts: TreeDetailsTabRightElement = useMemo(() => {
return {
- 'global.tests': testStatusSummary ? (
+ 'global.tests': (
- ) : (
- <>>
),
- 'global.boots': bootStatusSummary ? (
+
+ 'global.boots': (
- ) : (
- <>>
),
- 'global.builds': buildStatusSummary ? (
-
- ) : (
- <>>
),
};
- }, [
- summaryResponse.data?.summary.boots,
- summaryResponse.data?.summary.builds,
- summaryResponse.data?.summary.tests,
- ]);
+ }, [bootStatusCount, buildStatusCount, testStatusCount]);
const treeData = useMemo(
() =>
@@ -301,13 +328,21 @@ function HardwareDetails(): JSX.Element {
],
);
+ const hardwareTitle = useMemo(() => {
+ return formatMessage(
+ { id: 'title.hardwareDetails' },
+ { hardwareName: hardwareId },
+ );
+ }, [formatMessage, hardwareId]);
+
return (
-
+
+