Is there a way to traverse through html elements in playwright like cy.get("abc").find("div") in cypress?
In other words, any find() equivalent method in playwright?
page.locator("abc").find() is not a valid method in playwright though :(
If you assign the parent element handle to a variable, any of the findBy* functions (or locator) will search only in the parent element. An example below where the parent is a div, the child is a button, and we use .locator() to find the elements.
test('foo', async ({ page }) => {
await page.goto('/foo');
const parent = await page.locator('div');
const child = await parent.locator('button');
});
You can just combine the selectors, this will resolve to div below abc
page.locator("abc div")
Let's consider the website https://www.example.com with the following HTML
<body style="">
<div>
<h1>Example Domain</h1>
<p>This domain is for use in illustrative examples in documents. You may use this
domain in literature without prior coordination or asking for permission.</p>
<p>
More information...
</p>
</div>
</body>
As mentioned by #agoff, you can use nested locator page.locator('p').locator('a') or you can specify the nested selector directly in the locator page.locator('p >> a')
// #ts-check
const playwright = require('playwright');
(async () => {
const browser = await playwright.webkit.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://www.example.com/');
// Here res1 and res2 produces same output
const res1 = await page.locator('p').locator('a'); // nested locator
const res2 = await page.locator('p >> a'); // directly specifying the selector to check the nested elements
const out1 = await res1.innerText();
const out2 = await res2.innerText();
console.log(out1 == out2); // output: true
console.log(out1); // output: More information...
console.log(out2); // output: More information...
// Traversal
const result = await page.locator('p');
const elementList = await result.elementHandles(); // elementList will contain list of <p> tags
for (const element of elementList)
{
const out = await element.innerText()
console.log(out);
}
// The above will output both the <p> tags' innerText i.e
// This domain is for use in illustrative examples in...
// More information...
await browser.close();
})();
Since you mentioned that you need to traverse through the HTML elements, elementHandles can be used to traverse through the elements specified by the locator as mentioned in the above code.
Related
I'm developing a Nike SNKRS BOT to buy shoes with Puppeteer and Node.js.
I'm having issues to distinguish and .click() Size button screenshot of html devtools and front end buttons
That's my code: i'm not experienced so i have tried everything
const xpathButton = '//*
[#id="root"]/div/div/div[1]/div/div[1]/div[2]/div/section[1]/div[2]/aside/div/div[2]/div/
div[2]/ul/li[1]/button'
const puppeteer = require('puppeteer')
const productUrl = 'https://www.nike.com/it/launch/t/air-max-97-coconut-
milk-black'
const idAcceptCookies = "button[class='ncss-btn-primary-dark btn-lg']"
async function givePage(){
const browser = await puppeteer.launch({headless: false})
const page = await browser.newPage();
return page;
}
async function addToCart(page){
await page.goto(urlProdotto);
await page.waitForSelector(idAcceptCookies);
await page.click(idAcceptCookies,elem => elem.click());
//this is where the issues begin
//attempt 1
await page.evaluate(() => document.getElementsByClassName('size-grid-
dropdown size-grid-button"')[1].click());
//attempt 2
const sizeButton = "button[class='size-grid-dropdown size-grid-button']
button[name='42']";
await page.waitForSelector(sizeButton);
await page.click(sizeButton,elem => elem.click());
}
//attempt 3
await page.click(xpathButton)
//attempt 4
document.evaluate("//button[contains ( ., '36')]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
async function checkout(){
var page = await givePage();
await addToCart(page)
}
checkout()
Attempt number 2 looks like the best approach, except your selector is wrong. The button does not have a name attribute, according to your screenshot, so you will need another approach, closer to attempt 3.
You can use puppeteer to select an element by with xpath, and xpath allows you to select by an element's text content.
Try this:
await page.waitForXPath('//button[contains(text(), "EU 36")]')
const [button] = await page.$x('//button[contains(text(), "EU 36")]')
await button.click()
Because the xpath selector is returning an array of element handles, I destructure the first element in the array (which should be the only match), and assign it a value of button. That element handle can now be clicked.
I am attempting to click an element in a shadow dom. I am using Selenium Webdriver Nodejs. I currently get access the shadow root. I also can find an element. So I thought the last bit clicking it would be straight forward but cant find out how. There is not much support for Shadow Dom with nodejs.But cant work out how to click that element.
async function getshadowDOM(driver) {
// This gets the 1st Shadow Root
const shadowHost = await driver.findElement(By.css("#container > div.sf_common_comp-Page__header > div > xweb-shellbar"),3000);
const shadowRoot = await driver.executeScript("return arguments[0].shadowRoot", shadowHost);
//This gets the 2nd Shadow Root
const shadowHost2nd = await shadowRoot.findElement(By.css("#shellbarContainer"));
const shadowRoot2 = await driver.executeScript("return arguments[0].shadowRoot",shadowHost2nd);
//Clicks the element in 2nd Shadow DOM
const elem = await shadowRoot2.findElement(By.css("div > div.ui5-shellbar-overflow-container.ui5-shellbar-overflow-container-left > button"));
await elem.click();
}
module.exports = getshadowDOM;
I think i found a solution.
Used nodeJs + selenium.
It's work bad with return so i handle like this.
const shadowHost = await driver.findElement(By.css("body > div.project > sn-component-va-web-client"));
const shadowRoot = await driver.executeScript("return arguments[0].shadowRoot", shadowHost);
const elem = await shadowRoot.findElement(By.css("div.menu-item.contact-support-clicker"));
elem.click();
in my case its works.
I need to check that a button is disabled (checking for a last page of a table). There are two with the same id (top and bottom of the table).
const nextPageButtons = await this.page.$$('button#_btnNext'); // nextPageButtons.length is 2, chekced via console.log
const nextPageButtonState = await nextPageButtons[0].isDisabled();
But when I do the above I get: elementHandle.isDisabled: Unable to adopt element handle from a different document.
Why doesn't this work?
So, this works:
const nextPageButtons = await this.page.$$('button#_btnNext');
const nextPageButton1 = await nextPageButtons[0];
const nextPageButton1State = await nextPageButtonsState.isDisabled();
I'm trying to get a text from a modal on Chrome. Using the console, I can get the inner text as follows:
document.querySelector('.my-form > a').innerText
// returns http://a-url.com
Now, on my test, I can evaluate the element using
const myText = Selector('.my-form > a').innerText;
await t
.expect(myText).contains('url');
and I can even click on that URL
await t.click(myText);
but I cannot put that inner text to a variable, for instance. I tried using a ClientFunction from this post
const getUrl = ClientFunction(() => document.querySelector('.my-form > a').innerText);
test('My Test', async t => {
const text = await getUrl();
console.log(text);
});
// this results in
// TypeError: Cannot read property 'innerText' of null
and tried using a plain Selector as this post suggests
const text = Selector('.my-form > a').innerText;
const inner = await text.textContent;
console.log(inner);
// prints: undefined
How to extract a text from an element? I understand that t.selectText is limited in this scenario, right?
From the documentation you want:
const text = await Selector('.my-form > a').innerText;
That will do the trick:
const paragraph = Selector("p").withText("possible entries");
const extractEntries = await paragraph.textContent;
I would like to get all the elements in my DOM with a specific css path:
var elements = await chromeless.evaluate(() => document.querySelectorAll('div a'))
console.log(elements[0].innerHTML)
console.log(elements[1].innerHTML)
but this code gives me the error "Object reference chain is too long" on the first line
This code works though:
var element = await chromeless.evaluate(() => document.querySelectorAll('div a')[0].innerHTML)
console.log(element)
and I could potentially use a loop to retrieve them all but I have no idea how many elements have this css in my DOM so I don't know how many times to loop.
What's the correct syntax to get all the desired elements?
const elements = await chromeless.evaluateHandle(() => {
const allOccurances = [...document.querySelectorAll("div a")];
const data = allOccurances.map((node) => node.innerHTML);
return data;
});
const response = await elements.jsonValue();
console.log(response);
Instead of chromeless we can use page by creating a page as per puppeteer documentation https://pptr.dev/#?product=Puppeteer&version=v13.1.3&show=api-class-page