From 0fb608ce492a2e7508e20e7c53225f51f78f3e08 Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Thu, 19 Mar 2026 12:43:52 +0100 Subject: [PATCH 1/8] feat: make pressElement more robust by using waitForFunction --- test/public/defaults.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/test/public/defaults.js b/test/public/defaults.js index d841c4bc05..b2a564126c 100644 --- a/test/public/defaults.js +++ b/test/public/defaults.js @@ -275,12 +275,26 @@ exports.waitForNavigation = waitForNavigation; * @returns {Promise} Whether the element was clickable or not. */ module.exports.pressElement = async (page, selector, jsClick = false) => { - const elementHandler = await page.waitForSelector(selector); + await page.waitForFunction( + (sel, isJsClick) => { + const element = document.querySelector(sel); + + if (!element) { + return false; + } + + if (isJsClick) { + element.click(); + } + + return true; + }, + {}, + selector, jsClick + ); - if (jsClick) { - await elementHandler.evaluate((element) => element.click()); - } else { - await elementHandler.click(selector); + if (!jsClick) { + await page.click(selector); } }; From d4f9e7216a0d110e76db952a6f6aa5f6ee4e0bad Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Thu, 19 Mar 2026 12:44:35 +0100 Subject: [PATCH 2/8] feat: make validateData wait for tablecolumns --- test/public/defaults.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/public/defaults.js b/test/public/defaults.js index b2a564126c..f15b01a9ab 100644 --- a/test/public/defaults.js +++ b/test/public/defaults.js @@ -869,10 +869,10 @@ module.exports.testTableSortingByColumn = async (page, columnId) => { * @return {Promise} resolve once data was successfully validated */ module.exports.validateTableData = async (page, validators) => { - await page.waitForSelector('table tbody'); for (const [columnId, validator] of validators) { + await page.waitForSelector(`table tbody .column-${columnId}`); + const columnData = await getColumnCellsInnerTexts(page, columnId); - expect(columnData, `Too few values for column ${columnId} or there is no such column`).to.be.length.greaterThan(0); expect( columnData.every((cellData) => validator(cellData)), `Invalid data in column ${columnId}: (${columnData})`, From 3c64037d8341356c47be16e049289a0d62b359c9 Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Thu, 19 Mar 2026 12:45:16 +0100 Subject: [PATCH 3/8] fix: fix export tests by having waiting for the button to become enabled before pressing it. --- test/public/runs/overview.test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/public/runs/overview.test.js b/test/public/runs/overview.test.js index 807b821ffc..f4f5e0fb0d 100644 --- a/test/public/runs/overview.test.js +++ b/test/public/runs/overview.test.js @@ -867,6 +867,12 @@ module.exports = () => { describe("Export", () => { const EXPORT_RUNS_TRIGGER_SELECTOR = '#export-data-trigger'; + const waitForExportButton = async () => + await page.waitForFunction((selector) => { + const button = document.querySelector(selector); + return button && !button.disabled; + }, {}, EXPORT_RUNS_TRIGGER_SELECTOR); + before(() => goToPage(page, 'run-overview')); @@ -885,6 +891,7 @@ module.exports = () => { let exportModal = await page.$('#export-data-modal'); expect(exportModal).to.be.null; + await waitForExportButton(); await page.$eval(EXPORT_RUNS_TRIGGER_SELECTOR, (button) => button.click()); await page.waitForSelector('#export-data-modal', { timeout: 5000 }); exportModal = await page.$('#export-data-modal'); @@ -893,6 +900,7 @@ module.exports = () => { }); it('should successfully display information when export will be truncated', async () => { + await waitForExportButton(); await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR, true); const truncatedExportWarning = await page.waitForSelector('#export-data-modal #truncated-export-warning'); @@ -912,6 +920,7 @@ module.exports = () => { }); it('should successfully export filtered runs', async () => { + await waitForExportButton(); const targetFileName = 'data.json'; // First export From c7c6527e8615ffeecb4ffcfbc9a2b89b0b5e7cdf Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Thu, 19 Mar 2026 13:01:57 +0100 Subject: [PATCH 4/8] fix: store table comparison by storing the table html before the change trigger occurs rather than after --- test/public/runs/runsPerDataPass.overview.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/public/runs/runsPerDataPass.overview.test.js b/test/public/runs/runsPerDataPass.overview.test.js index 5eeef9c018..73f977a303 100644 --- a/test/public/runs/runsPerDataPass.overview.test.js +++ b/test/public/runs/runsPerDataPass.overview.test.js @@ -665,8 +665,8 @@ module.exports = () => { // Press again actions dropdown to re-trigger render await pressElement(page, '#actions-dropdown-button .popover-trigger', true); setConfirmationDialogToBeAccepted(page); - await pressElement(page, `${popoverSelector} button:nth-child(4)`, true); const oldTable = await page.waitForSelector('table').then((table) => table.evaluate((t) => t.innerHTML)); + await pressElement(page, `${popoverSelector} button:nth-child(4)`, true); await pressElement(page, '#actions-dropdown-button .popover-trigger', true); await waitForTableLength(page, 3, undefined, oldTable); // Processing of data might take a bit of time, but then expect QC flag button to be there From 8d2d30169a3a022c87ea37ed7a583f5f5d3ccf7b Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Thu, 26 Mar 2026 15:19:58 +0100 Subject: [PATCH 5/8] separate the click from the waitForFunction check --- test/public/defaults.js | 23 ++++++------------- .../runs/runsPerLhcPeriod.overview.test.js | 14 +++++++---- .../runsPerSimulationPass.overview.test.js | 13 ++++++++--- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/test/public/defaults.js b/test/public/defaults.js index f15b01a9ab..32c89eb20c 100644 --- a/test/public/defaults.js +++ b/test/public/defaults.js @@ -275,25 +275,16 @@ exports.waitForNavigation = waitForNavigation; * @returns {Promise} Whether the element was clickable or not. */ module.exports.pressElement = async (page, selector, jsClick = false) => { - await page.waitForFunction( - (sel, isJsClick) => { - const element = document.querySelector(sel); - - if (!element) { - return false; - } + await page.waitForFunction( (sel) => !!document.querySelector(sel), {}, selector); - if (isJsClick) { + if (jsClick) { + await page.evaluate((sel) => { + const element = document.querySelector(sel); + if (element) { element.click(); } - - return true; - }, - {}, - selector, jsClick - ); - - if (!jsClick) { + }, selector); + } else { await page.click(selector); } }; diff --git a/test/public/runs/runsPerLhcPeriod.overview.test.js b/test/public/runs/runsPerLhcPeriod.overview.test.js index 75b5eb978c..a539ce4674 100644 --- a/test/public/runs/runsPerLhcPeriod.overview.test.js +++ b/test/public/runs/runsPerLhcPeriod.overview.test.js @@ -75,6 +75,13 @@ module.exports = () => { after(async () => { [page, browser] = await defaultAfter(page, browser); }); + const EXPORT_RUNS_TRIGGER_SELECTOR = '#export-data-trigger'; + + const waitForExportButton = async () => + await page.waitForFunction((selector) => { + const button = document.querySelector(selector); + return button && !button.disabled; + }, {}, EXPORT_RUNS_TRIGGER_SELECTOR); it('loads the page successfully', async () => { const response = await goToPage(page, 'runs-per-lhc-period', { queryParameters: { lhcPeriodId: 1 } }); @@ -204,7 +211,6 @@ module.exports = () => { await waitForTableLength(page, 4); }); - const EXPORT_RUNS_TRIGGER_SELECTOR = '#export-data-trigger'; it('should successfully export all runs per lhc Period', async () => { await page.evaluate(() => { @@ -213,7 +219,7 @@ module.exports = () => { }); const targetFileName = 'data.json'; - + await waitForExportButton(); // First export await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR, true); await page.waitForSelector('select.form-control', { timeout: 200 }); @@ -286,9 +292,9 @@ module.exports = () => { await navigateToRunsPerLhcPeriod(page, 1, 4); const targetFileName = 'data.csv'; - + await waitForExportButton(); // Export - await pressElement(page, '#export-data-trigger'); + await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR); await page.waitForSelector('#export-data-modal'); await page.waitForSelector('#send:disabled'); await page.waitForSelector('.form-control'); diff --git a/test/public/runs/runsPerSimulationPass.overview.test.js b/test/public/runs/runsPerSimulationPass.overview.test.js index 112d6b5b7d..64bb5bc1d8 100644 --- a/test/public/runs/runsPerSimulationPass.overview.test.js +++ b/test/public/runs/runsPerSimulationPass.overview.test.js @@ -74,6 +74,14 @@ module.exports = () => { [page, browser] = await defaultAfter(page, browser); }); + const EXPORT_RUNS_TRIGGER_SELECTOR = '#export-data-trigger'; + + const waitForExportButton = async () => + await page.waitForFunction((selector) => { + const button = document.querySelector(selector); + return button && !button.disabled; + }, {}, EXPORT_RUNS_TRIGGER_SELECTOR); + it('loads the page successfully', async () => { const response = await goToPage(page, 'runs-per-simulation-pass', { queryParameters: { simulationPassId: 2 } }); @@ -235,11 +243,10 @@ module.exports = () => { it('should successfully export runs', async () => { await navigateToRunsPerSimulationPass(page, 1, 2, 3); - const EXPORT_RUNS_TRIGGER_SELECTOR = '#export-data-trigger'; - const targetFileName = 'data.json'; // Export + await waitForExportButton(); await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR); await page.waitForSelector('#export-data-modal'); await page.waitForSelector('#send:disabled'); @@ -270,7 +277,7 @@ module.exports = () => { const targetFileName = 'data.csv'; // Export - await pressElement(page, '#export-data-trigger'); + await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR); await page.waitForSelector('#export-data-modal'); await page.waitForSelector('#send:disabled'); await page.waitForSelector('.form-control'); From 5bf46376d981f0189c13ae9084599d57652f9a0f Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Thu, 26 Mar 2026 15:45:46 +0100 Subject: [PATCH 6/8] remove the element check from the evaluate call --- test/public/defaults.js | 18 ++++++++++++------ test/public/runs/overview.test.js | 13 ++++--------- .../runs/runsPerLhcPeriod.overview.test.js | 11 +++-------- .../runsPerSimulationPass.overview.test.js | 10 +++------- 4 files changed, 22 insertions(+), 30 deletions(-) diff --git a/test/public/defaults.js b/test/public/defaults.js index 32c89eb20c..3fd37125c2 100644 --- a/test/public/defaults.js +++ b/test/public/defaults.js @@ -278,12 +278,7 @@ module.exports.pressElement = async (page, selector, jsClick = false) => { await page.waitForFunction( (sel) => !!document.querySelector(sel), {}, selector); if (jsClick) { - await page.evaluate((sel) => { - const element = document.querySelector(sel); - if (element) { - element.click(); - } - }, selector); + await page.evaluate((sel) => document.querySelector(sel).click(), selector); } else { await page.click(selector); } @@ -982,3 +977,14 @@ module.exports.resetFilters = async (page) => { { timeout: 5000 }, ); }; + +/** + * Fuction that waits for a button to become active + * @param {puppeteer.page} page page handler + * @param {string} selector Css selector for the button. + */ +module.exports.waitForButtonToBecomeActive = async (page, selector) => await page.waitForFunction((sel) => { + const button = document.querySelector(sel); + return button && !button.disabled; + }, {}, selector); + diff --git a/test/public/runs/overview.test.js b/test/public/runs/overview.test.js index f4f5e0fb0d..3b7cb97da4 100644 --- a/test/public/runs/overview.test.js +++ b/test/public/runs/overview.test.js @@ -40,6 +40,7 @@ const { getColumnCellsInnerTexts, resetFilters, openFilteringPanel, + waitForButtonToBecomeActive, } = require('../defaults.js'); const { RUN_QUALITIES, RunQualities } = require('../../../lib/domain/enums/RunQualities.js'); const { resetDatabaseContent } = require('../../utilities/resetDatabaseContent.js'); @@ -867,12 +868,6 @@ module.exports = () => { describe("Export", () => { const EXPORT_RUNS_TRIGGER_SELECTOR = '#export-data-trigger'; - const waitForExportButton = async () => - await page.waitForFunction((selector) => { - const button = document.querySelector(selector); - return button && !button.disabled; - }, {}, EXPORT_RUNS_TRIGGER_SELECTOR); - before(() => goToPage(page, 'run-overview')); @@ -891,7 +886,7 @@ module.exports = () => { let exportModal = await page.$('#export-data-modal'); expect(exportModal).to.be.null; - await waitForExportButton(); + await waitForButtonToBecomeActive(page, EXPORT_RUNS_TRIGGER_SELECTOR); await page.$eval(EXPORT_RUNS_TRIGGER_SELECTOR, (button) => button.click()); await page.waitForSelector('#export-data-modal', { timeout: 5000 }); exportModal = await page.$('#export-data-modal'); @@ -900,7 +895,7 @@ module.exports = () => { }); it('should successfully display information when export will be truncated', async () => { - await waitForExportButton(); + await waitForButtonToBecomeActive(page, EXPORT_RUNS_TRIGGER_SELECTOR); await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR, true); const truncatedExportWarning = await page.waitForSelector('#export-data-modal #truncated-export-warning'); @@ -920,7 +915,7 @@ module.exports = () => { }); it('should successfully export filtered runs', async () => { - await waitForExportButton(); + await waitForButtonToBecomeActive(page, EXPORT_RUNS_TRIGGER_SELECTOR); const targetFileName = 'data.json'; // First export diff --git a/test/public/runs/runsPerLhcPeriod.overview.test.js b/test/public/runs/runsPerLhcPeriod.overview.test.js index a539ce4674..273baca07d 100644 --- a/test/public/runs/runsPerLhcPeriod.overview.test.js +++ b/test/public/runs/runsPerLhcPeriod.overview.test.js @@ -32,6 +32,7 @@ const { expectColumnValues, openFilteringPanel, resetFilters, + waitForButtonToBecomeActive } = require('../defaults.js'); const { RUN_QUALITIES, RunQualities } = require('../../../lib/domain/enums/RunQualities.js'); const { resetDatabaseContent } = require('../../utilities/resetDatabaseContent.js'); @@ -77,12 +78,6 @@ module.exports = () => { }); const EXPORT_RUNS_TRIGGER_SELECTOR = '#export-data-trigger'; - const waitForExportButton = async () => - await page.waitForFunction((selector) => { - const button = document.querySelector(selector); - return button && !button.disabled; - }, {}, EXPORT_RUNS_TRIGGER_SELECTOR); - it('loads the page successfully', async () => { const response = await goToPage(page, 'runs-per-lhc-period', { queryParameters: { lhcPeriodId: 1 } }); @@ -219,7 +214,7 @@ module.exports = () => { }); const targetFileName = 'data.json'; - await waitForExportButton(); + await waitForButtonToBecomeActive(page, EXPORT_RUNS_TRIGGER_SELECTOR); // First export await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR, true); await page.waitForSelector('select.form-control', { timeout: 200 }); @@ -292,7 +287,7 @@ module.exports = () => { await navigateToRunsPerLhcPeriod(page, 1, 4); const targetFileName = 'data.csv'; - await waitForExportButton(); + await waitForButtonToBecomeActive(page, EXPORT_RUNS_TRIGGER_SELECTOR); // Export await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR); await page.waitForSelector('#export-data-modal'); diff --git a/test/public/runs/runsPerSimulationPass.overview.test.js b/test/public/runs/runsPerSimulationPass.overview.test.js index 64bb5bc1d8..e6748ffb94 100644 --- a/test/public/runs/runsPerSimulationPass.overview.test.js +++ b/test/public/runs/runsPerSimulationPass.overview.test.js @@ -31,6 +31,7 @@ const { testTableSortingByColumn, waitForTableLength, expectColumnValues, + waitForButtonToBecomeActive, } = require('../defaults.js'); const { expect } = chai; @@ -76,12 +77,6 @@ module.exports = () => { const EXPORT_RUNS_TRIGGER_SELECTOR = '#export-data-trigger'; - const waitForExportButton = async () => - await page.waitForFunction((selector) => { - const button = document.querySelector(selector); - return button && !button.disabled; - }, {}, EXPORT_RUNS_TRIGGER_SELECTOR); - it('loads the page successfully', async () => { const response = await goToPage(page, 'runs-per-simulation-pass', { queryParameters: { simulationPassId: 2 } }); @@ -246,7 +241,7 @@ module.exports = () => { const targetFileName = 'data.json'; // Export - await waitForExportButton(); + await waitForButtonToBecomeActive(page, EXPORT_RUNS_TRIGGER_SELECTOR); await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR); await page.waitForSelector('#export-data-modal'); await page.waitForSelector('#send:disabled'); @@ -277,6 +272,7 @@ module.exports = () => { const targetFileName = 'data.csv'; // Export + await waitForButtonToBecomeActive(page, EXPORT_RUNS_TRIGGER_SELECTOR); await pressElement(page, EXPORT_RUNS_TRIGGER_SELECTOR); await page.waitForSelector('#export-data-modal'); await page.waitForSelector('#send:disabled'); From 76fc4597485d3eaf9a456047c845020c0b624617 Mon Sep 17 00:00:00 2001 From: GuustMetz Date: Thu, 26 Mar 2026 16:11:46 +0100 Subject: [PATCH 7/8] undo pressElement change --- test/public/defaults.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/test/public/defaults.js b/test/public/defaults.js index 3fd37125c2..7fd200274f 100644 --- a/test/public/defaults.js +++ b/test/public/defaults.js @@ -275,11 +275,25 @@ exports.waitForNavigation = waitForNavigation; * @returns {Promise} Whether the element was clickable or not. */ module.exports.pressElement = async (page, selector, jsClick = false) => { - await page.waitForFunction( (sel) => !!document.querySelector(sel), {}, selector); + await page.waitForFunction( + (sel, isJsClick) => { + const element = document.querySelector(sel); + + if (!element) { + return false; + } + + if (isJsClick) { + element.click(); + } + + return true; + }, + {}, + selector, jsClick + ); - if (jsClick) { - await page.evaluate((sel) => document.querySelector(sel).click(), selector); - } else { + if (!jsClick) { await page.click(selector); } }; From 76bb2cb4e0c547ed23dda8662c887d94f6f32f14 Mon Sep 17 00:00:00 2001 From: NarrowsProjects <150556207+NarrowsProjects@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:43:04 +0100 Subject: [PATCH 8/8] Update defaults.js --- test/public/defaults.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/public/defaults.js b/test/public/defaults.js index 7fd200274f..0523d8462b 100644 --- a/test/public/defaults.js +++ b/test/public/defaults.js @@ -282,7 +282,7 @@ module.exports.pressElement = async (page, selector, jsClick = false) => { if (!element) { return false; } - + // Moving the click to outside the function causes it to fail for unknown reasons if (isJsClick) { element.click(); }