Why must I use browser.sleep while writing protractor tests - javascript

My first run at E2E tests. I'm trying to digest someone else's protractor tests.
Problem: There are a lot of browser.driver.sleep and this seems fragile.
Goal: not to use browser.driver.sleep
Question: What is a better approach to browser.driver.sleep? Something less fragile like a promise or something I dont know about lol?
var config = require('../../protractor.conf.js').config;
describe('this Homepage Body Tests', function(){
browser.driver.get(config.homepageUrl);
it("should open find a clinic page", function(){
// page loads :: want to fix this random wait interval
browser.driver.sleep(2000);
browser.ignoreSynchronization = true;
var string = 'clinic';
var main = '.search-large-text';
var link = element(by.cssContainingText('.submenu li a', string));
link.click().then(function() {
// page reloads :: want to fix this random wait interval
browser.driver.sleep(3000);
var title = element(by.cssContainingText(main, string));
expect(title.getText()).toBe(string);
});
});
});

Since there is an ignoreSynchronization turned on, you cannot use waitForAngular(), which would be a solution in case of an angular-site testing.
A better solution here would be to set a page load timeout:
browser.manage().timeouts().pageLoadTimeout(10000); // 10 seconds
See also these relevant threads on explicit waits and timeouts:
Use protractor to test login on non-AngularJS page (Leo's answer is very detailed)
Protractor : How to wait for page complete after click a button?
Timeouts

Related

Cypress conditional quit runner/test

So I built out a command that I'm using in multiple tests that looks at the page and if a prompt is there then the page is redirected to another page to handle the prompt (in this case approving a schedule). Then it additionally looks at that new page and if the page has some text instead then it redirects to the home page (where the issue lies) OR it clicks the button to approve and redirects to the home page and continues normally through the test.
Cypress.Commands.add('approval_redirect', () => {
cy.get('body').then(($body) => {
if ($body.text().includes(
'You must approve your weekly schedule before starting!'
)) {
cy.get('.container a')
.first()
.click()
cy.get('main').then(($main) => {
if ($main.text().includes('schedule')) {
cy.get('button')
.click()
cy.pause()
} else {
cy.get('ul > button')
.click()
}
})
}
})
})
Right now if it's going to the new page to verify the schedule and does NOT have a button to click it's returning home and then pausing. I put in the pause because it would then continue the test with massive failures.
So for example in one test I have:
it('starts here', function (){
cy.login()
.wait(1000)
cy.approval_redirect()
cy.get('#Route')
.click()
.wait(1000)
})
So in this if it redirects home after not clicking the button I'd like for it to completely stop the test. There's nothing to actually do.
Is there a way to completely stop the runner? How do I put that in my test to check against the command for failure?
I thought I could just wrap a function around the command with something like:
function findFailure(failure,success){cy.get...}
Then instead of cy.pause() I put
failure();
return;
And under the ul > button I put
success();
return;
Then in my test I did:
it('starts here', function (){
cy.login()
.wait(1000)
cy.approval_redirect()
const failure = function(){
this.skip()
}
const success = function(){
cy.get('#Route')
.click()
.wait(1000)
}
})
There are no errors and the test runs but it doesn't actually go through the command now. So how do I conditionally stop the cypress test?
This one is interesting. I commend your resourcefulness, especially since Cypress has strong feelings when it comes to conditional testing due to its asynchronous nature.
I had a similar problem recently. For me, nothing on a page was clickable at a certain point if there was network activity, and a toast would appear saying "Loading...", and nothing could be done until network activity was done and the toast disappeared. Basically, I needed the page to wait as long as it needed for the toast to disappear before continuing (thus conditional testing).
What I did was something like this:
In commands.js:
Cypress.Commands.add('waitForToast', () => {
cy.get('toast-loading', {timeout: 20000}).should('not.exist');
});
Then in the actual test.spec.js, using the command:
cy.waitForToast();
What's happening here is Cypress is pausing the test and looking for the toast, and waiting with a timeout of 20000 milliseconds. If the toast gets stuck, the test will exit/fail. If the toast disappears, the test will continue.
Using asserts so far has been the most effective way to do conditional testing. If you're waiting for a button to appear, use an assert with a longer timeout to wait for it. Then, if it doesn't show up, the test will exit and fail.
In your case, you can assert if you didn't go home.
Alternatively
If you want the test to just end without asserting and failing, you can try something like this:
cy.get('home-page-item`)
.its('length')
.then(numberOfItems => {
if (numberOfItems > 0) {
DO-NOTHING-ASSERT
} else {
CONTINUE-TEST
}
});
Otherwise I don't think Cypress has anything like cy.abortTest(), so you'll have to work around their anti-conditional testing methodology with asserts or if else.
After a bit I noticed an issue with my conditional as it was looking for if a prompt was there then go to the schedule; then if certain text wasn't there then go home (where the pause/desire to abort lived) OR click on the button, go home and finish. I noticed that I didn't put in another else on the first condition.
My solution was to just create a function that contained all the steps for a complete test barring no redirects/redirect that had a button click. Then I put in the failures a console log about what happened to end the test. Something like:
cy.log('Schedule not approved, redirected, no published schedule. Test ended.')
This gives a clean end/break to the test. Albeit wrapping a success/failure would be wonderful.

How Can I wait to an element to appears using protractor and my file conf?

I'm trying to using protractor to wait to my input 'Username' to appears and then insert value on it. How Can I do it?
browser.get('http://localhost:5555/#');
var login = browser.driver.findElement(by.id('Username'));
Use Expected Conditions to wait for certain conditions, i.e. for an element to be present or visible. Use sendKeys to fill an input.
var login = element(by.id('Username'));
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf(login)).then(function() {
login.sendKeys('myuser');
});
This belongs in your spec, not your config.
If your's is an Angular App and if you are doing everything right , Then You need not wait for the Element or Page to Load.
Protractor does it for you. Refer API doc for WaitForAngular
Also check this you are interested. Wrote more info on my blog Protractor Over Selenium
I would assume couple of things
using shortcut version of element(by.id())
setting variables to let, instead var
provide timeout for wait, or it will be endless, and will fail only on test timeout (wasting time for wait)
provide wait failed error message (3rd parameter in wait function) - better readability on failures
No need to put sendKeys() to callback, protractor controlFlow will execute commands in correct order even without this.
Here is code example:
let loginField = $('#Username');
let EC = protractor.ExpectedConditions;
browser.wait(EC.visibilityOf(loginField), 3000, 'Login field should be visible before entering text');
loginField.sendKeys('myuser');

Protractor Test cases

I am new to protractor e2e testing. and i have written my first test code.. I would like to know your feedback and ways i can improve it.
describe("Map feedback Automation",function(){
it("Check if the Url works ",function()
{
browser.get(browser.params.url);
expect(browser.getCurrentUrl()).toContain("report");
});it("test browser should reach report road option",function()
{
element.all(by.css('div[ng-click="setLocation(\'report_road\')"]')).click();
expect(browser.getCurrentUrl()).toContain("report_road");
});
it("test browser should reach report road missing",function()
{
element.all(by.css('div[ ng-click="mapFeedBack.editObject= mapFeedBack.createMapObjectModel();setLocation(mapFeedBack.noMap?\'road_new\':\'choose_location_road_new/road_new\')"]')).click();
expect(browser.getCurrentUrl()).toContain("choose_location_road_new/road_new");
browser.sleep(browser.params.sleeptime);
});
it("test browser should zoom on map ",function() //manual
{
element.all(by.css('div[ng-click="zoomIn()"]')).click();
browser.sleep(browser.params.sleeptime);
element.all(by.css('div[ng-click="zoomIn()"]')).click();
browser.sleep(browser.params.sleeptime);
element.all(by.css('div[ng-click="zoomIn()"]')).click();
browser.sleep(browser.params.sleeptime);
element.all(by.css('div[ng-click="zoomIn()"]')).click();
browser.sleep(browser.params.sleeptime);
});
it("Should click on ok option",function()
{
element(by.buttonText('OK')).click();
expect(browser.getCurrentUrl()).toContain("road_new");
});
it("test browser should reach report road option",function()
{
browser.sleep(browser.params.sleeptime);
expect(browser.getCurrentUrl()).toContain("road_new");
});
it("should enter a road name",function()
{
browser.sleep(browser.params.sleeptime);
var testroadname = browser.params.testroadname;
element(by.model("mapFeedBack.editObject.roadName")).sendKeys(testroadname);
browser.sleep(browser.params.sleeptime);
});
it("should check the type of road is highway",function() //spec3
{
element(by.model("mapFeedBack.editObject[attrs.select].selected")).$("[value='string:app.road.roadType.highway']").click();
});
it("should submmit the map feedback",function()
{
element(by.css('button[ng-click="onSubmit({reportType: reportType})"]')).click();
browser.sleep(browser.params.sleeptime);
});});
A colleague of mine told me to remove the delay
browser.sleep(browser.params.sleeptime);
and add some event when the zoom in button is clicked. can you suggest me the ways i can achieve it?
As they say, every code has it's own smell. One the worst smells produced by Protractor-specific code is the use of browser.sleep() to tackle timing issues. browser.sleep() calls usually are making the tests much slower than it is needed and occasionally don't add enough of a delay to make a test pass making the author of the code increase the sleep delay which again makes a test even more slower. And, by the way, there is a related third-party ESLint rule that would help to prevent you from having browser.sleep() in the e2e codebase.
A more robust alternative to having a hardcode delay, is to use browser.wait() and a set of built-in Expected Conditions. The main advantage here is that browser.wait() would wait as long as it is necessary continuously checking the state of the expected condition.
For example, in your case, you might make the world a better place to do test automation by using elementToBeClickable condition:
var EC = protractor.ExpectedConditions;
var elm = element(by.id("myid"));
browser.wait(EC.elementToBeClickable(elm), 10000);
elm.click();
Here, Protractor would wait up to (up to is really what makes the difference) 10 seconds (yes, you still need a timeout value) and would raise a Timeout Exception if the element would not become clickable.
You can also wait for the button to be visible using the following wait command:
var btn = element(by.css("mycss"));
browser.driver.wait(protractor.until.elementIsVisible(btn), 5000);
btn.click();
Visibility means that the element is not only displayed but also has a height and width that is greater than 0.

Trying to drag an element to an element that is inside of an iFrame (using Webdriver-io)?

I am currently using Webdriver IO, Chimp JS and Cucumber JS, and I'm having a bit of a hard time dragging an element to another element that's inside of an iframe. I've been able to locate the element I want to move, as well as the element in the iframe after using client.frame(0); however I haven't found a way to click on the element, switch to the iframe to locate the element I want to move to, then move the element.
To make it easier, here's a picture. I want to move element 1 to element 2. But element 2 is in an iframe:
Looking through the docs, I see a lot of potentially helpful actions like hold, release ext. But I am working on desktop, and so I can't use any of the mobile actions.
With this limitation, it looks like the only drag and drop function available to me is dragAndDrop, but there doesn't seem to be a way to drag and drop an object into an element in an iframe in the javascript version of Webdriver. Am I correct in thinking this? Is there someway to do this solely using Cucumber JS? I feel like I'm missing something huge here, but I can't seem to figure it out :\
The selenium standalone driver I used is selenium-server-standalone-2.50.0.jar(selenium-release.storage.googleapis.com/index.html?path=2.50/) and chrome driver I used is ChromeDriver 2.29 (https://sites.google.com/a/chromium.org/chromedriver/downloads)
var webdriverio = require('webdriverio'),
dragAndDrop = require('html-dnd').codeForSelectors,
should = require('should');
// a test script block or suite
describe('Title Test for Web Driver IO - Tutorial Test Page Website', function() {
// set timeout to 10 seconds
this.timeout(10000);
var driver = {};
// hook to run before tests
before( function () {
// load the driver for browser
driver = webdriverio.remote({ desiredCapabilities: {browserName: 'chrome'} });
return driver.init();
});
// a test spec - "specification"
it('should be load correct page and title', function () {
var sectionId = "";
// load page, then call function()
return driver
.url('http://localhost:9000') //your url
.pause(7000)
.moveToObject('#element1')
.buttonDown()
.moveToObject('#element2')
.buttonUp()
.pause(2000)
.end()
});
// a "hook" to run after all tests in this block
after(function() {
return driver.end();
});
});

Protractor/Jasmine test browser.isElementPresent not working when looking for a class

So i'm trying to run some tests with jasmine but i'm fairly new to the whole thing. I've spent way more time than i'm willing to admit trying to work out why my my webdriver is closing the browser before it has a chance to check the '.detailsColumn' element for expected results. After a while I've worked out that i can use browser.wait to make the browser stay alive long enough for the element to be ready.
My latest version of the test is below. The error I get is an invalidSelectorError and no info about which line the error was thrown on. I'd hazard a guess that the invalidSelectorError points to either my declaration or use of the detailsColumn variable though.
Can anyone see why this wouldn't work? I'm at a loss.
I'm using protractor/jasmine to do my tests, and using selenium for my web driver.
it('Should display result summary correctly when searching for multiple articles only', function () {
var TrackID= ExpectedArticle1Details.TrackingID + ', ' + ExpectedArticle2Details.TrackingID;
landingPage.get();
landingPage.setTrackID(TrackID)
landingPage.clickTrackButton();
expect(resultPage.allElementsofLandingPageAreVisible()).toEqual(true);
expect(resultPage.allHeadersofResultsPageAreVisible()).toEqual(true);
browser.wait(function () {
var detailsColumn = protractor.By.css('.detailsColumn.status:eq(0)');
return browser.isElementPresent(detailsColumn).then(function (result) {
expect(result).toBe(true);
return result;
console.log(result);
});
}, 10000);
JQuery index-related selectors like eq() are not supported by selenium webdriver.
You probably want to use nth-child instead:
.detailsColumn.status:nth-child(1)
Or, you may even replace it with element.all() plus first():
element.all(by.css(".detailsColumn.status")).first();
Additionally, if you have to use browser.wait(), I think you can replace the whole browser.wait() block you currently have with:
var EC = protractor.ExpectedConditions;
var detailsColumn = element(by.css('.detailsColumn.status:nth-child(1)'));
browser.wait(EC.presenceOf(detailsColumn), 10000);

Categories

Resources