In Wix, I have a text field in a repeater that is used for navigating to other dynamic pages. The link works, but there are two problems with that. First, I have to click two times, not double click, for functioning the link. Second, I want to make the text field act as a button link, I mean be able to right click on that and choose 'open in new tab'. How can I fix these problems in my code?
Here is the code
// Navigating to related dynaic page
import wixLocation from 'wix-location';
export function ndText_click(event) {
$w("#repeater1").onItemReady(($item, itemData, index) => {
$item("#nText").onClick((event) => {
let postTypeValue = itemData.pType
wixData.query("Collection1").eq("_id", itemData._id)
.find()
.then(results => {
let item = results.items[0];
let pIDValue = item.postId;
if (postTypeValue == "R") {
wixLocation.to('/re/' + postIDValue);
} else if (postTypeValue == "L") {
wixLocation.to('/lo/' + postIDValue);
}
})
});
})
}
I suggest trying to use a button instead of the text element. You can usually style the button so it looks the same as the text element you already have. Then instead of setting the onClick, try setting the button's link and target properties.
Is there any method (didn't find in API) or solution to click on element with text?
For example I have html:
<div class="elements">
<button>Button text</button>
<a href=#>Href text</a>
<div>Div text</div>
</div>
And I want to click on element in which text is wrapped (click on button inside .elements), like:
Page.click('Button text', '.elements')
Short answer
This XPath expression will query a button which contains the text "Button text":
const [button] = await page.$x("//button[contains(., 'Button text')]");
if (button) {
await button.click();
}
To also respect the <div class="elements"> surrounding the buttons, use the following code:
const [button] = await page.$x("//div[#class='elements']/button[contains(., 'Button text')]");
Explanation
To explain why using the text node (text()) is wrong in some cases, let's look at an example:
<div>
<button>Start End</button>
<button>Start <em>Middle</em> End</button>
</div>
First, let's check the results when using contains(text(), 'Text'):
//button[contains(text(), 'Start')] will return both two nodes (as expected)
//button[contains(text(), 'End')] will only return one nodes (the first) as text() returns a list with two texts (Start and End), but contains will only check the first one
//button[contains(text(), 'Middle')] will return no results as text() does not include the text of child nodes
Here are the XPath expressions for contains(., 'Text'), which works on the element itself including its child nodes:
//button[contains(., 'Start')] will return both two buttons
//button[contains(., 'End')] will again return both two buttons
//button[contains(., 'Middle')] will return one (the last button)
So in most cases, it makes more sense to use the . instead of text() in an XPath expression.
You may use a XPath selector with page.$x(expression):
const linkHandlers = await page.$x("//a[contains(text(), 'Some text')]");
if (linkHandlers.length > 0) {
await linkHandlers[0].click();
} else {
throw new Error("Link not found");
}
Check out clickByText in this gist for a complete example. It takes care of escaping quotes, which is a bit tricky with XPath expressions.
You can also use page.evaluate() to click elements obtained from document.querySelectorAll() that have been filtered by text content:
await page.evaluate(() => {
[...document.querySelectorAll('.elements button')].find(element => element.textContent === 'Button text').click();
});
Alternatively, you can use page.evaluate() to click an element based on its text content using document.evaluate() and a corresponding XPath expression:
await page.evaluate(() => {
const xpath = '//*[#class="elements"]//button[contains(text(), "Button text")]';
const result = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);
result.iterateNext().click();
});
made quick solution to be able to use advanced css selectors like ":contains(text)"
so using this library you can just
const select = require ('puppeteer-select');
const element = await select(page).getElement('button:contains(Button text)');
await element.click()
The solution is
(await page.$$eval(selector, a => a
.filter(a => a.textContent === 'target text')
))[0].click()
Here is my solution:
let selector = 'a';
await page.$$eval(selector, anchors => {
anchors.map(anchor => {
if(anchor.textContent == 'target text') {
anchor.click();
return
}
})
});
There is no supported css selector syntax for text selector or a combinator option, my work around for this would be:
await page.$$eval('selector', selectorMatched => {
for(i in selectorMatched)
if(selectorMatched[i].textContent === 'text string'){
selectorMatched[i].click();
break;//Remove this line (break statement) if you want to click on all matched elements otherwise the first element only is clicked
}
});
Since OP's use case appears to be an exact match on the target string "Button text", <button>Button text</button>, text() seems like the correct method rather than the less-precise contains().
Although Thomas makes a good argument for contains when there are sub-elements, avoiding false negatives, using text() avoids a false positive when the button is, say, <button>Button text and more stuff</button>, which seems just as likely a scenario. It's useful to have both tools on hand so you can pick the more appropriate one on a case-by-case basis.
const xp = '//*[#class="elements"]//button[text()="Button text"]';
const [el] = await page.$x(xp);
await el?.click();
Note that many other answers missed the .elements parent class requirement.
Another XPath function is [normalize-space()="Button text"] which "strips leading and trailing white-space from a string, replaces sequences of whitespace characters by a single space" and may be useful for certain cases.
Also, it's often handy to use waitForXPath which waits for, then returns, the element matching the XPath or throws if it's not found within the specified timeout:
const xp = '//*[#class="elements"]//button[text()="Button text"]';
const el = await page.waitForXPath(xp);
await el.click();
With puppeteer 12.0.1, the following works for me:
await page.click("input[value='Opt1']"); //where value is an attribute of the element input
await page.waitForTimeout(1000);
await page.click("li[value='Nested choice 1']"); //where value is an attribute of the element li after clicking the previous option
await page.waitForTimeout(5000);
I had to:
await this.page.$eval(this.menuSelector, elem => elem.click());
You can just use the query selector.
await page.evaluate(() => {
document.querySelector('input[type=button]').click();
});
Edits ----
You can give your button a className and use that to select the button element since you know exactly what you're trying to click:
await page.evaluate(() => {
document.querySelector('.button]').click();
});
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();
I have a grid on this page. The last column is off screen to the right. I want to read the header cell text.
I saw this snippet at
https://pptr.dev/#?product=Puppeteer&version=v5.1.0&show=api-class-mouse
So I ran the code below in cucumber/puppeteer. There were no errors but nothing happened in the browser. So how do I scroll to the right using this feature or any other way.
I can't use querySelector... to get the header because the selectors don't exist until the column is visible. Please advise.
async function scrollRight() {
await this.page.mouse.wheel({ deltaX: 2500 })
}
Your scrollRight() function should have a part where the mouse hovers over the right column. In the linked example there is a page.mouse.move(x,y) which should be applied in your case as well.
To get the X Y coordinates of your column you can use elementHandle.boundingBox puppeteer function. With a simple formula you can position the cursor exactly to the center of the column.
E.g.:
async function scrollRight() {
const elem = await page.$('.last-column');
const boundingBox = await elem.boundingBox();
await page.mouse.move(
boundingBox.x + boundingBox.width / 2, // x
boundingBox.y + boundingBox.height / 2 // y
);
await page.mouse.wheel({ deltaX: 2500 });
}
To check visibility of the column you can use page.waitForSelector's visible: true option, which waits for the element to be visible, i.e. to not have display: none or visibility: hidden CSS properties. It defaults to false, so if it caused problems in your use case that it was not visible it may help.
await page.waitForSelector('.last-column', {
visible: true,
});
After switching to new window, only 'urlContains' is working.
Nothing else is happening(neither click nor elementVisible or anything)
Partial code of nightwatch:
'Order Module' : function(browser) {
browser
.useXpath()
.waitForElementVisible("#OrderTab", 20000)
.pause(5000)
.click("#OrderTab")
.pause(5000)
.useCss()
.waitForElementVisible("input[title = 'New']", 20000)
.click("input[title = 'New']")
.useXpath()
.waitForElementVisible("#OrderTextBox']", 20000)
.verify.urlContains('https://invoiceit-s.na30.visual.force.com/apex/createJobStep1?retURL=%2Fa0K%2Fo&save_new=1&sfdc.override=1')
.setValue("#OrderTextBox", "Order Name")
.pause(10000)
.click("#LinkIconForNewWindow")
.pause(10000)
.window_handles(function(result) {
this.verify.equal(result.value.length, 2, '2 windows should be open')
var handle = result.value[1]
this.switchWindow(handle)
this.verify.urlContains('https://invoiceit-s.na30.visual.force.com/_ui/common/data/LookupPage?lkfm=j_id0%3AjobForm&lknm=j_id0%3AjobForm%3Apb%3Arender%3Aj_id38%3A0%3Aj_id39&lktp=001&lksrch=')
this.useXpath()
this.waitForElementVisible("#searchBox", 20000)
})
.pause(20000)
.end() }
i am not familiar with Xpath,Can you post the screen-print of the element block? May be i can help you
PS:Dont use too much pause(), it will slow down your test program,nightwatch will check element every 500ms.
Edit 1 :The problem may be because of '#searchBox' element,is this in the first tab of your browser after you clicked Pop-up Button ? If yes, it should be like:
.window_handles(function(result) {
var home = result.value[0], // your first tab
handle = result.value[1]; // second tab
this
.switchWindow(handle)
.verify.urlContains('https://invoiceit')
.useXpath()
.switchWindow(home) // you need to switch back to first tab , even you close the pop-up.
.waitForElementVisible("#searchBox", 20000)
})
PS:my answer from this post may help.Loop is not working properly - nightwatch