From 0a7c441af0d7dc48bee342153a209ff2530fe462 Mon Sep 17 00:00:00 2001 From: Gianpaolo Date: Tue, 28 Apr 2026 22:02:59 +0200 Subject: [PATCH 1/6] fix(e2e): use getByRole('combobox') to open employment MultiSelect dropdown --- tests/e2e/plan/modules/employment.spec.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/e2e/plan/modules/employment.spec.ts b/tests/e2e/plan/modules/employment.spec.ts index 0944be12c..ba2ecfc86 100644 --- a/tests/e2e/plan/modules/employment.spec.ts +++ b/tests/e2e/plan/modules/employment.spec.ts @@ -54,13 +54,7 @@ test.describe('The employment module defines the screen participants employments }); test('It should have a number > 0 as an output', async ({ i18n, page }) => { - await employmentModule - .elements() - .module() - .getByLabel('Employment status') - .locator('svg') - .nth(2) - .click(); + await employmentModule.elements().module().getByRole('combobox').click(); await page .getByRole('option', { name: i18n.t('__PLAN_PAGE_MODULE_EMPLOYMENT_OPTION_UNEMPLOYED'), @@ -68,7 +62,6 @@ test.describe('The employment module defines the screen participants employments .click(); const response = await planPage.saveConfiguration(); const data = response.request().postDataJSON(); - // Find the locality module and check its output const localityModuleData = data.config.modules.find( (m: any) => m.type === 'employment' ); From 11d4a2249a3b8b0ec82aafefa4123d3289e0f069 Mon Sep 17 00:00:00 2001 From: Gianpaolo Date: Tue, 28 Apr 2026 22:25:24 +0200 Subject: [PATCH 2/6] fix(e2e): click moduleInput trigger div instead of hidden combobox input --- tests/e2e/plan/modules/employment.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/plan/modules/employment.spec.ts b/tests/e2e/plan/modules/employment.spec.ts index ba2ecfc86..b9e2f2464 100644 --- a/tests/e2e/plan/modules/employment.spec.ts +++ b/tests/e2e/plan/modules/employment.spec.ts @@ -54,7 +54,7 @@ test.describe('The employment module defines the screen participants employments }); test('It should have a number > 0 as an output', async ({ i18n, page }) => { - await employmentModule.elements().module().getByRole('combobox').click(); + await employmentModule.elements().moduleInput().click(); await page .getByRole('option', { name: i18n.t('__PLAN_PAGE_MODULE_EMPLOYMENT_OPTION_UNEMPLOYED'), From 6b8bb361e42bcbc3cba46acfa864e670caad77cb Mon Sep 17 00:00:00 2001 From: Gianpaolo Date: Wed, 29 Apr 2026 10:26:07 +0200 Subject: [PATCH 3/6] fix(e2e): wait for UNEMPLOYED tag before saving to avoid race condition in CI --- tests/e2e/plan/modules/employment.spec.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/e2e/plan/modules/employment.spec.ts b/tests/e2e/plan/modules/employment.spec.ts index b9e2f2464..c38073522 100644 --- a/tests/e2e/plan/modules/employment.spec.ts +++ b/tests/e2e/plan/modules/employment.spec.ts @@ -60,6 +60,16 @@ test.describe('The employment module defines the screen participants employments name: i18n.t('__PLAN_PAGE_MODULE_EMPLOYMENT_OPTION_UNEMPLOYED'), }) .click(); + await expect( + employmentModule + .elements() + .module() + .getByLabel( + `${i18n.t( + '__PLAN_PAGE_MODULE_EMPLOYMENT_OPTION_UNEMPLOYED' + )}, press delete or` + ) + ).toBeVisible(); const response = await planPage.saveConfiguration(); const data = response.request().postDataJSON(); const localityModuleData = data.config.modules.find( From 24c81bef3a5dc19e2d1db7ea87aafb46fd85fec1 Mon Sep 17 00:00:00 2001 From: Gianpaolo Date: Wed, 29 Apr 2026 13:03:52 +0200 Subject: [PATCH 4/6] fix: make employment multiselect interaction reliable in CI Target the StyledTrigger element directly instead of the outer wrapper, whose onClick is a no-op in non-editable mode, causing intermittent failures when the click coordinates miss the inner trigger in CI. Also wait for the option to be visible before clicking it. --- tests/e2e/plan/modules/employment.spec.ts | 10 +++++----- tests/fixtures/pages/Plan/Module_employment.ts | 5 ++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/e2e/plan/modules/employment.spec.ts b/tests/e2e/plan/modules/employment.spec.ts index c38073522..712355f99 100644 --- a/tests/e2e/plan/modules/employment.spec.ts +++ b/tests/e2e/plan/modules/employment.spec.ts @@ -55,11 +55,11 @@ test.describe('The employment module defines the screen participants employments test('It should have a number > 0 as an output', async ({ i18n, page }) => { await employmentModule.elements().moduleInput().click(); - await page - .getByRole('option', { - name: i18n.t('__PLAN_PAGE_MODULE_EMPLOYMENT_OPTION_UNEMPLOYED'), - }) - .click(); + const unemployedOption = page.getByRole('option', { + name: i18n.t('__PLAN_PAGE_MODULE_EMPLOYMENT_OPTION_UNEMPLOYED'), + }); + await expect(unemployedOption).toBeVisible(); + await unemployedOption.click(); await expect( employmentModule .elements() diff --git a/tests/fixtures/pages/Plan/Module_employment.ts b/tests/fixtures/pages/Plan/Module_employment.ts index 8205d789b..3467111a3 100644 --- a/tests/fixtures/pages/Plan/Module_employment.ts +++ b/tests/fixtures/pages/Plan/Module_employment.ts @@ -18,7 +18,10 @@ export class EmploymentModule { module: () => this.page.getByTestId('employment-module'), moduleError: () => this.elements().module().getByTestId('employment-error'), - moduleInput: () => this.page.getByTestId('employment-input'), + moduleInput: () => + this.page + .getByTestId('employment-module') + .locator('[data-garden-id="dropdowns.combobox.trigger"]'), }; } From 5b2ad45727d627cdcc0a8caa3be7b1007de4bf4f Mon Sep 17 00:00:00 2001 From: Gianpaolo Date: Wed, 29 Apr 2026 14:27:33 +0200 Subject: [PATCH 5/6] fix(e2e): fix employment module test to interact with the combobox input correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MultiSelect component uses isAutocomplete which renders the listbox via a portal (listboxAppendToNode={document.body}). Clicking a portal option causes mousedown to blur the combobox input before the click registers, making the test flaky in CI. The trigger click also leaves focus on the wrapper div, not the inner input, so keyboard.type() was going to the wrong element. Fix: after opening the dropdown, wait for the input[role="combobox"] to be visible, fill it to filter options, then use keyboard ArrowDown+Enter to select via the input:keyDown:Enter path — no mousedown on the portal option. --- tests/e2e/plan/modules/employment.spec.ts | 35 +++++++++++++++++------ 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/tests/e2e/plan/modules/employment.spec.ts b/tests/e2e/plan/modules/employment.spec.ts index 712355f99..21840c8e6 100644 --- a/tests/e2e/plan/modules/employment.spec.ts +++ b/tests/e2e/plan/modules/employment.spec.ts @@ -54,21 +54,38 @@ test.describe('The employment module defines the screen participants employments }); test('It should have a number > 0 as an output', async ({ i18n, page }) => { + const optionLabel = i18n.t( + '__PLAN_PAGE_MODULE_EMPLOYMENT_OPTION_UNEMPLOYED' + ); + + // Open the dropdown by clicking the trigger await employmentModule.elements().moduleInput().click(); - const unemployedOption = page.getByRole('option', { - name: i18n.t('__PLAN_PAGE_MODULE_EMPLOYMENT_OPTION_UNEMPLOYED'), - }); + + // The actual input element (hidden initially, visible after dropdown opens) + const comboboxInput = employmentModule + .elements() + .module() + .locator('input[role="combobox"]'); + await comboboxInput.waitFor({ state: 'visible' }); + + // Fill the input directly to ensure focus lands on the right element and + // triggers input:change, so matchingOptions filters down to only UNEMPLOYED + await comboboxInput.fill(optionLabel); + + // Verify the filtered option is visible in the portal listbox + const unemployedOption = page.getByRole('option', { name: optionLabel }); await expect(unemployedOption).toBeVisible(); - await unemployedOption.click(); + + // Select via keyboard on the input element - avoids mousedown/blur race + // condition that occurs when clicking a portal-rendered option + await comboboxInput.press('ArrowDown'); + await comboboxInput.press('Enter'); + await expect( employmentModule .elements() .module() - .getByLabel( - `${i18n.t( - '__PLAN_PAGE_MODULE_EMPLOYMENT_OPTION_UNEMPLOYED' - )}, press delete or` - ) + .getByLabel(`${optionLabel}, press delete or`) ).toBeVisible(); const response = await planPage.saveConfiguration(); const data = response.request().postDataJSON(); From 5913464cf254a6cec7743035e89457f2cdab7036 Mon Sep 17 00:00:00 2001 From: Gianpaolo Date: Wed, 29 Apr 2026 15:29:14 +0200 Subject: [PATCH 6/6] fix(e2e): wait for option filtering before keyboard navigation in employment test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After fill(), React needs two renders to propagate inputValue→matchingOptions→ aria-hidden on non-matching options. In CI this happens too slowly, so ArrowDown was firing before Garden v9's ne.values updated, causing navigation to land on Employee instead of Unemployed. Fix: wait for getByRole('option').toHaveCount(1) before pressing ArrowDown. Hidden options receive aria-hidden=true and drop out of getByRole results, so count=1 reliably signals that Downshift's navigation pool is ready. --- tests/e2e/plan/modules/employment.spec.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/e2e/plan/modules/employment.spec.ts b/tests/e2e/plan/modules/employment.spec.ts index 21840c8e6..bac7bf545 100644 --- a/tests/e2e/plan/modules/employment.spec.ts +++ b/tests/e2e/plan/modules/employment.spec.ts @@ -72,9 +72,10 @@ test.describe('The employment module defines the screen participants employments // triggers input:change, so matchingOptions filters down to only UNEMPLOYED await comboboxInput.fill(optionLabel); - // Verify the filtered option is visible in the portal listbox - const unemployedOption = page.getByRole('option', { name: optionLabel }); - await expect(unemployedOption).toBeVisible(); + // Wait for Garden v9 to finish filtering: hidden options receive aria-hidden=true + // which removes them from getByRole results. Only UNEMPLOYED should remain. + // This ensures ne.values (Downshift's navigation pool) is updated before ArrowDown. + await expect(page.getByRole('option')).toHaveCount(1); // Select via keyboard on the input element - avoids mousedown/blur race // condition that occurs when clicking a portal-rendered option