From 2615f49642f6b100f63db671d5ac247530d89a0f Mon Sep 17 00:00:00 2001 From: MarkS Date: Fri, 5 Dec 2025 16:33:24 -0700 Subject: [PATCH] Draft jobs table: Show caution for older data --- .github/copilot-instructions.md | 4 +++ .../draft-jobs.component.html | 7 +++++ .../draft-jobs.component.spec.ts | 29 +++++++++++++++++++ .../draft-jobs.component.ts | 13 +++++++++ 4 files changed, 53 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 91c5975fc8..4a9c7f9910 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -112,6 +112,10 @@ This repository contains three interconnected applications: - Pay attention to available types and type guards in src/SIL.XForge.Scripture/ClientApp/src/type-utils.ts. +# Tests + +- Place a line with "// SUT" before the line that causes the code being tested to be exercised. + # Running commands - If you run frontend tests, run them in the `src/SIL.XForge.Scripture/ClientApp` directory with a command such as `npm run test:headless -- --watch=false --include '**/text.component.spec.ts' --include '**/settings.component.spec.ts'` diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.html b/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.html index df01a2d9d7..7a60def055 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.html +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.html @@ -48,6 +48,13 @@ + @if (shouldShowDurationComparisonCaution) { + + Be careful about comparing Serval build outcomes and durations before 2025-12-18 with those after, because of + changes to the event grouping system. + + } + @if (!isLoading) { @if (currentProjectFilter) { diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.spec.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.spec.ts index 3144953edf..8a9960edb9 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.spec.ts +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.spec.ts @@ -650,6 +650,35 @@ describe('DraftJobsComponent', () => { })); }); + describe('shouldShowDurationComparisonCaution', () => { + it('should return false if no date range is set', fakeAsync(() => { + const env = new TestEnvironment({ hasEvents: false }); + env.wait(); + env.component['currentDateRange'] = undefined; + + // SUT + expect(env.component.shouldShowDurationComparisonCaution).toBeFalse(); + })); + + it('should return true when the date range starts before the cutoff', fakeAsync(() => { + const env = new TestEnvironment({ hasEvents: false }); + env.wait(); + env.setDateRange(new Date(2025, 12 - 1, 1), new Date(2026, 1 - 1, 20)); + + // SUT + expect(env.component.shouldShowDurationComparisonCaution).toBeTrue(); + })); + + it('should return false when the date range starts on or after the cutoff', fakeAsync(() => { + const env = new TestEnvironment({ hasEvents: false }); + env.wait(); + env.setDateRange(new Date(2026, 1 - 1, 20), new Date(2026, 2 - 1, 20)); + + // SUT + expect(env.component.shouldShowDurationComparisonCaution).toBeFalse(); + })); + }); + describe('formatDurationInHours', () => { it('should format duration in decimal hours', fakeAsync(() => { const env = new TestEnvironment({ hasEvents: false }); diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.ts index 05a6bfc256..5a95b5f581 100644 --- a/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.ts +++ b/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.ts @@ -210,6 +210,19 @@ export class DraftJobsComponent extends DataLoadingComponent implements OnInit { return this.formatDurationInHours(this.maxDuration); } + /** Whether to show a caution notice about comparing older durations. */ + get shouldShowDurationComparisonCaution(): boolean { + if (this.currentDateRange == null) return false; + + // Day after the date when draftGenerationRequestId began to be used, which was in SFv5.46.1 at + // 2025-12-18T17:48:57Z. + const requestIdIntroductionDate: Date = new Date(2025, 12 - 1, 18 + 1); + + const rangeStartMs: number = this.currentDateRange.start.getTime(); + const cutoffMs: number = requestIdIntroductionDate.getTime(); + return rangeStartMs < cutoffMs; + } + ngOnInit(): void { if ( !this.columnsToDisplay.includes('buildDetails') &&