Cypress: Stub open window - javascript

in my app there is an recommendations list, which on click opens a new window with a dynamic address:
$window.open(_shopURL, '_blank');
Now I'm trying to stub the windows.open event as shown in https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/stubbing-spying__window/cypress/integration/window-stubbing.spec.js
Cypress.on('window:before:load', (win) => {
win.open = cy.stub().as('windowOpen')
})
describe('Shop integration', () => {
beforeEach(function () {
cy.visitHome(countryCode, resellerId)
})
it('can stub the window open event', function () {
cy.get(`.recommendations-list .recommendations-cover:nth-of-type(1)`)
.click()
cy.get('#windowOpen').should('be.calledWith', 'page1.html')
})
But it's always opening the new tab and the logs are wrong:
Cypress: stub open window
Does anybody has an idea why it's not working?
Cheers!

Code below will help you to stub window.open and further assert it that function has been triggered:
it('opens the about page', () => {
cy.visit('/')
cy.window().then(win => {
cy.stub(win, 'open').as('Open')
})
cy.get('.your-selector').click()
cy.get('#Open').should('have.been.calledOnceWithExactly', yourUrl)
})
You also can stub window.open in cy.on hook as you did, what helps you to yield new window object each time after page reload. However, if you want to actually open the new Url in existing tab instead of new one you can use this code below by passing "_self" param to overwrite old "_blank":
cy.window().then(win => {
cy.stub(win, 'open').callsFake((url) => {
return win.open.wrappedMethod.call(win, url, '_self');
}).as('Open');
});
callsFake function dynamically withdraws url which has been placed into original window.open(url, "_blank"), or you can manually change url inside .call(win, url, '_self'); with static one, so regardless on which link or button you clicked, which triggers window.open, they all will open the same url.

I'm using page-objects for every page I want to test. So in my parent page-object which gets inherited by every other PO I do the following when opening a url:
public navigateTo(url: string, defaultTimeout: number = 5000) {
return cy.visit(url, {
onBeforeLoad: (win: any) => {
cy.stub(win, 'open');
},
timeout: defaultTimeOut
});
}
This prevents window to open a new page.

You also can use this easy way:
const newUrl = 'your url';
cy.window().then((win) => {
cy.stub(win, 'open').callsFake(url => {
newUrl = url
}).as('windowOpen')
})
cy.get('your path').click()
cy.get('#windowOpen').should('be.called')
cy.visit(newUrl)

Related

Cypress: How do I overwrite the visit command to always attempt to dismiss a popup after the page is loaded?

Cypress overwrite: I would like to overwrite the existing visit command so that it still operates as is, but will attempt to dismiss a popup after the visit has successfully executed.
The popup is something we have very little control over and it appears after you login. Seeing as we're bypassing the login screen and logging in programmatically, we'll see the popup when we navigate to any page. The insufficient code I currently have:
Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
originalFn(url, options);
cy.get("body").then($body => {
if ($body.find("[text='Got it']").length > 0) {
cy.contains("Got it", { matchCase: false }).click();
}
});
});
Thanks
You can do this by overwriting cy.visit() command. Try this:
Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
originalFn(url, options);
// make sure to add a return here!
return cy.get('body').then($body => {
if ($body.find("[text='Got it']").length > 0) {
cy.contains('Got it', { matchCase: false }).click();
}
});
});
source: https://docs.cypress.io/api/cypress-api/custom-commands#Overwrite-visit-command

How to catch the load event for a window, after location.replace (url change)

So I opened a window with window.open('https://example.com/foo'). I am on the same domain and need to listen for the load event.
The load event triggers for the first window.open but I can't get another event when i change the url with window.replace('https://example.com/bar')
CodeSandbox
Open the browser console
Click the open button (observe the log in the console)
Click the switch button (observe that nothing gets logged)
Full example
let open = null;
document.getElementById('open-button').addEventListener('click', () => {
open = window.open('https://523k29z484.codesandbox.io/pages/foo.html');
open.addEventListener('load', () => console.log('this fires'));
});
document.getElementById('switch-button').addEventListener('click', () => {
open.addEventListener('load', () => console.log('before change does not fire'));
open.location.replace('https://523k29z484.codesandbox.io/pages/bar.html');
open.addEventListener('load', () => console.log('after change does not fire'));
});
I solved this by carefully chaining the following code.
open.location.replace('https://211vv50z30.codesandbox.io/pages/bar.html');
// old page unloaded
open.addEventListener('unload', () => {
// we can only add the load event after one tick
setTimeout(() => {
open.addEventListener('load', () => console.log('gotcha'));
}, 0);
});
location.replace -> unload -> setTimeout (one tick) -> load
CodeSandbox

Angular 2+ detect closing window

i have troubles detecting a closing window after the build is done.
const newWindow = window.open(url, '_blank', options);
newWindow.onbeforeunload = () => null;
newWindow.addEventListener('beforeunload', (evt: BeforeUnloadEvent) =>
{
console.log(evt)
}
);
it works great until i do the build, there the beforeunload event does not get triggered. i also tried placing a host listener in the new window's component:
#HostListener('window:beforeunload', [ '$event' ])
beforeUnloadHander(event: BeforeUnloadEvent): void {
debugger;
}
but the same problem here. after the build is done, we don't arrive at the debugger anymore
anybody any idea what i am doing wrong? thanks for your help!
Edit Workaround
const heartBeatNewWindow = setInterval(() => {
if (newWindow.closed) {
this.canvasSettings.displayInNewWindow = false;
clearTimeout(heartBeatNewWindow);
}
}, 1500);
I had to do something similar and my approach was the following:
I created a generic catch from close event windows in the constructor of my service, them call method what handle this event. Inside this method I validate the origin of this event is the correct to execute the logic I needed. Look this example:
Inside the constructor:
if(window.addEventListener){
window.addEventListener("message", this.authService.handleMessage.bind(this), false);
}else{
(<any>window).attachEvent('onmessage', this.authService.handleMessage.bind(this));
}
And my method to handle that event:
handleMessage(event: Event) {
event.preventDefault();
const message = event as MessageEvent;
// Only trust messages from the below origin.
//
if ((message.origin !== environment.BASE_URL)) return;
const result = JSON.parse(message.data);
//Add your logic here
I Hope be helpfull.

How to open new browser window in intern js?

I am automating the UI test of my application. There are some cases when i want my test script to close the current browser and and run next test by opening new browser. The problem is that I am unable to figure out how to open new browser window in intern. remote.get(URL) doesn't do what i want to do here. Can someone please help.
I have updated my question to include code as well. My question is pretty much straight forward though. How to open new browser window using intern from inside test ?.
though if you want to see the code please comment, I will write it down .
Thanks.
// in tests/functional/index.js
define([
'intern!object',
'intern/chai!assert',
'Automation/ConfigFiles/dataurl',
'Automation/pages/login/loginpage',
'intern/dojo/node!fs',
'intern/dojo/node!leadfoot/helpers/pollUntil'
], function (registerSuite, assert, dataurl, LoginPage, fs, pollUntil) {
registerSuite(function () {
var loginPage;
var values;
return {
setup: function () {
var data = fs.readFileSync(loginpage, 'utf8');
json = JSON.parse(data);
values = json.values;
loginPage = new LoginPage(this.remote, json.locator);
return this.remote
.get(require.toUrl(json.locator.URL)).setFindTimeout(60000000000).sleep(5000)
},
beforeEach:function() {
// here i want to open new window
},
'valid loginname lands to password page':function () {
loginPage.submitLoginName(values.unamevalue);
loginPage.isPasswordPageDisplayed().then(function(isPasswordPageDisplayed) {
assert.true(isPasswordPageDisplayed, 'password page is not displayed, Invalid Login name');
})
},
'successful login': function () {
loginPage
.login(values.unamevalue, values.pwdvalue)
loginPage.isLoginSuccess().then(function (loginSuccess) {
assert.isTrue(loginSuccess, 'Login Failed');
});
},
afterEach: function () {
return this.remote.closeCurrentWindow()
}
};
});
});
You can open a new window with window.open. The trick is that you want to run that command in the remote browser. Intern (technically Leadfoot) gives you two methods for doing that on this.remote: execute and executeAsync. For example, to simply open a new window, you could do:
this.remote.execute(function () {
window.open();
})
Once you've opened a new window, you need to switch to it to interact with it. A script might look something like:
var windowHandles;
this.remote
.getAllWindowHandles()
.then(function (handles) {
windowHandles = handles;
})
.execute(function () { window.open() })
.sleep(500)
.getAllWindowHandles()
.then(function (handles) {
// compare the new handles to windowHandles to figure out which
// is the new window, then switch to it
return this.remote.switchToWindow(newWindowHandle);
})
.get('some_new_url')
// rest of test

Avoid popup blocker when opening new window that requires an async lookup

I've got a button in my Meteor application that does the following:
user clicks button > event calls method > method calls external api using http > external api returns single sign on link > method returns link > event opens new window (tab) with link as url
My problem is that the new tab is being blocked by a popup blocker even though it is based on user action
Here's the event code:
Template.welcome.events({
'click #accessLms': function(e) {
e.preventDefault();
​
var submitButton = $('#accessLms').button('loading');
​
Meteor.call('getLmsLink', function(error, portalLink) {
if(error) {
sAlert.error(error.message);
submitButton.button('reset');
} else if(portalLink) {
window.open(
portalLink,
'_blank'
);
submitButton.button('reset');
}
});
}
});
Here is the method:
Meteor.methods({
'getLmsLink': function () {
[set vars...]
try {
var response = HTTP.call( verb, wceaApiAddress + endPoint, {
headers: {
"Request-Time": timeStamp,
"Api-Key": key,
"Signature": hash
}
});
} catch(error) {
throw new Meteor.Error(501, 'There was a problem getting a link to the E-Learning Portal');
}
​
var result = JSON.parse(response.content);
var portalLink = result.records.accessLink;
return portalLink;
}
});
Basic approach:
On the click event in your app open a new window with a specific url to your own app
Include a route parameter that can be used in the new window, for example /redirect/token/
In the Template.onCreated event of the template used in that route, perform the method call and get the url and auth token to the 3rd party site.
Finally just set location = newSiteHref in that same code (in the new window) and redirect the user

Categories