I am currently testing a non Angular js website with protractor. My code is as follows :
describe("Login ", function () {
it("test", co.wrap(function* () {
browser.ignoreSynchronization = true;
yield browser.get("URL");
var elmOK = element(by.css('a[href="#partner"]'))
yield elmOK.click();
expect(browser.getCurrentUrl()).toContain('login');
}));
});
Test Scenario : My test opens the URL mentioned and selects the link with href=#partner. A login page which should pop up. But when I run the test and the link is clicked the login page doesn't popp up.
Please tell me what I am doing wrong?
Protractor appears synchronous because it waits for angular to be stable prior to moving to the next action. So if the above was an angular page, it would:
load the page, then Protractor would wait for angular (wait for the page to be stable)
tell selenium webdriver to find the element and click on it.
In a non-angular page, setting the ignore synchronization set to true in the right direction. What you need to do is to come up with your own sleep between getting the page and clicking the element. After you click on the element, you'll also need to wait for the next window to pop up, change focus to the next window to see if the url contains login.
browser.ignoreSynchronization = true;
browser.get("URL");
// let the page load
browser.sleep(2000);
element(by.css('a[href="#partner"]')).click().then(() => {
// may have to add sleep in for the page to load
browser.sleep(2000);
// switch to the next window for the popup
browser.driver.getAllWindowHandles().then((windowHandles) => {
let popupLoginHandle = windowHandles[1];
browser.driver.switchTo().window(popupLoginHandle).then(() => {
// check to see if the pop up has a url that contains 'login'
expect(browser.getCurrentUrl()).toContain('login');
});
});
});
Putting Hard coded waits is not an good practice.
If you are using jasmine for reporting , you may put the following code inside conf.js:
jasmineNodeOpts: {
defaultTimeoutInterval: 600000,
},
Related
According to Testcafe's documentation, I should be able to inject a clientScript into all pages: https://testcafe.io/documentation/402843/guides/advanced-guides/inject-client-scripts#add-client-scripts-to-all-tests
I currently have it set up to inject this script so that it can dismiss notifications that pop up in our application which overlay buttons that we need to interact with.
const notifications_div = document.querySelector('.notifications')
if (notifications_div) {
// Creates the MutationObserver and triggers the callback
const mutationObserver = new MutationObserver(() => {
// Checks to see if the style is set to 'block'
if (notifications_div.style.display == 'block') {
// Set the dismiss button to a variable each time since the previous will no longer exist.
let dismiss_button = document.querySelector("a[data-turbo-method='delete']")
// Click the dismiss button; Timeout is needed to avoid race condition errors.
setTimeout(() => { dismiss_button.click(); }, 3000);
// Hide the notifications_div again since it never truly goes away; Timeout is needed to avoid race condition errors.
setTimeout(() => { notifications_div.style.display = 'none'; }, 3000);
}
})
// Starts the observation of the notifications_div and checks for a change on 'style'
mutationObserver.observe(notifications_div, {
attributes: true,
attributeOldValue: true,
attributeFilter: ['style']
})
}
When I run this code in the console and then trigger a notification, it works just fine. When I run a testcafe suite I still end up seeing notifications (that asynchronously pop up), cover the button that I need to interact with, and never close.
When does the code actually get injected? Is it every page load?
Video of the script working fine via the console: https://www.loom.com/share/1a5b96d054a345748e4f018bc56af413
The Client Script injection should work even after a new page is loaded. The following code snippet demonstrates that the script is injected:
fixture`f`
.page`http://example.com`;
const clientScript = `
console.log('location: ' + window.location.href);
`;
test
('My test', async t => {
await t.navigateTo('http://google.com');
await t.debug();
})
.clientScripts({ content: clientScript });
If this code does not help, please create a separate issue in the TestCafe official repository using the following template and share an example where the problem is reproduced: https://github.com/DevExpress/testcafe/issues/new?assignees=&labels=TYPE%3A+bug&template=bug_report.yaml.
No, but really! I know this generic question has been asked thousands of times, but there is something more specific that looks feasible to me and thus I want to know how to achieve it
The problem
I'm testing an angular app with protractor. Inside the app I want to verify that when I click a link I'm being redirected to the right page (non-angular). The problem is that until I reach the page I'm verifying, the url changes 3 times or so (there are multiple redirections happening), so I can't make a waiting function wait until the page is loaded completely
What I tried/what won't work for me
I'm against browser.sleep() for more than 1000 ms!
browser.waitForAngular() as this is not an angular page
ExpectedConditions.urlIs() the url is the variable I'm asserting
ExpectedConditions.presenseOf() the page maybe changing so I can't rely on elements inside
browser.executeScript("return window.document.readyState") returns compete immediately, though the page is still loading (I was certain this is what I need, but that didn't work either)
I tried even adding a functions that waits for innerHtml of the whole page not change for at least 3 sec, but it fails because at times there is a pause of more than 3 sec between redirects are happening. Everything above 3 sec isn't a reasonable timeout
The question
What I noticed is when the browser is loading the page, Reload this page button changes its state to Stop loading this page (X icon) until I'm redirected to the final page (screenshots below). So the question is is there a way to make protractor point to the same condition that chrome uses to choose which icon is displayed?
vs
And if not exactly the same, but how do I make protractor hang until the page is fully loaded
Important to consider
Obviously there are a lot of dirty solutions that I can do like explicit waits. But I'm coming back to this question every once in a while, so I'm not interested in these dirty solutions that work 70% of the time for a specific cause
P.S. I figured that the button changes the icon on document.load() event. But I can't figure out what should I write in the console in order for that script to log a message when I refresh they page
Have you tried
await browser.wait(async () =>
await browser.driver.executeScript('return document.readyState;') === 'loading', 2000)
.then(() => true, () => true);
await browser.wait(async () =>
await browser.driver.executeScript('return document.readyState;') === 'complete', 5000)
I have the following snippet for just the problem, I wait for an element to be displayed:
ElementFinder.prototype.secureIsDisplayed = function () {
return browser
.wait(EC.visibilityOf(this), actionTimeout)
.then(() => this.isDisplayed())
.catch(err => {
throw new Error(`Expected ElementFinder ${this.locator()} to be displayed. ${err.toString()}`);
});
};
where actionTimeout is a const int and EC = protractor.ExpectedConditions you will need to import the file and call it like so:
import './asd.ts'
element(by.css('.myClass')). secureIsDisplayed()
I if you can find the last element that loads, or just get an element of a redirect it can do wonders.
Cheers.
Have you tried...
browser.executeScript("return window.jQuery.active").equals(0)
I created a basic test with protractor to click on a element and on the new page check if given element exists, but Protractor does not seem to wait and it runs the assertion just after the click but before the new page loads. The element I am looking for is available on both pages, so protractor sees the element on the old page, before the new page loads. Can someone please tell me what am I doing wrong?
it('should check when new page is loaded', function () {
button.click().then(function (){
return expect(newElement).to.exist;
});
First of all ,add "getPageTimeout" variable to your Protractor configuration file.
If you are not using it already.This is to set global page timeout based on your average page load time in your application.
conf.js
getPageTimeout: 120000,//change it based on your app response time
If it does not help even then verify the page title of next page(assuming its different from previous page) before checking the actual element you are looking for.
it('should check when new page is loaded', function () {
button.click().then(function (){
browser.getCurrentUrl();
browser.getTitle().then(function (title) {
expect(title).toEqual('Next Page Title');
});
return expect(newElement.isDisplayed()).toBeTruthy();
});
Even if that does not help you may use expected conditions.There are several predefined conditions to explicitly wait for. In case you want to wait for an element to become present:
var EC = protractor.ExpectedConditions;
var e = element(by.id('xyz'));
browser.wait(EC.presenceOf(e), 10000);
expect(e.isPresent()).toBeTruthy();
You can use below method in Configuration file
jasmineNodeOpts: {
showColors: true,
includeStackTrace: true,
defaultTimeoutInterval: 1440000
},
And In Spec file you can customise the wait time according to page and object visibility
browser.sleep(20000);
I hope above method will work fine in all the cases if you customise the wait time Properly.
I'm trying to implement the following test scenario:
perform a click on a logo on the page
assert there is a new browser window opened (tab in Chrome) and check the current URL
The problem is that the page opened in a new browser window is a non-angular page while the main page I'm performing the click in is an angular page.
Here is my first attempt:
it("should show logo", function () {
var logo = scope.page.logo;
expect(logo.isDisplayed()).toEqual(true);
// opens a new page on click
logo.click().then(function () {
browser.getAllWindowHandles().then(function (handles) {
browser.switchTo().window(handles[1]).then(function () {
expect(browser.getCurrentUrl()).toEqual('http://myurl.com/');
});
// switch back to the main window
browser.switchTo().window(handles[0]);
});
});
});
which fails with:
Error: Error while waiting for Protractor to sync with the page:
"angular could not be found on the window"
which is understandable.
My second attempt was to play around with ignoreSynchronization boolean flag:
browser.ignoreSynchronization = true;
logo.click().then(function () {
browser.getAllWindowHandles().then(function (handles) {
browser.switchTo().window(handles[1]).then(function () {
expect(browser.getCurrentUrl()).toEqual('http://myurl.com/');
});
// switch back to the main window
browser.switchTo().window(handles[0]);
});
This actually makes this particular test pass without any errors, but it affects every test executed after this one, because browser is a global object and is shared between tests - protractor no longer syncs with angular on a page which results into all sorts of errors.
How should I implement the test?
As a workaround, I can change the restartBrowserBetweenTests setting to true and change ignoreSynchronization value without any problems - but it slows down the tests dramatically.
You can set ignoreSynchronization to be false once your verification is done. However, note that ignoreSynchronization is synchronous while everything else (click/get/etc) is asynchronous, so you need to be careful with that. Probably the safest way is to set it to true in beforeEach and false in afterEach, and just test that single logo click in that test.
Another solution: I have used sleep since getWindowHandles was
returning only one window name(parent/angular window). Let me know if there
is a better way.
this.validateProductPageInfo = function (productTitle) {
browser.sleep(5000);
browser.getAllWindowHandles().then(function (handles) {
if (handles.length === 2) {
browser.switchTo().window(handles[1]).then(function () {
var prodTitle = by.xpath('//h1[#id="fw-pagetitle"]');
browser.driver.findElement(prodTitle).getText().then(function (text) {
expect(text).toBe(productTitle);
});
});
browser.switchTo().window(handles[0]);
} else {
console.log("Error in switching to non angular window");
}
});
};
I am new to Protractor. I think I have this down when dealing with an Angular page, but can't figure it out for a non-Angular page. Any help would be appreciated.
describe('Search', function() {
it('should click Search button and wait for results', function() {
browser.driver.findElement(by.id('search')).click();
});
});
Testing non-angular pages with Protractor can be tricky regarding waiting for stuff.
I suggest you upgrade Protractor to latest (1.5.0 as of now), use a custom function waitReady() that browser.wait for elements ready and rewrite your test like below. Note you can put everything within 1 spec if you like so.
// TODO: use page objects
var searchBtnElm = $('#search'); // use element(by.id('search')) if you prefer
it('waits for the elements present and visible (non-angular)', function() {
expect(searchBtnElm.waitReady()).toBeTruthy();
});
it('should click Search button', function() {
searchBtnElm.click();
});
it('wait for more results', function() {
// keep using waitReady() before interacting with the elements
// and before performing expectations on them
});
More details of why waitReady here.
Note: remember to set ignore synchronization for testing a non-angular page:
browser.ignoreSynchronization = true;
You can set it before browser.get the non-angular page.
I've suggested setting a high implicit wait in the past, e.g.
browser.manage().timeouts().implicitlyWait(5000);
That hack allows to you avoid waitReady and keep using the standard
expect(searchBtnElm.isPresent()).toBeTruthy();
But has an ugly disadvantage when testing for elements NOT present, i.e. when testing for absent or non visible elements in which case it will wait 5 seconds (5000ms) in vane, e.g. when doing
expect(someNonExistingElm.isPresent()).toBeFalsy();
Figured this out. I simply added the code below, after the click method:
describe('Search', function() {
it('should click Search button and wait for results', function() {
browser.driver.findElement(by.id('search')).click();
dvr.wait(function() {
return dvr.isElementPresent(by.xpath(
'/html/body/div/div[4]/div/div[2]/div/div/div/span'));
}, 20000);
});
});
Another Neat approach is to use "Expected Conditions" inside browser.wait - something like this:
var EC = protractor.ExpectedConditions;
var search = element(by.id('search'))
browser.wait(EC.visibilityOf(search), 2000).then(function(){
search.click()
})
You can get more details here: https://angular.github.io/protractor/#/api?view=ExpectedConditions
In protractor there are two types terms for on the page. isPresent ask if the element is exists on the page. isDisplayed asks if the element is visible. If you are waiting for a page to load you need to wait for isDisplayed, but that will error if it is not present, so wait for isPresent first. I use a function to wait for an element.
function waitForElement(el, waitTimeoutMilliseconds){
return browser.wait(function() { return el.isPresent(); }, waitTimeoutMilliseconds)
.then(function(){
return browser.wait(function() { return el.isDisplayed(); }, waitTimeoutMilliseconds);
});
}
Then just call that function in your test.
describe('Search', function() {
it('should click Search button and wait for results', function() {
var el = element(by.id('search'));
waitForElement(el, 5000);
el.click();
});
});