From 6dd53a47b80342d050c65592ec9f228e274b8178 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Wed, 27 May 2026 15:58:59 +0200 Subject: [PATCH 1/7] [OGUI-914] Disable DEBUG severity filter at OPS log level When OPS is selected, the DEBUG severity button is disabled and stripped from filters automatically from both the URI and the button methods via `LogFilter.setCriteria` central enforcement. Log level is set to OPS at startup and in the profile defaults. --- InfoLogger/lib/ProfileService.js | 2 +- .../constants/log-level-filters.const.js | 32 ++++++++++++++++ InfoLogger/public/log/Log.js | 1 + InfoLogger/public/logFilter/LogFilter.js | 37 +++++++++++++++++++ InfoLogger/public/logFilter/commandFilters.js | 20 ++++++---- 5 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 InfoLogger/public/constants/log-level-filters.const.js diff --git a/InfoLogger/lib/ProfileService.js b/InfoLogger/lib/ProfileService.js index ce0801542..b24b28736 100644 --- a/InfoLogger/lib/ProfileService.js +++ b/InfoLogger/lib/ProfileService.js @@ -59,7 +59,7 @@ class ProfileService { errsource: { match: '', exclude: '' }, message: { match: '', exclude: '' }, severity: { in: 'I W E F' }, - level: { max: null }, + level: { max: 1 }, }; } diff --git a/InfoLogger/public/constants/log-level-filters.const.js b/InfoLogger/public/constants/log-level-filters.const.js new file mode 100644 index 000000000..a3161ee0b --- /dev/null +++ b/InfoLogger/public/constants/log-level-filters.const.js @@ -0,0 +1,32 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * 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. + */ + +/** + * Maps each log level to the set of severity codes that are NOT available at that level. + * @type {Map} + */ +const DISABLED_SEVERITIES_BY_LEVEL = new Map([ + [1, ['D']], + [6, []], + [11, []], + [null, []], +]); + +/** + * Return the severity codes that are disabled for a given log level. + * @param {number|null} level - the current log level (1=Ops, 6=Support, 11=Developer, null=Trace) + * If provided a level not in the map, we enable all severities as this will be a custom event + * @returns {string[]} severity codes that should be disabled at the given log level + */ +export const getDisabledSeverities = (level) => DISABLED_SEVERITIES_BY_LEVEL.get(level) ?? []; diff --git a/InfoLogger/public/log/Log.js b/InfoLogger/public/log/Log.js index ad7a75458..c13dd5233 100644 --- a/InfoLogger/public/log/Log.js +++ b/InfoLogger/public/log/Log.js @@ -32,6 +32,7 @@ export default class Log extends Observable { this.model = model; this.filter = new LogFilter(model); + this.filter.setCriteria('level', 'max', 1); this.filter.bubbleTo(this); this.focus = { // show date picker on focus diff --git a/InfoLogger/public/logFilter/LogFilter.js b/InfoLogger/public/logFilter/LogFilter.js index 2410dcb00..f9c9bf8bc 100644 --- a/InfoLogger/public/logFilter/LogFilter.js +++ b/InfoLogger/public/logFilter/LogFilter.js @@ -14,6 +14,7 @@ import { Observable } from '/js/src/index.js'; import { TEXT_FILTER_OPERATORS } from '../constants/text-filter-operators.const.js'; +import { getDisabledSeverities } from '../constants/log-level-filters.const.js'; /** * @typedef Criteria @@ -88,6 +89,11 @@ export default class LogFilter extends Observable { throw new Error('unknown operator'); } + // enforces on both severity and level as fromObject can set them in either order + if (field === 'severity' || field === 'level') { + this.enforceDisabledSeverities(); + } + this.notify(); return true; } else { @@ -153,6 +159,37 @@ export default class LogFilter extends Observable { TEXT_FILTER_OPERATORS.some((operator) => criteria[operator]?.trim())); } + /** + * Check whether a severity is disabled for the current log level. + * @param {string} severityCode - [D, I, W, E, F] + * @returns {boolean} true if the severity is not allowed at the current level + */ + isSeverityDisabled(severityCode) { + return getDisabledSeverities(this.criterias.level.max).includes(severityCode); + } + + /** + * Remove any active severity selections that are disallowed by the current level. + */ + enforceDisabledSeverities() { + const disabled = getDisabledSeverities(this.criterias.level.max); + if (disabled.length === 0 || !this.criterias.severity.$in) { + return; + } + + const current = this.criterias.severity.$in; + if (!current) { + return; + } + + const filteredSeverities = current.filter((s) => !disabled.includes(s)); + // Only update if there is a change + if (filteredSeverities.length !== current.length) { + this.criterias.severity.$in = filteredSeverities; + this.criterias.severity.in = filteredSeverities.join(' '); + } + } + /** * Generates a function to filter a log passed as argument to it * Output of function is boolean. diff --git a/InfoLogger/public/logFilter/commandFilters.js b/InfoLogger/public/logFilter/commandFilters.js index 6e3066819..210563363 100644 --- a/InfoLogger/public/logFilter/commandFilters.js +++ b/InfoLogger/public/logFilter/commandFilters.js @@ -59,14 +59,18 @@ export default (model) => [ * @param {string} value - a char to represent severity: W E F or I, can be many with spaces like 'W E' * @returns {vnode} - the button to toggle severity */ -const buttonSeverity = (model, label, title, value) => h('button.btn', { - className: model.log.filter.criterias.severity.in.includes(value) ? 'active' : '', - onclick: (e) => { - model.log.setCriteria('severity', 'in', value); - e.target.blur(); // remove focus so user can 'enter' without actually toggle again the button - }, - title: title, -}, label); +const buttonSeverity = (model, label, title, value) => { + const disabled = model.log.filter.isSeverityDisabled(value); + return h('button.btn', { + className: disabled ? 'disabled' : model.log.filter.criterias.severity.in.includes(value) ? 'active' : '', + onclick: disabled ? null : (e) => { + model.log.setCriteria('severity', 'in', value); + e.target.blur(); + }, + disabled, + title: disabled ? `${label} is not available at the current log level` : title, + }, label); +}; /** * Makes a button to set filtering level (shifter, debug, etc) with number From 78066642af208d37b0bad59d201c1638132d4143 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Wed, 27 May 2026 16:04:45 +0200 Subject: [PATCH 2/7] [OGUI-914] Fix resetting bug The reset button and context menu severity reset were calling filter methods directly which skipped the notification and more importantly in live mode would skip applying the filter to the websocket. --- InfoLogger/public/log/Log.js | 35 +++++++++++++------ InfoLogger/public/log/cellContextMenu.js | 1 + InfoLogger/public/logFilter/commandFilters.js | 2 +- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/InfoLogger/public/log/Log.js b/InfoLogger/public/log/Log.js index c13dd5233..b71bc8495 100644 --- a/InfoLogger/public/log/Log.js +++ b/InfoLogger/public/log/Log.js @@ -402,16 +402,31 @@ export default class Log extends Observable { value = copy.join(' '); } if (this.filter.setCriteria(field, operator, value)) { - if (this.isLiveModeRunning()) { - this.model.ws.setFilter(this.model.log.filter.toStringifyFunction()); - this.model.notification.show( - 'The current live session has been adapted to the new filter configuration.', - 'primary', - 2000, - ); - } else if (this.isActiveModeQuery()) { - this.model.notification.show('Filters have changed. Query again for updated results', 'primary', 2000); - } + this.notifyFilterChanged(); + } + } + + /** + * Reset all filter criteria and notify active mode of the change. + */ + resetFilters() { + this.filter.resetCriteria(); + this.notifyFilterChanged(); + } + + /** + * Notify the active mode (live or query) that filters have changed. + */ + notifyFilterChanged() { + if (this.isLiveModeRunning()) { + this.model.ws.setFilter(this.filter.toStringifyFunction()); + this.model.notification.show( + 'The current live session has been adapted to the new filter configuration.', + 'primary', + 2000, + ); + } else if (this.isActiveModeQuery()) { + this.model.notification.show('Filters have changed. Query again for updated results', 'primary', 2000); } } diff --git a/InfoLogger/public/log/cellContextMenu.js b/InfoLogger/public/log/cellContextMenu.js index 25af0b0d0..97898f65b 100644 --- a/InfoLogger/public/log/cellContextMenu.js +++ b/InfoLogger/public/log/cellContextMenu.js @@ -92,6 +92,7 @@ export const cellContextMenu = (model) => { ), createMenuItem(iconTrash(), 'danger', 'Reset Severity Filter', () => { model.log.filter.setCriteria('severity', 'in', 'I W E F'); + model.log.notifyFilterChanged(); hideMenu(); }, model.log.filter.criterias.severity.in === 'I W E F'), ]; diff --git a/InfoLogger/public/logFilter/commandFilters.js b/InfoLogger/public/logFilter/commandFilters.js index 210563363..05e018b67 100644 --- a/InfoLogger/public/logFilter/commandFilters.js +++ b/InfoLogger/public/logFilter/commandFilters.js @@ -104,6 +104,6 @@ const buttonLogLimit = (model, label, limit) => h('button.btn', { * @returns {vnode} - component representing the creation of a button to reset filters */ const buttonReset = (model) => h('button.btn', { - onclick: () => model.log.filter.resetCriteria(), + onclick: () => model.log.resetFilters(), title: 'Reset date, time, matches, excludes, log levels', }, 'Reset filters'); From e17390eb38b2cd653d76624c777839fdb536e19f Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Wed, 27 May 2026 16:06:25 +0200 Subject: [PATCH 3/7] [OGUI-914] Align severity array order --- InfoLogger/public/logFilter/LogFilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InfoLogger/public/logFilter/LogFilter.js b/InfoLogger/public/logFilter/LogFilter.js index f9c9bf8bc..fb7bb5af7 100644 --- a/InfoLogger/public/logFilter/LogFilter.js +++ b/InfoLogger/public/logFilter/LogFilter.js @@ -426,7 +426,7 @@ export default class LogFilter extends Observable { }, severity: { in: 'I W E F', - $in: ['W', 'I', 'E', 'F'], + $in: ['I', 'W', 'E', 'F'], }, level: { max: null, // 0, 1, 6, 11, 21 From e5a339987b4b555def439bf05fe22ca4c8097500 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Wed, 27 May 2026 16:32:13 +0200 Subject: [PATCH 4/7] [OGUI-914] Add tests for DEBUG disabled enforcement Adds tests that cover the DEBUG button being disabled/enabled based on the log level and the severity being stripped from the URL if not allowed. --- InfoLogger/test/lib/mocha-profile-service.js | 4 +- InfoLogger/test/mocha-index.js | 4 +- InfoLogger/test/public/live-mode-mocha.js | 2 +- .../test/public/log-filter-actions-mocha.js | 108 +++++++++++++++++- 4 files changed, 109 insertions(+), 9 deletions(-) diff --git a/InfoLogger/test/lib/mocha-profile-service.js b/InfoLogger/test/lib/mocha-profile-service.js index d2776e994..cecffae2c 100644 --- a/InfoLogger/test/lib/mocha-profile-service.js +++ b/InfoLogger/test/lib/mocha-profile-service.js @@ -94,7 +94,7 @@ const DEFAULT_PROFILE = { errsource: { match: '', exclude: '' }, message: { match: '', exclude: '' }, severity: { in: 'I W E F' }, - level: { max: null }, + level: { max: 1 }, }, }, }; @@ -135,7 +135,7 @@ const FULL_PROFILE = { errsource: { match: '', exclude: '' }, message: { match: '', exclude: '' }, severity: { in: 'I W E F' }, - level: { max: null }, + level: { max: 1 }, }, }, }; diff --git a/InfoLogger/test/mocha-index.js b/InfoLogger/test/mocha-index.js index d277067fb..3ec3929bb 100644 --- a/InfoLogger/test/mocha-index.js +++ b/InfoLogger/test/mocha-index.js @@ -93,12 +93,12 @@ describe('InfoLogger', function() { } }); - it('should have redirected to default page "/?q={"severity":{"in":"I W E F"}}"', async function() { + it('should have redirected to default page "/?q={"severity":{"in":"I W E F"},"level":{"max":1}}"', async function() { await page.goto(baseUrl, {waitUntil: 'networkidle0'}); const location = await page.evaluate(() => window.location); const search = decodeURIComponent(location.search); - assert.deepStrictEqual(search, '?q={"severity":{"in":"I W E F"}}'); + assert.deepStrictEqual(search, '?q={"severity":{"in":"I W E F"},"level":{"max":1}}'); }); require('./public/user-actions-mocha'); diff --git a/InfoLogger/test/public/live-mode-mocha.js b/InfoLogger/test/public/live-mode-mocha.js index 0f3ae17ef..e74a5179a 100644 --- a/InfoLogger/test/public/live-mode-mocha.js +++ b/InfoLogger/test/public/live-mode-mocha.js @@ -27,7 +27,7 @@ describe('Live Mode test-suite', async () => { const location = await page.evaluate(() => window.location); const search = decodeURIComponent(location.search); - assert.deepStrictEqual(search, '?q={"severity":{"in":"I W E F"}}'); + assert.deepStrictEqual(search, '?q={"severity":{"in":"I W E F"},"level":{"max":1}}'); }); it('should successfully enable LIVE mode', async () => { diff --git a/InfoLogger/test/public/log-filter-actions-mocha.js b/InfoLogger/test/public/log-filter-actions-mocha.js index 904b53ad0..c39ce6607 100644 --- a/InfoLogger/test/public/log-filter-actions-mocha.js +++ b/InfoLogger/test/public/log-filter-actions-mocha.js @@ -25,13 +25,14 @@ describe('Filter actions test-suite', async () => { page = test.page; }); + // "physicist" is not a distinct stored profile; the server returns defaultCriterias for any name it('should succesfully load a page with profile in the URI', async function() { await page.goto(baseUrl + "?profile=physicist", {waitUntil: 'networkidle0'}); const location = await page.evaluate(() => window.location); const search = decodeURIComponent(location.search); // for now, check if redirected to default page - assert.strictEqual(search, '?q={"severity":{"in":"I W E F"}}'); + assert.strictEqual(search, '?q={"severity":{"in":"I W E F"},"level":{"max":1}}'); }); it('should update column headers based on profile when passed in the URI', async () => { @@ -62,7 +63,7 @@ describe('Filter actions test-suite', async () => { it('should update filters based on profile when passed in the URI', async () => { // for now check if the filters are reset once the profile is passed - const expectedParams = '?q={%22severity%22:{%22in%22:%22I%20W%20E%20F%22}}'; + const expectedParams = '?q={%22severity%22:{%22in%22:%22I%20W%20E%20F%22},%22level%22:{%22max%22:1}}'; const searchParams = await page.evaluate(() => { const params = {profile: 'physicist'}; @@ -80,7 +81,7 @@ describe('Filter actions test-suite', async () => { it('should reset filters and show warning message when profile and filters are passed', async () => { // wait until the previous notification is hidden await page.waitForFunction(`window.model.notification.state === 'hidden'`); - const expectedParams = '?q={%22severity%22:{%22in%22:%22I%20W%20E%20F%22}}'; + const expectedParams = '?q={%22severity%22:{%22in%22:%22I%20W%20E%20F%22},%22level%22:{%22max%22:1}}'; const searchParams = await page.evaluate(() => { const params = {profile: "physicist", q: '"severity":{"in":"I W E F"}}'}; window.model.parseLocation(params); @@ -222,6 +223,105 @@ describe('Filter actions test-suite', async () => { assert.strictEqual(criterias.timestamp.since, ''); assert.strictEqual(criterias.timestamp.$since, null); assert.strictEqual(criterias.severity.in, 'I W E F'); - assert.deepStrictEqual(criterias.severity.$in, ['W', 'I', 'E', 'F']); + assert.deepStrictEqual(criterias.severity.$in, ['I', 'W', 'E', 'F']); + }); + + describe('Severity filter disabled states', async () => { + it('should report DEBUG severity as disabled at OPS level', async () => { + const disabled = await page.evaluate(() => { + window.model.log.filter.setCriteria('level', 'max', 1); + return window.model.log.filter.isSeverityDisabled('D'); + }); + + assert.strictEqual(disabled, true); + }); + + it('should report DEBUG severity as enabled when level allows it', async () => { + const disabled = await page.evaluate(() => { + window.model.log.filter.setCriteria('level', 'max', 6); + return window.model.log.filter.isSeverityDisabled('D'); + }); + + assert.strictEqual(disabled, false); + }); + + it('should strip DEBUG from severity filter when switching to OPS', async () => { + const severity = await page.evaluate(() => { + window.model.log.filter.setCriteria('level', 'max', 11); + window.model.log.filter.setCriteria('severity', 'in', 'I W E F D'); + window.model.log.filter.setCriteria('level', 'max', 1); + return { + in: window.model.log.filter.criterias.severity.in, + $in: window.model.log.filter.criterias.severity.$in, + }; + }); + + assert.ok(!severity.$in.includes('D')); + assert.ok(!severity.in.includes('D')); + }); + + it('should strip DEBUG from URL when severity is set before level', async () => { + const severity = await page.evaluate(() => { + window.model.log.filter.fromObject({ severity: { in: 'I W E F D' }, level: { max: 1 } }); + return { + in: window.model.log.filter.criterias.severity.in, + $in: window.model.log.filter.criterias.severity.$in, + }; + }); + + assert.ok(!severity.$in.includes('D')); + assert.ok(!severity.in.includes('D')); + }); + + it('should strip DEBUG from URL when level is set before severity', async () => { + const severity = await page.evaluate(() => { + window.model.log.filter.fromObject({ level: { max: 1 }, severity: { in: 'I W E F D' } }); + return { + in: window.model.log.filter.criterias.severity.in, + $in: window.model.log.filter.criterias.severity.$in, + }; + }); + + assert.ok(!severity.$in.includes('D')); + assert.ok(!severity.in.includes('D')); + }); + + it('should re-enable DEBUG after reset', async () => { + const result = await page.evaluate(() => { + window.model.log.filter.setCriteria('level', 'max', 1); + window.model.log.resetFilters(); + return { + disabled: window.model.log.filter.isSeverityDisabled('D'), + level: window.model.log.filter.criterias.level.max, + }; + }); + + assert.strictEqual(result.disabled, false); + assert.strictEqual(result.level, null); + }); + + it('should disable DEBUG button at OPS level', async () => { + await page.evaluate(() => { + window.model.log.filter.setCriteria('level', 'max', 1); + }); + + await page.waitForFunction(() => { + const buttons = Array.from(document.querySelectorAll('.btn-group button.btn')); + const debugBtn = buttons.find((b) => b.textContent.trim() === 'Debug'); + return debugBtn?.classList.contains('disabled'); + }); + }); + + it('should enable DEBUG button when level is not OPS', async () => { + await page.evaluate(() => { + window.model.log.filter.setCriteria('level', 'max', 11); + }); + + await page.waitForFunction(() => { + const buttons = Array.from(document.querySelectorAll('.btn-group button.btn')); + const debugBtn = buttons.find((b) => b.textContent.trim() === 'Debug'); + return !debugBtn?.classList.contains('disabled'); + }); + }); }); }); From 9317c032a3b4f34a797f77ae5199b3cf9ea46283 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Wed, 27 May 2026 17:53:23 +0200 Subject: [PATCH 5/7] [OGUI-914] Filter change is now observed, default level setting is moved to within resetCriteria, contextMenu resets to OPS level Attach Log to LogFilter as an observer and make filter updates drive behaviour instead of manual notifications. Adjust tests to take into account new default filter state. --- InfoLogger/public/log/Log.js | 16 +++---------- InfoLogger/public/log/cellContextMenu.js | 6 ++--- InfoLogger/public/logFilter/LogFilter.js | 4 ++-- InfoLogger/public/logFilter/commandFilters.js | 2 +- .../test/public/log-context-menu-mocha.js | 24 +++++++++++-------- .../test/public/log-filter-actions-mocha.js | 24 ++++--------------- 6 files changed, 28 insertions(+), 48 deletions(-) diff --git a/InfoLogger/public/log/Log.js b/InfoLogger/public/log/Log.js index b71bc8495..fbcd8e10f 100644 --- a/InfoLogger/public/log/Log.js +++ b/InfoLogger/public/log/Log.js @@ -32,8 +32,8 @@ export default class Log extends Observable { this.model = model; this.filter = new LogFilter(model); - this.filter.setCriteria('level', 'max', 1); this.filter.bubbleTo(this); + this.filter.observe(this.onFilterChange.bind(this)); this.focus = { // show date picker on focus timestampSince: false, @@ -401,23 +401,13 @@ export default class Log extends Observable { } value = copy.join(' '); } - if (this.filter.setCriteria(field, operator, value)) { - this.notifyFilterChanged(); - } - } - - /** - * Reset all filter criteria and notify active mode of the change. - */ - resetFilters() { - this.filter.resetCriteria(); - this.notifyFilterChanged(); + this.filter.setCriteria(field, operator, value); } /** * Notify the active mode (live or query) that filters have changed. */ - notifyFilterChanged() { + onFilterChange() { if (this.isLiveModeRunning()) { this.model.ws.setFilter(this.filter.toStringifyFunction()); this.model.notification.show( diff --git a/InfoLogger/public/log/cellContextMenu.js b/InfoLogger/public/log/cellContextMenu.js index 97898f65b..687c3b76d 100644 --- a/InfoLogger/public/log/cellContextMenu.js +++ b/InfoLogger/public/log/cellContextMenu.js @@ -125,10 +125,10 @@ export const cellContextMenu = (model) => { hideMenu(); }, ), - createMenuItem(iconTrash(), 'danger', 'Clear Level Filter', () => { - model.log.setCriteria('level', 'max', null); + createMenuItem(iconTrash(), 'danger', 'Reset Level Filter', () => { + model.log.setCriteria('level', 'max', 1); hideMenu(); - }, model.log.filter.criterias.level.max === null), + }, model.log.filter.criterias.level.max === 1), ]; } return [ diff --git a/InfoLogger/public/logFilter/LogFilter.js b/InfoLogger/public/logFilter/LogFilter.js index fb7bb5af7..7e70dd646 100644 --- a/InfoLogger/public/logFilter/LogFilter.js +++ b/InfoLogger/public/logFilter/LogFilter.js @@ -429,8 +429,8 @@ export default class LogFilter extends Observable { $in: ['I', 'W', 'E', 'F'], }, level: { - max: null, // 0, 1, 6, 11, 21 - $max: null, // 0, 1, 6, 11, 21 + max: 1, + $max: 1, }, }; this.notify(); diff --git a/InfoLogger/public/logFilter/commandFilters.js b/InfoLogger/public/logFilter/commandFilters.js index 05e018b67..210563363 100644 --- a/InfoLogger/public/logFilter/commandFilters.js +++ b/InfoLogger/public/logFilter/commandFilters.js @@ -104,6 +104,6 @@ const buttonLogLimit = (model, label, limit) => h('button.btn', { * @returns {vnode} - component representing the creation of a button to reset filters */ const buttonReset = (model) => h('button.btn', { - onclick: () => model.log.resetFilters(), + onclick: () => model.log.filter.resetCriteria(), title: 'Reset date, time, matches, excludes, log levels', }, 'Reset filters'); diff --git a/InfoLogger/test/public/log-context-menu-mocha.js b/InfoLogger/test/public/log-context-menu-mocha.js index 522f6cf3b..5a5b37b25 100644 --- a/InfoLogger/test/public/log-context-menu-mocha.js +++ b/InfoLogger/test/public/log-context-menu-mocha.js @@ -276,7 +276,7 @@ describe('Cell Context Menu', async () => { it('should show correct actions for level field', async () => { await openContextMenu(page, 'level', '3', 100, 120); const labels = await getMenuActionLabels(page); - assert.deepStrictEqual(labels, ['Set Level To Support', 'Set Level To Ops', 'Clear Level Filter', 'Copy', 'Open Inspector']); + assert.deepStrictEqual(labels, ['Set Level To Support', 'Set Level To Ops', 'Reset Level Filter', 'Copy', 'Open Inspector']); }); }); @@ -561,7 +561,7 @@ describe('Cell Context Menu', async () => { }); }); - describe('Set/Clear level filter for level field', async () => { + describe('Set/Reset Level Filter for level field', async () => { it('should set level to nearest threshold above via include', async () => { await openContextMenu(page, 'level', '3', 100, 120); @@ -586,34 +586,38 @@ describe('Cell Context Menu', async () => { assert.strictEqual(level.$max, 1); }); - it('should disable "Clear Level Filter" when no level filter is set', async () => { + it('should disable "Reset Level Filter" when level is already cleared', async () => { + await page.evaluate(() => { + window.model.log.filter.setCriteria('level', 'max', 1); + }); + await openContextMenu(page, 'level', '3', 100, 120); - assert.strictEqual(await isMenuItemDisabled(page, 'Clear Level Filter'), true); + assert.strictEqual(await isMenuItemDisabled(page, 'Reset Level Filter'), true); }); - it('should enable "Clear Level Filter" when a level filter is active', async () => { + it('should enable "Reset Level Filter" when a level filter is active', async () => { await page.evaluate(() => { window.model.log.filter.setCriteria('level', 'max', 6); }); await openContextMenu(page, 'level', '3', 100, 120); - assert.strictEqual(await isMenuItemDisabled(page, 'Clear Level Filter'), false); + assert.strictEqual(await isMenuItemDisabled(page, 'Reset Level Filter'), false); }); - it('should clear level filter back to null', async () => { + it('should reset level filter back to default', async () => { await page.evaluate(() => { window.model.log.filter.setCriteria('level', 'max', 6); }); await openContextMenu(page, 'level', '3', 100, 120); - await clickMenuItemByLabel(page, 'Clear Level Filter'); + await clickMenuItemByLabel(page, 'Reset Level Filter'); const level = await page.evaluate(() => window.model.log.filter.criterias.level); - assert.strictEqual(level.max, null); - assert.strictEqual(level.$max, null); + assert.strictEqual(level.max, 1); + assert.strictEqual(level.$max, 1); }); }); diff --git a/InfoLogger/test/public/log-filter-actions-mocha.js b/InfoLogger/test/public/log-filter-actions-mocha.js index c39ce6607..21eb8a098 100644 --- a/InfoLogger/test/public/log-filter-actions-mocha.js +++ b/InfoLogger/test/public/log-filter-actions-mocha.js @@ -95,7 +95,7 @@ describe('Filter actions test-suite', async () => { }); it('should redirect to default filters and show JSON parse error on malformed q in URI', async () => { - const expectedDefaultParams = '?q={"severity":{"in":"I W E F"}}'; + const expectedDefaultParams = '?q={"severity":{"in":"I W E F"},"level":{"max":1}}'; const locationAndNotification = await page.evaluate(() => { const params = { q: '{"severity":{"in":"W I E F"' }; @@ -116,8 +116,8 @@ describe('Filter actions test-suite', async () => { it('should update URI with new encoded "match" criteria', async () => { /* eslint-disable max-len */ - const decodedParams = '?q={"hostname":{"match":"\\"%ald_qdip01%"},"severity":{"in":"I W E F"}}'; - const expectedParams = '?q={%22hostname%22:{%22match%22:%22%5C%22%25ald_qdip01%25%22},%22severity%22:{%22in%22:%22I%20W%20E%20F%22}}'; + const decodedParams = '?q={"hostname":{"match":"\\"%ald_qdip01%"},"severity":{"in":"I W E F"},"level":{"max":1}}'; + const expectedParams = '?q={%22hostname%22:{%22match%22:%22%5C%22%25ald_qdip01%25%22},%22severity%22:{%22in%22:%22I%20W%20E%20F%22},%22level%22:{%22max%22:1}}'; const searchParams = await page.evaluate(() => { window.model.log.filter.setCriteria('hostname', 'match', '"%ald_qdip01%'); window.model.updateRouteOnModelChange(); @@ -130,8 +130,8 @@ describe('Filter actions test-suite', async () => { it('should update URI with new encoded "exclude" criteria', async () => { /* eslint-disable max-len */ - const decodedParams = '?q={"hostname":{"exclude":"\\"%ald_qdip01%"},"severity":{"in":"I W E F"}}'; - const expectedParams = '?q={%22hostname%22:{%22exclude%22:%22%5C%22%25ald_qdip01%25%22},%22severity%22:{%22in%22:%22I%20W%20E%20F%22}}'; + const decodedParams = '?q={"hostname":{"exclude":"\\"%ald_qdip01%"},"severity":{"in":"I W E F"},"level":{"max":1}}'; + const expectedParams = '?q={%22hostname%22:{%22exclude%22:%22%5C%22%25ald_qdip01%25%22},%22severity%22:{%22in%22:%22I%20W%20E%20F%22},%22level%22:{%22max%22:1}}'; const searchParams = await page.evaluate(() => { window.model.log.filter.resetCriteria(); window.model.log.filter.setCriteria('hostname', 'exclude', '"%ald_qdip01%'); @@ -286,20 +286,6 @@ describe('Filter actions test-suite', async () => { assert.ok(!severity.in.includes('D')); }); - it('should re-enable DEBUG after reset', async () => { - const result = await page.evaluate(() => { - window.model.log.filter.setCriteria('level', 'max', 1); - window.model.log.resetFilters(); - return { - disabled: window.model.log.filter.isSeverityDisabled('D'), - level: window.model.log.filter.criterias.level.max, - }; - }); - - assert.strictEqual(result.disabled, false); - assert.strictEqual(result.level, null); - }); - it('should disable DEBUG button at OPS level', async () => { await page.evaluate(() => { window.model.log.filter.setCriteria('level', 'max', 1); From 95e51ad53243eed4e710211e27150a12fbd25dca Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Wed, 27 May 2026 18:22:04 +0200 Subject: [PATCH 6/7] [OGUI-914] Remove unneeded notifyFilterChanged --- InfoLogger/public/log/cellContextMenu.js | 1 - 1 file changed, 1 deletion(-) diff --git a/InfoLogger/public/log/cellContextMenu.js b/InfoLogger/public/log/cellContextMenu.js index 687c3b76d..009f20bcf 100644 --- a/InfoLogger/public/log/cellContextMenu.js +++ b/InfoLogger/public/log/cellContextMenu.js @@ -92,7 +92,6 @@ export const cellContextMenu = (model) => { ), createMenuItem(iconTrash(), 'danger', 'Reset Severity Filter', () => { model.log.filter.setCriteria('severity', 'in', 'I W E F'); - model.log.notifyFilterChanged(); hideMenu(); }, model.log.filter.criterias.severity.in === 'I W E F'), ]; From d59d72b8780b6b952147c7b96d3d3a458550a5ba Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Wed, 27 May 2026 18:25:52 +0200 Subject: [PATCH 7/7] [OGUI-914] Adjust tests to account for that the default state is now level 1 not null --- InfoLogger/test/public/live-mode-mocha.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/InfoLogger/test/public/live-mode-mocha.js b/InfoLogger/test/public/live-mode-mocha.js index e74a5179a..0b7cef97e 100644 --- a/InfoLogger/test/public/live-mode-mocha.js +++ b/InfoLogger/test/public/live-mode-mocha.js @@ -60,6 +60,7 @@ describe('Live Mode test-suite', async () => { await page.evaluate(() => window.model.log.liveStop('Paused')); await page.evaluate(() => { window.model.log.filter.resetCriteria(); + window.model.log.filter.setCriteria('level', 'max', null); window.model.log.filter.setCriteria('hostname', 'match', 'aldaqecs01-v1'); }); await page.evaluate(() => window.model.log.liveStart()); @@ -76,6 +77,7 @@ describe('Live Mode test-suite', async () => { await page.evaluate(() => window.model.log.liveStop('Query')); await page.evaluate(() => { window.model.log.filter.resetCriteria(); + window.model.log.filter.setCriteria('level', 'max', null); window.model.log.filter.setCriteria('hostname', 'exclude', 'aldaqdip01'); }); await page.evaluate(() => window.model.log.liveStart()); @@ -90,7 +92,10 @@ describe('Live Mode test-suite', async () => { it('should filter messages based on SQL Wildcards `hostname` excluding `%ldaqdip%` and username matching `a_iceda_`' + ' without changing state of live mode', async () => { - await page.evaluate(() => window.model.log.filter.resetCriteria()); + await page.evaluate(() => { + window.model.log.filter.resetCriteria(); + window.model.log.filter.setCriteria('level', 'max', null); + }); await page.evaluate(() => { window.model.log.setCriteria('hostname', 'exclude', '%ldaqdip%'); window.model.log.setCriteria('username', 'match', 'a_iceda_');