package net.thucydides.core.webdriver.javascript;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.type.TypeFactory;
import net.thucydides.core.pages.jquery.JQueryEnabledPage;
import net.thucydides.core.webdriver.WebDriverFacade;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import java.io.IOException;
import java.util.List;
import static net.thucydides.core.webdriver.javascript.JavascriptSupport.javascriptIsSupportedIn;
/**
* Simple encapsulation of Javascript execution.
*/
public class JavascriptExecutorFacade {
private WebDriver driver;
private ObjectMapper mapper;
private InjectableValues inject;
public JavascriptExecutorFacade(final WebDriver driver) {
this.driver = driver;
}
public JavascriptExecutorFacade withObjectMapper(ObjectMapper mapper){
this.mapper = mapper;
return this;
}
public JavascriptExecutorFacade withInjectableValues(InjectableValues inject){
this.inject = inject;
return this;
}
/**
* Execute some Javascript in the underlying WebDriver driver.
* @param script
*/
public Object executeScript(final String script) {
if (javascriptIsSupportedIn(driver)) {
JavascriptExecutor js = getJavascriptEnabledDriver();
return js.executeScript(script);
} else {
return null;
}
}
public Object executeScript(final String script, final Object... params) {
if (javascriptIsSupportedIn(driver)) {
JavascriptExecutor js = getJavascriptEnabledDriver();
return js.executeScript(script, params);
} else {
return null;
}
}
private String executeAndGetJsonAsString(final String script, final Object... params){
JQueryEnabledPage jQueryEnabledPage = JQueryEnabledPage.withDriver(getRealDriver());
jQueryEnabledPage.injectJavaScriptUtils();
return (String)executeScript("return JSON.stringify(JSON.decycle(function(arguments){"+ script + "}(arguments)));", params);
}
private <T> T deserializeJsonAs(Class<T> classOfT, final String objString){
ObjectMapper mapper = getMapper();
ObjectReader reader = mapper.reader(classOfT);
if (inject != null){
reader = reader.with(inject);
}
try {
return reader.readValue(objString);
} catch (JsonParseException e) {
throw new WebDriverException(e);
} catch (JsonMappingException e) {
throw new WebDriverException(e);
} catch (IOException e) {
throw new WebDriverException(e);
}
}
private <T> List<T> deserializeJsonAsListOf(Class<T> classOfT, final String objString){
ObjectMapper mapper = getMapper();
ObjectReader reader = mapper.reader(TypeFactory.defaultInstance().constructCollectionType(List.class, classOfT));
if (inject != null){
reader = reader.with(inject);
}
try {
return reader.readValue(objString);
} catch (JsonParseException e) {
throw new WebDriverException(e);
} catch (JsonMappingException e) {
throw new WebDriverException(e);
} catch (IOException e) {
throw new WebDriverException(e);
}
}
private ObjectMapper getMapper(){
if (mapper == null){
mapper = new ObjectMapper();
}
return mapper;
}
/**
* Executes the JavaScript code and deserializes the resulting object as a classOfT.
*
* @param classOfT Java Class
* @param script that returns JavaScript Object
* @param params for the script
* @return deserialized as classOfT object
*/
public <T> T deserializeScriptResultAs(Class<T> classOfT, final String script, final Object... params){
String objString = executeAndGetJsonAsString(script, params);
if (objString == null)
return null;
return deserializeJsonAs(classOfT, objString);
}
/**
* Executes the JavaScript code and deserializes the resulting object as a List of classOfT.
*
* @param classOfT Java Class to reflect on
* @param script that returns JavaScript Object
* @return deserialized as List of classOfT
*/
public <T> List<T> deserializeScriptResultAsListOf(Class<T> classOfT, final String script, final Object... params){
String objString = executeAndGetJsonAsString(script, params);
if (objString == null)
return null;
return deserializeJsonAsListOf(classOfT, objString);
}
private WebDriver getRealDriver() {
if (WebDriverFacade.class.isAssignableFrom(driver.getClass())) {
WebDriverFacade driverFacade = (WebDriverFacade) driver;
return driverFacade.getProxiedDriver();
} else {
return driver;
}
}
private JavascriptExecutor getJavascriptEnabledDriver() {
return (JavascriptExecutor) getRealDriver();
}
}