From 87abb145d2779d545cef46e31219709975b6a99c Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Thu, 21 Sep 2023 15:06:28 -0500 Subject: [PATCH] Update `Timeline for All Versions` table on Test Plan Versions Page (#773) * Show all versions in 'Timeline for All Versions' table * Update timestamp migrations to use more real world change phase change times (avoid exact time being set for when one phase ends and another begins) * Update import script to have a 'delayed' time on the report being deprecated * Add td -> th for first column cell for Version Summary and Timeline related tables * Match sort order described in #719 * Correct deprecation date (#780) * Compare most recent test plan version to deprecate against * Correct deprecation date without using the more recent test plan version since it's being set already --------- Co-authored-by: Erika Miguel --- .../components/TestPlanVersionsPage/index.jsx | 85 +++++++++++++++++-- ...PhaseReachedAtColumnsForTestPlanVersion.js | 12 ++- ...gDeprecatedAtAndReorderPhaseChangeDates.js | 40 +++++---- server/scripts/import-tests/index.js | 12 ++- 4 files changed, 118 insertions(+), 31 deletions(-) diff --git a/client/components/TestPlanVersionsPage/index.jsx b/client/components/TestPlanVersionsPage/index.jsx index 405ac9d0f..24c3d5d37 100644 --- a/client/components/TestPlanVersionsPage/index.jsx +++ b/client/components/TestPlanVersionsPage/index.jsx @@ -188,6 +188,69 @@ const TestPlanVersionsPage = () => { return new Date(b.updatedAt) - new Date(a.updatedAt); }); + const timelineForAllVersions = []; + + testPlanVersions.forEach(testPlanVersion => { + const event = { + id: testPlanVersion.id, + updatedAt: testPlanVersion.updatedAt + }; + timelineForAllVersions.push({ ...event, phase: 'RD' }); + + if (testPlanVersion.draftPhaseReachedAt) + timelineForAllVersions.push({ + ...event, + phase: 'DRAFT', + draftPhaseReachedAt: testPlanVersion.draftPhaseReachedAt + }); + if (testPlanVersion.candidatePhaseReachedAt) + timelineForAllVersions.push({ + ...event, + phase: 'CANDIDATE', + candidatePhaseReachedAt: testPlanVersion.candidatePhaseReachedAt + }); + if (testPlanVersion.recommendedPhaseReachedAt) + timelineForAllVersions.push({ + ...event, + phase: 'RECOMMENDED', + recommendedPhaseReachedAt: + testPlanVersion.recommendedPhaseReachedAt + }); + if (testPlanVersion.deprecatedAt) + timelineForAllVersions.push({ + ...event, + phase: 'DEPRECATED', + deprecatedAt: testPlanVersion.deprecatedAt + }); + }); + + const phaseOrder = { + RD: 0, + DRAFT: 1, + CANDIDATE: 2, + RECOMMENDED: 3, + DEPRECATED: 4 + }; + + timelineForAllVersions.sort((a, b) => { + const dateA = + a.recommendedPhaseReachedAt || + a.candidatePhaseReachedAt || + a.draftPhaseReachedAt || + a.deprecatedAt || + a.updatedAt; + const dateB = + b.recommendedPhaseReachedAt || + b.candidatePhaseReachedAt || + b.draftPhaseReachedAt || + b.deprecatedAt || + b.updatedAt; + + // If dates are the same, compare phases + if (dateA === dateB) return phaseOrder[a.phase] - phaseOrder[b.phase]; + return new Date(dateA) - new Date(dateB); + }); + const issues = uniqueBy( testPlanVersions.flatMap(testPlanVersion => testPlanVersion.testPlanReports.flatMap(testPlanReport => @@ -198,7 +261,11 @@ const TestPlanVersionsPage = () => { ) ), item => item.link - ); + ).sort((a, b) => { + const aCreatedAt = new Date(a.createdAt); + const bCreatedAt = new Date(b.createdAt); + return bCreatedAt - aCreatedAt; + }); return ( @@ -240,7 +307,7 @@ const TestPlanVersionsPage = () => { {testPlanVersions.map(testPlanVersion => ( - + { )} autoWidth={false} /> - + {(() => { // Gets the derived phase even if deprecated by checking @@ -376,7 +443,7 @@ const TestPlanVersionsPage = () => { - {testPlanVersions.map(testPlanVersion => { + {timelineForAllVersions.map(testPlanVersion => { const versionString = ( { const eventBody = getEventBody(testPlanVersion.phase); return ( - - {getEventDate(testPlanVersion)} + + {getEventDate(testPlanVersion)} {versionString} {eventBody} @@ -541,12 +610,12 @@ const TestPlanVersionsPage = () => { return events.map(([phase, date]) => ( - + {convertDateToString( date, 'MMM D, YYYY' )} - + {getEventBody(phase)} )); diff --git a/server/migrations/20230626203205-updatePhaseAndDraftPhaseReachedAtColumnsForTestPlanVersion.js b/server/migrations/20230626203205-updatePhaseAndDraftPhaseReachedAtColumnsForTestPlanVersion.js index 8ed875cc0..1de8cbf57 100644 --- a/server/migrations/20230626203205-updatePhaseAndDraftPhaseReachedAtColumnsForTestPlanVersion.js +++ b/server/migrations/20230626203205-updatePhaseAndDraftPhaseReachedAtColumnsForTestPlanVersion.js @@ -37,15 +37,21 @@ module.exports = { for (const testPlanVersion of testPlanVersions) { const { id, updatedAt, hasTestPlanReport } = testPlanVersion; - if (hasTestPlanReport) + if (hasTestPlanReport) { + const draftPhaseReachedAt = new Date(updatedAt); + draftPhaseReachedAt.setSeconds( + // Set draftPhaseReachedAt to happen 60 seconds after updatedAt for general + // 'correctness' and to help with any app sorts + draftPhaseReachedAt.getSeconds() + 60 + ); await queryInterface.sequelize.query( `UPDATE "TestPlanVersion" SET "draftPhaseReachedAt" = ? WHERE id = ?`, { - replacements: [updatedAt, id], + replacements: [draftPhaseReachedAt, id], transaction } ); - else + } else await queryInterface.sequelize.query( `UPDATE "TestPlanVersion" SET phase = ? WHERE id = ?`, { diff --git a/server/migrations/20230830225248-addMissingDeprecatedAtAndReorderPhaseChangeDates.js b/server/migrations/20230830225248-addMissingDeprecatedAtAndReorderPhaseChangeDates.js index c05f49607..77d36372a 100644 --- a/server/migrations/20230830225248-addMissingDeprecatedAtAndReorderPhaseChangeDates.js +++ b/server/migrations/20230830225248-addMissingDeprecatedAtAndReorderPhaseChangeDates.js @@ -12,7 +12,7 @@ module.exports = { // 1178 (Radio Group Example Using aria-activedescendant) TestPlanVersions const testPlanVersionsToSetToCandidate = await queryInterface.sequelize.query( - `select "TestPlanVersion".id, phase, "markedFinalAt" + `select "TestPlanVersion".id, phase, "draftPhaseReachedAt", "markedFinalAt" from "TestPlanVersion" join "TestPlanReport" on "TestPlanVersion".id = "TestPlanReport"."testPlanVersionId" where "markedFinalAt" is not null @@ -23,15 +23,23 @@ module.exports = { } ); - const candidatePhaseReachedAt = new Date(); - const recommendedPhaseTargetDate = new Date( - candidatePhaseReachedAt - ); - recommendedPhaseTargetDate.setDate( - candidatePhaseReachedAt.getDate() + 180 - ); - for (const testPlanVersion of testPlanVersionsToSetToCandidate) { + const candidatePhaseReachedAt = new Date( + testPlanVersion.draftPhaseReachedAt + ); + + // Set candidatePhaseReachedAt to draftPhaseReachedAt date (+1 day) + candidatePhaseReachedAt.setDate( + candidatePhaseReachedAt.getDate() + 1 + ); + + const recommendedPhaseTargetDate = new Date( + candidatePhaseReachedAt + ); + recommendedPhaseTargetDate.setDate( + candidatePhaseReachedAt.getDate() + 180 + ); + await queryInterface.sequelize.query( `update "TestPlanVersion" set "candidatePhaseReachedAt" = ?, @@ -97,7 +105,7 @@ module.exports = { testPlanVersion.candidatePhaseReachedAt ); - // Update candidatePhaseReachedAt to be the draftPhaseReachedAt date (+1) + // Update candidatePhaseReachedAt to be the draftPhaseReachedAt date (+1 day) // (because that phase happening before shouldn't be possible) if (candidatePhaseReachedAt < draftPhaseReachedAt) { const newCandidatePhaseReachedAt = new Date( @@ -126,6 +134,9 @@ module.exports = { } if (testPlanVersion.deprecatedAt) { + const deprecatedAt = new Date(testPlanVersion.deprecatedAt); + deprecatedAt.setSeconds(deprecatedAt.getSeconds() - 1); + // Add deprecatedAt for applicable testPlanVersions await queryInterface.sequelize.query( `update "TestPlanVersion" @@ -133,14 +144,7 @@ module.exports = { phase = 'DEPRECATED' where id = ?`, { - replacements: [ - testPlanVersion.recommendedPhaseReachedAt - ? testPlanVersion.recommendedPhaseReachedAt - : testPlanVersion.candidatePhaseReachedAt - ? testPlanVersion.candidatePhaseReachedAt - : testPlanVersion.deprecatedAt, - testPlanVersion.id - ], + replacements: [deprecatedAt, testPlanVersion.id], transaction } ); diff --git a/server/scripts/import-tests/index.js b/server/scripts/import-tests/index.js index d34b55818..b36f9e7b7 100644 --- a/server/scripts/import-tests/index.js +++ b/server/scripts/import-tests/index.js @@ -157,11 +157,19 @@ const importTestPlanVersions = async () => { }); if (testPlanVersionsToDeprecate.length) { for (const testPlanVersionToDeprecate of testPlanVersionsToDeprecate) { - if (new Date(testPlanVersionToDeprecate.updatedAt) < updatedAt) + if ( + new Date(testPlanVersionToDeprecate.updatedAt) < updatedAt + ) { + // Set the deprecatedAt time to a couple seconds less than the updatedAt date. + // Deprecations happen slightly before update during normal app operations. + // This is to maintain correctness and any app sorts issues + const deprecatedAt = new Date(updatedAt); + deprecatedAt.setSeconds(deprecatedAt.getSeconds() - 60); await updateTestPlanVersion(testPlanVersionToDeprecate.id, { phase: 'DEPRECATED', - deprecatedAt: updatedAt + deprecatedAt }); + } } }