there has been an automation side project I've been working on in my spare time and I've been trying to incorporate JavascriptExecutor into my Page Object Model since some of the portions of the website I picked isn't functioning when clicking on tag.
Currently this is how I my code set up in the PageObject class (my superclass for my Page-Object Model):
public class PageObject {
protected Driver driver;
public Actions act;
public JavascriptExecutor js = (JavascriptExecutor) this.driver;
PageObject(Driver driver){
this.driver = driver;
PageFactory.initElements(driver, this);
this.act = new Actions(driver);
}
}
This is the layout for the Driver class (which implements the WebDriver):
public class Driver implements WebDriver {
public WebDriver driver;
String browserName;
public JavascriptExecutor js;
public Driver(String browserName) {
this.browserName = browserName;
if (browserName.equalsIgnoreCase("chrome")) {
System.setProperty("webdriver.chrome.driver", "./resources/webdrivers/chromedriver_win32/chromedriver.exe");
this.driver = new ChromeDriver();
this.js = (JavascriptExecutor) this.driver;
}
// Below are other if conditions for different browsers.
Currently, I'm getting a NullpointerException with js, but I know if I initialize js inside the PageObject constructor, I get hit with a ClassCastException. (I have these classes set up in a Maven Project.)
I've looked around online and this doesn't appear to be a common topic that gets asked and right now, I'm completely stumped on how to solve this or if it's even possible. Has anyone else encountered this situation?
Update: figured out the problem.
public class PageObject {
protected Driver driver;
public Actions act;
PageObject(Driver driver){
this.driver = driver;
PageFactory.initElements(driver, this);
this.act = new Actions(driver);
}
}
public class Driver implements WebDriver {
public WebDriver driver;
String browserName;
public static JavascriptExecutor js;
public Driver(String browserName) {
this.browserName = browserName;
if (browserName.equalsIgnoreCase("chrome")) {
System.setProperty("webdriver.chrome.driver", "./resources/webdrivers/chromedriver_win32/chromedriver.exe");
this.driver = new ChromeDriver();
js = (JavascriptExecutor) this.driver;
}
// Below are other if conditions for different browsers.
I had to change js into static and then wherever I have a portion of the website where the are non-clickable, I call on Driver.js to execute whatever command I need for any webpage my framework tests.
Related
Recently I've had the assignment to create a bi-directional interop bridge between a shell app and a webpage in .NET MAUI. Not finding any way to solve this I had the idea of creating it in Xamarin.Forms first seeing as MAUI is a continuation on it.
After having created this app, I've tried to convert it over to MAUI using Microsoft's instructions on the dotnet/maui github wiki.
The main problem i'm encountering right now is that I've been using extensions on Android's WebViewRenderer, WebViewClient and Java.Lang.Object to be able to send and receive javascript to and from the WebView.
public class ExtendedWebViewRenderer : WebViewRenderer
{
private const String JavascriptFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
public ExtendedWebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
Control.RemoveJavascriptInterface("jsBridge");
((ExtendedWebView)Element).Cleanup();
}
if (e.NewElement != null)
{
Control.SetWebViewClient(new JavascriptWebViewClient($"javascript: {JavascriptFunction}"));
Control.AddJavascriptInterface(new JsBridge(this), "jsBridge");
}
}
}
public class JavascriptWebViewClient : WebViewClient
{
private readonly String _javascript;
public JavascriptWebViewClient(String javascript)
{
_javascript = javascript;
}
public override void OnPageFinished(WebView view, String url)
{
base.OnPageFinished(view, url);
view.EvaluateJavascript(_javascript, null);
}
}
public class JsBridge : Java.Lang.Object
{
private readonly WeakReference<ExtendedWebViewRenderer> _extendedWebViewMainRenderer;
public JsBridge(ExtendedWebViewRenderer extendedRenderer)
{
_extendedWebViewMainRenderer = new WeakReference<ExtendedWebViewRenderer>(extendedRenderer);
}
[JavascriptInterface]
[Export("invokeAction")]
public void InvokeAction(String data)
{
if (_extendedWebViewMainRenderer != null && _extendedWebViewMainRenderer.TryGetTarget(out var extendedRenderer))
{
((ExtendedWebView)extendedRenderer.Element).InvokeAction(data);
}
}
}
All three of these are either not available right now, or will not be implemented in MAUI, since a lot of platform dependent code has been automated now. Which leaves me with the problem that I can't seem to figure out how to change my current code to accomplish the same thing in MAUI.
Seeing as MAUI is currently not even fully released, I was wondering if this is currently just not possible or if there is some workaround to make it possible.
Any help would be greatly appreciated.
Calling C# from a webview is actually extremely simple. Just navigate and then intercept that in c#.
the xaml:
<WebView WidthRequest="400" HeightRequest="400" Navigating="WebView1_Navigating">
<WebView.Source>`enter code here`
<HtmlWebViewSource>
<HtmlWebViewSource.Html>
<![CDATA[
<HTML>
<script>
function callCsharp(){
window.location.href = 'http://poc.MyFunction?name=john&country=DK';
}
</script>
<BODY
A link that triggers C#
<br>
<button onclick="callCsharp()" type="button">A button calling javascript</button>
</BODY>
</HTML>
]]>
</HtmlWebViewSource.Html>
</HtmlWebViewSource>
</WebView.Source>
The C#:
private async void WebView1_Navigating(object sender, WebNavigatingEventArgs e)
{
var urlParts = e.Url.Split(".");
if (urlParts[0].ToLower().Equals("http://poc"))
{
var funcToCall = urlParts[1].Split("?");
var methodName = funcToCall[0];
var funcParams = funcToCall[1];
Debug.WriteLine("Calling " + methodName);
// prevent the navigation to complete
e.Cancel = true;
// TODO smart parsing and type casting of parameters and then some reflection magic
}
}
MAUI's default WebView has the Eval and EvaluateJavaScriptAsync functions to call JavaScript code from C#:
Eval just executes the script string you pass in a fire-and-forget way.
EvaluateJavaScriptAsync needs to be awaited but also returns a string with a stringified result of the data that the script returned.
If you want to use callback/bridge methods to automatically receive data from the JavaScript side without any input from the C# side of the app, you will have to extend the default per-platform renderers to add that functionality. The good news is that there is an official tutorial on how to do it for Xamarin Forms at Customizing a WebView which is almost straightforward to port to .NET MAUI - you only have to change how renderers are registered.
I have an application that views a page in QWebEngineView widget how can I redirect my javascript console log into my GUI? it currently shows in my debug output.
You have to subclass QWebEnginePage to override javaScriptConsoleMessage virtual function. (http://doc.qt.io/qt-5/qwebenginepage.html#javaScriptConsoleMessage)
class CustomPage : public QWebEnginePage
{
public:
CustomPage(QObject* parent = 0) : QWebEnginePage(parent) {}
virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID)
{
// Here goes your logging code
}
};
I am a novice in selenium. I want to hit a url, search for all the iframes and in every iframe, I want to inject a Javascript code. So how would I do that. So far, I have come up with basic selenium code but do not know how to inject JS.
public class Poc {
public static void main(String[] args) {
System.setProperty("webdriver.chrome.driver","/home/xxx/xxx/xxx/chromedriver");
WebDriver driver = new ChromeDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
driver.get("http://www.sss.org/");
List<WebElement> elements = driver.findElements(By.tagName("iframe"));
for(WebElement element:elements) {
System.out.println(element.getAttribute("id"));
}
driver.close();
System.exit(0);
}
}
To inject code into each iframe you first have to switch to it
import org.openqa.selenium.JavascriptExecutor;
List<WebElement> elements = driver.findElements(By.tagName("iframe"));
for(WebElement element:elements) {
driver.switchTo().defaultContent();
driver.switchTo.frame(element);
if (driver instanceof JavascriptExecutor) {
((JavascriptExecutor) driver).executeScript("alert('hello world');");
}
System.out.println(element.getAttribute("id"));
}
Use JavascriptExecutor for writing javascript code in selenium
Example code for you is
JavascriptExecutor js = (JavascriptExecutor) driver;
WebElement element = driver.findElement(By.linkText("Click ME"));
js.executeScript("arguments[0].setAttribute('attr', '10')",element);
I am working on a task in which we need to put one of our HTML & JS based project inside a JavaFX project or any other suitable containers which are out there. The purpose is to create an app, which can directly be deployed and would prevent any users from checking out the source code of HTML & JS.
Some time back when I was checking out JavaFX, I read that it supports JS, and JS can be used with it. Is there any way to create a container inside which I can put my HTML&JS files by giving path, etc?
How can I go about this? Whatever I am trying to do, what is it called. Any help, pointers, suggestions, would be nice.
Initial test
public class Main extends Application {
private Scene scene;
MyBrowser myBrowser;
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Test web");
myBrowser = new MyBrowser();
scene = new Scene(myBrowser, 1920, 1200);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class MyBrowser extends Region {
final String hellohtml = "hello.html";
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
public MyBrowser(){
URL urlHello = getClass().getResource("hello.html");
webEngine.load(urlHello.toExternalForm());
getChildren().add(webView);
}
}
As #sillyfly suggested use a WebView:
File f = new File(..);
// ..
final WebView webview = new WebView();
webview.getEngine().load(f.toURI().toURL().toString());
The hard part for me is always to figure out the right location to be used to reference the file.
Another option is when you have the HTML in the form of a string to load that as content:
String html = ...
webview.getEngine().loadcontent(html)
Be sure to check out at least the JavaDoc on WebView and WebEngine`
I tried to display a web page using the android WebView but the javascript animations are very slow ... I tried to open the same page from the browser and it works correctly ...
The problem is that i'm using using a JavaScript Interface to call some Java methods (With the WebView) :
mWebView = (WebView) findViewById(R.id.spacetree);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.setWebChromeClient(new MyWebChromeClient());
mWebView.addJavascriptInterface(new JavaScriptInterface(this), "API");
mWebView.loadUrl("MyURL");
And i don't know how to use this interface when i launch this page on a browser (so the animations can be displayed correctly) using this code :
Intent i = new Intent(Intent.ACTION_VIEW,
Uri.parse("MyUrl"));
startActivity(i);
PS: The WebPage is stored locally ('file:///android_asset')
its very easy:
mWebView.loadUrl("javascript: example()");
this can you do after a page is loaded and wont redirect you to a diffrent URL, if your JS is correct.
hope this helps
in file HTML create function
function androidResponse(index) {
AndroidFunction.sendToAndroid(index);
}
in file java code
final IJavascriptHandler handle = new IJavascriptHandler(
ListMapActivity.this);
webMap.addJavascriptInterface(handle, "AndroidFunction");
define class IJavascriptHander
final class IJavascriptHandler {
ListMapActivity ctx;
IJavascriptHandler(ListMapActivity c) {
ctx = c;
}
#JavascriptInterface
public void sendToAndroid(String text) {
final String msgeToast = text;
// this is called from JS with passed value
myHandler.post(new Runnable() {
#Override
public void run() {
// This gets executed on the UI thread so it can safely
// modify Views
shopMapPager.setCurrentItem(Integer.parseInt(msgeToast));
}
});
Toast t = Toast.makeText(getApplicationContext(), text, 2000);
t.show();
}
}
and callBack result with
webMap.loadUrl("javascript:androidResponse();void(0)");