Using puppeteer how do you get all child nodes of a node? - node.js

I'm having trouble finding a way to iterate subnodes of a given node in puppeteer. I do not know the html structure beforehand, just the id of the parent element.
var elemId = "myelemid";
const doc = await page._client.send('DOM.getDocument');
const node = await page._client.send('DOM.querySelector', {
nodeId: doc.root.nodeId,
selector: '#' + elemId
});
//node.children empty
//node['object'].children empty
//try requesting childnodes
var id = node.nodeId;
var childNodes = await page._client.send('DOM.requestChildNodes', {
nodeId: id
});
//childNodes empty
//try requesting by resolveNode?
var aNode = await page._client.send('DOM.resolveNode', {
nodeId: id
});
//aNode.children is empty
Is there a way to get the children of a node if you don't know the html structure in puppeteer?

What I would do here is to use the evaluate method of Puppeteer to return the children elements of your node to your script as follows:
const nodeChildren = await page.$eval(cssSelector, (uiElement) => {
return uiElement.children;
});
console.log(nodeChildren); // Outputs the array of the nodes children
Hope this helps!

I ended up using page.evaluate to run some js that adds unique classnames to every element and subelement I want analyzed and then pass that back as JSON string since page.evaluate only returns a string. Then I just call DOM.querySelector on each of those unique selectors and loop through them that way.
Returning children from page.$eval doesn't give me protocol nodes that I can run more dev protocol functions on, and xpath doesn't solve my real problem because it can't recursively loop through all sub-children, then the sub-children of those children.
I'm closing the issue since labeling using unique classnames solves my problem.

Related

Parse returned element with nodeJS and Selenium

For the first time I'm trying Selenium and nodeJS. I need to parse HTML page and I'm having problem to parse returned element with XPATH again, to perform deeper search. Here is my code:
async function parse () {
let driver = await new Builder().forBrowser("firefox").build();
await driver.get("https://web-url.com");
await sleep(2000); //custom function
let elements = await driver.findElements(By.xpath("//ul[contains(#class, 'some-class')]//li"));
for (let i = 0; i < elements.length; i++) {
let timeContainer = await elements[i].findElement(By.xpath("//time[contains(#class, 'time-class')]")).getAttribute("datetime");
let innerHTML = await elements[i].getAttribute("innerHTML");
console.log(timeContainer);
}
await sleep(2000); //custom function
driver.close();
}
innerHTML variable returns correct HTML string for each element, but if I try to print timeContainer var, it prints same value for all elements. How to perform additional XPATH search for each element?
Thank you.
You need to make the XPath in this line elements[i].findElement(By.xpath("//time[contains(#class, 'time-class')]")) relative.
This is done by putting a dot . before the rest of XPath expression, as following:
elements[i].findElement(By.xpath(".//time[contains(#class, 'time-class')]"))
By default, when the page is scanned to find the match to the passed XPath expression it starts from the top of the page. This is why this line returns you the first (the same) match each time.
But when we put a dot . there, it starts searching inside the current node i.e. inside the elements[i] element.

How to check if an element is in the document with playwright?

I want to test if an element had been rendered. So I want expect that if is present. Is there a command for this?
await page.goto(‘<http://localhost:3000/>');
const logo = await page.$(‘.logo’)
// expect(logo.toBeInDocument())
If you query one element with page.$(), you can simply use:
const logo = await page.$('.logo');
if (logo) {
}
Similarly if you query multiple elements with page.$$():
const logo = await page.$$('.logo');
if (logo) {
}
Since this example returns (after awaiting) an array of element handles, you can also use property length in the condition:
const logo = await page.$$('.logo');
if (logo.length) {
}
The key in all these examples is to await the promise that page.$() and page.$$() return.
Since the use of ElementHandle (page.$(), page.$$()) is discouraged by the Playwright Team, you could use the Locator object and the count() method:
expect(await page.locator('data-testid=exists').count()).toBeTruthy();
expect(await page.locator('data-testid=doesnt-exist').count()).toBeFalsy();
If you want to check if the element is rendered (I assume you mean visible) you could use the toBeVisible assertion:
await expect(page.locator('data-testid=is-visible')).toBeVisible();

JS filter or find an element in array where owner_name matches in react

I have got the two following arrays.
producers = { owner_name: "testing123, url: "http://test.com/", logo_svg: "http://test.com/logo.svg"}, { owner_name: "testing456, url: "http://test456.com/", logo_svg: "http://test456.com/logo.svg"}
result = { owner_name: "testing123, true, false}
I am trying to find a way to extract the logo_svg url from producers array when the owner_name match. I am very new to JS, so I'm not very good at it.
I manage to put this together but unclear on how I can get the logo_svg from that.
image={producers.filter((producer) => producer.owner_name === result.owner_name)}
try image = producers.find(p => p.owner_name == result.owner_name).logo_svg
but bear in mind that if the owner name does not exist in producers then find returns undefined and so you'll get an error so you can make the whole expression more robust by doing
obj = producers.find(p => p.owner_name === result.owner_name)
obj_url = obj ? obj.logo_svg || 'default_url_location'
image = obj_url
According to MDN, filter produces an array, so that is why you cannot get a valid variable inside your image.
You can try something like:
image={producers[producers.findIndex(el => el.owner_name === result.owner_name)]}
This will find the index of the item you are looking for and pass it immediately as the index in the producers array.

setting context with list of objects as prameters in dialogflow

I have a list of values each having another KEY value corresponding to it, when i present this list to user, user has to select a value and agent has to call an external api with selected value's KEY. how can i achieve this in dialogflow?
I tried to send the entire key value pair in the context and access it in the next intent but for some reason when i set a list(array) to context parameters dialogflow simply ignoring the fulfillment response.
What is happening here and is there any good way to achieve this? I am trying to develop a food ordering chatbot where the category of items in menu is presented and list items in that menu will fetched when user selects a category, this menu is not static thats why i am using api calls to get the dynamic menu.
function newOrder(agent)
{
var categories = []
var cat_parameters = {}
var catarray = []
const conv = agent.conv();
//conv.ask('sure, select a category to order');
agent.add('select a category to order');
return getAllCategories().then((result)=>{
for(let i=0; i< result.restuarantMenuList.length; i++)
{
try{
var name = result.restuarantMenuList[i].Name;
var catid = result.restuarantMenuList[i].Id;
categories.push(name)
//categories.name = catid
cat_parameters['id'] = catid;
cat_parameters['name'] = name
catarray.push(cat_parameters)
}catch(ex)
{
agent.add('trouble getting the list please try again later')
}
}
agent.context.set({
name: 'categorynames',
lifespan: 5,
parameters: catarray, // if i omit this line, the reponse is the fultillment response with categories names, if i keep this line the reponse is fetching from default static console one.
})
return agent.add('\n'+categories.toString())
})
function selectedCategory(agent)
{
//agent.add('category items should be fetched and displayed here');
var cat = agent.parameters.category
const categories = agent.context.get('categorynames')
const cat_ob = categories.parameters.cat_parameters
// use the key in the catarray with the parameter cat to call the external API
agent.add('you have selected '+ cat );
}
}
The primary issue is that the context parameters must be an object, it cannot be an array.
So when you save it, you can do something like
parameters: {
"cat_parameters": catarray
}
and when you deal with it when you get the reply, you can get the array back with
let catarray = categories.parameters.cat_parameters;
(There are some other syntax and scoping issues with your code, but this seems like it is the data availability issue you're having.)

How to bind data to html in angular 2/4 by using thread or async

I have a Component, which have child component. at ngOnInit() I'm calling Web API and get list of data.
Initial point length of the list is 10, But it will have more.
Need to execute some method (task|process|job) in background to take rest of the data 10 by 10 in a loop which would run parallel to other task in background no matter what the user is currently doing, which component he/she is interacting with. And execute that method so that it doesn't block others.
What is the correct way to do this?
Seems like a recursive call to me !
firstResults: any[] = []; // First 10 results to show to your user
results: any[] = []; // All results
currentPosition = 0; // The current position of your last result fetched
getData() {
this.myService.getResults().subscribe(results => {
if(!this.firstResults.length) {
this.firstResults = results
}
this.results.push(...results);
this.currentPosition += results.length;
this.getData();
});
}
I don't imagine you're trying to do. But if the list is not so largger, you can get all the data and "paginate" the array
allData:any[];
page:number=0; //page is 0,1,2,3,4....
paginateData:any[]
this.httpClient.get("url").subscribe(res=>{
allData=res;
paginateData=allData.slice(10*this.page,10*(this.page+1));
}

Resources