Selenium JavaScript Try Catch block is not working as expected - javascript

I am trying to throw an error on the console if the first link of bing search results is clicked. My code is working fine until the first link is opened, but I am not seeing any message on the console that first link is successfully clicked and when I make an intentional code change to catch error, catch is not throwing error. I am using node.js, selenium and edge webdriver. Please help me fix this.
const { Builder, By } = require('selenium-webdriver');
async function launchEdgeBing() {
let driver = await new Builder().forBrowser('MicrosoftEdge').build();
await driver.get('https://bing.com');
await driver.manage().window().maximize();
const element = await driver.findElement(By.id('sb_form_q'));
await driver.sleep(5000);
await element.sendKeys('latest news');
await element.submit();
await driver.sleep(5000);
try {
const clickFirstResult = await driver.findElement(
By.className('b_title')
);
clickFirstResult.click();
await driver.sleep(5000);
var title = await driver.getTitle();
console.log('Title is:', title);
} catch (e) {
if (e == null) {
console.log('First link opened successfully');
} else {
console.log('Error is:', e);
}
} finally {
await driver.quit();
}
}
launchEdgeBing();

Related

How to reload and wait for an element to appear?

I tried searching for this answer but there doesn't seem to be an answer on the Internet. What I want to do is use node js to reload a page until it finds the element with the query I want. I will be using puppeteer for other parts of the program if that will help.
Ok, I used functions from both answers and came up with this, probably unoptimized code:
const puppeteer = require("puppeteer");
(async () => {
try {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("http://127.0.0.1:5500/main.html");
await page.waitForSelector("#buy-button");
console.log("worked");
} catch (err) {
console.log(`ERROR: ${err}`);
}
})();
But what I don't know how to do is to reload the page, and keep reloading until the id I want is there. For example, keep reloading youtube until the video you want is there(unpractical example, but I think it gets the point across).
Here's how I solved waiting for an element in puppeteer and reloading the page if it wasn't found;
async waitForSelectorWithReload(selector: string) {
const MAX_TRIES = 5;
let tries = 0;
while (tries <= MAX_TRIES) {
try {
const element = await this.page.waitForSelector(selector, {
timeout: 5000,
});
return element;
} catch (error) {
if (tries === MAX_TRIES) throw error;
tries += 1;
void this.page.reload();
await this.page.waitForNavigation({ waitUntil: 'networkidle0' });
}
}
}
And can be used as;
await waitForSelectorWithReload("input#name")
You can use "waitUntil: "networkidle2" to make sure the page is done loading. Obviously change the url, unless you are actually using evil.com
const puppeteer = require("puppeteer"); // include library
(async () =>{
const browser = await puppeteer.launch(); // run browser
const page = await browser.newPage(); // create new tab
await page.goto(
`http://www.evil.com`,
{
waitUntil: "networkidle2",
}
);
// do your stuff here
await browser.close();
})();
const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
page
.waitForSelector('#myId')
.then(() => console.log('got it'));
browser.close();
});

UnhandledPromiseRejectionWarning: ReferenceError: browser is not defined for azure login with puppeteer

I am trying to automate my application which is running on azure portal using puppeteer. And I am getting following error after entering the password it is not clicking the submit button.
node:55768) UnhandledPromiseRejectionWarning: ReferenceError: browser is not defined
Here is my sample code:
(async () => {
try {
const launchOptions = { headless: false, args: ['--start-maximized'] };
const browser = await puppeteer.launch(launchOptions);
const page = await browser.newPage();
await page.emulate(iPhonex);
await page.goto('https://apps.testpowerapps.com/play/72ff5b93-2327-404d-9423-92eedb44a287?tenantId=n082027');
//Enter User Name
const [userName] = await page.$x('//*[#id="i0116"]');
await userName.type("jyoti.m#azure.com");
const [loginButton] = await page.$x('//*[#id="idSIButton9"]');
await loginButton.press('Enter');
//Enter Password
const [passWord] = await page.$x('//*[#id="i0118"]');
await passWord.type("Pass123");
const [submitButton] = await page.$x('//*[#id="idSIButton9"]');
await submitButton.press('Enter');
//await page.keyboard.press('Enter');
}
catch(error){
console.error(error);
}
finally {
await browser.close();
}
})();
Tried with both way but not working only catch is the xpath for both the pages are same.
const [submitButton] = await page.$x('//*[#id="idSIButton9"]');
await submitButton.press('Enter');
//await page.keyboard.press('Enter');
any clue to resolve this.
You define the browser value in the try but you also use it in the catch. consts are block-scoped, so they are tied to the block, so a different block (the finally) can not see it.
Here is the problem:
try {
const browser = ...;
}
finally {
// different block!
await browser.close();
}
To solve this, move the browser out of the try-catch:
const browser = ...
try {
}
finally {
await browser.close();
}
This way it's available in the finally block.

puppeteer event error handler don't go to catch block

I'm creating a web scraping with puppeteer(nodejs), in some specific part of the web scraping i'm listening an event that is fired when the browser launch an dialog box with a message. on when this event is launched, i throw an exception, but this exception make the main process exit, and doesn't go to catch(err)
Example:
let page = null
const login = async () => {
//Event listening
page.on('dialog', async dialog => {
throw new Error('login_error')
}
async function processWebScraping () {
try{
page = // Initialize puppeteer page
await login()
[...]
} catch(e){
// the Error doen't come here, i got an uncaughtException
console.log(e)
}
someone know how can I get this error and handdle it?
Try wrapping your listener in a Promise instead. Doing so will allow to call .then() to resolve it and .catch() to catch an error. In your example it will look like this
const login = () => new Promise((resolve, reject) => {
page.on('dialog', dialog => {
reject(new Error('login_error'));
});
});
function processWebScraping() {
// page initialization
login().then(() => {
console.log('No error here');
}).catch(err => {
// Handle the error
console.error(err);
});
}

How to pass the "page" element to a function with puppeteer?

I’m trying check if elements are available on a page from within a function, if the element is on the page, good, continue with the code, if not, log the error.
Using the try puppeteer page, here is what I tried:
const browser = await puppeteer.launch();
const page = await browser.newPage();
const check = element => {
try {
await page.waitFor(element, {timeout: 1000});
} catch(e) {
console.log("error : ", e)
await browser.close();
}
}
await page.goto('https://www.example.com/');
check("#something");
console.log("done")
await browser.close();
I get Error running your code. SyntaxError: Unexpected identifier. I debugged a bit and it seems that page within the check function is the unexpected identifier. So I tried to pass it in with force like this:
const browser = await puppeteer.launch();
const page = await browser.newPage();
const check = (element, page) => {
try {
await page.waitFor(element, {timeout: 1000});
} catch(e) {
console.log("error : ", e)
await browser.close();
}
}
await page.goto('https://www.example.com/');
check("#something", page);
console.log("done")
await browser.close();
but I get the same Error running your code. SyntaxError: Unexpected identifier error...
What am I doing wrong?
You can use this variant to check if the element is in the page or not.
if (await page.$(selector) !== null) console.log('found');
else console.log('not found');
Now back to your code, it's throwing error because of this function is not async,
const check = async element => { // <-- make it async
try {
await page.waitFor(element, {timeout: 1000});
} catch(e) {
console.log("error : ", e)
await browser.close();
}
}
Anytime you call await, it must be inside an async function. You cannot call await everywhere. So your check function should be called like this,
await check("#something", page);
So altogether we can rewrite the code snippet this way, you can go ahead and try this one.
const browser = await puppeteer.launch();
const page = await browser.newPage();
const check = async(element, page) => (await page.$(element) !== null); // Make it async, return true if the element is visible
await page.goto('https://www.example.com/');
// now lets check for the h1 element on example.com
const foundH1 = await check("h1", page);
console.log(`Element Found? : ${foundH1}`);
// now lets check for the h2 element on example.com
const foundH2 = await check("h2", page);
console.log(`Element Found? : ${foundH2}`);
await browser.close();
Also async functions will return promises, so you have to catch that promise or use another await. Read more about async await here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
https://ponyfoo.com/articles/understanding-javascript-async-await

Multiple Async/Await Try-Catch Block

I am here because i am having troubles debugging my app. It's very annoying not see why my app crashes. I was using promises (with then/catch blocks) but i get on the necessity of using async/await.
I have a method where i do multiple await on it. The problem here is that if my app crashes because any reason i never know whats the problem on it. I have described the block like this:
static async processCSGOGroupsAndUsers (groupName) {
try{
const csgoApiData = await csgoApi(groupName);
const parsedData = await xmltojson(csgoApiData);
const id = parsedData.memberList.groupID64;
//const members = await retrieveMembers(groupName, parsedData.memberList.memberCount);
const totalUsers = await UsersService.processCSGOUsers(id, parsedData);
const csgoGroup = {
name: parsedData.memberList.groupDetails.groupName,
siteUrl: parsedData.memberList.groupDetails.groupURL,
id,
totalUsers
};
await GroupsDao.save(csgoGroup);
}catch (err){
return err;
}
}
static async processCSGOUsers (groupId, parsedData) {
try{
let steamIdsArr = [];
const usersSteamIdsObj = parsedData.memberList.members.steamID64;
Object.keys(usersSteamIdsObj).forEach(key => {
//if (steamIdsArr.length < 2) // TODO csGOBackPackAPI don't let me do more than 50 request per hour
steamIdsArr.push({
steam_group_id_64: groupId,
steam_id_64: usersSteamIdsObj[key]
});
});
//const filteredUsers = await UserService.filterUsersByInventoryValue(steamIdsArr);
UsersDao.saveUsers(steamIdsArr);
} catch(err){
console.log(err);
return err;
}
}
static processCSGOGroups(req, res){
GroupService
.processCSGOGroupsAndUsers(req.body.group)
.then( () => res.status(200).end())
.catch( error => res.status(400).send(error));
}
Is there a better approach than mine's?
I created an NPM package to help with situations like this.
https://www.npmjs.com/package/#simmo/task
The following shows example usage. The idea is that it should help remove try/catch replacing with conditional logic - hopefully helping to make everything a little more readable! :)
import task from '#simmo/task'
const { error, data } = await task(fetch('/some-api'))
if (error) {
// Failure
console.error(error)
} else {
// Success
console.log(data)
}

Categories

Resources