Clicking on an element, if present, in webdriverio - javascript

I'm using the latest Appium and webdriverion versions to test a native app, and what I need to do in my script is Click on a Button if it exists, but continue if it doesn't.
So, if the Accept and Close button is present it will be clicked, and then the subsequent Allow button will be clicked.
But, if the Accept and Close button isn't present, then it will go straight to the next command and click the Allow button straight away.
My code currently looks like this;
if (await expect(await closeCMP).isExisting()) {
await closeCMP.click()
} else {
var allow = await $('~Allow');
await allow.click();
}
});
});
However, if the Accept and Close button isn't present then the test fails (with a timeout error, as the Accept and Close element cannot be found) rather than perform the next command (which is to click on the Allow button).
Is there an obvious reason for this?
Also, do I need the else part of the loop, or will just the if part of the loop suffice?
I appreciate that some may perceive this as bad practice, but I'm not able to alter anything on the DOM, or do anything with Cookies so I'm not sure what else I can do to get this to work.
Any help would be greatly appreciated.

There's more than one way to do this, but I generally use try/catch blocks to prevent tests from failing when evaluating conditionals.
Here's an example of how it could look using this approach.
try {
await expect(await closeCMP).isExisting();
await closeCMP.click();
} catch {
console.warn('one of the previous lines threw an error');
}
const allow = await $('~Allow');
await allow.click();

Related

How to prevent the browser from closing while running code in Playwright Tests in Javascript

I'm trying to log the network calls in the browser. For my use case I require the browser to be in an open state and should be closed only with the scripts. I am currently using the page.pause() function to prevent the browser from automatically closing. Is there any other way to prevent the browser from closing automatically.
test('Verify home page Load event',async({page})=>{
//const browser = await chromium.launchPersistentContext("",{headless:false});
await page.goto("https://samplesite.com")
await page.on('request',req=>{
const requestUrl = req.url();
if(requestUrl.indexOf("google-analytics.com/collect")>-1){
console.log("Intercepted:->"+requestUrl);
}else{
req.continue
}
})
await page.pause();
})
I tried checking out this [link] (How to keep browser opening by the end of the code running with playwright-python?) for python but could not apply it to JS.
Similar to what was described in the answer to the python question, you need to keep your script alive somehow.
This answer describes a couple of ways to do that.
However, page.pause() is definitely the recommended approach- it exists precisely for this kind of situation where you need to inspect the browser while your script is executing. Your script also has some problems- as it stands when you encounter your target request you are logging something but not calling request.continue() (note that this a method, not a property). That will cause all requests to hang indefinitely until it is continued or aborted.
You probably want to do something like this:
await page.route('**/*', (route, request) => {
const rurl = request.url();
if (rurl.includes('google-analytics.com/collect')) {
console.log(`Intercepted request to ${rurl}`);
// Do other stuff?
}
route.continue();
});
It's not clear what you are trying to accomplish from your snippet- if you just need to wait for a particular request to fire, you can use either:
page.waitForRequest or page.waitForResponse, and do away with worrying about keeping the browser open.
I tried to use await page.pause() and it doesn't work for me, but I found the tricky way, and it works well, just put at the end of your test:
await new Promise(() => {})
Reference on the link.
You can try await Task.Delay(-1)

How to make Protractor's ElementArrayFinder 'each' function wait for the current action to complete before advancing to next iteration?

I am using protractor to do e2e tests in an Angular 8 app and I am currently having trouble making an 'each' loop work as I need.
In my page object I have created a method that returns an ElementArrayFinder.
public getCards(): ElementArrayFinder {
return element.all(
by.css(
`${this.baseSelector} .board-body cs-medication-card`,
),
);
}
For each of the elements returned I want to perform a set of actions that consists in clicking in a button that opens a menu list (while the menu is open there is an overlay element over all the view except the menu list) and pick one of the options. In my test I have this:
await page.board
.getCards()
.each(async (el: ElementFinder) => {
await until.invisibilityOf(await page.getOverlay());
await el
.element(by.css('.card-header .actions'))
.getWebElement()
.click();
await expect(page.isItemInMenu('X')).toBeTruthy();
await page
.getMenuItemByLabel('X')
.getWebElement()
.click();
});
I was expecting that for each card it would click the actions button, check if the option is in the list and click in the option.
What is happening is that it seems that protractor is trying to do everything at same time, since it says it cannot click on the actions button because the overlay is over it. The only way to the overlay be over the button is if the action in the previous iteration is not complete. I have already threw in the ExpectedCondition to wait for the overlay to be invisible but no luck. If only one element is returned by the ElementArrayFinder it does what is supposed.
I am out of ideas, any help would be much appreciated.
Protractor's each is asynchronous.
It's fine when you need to perform some action over an array of element (e.g get text or count) but it's not the best choice when you need to perform some kind of scenario.
I'm suggesting to use any other way like for loop.
Another workaround (which again might not work because of async nature of .each) is FIRST to wait for overlay to appear and the wait for it to disappear.
The each function is almost of no use. It even doesn't wait for the promise returned by each Function.
await $$('...').each((ele, index) => {
return ele.$('.fake_class').click().then(() => {
console.log(`the item ${index} is clicked.`)
return browser.wait(5000)
});
});
I think there are some issues in the function of each.
getWebElement() is unnecessary
await expect(page.isItemInMenu('X')).toBeTruthy(); seems wrong
await until.invisibilityOf(await page.getOverlay()); I'm not sure it work well.
Please try following code:
await page.board
.getCards()
.each(async (el: ElementFinder) => {
// await until.invisibilityOf(await page.getOverlay());
browser.sleep(10*1000)
await el
.element(by.css('.card-header .actions'))
.click();
// expect(await page.isItemInMenu('X')).toBeTruthy();
await page
.getMenuItemByLabel('X')
.click();
})
.catch((err)->{ // use catch() to capture any runtime error in previous each
console.log('err: ' + err)
})
If above code worked, you can revert back the expect and run again, then remove browser.sleep() and revert back your await until.invisibilityOf(....) and run again.

Protractor on AngularJS - 'script timeout: result was not received in 11 seconds' after login page change

Protractor hangs completely when trying to get any element property after logging in (idk if it's related to logging in or related just to switching pages).
it("Should get location of main container", async function() {
await LoginPage.validLogin();
// Works and logs in the dashboard
await browser.sleep(3000);
// Get the main container by class name
const container = await element(by.css(".main-container"));
// Logs properly the element functions (as expected)
console.log(container);
console.log(await container.getLocation()); // Hangs here
});
In this case, I'm trying to get the location of the main container element on the page. The first console.log fires and shows properly, while the second hangs completely, so I get the script timeout. Increasing the timeout time doesn't help at all...
I found online that misusing $timeout in AngularJS instead of using $interval may lead to this strange behaviour, but I really can't skim through the entire (very big!) project's codebase to change everything hoping that it just works, not to talk about the external libraries using $timeout.
I have SELENIUM_PROMISE_MANAGER = false; in my Protractor config so I disabled the built-in Control Flow in order to manually manage the promises using async/await, but even if I use the built-in Control Flow without using async/await I get the very same behaviour and error.
I'm using Jasmine as testing framework.
Maybe I'm missing something? Any help would be much appreciated, thanks!
This is caused by the fact that angular is not stable. Have a look at the link below. I found my answer there. When the page you are trying to test is open go to the browser dev tools and type in the console getAllAngularTestabilities(). There are a few properties here that indicate whether angular is ready to be tested. hasPendingMicrotasts needs to be false. hasPendingMacroTasks needs to be false. isStable needs to be true. I put a screenshot below. In my screenshot hasPendingMacrotasks is true and it must be false. So the page I looked at was not ready to be tested.
Failed: script timeout: result was not received in 11 seconds From: Task: Protractor.waitForAngular() - Locator: By(css selector, #my-btn)
Try something like this:
it("Should get location of main container", async function() {
await LoginPage.validLogin();
const container = await element(by.css(".main-container"));
await browser.wait(protractor.ExpectedConditions.presenceOf(container), 5000, 'Element taking too long to appear in the DOM');
await console.log(await container.getLocation());
});
I don't think that getLocation() exists in the Javascript bindings for selenium. I couldn't find it in the source code anyway. So that promise will never return which is why it hangs. But I the you can achieve basically the same thing with getRect():
it("Should get location of main container", async function() {
await LoginPage.validLogin();
const container = await element(by.css(".main-container"));
await browser.wait(protractor.ExpectedConditions.presenceOf(container), 5000, 'Element taking too long to appear in the DOM');
await console.log(await container.getRect());
});

How to apply wait time when Your page javascript reloads the element

Hi i am using selenium webdriver to automate my script and i have used wait.until condition in my script to click on the delivery bttn in below html page. The problem is selenium is finding my element but since the java script reloads the certain element. And the delivery bttn only becomes clickable after reloading. And my selenium script throws "stale element reference: element is not attached to the page document". What should i do to overcome this error.
WebElement delibttn=wait.until(ExpectedConditions.elementToBeClickable(By.xpath("(//button[#class='btn-to-cart nopricetohide btn btn-primary your-catalog-deliver btn-block btn-unpadded tocart-rounded'])[1]")));
delibttn.click();
WebElement contshopping=wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//a[#class='btn btn-link full-width mb-10']")));
Screenshot:
there are two ways to solve your problem.
1) Run the code with Async, that way you can 'await' a line of code, for example..
function async(test1){
await driver.findElement(By.id("");
driver.click();
});
or you can also do the following
2)
function (test1) {
let element = driver.findElement(By.id(elementId));
driver.wait(until.elementIsVisible(element), 10000).then(async () =>{
element.click();
});
This wait in number 2, is the one that i use in my code and it always works.
A very barbaric way of doing it would be to add a ridiculous wait time to check that it isn't something else showing an error similar to a wait problem
driver.sleep(10000);
or
thread.sleep(10000);
(the measurement is in milliseconds unless defined otherwise)
Please let me know if these solutions do not solve the problem.
as Jack suggested you could use async, but I always used an infinte while loop
Code i have given below is in python, but you can use the logic in java too
def wait_for_element():
val = True
while val:
web_elem = driver.find_element_by_id('id')
try:
web_elem.is_displayed()
except Exception as ex:
val = True
else:
val = False
i know infinite loop is not a better way than async, but if there are cases where you can't use async you can use this. Also keep in mind to put timeout for loop, otherwise you would looping infinitely when the page was unresponsive or has not loaded.
the reason it is still throwing this issue is because you are not handling your exceptions properly, this is a response to it still throwing stale element errors.
Add something like this to your project, if you look at the bottom of my code you will see that i have added exceptions to catch errors so it does not affect the code the way it is doing.
driver.findElement(By.id(buttonID)).then(pageElement => {
driver.wait(until.elementIsVisible(pageElement), 10000).then( () => {
pageElement.click();
next();
})
.catch(ex => {
console.log(ex.message, ex.stack)
});
}).catch(ex => {console.log(ex.message, ex.stack)});
This is the example of how i am using catches, however many promises you have in your function the more catches you will need, if you hover over an element in Visual Code / Studio you will be able to see if it throws a promise or not.

Clicking through an undefined number of dialog boxes

Currently I am using ProtractorJS to access a page that has an unspecified number of popup dialog boxes that are not crucial to the operation of the webpage, but block further interaction with it.
For example, when I open the login screen several (but of unknown quantity) pop-ups appear, and one after another (they are not present at the same time). I am currently handling this in a sloppy way (I see if the object is present and click the existing button to terminate it) and I believe their has to be a better way of handling this... Essentially, I would like to "loop" through these actions till they are finished, in a promise like manner if possible.
Also, as a caveat, I would like to be able to handle messages that randomly should appear, without disrupting the flow of my tests. I understand the latter maybe a little to good to be true, but I figured I would ask.
This is one way.
boolean popupFound=true;
while (popupFound) {
try {
// You should temporarily lower implicit wait to avoid slowing things down
driver.setImplicitWait(6);
driver.switchTo().alert().accept(); // select ok or cancel
} catch (NoAlertPresentException ex) {
// Not needed so exit; assumes no delay between popups
popupFound = false;
} finally {
driver.setImplicitWait(60); // Or whatever you had it set at originally
}
}
Better make sure that implicit wait is only as large as it needs to be.

Categories