From 5acd815e0cedf8b4a99dde4614813b0b3759d43b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Sat, 20 Jun 2026 13:56:38 +0100 Subject: [PATCH] refactor(e2e): tighten option-active scenario wait + fix comment option-active now waits for and returns the [data-active] option (the element that actually signals the keyboard-active state) instead of the already-visible content panel, so target.waitFor() in the driver confirms the state landed. Adds a comment for Task 10 copiers about async-focus components. Also corrects the no-results comment: fixme:true is set on the scenario object here; scenarios.spec.mjs only reads it. Co-Authored-By: Claude Sonnet 4.6 --- test/e2e/support/scenarios/combobox.mjs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/test/e2e/support/scenarios/combobox.mjs b/test/e2e/support/scenarios/combobox.mjs index 0c91ff0..ee40a23 100644 --- a/test/e2e/support/scenarios/combobox.mjs +++ b/test/e2e/support/scenarios/combobox.mjs @@ -16,7 +16,8 @@ // carries role="status" but is a direct child of role="listbox". ARIA does // not permit role="status" as a listbox child; axe flags this as // aria-required-children (critical). This is tracked in FOLLOWUPS.md. -// The fixme flag on that case is set in scenarios.spec.mjs. +// That case carries `fixme: true` below; scenarios.spec.mjs reads the flag +// and runs it via test.fixme so the defect is tracked, not silenced. const preview = (page) => page.locator("[data-shadix-preview]").first(); const trigger = (page) => preview(page).locator("[data-slot='combobox-trigger']").first(); @@ -24,6 +25,8 @@ const content = (page) => preview(page).locator("[data-slot='combobox-content']" const search = (page) => content(page).locator("[data-slot='combobox-input']"); // Visible options: the hook adds class="hidden" to non-matching items. const visibleOption = (page) => content(page).locator("[role='option']:visible").first(); +// Active option: the hook marks the keyboard-active item with [data-active]. +const activeOption = (page) => content(page).locator("[role='option'][data-active]").first(); const empty = (page) => content(page).locator("[data-slot='combobox-empty']"); export default [ @@ -50,11 +53,14 @@ export default [ run: async (page) => { await trigger(page).click(); await content(page).waitFor({ state: "visible" }); - // ArrowDown from the search input activates the first visible option. - // The hook focuses the search input on open, so ArrowDown works immediately. + // ArrowDown from the search input activates the first visible option + // (the hook marks it [data-active] + points aria-activedescendant at it). + // keyboard nav is synchronous here; for a component with async focus, wait + // for the active option's [aria-selected]/[data-active] before returning. await search(page).press("ArrowDown"); - await visibleOption(page).waitFor({ state: "visible" }); - return content(page); + await activeOption(page).waitFor({ state: "visible" }); + // Return the active option itself — it is the element that signals the state. + return activeOption(page); }, }, { -- 2.51.2