I am trying to automate couple of pages using selenium web driver and node js . I was able to login , but after login I want to use same session initiated by web driver so that I can do automated testing on session protected page. This is my attempt
async function login(){
Let d = await new Builder()
.forBrowser('chrome')
.build();
await d.get('https://demo.textdomain.com/')
await d.findElement(By.id('username')).sendKeys('admin ')
await d.findElement(By.id('password')).sendKeys('admin');
await d.findElement(By.css('button[type="submit"]')).click();
d.getPageSource().then(function(content) {
if(content.indexOf('Welcome text') !==-1 ) {
console.log('Test passed');
console.log('landing page');
d.get('https://demo.textdomain.com/landingpage') //this is still going to login page as i cannot use the previous session
} else {
console.log('Test failed');
return false;
}
//driver.quit();
});
}
login();
Am I accidentally discarding the browser after login.
From a similar question on SQA StackExchange, you can store and restore the current session's cookies:
Using Javascript:
// Storing cookies:
driver.manage().getCookies().then(function (cookies) {
allCookies = cookies;
});
// Restoring cookies:
for (var key in allCookies) {
driver.manage().addCookie(key, allCookies[key]);
}
You might just be dealing with timing issues. Selenium moves very fast. Way faster than you can interact as a user. So it often acts in what seems like unpredictable ways. But that's only because Selenium is acting much faster than you would as a user. In order to work around this, you should make good use of Selenium's built-in driver.wait. For example:
const button = driver.wait(
until.elementLocated(By.id('my-button')),
20000
);
button.click();
The above waits until the button with id my-button is present in the DOM, and then clicks it. It will wait for a maximum of 20000 milliseconds, but will finish as soon as the button becomes available.
So in your case, if there is something that becomes available after the user is successfully logged in, you could wait on that element before going to the new page in your code.
As an aside, I'm also not so sure why you are using getPageSource()? That seems like a very heavy-handed way to get what you are looking for. Isn't that content inside an element you could get the contents of?
I wrote an article about How to write reliable browser tests using Selenium and Node.js which might help you understand in more detail the code example above from the article, along with other techniques you can use to wait reliably for a variety of conditions in the browser.
I believe your problem is not properly waiting for the login to complete.
Selenium doesn't wait for asynchronous actions to be done, it moves to the next line, so when you ask for the page source, there is a good chance the login action didn't complete on the server and the result is not what you expect it to be.
you have to explicitly tell Selenium to wait, so you need to add some code between the login and the code that checks if the user is login, for the sake of this assumption, add a 10 seconds timeout.
if this works for you, you wouldn't want to just waste time, so you need to wait for certain elements on the page to change because of the login, for example, you need to wait for the presence (or visibility if it is already in the DOM) of the user photo in the header.
also, I'm not sure how the "getPageSource" function behaves, it can use the existing page, or it can ask for a fresh copy.
I would advise you to use other ways to test if the user is logged in, by inspecting the DOM.
I suggest to re-use the session-cookie after first login in other web-driver instances.
First store the cookie:
var cookieValue = firstWebDriver.Manage().Cookies.GetCookieNamed(name:"cookie_name");
Then you can pass it by to any WebDriver instance, set it and drive the web-app as it would be the same user with different browser instances:
anotherWebDriver.Manage().Cookies.AddCookie(new Cookie(name:"cookie_name", value:cookieValue));
If you want to use the same browser instance, you have to synchronize them, because WebDriver invocations are in general not thread-safe and would probalby often lead to exceptions (e.g. stale because an element was changed or notfound, because one web-driver navigated to a different page).
Then I suggest to just use the window handle for the next instance, without caring about the session. The first one opens and the last one closes the session (count the referenced handles) and be sure only one driver uses this handle at a time. You can also create new browser windows and this will keep the session and give you a new handle:
var handle = firstWebDriver.CurrentWindowHandle;
otherWebDriver.SwitchTo().Window(handle);
I wrote the code in C# but should be easily adaptable to JavaScript.
Related
I am trying to do an API call when the user is trying to close/reload the browser/tab. I don't want to call the API if the user clicks on cancel. I followed JavaScript, browsers, window close - send an AJAX request or run a script on window closing, but it didn't solve my issue. I followed catching beforeunload confirmation canceled? for differentiating between confirm and cancel. I have no idea how to make the API call when the user reloads/closes the browser and not to call the API when user clicks on cancel. I followed JavaScript, browsers, window close - send an AJAX request or run a script on window closing and tried like
For showing alert on reload or close the tab
<script>
window.addEventListener("onbeforeunload", function(evt){
evt.preventDefault()
const string = '';
evt.returnValue = string;
return string;
})
</script>
and on click of cancel, nothing should happen. If the user is forcefully closing the browser or reloading, the API should be called
<script type="module">
import lifecycle from 'https://cdn.rawgit.com/GoogleChromeLabs/page-lifecycle/0.1.1/dist/lifecycle.mjs';
lifecycle.addEventListener('statechange', function(event) {
if (event.originalEvent === 'visibilitychange' && event.newState === 'hidden') {
var URL = "https://api.com/" //url;
var data = '' //payload;
navigator.sendBeacon(URL, data);
}
});
</script>
But it's not happening. Any help is appreciated. Thanks
Your problem is happening because you're using beforeunload to present a prompt.
I can see that you're handling the beforeunload event properly, so you must already be aware that browser vendors have deliberately limited the ability of script authors to do custom stuff when the user wants to leave the page. This is to prevent abuse.
Part of that limitation is that you don't get to find out what the user decides to do. And there will not be any clever workarounds, either. Once you tell the browser to present the beforeunload prompt, you lose all your power. If the user clicks the Okay button (i.e. decides to leave the page), the browser will refuse to run any more of your code.
Presenting the prompt creates a fork in the road that you are prevented from observing. So, put a laser tripwire there instead of a fork:
window.addEventListener("onbeforeunload", function(evt) {
navigator.sendBeacon(url, payload)
})
This is guaranteed to run when the user actually leaves the page, and only when the user actually leaves the page. But, you sacrifice the ability to try to talk the user out of leaving. You can't have it both ways.
You can't always get what you want, but if you try, sometimes you just might find you get what you need. -- The Rolling Stones
I can only think of one way to accomplish what you need, but it requires help from the server. This is not an option for most people (usually because the beacon goes to a third-party analytics provider who won't do this), but I'm including it here for completeness.
before the beforeunload handler returns, fire a beacon message that says "user is maybe leaving the page"
after firing that beacon, and still before returning, set up a document-wide mousemove handler that fires a second beacon message that says "the user is still here" (and also de-registers itself)
return false to present the prompt
modify your server so that it will reconcile these two events after some kind of delay:
if the server receives beacon 1 and then also receives beacon 2 (within some reasonably short time-frame, e.g. 5 minutes), it means the user tried to leave but then changed their mind, and so the server should delete the record of beacon 1
if the server receives beacon 1 but doesn't receive beacon 2 within the time-frame, then it means the user really did leave, and so the server would rewrite the previous beacon datapoint to say "user actually departed"; you wouldn't need to actually write beacon 2 to your datastore
(Or, depending on expected traffic and your infrastructure, maybe the server just holds the beacon 1 datapoint in RAM for the 5 minutes and commits it to your datastore only if beacon 2 never shows up. Or you could write both beacons to the database and then have a different process reconcile the beacons later. The outcome is identical, but they have different performance characteristics and resource requirements.)
P.S.: Never use "URL" (all caps) as a variable name in javascript. "URL" is actually a useful web API, so if you use that exact variable name, you're clobbering a useful ability. It's just like if you did let navigator = 'Henry'. Yes, it will execute without error, but it shadows a useful native capability.
I am developing a enterprise application using spring and struts. But, i'm getting issue on Tab close. how to force user logging out when close the browser tab or open same page on another tab.
i have been try using onbeforeunload but i am getting issue when application running on mobile browser. and i also have seen following sample but No one has clear explanation.
How to kill session when user closed the browser without logout
How to Detect Browser Window /Tab Close Event?
is there any solution to achieve this problem using javascript or from server?
Thanks
Finally I found a solution that worked!
When the user logs in I use sessionStorage to store that a user has been logged in:
sessionStorage.setItem('logged', true)
SessionStorage will hold that property until the tab or the browser closes.
So inside the application, I check if the SessionStorage still holds that property. If not then I logout the user and I redirect him to the login.
if (!sessionStorage.getItem('logged')) {
localStorage.removeItem('token')
navigator.sendBeacon('api/logout')
window.location.replace("/login") //redirect to login
}
For those who wonder what is navigator.sendBeacon you can read here to learn more. Practically I use it because I want the api/logout to be accessed even if the tab closes.
You may start a ajax request by page onload, and get a tracking session id/serial from server.
Later force all of the requests operations to include the tracking session you just gave to the page using the ajax call above.
If user opens up a new tab, the ajax load starts again. and As far as you check the live session tracks in the server with associated user, you may redirect the user to somewhere else or etc.
I don't think you may rely on onbeforeunload, just the same thing you experience.
As others stated, http is stateless, and cookies and requests are only stuffs you can check the state of the user.
At the server, session cookies could be invalidated by session timeout(if we assume user going to brew some coffee, e.g. it closes the page/tab).
And as explained in above solution, if he/she opens a new tab, the new ajax call might block him/her to perform a new login or etc.
I suggest you may find another solution instead of this approach you are trying to get. It's much easier and more senseful to sync the page state with the last opened page. Let the user opens 5 same page, and just sync them all with each other(simplest solution: refresh others when one gets updated)
You may have a javascript function and invoke it on "onunload" of body and in that script invoke backend code to invalidate the user session.
localStorage can be use to keep idle time for the application with multiple tabs are opened.
// Check browser support
if (typeof(Storage) !== "undefined") {
// Store an item to localStorage
localStorage.setItem("timeIdle", "0");
console.log(localStorage.getItem("idleTime"));
// Retrieve the added item
} else {
//display this message if browser does not support localStorage
console.log("Sorry, your browser does not support Web Storage.");
}
function func(){
$(this).keypress(function(e) {
localStorage.setItem("timeIdle", "0");
});
$(this).click(function(e) {
localStorage.setItem("timeIdle", "0");
});
timerIncrement();
}
function timerIncrement() {
var timeIdle = localStorage.getItem("timeIdle");
timeIdle = parseInt(timeIdle) + 1;
if (timeIdle >= 1) {
logoutCall();
window.location = window.location.origin+"/riskoffice-ui/Login";
}
localStorage.setItem("timeIdle", timeIdle.toString());
}
setInterval(func,1800000); //Runs the "func" function every second
This may not actually be an issue with Identity Server or the oidc-client, but I am having trouble pinning down the problem. I am running this through System.js in an Aurelia application, so it's possible the issue originates from one of these external libraries.
In CheckSessionIFrame.start(session_state), we have the following code:
this._timer = window.setInterval(() => {
this._frame.contentWindow.postMessage(this._client_id + " " + this._session_state, this._frame_origin);
}, this._interval);
The first time the interval fires, there appear to be no problems. The iFrame's contentWindow exists (as expected) and the postMessage method is called without issue. Two seconds later, when the interval fires again, this._frame.contentWindow is undefined - so my best guess is the iFrame is dying somehow. Again, this may not be an issue with oidc-client, but I'm looking for any helpful guidance on what could cause this iFrame to die (perhaps internally it could be dying on a script?) such as a missing necessary config value for oidc-client.
For oidc-client to work with silent renew, you need to have your aurelia-app on an element that is not the body, so you can place elements within the body yet outside of your aurelia-app.
This allows you to put the IFrame outside of the aurelia-app, which prevents the Aurelia bootstrapper from eating it and lets oidc-client function independently of Aurelia.
EDIT
Based on your comment, and a little memory refreshing on my part, I rephrase/clarify:
The session checker and the silent renew functions work independently of each other. You can silent renew before the session checker has started with a manual call. You can also start the session checker without doing any silent renew. They are just convenient to use together, but that's their only relationship.
I'm assuming you use the hybrid flow and have the standard session checker implementation with an RP and OP iframe, where the OP iframe is in a check_session.html page and the RP iframe is somewhere in your aurelia app. In one of my projects I have the RP iframe in the index.html, outside of the aurelia-app element so it works independently of aurelia. But I guess it doesn't necessarily have to be there.
The session checker starts when you set the src property of the RP iframe to the location of your check_session.html with the session_state, check_session_iframe and client_id after the hash.
The check_session.html page will respond to that by starting the periodic polling and post a message back to the window of your aurelia app if the state has changed.
From your aurelia app, you listen to that message and do the signinSilent() call if it indicates a changed state. And from the silent_renew.html page, you respond to that with signinSilentCallback()
All that being in place, it really doesn't matter when you start the session checker. Tuck it away in a feature somewhere and load that feature last.
The only two things you need to worry about during the startup of your application is:
Check for window.hash starting with #code and call signinRedirectCallback(code) if it does
If it does not, just call signinSilent() right away (that leaves you with the least amount of things to check)
And then after either of those have been done, do getUser() and check if it's null or if the expired property === true. If either of those is the case, do the signinRedirect(). If not, your user is authenticated and you can let the aurelia app do it's thing and start the session checker etc.
I would definitely not put the initial authentication checks on your index.html within the aurelia-app. Because if aurelia happens to finish loading before the oidc checks are done, the process will fail. You also probably want to store the user object (and UserManager) in some cache/service/other type of singleton class so you can easily interact with oidc from your aurelia application.
We're writing an app that monitors online presence. There are multiple scenarios where we need the user to have more than one browser window open. We're running into a problem where the user, after opening and running the firebase js code in a secondary browser window, will close that secondary window. This sets their presence to offline in the primary window because the onDisconnect event fires in the secondary window.
Is there a workaround for this scenario? Is this where the special /.info/connected location could be used?
The .info/connected presence data only tells a given client if they are linked up to the Firebase server, so it won't help you in this case. You could try one of these:
Reset the variable if it goes offline
If you have multiple clients monitoring the same variable (multiple windows falls into this category), it's natural to expect them to conflict about presence values. Have each one monitor the variable for changes and correct it as necessary.
var ref = firebaseRef.child(MY_ID).child('status');
ref.onDisconnect().remove();
ref.on('value', function(ss) {
if( ss.val() !== 'online' ) {
// another window went offline, so mark me still online
ss.ref().set('online');
}
});
This is fast and easy to implement, but may be annoying since my status might flicker to offline and then back online for other clients. That could, of course, be solved by putting a short delay on any change event before it triggers a UI update.
Give each connection its own path
Since the purpose of the onDisconnect is to tell you if a particular client goes offline, then we should naturally give each client its own connection:
var ref = firebaseRef.child(MY_ID).child('status').push(1);
ref.onDisconnect().remove();
With this use case, each client that connects adds a new child under status. If the path is not null, then there is at least one client connected.
It's actually fairly simple to check for online presence by just looking for a truthy value at status:
firebaseRef.child(USER_ID).child('status').on('value', function(ss) {
var isOnline = ss.val() !== null;
});
A downside of this is that you really only have a thruthy value for status (you can't set it to something like "idle" vs "online"), although this too could be worked around with just a little ingenuity.
Transactions don't work in this case
My first thought was to try a transaction, so you could increment/decrement a counter when each window is opened/closed, but there doesn't seem to be a transaction method off the onDisconnect event. So that's how I came up with the multi-path presence value.
Another simple solution (Using angularfire2, but using pure firebase is similar):
const objRef = this.af.database.list('/users/' + user_id);
const elRef = objRef.push(1);
elRef.onDisconnect().remove();
Each time a new tab is opened a new element is added to the array. The reference for "onDisconnect" is with the new element, and only it is excluded.
The user will be offline when this array is empty.
I want to implement an idle time-out for the web application that we are building. I had earlier achieved this using AsynchronousSessionAuditor from codeplex, which essentially looks for the formsauthentication and session cookie timeout by constant polling.
But it has a draw back of not respecting the client side events, it will look for only last postback to decide when to log off.
The jquery plug jquery-idle-timeout-plugin from erichynds solves this issue of client side events but suffers from another drawback that is not able to recognise user is active on some other tab.
Is there anyone already fixed the TABBED browsing issue with jquery-idle-timeout-plugin already? Or is there any better approach of application time out for web applications (by the way this web app is build using asp.net f/w)
If I understand your question right, it is not possible, since there are no events triggered in javascript for activity outside of the current window/tab.
Unless you have a addon to go along with your website for each browser, which could monitor all activity in the browser, but that is not really a practical approach.
Well, you'd have to code it by hand, which is not really hard. You can use the onfocus and onblur functions to do something like this:
$(function() {
window.isActive = true;
$(window).focus(function() { this.isActive = true; });
$(window).blur(function() { this.isActive = false; });
showIsActive();
});
function showIsActive()
{
console.log(window.isActive)
window.setTimeout("showIsActive()", 2000);
}
function doWork()
{
if (!window.isActive) { /* Check for idle time */}
}
If you make a little search you can find that varaieties of this question have already been asked and answered, you can probably find a solution you can implement with one of the plugins you mentioned.
Try:
Run setTimeout only when tab is active
or
How to tell if browser/tab is active
EDIT--> ADDED:
Or I'd try a different approach. You could create a cookie with some hash and save that hash in your DB with a timestamp that updates whenever the window is active (you could check every 5 seconds or something, it's not an intensive request)
Then, do another check before(but in the same request) to see how much time has passed since the last timestamp and log them out if necessary.
it won't log them out isntantly when time has passed, but it will when they try to access the site either by opening it again or by focusing on the tab/window.