diff --git a/lib/public/components/Filters/RunsFilter/MultiCompositionFilterModel.js b/lib/public/components/Filters/RunsFilter/MultiCompositionFilterModel.js index 8a4cc68c66..80aafc8644 100644 --- a/lib/public/components/Filters/RunsFilter/MultiCompositionFilterModel.js +++ b/lib/public/components/Filters/RunsFilter/MultiCompositionFilterModel.js @@ -75,6 +75,13 @@ export class MultiCompositionFilterModel extends FilterModel { return Object.values(this._filters).every((filter) => filter.isEmpty); } + /** + * @inheritDoc + */ + get isInactive() { + return Object.values(this._filters).every((filter) => filter.isInactive); + } + /** * @inheritDoc */ diff --git a/lib/public/components/Filters/RunsFilter/RunDefinitionFilterModel.js b/lib/public/components/Filters/RunsFilter/RunDefinitionFilterModel.js index b0011177bf..ac41defd53 100644 --- a/lib/public/components/Filters/RunsFilter/RunDefinitionFilterModel.js +++ b/lib/public/components/Filters/RunsFilter/RunDefinitionFilterModel.js @@ -31,7 +31,6 @@ export class RunDefinitionFilterModel extends SelectionModel { if (!this.isPhysicsOnly()) { this.selectedOptions = []; this.select(RunDefinition.Physics); - this.notify(); } } diff --git a/lib/public/components/Filters/common/FilterModel.js b/lib/public/components/Filters/common/FilterModel.js index 2aae7b1a10..d16f1226f7 100644 --- a/lib/public/components/Filters/common/FilterModel.js +++ b/lib/public/components/Filters/common/FilterModel.js @@ -77,6 +77,15 @@ export class FilterModel extends Observable { return this._visualChange$; } + /** + * States if the filter is active. By default this is equivalent to isEmpty + * + * @return {boolean} true if the filter is active + */ + get isInactive() { + return this.isEmpty; + } + /** * Utility function to register a filter model as sub-filter model * diff --git a/lib/public/components/Filters/common/FilteringModel.js b/lib/public/components/Filters/common/FilteringModel.js index 545d848190..4bba7bb35c 100644 --- a/lib/public/components/Filters/common/FilteringModel.js +++ b/lib/public/components/Filters/common/FilteringModel.js @@ -66,7 +66,7 @@ export class FilteringModel extends Observable { if (clearUrl) { const { params } = this._router; - delete params.filter; + params.filter = this.normalized; this._router.go(buildUrl('?', params), false, true); } } @@ -93,12 +93,7 @@ export class FilteringModel extends Observable { * @return {boolean} true if at least one filter is active */ isAnyFilterActive() { - for (const model of this._filterModels) { - if (!model.isEmpty) { - return true; - } - } - return false; + return !this._filterModels.every((model) => model.isInactive); } /** @@ -140,6 +135,32 @@ export class FilteringModel extends Observable { this.notify(); } + /** + * Look for parameters used for filtering in URL and apply them in the layout if it exists + * + * @param {boolean} notify if observers should be notified after setting the filters + * @returns {undefined} + */ + setFilterFromURL(notify = false) { + const { params: { page = '', filter } } = this._router; + + if (this._pageIdentifier === page) { + if (filter) { + for (const [key, value] of Object.entries(filter)) { + if (key in this._filters) { + this._filters[key].normalized = value; + } + } + } else { + this.reset(); + } + } + + if (notify) { + this.notify(); + } + } + /** * Add new filter * diff --git a/lib/public/components/Filters/common/filters/ToggleFilterModel.js b/lib/public/components/Filters/common/filters/ToggleFilterModel.js index c7a28ff944..ee22703852 100644 --- a/lib/public/components/Filters/common/filters/ToggleFilterModel.js +++ b/lib/public/components/Filters/common/filters/ToggleFilterModel.js @@ -19,9 +19,9 @@ export class ToggleFilterModel extends SelectionModel { /** * Constructor * @param {boolean} toggledByDefault If the filter should be toggled by default - * @param {boolean} untoggledIsEmpty if true, will treat the untoggled state (false) as empty. + * @param {boolean} defaultIsInactive if true, will treat the untoggled state (false) as empty. */ - constructor(toggledByDefault = false, untoggledIsEmpty = false) { + constructor(toggledByDefault = false, defaultIsInactive = false) { super({ availableOptions: [{ value: true }, { value: false }], defaultSelection: [{ value: toggledByDefault }], @@ -29,7 +29,7 @@ export class ToggleFilterModel extends SelectionModel { allowEmpty: false, }); - this._untoggledIsEmpty = untoggledIsEmpty; + this._defaultIsInactive = defaultIsInactive; } /** @@ -51,15 +51,22 @@ export class ToggleFilterModel extends SelectionModel { } /** - * Determines whether the current value should be considered empty. - * When this._untoggledIsEmpty is set to 'true', then untoggled states (false) will be treated as an empty filter + * Toggles are always filled, as 'false' / untoggled is also considered a value * - * @return {boolean} `true` if the current value is considered empty, - * otherwise `false`. + * @return {boolean} `false` */ get isEmpty() { - if (this._untoggledIsEmpty) { - return this.isToggled === false; + return false; + } + + /** + * Returns if the toggle filter is considered 'inactive' + * + * @return {boolean} + */ + get isInactive() { + if (this._defaultIsInactive) { + return this.hasOnlyDefaultSelection(); } return false; diff --git a/lib/public/components/common/selection/SelectionModel.js b/lib/public/components/common/selection/SelectionModel.js index 44fdec5c68..b9926b4f32 100644 --- a/lib/public/components/common/selection/SelectionModel.js +++ b/lib/public/components/common/selection/SelectionModel.js @@ -113,6 +113,15 @@ export class SelectionModel extends Observable { return selected.length === defaultSelection.length && selected.every((item) => defaultSelection.includes(item)); } + /** + * States if the filter is active. By default this is equivalent to isEmpty + * + * @return {boolean} true if the filter is active + */ + get isInactive() { + return this.isEmpty; + } + /** * Reset the selection to the default * @@ -358,9 +367,19 @@ export class SelectionModel extends Observable { */ set normalized(value) { const options = value.split(',').map((option) => ({ value: option.trim() })); - const postponeSelection = this.options instanceof RemoteData || !this.options?.length; - - if (postponeSelection) { + const isRemoteData = this.options instanceof RemoteData; + const noOptions = !this.options?.length; + + if (isRemoteData) { + this._availableOptions.match({ + Success: (_) => { + this.selectedOptions = options; + }, + Other: () => { + this._selectionBacklog = options; + }, + }); + } else if (noOptions) { this._selectionBacklog = options; } else { this.selectedOptions = options; diff --git a/lib/public/components/runEorReasons/runEorReasonSelection.js b/lib/public/components/runEorReasons/runEorReasonSelection.js index dbe86cde87..c7a3ad14a3 100644 --- a/lib/public/components/runEorReasons/runEorReasonSelection.js +++ b/lib/public/components/runEorReasons/runEorReasonSelection.js @@ -22,6 +22,7 @@ import { h } from '/js/src/index.js'; */ export const eorReasonFilterComponent = (eorReasonFilterModel, eorReasonTypes) => { const eorReasonsCategories = [...new Set(eorReasonTypes.map(({ category }) => category))]; + const { category: currentCategory, title: currentTitle } = eorReasonFilterModel; return [ h('.flex-row', [ @@ -36,7 +37,7 @@ export const eorReasonFilterComponent = (eorReasonFilterModel, eorReasonTypes) = h('option', { selected: eorReasonFilterModel.category === '', value: '' }, '-'), eorReasonsCategories.map((category, index) => h( `option#eorCategory${index}`, - { key: category, value: category }, + { key: category, value: category, selected: category === currentCategory }, category, )), ], @@ -54,7 +55,7 @@ export const eorReasonFilterComponent = (eorReasonFilterModel, eorReasonTypes) = .filter((reason) => reason.category === eorReasonFilterModel.category) .map(({ title }, index) => h( `option#eorTitle${index}`, - { key: title, value: title }, + { key: title, value: title, selected: title === currentTitle }, title || '(empty)', )), ], diff --git a/lib/public/views/DataPasses/ActiveColumns/dataPassesActiveColumns.js b/lib/public/views/DataPasses/ActiveColumns/dataPassesActiveColumns.js index d2e8ebba06..e0b6d87316 100644 --- a/lib/public/views/DataPasses/ActiveColumns/dataPassesActiveColumns.js +++ b/lib/public/views/DataPasses/ActiveColumns/dataPassesActiveColumns.js @@ -20,7 +20,7 @@ import { h } from '/js/src/index.js'; import { formatDataPassName } from '../format/formatDataPassName.js'; import { formatDataPassStatusHistory } from '../format/formatStatusHistory.js'; import { checkboxes } from '../../../components/Filters/common/filters/checkboxFilter.js'; -import { textInputFilter } from '../../../components/Filters/common/filters/textInputFilter.js'; +import { textFilter } from '../../../components/Filters/common/filters/textFilter.js'; /** * List of active columns for a generic data passes table @@ -35,7 +35,7 @@ export const dataPassesActiveColumns = { visible: true, sortable: true, format: (_, dataPass) => formatDataPassName(dataPass), - filter: (filteringModel) => textInputFilter(filteringModel, 'names', 'e.g. LHC22a_apass1, ...', 'w-75'), + filter: (filteringModel) => textFilter(filteringModel.get('names'), { class: 'w-75 mt1', placeholder: 'e.g. LHC22a, lhc23b, ...' }), balloon: true, classes: 'w-20', }, diff --git a/lib/public/views/DataPasses/DataPassesModel.js b/lib/public/views/DataPasses/DataPassesModel.js index 78589a56cd..42fed10c3a 100644 --- a/lib/public/views/DataPasses/DataPassesModel.js +++ b/lib/public/views/DataPasses/DataPassesModel.js @@ -40,6 +40,7 @@ export class DataPassesModel extends Observable { * @returns {void} */ loadPerLhcPeriodOverview({ lhcPeriodId }) { + this._perLhcPeriodOverviewModel.setFilterFromURL(false); this._perLhcPeriodOverviewModel.load({ lhcPeriodId }); } @@ -68,6 +69,7 @@ export class DataPassesModel extends Observable { */ loadPerSimulationPassOverview({ simulationPassId }) { this._perSimulationPassOverviewModel.simulationPassId = parseInt(simulationPassId, 10); + this._perSimulationPassOverviewModel.setFilterFromURL(false); this._perSimulationPassOverviewModel.load(); } diff --git a/lib/public/views/DataPasses/DataPassesOverviewModel.js b/lib/public/views/DataPasses/DataPassesOverviewModel.js index 31b61b11cd..728f995f85 100644 --- a/lib/public/views/DataPasses/DataPassesOverviewModel.js +++ b/lib/public/views/DataPasses/DataPassesOverviewModel.js @@ -54,6 +54,15 @@ export class DataPassesOverviewModel extends OverviewPageModel { return this._filteringModel.normalized; } + /** + * Set underlying FilteringModel's filters from the query parameters in the URL + * + * @param {boolean} notify if the FilteringModel should notify it's observers after finishing setting the filters + */ + setFilterFromURL(notify) { + this._filteringModel.setFilterFromURL(notify); + } + /** * Reset this model to its default * diff --git a/lib/public/views/Environments/EnvironmentModel.js b/lib/public/views/Environments/EnvironmentModel.js index 53be81b0a0..1cc7fa484d 100644 --- a/lib/public/views/Environments/EnvironmentModel.js +++ b/lib/public/views/Environments/EnvironmentModel.js @@ -42,6 +42,7 @@ export class EnvironmentModel extends Observable { */ loadOverview() { if (!this._overviewModel.pagination.isInfiniteScrollEnabled) { + this._overviewModel.setFilterFromURL(false); this._overviewModel.load(); } } diff --git a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js index 3c9f176f12..59ca3961f5 100644 --- a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js +++ b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js @@ -54,7 +54,6 @@ export class EnvironmentOverviewModel extends OverviewPageModel { this._filteringModel.observe(() => this._applyFilters(true)); this._filteringModel.visualChange$?.bubbleTo(this); - this.reset(false); const updateDebounceTime = () => { this._debouncedLoad = debounce(this.load.bind(this), model.inputDebounceTime); }; @@ -70,6 +69,15 @@ export class EnvironmentOverviewModel extends OverviewPageModel { return buildUrl('/api/environments', { filter: this.filteringModel.normalized }); } + /** + * Set underlying FilteringModel's filters from the query parameters in the URL + * + * @param {boolean} notify if the FilteringModel should notify it's observers after finishing setting the filters + */ + setFilterFromURL(notify) { + this._filteringModel.setFilterFromURL(notify); + } + /** * Returns the current environments list as remote data * diff --git a/lib/public/views/LhcFills/LhcFills.js b/lib/public/views/LhcFills/LhcFills.js index 1fda55ab58..a4343be26a 100644 --- a/lib/public/views/LhcFills/LhcFills.js +++ b/lib/public/views/LhcFills/LhcFills.js @@ -42,6 +42,7 @@ export default class LhcFills extends Observable { * @returns {void} */ loadOverview() { + this._overviewModel.setFilterFromURL(false); this._overviewModel.load(); } diff --git a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js index 4487e216f8..8855e3fac5 100644 --- a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js +++ b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js @@ -54,8 +54,6 @@ export class LhcFillsOverviewModel extends OverviewPageModel { this._filteringModel.pageIdentifier = pageIdentifier; this._filteringModel.observe(() => this._applyFilters()); this._filteringModel.visualChange$.bubbleTo(this); - - this.reset(false); } /** @@ -72,10 +70,16 @@ export class LhcFillsOverviewModel extends OverviewPageModel { * @inheritDoc */ getRootEndpoint() { - const params = { - filter: this.filteringModel.normalized, - }; - return buildUrl('/api/lhcFills', params); + return buildUrl('/api/lhcFills', { filter: this.filteringModel.normalized }); + } + + /** + * Set underlying FilteringModel's filters from the query parameters in the URL + * + * @param {boolean} notify if the FilteringModel should notify it's observers after finishing setting the filters + */ + setFilterFromURL(notify) { + this._filteringModel.setFilterFromURL(notify); } /** diff --git a/lib/public/views/Logs/LogsModel.js b/lib/public/views/Logs/LogsModel.js index 6c5c463649..7822fa4d79 100644 --- a/lib/public/views/Logs/LogsModel.js +++ b/lib/public/views/Logs/LogsModel.js @@ -55,6 +55,7 @@ export class LogsModel extends Observable { */ loadOverview() { if (!this._overviewModel.pagination.isInfiniteScrollEnabled) { + this._overviewModel.setFilterFromURL(false); this._overviewModel.fetchLogs(); } } diff --git a/lib/public/views/Logs/Overview/LogsOverviewModel.js b/lib/public/views/Logs/Overview/LogsOverviewModel.js index f2d531335b..d0ba79ef33 100644 --- a/lib/public/views/Logs/Overview/LogsOverviewModel.js +++ b/lib/public/views/Logs/Overview/LogsOverviewModel.js @@ -53,30 +53,38 @@ export class LogsOverviewModel extends Observable { }, ); - this._filteringModel.pageIdentifier = pageIdentifier; + this._overviewSortModel = new SortModel(); + this._pagination = new PaginationModel(); + const updateDebounceTime = () => { + this._debouncedFetchAllLogs = debounce(this.fetchLogs.bind(this), model.inputDebounceTime); + }; + + updateDebounceTime(); + model.appConfiguration$.observe(() => updateDebounceTime()); + + // Filters + this.filteringModel.pageIdentifier = pageIdentifier; + excludeAnonymous && this._filteringModel.get('author').update('!Anonymous'); this._filteringModel.observe(() => this._applyFilters()); this._filteringModel.visualChange$.bubbleTo(this); // Sub-models - this._overviewSortModel = new SortModel(); this._overviewSortModel.observe(() => this._applyFilters(true)); this._overviewSortModel.visualChange$.bubbleTo(this); - this._pagination = new PaginationModel(); this._pagination.observe(() => this.fetchLogs()); this._pagination.itemsPerPageSelector$.observe(() => this.notify()); this._logs = RemoteData.NotAsked(); + } - const updateDebounceTime = () => { - this._debouncedFetchAllLogs = debounce(this.fetchLogs.bind(this), model.inputDebounceTime); - }; - model.appConfiguration$.observe(() => updateDebounceTime()); - updateDebounceTime(); - - excludeAnonymous && this._filteringModel.get('author').update('!Anonymous'); - - this.reset(false); + /** + * Set underlying FilteringModel's filters from the query parameters in the URL + * + * @param {boolean} notify if the FilteringModel should notify it's observers after finishing setting the filters + */ + setFilterFromURL(notify) { + this._filteringModel.setFilterFromURL(notify); } /** diff --git a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js index 36c3f33f50..531889a6c5 100644 --- a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js +++ b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js @@ -54,6 +54,15 @@ export class QcFlagTypesOverviewModel extends OverviewPageModel { return buildUrl('/api/qcFlagTypes', { filter: this._filteringModel.normalized }); } + /** + * Set underlying FilteringModel's filters from the query parameters in the URL + * + * @param {boolean} notify if the FilteringModel should notify it's observers after finishing setting the filters + */ + setFilterFromURL(notify) { + this._filteringModel.setFilterFromURL(notify); + } + /** * Return the model managing all filters * diff --git a/lib/public/views/QcFlagTypes/QcFlagTypesModel.js b/lib/public/views/QcFlagTypes/QcFlagTypesModel.js index fd5c391ada..9fe8118a76 100644 --- a/lib/public/views/QcFlagTypes/QcFlagTypesModel.js +++ b/lib/public/views/QcFlagTypes/QcFlagTypesModel.js @@ -38,6 +38,7 @@ export class QcFlagTypesModel extends Observable { * @return {void} */ loadOverview() { + this._overviewModel.setFilterFromURL(false); this._overviewModel.load(); } diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index 47a12044af..53bcffd59f 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -106,7 +106,6 @@ export class RunsOverviewModel extends OverviewPageModel { this._filteringModel.observe(() => this._applyFilters(true)); this._filteringModel.visualChange$.bubbleTo(this); - this.reset(false); const updateDebounceTime = () => { this._debouncedLoad = debounce(this.load.bind(this), model.inputDebounceTime); }; @@ -162,6 +161,15 @@ export class RunsOverviewModel extends OverviewPageModel { } } + /** + * Set underlying FilteringModel's filters from the query parameters in the URL + * + * @param {boolean} notify if the FilteringModel should notify it's observers after finishing setting the filters + */ + setFilterFromURL(notify) { + this._filteringModel.setFilterFromURL(notify); + } + /** * Checks if any filter value has been modified from their default (empty) * @return {Boolean} If any filter is active diff --git a/lib/public/views/Runs/Overview/RunsWithQcModel.js b/lib/public/views/Runs/Overview/RunsWithQcModel.js index 5a3f340282..cc1687cf30 100644 --- a/lib/public/views/Runs/Overview/RunsWithQcModel.js +++ b/lib/public/views/Runs/Overview/RunsWithQcModel.js @@ -73,6 +73,9 @@ export class RunsWithQcModel extends RunsOverviewModel { constructor(model, pageIdentifier) { super(model, pageIdentifier); + this._detectorsNotBadFractionRegistered = false; + this._detectorsForQcFlagRegistered = false; + this._observablesQcFlagsSummaryDependsOn$ = null; // This filter instance will be added as a sub-filter for a MultiCompositionFilter and a GaqFilter later. this._mcReproducibleAsNotBad = new ToggleFilterModel(); @@ -145,9 +148,17 @@ export class RunsWithQcModel extends RunsOverviewModel { Success: (detectors) => detectors.forEach(({ id }) => detectorsQcNotBadFraction.putFilter(`_${id}`, new NumericalComparisonFilterModel({ scale: 0.01, integer: false }))), }); + + if (current?.isSuccess() && !this._detectorsNotBadFractionRegistered) { + this.filteringModel.setFilterFromURL(); + this._detectorsNotBadFractionRegistered = true; + } }; - detectors$.observe(callback); - callback(detectors$); + + if (!this._detectorsNotBadFractionRegistered) { + detectors$.observe(callback.bind(this)); + callback(detectors$); + } } /** @@ -161,6 +172,7 @@ export class RunsWithQcModel extends RunsOverviewModel { const current = observableData.getCurrent(); current?.apply({ Success: (detectors) => { + this._detectorsForQcFlagRegistered = true; this._exportModel.setDataExportConfiguration({ ...baseDataExportConfiguration, ...qcFlagsExportConfigurationFactory(detectors), @@ -169,9 +181,11 @@ export class RunsWithQcModel extends RunsOverviewModel { Other: () => null, }); }; - detectors$.observe(callback); - // Also trigger immediately if detectors are already loaded - callback(detectors$); + + if (!this._detectorsForQcFlagRegistered) { + detectors$.observe(callback.bind(this)); + callback(detectors$); + } } /** @@ -180,6 +194,10 @@ export class RunsWithQcModel extends RunsOverviewModel { * @param {ObservableData>} detectors$ observable data which QC flags fetching operation success depends on */ registerObservablesQcSummaryDependsOn(detectors$) { + if (detectors$ === this._observablesQcFlagsSummaryDependsOn$) { + return; + } + this._observablesQcFlagsSummaryDependsOn$ = detectors$; const callback = (observableData) => { const current = observableData.getCurrent(); diff --git a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js index ac038c6192..5bceeeeeb4 100644 --- a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js +++ b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js @@ -52,6 +52,8 @@ export class RunsPerDataPassOverviewModel extends FixedPdpBeamTypeRunsOverviewMo })) .build(); + this._filteringModel.put('gaq', new GaqFilterModel(this._mcReproducibleAsNotBad)); + this._detectors$.bubbleTo(this); this._markAsSkimmableRequestResult$ = new ObservableData(RemoteData.notAsked()); @@ -66,8 +68,6 @@ export class RunsPerDataPassOverviewModel extends FixedPdpBeamTypeRunsOverviewMo this._skimmableRuns$ = new ObservableData(RemoteData.notAsked()); this._skimmableRuns$.bubbleTo(this); - this._filteringModel.put('gaq', new GaqFilterModel(this._mcReproducibleAsNotBad)); - this._freezeOrUnfreezeActionState$ = new ObservableData(RemoteData.notAsked()); this._freezeOrUnfreezeActionState$.bubbleTo(this); @@ -272,7 +272,7 @@ export class RunsPerDataPassOverviewModel extends FixedPdpBeamTypeRunsOverviewMo * @param {number} dataPassId id of Data Pass */ set dataPassId(dataPassId) { - if (dataPassId !== this._dataPassId) { + if (this._dataPassId && dataPassId !== this._dataPassId) { this.reset(false); } this._dataPassId = dataPassId; diff --git a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js index 7a63578bef..2ae78a395c 100644 --- a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js +++ b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js @@ -96,13 +96,8 @@ export class RunsPerLhcPeriodOverviewModel extends FixedPdpBeamTypeRunsOverviewM * @inheritdoc */ getRootEndpoint() { - return buildUrl(super.getRootEndpoint(), { - filter: { - lhcPeriodIds: [this._lhcPeriodId], - runQualities: 'good', - definitions: 'PHYSICS', - }, - }); + const filter = { lhcPeriodIds: [this._lhcPeriodId], runQualities: 'good', definitions: 'PHYSICS' }; + return buildUrl(super.getRootEndpoint(), { filter }); } /** @@ -152,7 +147,7 @@ export class RunsPerLhcPeriodOverviewModel extends FixedPdpBeamTypeRunsOverviewM * @param {string} lhcPeriodId id of a LHC period */ set lhcPeriodId(lhcPeriodId) { - if (lhcPeriodId !== this._lhcPeriodId) { + if (this._lhcPeriodId && lhcPeriodId !== this._lhcPeriodId) { this.reset(false); } this._lhcPeriodId = lhcPeriodId; diff --git a/lib/public/views/Runs/RunsModel.js b/lib/public/views/Runs/RunsModel.js index fa2106a2cb..007a456368 100644 --- a/lib/public/views/Runs/RunsModel.js +++ b/lib/public/views/Runs/RunsModel.js @@ -48,6 +48,7 @@ export class RunsModel extends Observable { */ loadOverview() { if (! this._overviewModel.pagination.isInfiniteScrollEnabled) { + this._overviewModel.setFilterFromURL(false); this._overviewModel.load(); } } @@ -93,6 +94,7 @@ export class RunsModel extends Observable { this._perLhcPeriodOverviewModel.tabbedPanelModel.currentPanelKey = panel; if (!this._perLhcPeriodOverviewModel.pagination.isInfiniteScrollEnabled) { this._perLhcPeriodOverviewModel.lhcPeriodId = lhcPeriodId; + this._perLhcPeriodOverviewModel.setFilterFromURL(false); this._perLhcPeriodOverviewModel.load(); } } @@ -120,6 +122,7 @@ export class RunsModel extends Observable { * so the pagination trigger will not refresh the data. * Thus, we need to trigger the load here. */ + this._perDataPassOverviewModel.setFilterFromURL(false); this._perDataPassOverviewModel.load(); } } @@ -148,6 +151,7 @@ export class RunsModel extends Observable { * so the pagination trigger will not refresh the data. * Thus, we need to trigger the load here. */ + this._perSimulationPassOverviewModel.setFilterFromURL(false); this._perSimulationPassOverviewModel.load(); } } diff --git a/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js b/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js index 5d081c33f5..084b57d130 100644 --- a/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js +++ b/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js @@ -75,13 +75,8 @@ export class RunsPerSimulationPassOverviewModel extends FixedPdpBeamTypeRunsOver * @inheritdoc */ getRootEndpoint() { - const params = { - filter: { - simulationPassIds: [this._simulationPassId], - }, - }; - - return buildUrl(super.getRootEndpoint(), params); + const filter = { simulationPassIds: [this._simulationPassId] }; + return buildUrl(super.getRootEndpoint(), { filter }); } /** @@ -89,7 +84,7 @@ export class RunsPerSimulationPassOverviewModel extends FixedPdpBeamTypeRunsOver * @param {number} simulationPassId simulation pass id */ set simulationPassId(simulationPassId) { - if (simulationPassId !== this._simulationPassId) { + if (this._simulationPassId && simulationPassId !== this._simulationPassId) { this.reset(false); } this._simulationPassId = simulationPassId; diff --git a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js index b74c665344..6fb3af5ea8 100644 --- a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js +++ b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js @@ -42,6 +42,15 @@ export class AnchoredSimulationPassesOverviewModel extends OverviewPageModel { this._dataPass = new ObservableData(RemoteData.notAsked()); } + /** + * Set underlying FilteringModel's filters from the query parameters in the URL + * + * @param {boolean} notify if the FilteringModel should notify it's observers after finishing setting the filters + */ + setFilterFromURL(notify) { + this._filteringModel.setFilterFromURL(notify); + } + /** * Fetch data pass info which simulation passes are fetched * @return {Promise} promise diff --git a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js index 51556d1f1c..e3651ce6e4 100644 --- a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js +++ b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js @@ -58,6 +58,15 @@ export class SimulationPassesPerLhcPeriodOverviewModel extends OverviewPageModel } } + /** + * Set underlying FilteringModel's filters from the query parameters in the URL + * + * @param {boolean} notify if the FilteringModel should notify it's observers after finishing setting the filters + */ + setFilterFromURL(notify) { + this._filteringModel.setFilterFromURL(notify); + } + /** * Return the model managing all filters * diff --git a/lib/public/views/SimulationPasses/SimulationPassesModel.js b/lib/public/views/SimulationPasses/SimulationPassesModel.js index 03ec1818fd..8ba624efd8 100644 --- a/lib/public/views/SimulationPasses/SimulationPassesModel.js +++ b/lib/public/views/SimulationPasses/SimulationPassesModel.js @@ -42,6 +42,7 @@ export class SimulationPassesModel extends Observable { loadPerLhcPeriodOverview({ lhcPeriodId }) { if (!this._perLhcPeriodOverviewModel.pagination.isInfiniteScrollEnabled) { this._perLhcPeriodOverviewModel.lhcPeriodId = lhcPeriodId; + this._perLhcPeriodOverviewModel.setFilterFromURL(false); this._perLhcPeriodOverviewModel.load(); } } @@ -71,6 +72,7 @@ export class SimulationPassesModel extends Observable { */ loadAnchoredOverview({ dataPassId }) { this._anchoredOverviewModel.dataPassId = dataPassId; + this._anchoredOverviewModel.setFilterFromURL(false); this._anchoredOverviewModel.load(); } diff --git a/lib/public/views/lhcPeriods/LhcPeriodsModel.js b/lib/public/views/lhcPeriods/LhcPeriodsModel.js index 7400961fec..74df7b9dc7 100644 --- a/lib/public/views/lhcPeriods/LhcPeriodsModel.js +++ b/lib/public/views/lhcPeriods/LhcPeriodsModel.js @@ -35,6 +35,7 @@ export class LhcPeriodsModel extends Observable { * @returns {void} */ loadOverview() { + this._overviewModel.setFilterFromURL(false); this._overviewModel.load(); } diff --git a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js index 11df6a7ab7..944032bef8 100644 --- a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js +++ b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js @@ -54,6 +54,15 @@ export class LhcPeriodsOverviewModel extends OverviewPageModel { return buildUrl('/api/lhcPeriodsStatistics', { filter: this._filteringModel.normalized }); } + /** + * Set underlying FilteringModel's filters from the query parameters in the URL + * + * @param {boolean} notify if the FilteringModel should notify it's observers after finishing setting the filters + */ + setFilterFromURL(notify) { + this._filteringModel.setFilterFromURL(notify); + } + /** * Return the model managing all filters * diff --git a/test/public/Filters/FilteringModel.test.js b/test/public/Filters/FilteringModel.test.js new file mode 100644 index 0000000000..cd7c6c512d --- /dev/null +++ b/test/public/Filters/FilteringModel.test.js @@ -0,0 +1,50 @@ +/** + * @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 { + defaultBefore, + defaultAfter, + goToPage, + fillInput, + waitForTableLength, +} = require('../defaults.js'); + +module.exports = () => { + let page; + let browser; + + before(async () => { + [page, browser] = await defaultBefore(); + }); + + it('should undo filters if the user presses go-back', async () => { + const filterInputSelector = '.runNumbers-textFilter'; + await goToPage(page, 'run-overview'); + + await waitForTableLength(page, 6); + await fillInput(page, filterInputSelector, '109', ['change']); + await waitForTableLength(page, 1); + await fillInput(page, filterInputSelector, '109,108', ['change']); + await waitForTableLength(page, 2); + await fillInput(page, filterInputSelector, '109,108,107', ['change']); + await waitForTableLength(page, 3); + await page.goBack(); + await waitForTableLength(page, 2); + await page.goBack(); + await waitForTableLength(page, 1); + await page.goBack(); + await waitForTableLength(page, 6); + }); + + after(async () => await defaultAfter(page, browser)); +} diff --git a/test/public/Filters/index.js b/test/public/Filters/index.js index e5146bb7fc..1023d11de8 100644 --- a/test/public/Filters/index.js +++ b/test/public/Filters/index.js @@ -12,7 +12,11 @@ */ const ToUrlSuite = require('./filtersToUrl.test.js'); +const ToFilterSuite = require('./urlToFilter.test.js'); +const FilteringModelSuite = require('./filteringModel.test.js'); module.exports = () => { describe('Filters to URL', ToUrlSuite); + describe('URL to Filters', ToFilterSuite); + describe('FilteringModel', FilteringModelSuite); }; diff --git a/test/public/Filters/urlToFilter.test.js b/test/public/Filters/urlToFilter.test.js new file mode 100644 index 0000000000..06eb280039 --- /dev/null +++ b/test/public/Filters/urlToFilter.test.js @@ -0,0 +1,372 @@ +/** + * @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 { + defaultBefore, + defaultAfter, + fillInput, + getPopoverSelector, + getPeriodInputsSelectors, + pressElement, + openFilteringPanel, + expectInputValue, +} = require('../defaults.js'); + +module.exports = () => { + let page; + let browser; + + before(async () => { + [page, browser] = await defaultBefore(); + }); + + it('should apply filters from url in logsOverviewPage', async () => { + const url = 'http://localhost:4000/?page=log-overview&filter[author]=Jane&filter[title]=bogusbogusbogus&filter[content]=particle'+ + '&filter[tags][values]=DPG&filter[tags][operation]=and&filter[runNumbers]=1%2C2&filter[environmentIds]=8E4aZTjY'+ + '&filter[fillNumbers]=1%2C%206&filter[created][from]=1580637600000&filter[created][to]=1580641200000'; + + + await page.goto(url, { waitUntil: 'load' }); + + 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 expectInputValue(page, '.title-textFilter', 'bogusbogusbogus'); + await expectInputValue(page, '#authorFilterText', 'Jane'); + await expectInputValue(page, '.content-textFilter', 'particle'); + await pressElement(page, '.tags-filter .dropdown-trigger'); + await page.waitForSelector(`#${firstCheckboxId}:checked`); + await expectInputValue(page, '.environments-filter input', '8E4aZTjY'); + await expectInputValue(page, '.runNumbers-textFilter', '1,2'); + await expectInputValue(page, '.fillNumbers-textFilter', '1, 6'); + await expectInputValue(page, fromDateSelector, '2020-02-02'); + await expectInputValue(page, toDateSelector, '2020-02-02'); + + await expectInputValue(page, fromTimeSelector, '10:00'); + await expectInputValue(page, toTimeSelector, '11:00'); + }); + + it('should set filters from EnvironmentsOverview to the URL', async () => { + const url = 'http://localhost:4000/?page=env-overview&filter[created][from]=1565301600000&filter[created][to]=1565474340000' + + '&filter[runNumbers]=10&filter[statusHistory]=C-R-D-X&filter[currentStatus]=DESTROYED&filter[ids]=Dxi029djX%2C%20TDI59So3d'; + await page.goto(url, { waitUntil: 'load' }); + await openFilteringPanel(page); + + const popoverTrigger = '.createdAt-filter .popover-trigger'; + const createdAtPopoverSelector = await getPopoverSelector(await page.$(popoverTrigger)); + const periodInputsSelectors = getPeriodInputsSelectors(createdAtPopoverSelector); + + await expectInputValue(page, '.runs-filter input', '10'); + await expectInputValue(page, '.id-filter input', 'Dxi029djX, TDI59So3d'); + await page.waitForSelector('#checkboxes-checkbox-DESTROYED:checked'); + await expectInputValue(page, '.historyItems-filter input', 'C-R-D-X'); + await expectInputValue(page, periodInputsSelectors.fromDateSelector, '2019-08-08'); + await expectInputValue(page, periodInputsSelectors.toDateSelector, '2019-08-10'); + await expectInputValue(page, periodInputsSelectors.fromTimeSelector, '22:00'); + await expectInputValue(page, periodInputsSelectors.toTimeSelector, '21:59'); + }); + + it('should set filters from LhcFillsOverview to the URL', async () => { + const url = 'http://localhost:4000/?page=lhc-fill-overview&filter[beamDuration][operator]=%3D&filter[beamDuration][limit]=00%3A01%3A40&' + + 'filter[runDuration][operator]=%3D&filter[runDuration][limit]=00%3A00%3A00&filter[hasStableBeams]=true&filter[stableBeamsStart][from]=1565251200000&' + + 'filter[stableBeamsStart][to]=1565258400000&filter[stableBeamsEnd][from]=1647907200000&filter[stableBeamsEnd][to]=1647989940000&filter[beamTypes]=p-Pb&filter[schemeName]=Single_12b_8_1024_8_2018'; + + await page.goto(url, { waitUntil: 'load' }); + + 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 expectInputValue(page, '#beam-duration-filter-operand', '00:01:40'); + await expectInputValue(page, '#run-duration-filter-operand', '00:00:00'); + await expectInputValue(page, sbStartFromDateSelector, '2019-08-08'); + await expectInputValue(page, sbStartToDateSelector, '2019-08-08'); + await expectInputValue(page, sbStartFromTimeSelector, '08:00'); + await expectInputValue(page, sbStartToTimeSelector, '10:00'); + await expectInputValue(page, sbEndFromDateSelector, '2022-03-22'); + await expectInputValue(page, sbEndToDateSelector, '2022-03-22'); + await expectInputValue(page, sbEndFromTimeSelector, '00:00'); + await expectInputValue(page, sbEndToTimeSelector, '22:59'); + await expectInputValue(page, filterSchemeNameInputField, 'Single_12b_8_1024_8_2018'); + await page.waitForSelector('#beam-types-checkbox-p-Pb:checked'); + }); + + it('should set filters from runsOverview to the URL', async () => { + const url = 'http://localhost:4000/?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%2C%203&filter[o2start][from]=1612347060000&filter[o2start][to]=1612357200000&filter[o2end][from]=1612347060000&' + + 'filter[o2end][to]=1612357200000&filter[definitions]=PHYSICS&filter[runDuration][operator]=%3D&filter[runDuration][limit]=90000000' + + '&filter[environmentIds]=Dxi029djX%2C%20TDI59So3d&filter[runTypes]=2&filter[beamModes]=NO%20BEAM&filter[runQualities]=bad&filter[nDetectors][operator]=%3D&' + + 'filter[nDetectors][limit]=1&filter[nEpns][operator]=%3D&filter[nEpns][limit]=10&filter[nFlps][operator]=%3D&filter[nFlps][limit]=10&filter[ctfFileCount][operator]=%3D&' + + 'filter[ctfFileCount][limit]=1&filter[tfFileCount][operator]=%3D&filter[tfFileCount][limit]=1&filter[otherFileCount][operator]=%3D&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'; + + await page.goto(url, { waitUntil: 'load' }); + + 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 page.waitForSelector('#detector-filter-dropdown-option-ITS:checked'); + await page.waitForSelector('#run-types-dropdown-option-2:checked'); + await page.waitForSelector('#beam-mode-dropdown-option-NO\\ BEAM:checked'); + await page.waitForSelector('#tag-dropdown-option-FOOD:checked'); + await page.waitForSelector('#run-definition-checkbox-PHYSICS:checked'); + await page.waitForSelector('#epnFilterRadioOFF:checked'); + await pressElement(page, '.timeO2Start-filter .popover-trigger'); + await page.waitForSelector('#checkboxes-checkbox-bad:checked'); + await page.waitForSelector('#triggerValue-checkbox-OFF:checked'); + await page.waitForSelector(`${dipolePopoverSelector} .dropdown-option:last-child input:checked`); + await expectInputValue(page, '#duration-operand', '1500'); + await expectInputValue(page, '#runOverviewFilter .runNumbers-textFilter', '101'); + await expectInputValue(page, '.fillNumbers-textFilter', '1, 3'); + await expectInputValue(page, '.environmentIds-textFilter', 'Dxi029djX, TDI59So3d'); + await expectInputValue(page, '#nDetectors-operand', '1'); + await expectInputValue(page, '#nFlps-operand', '10'); + await expectInputValue(page, '#nEpns-operand', '10'); + await expectInputValue(page, '#ctfFileCount-operand', '1'); + await expectInputValue(page, '#tfFileCount-operand', '1'); + await expectInputValue(page, '#otherFileCount-operand', '1'); + await expectInputValue(page, '#eorDescription', 'some'); + await expectInputValue(page, '#eorTitles', 'CPV'); + await expectInputValue(page, '#eorCategories', 'DETECTORS'); + await expectInputValue(page, startFromTimeSelector, '10:11'); + await expectInputValue(page, startToTimeSelector, '13:00'); + await expectInputValue(page, startFromDateSelector, '2021-02-03'); + await expectInputValue(page, startToDateSelector, '2021-02-03'); + await expectInputValue(page, endFromTimeSelector, '10:11'); + await expectInputValue(page, endToTimeSelector, '13:00'); + await expectInputValue(page, endFromDateSelector, '2021-02-03'); + await expectInputValue(page, endToDateSelector, '2021-02-03'); + }); + + it('should set filters from lhcPriodOverview to the URL', async () => { + const url = 'http://localhost:4000/?page=lhc-period-overview&filter[names][]=LHC22a&filter[years][]=2022&filter[pdpBeamTypes][]=PbPb'; + await page.goto(url, { waitUntil: 'load' }); + + await expectInputValue(page, 'div.flex-row.items-baseline:nth-of-type(1) input[type=text]', 'LHC22a'); + await expectInputValue(page, 'div.flex-row.items-baseline:nth-of-type(2) input[type=text]', '2022'); + await expectInputValue(page, 'div.flex-row.items-baseline:nth-of-type(3) input[type=text]', 'PbPb'); + }); + + it('should set filters from qcFlagTypesOverview to the URL', async () => { + const url = 'http://localhost:4000/?page=qc-flag-types-overview&filter[names][]=bad&filter[methods][]=bad&filter[bad]=true'; + await page.goto(url, { waitUntil: 'load' }); + + await expectInputValue(page, '.name-filter input[type=text]', 'bad'); + await expectInputValue(page, '.method-filter input[type=text]', 'bad'); + await page.waitForSelector('#badFilterRadioBad:checked'); + }); + + it('should set filters from runsPerLhcPeriodOverview to the URL', async () => { + const url = 'http://localhost:4000/?page=runs-per-lhc-period&lhcPeriodId=2&filter[runNumbers]=101&filter[fillNumbers]=1%2C%203&filter[o2start][from]=1612347060000&' + + 'filter[o2start][to]=1612357200000&filter[o2end][from]=1612347060000&filter[o2end][to]=1612357200000&filter[magnets][l3]=30003&filter[magnets][dipole]=0&' + + 'filter[muInelasticInteractionRate][operator]=%3D&filter[muInelasticInteractionRate][limit]=100000&filter[inelasticInteractionRateAvg][operator]=%3D&filter[inelasticInteractionRateAvg][limit]=100000'; + await page.goto(url, { waitUntil: 'load' }); + + 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 expectInputValue(page, '#inelasticInteractionRateAvg-operand', '100000'); + await expectInputValue(page, '#muInelasticInteractionRate-operand', '100000'); + await expectInputValue(page, '#runOverviewFilter .runNumbers-textFilter', '101'); + await expectInputValue(page, '.fillNumbers-textFilter', '1, 3'); + await expectInputValue(page, startFromTimeSelector, '10:11'); + await expectInputValue(page, startToTimeSelector, '13:00'); + await expectInputValue(page, startFromDateSelector, '2021-02-03'); + await expectInputValue(page, startToDateSelector, '2021-02-03'); + await expectInputValue(page, endFromTimeSelector, '10:11'); + await expectInputValue(page, endToTimeSelector, '13:00'); + await expectInputValue(page, endFromDateSelector, '2021-02-03'); + await expectInputValue(page, endToDateSelector, '2021-02-03'); + await page.waitForSelector(`${dipolePopoverSelector} .dropdown-option:last-child input:checked`); + }); + + it('should set filters from DataPassesPerLhcPeriodOverview to the URL', async () => { + const url = 'http://localhost:4000/?page=data-passes-per-lhc-period-overview&lhcPeriodId=2&filter[names][]=LHC22b_apass1&filter[permittedNonPhysicsNames]=test'; + await page.goto(url, { waitUntil: 'load' }); + + await expectInputValue(page, 'div.flex-row.items-baseline:nth-of-type(1) input[type=text]', 'LHC22b_apass1'); + await page.waitForSelector('#checkboxes-checkbox-test:checked'); + }); + + it('should set filters from DataPassesPerSimulationPassOverview to the URL', async () => { + const url = 'http://localhost:4000/?page=data-passes-per-simulation-pass-overview&simulationPassId=1&filter[names][]=LHC22b_apass1&filter[permittedNonPhysicsNames]=test'; + await page.goto(url, { waitUntil: 'load' }); + + await expectInputValue(page, 'div.flex-row.items-baseline:nth-of-type(1) input[type=text]', 'LHC22b_apass1'); + await page.waitForSelector('#checkboxes-checkbox-test:checked'); + }); + + it('should set filters from AnchoredSimulationPassesOverview to the URL', async () => { + const url = 'http://localhost:4000/?page=anchored-simulation-passes-overview&dataPassId=1&filter[names][]=LHC23k6c'; + await page.goto(url, { waitUntil: 'load' }); + + await expectInputValue(page, '.name-filter input', 'LHC23k6c'); + }); + + it('should set filters from RunsPerSimulationPass to the URL', async () => { + const url = 'http://localhost:4000/?page=runs-per-simulation-pass&simulationPassId=2&filter[o2start][from]=1612347060000&' + + 'filter[o2start][to]=1612357200000&filter[o2end][from]=1612347060000&filter[o2end][to]=1612357200000&' + + 'filter[magnets][l3]=30003&filter[magnets][dipole]=0&filter[inelasticInteractionRateAtStart][operator]=%3D&' + + 'filter[inelasticInteractionRateAtStart][limit]=1&filter[inelasticInteractionRateAtMid][operator]=%3D&' + + 'filter[inelasticInteractionRateAtMid][limit]=1&filter[inelasticInteractionRateAtEnd][operator]=%3D&' + + 'filter[inelasticInteractionRateAtEnd][limit]=1&filter[detectorsQcNotBadFraction][mcReproducibleAsNotBad]=true&' + + 'filter[detectorsQcNotBadFraction][_20][operator]=%3D&filter[detectorsQcNotBadFraction][_20][limit]=0.01&' + + 'filter[detectorsQcNotBadFraction][_17][operator]=%3D&filter[detectorsQcNotBadFraction][_17][limit]=0.01'; + + await page.goto(url, { waitUntil: 'load' }); + + 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 expectInputValue(page, '.inelasticInteractionRateAtMid-filter input', '1'); + await expectInputValue(page, '.inelasticInteractionRateAtEnd-filter input', '1'); + await expectInputValue(page, '.inelasticInteractionRateAtStart-filter input', '1'); + await expectInputValue(page, startFromTimeSelector, '10:11'); + await expectInputValue(page, startToTimeSelector, '13:00'); + await expectInputValue(page, startFromDateSelector, '2021-02-03'); + await expectInputValue(page, startToDateSelector, '2021-02-03'); + await expectInputValue(page, endFromTimeSelector, '10:11'); + await expectInputValue(page, endToTimeSelector, '13:00'); + await expectInputValue(page, endFromDateSelector, '2021-02-03'); + await expectInputValue(page, endToDateSelector, '2021-02-03'); + await page.waitForSelector(`${dipolePopoverSelector} .dropdown-option:last-child input:checked`); + await page.waitForSelector('#mcReproducibleAsNotBadToggle input:checked'); + + + // These two are detectorQCNotBadFraction[_id] filters. There are a dozen more, but they are all identical hence why only these were tested + await expectInputValue(page, '.QC-SPECIFIC-filter input', '1'); + await expectInputValue(page, '.ACO-filter input', '1'); + }); + + it('should set filters from RunsPerSimulationPass to the URL', async () => { + const url = 'http://localhost:4000/?page=runs-per-data-pass&dataPassId=1&filter[detectors][operator]=and&filter[detectors][values]=ITS&' + + 'filter[tags][values]=FOOD&filter[tags][operation]=and&filter[o2start][from]=1612347060000&filter[o2start][to]=1612357200000&' + + 'filter[o2end][from]=1612347060000&filter[o2end][to]=1612357200000&filter[runDuration][operator]=%3D&filter[runDuration][limit]=90000000&' + + 'filter[magnets][l3]=30003&filter[magnets][dipole]=0&filter[muInelasticInteractionRate][operator]=%3D&filter[muInelasticInteractionRate][limit]=1&' + + 'filter[inelasticInteractionRateAvg][operator]=%3D&filter[inelasticInteractionRateAvg][limit]=1&filter[detectorsQcNotBadFraction][mcReproducibleAsNotBad]=true&' + + 'filter[detectorsQcNotBadFraction][_20][operator]=%3D&filter[detectorsQcNotBadFraction][_20][limit]=0.01&filter[detectorsQcNotBadFraction][_17][operator]=%3D&' + + 'filter[detectorsQcNotBadFraction][_17][limit]=0.01&filter[gaq][notBadFraction][operator]=%3D&filter[gaq][notBadFraction][limit]=0.01&filter[gaq][mcReproducibleAsNotBad]=true'; + + await page.goto(url, { waitUntil: 'load' }); + + 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 expectInputValue(page, startFromTimeSelector, '10:11'); + await expectInputValue(page, startToTimeSelector, '13:00'); + await expectInputValue(page, startFromDateSelector, '2021-02-03'); + await expectInputValue(page, startToDateSelector, '2021-02-03'); + await expectInputValue(page, endFromTimeSelector, '10:11'); + await expectInputValue(page, endToTimeSelector, '13:00'); + await expectInputValue(page, endFromDateSelector, '2021-02-03'); + await expectInputValue(page, endToDateSelector, '2021-02-03'); + await expectInputValue(page, '#duration-operand', '1500'); + await expectInputValue(page, '.muInelasticInteractionRate-filter input', '1'); + await expectInputValue(page, '.inelasticInteractionRateAvg-filter input', '1'); + await expectInputValue(page, '.globalAggregatedQuality-filter input', '1'); + await fillInput(page, '.ACO-filter input', '1', ['change']); + await fillInput(page, '.QC-SPECIFIC-filter input', '1', ['change']); + + await page.waitForSelector('#detector-filter-dropdown-option-ITS'); + await page.waitForSelector('#tag-dropdown-option-FOOD'); + await page.waitForSelector(`${dipolePopoverSelector} .dropdown-option:last-child input:checked`); + await page.waitForSelector('#mcReproducibleAsNotBadToggle input:checked'); + }); + + after(async () => await defaultAfter(page, browser)); +} diff --git a/test/public/dataPasses/overviewPerLhcPeriod.test.js b/test/public/dataPasses/overviewPerLhcPeriod.test.js index 4ab08e8de0..a6215dd989 100644 --- a/test/public/dataPasses/overviewPerLhcPeriod.test.js +++ b/test/public/dataPasses/overviewPerLhcPeriod.test.js @@ -164,7 +164,7 @@ module.exports = () => { it('should successfully apply data pass name filter', async () => { await pressElement(page, '#openFilterToggle'); - await fillInput(page, 'div.flex-row.items-baseline:nth-of-type(1) input[type=text]', 'LHC22b_apass1', ['change']); + await fillInput(page, 'div.flex-row.items-baseline:nth-of-type(1) input[type=text]', 'LHC22b_apass1'); await expectColumnValues(page, 'name', ['deleted\nLHC22b_apass1\nSkimmable']); diff --git a/test/public/dataPasses/overviewPerSimulationPass.test.js b/test/public/dataPasses/overviewPerSimulationPass.test.js index 27b6c2d2c9..188ec17dc2 100644 --- a/test/public/dataPasses/overviewPerSimulationPass.test.js +++ b/test/public/dataPasses/overviewPerSimulationPass.test.js @@ -113,7 +113,7 @@ module.exports = () => { it('should successfully apply data pass name filter', async () => { await pressElement(page, '#openFilterToggle'); - await fillInput(page, 'div.flex-row.items-baseline:nth-of-type(1) input[type=text]', 'LHC22b_apass1', ['change']); + await fillInput(page, 'div.flex-row.items-baseline:nth-of-type(1) input[type=text]', 'LHC22b_apass1'); await expectColumnValues(page, 'name', ['deleted\nLHC22b_apass1\nSkimmable']); await pressElement(page, '#reset-filters', true);