nl.hsac.fitnesse.fixture.slim.web.RichFacesTest Maven / Gradle / Ivy
package nl.hsac.fitnesse.fixture.slim.web;
import fitnesse.slim.fixtureInteraction.FixtureInteraction;
import nl.hsac.fitnesse.fixture.slim.SlimFixtureException;
import org.openqa.selenium.JavascriptException;
import org.openqa.selenium.ScriptTimeoutException;
import org.openqa.selenium.WebElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Version of {@link BrowserTest} with added functionality to deal with RichFaces, JSF pages.
*/
public class RichFacesTest extends BrowserTest {
protected static final String RICH_FACES_AJAX_CALL = "RichFaces.ajax(";
private final List eventsThatMayRequireWaiting = new ArrayList<>(Arrays.asList("onchange", "onclick"));
private boolean ajaxStartWithOnly = false;
private boolean ignoreImplicitAjaxWaitTimeouts = true;
private boolean shouldWaitForAjax = false;
private String previousLocation = null;
public RichFacesTest() {
super();
}
public RichFacesTest(int secondsBeforeTimeout) {
super(secondsBeforeTimeout);
}
public boolean onlyWaitIfEventHandlerStartsWithAjaxCall() {
return ajaxStartWithOnly;
}
public void setOnlyWaitIfEventHandlerStartsWithAjaxCall(boolean newValue) {
ajaxStartWithOnly = newValue;
}
@Override
protected void beforeInvoke(Method method, Object[] arguments) {
setShouldWaitForAjax(false);
super.beforeInvoke(method, arguments);
}
@Override
protected Object invoke(FixtureInteraction interaction, Method method, Object[] arguments) throws Throwable {
Object result = super.invoke(interaction, method, arguments);
if (shouldWaitForAjax()) {
try {
waitForJsfAjaxImpl(true);
} catch (ScriptTimeoutException e) {
if (ignoreImplicitAjaxWaitTimeouts) {
// log message but do not throw
System.err.println("Timeout while waiting for ajax call after: " + method.getName() + " with arguments: " + Arrays.toString(arguments));
String msg = createAjaxTimeoutMessage(e);
System.err.println("Exception not forwarded to wiki: " + msg);
} else {
throw createAjaxTimeout(e);
}
}
}
return result;
}
@Override
protected void sendValue(WebElement element, String value) {
boolean triggersAjax = willTriggerAjax(element);
super.sendValue(element, value);
if (triggersAjax) {
// ensure we trigger change event
pressTab();
setShouldWaitForAjax(true);
}
}
@Override
protected boolean clickSelectOption(WebElement element, String optionValue) {
boolean triggersAjax = willTriggerAjax(element);
boolean result = super.clickSelectOption(element, optionValue);
if (triggersAjax && result) {
setShouldWaitForAjax(true);
}
return result;
}
@Override
protected boolean clickElement(WebElement element) {
boolean triggersAjax = willTriggerAjax(element);
boolean result = super.clickElement(element);
if (triggersAjax && result) {
setShouldWaitForAjax(true);
}
return result;
}
@Override
protected boolean repeatUntil(RepeatCompletion repeat) {
// disable checking for ajax attributes, by indicating we already know we must wait.
// this method does its own waiting, irrespective of ajax calls
boolean previousWaitForAjax = shouldWaitForAjax();
setShouldWaitForAjax(true);
try {
return super.repeatUntil(repeat);
} finally {
// reset wait for ajax to original value
setShouldWaitForAjax(previousWaitForAjax);
}
}
protected boolean willTriggerAjax(WebElement element) {
// no need to inspect element attributes if we already know we must wait
return shouldWaitForAjax() || hasRichFacesAjax(element);
}
protected boolean hasRichFacesAjax(WebElement element) {
if (element == null) {
return false;
}
boolean result = isAjaxEventPresent(element);
if (!result) {
String tagName = element.getTagName();
if ("label".equalsIgnoreCase(tagName)) {
WebElement labelTarget = getSeleniumHelper().getLabelledElement(element);
if (labelTarget != null) {
result = isAjaxEventPresent(labelTarget);
}
}
}
if (result) {
// store current URL so we can check against it later when waiting for Ajax
storeLocationBeforeAction();
}
return result;
}
protected boolean isAjaxEventPresent(WebElement element) {
boolean result = false;
for (String event : getEventsThatMayRequireWaiting()) {
result = eventTriggersAjax(element, event);
if (result) {
break;
}
}
return result;
}
protected void storeLocationBeforeAction() {
previousLocation = location();
}
protected boolean eventTriggersAjax(WebElement element, String attribute) {
String eventHandler = element.getAttribute(attribute);
return eventHandler != null
&& (ajaxStartWithOnly? eventHandler.startsWith(RICH_FACES_AJAX_CALL)
: eventHandler.contains(RICH_FACES_AJAX_CALL));
}
public void waitForJsfAjax() {
try {
waitForJsfAjaxImpl(false);
} catch (ScriptTimeoutException e) {
throw createAjaxTimeout(e);
}
}
protected void waitForJsfAjaxImpl(boolean checkLocation) {
try {
// if jsf is present on page, add an event listener that will be triggered when next Ajax request completes
if (checkLocation) {
waitForJavascriptCallback("if(!window.jsf||window.location.href!==arguments[0]){callback();}else{jsf.ajax.addOnEvent(function(data){if(data.status=='success')callback();});}",
previousLocation);
} else {
waitForJavascriptCallback("if(!window.jsf){callback();}else{jsf.ajax.addOnEvent(function(data){if(data.status=='success')callback();});}");
}
} catch (JavascriptException e) {
String msg = e.getMessage();
if (msg.startsWith("javascript error: document unloaded while waiting for result")) {
// document is reloaded, no problem
} else {
throw e;
}
} finally {
setShouldWaitForAjax(false);
}
}
protected void setShouldWaitForAjax(boolean shouldWaitForAjax) {
this.shouldWaitForAjax = shouldWaitForAjax;
}
protected boolean shouldWaitForAjax() {
return shouldWaitForAjax;
}
public List getEventsThatMayRequireWaiting() {
return eventsThatMayRequireWaiting;
}
public void setIgnoreImplicitAjaxWaitTimeouts(boolean ignoreAjaxWaitTimeouts) {
ignoreImplicitAjaxWaitTimeouts = ignoreAjaxWaitTimeouts;
}
public boolean willIgnoreImplicitAjaxWaitTimeouts() {
return ignoreImplicitAjaxWaitTimeouts;
}
protected AjaxTimeout createAjaxTimeout(ScriptTimeoutException e) {
return new AjaxTimeout(createAjaxTimeoutMessage(e), e);
}
protected String createAjaxTimeoutMessage(ScriptTimeoutException e) {
String messageBase = "Did not detect completion of RichFaces ajax call: " + e.getMessage();
return getSlimFixtureExceptionMessage("timeouts", "rfAjaxTimeout", messageBase, e);
}
/**
* Exception to indicate timeout while waiting for RichFace's ajax call to complete.
*/
public static class AjaxTimeout extends SlimFixtureException {
public AjaxTimeout(String message, ScriptTimeoutException e) {
super(false, message, e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy