Protractor - StaleElementReferenceException occurs sporadically - javascript

I've stuck with problem with Stale Element Reference Exception using protractor/jasmine2.
My spec:
var LoginPage = require('../pages/login_page.js');
var WelcomePage = require('../pages/welcome_page.js');
describe('Test -> testing something', function () {
var loginPage;
var EC = protractor.ExpectedConditions;
var waitTimeout = 10000;
function logIn() {
loginPage.setUser('user');
loginPage.setPassword('password');
loginPage.login();
}
beforeEach(function () {
browser.ignoreSynchronization = true;
loginPage = new LoginPage();
browser.wait(EC.presenceOf(loginPage.userLogin), waitTimeout);
logIn();
var welcomePage = new WelcomePage();
browser.wait(EC.visibilityOf(welcomePage.usersButton), waitTimeout);
welcomePage.usersButton.click();
});
The problem is that StaleElementReferenceException occurs randomly on the last line of beforeEach function when I want to click on the usersButton.
No idea why ExpectedCondition do not work (have tried as well different EC like presenceOf, elementToBeClickable etc. but none solved problem).
See page with usersButton defined for reference:
'use strict';
var WelcomePage = function () {
};
WelcomePage.prototype = Object.create({}, {
usersButton: {
get: function () {
return element(by.css('#users a'));
}
}
});
module.exports = WelcomePage;
I think that some generic retry_mechanism would be needed to handle it, Anyone has similiar issue ?
Finally written function like that
var clickOn = function (element) {
browser.wait(EC.visibilityOf(element), waitTimeout).then(function() {
element.click();
});}
which is invoked like this:
clickOn(welcomePage.usersButton);
Update:
Have tested it several times and when I am running tests on selenium grid I am still getting Stale Element exception on this exact element. So provided solution did not work ...
Failed: stale element reference: element is not attached to the page document (Session info: chrome=45.0.2454.93) (Driver info: chromedriver=2.19.346078 (6f1f0cde889532d48ce8242342d0b84f94b114a1),platform=Windows NT 6.1 SP1 x86_64) (WARNING: The server did not provide any stacktrace information) Command duration or timeout: 15 milliseconds For documentation on this error, please visit: http://seleniumhq.org/exceptions/stale_element_reference.html Build info: version: '2.47.1', revision: '411b314', time: '2015-07-30 03:03:16' System info: host: 'ITHFPC17', ip: '10.98.0.48', os.name: 'Windows 7', os.arch: 'x86', os.version: '6.1', java.version: '1.8.0_40' Driver info: org.openqa.selenium.chrome.ChromeDriver Capabilities [{applicationCacheEnabled=false, rotatable=false, mobileEmulationEnabled=false, chrome={userDataDir=C:\Users\SELENI~1\AppData\Local\Temp\scoped_dir2384_11396}, takesHeapSnapshot=true, databaseEnabled=false, handlesAlerts=true, hasTouchScreen=false, version=45.0.2454.93, platform=XP, browserConnectionEnabled=false, nativeEvents=true, acceptSslCerts=true, locationContextEnabled=true, webStorageEnabled=true, browserName=chrome, takesScreenshot=true, javascriptEnabled=true, cssSelectorsEnabled=true}] Session ID: 3244710644015ee170986333564ab806
Failed: Wait timed out after 10032ms
The next approach is to wait until element is present, afterwards if it's visible and afterwards if it's clickable... but it doesn't work as well. The strange thing is that on selenium grid everything works fine but when I am trying to run test locally then getting previously mentioned exception.
exports.clickOn = function (element) {
browser.wait(EC.presenceOf(element), waitTimeout).then(function () {
browser.wait(EC.visibilityOf(element), waitTimeout)
}).then(function () {
browser.wait(EC.elementToBeClickable(element), waitTimeout)
}).then(function () {
element.click();
});
};
Anyone has any idea how to deal with it ? ... I am stuck.

Wait until the element is eligible to be clicked on by resolving the promise that the wait until elementToBeClickable() function returns. Also make sure that your actions before click() are completed, so that protractor can find the element as intended. Probably chaining all the actions to one another can be a good solution. That way the StaleElementReferenceException error can be avoided. Here's how -
browser.wait(EC.presenceOf(loginPage.userLogin), waitTimeout).then(function(){
logIn();
}).then(function(){
var welcomePage = new WelcomePage();
}).then(function(){
browser.wait(EC.elementToBeClickable(welcomePage.usersButton), waitTimeout).then(function(){
welcomePage.usersButton.click();
});
});
Hope it helps.

Finally I've managed this problem by adding 'not nice' simple retrying mechanism.
exports.clickOnElementWithWait = function (element, attempts) {
if (attempts == null) {
attempts = 10;
}
return element.click().then(function (found) {
}, function (err) {
if (attempts > 0) {
browser.sleep(100);
return exports.clickOnElementWithWait(element, attempts - 1);
} else {
throw err;
}
});
};
Update: Unfortunately it works as well randomly. The number of exceptions decreased but still ...
I have Java code which helped me when I've experienced it in pure selenium/junit and wondering if it's any free tool to convert it to javascript ?
Anyone could help me ?
public class RetryMechanism {
private static final int DEFAULT_RETRY_NR = 20;
private static final long DEFAULT_WAIT_TIME_IN_MILLI = 1000;
private int numberOfRetries;
private int numberOfTriesLeft;
private static long timeToWait;
public RetryMechanism() {
this(DEFAULT_RETRY_NR, DEFAULT_WAIT_TIME_IN_MILLI);
}
public RetryMechanism(int numberOfRetries, long timeToWait) {
this.numberOfRetries = numberOfRetries;
numberOfTriesLeft = numberOfRetries;
this.timeToWait = timeToWait;
}
public boolean shouldRetry() {
return numberOfTriesLeft > 0;
}
public void errorOccurred(Throwable throwable) throws Exception {
numberOfTriesLeft--;
if (!shouldRetry()) {
throw new Exception("Retry Failed: Total " + numberOfRetries
+ " attempts made at interval " + getTimeToWait()
+ "ms.\n"
+ "Error message is : " + throwable.getMessage()
+ "\n"
+ "Caused by : " + throwable.getCause());
}
waitUntilNextTry();
}
public static long getTimeToWait() {
return timeToWait;
}
public static void waitUntilNextTry() {
try {
Thread.sleep(getTimeToWait());
} catch (InterruptedException ignored) {
}
}
public interface Action {
void doJob();
}
public static void retry(Action action) throws Exception {
RetryMechanism retry = new RetryMechanism();
while (retry.shouldRetry()) try {
action.doJob();
break;
} catch (Exception e) {
retry.errorOccurred(e);
}
}
}

Related

Websocket - Why can't I send messages to a specific user?

Question:
Why am I being able to send messages to all users but not to a specific user? Am I missing something obvious here?
Problem:
My POC uses the following resources:
The official Spring Boot tutorial for sending messages to all users
This Baeldung tutorial for sending messages to specific users
I am currently using two endpoints:
The first one sends messages to all users and works fine.
Send to all users - working
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public StompResponse greeting(StompRequest message) throws Exception {
Thread.sleep(1000); // simulated delay
return new StompResponse("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
The second one doesn't work. The method sendSpecific never gets called and I'm stuck trying to understand why.
Send to specific user - NOT working
/**
* Example of sending message to specific user using 'convertAndSendToUser()' and '/queue'
*/
#MessageMapping(Constants.SECURED_CHAT_ROOM)
public void sendSpecific(#Payload StompRequest message, Principal user, #Header("simpSessionId") String sessionId) throws Exception {
System.out.println("METHOD CALLED"); // This method never gets called
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
StompResponse out = new StompResponse(HtmlUtils.htmlEscape(message.getName()));
simpMessagingTemplate.convertAndSendToUser("hardcoded_username", Constants.SECURED_CHAT_SPECIFIC_USER, out, headerAccessor.getMessageHeaders());
}
Socket configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", Constants.SECURED_CHAT_SPECIFIC_USER);
config.setUserDestinationPrefix("/secured/user");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/connection-uri").withSockJS();
registry.addEndpoint(SECURED_CHAT_ROOM).withSockJS();
}
}
Client code:
const SocketConnection = function(l) {
const listener = l;
let sessionId;
const parseSessionId = (url) => {
console.log(stompClient.ws._transport.url);
url = url.replace("ws://localhost:8080" + Uri.CONNECTION_URI + "/", "");
url = url.replace("/websocket", "");
url = url.replace(/^[0-9]+\//, "");
console.log("Your current session is: " + url);
return url;
};
this.connect = () => {
const socket = new SockJS(Uri.CONNECTION_URI);
stompClient = Stomp.over(socket);
stompClient.connect({}, (frame) => {
listener.onSocketConnect(true);
console.log('Connected: ' + frame);
stompClient.subscribe(Uri.ALL_USERS, function (greeting) {
listener.onResponseReceived(JSON.parse(greeting.body).content);
});
// WIP
const url = stompClient.ws._transport.url
sessionId = parseSessionId(url);
console.log('SUBSCRIBING FOR SPECIFIC USER: ', Uri.SECURED_CHAT_SPECIFIC_USER)
// stompClient.subscribe(Uri.SECURED_CHAT_SPECIFIC_USER + sessionId, function (msgOut) {
stompClient.subscribe(Uri.SECURED_CHAT_SPECIFIC_USER + "-user" + sessionId, function (msgOut) {
// that.messageOut(JSON.parse(msgOut.body), opts);
console.log('SINGLE USER WORKS!!!!!!!',JSON.parse(msgOut.body))
});
});
}
this.disconnect = () => {
if (stompClient !== null) {
stompClient.disconnect();
}
listener.onSocketConnect(false);
console.log("Disconnected");
}
this.sendRequest = (uri) => {
stompClient.send(uri, {}, JSON.stringify({'name': $("#name").val()}));
}
};
Client initialization:
$(function () {
const socketClient = new SocketConnection(new FormUI());
$("form").on('submit', function (e) {
e.preventDefault();
});
$( "#connect" ).click(function() { socketClient.connect(); });
$( "#disconnect" ).click(function() { socketClient.disconnect(); });
$( "#send" ).click(function() {
socketClient.sendRequest(Uri.ALL_USERS);
socketClient.sendRequest(Uri.SECURED_CHAT_ROOM);
});
});

I get this the error "Error: invalid address ...." while running my dapp

I'm developing a simple dapp for my private blockchain Quorum. I wrote my smart contract in Solidity and a script with JavaScript.
After running my blockchain and my JavaScript console, I did this:
and I get the error:
Error: invalid address
at web3.js:3930:15
at web3.js:3756:20
at web3.js:5025:28
at map ( < native code > )
at web3.js:5024:12
at web3.js:5050:18
at web3.js:5075:23
at web3.js:4137:16
My smart contract is this one:
pragma solidity >=0.4.0 <0.7.5;
contract MyContract {
int value;
constructor() public{
value=10;
}
function get() public view returns(int) {
return value;
}
function set(int value2) public {
value=value2;
}
}
And my script in javascript is this one:
a = eth.accounts[0]
web3.eth.defaultAccount = a;
// Risultato del comando > solcjs --bin --abi MyContract.sol
var abi = [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"get","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"value2","type":"int256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"}];
var bytecode = "0x60806040523480156100115760006000fd5b505b600a60006000508190909055505b610026565b60db806100346000396000f3fe608060405234801560105760006000fd5b506004361060365760003560e01c80636d4ce63c14603c578063e5c19b2d146058576036565b60006000fd5b60426084565b6040518082815260200191505060405180910390f35b608260048036036020811015606d5760006000fd5b81019080803590602001909291905050506095565b005b600060006000505490506092565b90565b8060006000508190909055505b5056fea264697066735822122032710d493b7bc22bd80599320435533d8a94bf485e2b4c09f9ba2b0dd44acb4964736f6c63430007040033";
// Creazione del nuovo contratto con nome MyContract
var simpleContract = web3.eth.contract(abi);
var simple = simpleContract.new({from:web3.eth.accounts[0], data: bytecode, gas: 0x47b760, privateFor: ["QfeDAys9MPDs2XHExtc84jKGHxZg/aj52DTh0vtA3Xc="]}, function(e, contract) {
if (e) {
console.log("Error creating contract", e);
} else {
if (!contract.address) {
console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");
} else {
console.log("Contract mined! Address: " + contract.address);
console.log(contract);
}
}
});
What can I try to resolve this?
I think the issue is that you have a small mistake in the ABI - the get() function should have "constant":true in the abi due to the function being labelled as view.
Without that, the get() function requires a from address to be defined for the transaction (i.e. you have to use private.get({"from": fromAddress}) or set the default account using web3.eth.defaultAccount = accountAddress).
If you correct the ABI then it should work.

Execute Javascript on external url using HybridView, Xamarin cross platform

I have tried to execute JavaScript on an external url (ie: http://facebook.com) using WebView from Visual Studio Mac 2019, and so far no results.
To do so, I have tried to follow along with the official tutorial here https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/hybridwebview, and also tried a simpler one here: https://xamarinhelp.com/xamarin-forms-webview-executing-javascript/
Here is what I did with explanations:
On my shared folder, I created an HybridWebView class with the following code:
public class HybridWebView : WebView
{
Action<string> action;
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(Func<string, Task<string>>),
declaringType: typeof(HybridWebView),
defaultValue: default(string));
public string Uri
{
get => (string)GetValue(UriProperty);
set
{
SetValue(UriProperty, value);
}
}
public void RegisterAction(Action<string> callback)
{
action = callback;
}
public void Cleanup()
{
action = null;
}
public void InvokeAction(string data)
{
if (action == null || data == null)
{
return;
}
action.Invoke(data);
}
public Func<string, Task<string>> ExecuteJavascript
{
get { return (Func<string, Task<string>>)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
}
From The macOS project which I use to test my cross-platform app, I tried the following custom renderer:
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, WKWebView>
{
protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
{
base.OnElementChanged(e);
var webView = e.NewElement as HybridWebView;
if (webView != null)
{
Control.LoadRequest(new NSUrlRequest(new NSUrl(Element.ExecuteJavascript.ToString())));
}
}
}
To note that the following part wouldn't work:
var webView = e.NewElement as HybridWebView;
if (webView != null)
webView.ExecuteJavascript = (js) =>
{
return Task.FromResult(this.ExecuteJavascript(js)); // issue at ExecuteJavascript with following error ('HybridWebViewRenderer' does not contain a definition for 'ExecuteJavascript' ), hence replaced by Control.LoadRequest ...
};
From my ViewModel, I did the following:
public Func<string, Task<string>> EvaluateJavascript { get; set; }
public async Task OnConnectTapped()
{
Console.WriteLine("on connect tapped");
// passing the url onto a connection service
var hybridWebView = new HybridWebView
{
Uri = "https://facebook.com/"
};
//hybridWebView.InvokeAction("document.getElementById('td');");
//var result = await hybridWebView.RegisterAction(data => DisplayAlert("Alert", "Hello " + data, "OK"));
var result = await hybridWebView.ExecuteJavascript("document.cookie;");
Console.WriteLine("result is {0}", result);
}
Here is the error when trying to execute my code:
System.NullReferenceException: Object reference not set to an instance of an object
at MyApp.ViewModel.MainModel.OnConnectTapped () [0x00031] in .../../././/ViewModel/MainModel.cs:451
at .......<.ctor>g__c5|48_9 () [0x0001f] in /../../../.cs:143
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__7_0 (System.Object state) [0x00000] in /Users/builder/jenkins/workspace/xamarin-macios/xamarin-macios/external/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs:1021
at Foundation.NSAsyncSynchronizationContextDispatcher.Apply () [0x00002] in /Library/Frameworks/Xamarin.Mac.framework/Versions/6.6.0.12/src/Xamarin.Mac/Foundation/NSAction.cs:178
at at (wrapper managed-to-native) AppKit.NSApplication.NSApplicationMain(int,string[])
at AppKit.NSApplication.Main (System.String[] args) [0x00040] in /Library/Frameworks/Xamarin.Mac.framework/Versions/6.6.0.12/src/Xamarin.Mac/AppKit/NSApplication.cs:100
at redacted.macOS.MainClass.Main (System.String[] args) [0x00017] in /Users/dom-bruise/Projects/redacted/redacted.macOS/Main.cs:11
For me, it could either be because I can't execute external pages, or the part where I replaced by the following messing up my attempt.
if (webView != null)
{
Control.LoadRequest(new NSUrlRequest(new NSUrl(Element.ExecuteJavascript.ToString())));
}
My main goal here is to have my app execute JavaScript underneath the hood on pages using WebView, and fill in forms automatically calling back C# from my app.

What is the alternative to `alert` in metro apps?

I created my first app on Windows 8 vs 2012 and it runs and works fine. But when I try to say "helloworld" from JavaScript like this:
alert("Hello World");
I get an error:
Unhandled exception at line 21,
column 13 in ms-appx://1af489cf-bac6-419b-8542-fdc18bdd2747/default.html
0x800a1391 - JavaScript runtime error: 'alert' is undefined
What is the alternative if alert is obsolete?
You should use Windows.UI.Popups.MessageDialog:
(new Windows.UI.Popups.MessageDialog("Content", "Title")).showAsync().done();
However, you should be aware that:
This is not blocking like the familiar alert
Because it's not blocking you may try to show them multiple messages boxes; this isn't allow.
I answered another question like this here. Here's the code to allow you to call alert, and have multiple messages in flight:
(function () {
var alertsToShow = [];
var dialogVisible = false;
function showPendingAlerts() {
if (dialogVisible || !alertsToShow.length) {
return;
}
dialogVisible = true;
(new Windows.UI.Popups.MessageDialog(alertsToShow.shift())).showAsync().done(function () {
dialogVisible = false;
showPendingAlerts();
})
}
window.alert = function (message) {
if (window.console && window.console.log) {
window.console.log(message);
}
alertsToShow.push(message);
showPendingAlerts();
}
})();
Remember that alert is not a JavaScript function, it's a browser (host) function, therefore, it's not available in non browser environments.
This link tells you to do the following
Replace all alert functions with firing an event window.external.notify("message");
Use scriptnotify event in webview to get that message.
Show metro own dialog: MessageDialog
javascript:
(function () {
window.alert = function (message) {
window.external.notify( message);
}
//do some test
alert("a");
alert("b");
alert("c");
window.setInterval(function () {
alert("e");
alert("f");
}, 5000);
window.setInterval(function () {
alert("d");
alert("2");
}, 10000);
})();
C#:
//register ScriptNotify event
webView2.ScriptNotify += webView2_ScriptNotify;
async void webView2_ScriptNotify(object sender, NotifyEventArgs e)
{
MSG.Alert(e.Value);
}
public class MSG
{
static List<string> messages = new List<string>();
public static void Alert(string message)
{
messages.Add(message);
if (messages.Count == 1)
{
Show(messages.First());
}
}
private static async Task Show(string message)
{
MessageDialog md = new MessageDialog(message, "Title");
md.Commands.Add(
new UICommand("OK", new UICommandInvokedHandler((cmd) =>
{
messages.RemoveAt(0);
})));
await md.ShowAsync();
while (messages.Count > 0)
{
await Show(messages.First());
}
}
}

memory leak in ajax - setInterval

I have an ajax code which causes memory leak (especially in IE).
function setStatus() {
var formInput=$(this).serialize();
$.getJSON('CheckStatus.action', formInput, function(data) {
if(data == false) {
function getEventsPeriodicaly() {
getEvents();
};
var timer = setInterval(function () {getEventsPeriodicaly();}, 5000);
}
}
);
}
function getEvents() {
var formInput=$(this).serialize();
$.getJSON('StartEP.action', formInput,function(data) {
var txt = $("#txtEventsArea");
if(data != null && data.toString().length!=0) {
txt.val(data.join('\n') + '\n' + txt.val());
data=null;
}
}
)}
StartEP
public String startEP() throws Exception {
logger.info("[EP] In startEP");
try {
synchronized(status) {
if(!getStatus()) {
EventProcessor amiep = EventProcessor.getInstance();
amiep.addObserver(this);
new Thread(amiep).start();
setStatus(true);
}
}
} catch (Exception ex) {
logger.error("Unable to start EP", ex);
return ERROR;
}
logger.info("[EP] In startEP, before loop");
while(!gotNewData) {
Thread.sleep(4000);
}
gotNewData = false;
logger.info("[EP] Out startEP");
return SUCCESS;
}
The StartEP action returns messages (about 5KB on each request). First I thought it concerned with setting text to textarea, but after some tests got that it is not the reason. Could it be setInterval method?
Is there any considerations?
thanks
I would say this looks pretty suspect:
while(!gotNewData) {
Thread.sleep(4000);
}
Where is gotNewData set? If you call the web service once and set gotNewData to true and then call another web service and set gotNewData to false I don't think there is a guarantee that you're setting the variable within the same instance of the application. Therefore, if you are not, then every time you're hitting the web service you are starting a new thread and then continually putting it back to sleep.

Categories