Protractor and locating element - protractor

I need to locate elemet "username" using Protractor.
I have used
browser.waitForAngularEnabled(false);
due to the fact that the login page is not Angular.
My script is as simple as this:
describe('slowchat', function() {
it('start test', function() {
browser.waitForAngularEnabled(false);
browser.get('https://www.test.no/eai/bankid20');
element(by.css('[value="Logg inn"]')).click();
browser.waitForAngularEnabled(true);
});
});
However I get error:
Failed: No element found using locator: By(css selector, [value="Logg inn"])
Please see attachment for the place I want to locate.
Any idea why this is not able to locate the button?

Now i saw that in circle is input with username but you ask about button to submit the form, btw you can turn off angular by flag
//entering non-angular site
browser.ignoreSynchronization=true;
//Some code
element(by.css('button[onclick="mySubmit()"])).click();
//when you go back to angular site
browser.ignoreSynchronization=false;

So when you disable waiting for angular or ignore synchronization, this means that you will need to roll your own waiting for some sort of stability. There are two ways you can accomplish this: 1. Turn off the control flow and use async await. 2. Resolve your promises before moving to the next action.
Resolving promises
I think you need to use the done() method call here because of the async calls. My general take away on resolving promises is ew... prefer using async / await and removing the control flow.
describe('slowchat', () => {
it('start test', (done) => {
browser.waitForAngularEnabled(false);
browser.get('https://www.test.no/eai/bankid20').then(() => {
return element(by.css('[value="Logg inn"]')).click().then() => {
browser.waitForAngularEnabled(true);
done();
});
});
});
});
Using async / await and removing the control flow
The control flow is being deprecated so this will get you ahead of the curve. I would suggest doing it this way. It will make your tests easier to debug.
describe('slowchat', () => {
it('start test', async() => {
await browser.waitForAngularEnabled(false);
await browser.get('https://www.test.no/eai/bankid20');
await element(by.css('[value="Logg inn"]')).click();
await browser.waitForAngularEnabled(true);
});
});
When launching this you will need to set the flag to turn off the control flow. In your config, you'll need to add this: SELENIUM_PROMISE_MANAGER: false

Related

How to determine whether user is ready and nonce can be submitted to server?

This is one of many (imho rather incomplete) examples in the docs:
var button = document.querySelector('#submit-button');
braintree.dropin.create({
authorization: 'CLIENT_AUTHORIZATION',
container: '#dropin-container'
}, function (createErr, instance) {
button.addEventListener('click', function () {
instance.requestPaymentMethod(function (requestPaymentMethodErr, payload) {
// Submit payload.nonce to your server
});
});
});
It's all nice and easy but I don't see how I can change the state of button according to the state of "is the user done with adding a payment method?".
Is this even possible? It seems that the click on the button actually performs the fetching of the nonce (which comes as payload.nonce). However, how can I disable button until the user has finished his conversation with Braintree/PayPal?
And the answer is look in the docs (no not those docs) - those docs.
I still don't know how I found that link.
The dropin instance has a on() function where you can register a callback for certain events (just do it - look at the docs already):
instance.on('paymentMethodRequestable', function(event) {
thiz._logger.info('Payment method is now requestable');
setTimeout(() => thiz.paymentMethodAvailable = true, 400);
});

How to stop all JS scripts in Puppeteer

I would like to be able to stop any scripts from being able to run in puppeteer after the page has loaded. The reason for this is to stop carousel images and lazy loading images and essentially get the page to behave as statically as possible to enable screenshots where the images aren't changing etc.
By doing page.evaluate('debugger;') it is possible to pause the whole script, but this does not let you continue with taking screen shots as the a evaluate function does not exit until you exit the debugger (If the gui is enabled)
const page = await browser.newPage()
page.setJavaScriptEnabled(false)
If you would like to disable JavaScript after the page has loaded, you can use debugger:
await page.evaluate(() => {
debugger;
});
I was able to take screenshots after using the debugger.
Alternatively, you can replace each original node with its clone to remove the events attached to each element:
await page.evaluate(() => {
document.querySelectorAll('*').forEach(element => {
element.parentNode.replaceChild(element.cloneNode(true), element);
});
});
You can also use removeEventListener() in a loop similar to the one above to remove specific events attached to a node.
Otherwise, if you can disable JavaScript before the page has loaded, you can use page.setJavaScriptEnabled() before navigating to the page:
await page.setJavaScriptEnabled(false);
A better solution is just to block all requests with the type equals to script:
const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on("request", request => {
if (request.resourceType() === "script") {
request.abort()
} else {
request.continue()
}
})
await page.goto("https://stackoverflow.com/")
await browser.close()
})()
Source: Disabling JavaScript Using Puppeteer
If you want to freeze the page and still be able to call evaluate on it, you can
navigate to the page, wait for it to load (and maybe let its JavaScript make some DOM transformations),
get HTML snapshot of the page,
disable JavaScript,
reload the page statically (no DOM transformations will occur since JavaScript is disabled),
profit (do any amount of evaluate or screenshots on a DOM that is guaranteed to stay the same).
await page.goto('<url>', { waitUntil: 'networkidle0' }); // 1
const html = await page.content(); // 2
page.setJavaScriptEnabled(false); // 3
await page.setContent(html, { waitUntil: 'networkidle0' }); // 4
After phoning a friend the following seems to work:
await page.evaluate('document.body.innerHTML = document.body.innerHTML')

Protractor tests are inconsistent even with browser.wait and helper functions

I am looking for some advice. I have been using protractor for a few weeks and just cannot get my tests to be consistent unless I use browser.sleep. I have tried helper functions as well as browser.wait(expectedCondition). I have reduced browser.sleep immensely, but protractor still just goes way to fast. I can never successfully run multiple tests unless I have a few browser.sleeps just so protractor can relax for a second. Here is an example:
The Test I need to select a user, delete that user and wait for a success message. Then I click the same user and click the activate button.
Outcome: Unless I have browser.sleep, my success messages do not even appear after deletion/activation. The tests fail because protractor is moving way too fast. Even with the expected conditions. My main problem is that protractor moves to fast for the angular web page. I have tried ifCLickable or isDisplayed but they do not fix the issue entirely. Here is the code:
async deleteUser() {
await sendClick(this.getNewUser());
await sendClick(this.getDelete());
await waitTillPresent(this.getDeleteConfirm());
await sendClick(this.getDeleteConfirm());
await waitTillPresent(this.getSuccessMsg())
expect(await page.getSuccessMsg().isDisplayed()).toBeTruthy();
}
async activateUser() {
await sendClick(this.getNewUser());
await waitTillPresent(this.getEditBtn())
await sendClick(this.getActive());
await waitTillPresent(this.getSuccessMsg())
expect(await page.getSuccessMsg().isDisplayed()).toBeTruthy();
}
Functions:
export async function sendClick(element: ElementFinder): Promise<boolean> {
try {
if(!await element.isDisplayed()) {
return false;
}
await browser.executeScript('arguments[0].click();', await element.getWebElement());
return true;
}
catch (err) {
return false;
}
}`
export async function waitTillPresent (element: ElementFinder, timeout: number = 10000) {
return browser.wait(() => {
return element.isPresent();
}, timeout);
}
My Question: Am I handling this correctly? Is there a better to ensure my tests are consistent? Before these tests, I visit a non-angular webpage. So I have to include the line browser.waitForAngularEnabled(false)
Does this mess with the async nature of angular? Thank you.
I worked the last few months on our e2e test suite to make it stable. I did not believe it's possible but I made it using correct wait functions and sometimes browser.sleep() as a last resort.
You have a correct approach for waiting for elements. But there are 2 problems regarding your implementation:
1) The function waitTillPresent() does exactly what its name stands for. But if you only wait until the element is present on the page it does not mean it's clickable or displayed. An element can be hidden and at the same time still be present. Please rename waitTillPresent() to waitTillDisplayed() and change it as follows:
export async function waitTillDisplayed(element: ElementFinder, timeout: number = 20000): Promise<boolean> {
let result = await browser.wait(() => element.isPresent(), timeout);
if(!result) {
return false;
}
return await browser.wait(element.isDisplayed(), timeout);
}
2) You should exceed the default timeout. Set it a bit higher like 20 to 25 seconds. Just play with it.
Unfortunately, I don't know how browser.waitForAngularEnabled(false) changes test behavior. We do not use it :)
Note:
These functions are all exactly the same:
function foo() { return 'hello world'; }
var foo = () => { return 'hello world'; };
var foo = () => 'hello world';
Play with arrow functions, it's syntactic sugar.
Cheers and gl!

e2e Testing in protractor. SignIn

I am not able to redirect on required page on sigIn page. It clicks on button but not giving the output as required. Not able to hit the server for further process after signin process.Plz help
describe("it should be able to run on different events as defined", function(){
it("should be get on browser", function(){
browser.get("http://www.localhost:8100/#/signin");
expect(browser.getCurrentUrl()).toEqual("http://www.localhost:8100/#/signin");
browser.sleep(2000);
element(by.css("[ng-model='user.email']")).sendKeys('ash.mat23.23#gmail.com');
element(by.css("[ng-model='user.email']")).getAttribute('placeholder').then(function(element){
expect(element).toEqual('Email');
browser.sleep(2000);
});
element(by.css("[ng-model='user.password']")).sendKeys('123456');
element(by.css("[ng-model='user.password']")).getAttribute('placeholder').then(function(element){
expect(element).toEqual('Password (at least 6 characters)');
browser.sleep(2000);
});
element(by.id('signin_submit_btn'));
browser.driver.actions().sendKeys(protractor.Key.ENTER).perform();
browser.sleep(5000);
});
});
Are you sure it's clicking the button properly? To my mind the line that reads:
element(by.id('signin_submit_btn'));
Should have a click event on the end, i.e. it should read:
element(by.id('signin_submit_btn')).click();
I'm not completely sure what you are asking. It looks to me that you are preforming all actions at the same time. You should make sure filling in the username and password is done before clicking on submit or pressing enter. I hope this helps:
describe("it should be able to run on different events as defined", function () {
it("should be get on browser", function () {
browser.get("http://www.localhost:8100/#/signin").then(() => {
expect(browser.getCurrentUrl()).toEqual("http://www.localhost:8100/#/signin");
Promise.all([
$("[ng-model='user.email']").sendKeys('ash.mat23.23#gmail.com'),
$("[ng-model='user.password']").sendKeys('123456'),
$("[ng-model='user.email']").getAttribute('placeholder').then(function (element) {
expect(element).toEqual('Email');
}),
$("[ng-model='user.password']").getAttribute('placeholder').then(function (element) {
expect(element).toEqual('Password (at least 6 characters)');
})
]).then(()=>{
$('#signin_submit_btn').click(); // or browser.driver.actions().sendKeys(protractor.Key.ENTER).perform();
})
})
});
});

wait for http request to complete in protractor

I am trying to wait for spinner to disappear and then for my steps to execute but nothing is working for me.
browser.wait(function () {
return this.spinner.isDisplayed().then(function (result) {
return !result;});}, 20000);
and i even tried with
browser.wait(function () {
return !browser.isElementPresent(this.spinner);}, 20000);
even with below method
browser.sleep(1000);
this.spinner.isPresent().then(function (result) {
if (result === true) {
var EC = protractor.ExpectedConditions;
browser.wait(EC.invisibilityOf(this.spinner), 10000);}});
then only thing that works is
browse.sleep(10000);
i don't want to use sleep in my code. can anyone help me with how to wait for complete http request to complete and then process with testing
you should consider using Expected Conditions since they return true/false based on current conditions
http://www.protractortest.org/#/api?view=ProtractorExpectedConditions.prototype.invisibilityOf
so your test case would become:
browser.wait(EC.invisibilityOf(this.spinner),20000).then(function(){
...continue test, spinner gone
});
UPDATE
in order to use done, you would generally pass this cb into your it() function. This means your test could look like
describe("example describe",function(){
it("should be an example only", function(done){
request.get("www.google.com",function(res){
//done with async request, now call done
done();
})
})
});
Since your entire code isn't posted up here, you should have something similar to:
it("should wait for spinner to go bye-bye",function(done){
browser.wait(EC.invisibilityOf(this.spinner),20000).then(function(){
done()
});
});