Skip to content

Commit

Permalink
Hardening against intermittent failures
Browse files Browse the repository at this point in the history
  • Loading branch information
OndrejKotek committed Mar 14, 2024
1 parent 7dcacb4 commit bdae1c1
Show file tree
Hide file tree
Showing 18 changed files with 170 additions and 51 deletions.
18 changes: 13 additions & 5 deletions common/src/main/java/org/jboss/hal/testsuite/Console.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.jboss.hal.testsuite.util.Library;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

Expand Down Expand Up @@ -127,11 +128,18 @@ public void verify(PlaceRequest placeRequest) {
/** Waits until all notifications are gone. */
public void waitNoNotification() {
List<WebElement> dismissibleNotifications = By.cssSelector(DOT + alertDismissable).findElements(browser);
for (WebElement notification : dismissibleNotifications) {
WebElement button = notification.findElement(By.cssSelector("button.close"));
if (button != null) {
button.click();
for (int remainingExpected = dismissibleNotifications.size(); !dismissibleNotifications.isEmpty()
&& remainingExpected > 0; remainingExpected--) {

try {
WebElement button = dismissibleNotifications.get(0).findElement(By.cssSelector("button.close"));
if (button != null) {
button.click();
}
} catch (StaleElementReferenceException ex) {
// Ignore the exception, the notification may be gone before clicking
}
dismissibleNotifications = By.cssSelector(DOT + alertDismissable).findElements(browser);
}
waitModel().until().element(By.cssSelector(DOT + toastNotificationsListPf + ":empty")).is().present();
}
Expand All @@ -152,7 +160,7 @@ public boolean verifyNoError() {
}

private void verifyNotification(String css) {
waitModel().until() // use waitModel() since it might take some time until the notification is visible
waitModel().until() // use waitModel() since the notification might take some time until the notification is visible
.element(By.cssSelector(DOT + toastNotificationsListPf + " ." + css))
.is().visible();
}
Expand Down
18 changes: 17 additions & 1 deletion common/src/main/java/org/jboss/hal/testsuite/CrudOperations.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@
import org.jboss.hal.testsuite.creaper.ManagementClientProvider;
import org.jboss.hal.testsuite.creaper.ResourceVerifier;
import org.jboss.hal.testsuite.fragment.AddResourceDialogFragment;
import org.jboss.hal.testsuite.fragment.EmptyState;
import org.jboss.hal.testsuite.fragment.FormFragment;
import org.jboss.hal.testsuite.fragment.TableFragment;
import org.openqa.selenium.TimeoutException;
import org.wildfly.extras.creaper.core.online.OnlineManagementClient;
import org.wildfly.extras.creaper.core.online.operations.Address;

import static org.jboss.arquillian.graphene.Graphene.waitGui;
import static org.jboss.hal.dmr.ModelDescriptionConstants.NAME;

/** Methods useful to test and verify CRUD operations in application views. */
Expand Down Expand Up @@ -76,7 +79,20 @@ public void createSingleton(Address address, FormFragment form, Consumer<FormFra
/** Adds a singleton resource using the main action of an empty state and the specified initial form values. */
public void createSingleton(Address address, FormFragment form, Consumer<FormFragment> initialValues,
VerifyChanges verifyChanges) throws Exception {
form.emptyState().mainAction();

try {
EmptyState emptyState = form.emptyState();
waitGui().until().element(emptyState.getRoot()).is().visible();
emptyState.mainAction();
} catch (TimeoutException ex) {
System.out.println("[DEBUG] Form text: '" + form.getRoot().getText() + "'");
// try again to get rid of intermittent failures
EmptyState emptyState = form.emptyState();
System.out.println("[DEBUG] Form emptyState text: '" + emptyState.getRoot().getText() + "'");
waitGui().until().element(emptyState.getRoot()).is().visible();
emptyState.mainAction();
}

if (initialValues != null) {
AddResourceDialogFragment dialog = console.addResourceDialog();
initialValues.accept(dialog.getForm()); // use the form of add resource dialog!
Expand Down
4 changes: 2 additions & 2 deletions common/src/main/java/org/jboss/hal/testsuite/Random.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ public static String jndiName(String name) {
return JNDI_PREFIX + name;
}

/** Returns a random integer between 0 and 99 */
/** Returns a random integer between 1 and 99 */
public static int number() {
return RandomUtils.nextInt(0, 100);
return RandomUtils.nextInt(1, 100);
}

/** Returns a random double between 0.001 and 99.999 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
package org.jboss.hal.testsuite.fragment;

import org.jboss.arquillian.graphene.fragment.Root;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;

import static org.jboss.arquillian.graphene.Graphene.waitGui;
import static org.jboss.hal.resources.CSS.blankSlatePfMainAction;
import static org.jboss.hal.resources.CSS.btnPrimary;

Expand All @@ -30,6 +32,7 @@ public class EmptyState {

/** Clicks on the main action */
public void mainAction() {
waitGui().until().element(root, By.cssSelector("." + blankSlatePfMainAction + " button." + btnPrimary)).is().visible();
primaryButton.click();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.openqa.selenium.By;
import org.openqa.selenium.ElementClickInterceptedException;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
Expand All @@ -35,6 +36,7 @@

import static org.jboss.arquillian.graphene.Graphene.createPageFragment;
import static org.jboss.arquillian.graphene.Graphene.waitGui;
import static org.jboss.arquillian.graphene.Graphene.waitModel;
import static org.jboss.hal.resources.CSS.*;
import static org.jboss.hal.resources.UIConstants.MASK_CHARACTER;

Expand Down Expand Up @@ -148,8 +150,18 @@ public void trySave() {
* {@link #trySave()} instead.
*/
public void save() {
console.scrollIntoView(saveButton).click();
waitGui().until().element(readOnlySection).is().visible();
try {
console.scrollIntoView(saveButton).click();
} catch (ElementClickInterceptedException ex) {
if (ex.getMessage().contains(autocompleteSuggestions)) {
// get rid of any open auto suggestions popup, which is interfering with the save button
saveButton.sendKeys(Keys.TAB);
waitGui().until().element(browser, ByJQuery
.selector(DOT + autocompleteSuggestions + "[style*='display: none']")).is().present();
console.scrollIntoView(saveButton).click();
}
}
waitModel().until().element(readOnlySection).is().visible();
}

/**
Expand All @@ -168,6 +180,15 @@ public void text(String name, String value) {
waitGui().until().element(inputElement).value().equalTo("");
inputElement.sendKeys(value);
waitGui().until().element(inputElement).value().equalTo(value);

// get rid of any open auto suggestions popup, which might interfere with save or cancel buttons
if (!ByJQuery.selector(DOT + autocompleteSuggestions + "[style*='display: block']")
.findElements(browser).isEmpty()) {

inputElement.sendKeys(Keys.TAB);
waitGui().until().element(browser, ByJQuery
.selector(DOT + autocompleteSuggestions + "[style*='display: none']")).is().present();
}
}

public void textByLabel(String labelContent, String value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.openqa.selenium.support.FindBy;

import static org.jboss.arquillian.graphene.Graphene.waitGui;
import static org.jboss.arquillian.graphene.Graphene.waitModel;
import static org.jboss.hal.resources.CSS.columnAction;
import static org.jboss.hal.resources.CSS.halTableButtons;
import static org.jboss.hal.testsuite.Selectors.contains;
Expand Down Expand Up @@ -91,6 +92,7 @@ public WebElement button(String text) {
*/
public void select(String value) {
By selector = ByJQuery.selector("td" + contains(value));
waitModel().until().element(root, selector).is().present();
goToPageWithElement(selector);
WebElement rowElement = root.findElement(selector);

Expand Down Expand Up @@ -120,6 +122,7 @@ public void select(String value) {
/** Clicks on the &lt;action&gt; column in the row which contains "&lt;value&gt;". */
public void action(String value, String action) {
By selector = ByJQuery.selector("td" + contains(value) + " ~ td button." + columnAction + contains(action));
waitModel().until().element(root, selector).is().present();
goToPageWithElement(selector);
root.findElement(selector).click();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public void selectPrimary(String id) {
public void selectSecondary(String primaryId, String secondaryId) {
WebElement primaryMenuItem = root.findElement(By.cssSelector("#" + primaryId + " > a"));
new Actions(browser).moveToElement(primaryMenuItem).perform();
primaryMenuItem.click();
waitGui().until().element(By.id(primaryId + "-secondary")).is().visible();
selectItem(secondaryId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,10 @@ public void navigate(Map<String, String> params) {
public void navigateAgain(String name, String value) {
browser.navigate().refresh();
navigate(name, value);
if (!console.verifyNoError()) {
// try again, the resource may be unavailable
browser.navigate().refresh();
navigate(name, value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
import static org.jboss.hal.testsuite.fixtures.ElytronFixtures.SQL_UPDATE2;
import static org.jboss.hal.testsuite.fixtures.ElytronFixtures.datasourceAddress;
import static org.jboss.hal.testsuite.fixtures.ElytronFixtures.jdbcRealmAddress;

@RunWith(Arquillian.class)
public class JDBCRealmTest {

Expand Down Expand Up @@ -628,9 +629,10 @@ public void principalQueryModularCryptMapperUpdate() throws Exception {
TableFragment table = page.getPrincipalQueryTable();
FormFragment form = page.getPrincipalQueryMcryptForm();
table.bind(form);
jdbcTable.filter(JDBC_RLM_UPDATE);
jdbcTable.action(JDBC_RLM_UPDATE, PQ_LABEL);
waitGui().until().element(table.getRoot()).is().visible();
table.select(SQL_MCM_UPD);
table.filterAndSelect(SQL_MCM_UPD);
page.getPrincipalQueryTabs().select(MCRYPT_MAPPER_TAB);
long number = 23;
ModelNode mapper = new ModelNode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
import org.wildfly.extras.creaper.core.online.operations.Operations;
import org.wildfly.extras.creaper.core.online.operations.Values;

import static org.jboss.arquillian.graphene.Graphene.waitGui;

@RunWith(Arquillian.class)
public class SessionOperationsTest {

Expand Down Expand Up @@ -147,6 +149,7 @@ private void verifyAttributeIsContained(String key, String value, List<WebElemen

private void reloadSessions() {
page.getSessionsTable().button("Reload").click();
waitGui().until().element(page.getSessionsTable().getRoot(), By.cssSelector("tr.odd")).is().visible();
}

@Test
Expand Down Expand Up @@ -212,7 +215,13 @@ public void verifyCreationTime() throws IOException {
String creationTime = selectedRowColumns.get(1).getText();
Calendar creationTimeCalendar = Calendar.getInstance();
creationTimeCalendar.setTimeInMillis(creationTimeInMillis);
Assert.assertEquals("Creation time should be matching", DATE_FORMAT.format(creationTimeCalendar.getTime()), creationTime);
try {
Assert.assertEquals("Creation time should be matching", DATE_FORMAT.format(creationTimeCalendar.getTime()), creationTime);
} catch (AssertionError e) {
// get rid of errors caused by delay when at the end of minute
creationTimeCalendar.add(Calendar.SECOND, 1);
Assert.assertEquals("Creation time should be matching", DATE_FORMAT.format(creationTimeCalendar.getTime()), creationTime);
}
invalidateSession(sessionId);
}

Expand All @@ -223,16 +232,21 @@ public void verifyLastAccessedTime() throws IOException, InterruptedException {
reloadSessions();
String sessionId = getSessionsFromModel().get(0);
page.getSessionsTable().select(sessionId);
long start = System.currentTimeMillis();
long end = start + TimeUnit.MINUTES.toMillis(1);
TimeUnit.MINUTES.sleep(1);
TimeUnit.SECONDS.sleep(5);
long accessTime = System.currentTimeMillis();
deploymentBrowser.findElement(By.cssSelector("input[value=\"Increment\"")).click();
reloadSessions();
List<WebElement> selectedRowColumns = page.getSessionsTable().getRoot().findElements(By.cssSelector("tr.selected > td"));
String lastAccessedTime = selectedRowColumns.get(2).getText();
Calendar lastAccessedTimeCalendar = Calendar.getInstance();
lastAccessedTimeCalendar.setTimeInMillis(end);
Assert.assertEquals("Last accessed time should be matching", DATE_FORMAT.format(lastAccessedTimeCalendar.getTime()), lastAccessedTime);
lastAccessedTimeCalendar.setTimeInMillis(accessTime);
try {
Assert.assertEquals("Last accessed time should be matching", DATE_FORMAT.format(lastAccessedTimeCalendar.getTime()), lastAccessedTime);
} catch (AssertionError e) {
// get rid of errors caused by delay when at the end of minute
lastAccessedTimeCalendar.add(Calendar.SECOND, 1);
Assert.assertEquals("Last accessed time should be matching", DATE_FORMAT.format(lastAccessedTimeCalendar.getTime()), lastAccessedTime);
}
invalidateSession(sessionId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ public class SingleSignOnAddTest {
@BeforeClass
public static void setUp() throws IOException {
operations.add(httpAuthenticationFactoryAddress(HTTP_AUTH_CREATE),
Values.of(HTTP_SERVER_MECH_FACTORY, "global").and(SECURITY_DOMAIN, "ApplicationDomain"));
Values.of(HTTP_SERVER_MECH_FACTORY, "global").and(SECURITY_DOMAIN, "ApplicationDomain")).assertSuccess();
operations.add(applicationSecurityDomain(APPLICATION_SECURITY_DOMAIN_TO_BE_TESTED2),
Values.of(ApplicationSecurityDomainFixtures.HTTP_AUTHENTICATION_FACTORY, HTTP_AUTH_CREATE));
Values.of(ApplicationSecurityDomainFixtures.HTTP_AUTHENTICATION_FACTORY, HTTP_AUTH_CREATE)).assertSuccess();
operations.add(ElytronFixtures.keyStoreAddress(KEY_STORE_TO_BE_ADDED),
Values.of(TYPE, ElytronFixtures.JKS)
.and(ElytronFixtures.CREDENTIAL_REFERENCE, new ModelNodeGenerator.ModelNodePropertiesBuilder()
.addProperty(ElytronFixtures.CREDENTIAL_REFERENCE_CLEAR_TEXT, Random.name()).build()));
.addProperty(ElytronFixtures.CREDENTIAL_REFERENCE_CLEAR_TEXT, Random.name()).build())).assertSuccess();
}

@AfterClass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ public class AJPListenerConfigurationTest {

@BeforeClass
public static void setUp() throws IOException, CommandFailedException {
operations.add(IOFixtures.bufferPoolAddress(BUFFER_POOL_TO_BE_EDITED));
operations.add(IOFixtures.workerAddress(WORKER_TO_BE_EDITED));
operations.add(serverAddress(UNDERTOW_SERVER_TO_BE_TESTED));
operations.add(IOFixtures.bufferPoolAddress(BUFFER_POOL_TO_BE_EDITED)).assertSuccess();
operations.add(IOFixtures.workerAddress(WORKER_TO_BE_EDITED)).assertSuccess();
operations.add(serverAddress(UNDERTOW_SERVER_TO_BE_TESTED)).assertSuccess();
client.apply(new AddLocalSocketBinding(SOCKET_BINDING));
client.apply(new AddLocalSocketBinding(SOCKET_BINDING_TO_BE_EDITED));
client.apply(new AddLocalSocketBinding(SOCKET_REDIRECT_TO_BE_EDITED));
operations.add(serverAddress(UNDERTOW_SERVER_TO_BE_TESTED).and("ajp-listener", AJP_LISTENER_TO_BE_EDITED),
Values.of("socket-binding", SOCKET_BINDING.toLowerCase() + "ref"));
Values.of("socket-binding", SOCKET_BINDING.toLowerCase() + "ref")).assertSuccess();
}

@AfterClass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
import org.wildfly.extras.creaper.core.online.OnlineManagementClient;
import org.wildfly.extras.creaper.core.online.operations.Address;
import org.wildfly.extras.creaper.core.online.operations.OperationException;
import org.wildfly.extras.creaper.core.online.operations.Operations;

import static org.jboss.arquillian.graphene.Graphene.waitGui;
import static org.jboss.hal.dmr.ModelDescriptionConstants.NAME;

@RunWith(Arquillian.class)
Expand Down Expand Up @@ -57,11 +60,11 @@ private static Address crawlerAddress(String servletContainer) {

@BeforeClass
public static void setUp() throws IOException {
operations.add(UndertowFixtures.servletContainerAddress(SERVLET_CONTAINER_EDIT));
operations.add(SERVLET_CONTAINER_EDIT_CRAWLER_ADDRESS);
operations.add(UndertowFixtures.servletContainerAddress(SERVLET_CONTAINER_CRAWLER_CREATE));
operations.add(UndertowFixtures.servletContainerAddress(SERVLET_CONTAINER_CRAWLER_REMOVE));
operations.add(crawlerAddress(SERVLET_CONTAINER_CRAWLER_REMOVE));
operations.add(UndertowFixtures.servletContainerAddress(SERVLET_CONTAINER_EDIT)).assertSuccess();
operations.add(SERVLET_CONTAINER_EDIT_CRAWLER_ADDRESS).assertSuccess();
operations.add(UndertowFixtures.servletContainerAddress(SERVLET_CONTAINER_CRAWLER_CREATE)).assertSuccess();
operations.add(UndertowFixtures.servletContainerAddress(SERVLET_CONTAINER_CRAWLER_REMOVE)).assertSuccess();
operations.add(crawlerAddress(SERVLET_CONTAINER_CRAWLER_REMOVE)).assertSuccess();
}

@AfterClass
Expand All @@ -74,6 +77,12 @@ public static void tearDown() throws IOException, OperationException {
@Test
public void create() throws Exception {
navigateToCrawlerForm(SERVLET_CONTAINER_CRAWLER_CREATE);
try {
waitGui().until().element(By.id(Ids.build(Ids.UNDERTOW_SERVLET_CONTAINER_CRAWLER, "form-empty"))).is().visible();
} catch (TimeoutException ex) {
// ignore the intermittent exception and try again
navigateToCrawlerForm(SERVLET_CONTAINER_CRAWLER_CREATE);
}
crudOperations.createSingleton(crawlerAddress(SERVLET_CONTAINER_CRAWLER_CREATE), page.getCrawlerForm());
}

Expand Down

0 comments on commit bdae1c1

Please sign in to comment.