From 3bc171a9bb5e617aea1183c486d2e0bb382e7edc Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 10:59:28 +0200 Subject: [PATCH 01/31] feat: update filteringModel to set filters to the url when they are updated. --- .../Filters/common/FilteringModel.js | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/public/components/Filters/common/FilteringModel.js b/lib/public/components/Filters/common/FilteringModel.js index ac3224ecc4..c56a9dbd38 100644 --- a/lib/public/components/Filters/common/FilteringModel.js +++ b/lib/public/components/Filters/common/FilteringModel.js @@ -11,6 +11,7 @@ * or submit itself to any jurisdiction. */ +import { buildUrl } from '@aliceo2/web-ui'; import { expandQueryLikeNestedKey } from '../../../utilities/expandNestedKey.js'; import { SelectionModel } from '../../common/selection/SelectionModel.js'; import { FilterModel } from './FilterModel.js'; @@ -23,17 +24,18 @@ export class FilteringModel extends Observable { /** * Constructor * + * @param {QueryRouter} router router that controls the application's page navigation * @param {Object} filters the filters with their label and model */ - constructor(filters) { + constructor(router, filters) { super(); - this._visualChange$ = new Observable(); + this._router = router; this._filters = filters; this._filterModels = Object.values(filters); for (const model of this._filterModels) { - model.bubbleTo(this); + model.observe(() => this.setFilterToURL()); model.visualChange$?.bubbleTo(this._visualChange$); } } @@ -107,6 +109,22 @@ export class FilteringModel extends Observable { return this._filters[key]; } + /** + * When the user updates the displayed Objects, the filters should be placed in the URL as well + * @returns {undefined} + */ + setFilterToURL() { + const { params } = this._router; + const newParams = { ...params }; + newParams.filter = this.normalized; + + if (this._pageIdentifiers.includes(params.page)) { + this._router.go(buildUrl('?', newParams), false, true); + } + + this.notify(); + } + /** * Add new filter * From 0957b9e123256015af71c47ff9441a0f136d0fdf Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 11:02:44 +0200 Subject: [PATCH 02/31] chore: mover router instantiation to before the pageModel instantiations --- lib/public/Model.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/public/Model.js b/lib/public/Model.js index 6818118c81..05e2dd2f2f 100644 --- a/lib/public/Model.js +++ b/lib/public/Model.js @@ -95,6 +95,12 @@ export default class Model extends Observable { this._appConfiguration$ = new Observable(); this._inputDebounceTime = INPUT_DEBOUNCE_TIME; + // Setup router + this.router = new QueryRouter(); + this.router.observe(this.handleLocationChange.bind(this)); + this.router.bubbleTo(this); + registerFrontLinkListener((e) => this.router.handleLinkEvent(e)); + // Models this.home = new HomePageModel(this); @@ -103,7 +109,7 @@ export default class Model extends Observable { this.lhcPeriods = new LhcPeriodsModel(this); this.lhcPeriods.bubbleTo(this); - this.dataPasses = new DataPassesModel(this); + this.dataPasses = new DataPassesModel(this.router); this.dataPasses.bubbleTo(this); this.qcFlags = new QcFlagsModel(this); @@ -178,12 +184,6 @@ export default class Model extends Observable { this.errorModel = new ErrorModel(); this.errorModel.bubbleTo(this); - // Setup router - this.router = new QueryRouter(); - this.router.observe(this.handleLocationChange.bind(this)); - this.router.bubbleTo(this); - registerFrontLinkListener((e) => this.router.handleLinkEvent(e)); - // Init pages this.handleLocationChange(); this.window.addEventListener('resize', debounce(() => this.notify(), 100)); From 23d02fc072d604fac6618c02df4e3d3d000dd19d Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 11:02:58 +0200 Subject: [PATCH 03/31] chore: pass router to the dataPassOverview models --- lib/public/views/DataPasses/DataPassesModel.js | 9 +++++---- .../DataPasses/DataPassesOverviewModel.js | 18 +++++++++++------- .../DataPassesPerLhcPeriodOverviewModel.js | 5 +++-- ...DataPassesPerSimulationPassOverviewModel.js | 5 +++-- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/public/views/DataPasses/DataPassesModel.js b/lib/public/views/DataPasses/DataPassesModel.js index 5d987b31d7..5af74e48be 100644 --- a/lib/public/views/DataPasses/DataPassesModel.js +++ b/lib/public/views/DataPasses/DataPassesModel.js @@ -21,14 +21,15 @@ import { DataPassesPerSimulationPassOverviewModel } from './PerSimulationPassOve export class DataPassesModel extends Observable { /** * The constructor of the model + * @param {QueryRouter} router router that controls the application's page navigation */ - constructor() { - super(); + constructor(router) { + super(router); - this._perLhcPeriodOverviewModel = new DataPassesPerLhcPeriodOverviewModel(); + this._perLhcPeriodOverviewModel = new DataPassesPerLhcPeriodOverviewModel(router); this._perLhcPeriodOverviewModel.bubbleTo(this); - this._perSimulationPassOverviewModel = new DataPassesPerSimulationPassOverviewModel(); + this._perSimulationPassOverviewModel = new DataPassesPerSimulationPassOverviewModel(router); this._perSimulationPassOverviewModel.bubbleTo(this); } diff --git a/lib/public/views/DataPasses/DataPassesOverviewModel.js b/lib/public/views/DataPasses/DataPassesOverviewModel.js index b85cc052d7..d32b7eef67 100644 --- a/lib/public/views/DataPasses/DataPassesOverviewModel.js +++ b/lib/public/views/DataPasses/DataPassesOverviewModel.js @@ -22,15 +22,19 @@ import { OverviewPageModel } from '../../models/OverviewModel.js'; export class DataPassesOverviewModel extends OverviewPageModel { /** * Constructor + * @param {QueryRouter} router router that controls the application's page navigation */ - constructor() { + constructor(router) { super(); - this._filteringModel = new FilteringModel({ - names: new TextTokensFilterModel(), - 'include[byName]': new SelectionFilterModel({ - availableOptions: NON_PHYSICS_PRODUCTIONS_NAMES_WORDS.map((word) => ({ label: word.toUpperCase(), value: word })), - }), - }); + this._filteringModel = new FilteringModel( + router, + { + names: new TextTokensFilterModel(), + 'include[byName]': new SelectionFilterModel({ + availableOptions: NON_PHYSICS_PRODUCTIONS_NAMES_WORDS.map((word) => ({ label: word.toUpperCase(), value: word })), + }), + }, + ); this._filteringModel.visualChange$.bubbleTo(this); this._filteringModel.observe(() => { diff --git a/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewModel.js b/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewModel.js index dc125e1a94..ee7f580ef7 100644 --- a/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewModel.js +++ b/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewModel.js @@ -19,9 +19,10 @@ import { buildUrl } from '/js/src/index.js'; export class DataPassesPerLhcPeriodOverviewModel extends DataPassesOverviewModel { /** * Constructor + * @param {QueryRouter} router router that controls the application's page navigation */ - constructor() { - super(); + constructor(router) { + super(router); this._lhcPeriodId = null; } diff --git a/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewModel.js b/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewModel.js index 30fd3c616c..12fd208a39 100644 --- a/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewModel.js +++ b/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewModel.js @@ -21,9 +21,10 @@ import { DataPassesOverviewModel } from '../DataPassesOverviewModel.js'; export class DataPassesPerSimulationPassOverviewModel extends DataPassesOverviewModel { /** * Constructor + * @param {QueryRouter} router router that controls the application's page navigation */ - constructor() { - super(); + constructor(router) { + super(router); this._simulationPass = new ObservableData(RemoteData.notAsked()); this._simulationPass.bubbleTo(this); } From 51b88275fbc6bd8e70fd5604adf480068fdbb04c Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 14:26:57 +0200 Subject: [PATCH 04/31] chore: pass router to the EnvironmentOverviewModel models --- .../Overview/EnvironmentOverviewModel.js | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js index 8498a02d79..f49de81b3b 100644 --- a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js +++ b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js @@ -32,19 +32,22 @@ export class EnvironmentOverviewModel extends OverviewPageModel { constructor(model) { super(); - this._filteringModel = new FilteringModel({ - created: new TimeRangeInputModel(), - runNumbers: new RawTextFilterModel(), - statusHistory: new RawTextFilterModel(), - currentStatus: new SelectionFilterModel({ - availableOptions: Object.keys(StatusAcronym).map((status) => ({ - value: status, - label: coloredEnvironmentStatusComponent(status), - rawLabel: status, - })), - }), - ids: new RawTextFilterModel(), - }); + this._filteringModel = new FilteringModel( + model.router, + { + created: new TimeRangeInputModel(), + runNumbers: new RawTextFilterModel(), + statusHistory: new RawTextFilterModel(), + currentStatus: new SelectionFilterModel({ + availableOptions: Object.keys(StatusAcronym).map((status) => ({ + value: status, + label: coloredEnvironmentStatusComponent(status), + rawLabel: status, + })), + }), + ids: new RawTextFilterModel(), + }, + ); this._filteringModel.observe(() => this._applyFilters(true)); this._filteringModel.visualChange$?.bubbleTo(this); From 22efa5ff1adbcdc09e9eebf8264911a69e670ca8 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 14:30:45 +0200 Subject: [PATCH 05/31] chore: pass router to the EnvironmentOverviewModel models --- .../views/Home/Overview/HomePageModel.js | 2 +- lib/public/views/LhcFills/LhcFills.js | 2 +- .../Overview/LhcFillsOverviewModel.js | 26 +++++++++++-------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/public/views/Home/Overview/HomePageModel.js b/lib/public/views/Home/Overview/HomePageModel.js index 40b6cfac85..1826bc5b61 100644 --- a/lib/public/views/Home/Overview/HomePageModel.js +++ b/lib/public/views/Home/Overview/HomePageModel.js @@ -32,7 +32,7 @@ export class HomePageModel extends Observable { this._logsOverviewModel = new LogsOverviewModel(model, true); this._logsOverviewModel.bubbleTo(this); - this._lhcFillsOverviewModel = new LhcFillsOverviewModel(true); + this._lhcFillsOverviewModel = new LhcFillsOverviewModel(model.router, true); this._lhcFillsOverviewModel.bubbleTo(this); } diff --git a/lib/public/views/LhcFills/LhcFills.js b/lib/public/views/LhcFills/LhcFills.js index 70b6c5eb3d..a9b4036ab0 100644 --- a/lib/public/views/LhcFills/LhcFills.js +++ b/lib/public/views/LhcFills/LhcFills.js @@ -29,7 +29,7 @@ export default class LhcFills extends Observable { this.model = model; // Sub-models - this._overviewModel = new LhcFillsOverviewModel(true); + this._overviewModel = new LhcFillsOverviewModel(model.router, true); this._overviewModel.bubbleTo(this); this._detailsModel = new LhcFillDetailsModel(); diff --git a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js index 8e292f9fbf..52c99c52c4 100644 --- a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js +++ b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js @@ -30,21 +30,25 @@ export class LhcFillsOverviewModel extends OverviewPageModel { /** * Constructor * + * @param {QueryRouter} router router that controls the application's page navigation * @param {boolean} [stableBeamsOnly=false] if true, overview will load stable beam only */ - constructor(stableBeamsOnly = false) { + constructor(router, stableBeamsOnly = false) { super(); - this._filteringModel = new FilteringModel({ - fillNumbers: new RawTextFilterModel(), - beamDuration: new TextComparisonFilterModel(), - runDuration: new TextComparisonFilterModel(), - hasStableBeams: new ToggleFilterModel(stableBeamsOnly), - stableBeamsStart: new TimeRangeFilterModel(), - stableBeamsEnd: new TimeRangeFilterModel(), - beamTypes: new BeamTypeFilterModel(), - schemeName: new RawTextFilterModel(), - }); + this._filteringModel = new FilteringModel( + router, + { + fillNumbers: new RawTextFilterModel(), + beamDuration: new TextComparisonFilterModel(), + runDuration: new TextComparisonFilterModel(), + hasStableBeams: new ToggleFilterModel(stableBeamsOnly), + stableBeamsStart: new TimeRangeFilterModel(), + stableBeamsEnd: new TimeRangeFilterModel(), + beamTypes: new BeamTypeFilterModel(), + schemeName: new RawTextFilterModel(), + }, + ); this._filteringModel.observe(() => this._applyFilters()); this._filteringModel.visualChange$.bubbleTo(this); From 325fb6ec229ff11723551824b10cadbd0709129f Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 14:33:14 +0200 Subject: [PATCH 06/31] chore: pass router to the LhcPeriodOverview model --- lib/public/Model.js | 2 +- lib/public/views/lhcPeriods/LhcPeriodsModel.js | 5 +++-- .../Overview/LhcPeriodsOverviewModel.js | 16 ++++++++++------ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/public/Model.js b/lib/public/Model.js index 05e2dd2f2f..3c1eacffa9 100644 --- a/lib/public/Model.js +++ b/lib/public/Model.js @@ -106,7 +106,7 @@ export default class Model extends Observable { this.home = new HomePageModel(this); this.home.bubbleTo(this); - this.lhcPeriods = new LhcPeriodsModel(this); + this.lhcPeriods = new LhcPeriodsModel(this.router); this.lhcPeriods.bubbleTo(this); this.dataPasses = new DataPassesModel(this.router); diff --git a/lib/public/views/lhcPeriods/LhcPeriodsModel.js b/lib/public/views/lhcPeriods/LhcPeriodsModel.js index 4f9d0ed185..b9ed51719e 100644 --- a/lib/public/views/lhcPeriods/LhcPeriodsModel.js +++ b/lib/public/views/lhcPeriods/LhcPeriodsModel.js @@ -20,11 +20,12 @@ import { LhcPeriodsOverviewModel } from './Overview/LhcPeriodsOverviewModel.js'; export class LhcPeriodsModel extends Observable { /** * The constructor of the model + * @param {QueryRouter} router router that controls the application's page navigation */ - constructor() { + constructor(router) { super(); - this._overviewModel = new LhcPeriodsOverviewModel(); + this._overviewModel = new LhcPeriodsOverviewModel(router); this._overviewModel.bubbleTo(this); } diff --git a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js index e6073eb68f..b989b60488 100644 --- a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js +++ b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js @@ -24,15 +24,19 @@ import { buildUrl } from '/js/src/index.js'; export class LhcPeriodsOverviewModel extends OverviewPageModel { /** * The constructor of the Overview model object + * @param {QueryRouter} router router that controls the application's page navigation */ - constructor() { + constructor(router) { super(); - this._filteringModel = new FilteringModel({ - names: new TextTokensFilterModel(), - years: new TextTokensFilterModel(), - pdpBeamTypes: new TextTokensFilterModel(), - }); + this._filteringModel = new FilteringModel( + router, + { + names: new TextTokensFilterModel(), + years: new TextTokensFilterModel(), + pdpBeamTypes: new TextTokensFilterModel(), + }, + ); this._filteringModel.visualChange$.bubbleTo(this); this._filteringModel.observe(() => { From 69521dc44b35c44525ab870aa320c532417c7de5 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 14:35:07 +0200 Subject: [PATCH 07/31] chore: pass router to the LogsOverview model --- .../views/Logs/Overview/LogsOverviewModel.js | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/public/views/Logs/Overview/LogsOverviewModel.js b/lib/public/views/Logs/Overview/LogsOverviewModel.js index 4cb565e9b9..eef752c91e 100644 --- a/lib/public/views/Logs/Overview/LogsOverviewModel.js +++ b/lib/public/views/Logs/Overview/LogsOverviewModel.js @@ -38,16 +38,19 @@ export class LogsOverviewModel extends Observable { constructor(model, excludeAnonymous = false) { super(); - this._filteringModel = new FilteringModel({ - author: new AuthorFilterModel(), - title: new RawTextFilterModel(), - content: new RawTextFilterModel(), - tags: new TagFilterModel(tagsProvider.items$), - runNumbers: new RawTextFilterModel(), - environmentIds: new RawTextFilterModel(), - fillNumbers: new RawTextFilterModel(), - created: new TimeRangeInputModel(), - }); + this._filteringModel = new FilteringModel( + model.router, + { + author: new AuthorFilterModel(), + title: new RawTextFilterModel(), + content: new RawTextFilterModel(), + tags: new TagFilterModel(tagsProvider.items$), + runNumbers: new RawTextFilterModel(), + environmentIds: new RawTextFilterModel(), + fillNumbers: new RawTextFilterModel(), + created: new TimeRangeInputModel(), + }, + ); this._filteringModel.observe(() => this._applyFilters()); this._filteringModel.visualChange$.bubbleTo(this); From fda7ea13f6a19f4f0cdeef0c2bdc2180a63007c4 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 14:37:40 +0200 Subject: [PATCH 08/31] chore: pass router to the QcFlagTypesOverview model --- .../Overview/QcFlagTypesOverviewModel.js | 16 ++++++++++------ lib/public/views/QcFlagTypes/QcFlagTypesModel.js | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js index 72abce90f5..a5d7a757ec 100644 --- a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js +++ b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js @@ -23,15 +23,19 @@ import { RadioButtonFilterModel } from '../../../components/Filters/common/Radio export class QcFlagTypesOverviewModel extends OverviewPageModel { /** * Constructor + * @param {QueryRouter} router router that controls the application's page navigation */ - constructor() { + constructor(router) { super(); - this._filteringModel = new FilteringModel({ - names: new TextTokensFilterModel(), - methods: new TextTokensFilterModel(), - bad: new RadioButtonFilterModel([{ label: 'Any' }, { label: 'Bad', value: true }, { label: 'Not Bad', value: false }]), - }); + this._filteringModel = new FilteringModel( + router, + { + names: new TextTokensFilterModel(), + methods: new TextTokensFilterModel(), + bad: new RadioButtonFilterModel([{ label: 'Any' }, { label: 'Bad', value: true }, { label: 'Not Bad', value: false }]), + }, + ); this._filteringModel.observe(() => { this._pagination.silentlySetCurrentPage(1); diff --git a/lib/public/views/QcFlagTypes/QcFlagTypesModel.js b/lib/public/views/QcFlagTypes/QcFlagTypesModel.js index 43468d3e34..b0802cfb97 100644 --- a/lib/public/views/QcFlagTypes/QcFlagTypesModel.js +++ b/lib/public/views/QcFlagTypes/QcFlagTypesModel.js @@ -29,7 +29,7 @@ export class QcFlagTypesModel extends Observable { this.model = model; // Overview - this._overviewModel = new QcFlagTypesOverviewModel(); + this._overviewModel = new QcFlagTypesOverviewModel(model.router); this._overviewModel.bubbleTo(this); } From 555cd6df36fa280bbc1eb3da0f1ce5e49974f95d Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 14:41:36 +0200 Subject: [PATCH 09/31] chore: pass router to the RunsOverview model --- .../views/Runs/Overview/RunsOverviewModel.js | 93 ++++++++++--------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index b5c400e5d2..ea383bb95c 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -53,51 +53,54 @@ export class RunsOverviewModel extends OverviewPageModel { constructor(model) { super(); - this._filteringModel = new FilteringModel({ - runNumbers: new RawTextFilterModel(), - detectors: new DetectorsFilterModel(detectorsProvider.dataTaking$), - tags: new TagFilterModel( - tagsProvider.items$, - [ - CombinationOperator.AND, - CombinationOperator.OR, - CombinationOperator.NONE_OF, - ], - ), - fillNumbers: new RawTextFilterModel(), - lhcPeriods: new RawTextFilterModel(), - o2start: new TimeRangeFilterModel(), - o2end: new TimeRangeFilterModel(), - definitions: new RunDefinitionFilterModel(), - runDuration: new NumericalComparisonFilterModel({ scale: 60 * 1000 }), - environmentIds: new RawTextFilterModel(), - runTypes: new RunTypesFilterModel(runTypesProvider.items$), - beamModes: new BeamModeFilterModel(beamModesProvider.items$), - runQualities: new SelectionFilterModel({ - availableOptions: RUN_QUALITIES.map((quality) => ({ - label: quality.toUpperCase(), - value: quality, - })), - }), - nDetectors: new NumericalComparisonFilterModel({ integer: true }), - nEpns: new NumericalComparisonFilterModel({ integer: true }), - nFlps: new NumericalComparisonFilterModel({ integer: true }), - ctfFileCount: new NumericalComparisonFilterModel({ integer: true }), - tfFileCount: new NumericalComparisonFilterModel({ integer: true }), - otherFileCount: new NumericalComparisonFilterModel({ integer: true }), - odcTopologyFullName: new RawTextFilterModel(), - eorReason: new EorReasonFilterModel(eorReasonTypeProvider.items$), - magnets: new MagnetsFilteringModel(magnetsCurrentLevelsProvider.items$), - muInelasticInteractionRate: new NumericalComparisonFilterModel(), - inelasticInteractionRateAvg: new NumericalComparisonFilterModel(), - inelasticInteractionRateAtStart: new NumericalComparisonFilterModel(), - inelasticInteractionRateAtMid: new NumericalComparisonFilterModel(), - inelasticInteractionRateAtEnd: new NumericalComparisonFilterModel(), - ddflp: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]), - dcs: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]), - epn: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]), - triggerValues: new SelectionModel({ availableOptions: TRIGGER_VALUES.map((value) => ({ label: value, value })) }), - }); + this._filteringModel = new FilteringModel( + model.router, + { + runNumbers: new RawTextFilterModel(), + detectors: new DetectorsFilterModel(detectorsProvider.dataTaking$), + tags: new TagFilterModel( + tagsProvider.items$, + [ + CombinationOperator.AND, + CombinationOperator.OR, + CombinationOperator.NONE_OF, + ], + ), + fillNumbers: new RawTextFilterModel(), + lhcPeriods: new RawTextFilterModel(), + o2start: new TimeRangeFilterModel(), + o2end: new TimeRangeFilterModel(), + definitions: new RunDefinitionFilterModel(), + runDuration: new NumericalComparisonFilterModel({ scale: 60 * 1000 }), + environmentIds: new RawTextFilterModel(), + runTypes: new RunTypesFilterModel(runTypesProvider.items$), + beamModes: new BeamModeFilterModel(beamModesProvider.items$), + runQualities: new SelectionFilterModel({ + availableOptions: RUN_QUALITIES.map((quality) => ({ + label: quality.toUpperCase(), + value: quality, + })), + }), + nDetectors: new NumericalComparisonFilterModel({ integer: true }), + nEpns: new NumericalComparisonFilterModel({ integer: true }), + nFlps: new NumericalComparisonFilterModel({ integer: true }), + ctfFileCount: new NumericalComparisonFilterModel({ integer: true }), + tfFileCount: new NumericalComparisonFilterModel({ integer: true }), + otherFileCount: new NumericalComparisonFilterModel({ integer: true }), + odcTopologyFullName: new RawTextFilterModel(), + eorReason: new EorReasonFilterModel(eorReasonTypeProvider.items$), + magnets: new MagnetsFilteringModel(magnetsCurrentLevelsProvider.items$), + muInelasticInteractionRate: new NumericalComparisonFilterModel(), + inelasticInteractionRateAvg: new NumericalComparisonFilterModel(), + inelasticInteractionRateAtStart: new NumericalComparisonFilterModel(), + inelasticInteractionRateAtMid: new NumericalComparisonFilterModel(), + inelasticInteractionRateAtEnd: new NumericalComparisonFilterModel(), + ddflp: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]), + dcs: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]), + epn: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]), + triggerValues: new SelectionModel({ availableOptions: TRIGGER_VALUES.map((value) => ({ label: value, value })) }), + }, + ); this._filteringModel.observe(() => this._applyFilters(true)); this._filteringModel.visualChange$.bubbleTo(this); From 2da2473353f42bf14289101b059c84ad0eb621bd Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 14:45:44 +0200 Subject: [PATCH 10/31] chore: pass router to the Simulation pass overview models --- lib/public/Model.js | 2 +- .../AnchoredSimulationPassesOverviewModel.js | 5 +++-- .../SimulationPassesPerLhcPeriodOverviewModel.js | 5 +++-- lib/public/views/SimulationPasses/SimulationPassesModel.js | 7 ++++--- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/public/Model.js b/lib/public/Model.js index 3c1eacffa9..0d0ae222f3 100644 --- a/lib/public/Model.js +++ b/lib/public/Model.js @@ -115,7 +115,7 @@ export default class Model extends Observable { this.qcFlags = new QcFlagsModel(this); this.qcFlags.bubbleTo(this); - this.simulationPasses = new SimulationPassesModel(this); + this.simulationPasses = new SimulationPassesModel(this.router); this.simulationPasses.bubbleTo(this); this.qcFlagTypes = new QcFlagTypesModel(this); diff --git a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js index d986ece53b..ad01197cef 100644 --- a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js +++ b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js @@ -23,11 +23,12 @@ import { FilteringModel } from '../../../components/Filters/common/FilteringMode export class AnchoredSimulationPassesOverviewModel extends OverviewPageModel { /** * Constructor + * @param {QueryRouter} router router that controls the application's page navigation */ - constructor() { + constructor(router) { super(); - this._filteringModel = new FilteringModel({ names: new TextTokensFilterModel() }); + this._filteringModel = new FilteringModel(router, { names: new TextTokensFilterModel() }); this._filteringModel.observe(() => { this._pagination.silentlySetCurrentPage(1); diff --git a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js index 0d8af9b309..fbd210982d 100644 --- a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js +++ b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js @@ -23,11 +23,12 @@ import { FilteringModel } from '../../../components/Filters/common/FilteringMode export class SimulationPassesPerLhcPeriodOverviewModel extends OverviewPageModel { /** * Constructor + * @param {QueryRouter} router router that controls the application's page navigation */ - constructor() { + constructor(router) { super(); - this._filteringModel = new FilteringModel({ names: new TextTokensFilterModel() }); + this._filteringModel = new FilteringModel(router, { names: new TextTokensFilterModel() }); this._filteringModel.visualChange$.bubbleTo(this); this._filteringModel.observe(() => { this._pagination.silentlySetCurrentPage(1); diff --git a/lib/public/views/SimulationPasses/SimulationPassesModel.js b/lib/public/views/SimulationPasses/SimulationPassesModel.js index 8e8d6e7969..22436b4c5c 100644 --- a/lib/public/views/SimulationPasses/SimulationPassesModel.js +++ b/lib/public/views/SimulationPasses/SimulationPassesModel.js @@ -21,14 +21,15 @@ import { AnchoredSimulationPassesOverviewModel } from './AnchoredOverview/Anchor export class SimulationPassesModel extends Observable { /** * The constructor of the model + * @param {QueryRouter} router router that controls the application's page navigation */ - constructor() { + constructor(router) { super(); - this._perLhcPeriodOverviewModel = new SimulationPassesPerLhcPeriodOverviewModel(); + this._perLhcPeriodOverviewModel = new SimulationPassesPerLhcPeriodOverviewModel(router); this._perLhcPeriodOverviewModel.bubbleTo(this); - this._anchoredOverviewModel = new AnchoredSimulationPassesOverviewModel(); + this._anchoredOverviewModel = new AnchoredSimulationPassesOverviewModel(router); this._anchoredOverviewModel.bubbleTo(this); } From 61bcae061f5e9520ea96fba879558d66a84a90f4 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 14:53:31 +0200 Subject: [PATCH 11/31] fixup: the import of buildUrl --- lib/public/components/Filters/common/FilteringModel.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/public/components/Filters/common/FilteringModel.js b/lib/public/components/Filters/common/FilteringModel.js index c56a9dbd38..380aaf92fb 100644 --- a/lib/public/components/Filters/common/FilteringModel.js +++ b/lib/public/components/Filters/common/FilteringModel.js @@ -11,11 +11,10 @@ * or submit itself to any jurisdiction. */ -import { buildUrl } from '@aliceo2/web-ui'; import { expandQueryLikeNestedKey } from '../../../utilities/expandNestedKey.js'; import { SelectionModel } from '../../common/selection/SelectionModel.js'; import { FilterModel } from './FilterModel.js'; -import { Observable } from '/js/src/index.js'; +import { buildUrl, Observable } from '/js/src/index.js'; /** * Model representing a filtering system, including filter inputs visibility, filters values and so on From 3f06051a7c27cbfd44f38a50768e5619771f3350 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 14:57:40 +0200 Subject: [PATCH 12/31] chore: add pageidentifiers field to FilteringModel --- .../components/Filters/common/FilteringModel.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/public/components/Filters/common/FilteringModel.js b/lib/public/components/Filters/common/FilteringModel.js index 380aaf92fb..0f9a455f1c 100644 --- a/lib/public/components/Filters/common/FilteringModel.js +++ b/lib/public/components/Filters/common/FilteringModel.js @@ -29,6 +29,7 @@ export class FilteringModel extends Observable { constructor(router, filters) { super(); this._visualChange$ = new Observable(); + this._pageIdentifiers = []; this._router = router; this._filters = filters; @@ -39,6 +40,17 @@ export class FilteringModel extends Observable { } } + /** + * Sets the page identifiers + * + * @param {string[]} identifiers Strings that identify the pages as shown in the router params. + * Used to prevent unneeded reads/writes from/to the url + * @returns {void} + */ + set pageIdentifiers(identifiers) { + this._pageIdentifiers = identifiers; + } + /** * Reset the filters * From fb562c845ace5fc3d0699f78497d54424f49b63c Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Tue, 21 Apr 2026 15:15:08 +0200 Subject: [PATCH 13/31] chore: add page identifiers to all of the overview page models --- .../DataPassesPerSimulationPassOverviewModel.js | 1 + .../views/Environments/Overview/EnvironmentOverviewModel.js | 1 + lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js | 1 + lib/public/views/Logs/Overview/LogsOverviewModel.js | 1 + .../views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js | 1 + lib/public/views/Runs/Overview/RunsOverviewModel.js | 1 + .../views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js | 1 + .../views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js | 1 + .../RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js | 1 + .../AnchoredOverview/AnchoredSimulationPassesOverviewModel.js | 1 + .../SimulationPassesPerLhcPeriodOverviewModel.js | 1 + 11 files changed, 11 insertions(+) diff --git a/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewModel.js b/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewModel.js index 12fd208a39..43c73d6988 100644 --- a/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewModel.js +++ b/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewModel.js @@ -27,6 +27,7 @@ export class DataPassesPerSimulationPassOverviewModel extends DataPassesOverview super(router); this._simulationPass = new ObservableData(RemoteData.notAsked()); this._simulationPass.bubbleTo(this); + this._filteringModel.pageIdentifiers = ['data-passes-per-simulation-pass-overview']; } /** diff --git a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js index f49de81b3b..f898a1d989 100644 --- a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js +++ b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js @@ -59,6 +59,7 @@ export class EnvironmentOverviewModel extends OverviewPageModel { model.appConfiguration$.observe(() => updateDebounceTime()); updateDebounceTime(); + this._filteringModel.pageIdentifiers = ['env-overview']; } /** diff --git a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js index 52c99c52c4..de58e1324d 100644 --- a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js +++ b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js @@ -54,6 +54,7 @@ export class LhcFillsOverviewModel extends OverviewPageModel { this._filteringModel.visualChange$.bubbleTo(this); this.reset(false); + this._filteringModel.pageIdentifiers = ['lhc-fill-overview']; } /** diff --git a/lib/public/views/Logs/Overview/LogsOverviewModel.js b/lib/public/views/Logs/Overview/LogsOverviewModel.js index eef752c91e..dbd1604e7c 100644 --- a/lib/public/views/Logs/Overview/LogsOverviewModel.js +++ b/lib/public/views/Logs/Overview/LogsOverviewModel.js @@ -75,6 +75,7 @@ export class LogsOverviewModel extends Observable { excludeAnonymous && this._filteringModel.get('author').update('!Anonymous'); this.reset(false); + this._filteringModel.pageIdentifiers = ['log-overview']; } /** diff --git a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js index a5d7a757ec..1f559eaf03 100644 --- a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js +++ b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js @@ -43,6 +43,7 @@ export class QcFlagTypesOverviewModel extends OverviewPageModel { }); this._filteringModel.visualChange$.bubbleTo(this); + this._filteringModel.pageIdentifiers = ['qc-flag-types-overview']; } /** diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index ea383bb95c..6945eeb1aa 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -119,6 +119,7 @@ export class RunsOverviewModel extends OverviewPageModel { model.appConfiguration$.observe(() => updateDebounceTime()); updateDebounceTime(); + this._filteringModel.pageIdentifiers = ['run-overview']; } /** diff --git a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js index 597a579d6b..b45b8a25df 100644 --- a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js +++ b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js @@ -74,6 +74,7 @@ export class RunsPerDataPassOverviewModel extends FixedPdpBeamTypeRunsOverviewMo this._discardAllQcFlagsActionState$.bubbleTo(this); this._item$.observe(() => this._fetchGaqSummaryForCurrentRuns()); + this._filteringModel.pageIdentifiers = ['runs-per-data-pass']; } /** diff --git a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js index 8c2332fb22..2cd931065b 100644 --- a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js +++ b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js @@ -55,6 +55,7 @@ export class RunsPerLhcPeriodOverviewModel extends FixedPdpBeamTypeRunsOverviewM this._tabbedPanelModel = new RunsPerLhcPeriodTabbedPanelModel(this._qcSummary$); this._tabbedPanelModel.bubbleTo(this); + this._filteringModel.pageIdentifiers = ['runs-per-lhc-period']; } /** diff --git a/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js b/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js index d5fbeb400f..0b78d2fe73 100644 --- a/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js +++ b/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js @@ -33,6 +33,7 @@ export class RunsPerSimulationPassOverviewModel extends FixedPdpBeamTypeRunsOver this._detectors$.bubbleTo(this); this._simulationPass$.bubbleTo(this); + this._filteringModel.pageIdentifiers = ['runs-per-simulation-pass']; } /** diff --git a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js index ad01197cef..8218bffbb1 100644 --- a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js +++ b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js @@ -38,6 +38,7 @@ export class AnchoredSimulationPassesOverviewModel extends OverviewPageModel { this._filteringModel.visualChange$.bubbleTo(this); this._dataPass = new ObservableData(RemoteData.notAsked()); + this._filteringModel.pageIdentifiers = ['anchored-simulation-passes-overview']; } /** diff --git a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js index fbd210982d..883e23c4ef 100644 --- a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js +++ b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js @@ -39,6 +39,7 @@ export class SimulationPassesPerLhcPeriodOverviewModel extends OverviewPageModel this._lhcPeriod.bubbleTo(this); this._lhcPeriodId = null; + this._filteringModel.pageIdentifiers = ['simulation-passes-per-lhc-period-overview']; } /** From 3a3fa99f298483bfa192d7e4a4f2e9c48ca6950f Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Wed, 22 Apr 2026 13:22:50 +0200 Subject: [PATCH 14/31] test: create tests for logsOverviewPage --- test/public/Filters/filtersToUrl.test.js | 77 ++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 test/public/Filters/filtersToUrl.test.js diff --git a/test/public/Filters/filtersToUrl.test.js b/test/public/Filters/filtersToUrl.test.js new file mode 100644 index 0000000000..ec647fd05a --- /dev/null +++ b/test/public/Filters/filtersToUrl.test.js @@ -0,0 +1,77 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE O2. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-o2.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +const { expect } = require('chai'); +const { + defaultBefore, + defaultAfter, + goToPage, + fillInput, + getPopoverSelector, + getPeriodInputsSelectors, + pressElement, + openFilteringPanel, +} = require('../defaults.js'); + +module.exports = () => { + let page; + let browser; + + before(async () => { + [page, browser] = await defaultBefore(); + }); + + const getQueryParameters = (page) => Object.fromEntries(new URL(page.url()).searchParams.entries()); + + it('Filters from LogsOverview should be set to the URL', async () => { + await goToPage(page, 'log-overview'); + const firstCheckboxId = 'tag-dropdown-option-DPG'; + const popoverTrigger = '.createdAt-filter .popover-trigger'; + + await page.waitForSelector(popoverTrigger); + await openFilteringPanel(page); + + const popOverSelector = await getPopoverSelector(await page.$(popoverTrigger)); + const { fromDateSelector, toDateSelector, fromTimeSelector, toTimeSelector } = getPeriodInputsSelectors(popOverSelector); + + await fillInput(page, '.title-textFilter', 'bogusbogusbogus', ['change']); + await fillInput(page, '#authorFilterText', 'Jane', ['change']); + await fillInput(page, '.content-textFilter', 'particle', ['change']); + await pressElement(page, '.tags-filter .dropdown-trigger'); + await pressElement(page, `#${firstCheckboxId}`, true); + await fillInput(page, '.environments-filter input', '8E4aZTjY', ['change']); + await fillInput(page, '.runNumbers-textFilter', '1,2', ['change']); + await fillInput(page, '.fillNumbers-textFilter', '1, 6', ['change']); + await fillInput(page, fromDateSelector, '2020-02-02', ['change']); + await fillInput(page, toDateSelector, '2020-02-02', ['change']); + await fillInput(page, fromTimeSelector, '11:00', ['change']); + await fillInput(page, toTimeSelector, '12:00', ['change']); + + const queryParameters = getQueryParameters(page); + expect(queryParameters).to.deep.equal({ + "page": "log-overview", + "filter[author]": "Jane", + "filter[title]": "bogusbogusbogus", + "filter[content]": "particle", + "filter[tags][values]": "DPG", + "filter[tags][operation]": "and", + "filter[runNumbers]": "1,2", + "filter[environmentIds]": "8E4aZTjY", + "filter[fillNumbers]": "1, 6", + "filter[created][from]": "1580641200000", + "filter[created][to]": "1580644800000" + }); + }); + + after(async () => await defaultAfter(page, browser)); +} From 4b7b1f178fc789132a73e3b6da43e4e3cdd88f7f Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Wed, 22 Apr 2026 14:28:43 +0200 Subject: [PATCH 15/31] test: create tests for logsOverviewPage --- test/public/Filters/filtersToUrl.test.js | 33 +++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/test/public/Filters/filtersToUrl.test.js b/test/public/Filters/filtersToUrl.test.js index ec647fd05a..88c015687a 100644 --- a/test/public/Filters/filtersToUrl.test.js +++ b/test/public/Filters/filtersToUrl.test.js @@ -33,7 +33,7 @@ module.exports = () => { const getQueryParameters = (page) => Object.fromEntries(new URL(page.url()).searchParams.entries()); - it('Filters from LogsOverview should be set to the URL', async () => { + it('should set filters from LogsOverview to the URL', async () => { await goToPage(page, 'log-overview'); const firstCheckboxId = 'tag-dropdown-option-DPG'; const popoverTrigger = '.createdAt-filter .popover-trigger'; @@ -73,5 +73,36 @@ module.exports = () => { }); }); + it('should set filters from EnvironmentsOverview to the URL', async () => { + await goToPage(page, 'env-overview'); + const popoverTrigger = '.createdAt-filter .popover-trigger'; + + await page.waitForSelector(popoverTrigger); + await openFilteringPanel(page); + + const createdAtPopoverSelector = await getPopoverSelector(await page.$(popoverTrigger)); + const periodInputsSelectors = getPeriodInputsSelectors(createdAtPopoverSelector); + + await fillInput(page, '.runs-filter input', '10', ['change']); + await fillInput(page, '.id-filter input', 'Dxi029djX, TDI59So3d', ['change']); + await pressElement(page, '#checkboxes-checkbox-DESTROYED'); + await fillInput(page, '.historyItems-filter input', 'C-R-D-X', ['change']); + await fillInput(page, periodInputsSelectors.fromDateSelector, '2019-08-09', ['change']); + await fillInput(page, periodInputsSelectors.toDateSelector, '2019-08-10', ['change']); + await fillInput(page, periodInputsSelectors.fromTimeSelector, '00:00', ['change']); + await fillInput(page, periodInputsSelectors.toTimeSelector, '23:59', ['change']); + + const queryParameters = getQueryParameters(page); + expect(queryParameters).to.deep.equal({ + "page": "env-overview", + "filter[created][from]": "1565308800000", + "filter[created][to]": "1565481540000", + "filter[runNumbers]": "10", + "filter[statusHistory]": "C-R-D-X", + "filter[currentStatus]": "DESTROYED", + "filter[ids]": "Dxi029djX, TDI59So3d" + }); + }); + after(async () => await defaultAfter(page, browser)); } From fd8d650a7138371909d1a2872a208617579dcba2 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Wed, 22 Apr 2026 14:48:31 +0200 Subject: [PATCH 16/31] feat: remove url params upon reset --- lib/public/components/Filters/common/FilteringModel.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/public/components/Filters/common/FilteringModel.js b/lib/public/components/Filters/common/FilteringModel.js index b837bbb3f0..c69a09feff 100644 --- a/lib/public/components/Filters/common/FilteringModel.js +++ b/lib/public/components/Filters/common/FilteringModel.js @@ -62,6 +62,10 @@ export class FilteringModel extends Observable { if (notify) { this.notify(); } + + const { params } = this._router; + delete params.filter; + this._router.go(buildUrl('?', params), false, true); } /** From 5a54dfe5689a883079e6b1d25ef78cc9e20fe075 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Wed, 22 Apr 2026 15:27:30 +0200 Subject: [PATCH 17/31] test: create tests for lhcFillsOverviewPage --- test/public/Filters/filtersToUrl.test.js | 61 ++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/test/public/Filters/filtersToUrl.test.js b/test/public/Filters/filtersToUrl.test.js index 88c015687a..3003e226a4 100644 --- a/test/public/Filters/filtersToUrl.test.js +++ b/test/public/Filters/filtersToUrl.test.js @@ -21,6 +21,7 @@ const { getPeriodInputsSelectors, pressElement, openFilteringPanel, + waitForTableLength, } = require('../defaults.js'); module.exports = () => { @@ -104,5 +105,65 @@ module.exports = () => { }); }); + it('should set filters from LhcFillsOverview to the URL', async () => { + await goToPage(page, 'lhc-fill-overview'); + await waitForTableLength(page, 5); + const filterSBDurationOperator= '#beam-duration-filter-operator'; + const filterSBDurationOperand= '#beam-duration-filter-operand'; + const filterRunDurationOperator= '#run-duration-filter-operator'; + const filterRunDurationOperand= '#run-duration-filter-operand'; + const filterBeamTypeP_Pb = '#beam-types-checkbox-p-Pb'; + const sbEndPopoverTrigger = '.stableBeamsEnd-filter .popover-trigger'; + const sbStartPopoverTrigger = '.stableBeamsStart-filter .popover-trigger'; + const sbStartPopOverSelector = await getPopoverSelector(await page.$(sbStartPopoverTrigger)); + const sbEndPopOverSelector = await getPopoverSelector(await page.$(sbEndPopoverTrigger)); + const filterSchemeNameInputField= '.fillingSchemeName-filter input'; + const { + fromDateSelector: sbStartFromDateSelector, + toDateSelector: sbStartToDateSelector, + fromTimeSelector: sbStartFromTimeSelector, + toTimeSelector: sbStartToTimeSelector + } = getPeriodInputsSelectors(sbStartPopOverSelector); + + const { + fromDateSelector: sbEndFromDateSelector, + toDateSelector: sbEndToDateSelector, + fromTimeSelector: sbEndFromTimeSelector, + toTimeSelector: sbEndToTimeSelector + } = getPeriodInputsSelectors(sbEndPopOverSelector); + + await openFilteringPanel(page); + await page.select(filterSBDurationOperator, '>='); + await fillInput(page, filterSBDurationOperand, '00:01:40', ['change']); + await page.select(filterRunDurationOperator, '<='); + await fillInput(page, filterRunDurationOperand, '00:00:00', ['change']); + await pressElement(page, filterBeamTypeP_Pb); + await fillInput(page, sbStartFromDateSelector, '2019-08-08', ['change']); + await fillInput(page, sbStartToDateSelector, '2019-08-08', ['change']); + await fillInput(page, sbStartFromTimeSelector, '10:00', ['change']); + await fillInput(page, sbStartToTimeSelector, '12:00', ['change']); + await fillInput(page, sbEndFromDateSelector, '2022-03-22', ['change']); + await fillInput(page, sbEndToDateSelector, '2022-03-22', ['change']); + await fillInput(page, sbEndFromTimeSelector, '01:00', ['change']); + await fillInput(page, sbEndToTimeSelector, '23:59', ['change']); + await fillInput(page, filterSchemeNameInputField, 'Single_12b_8_1024_8_2018', ['change']); + + const queryParameters = getQueryParameters(page); + expect(queryParameters).to.deep.equal({ + "page": "lhc-fill-overview", + "filter[beamDuration][operator]": ">=", + "filter[beamDuration][limit]": "00:01:40", + "filter[runDuration][operator]": "<=", + "filter[runDuration][limit]": "00:00:00", + "filter[hasStableBeams]": "true", + "filter[stableBeamsEnd][from]": "1647910800000", + "filter[stableBeamsEnd][to]": "1647993540000", + "filter[stableBeamsStart][from]": "1565258400000", + "filter[stableBeamsStart][to]": "1565265600000", + "filter[beamTypes]": "p-Pb", + "filter[schemeName]": "Single_12b_8_1024_8_2018" + }); + }); + after(async () => await defaultAfter(page, browser)); } From aa0fdbaf2da7103f5521deeb566825e71d06a9a1 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Wed, 22 Apr 2026 16:41:49 +0200 Subject: [PATCH 18/31] test: create tests for runsOverviewPage --- test/public/Filters/filtersToUrl.test.js | 84 ++++++++++++++++++++++-- test/public/index.js | 2 + 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/test/public/Filters/filtersToUrl.test.js b/test/public/Filters/filtersToUrl.test.js index 3003e226a4..4b90ad0ea2 100644 --- a/test/public/Filters/filtersToUrl.test.js +++ b/test/public/Filters/filtersToUrl.test.js @@ -108,9 +108,7 @@ module.exports = () => { it('should set filters from LhcFillsOverview to the URL', async () => { await goToPage(page, 'lhc-fill-overview'); await waitForTableLength(page, 5); - const filterSBDurationOperator= '#beam-duration-filter-operator'; const filterSBDurationOperand= '#beam-duration-filter-operand'; - const filterRunDurationOperator= '#run-duration-filter-operator'; const filterRunDurationOperand= '#run-duration-filter-operand'; const filterBeamTypeP_Pb = '#beam-types-checkbox-p-Pb'; const sbEndPopoverTrigger = '.stableBeamsEnd-filter .popover-trigger'; @@ -133,9 +131,7 @@ module.exports = () => { } = getPeriodInputsSelectors(sbEndPopOverSelector); await openFilteringPanel(page); - await page.select(filterSBDurationOperator, '>='); await fillInput(page, filterSBDurationOperand, '00:01:40', ['change']); - await page.select(filterRunDurationOperator, '<='); await fillInput(page, filterRunDurationOperand, '00:00:00', ['change']); await pressElement(page, filterBeamTypeP_Pb); await fillInput(page, sbStartFromDateSelector, '2019-08-08', ['change']); @@ -151,9 +147,9 @@ module.exports = () => { const queryParameters = getQueryParameters(page); expect(queryParameters).to.deep.equal({ "page": "lhc-fill-overview", - "filter[beamDuration][operator]": ">=", + "filter[beamDuration][operator]": "=", "filter[beamDuration][limit]": "00:01:40", - "filter[runDuration][operator]": "<=", + "filter[runDuration][operator]": "=", "filter[runDuration][limit]": "00:00:00", "filter[hasStableBeams]": "true", "filter[stableBeamsEnd][from]": "1647910800000", @@ -165,5 +161,81 @@ module.exports = () => { }); }); + it('should set filters from runsOverview to the URL', async () => { + await goToPage(page, 'lhc-fill-overview'); + await goToPage(page, 'run-overview'); + const o2StartPopoverSelector = await getPopoverSelector(await page.$('.timeO2Start-filter .popover-trigger')); + const { fromTimeSelector, toTimeSelector, fromDateSelector, toDateSelector } = getPeriodInputsSelectors(o2StartPopoverSelector); + const popoverSelector = await getPopoverSelector(await page.$('.aliceL3AndDipoleCurrent-filter .popover-trigger')); + + await openFilteringPanel(page); + await pressElement(page, '#detector-filter-dropdown-option-ITS', true); + await pressElement(page, '#tag-dropdown-option-FOOD', true); + await pressElement(page, '#run-definition-checkbox-PHYSICS', true); + await pressElement(page, '.timeO2Start-filter .popover-trigger'); + await fillInput(page, fromTimeSelector, '11:11', ['change']); + await fillInput(page, toTimeSelector, '14:00', ['change']); + await fillInput(page, fromDateSelector, '2021-02-03', ['change']); + await fillInput(page, toDateSelector, '2021-02-03', ['change']); + await fillInput(page, '#duration-operand', '1500', ['change']); + await pressElement(page, `${popoverSelector} .dropdown-option:last-child`, true); + await pressElement(page, '#checkboxes-checkbox-bad'); + await pressElement(page, '#triggerValue-checkbox-OFF'); + await fillInput(page, '#runOverviewFilter .runNumbers-textFilter', '101'); + await fillInput(page, '.fillNumbers-textFilter', '1, 3', ['change']); + await fillInput(page, '.environmentIds-textFilter', 'Dxi029djX, TDI59So3d', ['change']); + await pressElement(page, '#run-types-dropdown-option-2', true); + await pressElement(page, '#beam-mode-dropdown-option-NO\\ BEAM', true); + await fillInput(page, '#nDetectors-operand', '1', ['change']); + await fillInput(page, '#nFlps-operand', '10', ['change']); + await fillInput(page, '#nEpns-operand', '10', ['change']); + await fillInput(page, '#ctfFileCount-operand', '1', ['change']); + await fillInput(page, '#tfFileCount-operand', '1', ['change']); + await fillInput(page, '#otherFileCount-operand', '1', ['change']); + await pressElement(page, '#epnFilterRadioOFF', true); + await page.select('#eorCategories', 'DETECTORS'); + await page.select('#eorTitles', 'CPV'); + await fillInput(page, '#eorDescription', 'some', ['change']); + + const queryParameters = getQueryParameters(page); + expect(queryParameters).to.deep.equal({ + "page": "run-overview", + "filter[runNumbers]": "101", + "filter[detectors][operator]": "and", + "filter[detectors][values]": "ITS", + "filter[tags][values]": "FOOD", + "filter[tags][operation]": "and", + "filter[fillNumbers]": "1, 3", + "filter[o2start][from]": "1612350660000", + "filter[o2start][to]": "1612360800000", + "filter[definitions]": "PHYSICS", + "filter[runDuration][operator]": "=", + "filter[runDuration][limit]": "90000000", + "filter[environmentIds]": "Dxi029djX, TDI59So3d", + "filter[runTypes][]": "2", + "filter[beamModes][]": "NO BEAM", + "filter[runQualities]": "bad", + "filter[nDetectors][operator]": "=", + "filter[nDetectors][limit]": "1", + "filter[nEpns][operator]": "=", + "filter[nEpns][limit]": "10", + "filter[nFlps][operator]": "=", + "filter[nFlps][limit]": "10", + "filter[ctfFileCount][operator]": "=", + "filter[ctfFileCount][limit]": "1", + "filter[tfFileCount][operator]": "=", + "filter[tfFileCount][limit]": "1", + "filter[otherFileCount][operator]": "=", + "filter[otherFileCount][limit]": "1", + "filter[eorReason][category]": "DETECTORS", + "filter[eorReason][title]": "CPV", + "filter[eorReason][description]": "some", + "filter[magnets][l3]": "30003", + "filter[magnets][dipole]": "0", + "filter[epn]": "false", + "filter[triggerValues]": "OFF" + }); + }); + after(async () => await defaultAfter(page, browser)); } diff --git a/test/public/index.js b/test/public/index.js index 8ebdc23e68..c5513a26c8 100644 --- a/test/public/index.js +++ b/test/public/index.js @@ -27,9 +27,11 @@ const ComponentsSuite = require('./components'); const SimulationPassesSuite = require('./simulationPasses'); const QcFlagTypesSuite = require('./qcFlagTypes'); const QcFlagsSuite = require('./qcFlags'); +const FilterSuite = require('./Filters'); module.exports = () => { describe('Components', ComponentsSuite); + describe('Filters', FilterSuite) describe('LhcPeriods', LhcPeriodsSuite); describe('LhcFills', LhcFillsSuite); describe('Logs', LogsSuite); From f1218bd64377ee094b3f205983b90769ea385906 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Wed, 22 Apr 2026 16:43:01 +0200 Subject: [PATCH 19/31] fix: add semicolon --- test/public/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/public/index.js b/test/public/index.js index c5513a26c8..293d9a9e94 100644 --- a/test/public/index.js +++ b/test/public/index.js @@ -31,7 +31,7 @@ const FilterSuite = require('./Filters'); module.exports = () => { describe('Components', ComponentsSuite); - describe('Filters', FilterSuite) + describe('Filters', FilterSuite); describe('LhcPeriods', LhcPeriodsSuite); describe('LhcFills', LhcFillsSuite); describe('Logs', LogsSuite); From f8372f94deb3f2a2c80fbb06a3cf3c95edd42547 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Thu, 23 Apr 2026 09:09:25 +0200 Subject: [PATCH 20/31] test: create tests for flagtypes overview and lhcperiod overview --- .../Overview/LhcPeriodsOverviewModel.js | 2 + test/public/Filters/filtersToUrl.test.js | 57 ++++++++++++++++--- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js index b989b60488..dbf707abce 100644 --- a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js +++ b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js @@ -43,6 +43,8 @@ export class LhcPeriodsOverviewModel extends OverviewPageModel { this._pagination.silentlySetCurrentPage(1); this.load(); }); + + this._filteringModel.pageIdentifiers = ['lhc-period-overview']; } /** diff --git a/test/public/Filters/filtersToUrl.test.js b/test/public/Filters/filtersToUrl.test.js index 4b90ad0ea2..e48d9baa8d 100644 --- a/test/public/Filters/filtersToUrl.test.js +++ b/test/public/Filters/filtersToUrl.test.js @@ -108,9 +108,6 @@ module.exports = () => { it('should set filters from LhcFillsOverview to the URL', async () => { await goToPage(page, 'lhc-fill-overview'); await waitForTableLength(page, 5); - const filterSBDurationOperand= '#beam-duration-filter-operand'; - const filterRunDurationOperand= '#run-duration-filter-operand'; - const filterBeamTypeP_Pb = '#beam-types-checkbox-p-Pb'; const sbEndPopoverTrigger = '.stableBeamsEnd-filter .popover-trigger'; const sbStartPopoverTrigger = '.stableBeamsStart-filter .popover-trigger'; const sbStartPopOverSelector = await getPopoverSelector(await page.$(sbStartPopoverTrigger)); @@ -131,9 +128,9 @@ module.exports = () => { } = getPeriodInputsSelectors(sbEndPopOverSelector); await openFilteringPanel(page); - await fillInput(page, filterSBDurationOperand, '00:01:40', ['change']); - await fillInput(page, filterRunDurationOperand, '00:00:00', ['change']); - await pressElement(page, filterBeamTypeP_Pb); + await fillInput(page, '#beam-duration-filter-operand', '00:01:40', ['change']); + await fillInput(page, '#run-duration-filter-operand', '00:00:00', ['change']); + await pressElement(page, '#beam-types-checkbox-p-Pb'); await fillInput(page, sbStartFromDateSelector, '2019-08-08', ['change']); await fillInput(page, sbStartToDateSelector, '2019-08-08', ['change']); await fillInput(page, sbStartFromTimeSelector, '10:00', ['change']); @@ -162,7 +159,6 @@ module.exports = () => { }); it('should set filters from runsOverview to the URL', async () => { - await goToPage(page, 'lhc-fill-overview'); await goToPage(page, 'run-overview'); const o2StartPopoverSelector = await getPopoverSelector(await page.$('.timeO2Start-filter .popover-trigger')); const { fromTimeSelector, toTimeSelector, fromDateSelector, toDateSelector } = getPeriodInputsSelectors(o2StartPopoverSelector); @@ -237,5 +233,52 @@ module.exports = () => { }); }); + it('should set filters from lhcPriodOverview to the URL', async () => { + await goToPage(page, 'lhc-period-overview'); + + await fillInput(page, 'div.flex-row.items-baseline:nth-of-type(1) input[type=text]', 'LHC22a'); + await fillInput(page, 'div.flex-row.items-baseline:nth-of-type(2) input[type=text]', '2022'); + await fillInput(page, 'div.flex-row.items-baseline:nth-of-type(3) input[type=text]', 'PbPb'); + const queryParameters = getQueryParameters(page); + expect(queryParameters).to.deep.equal({ + "page": "lhc-period-overview", + "filter[names][]": "LHC22a", + "filter[years][]": "2022", + "filter[pdpBeamTypes][]": "PbPb" + }); + }); + + it('should set filters from qcFlagTypesOverview to the URL', async () => { + await goToPage(page, 'qc-flag-types-overview'); + + await fillInput(page, '.name-filter input[type=text]', 'bad'); + await fillInput(page, '.method-filter input[type=text]', 'bad'); + await pressElement(page, '#badFilterRadioBad', true); + + const queryParameters = getQueryParameters(page); + expect(queryParameters).to.deep.equal({ + "page": "qc-flag-types-overview", + "filter[names][]": "bad", + "filter[methods][]": "bad", + "filter[bad]": "true" + }); + }); + + it.skip('should set filters from runsPerLhcPeriodOverview to the URL', async () => { + await goToPage(page, 'runs-per-lhc-period'); + + await fillInput(page, '.name-filter input[type=text]', 'bad'); + await fillInput(page, '.method-filter input[type=text]', 'bad'); + await pressElement(page, '#badFilterRadioBad', true); + + const queryParameters = getQueryParameters(page); + expect(queryParameters).to.deep.equal({ + "page": "qc-flag-types-overview", + "filter[names][]": "bad", + "filter[methods][]": "bad", + "filter[bad]": "true" + }); + }); + after(async () => await defaultAfter(page, browser)); } From c9f979afeac350725e112f84a1fcda51e949fe1a Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Thu, 23 Apr 2026 09:44:54 +0200 Subject: [PATCH 21/31] test add runsPerLhcPeriodOverview tests --- test/public/Filters/filtersToUrl.test.js | 93 +++++++++++++++++++----- 1 file changed, 76 insertions(+), 17 deletions(-) diff --git a/test/public/Filters/filtersToUrl.test.js b/test/public/Filters/filtersToUrl.test.js index e48d9baa8d..27bb403bb1 100644 --- a/test/public/Filters/filtersToUrl.test.js +++ b/test/public/Filters/filtersToUrl.test.js @@ -160,21 +160,39 @@ module.exports = () => { it('should set filters from runsOverview to the URL', async () => { await goToPage(page, 'run-overview'); - const o2StartPopoverSelector = await getPopoverSelector(await page.$('.timeO2Start-filter .popover-trigger')); - const { fromTimeSelector, toTimeSelector, fromDateSelector, toDateSelector } = getPeriodInputsSelectors(o2StartPopoverSelector); - const popoverSelector = await getPopoverSelector(await page.$('.aliceL3AndDipoleCurrent-filter .popover-trigger')); + const dipolePopoverSelector = await getPopoverSelector(await page.$('.aliceL3AndDipoleCurrent-filter .popover-trigger')); + const startPopoverSelector = await getPopoverSelector(await page.$('.timeO2Start-filter .popover-trigger')); + const endPopoverSelector = await getPopoverSelector(await page.$('.timeO2End-filter .popover-trigger')); + + const { + fromDateSelector: startFromDateSelector, + toDateSelector: startToDateSelector, + fromTimeSelector: startFromTimeSelector, + toTimeSelector: startToTimeSelector + } = getPeriodInputsSelectors(startPopoverSelector); + + const { + fromDateSelector: endFromDateSelector, + toDateSelector: endToDateSelector, + fromTimeSelector: endFromTimeSelector, + toTimeSelector: endToTimeSelector + } = getPeriodInputsSelectors(endPopoverSelector); await openFilteringPanel(page); await pressElement(page, '#detector-filter-dropdown-option-ITS', true); await pressElement(page, '#tag-dropdown-option-FOOD', true); await pressElement(page, '#run-definition-checkbox-PHYSICS', true); await pressElement(page, '.timeO2Start-filter .popover-trigger'); - await fillInput(page, fromTimeSelector, '11:11', ['change']); - await fillInput(page, toTimeSelector, '14:00', ['change']); - await fillInput(page, fromDateSelector, '2021-02-03', ['change']); - await fillInput(page, toDateSelector, '2021-02-03', ['change']); + await fillInput(page, startFromTimeSelector, '11:11', ['change']); + await fillInput(page, startToTimeSelector, '14:00', ['change']); + await fillInput(page, startFromDateSelector, '2021-02-03', ['change']); + await fillInput(page, startToDateSelector, '2021-02-03', ['change']); + await fillInput(page, endFromTimeSelector, '11:11', ['change']); + await fillInput(page, endToTimeSelector, '14:00', ['change']); + await fillInput(page, endFromDateSelector, '2021-02-03', ['change']); + await fillInput(page, endToDateSelector, '2021-02-03', ['change']); await fillInput(page, '#duration-operand', '1500', ['change']); - await pressElement(page, `${popoverSelector} .dropdown-option:last-child`, true); + await pressElement(page, `${dipolePopoverSelector} .dropdown-option:last-child`, true); await pressElement(page, '#checkboxes-checkbox-bad'); await pressElement(page, '#triggerValue-checkbox-OFF'); await fillInput(page, '#runOverviewFilter .runNumbers-textFilter', '101'); @@ -204,6 +222,8 @@ module.exports = () => { "filter[fillNumbers]": "1, 3", "filter[o2start][from]": "1612350660000", "filter[o2start][to]": "1612360800000", + "filter[o2end][from]": "1612350660000", + "filter[o2end][to]": "1612360800000", "filter[definitions]": "PHYSICS", "filter[runDuration][operator]": "=", "filter[runDuration][limit]": "90000000", @@ -264,19 +284,58 @@ module.exports = () => { }); }); - it.skip('should set filters from runsPerLhcPeriodOverview to the URL', async () => { - await goToPage(page, 'runs-per-lhc-period'); + it('should set filters from runsPerLhcPeriodOverview to the URL', async () => { + await goToPage(page, 'runs-per-lhc-period', { queryParameters: { lhcPeriodId: 2 }}); + const startPopoverSelector = await getPopoverSelector(await page.$('.timeO2Start-filter .popover-trigger')); + const endPopoverSelector = await getPopoverSelector(await page.$('.timeO2End-filter .popover-trigger')); + const dipolePopoverSelector = await getPopoverSelector(await page.$('.aliceL3AndDipoleCurrent-filter .popover-trigger')); + + const { + fromDateSelector: startFromDateSelector, + toDateSelector: startToDateSelector, + fromTimeSelector: startFromTimeSelector, + toTimeSelector: startToTimeSelector + } = getPeriodInputsSelectors(startPopoverSelector); + + const { + fromDateSelector: endFromDateSelector, + toDateSelector: endToDateSelector, + fromTimeSelector: endFromTimeSelector, + toTimeSelector: endToTimeSelector + } = getPeriodInputsSelectors(endPopoverSelector); + + await fillInput(page, '#inelasticInteractionRateAvg-operand', '100000', ['change']); + await fillInput(page, '#muInelasticInteractionRate-operand', '100000', ['change']); + await fillInput(page, '#runOverviewFilter .runNumbers-textFilter', '101'); + await fillInput(page, '.fillNumbers-textFilter', '1, 3', ['change']); + + await pressElement(page, `${dipolePopoverSelector} .dropdown-option:last-child`, true); + await fillInput(page, startFromTimeSelector, '11:11', ['change']); + await fillInput(page, startToTimeSelector, '14:00', ['change']); + await fillInput(page, startFromDateSelector, '2021-02-03', ['change']); + await fillInput(page, startToDateSelector, '2021-02-03', ['change']); + await fillInput(page, endFromTimeSelector, '11:11', ['change']); + await fillInput(page, endToTimeSelector, '14:00', ['change']); + await fillInput(page, endFromDateSelector, '2021-02-03', ['change']); + await fillInput(page, endToDateSelector, '2021-02-03', ['change']); - await fillInput(page, '.name-filter input[type=text]', 'bad'); - await fillInput(page, '.method-filter input[type=text]', 'bad'); - await pressElement(page, '#badFilterRadioBad', true); const queryParameters = getQueryParameters(page); expect(queryParameters).to.deep.equal({ - "page": "qc-flag-types-overview", - "filter[names][]": "bad", - "filter[methods][]": "bad", - "filter[bad]": "true" + "page": "runs-per-lhc-period", + "lhcPeriodId": "2", + "filter[runNumbers]": "101", + "filter[fillNumbers]": "1, 3", + "filter[o2end][from]": "1612350660000", + "filter[o2end][to]": "1612360800000", + "filter[o2start][from]": "1612350660000", + "filter[o2start][to]": "1612360800000", + "filter[magnets][l3]": "30003", + "filter[magnets][dipole]": "0", + "filter[muInelasticInteractionRate][operator]": "=", + "filter[muInelasticInteractionRate][limit]": "100000", + "filter[inelasticInteractionRateAvg][operator]": "=", + "filter[inelasticInteractionRateAvg][limit]": "100000" }); }); From 3045d5acb36ac8b194a8b02fe51803fd0710cd1e Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Thu, 23 Apr 2026 09:52:36 +0200 Subject: [PATCH 22/31] test add DataPassesPerLhcPeriodOverview tests --- .../DataPassesPerLhcPeriodOverviewModel.js | 1 + test/public/Filters/filtersToUrl.test.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewModel.js b/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewModel.js index ee7f580ef7..57c4e122d9 100644 --- a/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewModel.js +++ b/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewModel.js @@ -24,6 +24,7 @@ export class DataPassesPerLhcPeriodOverviewModel extends DataPassesOverviewModel constructor(router) { super(router); this._lhcPeriodId = null; + this._filteringModel.pageIdentifiers = ['data-passes-per-lhc-period-overview']; } /** diff --git a/test/public/Filters/filtersToUrl.test.js b/test/public/Filters/filtersToUrl.test.js index 27bb403bb1..b6216849f3 100644 --- a/test/public/Filters/filtersToUrl.test.js +++ b/test/public/Filters/filtersToUrl.test.js @@ -339,5 +339,21 @@ module.exports = () => { }); }); + it('should set filters from DataPassesPerLhcPeriodOverview to the URL', async () => { + await goToPage(page, 'data-passes-per-lhc-period-overview', { queryParameters: { lhcPeriodId: 2 }}); + + await fillInput(page, 'div.flex-row.items-baseline:nth-of-type(1) input[type=text]', 'LHC22b_apass1', ['change']); + await pressElement(page, '#checkboxes-checkbox-test', true); + + + const queryParameters = getQueryParameters(page); + expect(queryParameters).to.deep.equal({ + "page": "data-passes-per-lhc-period-overview", + "lhcPeriodId": "2", + "filter[names][]": "LHC22b_apass1", + "filter[include][byName]": "test" + }); + }); + after(async () => await defaultAfter(page, browser)); } From c11492b01f65f0928a5648df9a170465f6379fa2 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Thu, 23 Apr 2026 09:57:31 +0200 Subject: [PATCH 23/31] test add anchoredSimulationPassOverview tests --- test/public/Filters/filtersToUrl.test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/public/Filters/filtersToUrl.test.js b/test/public/Filters/filtersToUrl.test.js index b6216849f3..a612c59602 100644 --- a/test/public/Filters/filtersToUrl.test.js +++ b/test/public/Filters/filtersToUrl.test.js @@ -355,5 +355,20 @@ module.exports = () => { }); }); + it('should set filters from AnchoredSimulationPassesOverview to the URL', async () => { + await goToPage(page, 'anchored-simulation-passes-overview', { queryParameters: { dataPassId: 1 }}); + + await fillInput(page, 'name-filter input', 'LHC23k6c', ['input']); + await pressElement(page, '#checkboxes-checkbox-test', true); + + + const queryParameters = getQueryParameters(page); + expect(queryParameters).to.deep.equal({ + "page": "anchored-simulation-passes-overview", + "dataPassId": "1", + "filter[names][]": "LHC23k6c" + }); + }); + after(async () => await defaultAfter(page, browser)); } From e2ac21788e5f2cdecc191aa6e772b899769041ec Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Thu, 23 Apr 2026 10:00:56 +0200 Subject: [PATCH 24/31] test add DataPassesPerSimulationPassOverview tests --- test/public/Filters/filtersToUrl.test.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/public/Filters/filtersToUrl.test.js b/test/public/Filters/filtersToUrl.test.js index a612c59602..55b8fb7727 100644 --- a/test/public/Filters/filtersToUrl.test.js +++ b/test/public/Filters/filtersToUrl.test.js @@ -355,10 +355,25 @@ module.exports = () => { }); }); + it('should set filters from DataPassesPerSimulationPassOverview to the URL', async () => { + await goToPage(page, 'data-passes-per-simulation-pass-overview', { queryParameters: { simulationPassId: 1 }}); + + await fillInput(page, 'div.flex-row.items-baseline:nth-of-type(1) input[type=text]', 'LHC22b_apass1', ['change']); + await pressElement(page, '#checkboxes-checkbox-test', true); + + const queryParameters = getQueryParameters(page); + expect(queryParameters).to.deep.equal({ + "page": "data-passes-per-simulation-pass-overview", + "simulationPassId": "1", + "filter[names][]": "LHC22b_apass1", + "filter[include][byName]": "test" + }); + }); + it('should set filters from AnchoredSimulationPassesOverview to the URL', async () => { await goToPage(page, 'anchored-simulation-passes-overview', { queryParameters: { dataPassId: 1 }}); - await fillInput(page, 'name-filter input', 'LHC23k6c', ['input']); + await fillInput(page, '.name-filter input', 'LHC23k6c', ['input']); await pressElement(page, '#checkboxes-checkbox-test', true); From fda76f8a81da602ba725f84b549c19458c5c5c6b Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Thu, 23 Apr 2026 11:25:44 +0200 Subject: [PATCH 25/31] add runs per simulationpass and datapass tests --- test/public/Filters/filtersToUrl.test.js | 145 ++++++++++++++++++++++- 1 file changed, 143 insertions(+), 2 deletions(-) diff --git a/test/public/Filters/filtersToUrl.test.js b/test/public/Filters/filtersToUrl.test.js index 55b8fb7727..44d2c2b3ac 100644 --- a/test/public/Filters/filtersToUrl.test.js +++ b/test/public/Filters/filtersToUrl.test.js @@ -22,6 +22,7 @@ const { pressElement, openFilteringPanel, waitForTableLength, + takeScreenshot, } = require('../defaults.js'); module.exports = () => { @@ -374,8 +375,6 @@ module.exports = () => { await goToPage(page, 'anchored-simulation-passes-overview', { queryParameters: { dataPassId: 1 }}); await fillInput(page, '.name-filter input', 'LHC23k6c', ['input']); - await pressElement(page, '#checkboxes-checkbox-test', true); - const queryParameters = getQueryParameters(page); expect(queryParameters).to.deep.equal({ @@ -385,5 +384,147 @@ module.exports = () => { }); }); + it('should set filters from RunsPerSimulationPass to the URL', async () => { + await goToPage(page, 'runs-per-simulation-pass', { queryParameters: { simulationPassId: 2 }}); + + const dipolePopoverSelector = await getPopoverSelector(await page.$('.aliceL3AndDipoleCurrent-filter .popover-trigger')); + const startPopoverSelector = await getPopoverSelector(await page.$('.timeO2Start-filter .popover-trigger')); + const endPopoverSelector = await getPopoverSelector(await page.$('.timeO2End-filter .popover-trigger')); + + const { + fromDateSelector: startFromDateSelector, + toDateSelector: startToDateSelector, + fromTimeSelector: startFromTimeSelector, + toTimeSelector: startToTimeSelector + } = getPeriodInputsSelectors(startPopoverSelector); + + const { + fromDateSelector: endFromDateSelector, + toDateSelector: endToDateSelector, + fromTimeSelector: endFromTimeSelector, + toTimeSelector: endToTimeSelector + } = getPeriodInputsSelectors(endPopoverSelector); + + await openFilteringPanel(page); + await pressElement(page, '.timeO2Start-filter .popover-trigger'); + await fillInput(page, startFromTimeSelector, '11:11', ['change']); + await fillInput(page, startToTimeSelector, '14:00', ['change']); + await fillInput(page, startFromDateSelector, '2021-02-03', ['change']); + await fillInput(page, startToDateSelector, '2021-02-03', ['change']); + await fillInput(page, endFromTimeSelector, '11:11', ['change']); + await fillInput(page, endToTimeSelector, '14:00', ['change']); + await fillInput(page, endFromDateSelector, '2021-02-03', ['change']); + await fillInput(page, endToDateSelector, '2021-02-03', ['change']); + await fillInput(page, '.inelasticInteractionRateAtMid-filter input', '1', ['change']); + await fillInput(page, '.inelasticInteractionRateAtEnd-filter input', '1', ['change']); + await fillInput(page, '.inelasticInteractionRateAtStart-filter input', '1', ['change']); + await pressElement(page, `${dipolePopoverSelector} .dropdown-option:last-child`, true); + await pressElement(page, '#mcReproducibleAsNotBadToggle', true); + + // These two are detectorQCNotBadFraction[_id] filters. There are a dozen more, but they are all identical hence why only these were tested + await fillInput(page, '.QC-SPECIFIC-filter input', '1', ['change']); + await fillInput(page, '.ACO-filter input', '1', ['change']); + + const queryParameters = getQueryParameters(page); + expect(queryParameters).to.deep.equal({ + "page": "runs-per-simulation-pass", + "simulationPassId": "2", + "filter[o2end][from]": "1612350660000", + "filter[o2start][from]": "1612350660000", + "filter[o2end][to]": "1612360800000", + "filter[o2start][to]": "1612360800000", + "filter[magnets][l3]": "30003", + "filter[magnets][dipole]": "0", + "filter[inelasticInteractionRateAtStart][operator]": "=", + "filter[inelasticInteractionRateAtStart][limit]": "1", + "filter[inelasticInteractionRateAtMid][operator]": "=", + "filter[inelasticInteractionRateAtMid][limit]": "1", + "filter[inelasticInteractionRateAtEnd][operator]": "=", + "filter[inelasticInteractionRateAtEnd][limit]": "1", + "filter[detectorsQcNotBadFraction][mcReproducibleAsNotBad]": "true", + "filter[detectorsQcNotBadFraction][_20][operator]": "=", + "filter[detectorsQcNotBadFraction][_20][limit]": "0.01", + "filter[detectorsQcNotBadFraction][_17][operator]": "=", + "filter[detectorsQcNotBadFraction][_17][limit]": "0.01" + }); + }); + + it('should set filters from RunsPerSimulationPass to the URL', async () => { + await goToPage(page, 'runs-per-data-pass', { queryParameters: { dataPassId: 1 }}); + + const dipolePopoverSelector = await getPopoverSelector(await page.$('.aliceL3AndDipoleCurrent-filter .popover-trigger')); + const startPopoverSelector = await getPopoverSelector(await page.$('.timeO2Start-filter .popover-trigger')); + const endPopoverSelector = await getPopoverSelector(await page.$('.timeO2End-filter .popover-trigger')); + + const { + fromDateSelector: startFromDateSelector, + toDateSelector: startToDateSelector, + fromTimeSelector: startFromTimeSelector, + toTimeSelector: startToTimeSelector + } = getPeriodInputsSelectors(startPopoverSelector); + + const { + fromDateSelector: endFromDateSelector, + toDateSelector: endToDateSelector, + fromTimeSelector: endFromTimeSelector, + toTimeSelector: endToTimeSelector + } = getPeriodInputsSelectors(endPopoverSelector); + + await openFilteringPanel(page); + await pressElement(page, '#detector-filter-dropdown-option-ITS', true); + await pressElement(page, '#tag-dropdown-option-FOOD', true); + await pressElement(page, '.timeO2Start-filter .popover-trigger'); + await fillInput(page, startFromTimeSelector, '11:11', ['change']); + await fillInput(page, startToTimeSelector, '14:00', ['change']); + await fillInput(page, startFromDateSelector, '2021-02-03', ['change']); + await fillInput(page, startToDateSelector, '2021-02-03', ['change']); + await fillInput(page, endFromTimeSelector, '11:11', ['change']); + await fillInput(page, endToTimeSelector, '14:00', ['change']); + await fillInput(page, endFromDateSelector, '2021-02-03', ['change']); + await fillInput(page, endToDateSelector, '2021-02-03', ['change']); + await fillInput(page, '#duration-operand', '1500', ['change']); + await fillInput(page, '.muInelasticInteractionRate-filter input', '1', ['change']); + await fillInput(page, '.inelasticInteractionRateAvg-filter input', '1', ['change']); + await fillInput(page, '.globalAggregatedQuality-filter input', '1', ['change']); + + await pressElement(page, `${dipolePopoverSelector} .dropdown-option:last-child`, true); + await pressElement(page, '#mcReproducibleAsNotBadToggle', true); + + // These two are detectorQCNotBadFraction[_id] filters. There are a dozen more, but they are all identical hence why only these were tested + await fillInput(page, '.QC-SPECIFIC-filter input', '1', ['change']); + await fillInput(page, '.ACO-filter input', '1', ['change']); + + const queryParameters = getQueryParameters(page); + expect(queryParameters).to.deep.equal({ + "page": "runs-per-data-pass", + "dataPassId": "1", + "filter[detectors][operator]": "and", + "filter[detectors][values]": "ITS", + "filter[tags][values]": "FOOD", + "filter[tags][operation]": "and", + "filter[o2end][from]": "1612350660000", + "filter[o2end][to]": "1612360800000", + "filter[o2start][from]": "1612350660000", + "filter[o2start][to]": "1612360800000", + "filter[runDuration][limit]": "90000000", + "filter[runDuration][operator]": "=", + "filter[runDuration][limit]": "90000000", + "filter[magnets][l3]": "30003", + "filter[magnets][dipole]": "0", + "filter[muInelasticInteractionRate][operator]": "=", + "filter[muInelasticInteractionRate][limit]": "1", + "filter[inelasticInteractionRateAvg][operator]": "=", + "filter[inelasticInteractionRateAvg][limit]": "1", + "filter[detectorsQcNotBadFraction][mcReproducibleAsNotBad]": "true", + "filter[detectorsQcNotBadFraction][_20][operator]": "=", + "filter[detectorsQcNotBadFraction][_20][limit]": "0.01", + "filter[detectorsQcNotBadFraction][_17][operator]": "=", + "filter[detectorsQcNotBadFraction][_17][limit]": "0.01", + "filter[gaq][notBadFraction][operator]": "=", + "filter[gaq][notBadFraction][limit]": "0.01", + "filter[gaq][mcReproducibleAsNotBad]": "true" + }); + }); + after(async () => await defaultAfter(page, browser)); } From 88062835a97fea729e9f1dda1ee467efad9ad5d7 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Thu, 23 Apr 2026 11:30:25 +0200 Subject: [PATCH 26/31] add filters index file --- test/public/Filters/index.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 test/public/Filters/index.js diff --git a/test/public/Filters/index.js b/test/public/Filters/index.js new file mode 100644 index 0000000000..e5146bb7fc --- /dev/null +++ b/test/public/Filters/index.js @@ -0,0 +1,18 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE O2. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-o2.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +const ToUrlSuite = require('./filtersToUrl.test.js'); + +module.exports = () => { + describe('Filters to URL', ToUrlSuite); +}; From 78923f2cd6ee82e8b030c7cfb4da96c0783fa2f2 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Thu, 23 Apr 2026 12:07:50 +0200 Subject: [PATCH 27/31] chore: remove router from the observable constructor in DatapassesModel --- lib/public/views/DataPasses/DataPassesModel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/views/DataPasses/DataPassesModel.js b/lib/public/views/DataPasses/DataPassesModel.js index 5af74e48be..48793bf3df 100644 --- a/lib/public/views/DataPasses/DataPassesModel.js +++ b/lib/public/views/DataPasses/DataPassesModel.js @@ -24,7 +24,7 @@ export class DataPassesModel extends Observable { * @param {QueryRouter} router router that controls the application's page navigation */ constructor(router) { - super(router); + super(); this._perLhcPeriodOverviewModel = new DataPassesPerLhcPeriodOverviewModel(router); this._perLhcPeriodOverviewModel.bubbleTo(this); From edbcbc3b879176d0b42b79747f0c982a677b3d73 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Thu, 23 Apr 2026 14:03:48 +0200 Subject: [PATCH 28/31] fix lint issues --- test/public/Filters/filtersToUrl.test.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/public/Filters/filtersToUrl.test.js b/test/public/Filters/filtersToUrl.test.js index 44d2c2b3ac..dc0dc24356 100644 --- a/test/public/Filters/filtersToUrl.test.js +++ b/test/public/Filters/filtersToUrl.test.js @@ -22,7 +22,6 @@ const { pressElement, openFilteringPanel, waitForTableLength, - takeScreenshot, } = require('../defaults.js'); module.exports = () => { @@ -508,7 +507,6 @@ module.exports = () => { "filter[o2start][to]": "1612360800000", "filter[runDuration][limit]": "90000000", "filter[runDuration][operator]": "=", - "filter[runDuration][limit]": "90000000", "filter[magnets][l3]": "30003", "filter[magnets][dipole]": "0", "filter[muInelasticInteractionRate][operator]": "=", From cbd335ddfca8ef9710ec83d22c27c4f92523ee47 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Thu, 23 Apr 2026 16:31:33 +0200 Subject: [PATCH 29/31] add clearUrl parameter to filterIngModel --- .../components/Filters/common/FilteringModel.js | 13 ++++++++----- .../views/DataPasses/DataPassesOverviewModel.js | 8 +++++--- .../Overview/EnvironmentOverviewModel.js | 5 +++-- .../LhcFills/Overview/LhcFillsOverviewModel.js | 5 +++-- .../Overview/QcFlagTypesOverviewModel.js | 8 +++++--- lib/public/views/Runs/Overview/RunsOverviewModel.js | 5 +++-- .../RunPerDataPass/RunsPerDataPassOverviewModel.js | 4 ++-- .../AnchoredSimulationPassesOverviewModel.js | 9 +++++---- .../SimulationPassesPerLhcPeriodOverviewModel.js | 9 +++++---- .../lhcPeriods/Overview/LhcPeriodsOverviewModel.js | 8 +++++--- 10 files changed, 44 insertions(+), 30 deletions(-) diff --git a/lib/public/components/Filters/common/FilteringModel.js b/lib/public/components/Filters/common/FilteringModel.js index c69a09feff..d55253ba44 100644 --- a/lib/public/components/Filters/common/FilteringModel.js +++ b/lib/public/components/Filters/common/FilteringModel.js @@ -52,9 +52,10 @@ export class FilteringModel extends Observable { * Reset the filters * * @param {boolean} [notify=false] if true the model notifies its observers + * @param {boolean} [clearUrl=false] if true filters will be removed from the url * @return {void} */ - reset(notify = false) { + reset(notify = false, clearUrl = false) { for (const model of this._filterModels) { model.reset(); } @@ -62,10 +63,12 @@ export class FilteringModel extends Observable { if (notify) { this.notify(); } - - const { params } = this._router; - delete params.filter; - this._router.go(buildUrl('?', params), false, true); + + if (clearUrl) { + const { params } = this._router; + delete params.filter; + this._router.go(buildUrl('?', params), false, true); + } } /** diff --git a/lib/public/views/DataPasses/DataPassesOverviewModel.js b/lib/public/views/DataPasses/DataPassesOverviewModel.js index d32b7eef67..73d7292b37 100644 --- a/lib/public/views/DataPasses/DataPassesOverviewModel.js +++ b/lib/public/views/DataPasses/DataPassesOverviewModel.js @@ -55,10 +55,12 @@ export class DataPassesOverviewModel extends OverviewPageModel { /** * Reset this model to its default * - * @returns {void} + * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} [clearUrl=false] if true filters will be removed from the url + * @return {void} */ - reset() { - this._filteringModel.reset(); + reset(_fetch = true, clearUrl = false) { + this._filteringModel.reset(false, clearUrl); super.reset(); } diff --git a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js index f898a1d989..1b9676eeae 100644 --- a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js +++ b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js @@ -91,10 +91,11 @@ export class EnvironmentOverviewModel extends OverviewPageModel { /** * Reset all filtering models * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} [clearUrl=false] if true filters will be removed from the url * @return {void} */ - resetFiltering(fetch = true) { - this._filteringModel.reset(); + resetFiltering(fetch = true, clearUrl = false) { + this._filteringModel.reset(false, clearUrl); if (fetch) { this._applyFilters(true); diff --git a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js index de58e1324d..7b415190b9 100644 --- a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js +++ b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js @@ -90,10 +90,11 @@ export class LhcFillsOverviewModel extends OverviewPageModel { /** * Reset all filtering models * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} [clearUrl=false] if true filters will be removed from the url * @return {void} */ - resetFiltering(fetch = true) { - this._filteringModel.reset(); + resetFiltering(fetch = true, clearUrl = false) { + this._filteringModel.reset(false, clearUrl); if (fetch) { this._applyFilters(); diff --git a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js index 1f559eaf03..38c7d0707f 100644 --- a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js +++ b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js @@ -74,10 +74,12 @@ export class QcFlagTypesOverviewModel extends OverviewPageModel { /** * Reset this model to its default * - * @returns {void} + * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} [clearUrl=false] if true filters will be removed from the url + * @return {void} */ - reset() { - this._filteringModel.reset(); + reset(_fetch = true, clearUrl = false) { + this._filteringModel.reset(false, clearUrl); super.reset(); } } diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index 6945eeb1aa..5a80a0ca9c 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -151,10 +151,11 @@ export class RunsOverviewModel extends OverviewPageModel { /** * Reset all filtering models * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} [clearUrl=false] if true filters will be removed from the url * @return {void} */ - resetFiltering(fetch = true) { - this._filteringModel.reset(); + resetFiltering(fetch = true, clearUrl = false) { + this._filteringModel.reset(false, clearUrl); if (fetch) { this._applyFilters(true); diff --git a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js index b45b8a25df..7f29e5458a 100644 --- a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js +++ b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js @@ -149,8 +149,8 @@ export class RunsPerDataPassOverviewModel extends FixedPdpBeamTypeRunsOverviewMo /** * @inheritdoc */ - resetFiltering(fetch = true) { - super.resetFiltering(fetch); + resetFiltering(fetch = true, clearUrl = false) { + super.resetFiltering(fetch, clearUrl); } /** diff --git a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js index 8218bffbb1..e5447f8a0d 100644 --- a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js +++ b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js @@ -74,13 +74,14 @@ export class AnchoredSimulationPassesOverviewModel extends OverviewPageModel { /** * Reset this model to its default * - * @returns {void} + * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} [clearUrl=false] if true filters will be removed from the url + * @return {void} */ - reset() { - this._filteringModel.reset(); + reset(_fetch = true, clearUrl = false) { + this._filteringModel.reset(false, clearUrl); super.reset(); } - /** * Return the model managing all filters * diff --git a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js index 883e23c4ef..6c5123056f 100644 --- a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js +++ b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js @@ -84,13 +84,14 @@ export class SimulationPassesPerLhcPeriodOverviewModel extends OverviewPageModel /** * Reset this model to its default * - * @returns {void} + * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} [clearUrl=false] if true filters will be removed from the url + * @return {void} */ - reset() { - this._filteringModel.reset(); + reset(_fetch = true, clearUrl = false) { + this._filteringModel.reset(false, clearUrl); super.reset(); } - /** * Set id of LHC Period which simulation passes are to be fetched * @param {number} lhcPeriodId id of LHC Period diff --git a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js index dbf707abce..c35cfb1dff 100644 --- a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js +++ b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js @@ -82,11 +82,13 @@ export class LhcPeriodsOverviewModel extends OverviewPageModel { /** * Reset this model to its default * - * @returns {void} + * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} [clearUrl=false] if true filters will be removed from the url + * @return {void} */ - reset() { + reset(_fetch = true, clearUrl = false) { super.reset(); - this._filteringModel.reset(); + this._filteringModel.reset(false, clearUrl); } /** From 1ec54b2098bc3cead430f8f40d990395c7731cfd Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Thu, 23 Apr 2026 16:48:59 +0200 Subject: [PATCH 30/31] test: upgrade the fillInput function --- test/public/defaults.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/test/public/defaults.js b/test/public/defaults.js index 0523d8462b..ed6726bece 100644 --- a/test/public/defaults.js +++ b/test/public/defaults.js @@ -667,14 +667,24 @@ module.exports.checkColumnBalloon = async (page, rowIndex, columnIndex) => { * @return {Promise} resolves once the value has been typed */ module.exports.fillInput = async (page, inputSelector, value, events = ['input']) => { - await page.waitForSelector(inputSelector); - await page.evaluate((inputSelector, value, events) => { + await page.waitForFunction((inputSelector, value, events) => { const element = document.querySelector(inputSelector); + + if (!element) { + return false; + } + element.value = value; + for (const eventKey of events) { element.dispatchEvent(new Event(eventKey, { bubbles: true })); } - }, inputSelector, value, events); + + return true; + }, + {}, + inputSelector, value, events + ); }; /** From 1a6fe588ba6cc8848e8f43d0708f78580480d963 Mon Sep 17 00:00:00 2001 From: NarrowsProjects Date: Thu, 23 Apr 2026 16:54:06 +0200 Subject: [PATCH 31/31] chore: fix eslint errors --- lib/public/components/Filters/common/FilteringModel.js | 2 +- lib/public/views/DataPasses/DataPassesOverviewModel.js | 2 +- .../views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js | 2 +- .../AnchoredOverview/AnchoredSimulationPassesOverviewModel.js | 3 ++- .../SimulationPassesPerLhcPeriodOverviewModel.js | 3 ++- .../views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/public/components/Filters/common/FilteringModel.js b/lib/public/components/Filters/common/FilteringModel.js index d55253ba44..8f6f70decc 100644 --- a/lib/public/components/Filters/common/FilteringModel.js +++ b/lib/public/components/Filters/common/FilteringModel.js @@ -63,7 +63,7 @@ export class FilteringModel extends Observable { if (notify) { this.notify(); } - + if (clearUrl) { const { params } = this._router; delete params.filter; diff --git a/lib/public/views/DataPasses/DataPassesOverviewModel.js b/lib/public/views/DataPasses/DataPassesOverviewModel.js index 73d7292b37..a5add50bc8 100644 --- a/lib/public/views/DataPasses/DataPassesOverviewModel.js +++ b/lib/public/views/DataPasses/DataPassesOverviewModel.js @@ -55,7 +55,7 @@ export class DataPassesOverviewModel extends OverviewPageModel { /** * Reset this model to its default * - * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} _fetch Whether to refetch all data after filters have been reset * @param {boolean} [clearUrl=false] if true filters will be removed from the url * @return {void} */ diff --git a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js index 38c7d0707f..0b19b00366 100644 --- a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js +++ b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js @@ -74,7 +74,7 @@ export class QcFlagTypesOverviewModel extends OverviewPageModel { /** * Reset this model to its default * - * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} _fetch Whether to refetch all data after filters have been reset * @param {boolean} [clearUrl=false] if true filters will be removed from the url * @return {void} */ diff --git a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js index e5447f8a0d..88e434b192 100644 --- a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js +++ b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js @@ -74,7 +74,7 @@ export class AnchoredSimulationPassesOverviewModel extends OverviewPageModel { /** * Reset this model to its default * - * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} _fetch Whether to refetch all data after filters have been reset * @param {boolean} [clearUrl=false] if true filters will be removed from the url * @return {void} */ @@ -82,6 +82,7 @@ export class AnchoredSimulationPassesOverviewModel extends OverviewPageModel { this._filteringModel.reset(false, clearUrl); super.reset(); } + /** * Return the model managing all filters * diff --git a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js index 6c5123056f..2d84730a19 100644 --- a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js +++ b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js @@ -84,7 +84,7 @@ export class SimulationPassesPerLhcPeriodOverviewModel extends OverviewPageModel /** * Reset this model to its default * - * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} _fetch Whether to refetch all data after filters have been reset * @param {boolean} [clearUrl=false] if true filters will be removed from the url * @return {void} */ @@ -92,6 +92,7 @@ export class SimulationPassesPerLhcPeriodOverviewModel extends OverviewPageModel this._filteringModel.reset(false, clearUrl); super.reset(); } + /** * Set id of LHC Period which simulation passes are to be fetched * @param {number} lhcPeriodId id of LHC Period diff --git a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js index c35cfb1dff..c729d89931 100644 --- a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js +++ b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js @@ -82,7 +82,7 @@ export class LhcPeriodsOverviewModel extends OverviewPageModel { /** * Reset this model to its default * - * @param {boolean} fetch Whether to refetch all data after filters have been reset + * @param {boolean} _fetch Whether to refetch all data after filters have been reset * @param {boolean} [clearUrl=false] if true filters will be removed from the url * @return {void} */