So, I am writing a protractor test to log into an app. When a user logs in successfully a popup notification appears stating "you have successfully logged in."
Right now I am doing a browser.sleep() to wait for the popup to pop up after the login button is clicked. I don't like doing this, on slower networks Protractor is not waiting long enough and the test is failing when I try to catch the popup or alert because alert.accept() is running too soon.
Is there a way to wait the promise from the login to return to continue on with the test?
UPDATE:
I think that I have figured it out. So I'm doing this:
LoginView.loginButton.click().then(function () {
browser.wait(exc.alertIsPresent()).then(function () {
App.catchAlert();
})
});
This seems to be working, but I haven't tested it on a slower network. What do you think?
You need to wait specifically for the alert to be present. There is a specific built-in Expected Condition called alertIsPresent:
var EC = protractor.ExpectedConditions;
browser.wait(EC.alertIsPresent(), 10000);
Related
The web app I am testing gives an alert. I wanted to use cypress to test if the alert is being shown and click cancel on the alert but cypress is automatically confirming the alert.
Here is the code:
cy.get('a').contains('Concepts').click();
cy.on('window:alert', (t) => {
//assertions
expect(t).to.contains('You have unsaved changes, are you sure you want to leave?');
return false;
})
I have returned false but still, on running the test, it says confirm without the assertion being handled and it goes to the new URL (which it should not). (see image below)
What am I doing wrong?
Look like you are using window:confirm not alert.
Change it to confirm instead
cy.on('window:confirm', (t) => {
//assertions
expect(t).to.contains('You have unsaved changes, are you sure you want to leave?');
return false;
})
I know this has been asked on SO and Github already, but none of the solutions are suitable for the scenario I have because, I am looking for successful SSO login by doing keypress/button-click actions on Google Auth and Identity Auth SSO options.
In these options the final Login page URL is too long and unknown(in it's entirety) so not fully and reliably verifiable by Cypress URL match methods, so I want Cypress to just click the button and wait for the next page to load until certain core-actionable element is found/present on the new page and then not stupidly wait any further and type required credentials(username) on that long-URL login page and press enter to go to password page and fill the password and press enter and let the rest of the steps continue "naturally".
Below is the code that I have and as I mentioned in the comment in the code, after the button click, it waits for new page to fully load, even after the new page already has actionable item, and increasing the defaultCommandTimeout to 20000 also doesn't help:
describe('Test if <ProjectName> Search page is reachable', () => {
it('Visits the <ProjectName> Search page', () => {
cy.visit('/')
cy.window().then(w => w.beforeReload = true)
// Sign in with Google button, cypress timeouts after this button has been clicked,
// even if the new page has actionable item("identifier" field) ready for use
cy.get('button.<sso-class>').first().click()
cy.window().should('not.have.prop', 'beforeReload')
cy.get('input[name=identifier]').type('<GoogleUsername>').type('{enter}')
cy.get('input[name=password]', {timeout: 4000}).type('<GooglePassword>').type('{enter}')
cy.url().should('eq', '/search')
})
})
And after waiting, times out throwing the below error:
(page load)
--waiting for new page to load--
CypressError
Cypress command timeout of 20000ms exceeded.
Because this error occurred during a after all hook we are skipping all of the remaining tests.
As you can see in the code, I have already tried one fix mentioned in this GH(Github) post and the other fix is not suitable, as I said, I don't know the final Login page URL of Google/Identity server, and also it's too long with too many querystrings which makes it unreliable for Login page-load checks...
Anyone has any better suggestions ?
I'm a beginner to Cypress. I'm sure it is a simple question and I already read the documentation of Cypress, but something still seems to wrong in my Cypress test. I want to wait for an xhr request to be finished, when I click on a different language of the page I want to test.
It works, when I use wait(5000), but I think, there is a better way to wait for the xhr request to be finished than fix wait 5 secs.
This is my code:
describe('test',() => {
it('should open homepage, page "history", click on English language, click on German language',() => {
cy.server();
cy.route('POST','/ajax.php').as('request');
cy.visit('http://localhost:1234/history');
cy.wait('#request');
cy.get('div[class="cursorPointer flagSelect flag-icon-gb"]').click({force:true});
cy.route('POST','/ajax.php').as('request');
cy.wait(['#request']);
//cy.wait(5000); // <- this works, but seems to be not the best way
cy.get('h2').should(($res) => {
expect($res).to.contain('History');
})
cy.get('.dataContainer').find('.container').should('have.length', 8);
});
});
The last check
cy.get('.dataContainer').find('.container').should('have.length', 8);
is not successful, because the xhr request is not yet finished.
The xhr request is being fired, when the click on the icon is done:
cy.get('div[class="cursorPointer flagSelect flag-icon-gb"]').click({force:true});
Here an image of the xhr request, if that helps to find the error:
Are you sure that this line is correct? Otherwise the cy.wait won't function as you want.
cy.route('POST','/ajax.php').as('request');
I expect something like
cy.route('GET','/endpoint').as('request');
You can lookup what route is it via developer tools (F12 in Chrome).
Go to network to monitor what kind of XHRs load when you open your page.
Find out request URL and Method - example with bing.com
Also:
I prefer to include the cy.server() and cy.route() command in the beforeEach.
Then you only need the cy.wait() in the test itself.
See https://docs.cypress.io/guides/references/best-practices.html#2-Run-shared-code-before-each-test for more information about that.
you should do like that:
describe('test',() => { //no here async mode
it('should open homepage, page "history", click on English language, click on German language', async () => { //but here
cy.server();
cy.route('POST','/ajax.php').as('request').as('requestToWait); // as-construction
const requestToWait = await cy.wait('#requestToWait');//here we are waiting and getting response object
// any other code
});
jQuery Error
I've done some extensive searching, and have found samples of code to handle an alert box when you expect it; but I haven't been able to find anything on handling a random alert box that might, or might not appear.
The website I'm dealing with is very stubborn to begin with. Several elements without any kind of ID's, timeouts, network failures, etc.
98% of the time when I run the tests, they run without getting the alert box error and everything is good. When the alert box does popup the other 2% of the time which says "Error:jQuery not found," all my other tests fail with unexpected alert errors.
My first question is, could it be something in my code that's causing the error to happen? (see code below) My gut tells me it's probably the website. So if that's the case, could someone please show me an example that would handle a "possible" alert box and accept it, without failing my test? The swithTab() test is running first, and then the setDates() test is running next. The alert box error pops up after the switch tab, as the page is loading. I've tried using a deferred promise to handle the alert, and catch the error, but it fails before it can even catch the error. It fails as soon as it hits browser.switchTo().alert() because the alert usually doesn't exist. I really appreciate any help I could get.
this.switchTab = function(){
browser.getAllWindowHandles().then(function(handles){
browser.switchTo().window(handles[1]);
browser.sleep(2000);
var lockBoxTitle = element(by.css('td.title'));
browser.driver.wait(EC.visibilityOf(lockBoxTitle),5000);
});
}
this.setDates = function(yesterdayDate){
browser.sleep(3000);
//handleAlert();
startDateTextBox.clear();
startDateTextBox.sendKeys(yesterdayDate);
endDateTextBox.clear();
endDateTextBox.sendKeys(yesterdayDate);
retrieveBtn.click();
browser.sleep(5000);
expect(validateStart.getText()).toEqual(yesterdayDate);
expect(validateEnd.getText()).toEqual(yesterdayDate);
}
You can check if popup is displayed and if is displayed click the button for closing it:
var popUpElm = element(by.model(""));
var closeBtn= element(by.model(""));
popUpElm.isDisplayed().then(function(isDisplayed) {
if (isDisplayed) {
closeBtn.click();
console.log("Popup is closed");
} else {
console.log("Popup is not displayed");
}
});
In a page, when I click on a certain button, I get a javascript popup with 'Ok' and 'Cancel' buttons. I want to click 'Ok' button in that popup using Ruby Watir script. So I use the following code. I get the FAIL message since javascript popup comes and goes out within fraction of a second in Firefox browser. So to check whether the script catches the alert, I did print p browser.alert.present? and I get that as false. How to handle such a issue?
if (certain_button_click)
p browser.alert.present? #I get this as 'false'
browser.wait_until(30) {
browser.alert.present?
} rescue nil
p browser.alert.present? #I get this as 'false'
if browser.alert.present?
browser.alert.ok
message = 'Click Ok - PASS'
else
message = 'Click Ok - FAIL'
end
end
Please help. Thanks in advance.
Thanks,
Ashwin
Honestly I can't tell what you are trying to accomplish here, but I see what your code is doing.
Selenium raises an UnhandledAlertError whenever an alert is present, but a non-alert related action is taken with the driver. What your code is doing is rescuing that exception (well, all exceptions, which you should avoid doing when you know which exception you want to avoid). Firefox has different behavior than Chrome in this respect in that it also closes the alert when it raises the exception. So your code is doing exactly what you are telling it to do.
if you use Watir 6.0 you don't have to specify the waits any longer. If you only want to dismiss an alert when it shows up, this code will automatically wait for the alert to show up and click ok when it does. If the alert never shows up it will give a timeout error.
element_causing_alert.click
browser.alert.ok
If you are trying to test that the alert shows up, then you can use this code:
begin
browser.alert.ok
true
rescue Watir::Wait::TimeoutError
false
end
Or if you want to specify the time to wait before timing out:
begin
browser.alert.wait_until(timeout: 5, &:present?).ok
true
rescue Watir::Wait::TimeoutError
false
end
Here's an alternate idea. You can use execute_script in the browser which 'stubs out' the alert call to set a global variable. Then another execute_script which checks that the variable was set.
Example:
def stub_alert
script = <<-JS
window.alert = function(){ window.alerted = true }
JS
browser.execute_script stub_alert
end
def check_for_alert
script = <<-JS
return window.alerted == true
JS
browser.execute_script stub_alert
end
Watir uses selenium under the hood, and there's some tricky things about working with alerts. I've experienced the situation where Selenium's reference to the alert disappeared if I ran any javascript before handling the alert.