From bc260ebcaf4b1be9a8d9b215b16befc8e576b59f Mon Sep 17 00:00:00 2001 From: James Cocker Date: Wed, 11 Feb 2026 10:23:57 +0000 Subject: [PATCH 1/2] Shortened runLog container, added buttons to refresh and jump to top/ bottom Signed-off-by: James Cocker --- galasa-ui/messages/de.json | 7 +- galasa-ui/messages/en.json | 5 +- galasa-ui/src/actions/runsAction.ts | 11 +++ .../test-runs/test-run-details/LogTab.tsx | 81 +++++++++++++++++-- .../test-run-details/TestRunDetails.tsx | 8 +- .../test-run-details/LogTab.module.css | 2 +- 6 files changed, 100 insertions(+), 14 deletions(-) diff --git a/galasa-ui/messages/de.json b/galasa-ui/messages/de.json index df5543a4..9b443eb3 100644 --- a/galasa-ui/messages/de.json +++ b/galasa-ui/messages/de.json @@ -228,7 +228,10 @@ "filterTrace": "Trace", "downloadButton": "Laufprotokoll herunterladen", "copyPermalinkButton": "Permalink mit ausgewählten Zeilen kopieren", - "selectLinesToCreatePermalink": "Zeilen für Permalink wählen" + "selectLinesToCreatePermalink": "Zeilen für Permalink wählen", + "refreshRunLog": "Aktualisiere das Ausführungsprotokoll", + "scrollToTop": "Zum Anfang des Protokolls scrollen", + "scrollToBottom": "Zum Ende des Protokolls scrollen" }, "MethodsTab": { "title": "Methoden", @@ -389,12 +392,10 @@ "isloading": "Diagramm wird geladen...", "errorLoadingGraph": "Beim Laden des Diagramms ist ein Fehler aufgetreten.", "noTestRunsFound": "Keine Testläufe gefunden.", - "limitExceeded": { "title": "Grenzwert überschritten", "subtitle": "Ihre Abfrage hat mehr als {maxRecords} Ergebnisse zurückgegeben. Es werden die ersten {maxRecords} Datensätze angezeigt. Um dies in Zukunft zu vermeiden, schränken Sie Ihren Zeitrahmen ein oder ändern Sie Ihre Suchkriterien, um weniger Ergebnisse zu erhalten." }, - "timeFrameText": { "range": "Zeige Testläufe von {from} bis {to}" } diff --git a/galasa-ui/messages/en.json b/galasa-ui/messages/en.json index b64c83d4..3033313c 100644 --- a/galasa-ui/messages/en.json +++ b/galasa-ui/messages/en.json @@ -207,7 +207,10 @@ "filterTrace": "Trace", "downloadButton": "Download Run Log", "copyPermalinkButton": "Copy permalink with selected lines", - "selectLinesToCreatePermalink": "Select log lines for permalink" + "selectLinesToCreatePermalink": "Select log lines for permalink", + "refreshRunLog": "Refresh the Run Log", + "scrollToTop": "Scroll to top of log", + "scrollToBottom": "Scroll to bottom of log" }, "MethodsTab": { "title": "Methods", diff --git a/galasa-ui/src/actions/runsAction.ts b/galasa-ui/src/actions/runsAction.ts index d0169d24..3cf836df 100644 --- a/galasa-ui/src/actions/runsAction.ts +++ b/galasa-ui/src/actions/runsAction.ts @@ -7,6 +7,7 @@ import { ResultArchiveStoreAPIApi, TagsAPIApi } from '@/generated/galasaapi'; import { createAuthenticatedApiConfiguration } from '@/utils/api'; +import { fetchRunDetailLogs } from '@/utils/testRuns'; import { CLIENT_API_VERSION } from '@/utils/constants/common'; export const downloadArtifactFromServer = async (runId: string, artifactUrl: string) => { @@ -92,3 +93,13 @@ export const getExistingTagObjects = async () => { }; } }; + +export const fetchRunLog = async (runId: string) => { + let runLog; + try { + runLog = await fetchRunDetailLogs(runId); + } catch (error: any) { + runLog = "Error fetching run log: " + error; + } + return runLog; +}; diff --git a/galasa-ui/src/components/test-runs/test-run-details/LogTab.tsx b/galasa-ui/src/components/test-runs/test-run-details/LogTab.tsx index 0739c212..1887aaa6 100644 --- a/galasa-ui/src/components/test-runs/test-run-details/LogTab.tsx +++ b/galasa-ui/src/components/test-runs/test-run-details/LogTab.tsx @@ -17,9 +17,13 @@ import { Term, LetterAa, Copy, + Renew, + ArrowUp, + ArrowDown, } from '@carbon/icons-react'; import { handleDownload } from '@/utils/artifacts'; import { useTranslations } from 'next-intl'; +import { fetchRunLog } from '@/actions/runsAction'; interface LogLine { content: string; @@ -43,6 +47,7 @@ enum RegexFlags { interface LogTabProps { logs: string; initialLine?: number; + runId: string; } interface selectedRange { @@ -55,10 +60,11 @@ interface selectedRange { const SELECTION_CHANGE_EVENT = 'selectionchange'; const HASH_CHANGE_EVENT = 'hashchange'; -export default function LogTab({ logs, initialLine }: LogTabProps) { +export default function LogTab({ logs, initialLine, runId }: LogTabProps) { const translations = useTranslations('LogTab'); const [logContent, setLogContent] = useState(''); + const [isRefreshing, setIsRefreshing] = useState(false); const [processedLines, setProcessedLines] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(''); @@ -84,6 +90,7 @@ export default function LogTab({ logs, initialLine }: LogTabProps) { ); const logContainerRef = useRef(null); + const scrollContainerRef = useRef(null); const debounceTimeoutRef = useRef(null); const DEBOUNCE_DELAY_MILLISECONDS = 300; @@ -162,6 +169,48 @@ export default function LogTab({ logs, initialLine }: LogTabProps) { } }; + const handleRefreshLog = async () => { + setIsRefreshing(true); + + try { + // Fetch fresh log from the server + const newRunLog = await fetchRunLog(runId); + + setLogContent(newRunLog); + + // Reset search and filters + setSearchTerm(''); + setDebouncedSearchTerm(''); + setCurrentMatchIndex(-1); + setTotalMatches(0); + setSearchCache(new Map()); + } catch (error) { + console.error('Error refreshing logs:', error); + // Fallback to existing logs if fetch fails + setLogContent(logs); + } finally { + setIsRefreshing(false); + } + }; + + const scrollToTop = () => { + if (scrollContainerRef.current) { + scrollContainerRef.current.scrollTo({ + top: 0, + behavior: 'smooth', + }); + } + }; + + const scrollToBottom = () => { + if (scrollContainerRef.current) { + scrollContainerRef.current.scrollTo({ + top: scrollContainerRef.current.scrollHeight, + behavior: 'smooth', + }); + } + }; + // Memoized regex creation to avoid recreating the same regex repeatedly const searchRegex = useMemo(() => { let regex: RegExp | null = null; @@ -587,9 +636,9 @@ export default function LogTab({ logs, initialLine }: LogTabProps) { {totalMatches > 0 ? translations('matchCounter', { - current: currentMatchIndex + 1, - total: totalMatches, - }) + current: currentMatchIndex + 1, + total: totalMatches, + }) : translations('noMatches')}