I'm trying to click on '0 - Bootstrapping' under Tutorial after going to https://docs.angularjs.org/tutorial, but Protractor is not waiting for the page to fully load, so I get an Error
Failed: Cannot read property 'click' of undefined
I tried manually waiting for the page to load using
browser.driver.sleep(10000);
but I still get the same Error.
conf.js
exports.config = {
// seleniumAddress: 'http://localhost:4444/wd/hub',
getPageTimeout : 12000000,
capabilities: {
'browserName': 'chrome'
},
specs: ['todo-spec.js'],
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 1440000,
}
};
todo-spec.js
describe('angularjs homepage', function () {
it('should greet the named user', function () {
var dropdown = element.all(by.css('.dropdown')).first(),
dropdownToggle = dropdown.element(by.css('.dropdown-toggle')),
dropdownMenu = dropdown.element(by.css('.dropdown-menu')),
menuItem = dropdownMenu.all(by.tagName('li')).first();
browser.get('http://www.angularjs.org');
dropdownToggle.click();
menuItem.click();
expect(browser.getCurrentUrl()).toEqual('https://docs.angularjs.org/tutorial');
//waiting 10 seconds for the page to fully load
browser.driver.sleep(10000);
browser.waitForAngular();
});
it('', async function () {
/* tried waiting for the element to be present
var EC = protractor.ExpectedConditions;
var e = element.all(by.css('.nav-index-listing'))[0];
browser.wait(EC.presenceOf(e), 10000);
Failed: Cannot read property 'isPresent' of undefined
*/
//clicking '0 - Bootstrapping' under Tutorial
element.all(by.css('.nav-index-listing'))[0].click();
expect(element(by.cssContainingText('.pln', 'npm install')).isEnabled()).toBe(true);
// Failed: Cannot read property 'click' of undefined
});
});
Edit
Replaced
element.all(by.css('.nav-index-listing'))[0].click();
expect(element(by.cssContainingText('.pln', 'npm install')).isEnabled()).toBe(true);
by
element.all(by.css('.nav-index-listing')).get(0).click();
browser.wait(protractor.ExpectedConditions.urlIs("https://docs.angularjs.org/tutorial/step_00"), 5000);
expect(browser.getCurrentUrl()).toEqual('https://docs.angularjs.org/tutorial/step_00');
expect(element(by.cssContainingText('.pln', 'npm install')).isEnabled()).toBe(true);
And got an Error
Failed: Wait timed out after 5001ms
If I remove browser.wait I get
Expected 'https://docs.angularjs.org/tutorial' to equal 'https://docs.angularjs.org/tutorial/step_00'.
First of all browser.driver.sleep(10000) is not required. For page load, you can use protractor.ExpectedConditions. It will reduce unnecessary waiting for 10 seconds, if the page is already loaded.
Secondly, there is a minor mistake in your code which is the root cause of your problem.
Change this line
element.all(by.css('.nav-index-listing'))[0].click();
To this
element.all(by.css('.nav-index-listing')).get(0).click();
UPDATE:
element.all(by.css('.nav-index-listing')) will only give you li elements which will pass the test. But clicking it, will not lead you to the desired page.
Change it to:
element.all(by.css('.nav-index-listing a')).get(0).click();
In this case, [0] will not work since the returned value is not an array. It is of type ElementArrayFinder. So, that's why .get(0) is required.
It will be better if you check for below conditions too, for your test.
browser.wait(EC.urlIs("https://docs.angularjs.org/tutorial/step_00"), 5000);
expect(browser.getCurrentUrl()).toEqual('https://docs.angularjs.org/tutorial/step_00');
These conditions checks whether your current URL is https://docs.angularjs.org/tutorial/step_00 or not.
Try using waitForAngualarEnabled(true) instead of expected wait which makes the protractor to wait until all the angular elements are loaded and add await in every line of your test.
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.
This question is closely related to the solutions given in this question
In my test script, I need to go to login script and need to logout in case the browser logs in automatically in the app. So following the solutions provided in the question How to create a condition in protractor for when an element exists or not, I have created this script:
beforeEach(function () {
browser.driver.manage().window().maximize();
browser.get(globalVariables.loginMain);
globalVariables.User_Menu_Dropdown.isDisplayed().then(function(Login_Menu) {
if (Login_Menu) {
globalVariables.User_Menu_Dropdown.click();
browser.wait(globalVariables.until.presenceOf(globalVariables.logOut_Button), 3000, 'The Logout menu too long to appear in the DOM');
globalVariables.logOut_Button.click();
browser.wait(globalVariables.until.presenceOf(globalVariables.Email_Input_box), 3000, 'The User Input box too long to appear in the DOM');
} else {
console.log("the app is on the login page")//do nothing
}
});
But when I run the script, I still get the following error
"Failed: No element found using locator: By(css selector, img[class="img-thumb-xs mr-1 align-middle"])". What am I doing wrong here? What is the best approach to achieve it?
you can use the ExpectedConditions in your case.
var EC = protractor.ExpectedConditions;
// Waits for the element with id 'abc' to be no longer visible on the dom.
browser.wait(EC.invisibilityOf($('#abc')), 5000);
or you can use the not condition which will lead to the same result
var EC = protractor.ExpectedConditions;
// Waits for the element with id 'abc' to be no longer visible on the dom.
browser.wait(EC.not(EC.visibilityOf($('#abc'))), 5000);
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 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,
},
I'm on my third day working with Protractor and I'm constantly hitting bric walls in regards to waiting around for pages to load and elements to appear. This test case in particular has grown ugly and I would like to solve the issues without having to rely on sleeps.
I am currently "outside of the land of AngularJS"
it('it should reflect in both the field and the title when the personnel name is changed', function() {
var inputField, personnelHeader, personnelName;
personnelName = element(By.css(".overlay.editnameoverlay")).click();
personnelHeader = element(By.id("personnel_name_header"));
inputField = element(By.css("input[name='newvalue']"));
inputField.clear();
inputField.sendKeys("Test 123");
element(By.css("input[name='ok_button']")).click();
// browser.driver.sleep(2000); This test only works with this sleep added
browser.wait(function() {
console.log("Waiting for header to change...");
return personnelHeader.getText().then(function(text) {
return text === "Test 123";
});
}, 5000);
return expect(personnelHeader.getText()).toBe(personnelName.getText());
});
So the test here changes the name in an input field. submits it and waits for the changes to become reflected in the header of the modal. The problem is that without the browser.driver.sleep(2000) I get an error saying
Stacktrace:
StaleElementReferenceError: stale element reference: element is not attached to the page document
How do I go about solving this in this particular case?
From the documentation for Expect Conditions:
var EC = protractor.ExpectedConditions;
// Waits for the element with id 'abc' to contain the text 'foo'.
browser.wait(EC.textToBePresentInElement($('#abc'), 'foo'), 5000);
When you use Protractor to test for non-angular pages you're on your own regarding waiting for elements to be ready for interaction.
StaleElementReferenceError is probably the most useless selenium error, it happens when the element got removed from the DOM but is still cached somehow, I also suffered this problem when started with Protractor and even tried to convince it should be automatically retried Protractor-side.
The solution for me is to always explicitly wait for an element to appear on the page using a custom function waitReady() that browser.wait for elements ready, i.e: waits for the element to be ready for interaction:
expect($('#login_field').waitReady()).toBeTruthy();
First integrate this snippet in your code: https://gist.github.com/elgalu/2939aad2b2e31418c1bb
Not only the custom waitReady() waits for the element but it also swallows any unrelated useless webdriver error like StaleElementReferenceError and will simply retry up until finding the element or it will timeout.
So waitReady() each element before interacting, i.e. before clear() or sendKeys() or click() ...
// TODO: Move to Page Objects module
var personnelNameElm = $(".overlay.editnameoverlay");
var personnelHeaderElm = $("#personnel_name_header");
var inputFieldElm = $("input[name='newvalue']");
var okBtnElm = $("input[name='ok_button']");
it('it should reflect in both the field and the title when the ' +
'personnel name is changed', function() {
expect(personnelNameElm.waitReady()).toBeTruthy();
personnelNameElm.click();
expect(inputFieldElm.waitReady()).toBeTruthy();
inputFieldElm.clear().sendKeys("Test 123");
expect(okBtnElm.waitReady()).toBeTruthy();
okBtnElm.click();
browser.wait(function() {
console.log("Waiting for header to change...");
// Using waitReady() before getText() avoids Stale element errors
return personnelHeaderElm.waitReady().then(function() {
return personnelHeaderElm.getText().then(function(text) {
return text === "Test 123";
});
});
}, 5000);
expect(personnelHeaderElm.getText()).toEqual(personnelNameElm.getText());
});