The question is as simple as that. In Cypress, how can I access a new window that opens up when running the test.
Steps to recreate :
Run the test. After some action, new window pops up (the url is dynamic in nature).
Fill in the fields in the new window, and click a few buttons.
After required actions are completed in the new Window, close the new window and move back to the main window.
Continue execution with the main window.
Point of interest: the focus should be
main window -> new window -> main window
I have read few things that relate to use of iframe and confirmation box, but here its none of those. Relates to accessing a whole new window. Something like Window Handlers in Selenium. Unfortunately could not find anything related to it.
Accessing new windows via Cypress is intentionally not supported.
However, there are many ways this functionality can be tested in Cypress now. You can split up your tests into separate pieces and still have confidence that your application is covered.
Write a test to check that when performing the action in your app, the window.open event is called by using cy.spy() to listen for a window.open event.
cy.visit('http://localhost:3000', {
onBeforeLoad(win) {
cy.stub(win, 'open')
}
})
// Do the action in your app like cy.get('.open-window-btn').click()
cy.window().its('open').should('be.called')
In a new test, use cy.visit() to go to the url that would have opened in the new window, fill in the fields and click the buttons like you would in a Cypress test.
cy.visit('http://localhost:3000/new-window')
// Do the actions you want to test in the new window
Fully working test example can be found here.
I am not cypress expert, just started using it few days ago, but I figured out this kind solution for stateful application with dynamic link:
// Get window object
cy.window().then((win) => {
// Replace window.open(url, target)-function with our own arrow function
cy.stub(win, 'open', url =>
{
// change window location to be same as the popup url
win.location.href = Cypress.config().baseUrl + url;
}).as("popup") // alias it with popup, so we can wait refer it with #popup
})
// Click button which triggers javascript's window.open() call
cy.get("#buttonWhichOpensPopupWithDynamicUrl").click()
// Make sure that it triggered window.open function call
cy.get("#popup").should("be.called")
// Now we can continue integration testing for the new "popup tab" inside the same tab
Is there any better way to do this?
// We can remove the offending attribute - target='_blank'
// that would normally open content in a new tab.
cy.get('#users').invoke('removeAttr', 'target').click()
// after clicking the <a> we are now navigated to the
// new page and we can assert that the url is correct
cy.url().should('include', 'users.html')
Cypress - tab handling anchor links
I was able to achieve the same requirement via the following:
let newUrl = '';
cy.window().then((win) => {
cy.stub(win, 'open').as('windowOpen').callsFake(url => {
newUrl = url;
});
})
cy.get('.open-window-btn').click()
cy.get('#windowOpen').should('be.called');
cy.visit(newUrl)
Here's a solution i'm using on my project based on "Cypress using child window"
Cypress Window Helpers (aka. Cypress Tab Helpers)
They're really popup-windows or child-windows, but i call them tabs for api brevity
cy.openTab(url, opts)
cy.tabVisit(url, window_name)
cy.switchToTab(tab_name)
cy.closeTab(index_or_name) - pass nothing to close active tab
cy.closeAllTabs() - except main root window
I was recently faced with this issue as well - url for the new tab is dynamic, so I don't know what it is. After much searching, some trial and error, and input from co-workers, resolved by doing the following:
// AFTER cy.visit()
cy.window().then((win) => {
cy.spy(win, 'open').as('windowOpen'); // 'spy' vs 'stub' lets the new tab still open if you are visually watching it
});
// perform action here [for me it was a button being clicked that eventually ended in a window.open]
// verify the window opened
// verify the first parameter is a string (this is the dynamic url) and the second is _blank (opens a new window)
cy.get('#windowOpen').should('be.calledWith', Cypress.sinon.match.string, '_blank');
this is how you can handle tabs in same window..
use this code snippet
cy.xpath("//a[#href='http://www.selenium.dev']").invoke('removeAttr','target').click();
Related
I'm automating tests using protractor.
In the web app that I'm automating, there's a button that, when pressed, refreshes the page.
Is there a way to wait for a refresh to happen & complete using protractor ?
Could you use for tests GET variables?
So basically on first load you click the button and it appends "?is_reloaded=true".
Maybe this could help.
But I badly have no experience in protractor.
Are you testing the functionality of the refresh button? If not then just check for changes in the page that you have refreshed, if yes try injecting a flag in the page (window) by using browser.executeScript()
For example:
let getCurrentWindowHash = () => {
return browser.executeScript("return window.location.hash");
};
await browser.executeScript("window.location.hash = 'Not yet refreshed'");
// Confirm that the has has been set
console.log(`current window hash = ${getCurrentWindowHash()}`); // Should log '#Not yet refreshed'
// click the refresh button
// On refresh, window properties would get reset.
// If the window location hash is empty, then your page was refreshed.
console.log(`current window hash = ${getCurrentWindowHash()}`); // Should be empty
Suppose, i have two chrome instance opened, instance1 and instance2, and my application is running in instance1, and when user do any click event on instance1, Can i open new url in instance 2 using javascript?
If the second instance is not opened from your application, then it have no authority to access that instance.
I would suggest using window.open from your application, then you can pass an additional parameter as per your requirements. This way you will have control over the other instance you have created.
Reference: https://www.w3schools.com/jsref/met_win_open.asp
Updated
Regarding your comment:
You can add multiple event listeners for you instance window to detect changes on them like (load, close, resize etc).
componentDidMount() {
let strWindowFeatures = 'location=yes'
let URL = 'http://localhost:3001'
let win3Instace = window.open(URL, '_blank', strWindowFeatures)
win3Instace.addEventListener('load', (event) => {
console.log('Instance 3 is loaded, add callback funtion here')
})
}
I need to simply open to browser windows when user perform an action (to keep it simple in this example I use setTimeout).
I have notice that the browser is able to open only the first window.open and not the remaining.
What is the cause? How to fix it?
setTimeout(function() {
window.open("https://www.w3schools.com");
window.open("https://www.google.com");
}, 3000);
You need to make the windows unique, by default, the browser gives the new window a name, but doesn't dynamically update it when multiple instances of window.open occur (source - first line of https://developer.mozilla.org/en-US/docs/Web/API/Window/open). Give them unique names (with ids help) like so:
window.open('/path/to/page.php', 'UNIQUE_WINDOW1', 'width=300,height=400');
window.open('/path/to/page2.php', 'MORE_UNIQUE_WINDOW5', 'width=300,height=400');
if this doesn't work you can do:
window.open('/path/to/page.php');
$.post('/path/to/page2.php', {}, function(res)
{
var win = window.open('', 'WINDOW_NAME', 'width=540,height=440');
with(win)
{
open();
write(res);
close();
}
});
what this will do is, post nothing to the page but, res returns the output of that file, so you assign window.open to a variable, and with it, open it and write the output to the file. :)
While building my Chrome extension, it's often very useful to open a new browser tab and paste this into it:
chrome-extension://xyzfegpcoexyzlibqrpmoeoodfiocgcn/popup.html
When I do that I'm able to work on my popup UI without it ever closing, and without having to click the extension icon at the top right and have the popup sometimes close on me.
Here's the problem: I need my js (referenced by popup.html) to know whether i'm in this debug tab, or whether it's running in "regular mode" (clicking the extension icon and running it normally). I first tried this:
var isDebugExtensionTab = (location.href.indexOf("chrome-extension:") == 0);
That doesn't work because it always evaluates to true -- that is the location.href in all cases, debug tab or regular mode.
How can I detect the difference?
Use chrome.tabs.getCurrent:
Gets the tab that this script call is being made from. May be undefined if called from a non-tab context (for example: a background page or popup view).
var isDebugExtensionTab = false;
chrome.tabs.getCurrent(function(tab) { isDebugExtensionTab = !!tab; });
It's asynchronous as all chrome.* API methods that may accept a callback so the result won't be available until the current context exits. If you need to use the value immediately, do it in the callback:
var isDebugExtensionTab = false;
chrome.tabs.getCurrent(function(tab) {
isDebugExtensionTab = !!tab;
runSomeDebugFunction();
});
I am working on a web based application, in which I have to open popup window. I am using window.open() method to open the popup, like this:
window.open(url, "popupWin");
where url contains the URL I would like my popup window to navigate to. Now, the problem is, if I execute window.open() from multiple tabs (with same or different URLs), at least on Chrome, it might / might not give you the same window which was opened earlier. This behaviour is inconsistent, I mean, either it should get me fresh window every time, or it should get me previously opened window every time.
I need to persist the same popup window for entire domain. How can I do that?
Well looks like there is a direction to go or at least to give it a try.
It fully remains on localStorage which gives you ability to share the knowledge across your tabs within a single domain.
The code I give below does not work yet (it is only a direction), so don't expect too much from running it as it is.
What it does: it saves the popups by the url in a localStorage and when you try to open a new one with the same url it won't do that. If you don't want to distinguish them by URL it is even simpler: store boolean in a localStorage instead of an object.
What it does not do but should:
it should listen to the popup onunload (close) event and reset the localStorage information accordingly. Best for you here is just to set your localStorage boolean value to false
it should listen to the current tab onunload (reload, close) event and also reset something according to Your logic. As I understand the best for you would be just check whether this tab is the last one from your domain (you can also do this using localStorage, e.g. on every new tab adding its identifier, e.g. creation timestamp and destroying it on tab close) and if it is set your localStorage boolean value to false.
This, I think, would be enough to solve the problem. And finally a small piece of code:
// get the localstorage url map
function getOpenPopups() {
var obj = localStorage.getItem('mypopups');
return obj ? JSON.parse(obj) : {};
}
// set the localstorage url map
function setOpenPopups(object) {
localStorage.setItem('mypopups', JSON.stringify(object))
}
// open the popup
function popup(url, title) {
var popups = getOpenPopups();
// check whether popup with this url is already open
// if not then set it and open the popup
if (!popups[url]) {
popups[url] = true;
setOpenPopups(popups);
return window.open('abc', 'cde');
}
else {
return false;
}
}
jsFiddle
From w3c documentation we can see that window.open() returns a reference to the newly created window, or null if the call failed. That means we can keep it in memory and check for closed flag of that window.
var newWindow = window.open('/some/path', 'TestWindow');
// ...
if (!newWindow.closed) {
}
Keep in mind that if window with following name exists, page will be loaded in the same window without opening new one.
Other variants of name parameter like _blank, _self, _top, _parent you can find in official docs too.