Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XWIKI-22582: Add automated test for "Switch between Icon Themes" #3628

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public class ThemesAdministrationSectionPage extends AdministrationSectionPage
* The select input to set the color theme.
*/
@FindBy(id = "XWiki.XWikiPreferences_0_colorTheme")
private WebElement colorThemeInput;

/**
* The select input to set the icon theme.
*/
@FindBy(id = "XWiki.XWikiPreferences_0_iconTheme")
private WebElement iconThemeInput;

@FindBy(xpath = "//label[@class='colorTheme']//a[contains(text(), 'Customize')]")
Expand All @@ -64,83 +70,150 @@ public ThemesAdministrationSectionPage()
super("Themes");
}

private List<WebElement> getThemeOptions(WebElement themeInput)
{
return themeInput.findElements(By.tagName("option"));
}

private List<WebElement> getColorThemeOptions()
{
return iconThemeInput.findElements(By.tagName("option"));
return getThemeOptions(colorThemeInput);
}

private List<WebElement> getIconThemeOptions()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line missing above the method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6468983 👍
Thanks!

{
return getThemeOptions(iconThemeInput);
}

private List<WebElement> getColibriThemeOptions()
{
return iconThemeInput.findElements(By.xpath("//optgroup[@label='Colibri Themes']//option"));
return colorThemeInput.findElements(By.xpath("//optgroup[@label='Colibri Themes']//option"));
}

private List<WebElement> getFlamingoThemeOptions()
{
return iconThemeInput.findElements(By.xpath("//optgroup[@label='Flamingo Themes']//option"));
return colorThemeInput.findElements(By.xpath("//optgroup[@label='Flamingo Themes']//option"));
}

/**
* @return the list of available color themes
* @param themeInput is the input from which to retrieve themes.
* @return a list of the name of all the themes proposed by the themeInput.
*/
public List<String> getColorThemes()
private List<String> getThemes(WebElement themeInput)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see there's a getColorThemes() below, what's the difference? Should this method be named getThemeOptions() instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe explain in javadoc that it returns options for both color themes abd icon themes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, it wasn't clear. I added some documentation for this private method in b893fd4 👍


I created this method as a means to factorize code between getColorThemes and getIconThemes which were very similar.

{
List<String> results = new ArrayList<>();
for (WebElement option : getColorThemeOptions()) {
for (WebElement option : getThemeOptions(themeInput)) {
results.add(option.getText());
}
return results;
}

/**
* Select the specified color theme.
* @param colorThemeName name of the color theme to select
* @return the list of available color themes
*/
public void setColorTheme(String colorThemeName)
public List<String> getColorThemes()
{
return getThemes(this.colorThemeInput);
}

/**
* @return the list of available icon themes
*/
public List<String> getIconThemes()
{
return getThemes(this.iconThemeInput);
}

private void setTheme(String themeName, WebElement themeInput)
{
// Make sure the color theme that we want to set is available from the list first
// Make sure the theme that we want to set is available from the list first
try {
getDriver().waitUntilCondition(driver -> getColorThemeOptionElement(colorThemeName) != null);
getDriver().waitUntilCondition(driver -> getThemeOptionElement(themeName, themeInput) != null);
} catch (TimeoutException e) {
// Collect all available options to display a nice message
List<String> options = new ArrayList<>();
for (WebElement option : getColorThemeOptions()) {
for (WebElement option : getThemeOptions(themeInput)) {
options.add(option.getText());
}
throw new TimeoutException(String.format("Color theme [%s] wasn't found among [%s]", colorThemeName,
throw new TimeoutException(String.format("Theme [%s] wasn't found among [%s]", themeName,
StringUtils.join(options, ',')), e);
}

// Click on it to set the theme
getColorThemeOptionElement(colorThemeName).click();
getThemeOptionElement(themeName, themeInput).click();

// Waiting to be sure the change is effective
getDriver().waitUntilCondition(driver -> StringUtils.equals(getCurrentColorTheme(), colorThemeName));
getDriver().waitUntilCondition(driver -> StringUtils.equals(getCurrentTheme(themeInput), themeName));
}

private WebElement getColorThemeOptionElement(String colorThemeName)
/**
* Select the specified color theme.
*
* @param colorThemeName name of the color theme to select
*/
public void setColorTheme(String colorThemeName)
{
setTheme(colorThemeName, colorThemeInput);
}

/**
* Select the specified icon theme.
*
* @param iconThemeName name of the icon theme to select
*/
public void setIconTheme(String iconThemeName)
{
setTheme(iconThemeName, iconThemeInput);
}

private WebElement getThemeOptionElement(String themeName, WebElement themeInput)
{
WebElement element = null;
for (WebElement option : getColorThemeOptions()) {
if (colorThemeName.equals(option.getText())) {
for (WebElement option : getThemeOptions(themeInput)) {
if (themeName.equals(option.getText())) {
element = option;
break;
}
}
return element;
}

/**
* @return the current color theme
*/
public String getCurrentColorTheme()
private WebElement getColorThemeOptionElement(String colorThemeName)
{
return getThemeOptionElement(colorThemeName, colorThemeInput);
}

private WebElement getIconThemeOptionElement(String iconThemeName)
{
return getThemeOptionElement(iconThemeName, iconThemeInput);
}

private String getCurrentTheme(WebElement themeInput)
{
for (WebElement option : getColorThemeOptions()) {
for (WebElement option : getThemeOptions(themeInput)) {
if (option.isSelected()) {
return option.getText();
}
}
return null;
}

/**
* @return the current color theme
*/
public String getCurrentColorTheme()
{
return getCurrentTheme(colorThemeInput);
}

/**
* @return the current icon theme
*/
public String getCurrentIconTheme()
{
return getCurrentTheme(iconThemeInput);
}

/**
* @return the list of colibri themes
*/
Expand Down Expand Up @@ -171,7 +244,7 @@ public List<String> getFlamingoThemes()
public void clickOnCustomizeColorTheme()
{
getDriver().waitUntilElementIsVisible(
By.xpath("//label[@class='colorTheme']//a[contains(text(), 'Customize')]"));
By.xpath("//label[@class='colorTheme']//a[contains(text(), 'Customize')]"));
customizeColorThemeButton.click();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xwiki.platform</groupId>
<artifactId>xwiki-platform-administration-test-pageobjects</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<!-- Runtime dependencies. -->
<dependency>
<groupId>org.xwiki.platform</groupId>
Expand All @@ -66,6 +72,12 @@
<type>xar</type>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.xwiki.platform</groupId>
<artifactId>xwiki-platform-administration-ui</artifactId>
<version>${project.version}</version>
<type>xar</type>
</dependency>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like this dep could be missing from icon ui module.

Copy link
Contributor Author

@Sereza7 Sereza7 Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not update anything in the icon-ui module in this PR. As far as I understand, it's useful in here because I need to navigate in the administration ui in the new tests. The actual icons are not impacted in any way by the administration UI.


I probably misunderstood your comment, let me know if it's alright to merge things like this. AFAICS builds are passing . Just tested mvn clean install -f xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-ui -Pquality to be sure, the build went through successfully.
$${\color{orange}ONGOING}$$

</dependencies>
<build>
<testSourceDirectory>src/test/it</testSourceDirectory>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@ class AllIT
class NestedIconThemesRestIT extends IconThemesRestIT
{
}

@Nested
@DisplayName("Icon Themes Admin UI tests")
class NestedIconThemesAdminIT extends IconThemesAdminIT
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.icon.test.ui;

import java.util.Arrays;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.openqa.selenium.By;
import org.xwiki.administration.test.po.AdministrablePage;
import org.xwiki.administration.test.po.AdministrationPage;
import org.xwiki.administration.test.po.ThemesAdministrationSectionPage;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.test.docker.junit5.UITest;
import org.xwiki.test.ui.TestUtils;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Functional tests of the icon theme Admin features.
*
* @version $Id$
* @since 16.10.0RC1
*/
@UITest
class IconThemesAdminIT
{
private static final String SILK_THEME = "Silk";

private static final String FA_THEME = "Font Awesome";

@Test
void validateIconThemeFeatures(TestUtils setup, TestInfo info)
{
setup.loginAsSuperAdmin();

// Validate setting a icon theme from the wiki Admin UI
validateSetThemeFromWikiAdminUI(setup);

// Validate setting a icon theme from the page Admin UI for the page and its children
validateSetThemeFromPageAdminUI(setup, info);
}

private void validateSetThemeFromWikiAdminUI(TestUtils setup)
{
// Go to the Theme Admin UI to verify we can set a new theme from there using the select control
AdministrationPage administrationPage = AdministrationPage.gotoPage();
ThemesAdministrationSectionPage presentationAdministrationSectionPage = administrationPage.clickThemesSection();
// We expect the default icon to be Silk
assertFalse(setup.isUsingFA());

// Set the new icon theme as the active theme
presentationAdministrationSectionPage.setIconTheme(FA_THEME);
assertEquals(FA_THEME, presentationAdministrationSectionPage.getCurrentIconTheme());
presentationAdministrationSectionPage.clickSave();

// Verify that the icon theme has been applied.
assertTrue(setup.isUsingFA());

// Switch back to Silk
administrationPage = AdministrationPage.gotoPage();
presentationAdministrationSectionPage = administrationPage.clickThemesSection();
presentationAdministrationSectionPage.setIconTheme(SILK_THEME);
presentationAdministrationSectionPage.clickSave();
}

private void validateSetThemeFromPageAdminUI(TestUtils setup, TestInfo info)
{
// Create two nested pages. We'll apply the theme on the top page and verify that the nested page has it too.
String simpleName = info.getTestClass().get().getSimpleName();
DocumentReference topPage = new DocumentReference("xwiki", simpleName + "Parent", "WebHome");
DocumentReference childPage = new DocumentReference("xwiki", Arrays.asList(simpleName + "Parent",
simpleName + "Child"), "WebHome");
setup.deletePage(topPage, true);
setup.createPage(childPage, "top page");
setup.createPage(topPage, "top page");
AdministrablePage ap = new AdministrablePage();

// Navigate to the top page's admin UI.
AdministrationPage page = ap.clickAdministerPage();
ThemesAdministrationSectionPage presentationAdministrationSectionPage = page.clickThemesSection();

// Set the newly created icon theme as the active theme for the page and children
presentationAdministrationSectionPage.setIconTheme(FA_THEME);
assertEquals(FA_THEME, presentationAdministrationSectionPage.getCurrentIconTheme());
presentationAdministrationSectionPage.clickSave();

// Verify that the icon theme has been applied to the top page
setup.gotoPage(topPage);
assertTrue(setup.isUsingFA());

// Verify that the icon theme has been applied to the children page
setup.gotoPage(childPage);
assertTrue(setup.isUsingFA());

// Possible extension of the test:
// Verify that the icon theme has not been applied to other pages.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1625,6 +1625,14 @@ public boolean isInAdminMode()
return getDriver().getCurrentUrl().contains("/admin/");
}

/**
* This function assumes that the breadcrumb is visible on the current page.
*/
public boolean isUsingFA()
{
return !getDriver().findElements(By.cssSelector("#hierarchy_breadcrumb .dropdown .fa.fa-home")).isEmpty();
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this go in TestUtils or in BasePage or BaseElement?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO both are fine.
Boolean methods of BasePage: hasLoginLink, isNotificationsMenuOpen, isForbidden, isNewDocument, canDelete, ... (9 total)
Boolean methods of TestUtils: isInViewMode (and a lot of other modes), pageExists (15 total)

/**
* Verify if the passed reference corresponds to the current page, independently of the wiki.
* Throws an {@link AssertionFailedError} if it's not the case.
Expand Down