diff --git a/client/src/app/components/pipeline/test-results/pipeline-test-results.component.html b/client/src/app/components/pipeline/test-results/pipeline-test-results.component.html index 7cc55094e..61af0e419 100644 --- a/client/src/app/components/pipeline/test-results/pipeline-test-results.component.html +++ b/client/src/app/components/pipeline/test-results/pipeline-test-results.component.html @@ -200,6 +200,12 @@

Test Results

> } + @if ( + (testCase.combinedFailureRate && testCase.combinedFailureRate > 0.5) || + (testCase.defaultBranchFailureRate && testCase.defaultBranchFailureRate > 0.5) + ) { + + } } @@ -264,6 +270,17 @@

Stack Trace

{{ selectedTestCase()?.stackTrace?.trimStart() }}
+
+ +
} @@ -294,6 +311,17 @@

Test Case Logs

} } +
+ +
} @@ -307,6 +335,17 @@

Test Suite Logs

} } +
+ +
} @@ -320,7 +359,7 @@

Test Suite Logs

- The flakiness score shows how unpredictable a test is, ranging from 0 (not flaky) to 100 (highly flaky). It’s based on failure rates from the default + The flakiness score shows how unpredictable a test is, ranging from 0 (not flaky) to 100 (highly flaky). It's based on failure rates from the default branch and all branches combined.

@@ -331,7 +370,7 @@

Test Suite Logs

Flakiness per Branch:
    If the failure rate is between 0% and 50%, we calculate:
    flakiness = (50% - failure rate) / 50%
-     If it’s 0% or over 50%, flakiness is 0. +     If it's 0% or over 50%, flakiness is 0.
  • Weighted Score:
    diff --git a/client/src/app/components/pipeline/test-results/pipeline-test-results.component.ts b/client/src/app/components/pipeline/test-results/pipeline-test-results.component.ts index 44e033634..4054a80d4 100644 --- a/client/src/app/components/pipeline/test-results/pipeline-test-results.component.ts +++ b/client/src/app/components/pipeline/test-results/pipeline-test-results.component.ts @@ -21,6 +21,9 @@ import { SliderModule } from 'primeng/slider'; import { provideTablerIcons, TablerIconComponent } from 'angular-tabler-icons'; import { IconCheck, + IconCircleX, + IconCircleChevronsRight, + IconDownload, IconChevronDown, IconChevronsRight, IconChevronUp, @@ -43,7 +46,7 @@ interface LogLevel { } const LOG_LEVELS: LogLevel[] = [ - { value: 0, name: 'OFF', label: 'OFF', color: 'text-gray-900', includes: ['OFF'] }, + { value: 0, name: 'OFF', label: 'OFF', color: '', includes: ['OFF'] }, { value: 1, name: 'FATAL', label: 'FATAL+', color: 'text-red-800', includes: ['FATAL', 'OFF'] }, { value: 2, name: 'ERROR', label: 'ERROR+', color: 'text-red-600', includes: ['ERROR', 'FATAL', 'OFF'] }, { value: 3, name: 'WARN', label: 'WARN+', color: 'text-amber-600', includes: ['WARN', 'ERROR', 'FATAL', 'OFF'] }, @@ -78,6 +81,9 @@ const LOG_LEVELS: LogLevel[] = [ provideTablerIcons({ IconProgress, IconCheck, + IconDownload, + IconCircleX, + IconCircleChevronsRight, IconX, IconChevronsRight, IconClock, @@ -104,11 +110,11 @@ export class PipelineTestResultsComponent { selectedTestCase = signal<(TestCaseDto & { suiteSystemOut: string | undefined }) | null>(null); // Log level filtering - selectedLogLevelValue = signal(7); // Default to ALL + selectedLogLevelValue = signal(2); // Default to ERROR // Get the current log level object based on the selected value selectedLogLevel = computed(() => { - return LOG_LEVELS.find(level => level.value === this.selectedLogLevelValue()) || LOG_LEVELS[7]; + return LOG_LEVELS.find(level => level.value === this.selectedLogLevelValue()) || LOG_LEVELS[2]; }); // Get the array of log level names that should be included based on the selection @@ -151,6 +157,27 @@ export class PipelineTestResultsComponent { return this.filterLogsByLevel(this.selectedTestCase()?.suiteSystemOut); }); + // Function to download logs with current filter applied + downloadLogs(logContent: string, fileName: string): void { + if (!logContent) return; + + const blob = new Blob([logContent], { type: 'text/plain' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + + // Create the download link + link.href = url; + link.download = fileName; + document.body.appendChild(link); + + // Trigger the download + link.click(); + + // Clean up + document.body.removeChild(link); + URL.revokeObjectURL(url); + } + // Helper function for log level styling (colors) getLogLevelClass(line: string): string { // Use the same regex pattern as in filterLogsByLevel