Selenium click doesn't work with angularjs ng-click event - javascript

I'm looking to verify a particular external function is called on a ng-click event. The input declaration is as follows. This works fine in the browsers, so there are no problems functionally, only during testing with Selenium I'm not seeing the correct result. myFunction is calling an external myExternalFunction, which is the behaviour I'm trying to validate. We already have jasmine tests with spies validating this behaviour in a different environment, but Selenium is needed anyways. Also we don't use protractor at the moment. I'm looking for a solution with Selenium.
<input class="myClass" ng-click="myFunction()">
In the C# Selenium tests, I'm writing:
// Creating the mock external function since the context doesn't have it.
IJavaScriptExecutor executor = WebDriver as IJavaScriptExecutor;
executor.ExecuteScript("window.called='false'");
executor.ExecuteScript("window.external = {}");
// The behavior will be just setting the called variable to true.
// We will retrieve the variable and check the value to see if the function was called.
executor.ExecuteScript("window.external.myExternalFunction = function() {window.called = 'true';}");
// Select the element - there's only one element with this classname
IWebElement element = WebDriver.FindElement(By.ClassName("myClass"));
string value = "";
// Verified via debugging that the element is actually valid.
// Tried the following options.
// 1) Just click and wait for event propagation - didn't work.
element.Click(); // Didn't do anything so added a Thread Sleep to make sure.
Thread.Sleep(1000);
value = WebDriver.ExecuteJavaScript<string>("return window.called;"); // returns false - expected to be true.
// 2) Use the actions to focus and click.
IWebDriver driver = WebDriver;
Actions a = new Actions(driver);
a.MoveToElement(element).Perform();
a.MoveToElement(element).Click().Perform();
Thread.Sleep(1000);
value = WebDriver.ExecuteJavaScript<string>("return window.called;"); // returns false - expected to be true.
// 3) Select and click via javascript instead.
executor.ExecuteScript("angular.element('.myClass').click();");
Thread.Sleep(1000);
value = WebDriver.ExecuteJavaScript<string>("return window.called;"); // returns false - expected to be true.
I'm pretty much out of ideas at this stage. Is there no way to make Selenium play nice with angularjs? Similar question here: Selenium click event does not trigger angularjs ng-click without conclusive answer.

I would suggest, use xpath for locating the element and you should be good. Please share the xpath, if possible.

You can use css selector and It should work :
By.cssSelector("input[ng-click='myFunction()']")

Related

How to fire change event of HTML element text using Javascript, in a Chrome Extension?

I'm creating an chrome extension, and having issue that even if I changed the element text, the submit button not enables as usual and other stuff is not processing as manual typing.
I'm trying to fire the text changed event to process the elements normal behavior of manual typing using following script;
var el2=document.getElementById("tmsg");
el2.innerText="hello world";
try{
el2.fireEvent("onchange");
}catch(error)
{
alert("err:"+error);
}
However, the text is set, but getting following error
TypeError: el2.fireEvent is not a function
I'm looking for solution with java script, without JQuery solutions, but none worked for me.
Can anyone please point me what I'm doing wrong here, or better way to do that?
You are getting that error because eventTarget.fireEvent() is a proprietary method used in Internet Explorer.
The modern and standard way to manually dispatch events, according to MDN is as follows:
// CREATE EVENT
const event = new Event('change', {bubbles: true});
// GET TARGET ELEMENT
const el2 = document.getElementById('tmsg');
// DISPATCH EVENT
el2.dispatchEvent(event);
Hope it works.

Detect user key/mouse in Python Selenium

I'm using Selenium Browser for day to day browsing, and I'd like to fire some code when I press some keys on any page. At first I thought I can just load javascript on every page that registers keys/mouse input, but I'd actually really prefer to have some python list available with past keys/mouse clicks, e.g. my key example in javascript:
var myhistory = []
document.addEventListener("keydown", keyDownTextField, false);
function keyDownTextField(e) {
var keyCode = e.keyCode;
myhistory.push(keyCode)
}
Is there any way to do this in pure Python/Selenium?
What I would try:
Execute a javascript that registers at the document body
<body onkeyup="my_javasctipt_keyup()" and onkeydown="my_javasctipt_keydown()">
using browser.execute_script. (partially solved, see question)
Save the key up and keydown events in a variable in javascript. (solved, see question)
use browser.execute_script to return the variables.
What I am uncertain about:
The return value of browser.execute_script may return json serializable objects or strings only
keyup and keydown in body may not work if they are used in child elements that define their own event listeners
Hopefully this is of help. If any code results form this I would be interested in knowing.
This code is what I feel should work:
from selenium import webdriver
browser = webdriver.Firefox()
browser.execute_script("""var myhistory = []
document.addEventListener("keydown", keyDownTextField, false);
function keyDownTextField(e) {
var keyCode = e.keyCode;
myhistory.push(keyCode)
}""")
def get_history():
return browser.execute_script("myhistory")
# now wait for a while and type on the browser
import time; time.sleep(5000)
print("keys:", get_history())
The point is that the code of selenium can never run at the same time as the browser handles keyboard input. As such, events need to be handled in javascript, the result saved, e.g. in an array and then, when selenium is asked, the array is returned to Python.
boppreh/keyboard would let you do that.
You install it. pip install keyboard
You import it. import keyboard
You use it. keyboard.add_hotkey('left', print, args=['You pressed the left arrow key'])
Then you disable it. keyboard.remove_all_hotkeys()
Well, in that case you had to choose the right tool for the job, i advice puppeteer a web-automation family instrument a pure-made JS, which can easily interact with the browser ( from js to js ) and catch events directly from the other side without any mediation.
Yet with selenium you can still achieve this transitively without messing too much with the pages's code or overcharging it with unnecessary tasks, also reloading the page content resets all its variables, which means it's lossy approach. The best closest way is to set an eventhandler internally and directly catch it from outside using Runtime.evaluate instead because it doesn't affect the page content and specifically it sticks to the function until it yields something using promise calls, it's better away than probing around some global variable over and over which is seen a bad practise see here.
myhistory = []
evt_handler = """
new Promise((rs,rj) => window.onkeydown= e => rs(e.keyCode) )
"""
def waitforclick():
try:
myhistory.append(browser.execute_cdp_cmd('Runtime.evaluate', {'expression': evt_handler, 'awaitPromise': True,'returnByValue': True})['result']['value'])
except:
waitforclick()
To avoid locking out the cpu you need to fork a thread in parallel.
from threading import Timer
t = Timer(0.0, waitforclick)
then t.start() instead of waitforclick().
Also you can use timeout if you want to reject the promise with a zero value after some time.

setReadOnly causes error when called on instanceReady of CKEditor

I'm trying to set my CKEditor instance to be "readOnly" after the instance has fully loaded but I'm getting a Javascript error: Cannot call method 'setReadOnly' of null. When I dig into it, the error is coming from this line in the ckeditor.js, within the editor.setReadOnly method: this.editable().setReadOnly(a); That means that the editor exists, but the editable method/attribute (on the CKEditor instance) does not.
Below is my code, and I'll explain it a little. My app is a combination of GWT and Backbone. The CKEditor itself is created by the Backbone code but the parent element is in GWT so that's where I initiate the setEnabled action.
private native void setEnabledOnLoad(boolean enabled, String id) /*-{
CKEDITOR.on("instanceReady", function(evt) {
if(evt.editor.name === id) {
Namespace.trigger(Namespace.Events.SET_ENABLED, enabled);
}
});
}-*/;
setEnabled: function(enabled) {
this.editor.setReadOnly(!enabled);
if(enabled){
this.editor.focusManager.focus();
} else {
this.editor.focusManager.blur();
}
}
The Backbone class has a listener for Namespace.Events.SET_ENABLED that triggers setEnabled.
Is there another CKEditor event that I should listen for? There doesn't appear to be an instanceReady event on editable. What am I missing?
EDIT
this.editor is created in the Backbone class render function like this:
this.editor = CKEDITOR.replace(this.$(this.id)[0], config);
The reason I don't add the instanceReady listener right after it's created is because the function setEnabledOnLoad is called in GWT before the instance has been fully initialized. This is a result of having the code in two places. GWT has said "ok, create the instance" but Backbone hasn't finished by the time GWT goes to the next line of code and wants to set it enabled/disabled.
Two years later, but here is my solution. Maybe someone else will find it useful.
As stated above, the event is appearantly triggered before the editable() function is fully set up, and therefore one solution is to simply wait for it to finish before setting it to readonly. This may be an ugly way to do it, but it works.
//Delayed execution - ckeditor must be properly initialized before setting readonly
var retryCount = 0;
var delayedSetReadOnly = function () {
if (CKEDITOR.instances['bodyEditor'].editable() == undefined && retryCount++ < 10) {
setTimeout(delayedSetReadOnly, retryCount * 100); //Wait a while longer each iteration
} else {
CKEDITOR.instances['bodyEditor'].setReadOnly();
}
};
setTimeout(delayedSetReadOnly, 50);
You could try subscribing to instanceReady event this way:
CKEDITOR.instances.editor.on("instanceReady", onInstanceReadyHandler)
However, the editor instance must have been already created by then (inspect CKEDITOR.instances in the debugger).
I'm a bit confused about the difference between editable and editor. Could you show the fragments of your code where this.editor and this.editable get assigned?
[EDITED] I guess I see what's going on. CKEDITOR is a global object, you may think of it as of a class which holds all CKEDITOR instances. Trying to handle events with CKEDITOR.on isn't right, you need to do it on a specific instance (like I've shown above). I assume, "editor" is the ID of your parent element you want to attach a CKEDITOR instance to (please correct me if I'm wrong). I'm not familiar with Backbone, but usually it's done with replace:
var editorInstance = CKEDITOR.replace("editor", { on: {
instanceReady: function(ev) { alert("editor is ready!"); }}});
Here we attach a new instance of CKEDITOR to the editor parent element and subscribe to the instanceReady event at the same time. The returned object editorInstance should provide all the APIs you may need, including setReadOnly. You could also access it through the global CKEDITOR object using the parent element ID, i.e. CKEDITOR.instances.editor. On the other hand, editable is rather a service object available on editor. I can't think of any specific case where you might need to use it directly.
I apologize for never updating this with my solution. I needed to decouple the GWT function further from the CKEditor behavior. So, I added a function in GWT 'setEnabled' that is called from the parent object when it wants to update the enabled state of the CKEditor object.
public void setEnabled(boolean enabled) {
this.enabled = enabled;
toggleCKEditorEnabled(enabled);
}
Then changed the function referenced above 'setEnabledOnLoad' to be 'toggleCKEditorEnabled' which triggers the SET_ENABLED event with the enabled value.
Instead of attaching the listener to the specific instance of CKEditor, I added in to the Backbone MessageEntryView class that is the container of the CKEditor instance. In the initialize function of the MessageEntryView, I added this line
Namespace.on(Namespace.Events.SET_ENABLED, this.setEnabled);
This only works because I have one instance of CKEditor loaded on the screen at any given time. This problem and its solution stopped us from being able to add more CKEditor instances to the page at a time, which is something we discussed before moving on and replacing our whole client with Backbone.

Overwrite a javascript variable from webdriver

I'm not sure whether is there any solution for my issue but, unfortunately I haven't found any article or information about it.
The situation is the following. We have a site which uses jQuery heavily and there is a service which refreshes a part of the site in every 5th or 10th second. Due to this half of the time I got this error from WebDriver:
"Element not found in the cache - perhaps the page has changed since it was looked up"
According to the Internet I got this error when the DOM tree has changed between the moment when the WebElement has been initialized and when I want to use it to perform, for example, a click event.
According to our developers our jquery solution has a variable which controls whether the page will be refreshed or not. So, to solve my issue I have to overwrite this variable. I have to overwrite this variable in that way the jQuery will be able to understand it - I mean in the same instance if this definition is proper in this context.
So, I would like to ask whether is possible or not? If so, than I would like to ask a small example.
Thanks in advance!
AndrĂ¡s
I can only agree with Aleh.
Using JavaScriptExecutor is the only way to handle such issues.
I had a problem with jQuery jNice library and couldn't find any other solution.
Here is an example in Java for filling a text field:
JavascriptExecutor js = (JavascriptExecutor) webDriver;
js.executeScript("document.getElementsByName('<field_name_gets_here>')[0].value='" + your_value + "'");
If the JavaScript variable you mentioned is global, then yes - you can overwrite it by executing JavaScript from your Selenium. For example, if that variable is called doRefresh, you can overwrite it by executing JS like this: doRefresh = false; from Selenium.
If that variable is not global, the above won't work. However, it sounds like the elements in question might be dynamically loaded via ajax - in which case the xhr object is global and you can access it instead.
So, first you can make a local copy of the xhr object and then overwrite the original (effectively disabling it) by executing JavaScript from Selenium:
// create a copy of the xhr object for later use
var xhrHolder = window.XMLHttpRequest;
// overwrite the original object to disable it
window.XMLHttpRequest = {};
Then find your element via Selenium as you would normally. And proceed with your test.
When finished, you can put the xhr object back in place (so the page can continue refreshing and doing ajax) by executing JavaScript from Selenium:
// put the xhr object back
window.XMLHttpRequest = xhrHolder;
You can try my approach - I created my own wrapper for situations where page might be loading. The below part of code tries to search element in the loop, for three seconds (configurable). BTW the driver variable below is instance of WebDriver
private WebElement foundElement;
public WebElement find(By by){
for (int milis=0; milis<3000; milis = milis+200){
try{
foundElement = driver.findElement(by);
}catch (Exception e){
try {
Thread.sleep(200);
} catch (InterruptedException e1) {
e1.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
}
return foundElement;
}
And later in the code:
WebElement submitButton = find(By.id("submitNewBid"));
submitButton.click();
I believe it is possible. Example for c#:
((IJavaScriptExecutor)driver).ExecuteScript("window.$('.class').data('var') = 0;")

Issue with partial refresh triggered twice

I have a dojo button bar which is bound to a csjs function. This function does a partialrefreshget() on a datable control. The datatable control contains a view as its datasource.
In the this.keys property I have defined some logic to see if the partialrefresh was triggered by checking for the context.getSubmittedValue(). While experimenting with this technique I noticed that the following code is triggered twice.
<xp:this.keys><![CDATA[#{javascript:
var vec = new java.util.Vector()
vec.add("category");
if(context.getSubmittedValue()!=null){
var x = context.getSubmittedValue().trim();
print("--")
}
return vec;}]]></xp:this.keys>
the print statement is printed twice to the console and the logic is therefore triggered twice. Can someone explain to me why this happens and what I can do about it? Should i check for submittedvalues somewhere else or?
I think if you implement a phase listener to print out each phase step, you'll see that this.keys is evaluated twice during the LifeCycle. Probably once during Render Response, and the other during Restore View or something. I would avoid putting application logic within property calculations as it can be triggered at times you would not think it should be unless you are very in tuned with the application lifecycle.
I actually see the submit two or three times on some controls. I have heard that it is an anomalie in the JSP engine that has not been resolved.
What I do is write the vec to a request scope variable after it is computed. then add logic before it is computed to fetch the request scope variable and if it exisits, return it instead of recomputing the value.
After a bit of testing i gave up calling my own partialrefreshget method.the extlib dojo toolbar contains a onclick event which is triggerd when on a node the submitvalue is set. In this onclik event i added code like
Var v = context.getsubmittedvaleu();
If("action".equals(v)){
// do stuff that changes the dataset..
}
The event handler is set to partial refresh a datatable wich receives the new data. This is a much cleaner implementation than checking the submittedvalue in the datasource ( as stated by (jeremy hodge).
This way the datasource is only refreshed once.
As a sidenote i would like add that it would be nice to add such an event directly to the treenode(s) as I would do in standard java swing /awt dev by adding a controllistener to a button.

Categories