diff --git a/xwiki-platform-core/xwiki-platform-panels/xwiki-platform-panels-test/xwiki-platform-panels-test-pageobjects/src/main/java/org/xwiki/panels/test/po/NavigationPanelAdministrationPage.java b/xwiki-platform-core/xwiki-platform-panels/xwiki-platform-panels-test/xwiki-platform-panels-test-pageobjects/src/main/java/org/xwiki/panels/test/po/NavigationPanelAdministrationPage.java index c336bb4445c1..850bd3797c1b 100644 --- a/xwiki-platform-core/xwiki-platform-panels/xwiki-platform-panels-test/xwiki-platform-panels-test-pageobjects/src/main/java/org/xwiki/panels/test/po/NavigationPanelAdministrationPage.java +++ b/xwiki-platform-core/xwiki-platform-panels/xwiki-platform-panels-test/xwiki-platform-panels-test-pageobjects/src/main/java/org/xwiki/panels/test/po/NavigationPanelAdministrationPage.java @@ -19,6 +19,7 @@ */ package org.xwiki.panels.test.po; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -109,6 +110,11 @@ public void exclude(String... pages) } if (source != null) { getDriver().dragAndDrop(source, this.excludedPagesPane); + // The drop is handled by synchronous client-side JavaScript (no server round-trip), but the resulting DOM + // mutation (hiding the tree nodes, toggling the "No pages found" placeholder) may not be reflected yet by + // the time the drag-and-drop Selenium command returns. Wait for it explicitly so callers don't read the + // tree state mid-mutation. + waitUntilTopLevelPagesState(Arrays.asList(pages), false); } } @@ -133,9 +139,26 @@ public void include(String... pages) } if (source != null) { getDriver().dragAndDrop(source, this.treeElement); + // See the comment in #exclude(String...) about why we need to wait for the drop to be fully processed. + waitUntilTopLevelPagesState(Arrays.asList(pages), true); } } + /** + * Wait until the given pages are (or aren't) part of the visible top level pages of the navigation tree. + * + * @param pages the pages to check + * @param visible {@code true} to wait until the pages are visible top level pages, {@code false} to wait until + * they aren't anymore + */ + private void waitUntilTopLevelPagesState(List pages, boolean visible) + { + getDriver().waitUntilCondition(driver -> { + List topLevelPages = getNavigationTree().getTopLevelPages(); + return pages.stream().allMatch(page -> topLevelPages.contains(page) == visible); + }); + } + public boolean isExcludingTopLevelExtensionPages() { return this.excludeTopLevelExtensionPagesCheckbox.isSelected(); @@ -169,6 +192,9 @@ public NavigationTreeElement getNavigationTree() private WebElement getPageByTitle(String pageTitle) { + // Make sure the tree has finished (re)loading before looking up a node in it, otherwise we might race with + // its initial AJAX load, e.g. right after the page was reloaded. + getNavigationTree(); return getDriver().findElementWithoutWaiting(this.treeElement, By.xpath("(.//li[contains(@class, 'jstree-node')]/a[. = '" + pageTitle + "'])")); }