From 0d6b17a5f5aa4187e85fd0ac56e971cfafb70b77 Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Thu, 19 Mar 2026 13:14:55 +0100 Subject: [PATCH 1/7] feat: separate toolbar rendering from table data rendering on the RunsPerLhcPeriodOverview page --- .../RunsPerLhcPeriodOverviewPage.js | 56 ++++++++++--------- .../runs/runsPerLhcPeriod.overview.test.js | 6 +- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js index 7526324b35..77da8a6eee 100644 --- a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js +++ b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js @@ -95,30 +95,34 @@ export const RunsPerLhcPeriodOverviewPage = ({ runs: { perLhcPeriodOverviewModel { sort: sortModel }, ); - return h( - '.intermediate-flex-column', - mergeRemoteData([remoteLhcPeriodStatistics, remoteRuns]).match({ - NotAsked: () => null, - Failure: (errors) => errorAlert(errors), - Loading: () => spinner(), - Success: ([lhcPeriodStatistics]) => { - const activeColumns = { - ...runsActiveColumns, - ...getInelasticInteractionRateColumns(pdpBeamTypes), + const activeColumns = { + ...runsActiveColumns, + ...getInelasticInteractionRateColumns(pdpBeamTypes), + }; - }; + const lhcPeriodName = remoteLhcPeriodStatistics?.match({ + Success: (lhcPeriodStatistics) => lhcPeriodStatistics.lhcPeriod.name, + Other: () => null, + }); - return [ - h('.flex-row.justify-between.items-center.g2', [ - filtersPanelPopover(perLhcPeriodOverviewModel, activeColumns, { profile: 'runsPerLhcPeriod' }), - h('.pl2#runOverviewFilter', runNumbersFilter(perLhcPeriodOverviewModel.filteringModel.get('runNumbers'))), - h('h2', `Good, physics runs of ${lhcPeriodStatistics.lhcPeriod.name}`), - mcReproducibleAsNotBadToggle( - mcReproducibleAsNotBad, - () => perLhcPeriodOverviewModel.setMcReproducibleAsNotBad(!mcReproducibleAsNotBad), - ), - exportTriggerAndModal(perLhcPeriodOverviewModel.exportModel, modalModel), - ]), + return [ + h('.flex-row.justify-between.items-center.g2', [ + filtersPanelPopover(perLhcPeriodOverviewModel, activeColumns, { profile: 'runsPerLhcPeriod' }), + h('.pl2#runOverviewFilter', runNumbersFilter(perLhcPeriodOverviewModel.filteringModel.get('runNumbers'))), + h('h2', `Good, physics runs of ${lhcPeriodName}`), + mcReproducibleAsNotBadToggle( + mcReproducibleAsNotBad, + () => perLhcPeriodOverviewModel.setMcReproducibleAsNotBad(!mcReproducibleAsNotBad), + ), + exportTriggerAndModal(perLhcPeriodOverviewModel.exportModel, modalModel), + ]), + h( + '.intermediate-flex-column', + remoteRuns.match({ + NotAsked: () => null, + Failure: (errors) => errorAlert(errors), + Loading: () => spinner(), + Success: () => [ ...tabbedPanelComponent( tabbedPanelModel, { @@ -153,9 +157,7 @@ export const RunsPerLhcPeriodOverviewPage = ({ runs: { perLhcPeriodOverviewModel { panelClass: ['scroll-auto'] }, ), paginationComponent(perLhcPeriodOverviewModel.pagination), - ]; - }, - }), - - ); + ] }), + ), + ]; }; diff --git a/test/public/runs/runsPerLhcPeriod.overview.test.js b/test/public/runs/runsPerLhcPeriod.overview.test.js index 75b5eb978c..e82b47862c 100644 --- a/test/public/runs/runsPerLhcPeriod.overview.test.js +++ b/test/public/runs/runsPerLhcPeriod.overview.test.js @@ -215,9 +215,9 @@ module.exports = () => { const targetFileName = 'data.json'; // First export - await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR, true); - await page.waitForSelector('select.form-control', { timeout: 200 }); - await page.waitForSelector('option[value=runNumber]', { timeout: 200 }); + await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR); + await page.waitForSelector('select.form-control'); + await page.waitForSelector('option[value=runNumber]'); await page.select('select.form-control', 'runQuality', 'runNumber', 'definition', 'lhcPeriod'); await expectInnerText(page, '#send:enabled', 'Export'); From 6efa56e61722a61ef38ca61fc6c0c2ce6b20feea Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Thu, 19 Mar 2026 13:17:26 +0100 Subject: [PATCH 2/7] feat: separate toolbar rendering from table data rendering on the RunsPerDataPassOverview page --- .../RunsPerDataPassOverviewPage.js | 354 ++++++++++-------- .../runs/runsPerDataPass.overview.test.js | 17 +- 2 files changed, 192 insertions(+), 179 deletions(-) diff --git a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js index 8f63fb608b..ce4c77fe05 100644 --- a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js +++ b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js @@ -117,177 +117,205 @@ export const RunsPerDataPassOverviewPage = ({ const commonTitle = h('h2#breadcrumb-header', { style: 'white-space: nowrap;' }, 'Physics Runs'); const runDetectorsSelectionIsEmpty = perDataPassOverviewModel.runDetectorsSelectionModel.selectedQueryString.length === 0; + const dataPass = remoteDataPass.match({ Other: () => null, Success: (data) => data }); + const detectors = remoteDetectors.match({ Other: () => null, Success: (data) => data }); + const qcSummary = remoteQcSummary.match({ Other: () => null, Success: (data) => data }); - return h( - '.intermediate-flex-column', - { onremove: () => { - perDataPassOverviewModel._abortGaqFetches(); - } }, - mergeRemoteData([remoteDataPass, remoteRuns, remoteDetectors, remoteQcSummary]).match({ - NotAsked: () => null, - Failure: (errors) => errorAlert(errors), - Success: ([dataPass, runs, detectors, qcSummary]) => { - const activeColumns = { - ...runsActiveColumns, - ...getInelasticInteractionRateColumns(pdpBeamTypes), - ...dataPass.skimmingStage === SkimmingStage.SKIMMABLE - ? { - readyForSkimming: { - name: 'Ready for skimming', - visible: true, - format: (_, { runNumber }) => remoteSkimmableRuns.match({ - Success: (skimmableRuns) => switchInput( - skimmableRuns[runNumber], - () => perDataPassOverviewModel.changeReadyForSkimmingFlagForRun({ - runNumber, - readyForSkimming: !skimmableRuns[runNumber], - }), - { - labelAfter: skimmableRuns[runNumber] - ? badge('ready', { color: Color.GREEN }) - : badge('not ready', { color: Color.WARNING_DARKER }), - }, + /* + * The table drawing can be done without using mergeRemoteData, but that will redraw it + * each independent update to dataPass, detectors, or qcSummary. + * While this wouldn't necessarly be noticable for users, it would would detach nodes from + * the document, which would make writing integration test difficult and unreliable. + */ + const fullPageData = mergeRemoteData([remoteRuns, remoteDataPass, remoteDetectors, remoteQcSummary]); + + const activeColumns = { + ...runsActiveColumns, + ...getInelasticInteractionRateColumns(pdpBeamTypes), + + globalAggregatedQuality: { + name: 'GAQ', + information: h( + '', + h('', 'Global aggregated flag based on critical detectors.'), + h('', 'Default detectors: FT0, ITS, TPC (and ZDC for heavy-ion runs)'), + ), + visible: true, + + format: (_, { runNumber }) => { + const runGaqSummary = remoteGaqSummary[runNumber]; + const spinnerEl = h('.flex-row.items-center.justify-center.black', spinner({ size: 2, absolute: false })); + + return runGaqSummary.match({ + Success: (gaqSummary) => { + const gaqDisplay = + gaqSummary?.undefinedQualityPeriodsCount === 0 + ? getQcSummaryDisplay(gaqSummary) + : h('button.btn.btn-primary.w-100', 'GAQ'); + + return frontLink(gaqDisplay, 'gaq-flags', { dataPassId, runNumber }); + }, + Loading: () => tooltip(spinnerEl), + NotAsked: () => tooltip(spinnerEl), + Failure: () => + frontLink( + h('button.btn.btn-primary.w-100', [ + 'GAQ', + h( + '.d-inline-block.va-t-bottom', + tooltip( + h('.f7', iconWarning()), + 'GAQ Summary failed, please click to view GAQ flags', ), - Loading: () => h('.mh3.ph4', '... ...'), - Failure: () => tooltip(iconWarning(), 'Error occurred'), - NotAsked: () => tooltip(iconWarning(), 'Not asked for data'), - }), - profiles: ['runsPerDataPass'], - }, - } - : {}, - globalAggregatedQuality: { - name: 'GAQ', - information: h( - '', - h('', 'Global aggregated flag based on critical detectors.'), - h('', 'Default detectors: FT0, ITS, TPC (and ZDC for heavy-ion runs)'), + ), + ]), + 'gaq-flags', + { dataPassId, runNumber }, ), - visible: true, - format: (_, { runNumber }) => { - const gaqLoadingSpinner = h('.flex-row.items-center.justify-center.black', spinner({ size: 2, absolute: false })); - const runGaqSummary = remoteGaqSummary[runNumber]; + }); + }, - return runGaqSummary.match({ - Success: (gaqSummary) => { - const gaqDisplay = gaqSummary?.undefinedQualityPeriodsCount === 0 - ? getQcSummaryDisplay(gaqSummary) - : h('button.btn.btn-primary.w-100', 'GAQ'); + filter: ({ filteringModel }) => + numericalComparisonFilter( + filteringModel.get('gaq[notBadFraction]'), + { step: 0.1, selectorPrefix: 'gaqNotBadFraction' }, + ), - return frontLink(gaqDisplay, 'gaq-flags', { dataPassId, runNumber }); - }, - Loading: () => tooltip(gaqLoadingSpinner, 'Loading GAQ summary...'), - NotAsked: () => tooltip(gaqLoadingSpinner, 'Loading GAQ summary...'), - Failure: () => { - const gaqDisplay = h('button.btn.btn-primary.w-100', [ - 'GAQ', - h( - '.d-inline-block.va-t-bottom', - tooltip(h('.f7', iconWarning()), 'GAQ Summary failed, please click to view GAQ flags'), - ), - ]); - return frontLink(gaqDisplay, 'gaq-flags', { dataPassId, runNumber }); + filterTooltip: 'not-bad fraction expressed as a percentage', + profiles: ['runsPerDataPass'], + }, + ...dataPass?.skimmingStage === SkimmingStage.SKIMMABLE && { + readyForSkimming: { + name: 'Ready for skimming', + visible: true, + + format: (_, { runNumber }) => + remoteSkimmableRuns.match({ + Success: (skimmableRuns) => + switchInput( + skimmableRuns[runNumber], + () => + perDataPassOverviewModel.changeReadyForSkimmingFlagForRun({ + runNumber, + readyForSkimming: !skimmableRuns[runNumber], + }), + { + labelAfter: skimmableRuns[runNumber] + ? badge('ready', { color: Color.GREEN }) + : badge('not ready', { color: Color.WARNING_DARKER }), }, - }); + ), + + Loading: () => h('.mh3.ph4', '... ...'), + Failure: () => tooltip(iconWarning(), 'Error occurred'), + NotAsked: () => tooltip(iconWarning(), 'Not asked for data'), + }), + + profiles: ['runsPerDataPass'], + }, + }, + }; + + detectors && dataPass && Object.assign( + activeColumns, + createRunDetectorsAsyncQcActiveColumns( + perDataPassOverviewModel.runDetectorsSelectionModel, + detectors, + remoteDplDetectorsUserHasAccessTo, + { dataPass }, + { + profile: 'runsPerDataPass', + qcSummary, + mcReproducibleAsNotBad, + }, + ), + ); + + return [ + h('.flex-row.justify-between.items-center.g2', [ + filtersPanelPopover(perDataPassOverviewModel, activeColumns, { profile: 'runsPerDataPass' }), + h('.pl2#runOverviewFilter', runNumbersFilter(perDataPassOverviewModel.filteringModel.get('runNumbers'))), + h( + '.flex-row.g1.items-center', + h('.flex-row.items-center.g1', [ + breadcrumbs([commonTitle, h('h2#breadcrumb-data-pass-name', dataPass?.name)]), + h('#skimmableControl', dataPass && skimmableControl( + dataPass, + () => { + if (confirm('The data pass is going to be set as skimmable. Do you want to continue?')) { + perDataPassOverviewModel.markDataPassAsSkimmable(); + } }, - filter: ({ filteringModel }) => numericalComparisonFilter( - filteringModel.get('gaq[notBadFraction]'), - { step: 0.1, selectorPrefix: 'gaqNotBadFraction' }, - ), - filterTooltip: 'not-bad fraction expressed as a percentage', - profiles: ['runsPerDataPass'], - }, - ...createRunDetectorsAsyncQcActiveColumns( - perDataPassOverviewModel.runDetectorsSelectionModel, - detectors, - remoteDplDetectorsUserHasAccessTo, - { dataPass }, + markAsSkimmableRequestResult, + )), + ]), + ), + mcReproducibleAsNotBadToggle( + mcReproducibleAsNotBad, + () => perDataPassOverviewModel.setMcReproducibleAsNotBad(!mcReproducibleAsNotBad), + ), + h('.mlauto', qcSummaryLegendTooltip()), + h('#actions-dropdown-button', DropdownComponent( + h('button.btn.btn-primary', h('.flex-row.g2', ['Actions', iconCaretBottom()])), + h('.flex-column.p2.g2', [ + exportTriggerAndModal(perDataPassOverviewModel.exportModel, modalModel, { autoMarginLeft: false }), + frontLink( + h('button.btn.btn-primary.w-100.h2}#set-qc-flags-trigger', { + disabled: runDetectorsSelectionIsEmpty, + }, 'Set QC Flags'), + 'qc-flag-creation-for-data-pass', { - profile: 'runsPerDataPass', - qcSummary, - mcReproducibleAsNotBad, + runNumberDetectorsMap: perDataPassOverviewModel.runDetectorsSelectionModel.selectedQueryString, + dataPassId, }, ), - }; - - return [ - h('.flex-row.justify-between.items-center.g2', [ - filtersPanelPopover(perDataPassOverviewModel, activeColumns, { profile: 'runsPerDataPass' }), - h('.pl2#runOverviewFilter', runNumbersFilter(perDataPassOverviewModel.filteringModel.get('runNumbers'))), + sessionService.hasAccess([BkpRoles.DPG_ASYNC_QC_ADMIN]) && [ h( - '.flex-row.g1.items-center', - h('.flex-row.items-center.g1', [ - breadcrumbs([commonTitle, h('h2#breadcrumb-data-pass-name', dataPass.name)]), - h('#skimmableControl', skimmableControl( - dataPass, - () => { - if (confirm('The data pass is going to be set as skimmable. Do you want to continue?')) { - perDataPassOverviewModel.markDataPassAsSkimmable(); - } - }, - markAsSkimmableRequestResult, - )), - ]), + 'button.btn.btn-danger', + { + ...freezeOrUnfreezeActionState.match({ + Loading: () => ({ + disabled: true, + title: 'Loading', + }), + Other: () => ({}), + }), + onclick: () => dataPass?.isFrozen + ? perDataPassOverviewModel.unfreezeDataPass() + : perDataPassOverviewModel.freezeDataPass(), + }, + `${dataPass?.isFrozen ? 'Unfreeze' : 'Freeze'} the data pass`, ), - mcReproducibleAsNotBadToggle( - mcReproducibleAsNotBad, - () => perDataPassOverviewModel.setMcReproducibleAsNotBad(!mcReproducibleAsNotBad), + h( + 'button.btn.btn-danger', + { + ...discardAllQcFlagsActionState.match({ + Loading: () => ({ + disabled: true, + title: 'Loading', + }), + Other: () => ({}), + }), + onclick: () => { + if (confirm('Are you sure you want to delete ALL the QC flags for this data pass?')) { + perDataPassOverviewModel.discardAllQcFlags(); + } + }, + }, + 'Delete ALL QC flags', ), - h('.mlauto', qcSummaryLegendTooltip()), - h('#actions-dropdown-button', DropdownComponent( - h('button.btn.btn-primary', h('.flex-row.g2', ['Actions', iconCaretBottom()])), - h('.flex-column.p2.g2', [ - exportTriggerAndModal(perDataPassOverviewModel.exportModel, modalModel, { autoMarginLeft: false }), - frontLink( - h('button.btn.btn-primary.w-100.h2}#set-qc-flags-trigger', { - disabled: runDetectorsSelectionIsEmpty, - }, 'Set QC Flags'), - 'qc-flag-creation-for-data-pass', - { - runNumberDetectorsMap: perDataPassOverviewModel.runDetectorsSelectionModel.selectedQueryString, - dataPassId, - }, - ), - sessionService.hasAccess([BkpRoles.DPG_ASYNC_QC_ADMIN]) && [ - h( - 'button.btn.btn-danger', - { - ...freezeOrUnfreezeActionState.match({ - Loading: () => ({ - disabled: true, - title: 'Loading', - }), - Other: () => ({}), - }), - onclick: () => dataPass.isFrozen - ? perDataPassOverviewModel.unfreezeDataPass() - : perDataPassOverviewModel.freezeDataPass(), - }, - `${dataPass.isFrozen ? 'Unfreeze' : 'Freeze'} the data pass`, - ), - h( - 'button.btn.btn-danger', - { - ...discardAllQcFlagsActionState.match({ - Loading: () => ({ - disabled: true, - title: 'Loading', - }), - Other: () => ({}), - }), - onclick: () => { - if (confirm('Are you sure you want to delete ALL the QC flags for this data pass?')) { - perDataPassOverviewModel.discardAllQcFlags(); - } - }, - }, - 'Delete ALL QC flags', - ), - ], - ]), - { alignment: 'right' }, - )), - ]), + ], + ]), + { alignment: 'right' }, + )), + ]), + h( + '.intermediate-flex-column', + { onremove: () => perDataPassOverviewModel._abortGaqFetches() }, + fullPageData.match({ + NotAsked: () => null, + Failure: (errors) => errorAlert(errors), + Success: ([runs]) => [ markAsSkimmableRequestResult.match({ Failure: (errors) => errorAlert(errors), Other: () => null, @@ -311,9 +339,9 @@ export const RunsPerDataPassOverviewPage = ({ { sort: sortModel }, ), paginationComponent(perDataPassOverviewModel.pagination), - ]; - }, - Loading: () => spinner(), - }), - ); + ], + Loading: () => spinner(), + }), + ), + ]; }; diff --git a/test/public/runs/runsPerDataPass.overview.test.js b/test/public/runs/runsPerDataPass.overview.test.js index 5eeef9c018..513bb072b3 100644 --- a/test/public/runs/runsPerDataPass.overview.test.js +++ b/test/public/runs/runsPerDataPass.overview.test.js @@ -423,7 +423,6 @@ module.exports = () => { await waitForTableLength(page, 2); await expectColumnValues(page, 'runNumber', ['108', '107']); - await pressElement(page, '#openFilterToggle'); await pressElement(page, '#reset-filters'); await waitForTableLength(page, 3); await expectColumnValues(page, 'runNumber', ['108', '107', '106']); @@ -438,7 +437,6 @@ module.exports = () => { await pressElement(page, '#detector-filter-dropdown-option-CPV', true); await expectColumnValues(page, 'runNumber', ['2', '1']); - await pressElement(page, '#openFilterToggle'); await pressElement(page, '#reset-filters'); await expectColumnValues(page, 'runNumber', ['55', '2', '1']); }); @@ -454,8 +452,6 @@ module.exports = () => { await expectColumnValues(page, 'runNumber', ['106']); - await page.waitForSelector('#openFilterToggle'); - await pressElement(page, '#openFilterToggle'); await pressElement(page, '#reset-filters'); await expectColumnValues(page, 'runNumber', ['108', '107', '106']); }); @@ -477,7 +473,6 @@ module.exports = () => { await expectColumnValues(page, 'runNumber', ['55', '1']); - await pressElement(page, '#openFilterToggle'); await pressElement(page, '#reset-filters'); await expectColumnValues(page, 'runNumber', ['55', '2', '1']); }); @@ -491,7 +486,6 @@ module.exports = () => { await expectColumnValues(page, 'runNumber', ['54']); - await pressElement(page, '#openFilterToggle'); await pressElement(page, '#reset-filters'); await expectColumnValues(page, 'runNumber', ['105', '56', '54', '49']); }); @@ -514,7 +508,6 @@ module.exports = () => { await fillInput(page, `#${property}-operand`, value, ['change']); await expectColumnValues(page, 'runNumber', expectedRuns); - await pressElement(page, '#openFilterToggle'); await pressElement(page, '#reset-filters', true); await expectColumnValues(page, 'runNumber', ['105', '56', '54', '49']); }); @@ -561,7 +554,6 @@ module.exports = () => { await fillInput(page, '#muInelasticInteractionRate-operand', 0.03, ['change']); await expectColumnValues(page, 'runNumber', ['106']); - await pressElement(page, '#openFilterToggle'); await pressElement(page, '#reset-filters', true); await expectColumnValues(page, 'runNumber', ['108', '107', '106']); }); @@ -620,7 +612,6 @@ module.exports = () => { it('should successfully disable QC flag creation when data pass is frozen', async () => { await waitForTableLength(page, 3); await page.waitForSelector('.select-multi-flag', { hidden: true }); - await pressElement(page, '#actions-dropdown-button .popover-trigger'); await page.waitForSelector('#set-qc-flags-trigger[disabled]'); await page.waitForSelector('#row107-ACO-text button[disabled]'); }); @@ -634,16 +625,10 @@ module.exports = () => { it('should successfully enable QC flag creation when data pass is un-frozen', async () => { await waitForTableLength(page, 3); - await pressElement(page, '.select-multi-flag'); - await pressElement(page, '#actions-dropdown-button .popover-trigger'); - await page.waitForSelector('#set-qc-flags-trigger[disabled]', { hidden: true }); + await page.waitForSelector('#set-qc-flags-trigger[disabled]'); await page.waitForSelector('#set-qc-flags-trigger'); await page.waitForSelector('#row107-ACO-text a'); }); - - after(async () => { - await pressElement(page, '#actions-dropdown-button .popover-trigger', true); - }); }); it('should successfully not display button to discard all QC flags for the data pass', async () => { From ac1020edd08ca39b123c8a69c120c69c9cdef144 Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Fri, 27 Mar 2026 13:02:05 +0100 Subject: [PATCH 3/7] undo some unneeded test changes --- test/public/runs/runsPerDataPass.overview.test.js | 2 +- test/public/runs/runsPerLhcPeriod.overview.test.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/public/runs/runsPerDataPass.overview.test.js b/test/public/runs/runsPerDataPass.overview.test.js index 7b0839b921..473b05066b 100644 --- a/test/public/runs/runsPerDataPass.overview.test.js +++ b/test/public/runs/runsPerDataPass.overview.test.js @@ -625,7 +625,7 @@ module.exports = () => { it('should successfully enable QC flag creation when data pass is un-frozen', async () => { await waitForTableLength(page, 3); - await page.waitForSelector('#set-qc-flags-trigger[disabled]'); + await page.waitForSelector('#set-qc-flags-trigger[disabled]', { hidden: true }); await page.waitForSelector('#set-qc-flags-trigger'); await page.waitForSelector('#row107-ACO-text a'); }); diff --git a/test/public/runs/runsPerLhcPeriod.overview.test.js b/test/public/runs/runsPerLhcPeriod.overview.test.js index e82b47862c..75b5eb978c 100644 --- a/test/public/runs/runsPerLhcPeriod.overview.test.js +++ b/test/public/runs/runsPerLhcPeriod.overview.test.js @@ -215,9 +215,9 @@ module.exports = () => { const targetFileName = 'data.json'; // First export - await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR); - await page.waitForSelector('select.form-control'); - await page.waitForSelector('option[value=runNumber]'); + await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR, true); + await page.waitForSelector('select.form-control', { timeout: 200 }); + await page.waitForSelector('option[value=runNumber]', { timeout: 200 }); await page.select('select.form-control', 'runQuality', 'runNumber', 'definition', 'lhcPeriod'); await expectInnerText(page, '#send:enabled', 'Export'); From f525a85d88a6f623b61e416d87297a1c6318d78a Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Fri, 27 Mar 2026 13:07:21 +0100 Subject: [PATCH 4/7] remove name whilst runs per lhc name is loading --- .../Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js | 8 ++------ .../Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js | 2 +- test/public/runs/runsPerDataPass.overview.test.js | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js index ce4c77fe05..e8dfc111a2 100644 --- a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js +++ b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js @@ -214,11 +214,7 @@ export const RunsPerDataPassOverviewPage = ({ profiles: ['runsPerDataPass'], }, }, - }; - - detectors && dataPass && Object.assign( - activeColumns, - createRunDetectorsAsyncQcActiveColumns( + ...detectors && dataPass && createRunDetectorsAsyncQcActiveColumns( perDataPassOverviewModel.runDetectorsSelectionModel, detectors, remoteDplDetectorsUserHasAccessTo, @@ -229,7 +225,7 @@ export const RunsPerDataPassOverviewPage = ({ mcReproducibleAsNotBad, }, ), - ); + }; return [ h('.flex-row.justify-between.items-center.g2', [ diff --git a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js index 77da8a6eee..f426c0d116 100644 --- a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js +++ b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js @@ -102,7 +102,7 @@ export const RunsPerLhcPeriodOverviewPage = ({ runs: { perLhcPeriodOverviewModel const lhcPeriodName = remoteLhcPeriodStatistics?.match({ Success: (lhcPeriodStatistics) => lhcPeriodStatistics.lhcPeriod.name, - Other: () => null, + Other: () => '', }); return [ diff --git a/test/public/runs/runsPerDataPass.overview.test.js b/test/public/runs/runsPerDataPass.overview.test.js index 473b05066b..7b0839b921 100644 --- a/test/public/runs/runsPerDataPass.overview.test.js +++ b/test/public/runs/runsPerDataPass.overview.test.js @@ -625,7 +625,7 @@ module.exports = () => { it('should successfully enable QC flag creation when data pass is un-frozen', async () => { await waitForTableLength(page, 3); - await page.waitForSelector('#set-qc-flags-trigger[disabled]', { hidden: true }); + await page.waitForSelector('#set-qc-flags-trigger[disabled]'); await page.waitForSelector('#set-qc-flags-trigger'); await page.waitForSelector('#row107-ACO-text a'); }); From 6369c8dcee7ad0ccbc756e2762916fc1105aa6a2 Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Fri, 27 Mar 2026 13:18:14 +0100 Subject: [PATCH 5/7] fix documentation spelling mistake --- .../views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js index e8dfc111a2..c49a5eca73 100644 --- a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js +++ b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js @@ -124,7 +124,7 @@ export const RunsPerDataPassOverviewPage = ({ /* * The table drawing can be done without using mergeRemoteData, but that will redraw it * each independent update to dataPass, detectors, or qcSummary. - * While this wouldn't necessarly be noticable for users, it would would detach nodes from + * While this wouldn't necessarily be noticeable for users, it would detach nodes from * the document, which would make writing integration test difficult and unreliable. */ const fullPageData = mergeRemoteData([remoteRuns, remoteDataPass, remoteDetectors, remoteQcSummary]); From b3baf821528a4a1eb860fe18140315af3d4a21ad Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Fri, 27 Mar 2026 13:49:54 +0100 Subject: [PATCH 6/7] implment header table loading separation for runsperSimulationPassOverviewpAGE --- .../RunsPerDataPassOverviewPage.js | 2 +- .../RunsPerLhcPeriodOverviewPage.js | 2 +- .../RunsPerSimulationPassOverviewPage.js | 116 ++++++++++-------- 3 files changed, 64 insertions(+), 56 deletions(-) diff --git a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js index c49a5eca73..4878f2daba 100644 --- a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js +++ b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js @@ -234,7 +234,7 @@ export const RunsPerDataPassOverviewPage = ({ h( '.flex-row.g1.items-center', h('.flex-row.items-center.g1', [ - breadcrumbs([commonTitle, h('h2#breadcrumb-data-pass-name', dataPass?.name)]), + breadcrumbs([commonTitle, h('h2#breadcrumb-data-pass-name', dataPass?.name ?? spinner({ size: 1, absolute: false }))]), h('#skimmableControl', dataPass && skimmableControl( dataPass, () => { diff --git a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js index f426c0d116..2aaee87d58 100644 --- a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js +++ b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js @@ -109,7 +109,7 @@ export const RunsPerLhcPeriodOverviewPage = ({ runs: { perLhcPeriodOverviewModel h('.flex-row.justify-between.items-center.g2', [ filtersPanelPopover(perLhcPeriodOverviewModel, activeColumns, { profile: 'runsPerLhcPeriod' }), h('.pl2#runOverviewFilter', runNumbersFilter(perLhcPeriodOverviewModel.filteringModel.get('runNumbers'))), - h('h2', `Good, physics runs of ${lhcPeriodName}`), + h('h2', ['Good physics runs of ', lhcPeriodName ?? spinner({ size: 1, absolute: false })]), mcReproducibleAsNotBadToggle( mcReproducibleAsNotBad, () => perLhcPeriodOverviewModel.setMcReproducibleAsNotBad(!mcReproducibleAsNotBad), diff --git a/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewPage.js b/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewPage.js index 55d4cdb988..fe651d8962 100644 --- a/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewPage.js +++ b/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewPage.js @@ -71,56 +71,64 @@ export const RunsPerSimulationPassOverviewPage = ({ const commonTitle = h('h2', 'Runs per MC'); - return h( - '.intermediate-flex-column', - mergeRemoteData([remoteSimulationPass, remoteRuns, remoteDetectors, remoteQcSummary]).match({ - NotAsked: () => null, - Failure: (errors) => errorAlert(errors), - Success: ([simulationPass, runs, detectors, qcSummary]) => { - const activeColumns = { - ...runsActiveColumns, - ...getInelasticInteractionRateColumns(pdpBeamTypes), - ...createRunDetectorsAsyncQcActiveColumns( - perSimulationPassOverviewModel.runDetectorsSelectionModel, - detectors, - remoteDplDetectorsUserHasAccessTo, - { simulationPass }, - { - profile: 'runsPerSimulationPass', - qcSummary, - }, - ), - }; + const fullPageData = mergeRemoteData([remoteRuns, remoteSimulationPass, remoteDetectors, remoteQcSummary]); + const simulationPass = remoteSimulationPass.match({ Other: () => null, Success: (data) => data }); + const detectors = remoteDetectors.match({ Other: () => null, Success: (data) => data }); + const qcSummary = remoteQcSummary.match({ Other: () => null, Success: (data) => data }); - return [ - h('.flex-row.justify-between.items-center.g2', [ - filtersPanelPopover(perSimulationPassOverviewModel, activeColumns, { profile: 'runsPerSimulationPass' }), - h('.pl2#runOverviewFilter', runNumbersFilter(perSimulationPassOverviewModel.filteringModel.get('runNumbers'))), - h( - '.flex-row.g1.items-center', - breadcrumbs([commonTitle, h('h2#breadcrumb-simulation-pass-name', simulationPass.name)]), - ), - mcReproducibleAsNotBadToggle( - mcReproducibleAsNotBad, - () => perSimulationPassOverviewModel.setMcReproducibleAsNotBad(!mcReproducibleAsNotBad), - ), - h('.mlauto', qcSummaryLegendTooltip()), - exportTriggerAndModal(perSimulationPassOverviewModel.exportModel, modalModel, { autoMarginLeft: false }), - frontLink( - h( - 'button.btn.btn-primary.w-100.h2}#set-qc-flags-trigger', - { - disabled: perSimulationPassOverviewModel.runDetectorsSelectionModel.selectedQueryString.length < 1, - }, - 'Set QC Flags', - ), - 'qc-flag-creation-for-simulation-pass', - { - runNumberDetectorsMap: perSimulationPassOverviewModel.runDetectorsSelectionModel.selectedQueryString, - simulationPassId, - }, - ), - ]), + const activeColumns = { + ...runsActiveColumns, + ...getInelasticInteractionRateColumns(pdpBeamTypes), + ...detectors && qcSummary && createRunDetectorsAsyncQcActiveColumns( + perSimulationPassOverviewModel.runDetectorsSelectionModel, + detectors, + remoteDplDetectorsUserHasAccessTo, + { simulationPass }, + { + profile: 'runsPerSimulationPass', + qcSummary, + }, + ), + }; + + return [ + h('.flex-row.justify-between.items-center.g2', [ + filtersPanelPopover(perSimulationPassOverviewModel, activeColumns, { profile: 'runsPerSimulationPass' }), + h('.pl2#runOverviewFilter', runNumbersFilter(perSimulationPassOverviewModel.filteringModel.get('runNumbers'))), + h( + '.flex-row.g1.items-center', + breadcrumbs([ + commonTitle, + h('h2#breadcrumb-simulation-pass-name', simulationPass?.name ?? spinner({ size: 1, absolute: false })), + ]), + ), + mcReproducibleAsNotBadToggle( + mcReproducibleAsNotBad, + () => perSimulationPassOverviewModel.setMcReproducibleAsNotBad(!mcReproducibleAsNotBad), + ), + h('.mlauto', qcSummaryLegendTooltip()), + exportTriggerAndModal(perSimulationPassOverviewModel.exportModel, modalModel, { autoMarginLeft: false }), + frontLink( + h( + 'button.btn.btn-primary.w-100.h2}#set-qc-flags-trigger', + { + disabled: perSimulationPassOverviewModel.runDetectorsSelectionModel.selectedQueryString.length < 1, + }, + 'Set QC Flags', + ), + 'qc-flag-creation-for-simulation-pass', + { + runNumberDetectorsMap: perSimulationPassOverviewModel.runDetectorsSelectionModel.selectedQueryString, + simulationPassId, + }, + ), + ]), + h( + '.intermediate-flex-column', + fullPageData.match({ + NotAsked: () => null, + Failure: (errors) => errorAlert(errors), + Success: ([runs]) => [ table( runs, activeColumns, @@ -132,9 +140,9 @@ export const RunsPerSimulationPassOverviewPage = ({ { sort: sortModel }, ), paginationComponent(perSimulationPassOverviewModel.pagination), - ]; - }, - Loading: () => spinner(), - }), - ); + ], + Loading: () => spinner(), + }), + ), + ]; }; From 30593aca9a5e601ed9dd478133819c47f717d712 Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Fri, 27 Mar 2026 14:35:53 +0100 Subject: [PATCH 7/7] remove unneeded re-openings of the filter pop-over --- test/public/runs/runsPerSimulationPass.overview.test.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/public/runs/runsPerSimulationPass.overview.test.js b/test/public/runs/runsPerSimulationPass.overview.test.js index e6748ffb94..f3c2d47316 100644 --- a/test/public/runs/runsPerSimulationPass.overview.test.js +++ b/test/public/runs/runsPerSimulationPass.overview.test.js @@ -217,7 +217,6 @@ module.exports = () => { await fillInput(page, '#detectorsQc-for-1-notBadFraction-operand', '90', ['change']); await expectColumnValues(page, 'runNumber', ['106']); - await pressElement(page, '#openFilterToggle', true); await pressElement(page, '#reset-filters', true); await expectColumnValues(page, 'runNumber', ['107', '106', '105']); }); @@ -231,7 +230,6 @@ module.exports = () => { await fillInput(page, '#detectorsQc-for-1-notBadFraction-operand', '90', ['change']); await expectColumnValues(page, 'runNumber', ['106']); - await pressElement(page, '#openFilterToggle', true); await pressElement(page, '#reset-filters', true); await expectColumnValues(page, 'runNumber', ['107', '106', '105']); });