Undefined when I try to get Text on a tspan element [WDIO] - typescript - undefined

I'm not able to get the text from a Tspan element, at the moment to see the value im getting undefined.
const element = await $("g.highcharts-legend-item.highcharts-line-series.highcharts-color-undefined.highcharts-series-1 tspan");
await testElement.isDisplayed();
await testElement.getText();
undefined

we probably need more context of your issue to give you a better answer.
As far as I can see in your post, you are trying to wait for the element to be displayed with this sentence:
await testElement.isDisplayed();
But this sentence doesn't wait for the element, it only returns the current state of the element.
If you want to wait for the element, you should try with the native wdio waits:
https://webdriver.io/docs/api/element/waitForDisplayed
So that line would turn into:
await testElement.waitForDisplayed();

Related

Puppeteer Sharp - Get element containing textContent

I'm trying to work out how to efficiently grab an element from a page that contains a specific string.
The element is in an <a> tag however it's classes/ids are randomly generated.
The only way I can achieve this is by looping over every <a> tag and checking if the textContent matches.
<a>Match this text foo</a>
I've also tried using the xPath expression however I can figure out how to use the returned elements.
//a[contains(text(),'Match this text')]
Anyone have a better solution?
The page.$x(expression) method returns: <Promise<Array<ElementHandle>>>. So you can get specific element by index or just with the destructuring assignment.
For instance:
const links = await page.$x('//a[text()="Specific Text"]'); // returns: <Promise<Array<ElementHandle>>>
await links[0].click();
or even better with the destructuring assignment:
const [ link ] = await page.$x('//a[text()="Specific Text"]');
await link.click();

TestCafe - How to get the html output of an element on fail

When I run tests I cannot capture screenshots at the moment in Jenkins without some heavy work. What I would like to do is get the HTML output of the body to see what is displayed when a particular error occurs.
What I've already tried is getting the textContent, which is pretty close but I would like to get the stringified HTML if possible.
await t
.click(something)
.wait(1000)
.click(somethingElse)
.wait(1000)
.expect(mySelector.exists)
.ok(await Selector('body').textContent, { timeout: 25000 }); // if .ok fails it should print out the contents of body
I get the text but would want HTML.
Take a look at the Selector Custom Properties. There is an example of how to get innerHTML of the element.

How to check the (initial) render state (not the update state) of a component in shadow DOM

Following the question I would like to ask about the appropriate way to check the initial render status of a component (not the update status) in shadow DOM. Is there any similar to document.readyState or a promise?
I have also tried to do:
getItems() {
this.updateComplete
.then(() => {
this.nodesLists = this.shadowRoot.querySelectorAll(".name");
})
.then(...)
}
which also failed.
Tia
await this.updateComplete (or this.updateComplete.then(...)) is the correct way to wait until the element has no pending render work before e.g. querying the state of the element's rendering, so your code should generally work as long as the element is connected to the document before running getItems.
Example: https://jsbin.com/jiquhez/edit?html,console,output
Note however, that if you await updateComplete before the element is connected and the element has no properties set that would trigger a render, then updateComplete currently resolves before the first render. This may be considered an unintended bug, filed at lit-element/#594.
Note you may also want to look into using the firstUpdated lifecycle method, depending on your use case. This is a method you can implement on your class to perform one-time work following the first update/render cycle for the element (useful for e.g. selecting static nodes that won't change based on rendering).
Example: https://jsbin.com/limikas/edit?html,console,output

Ionic3 - ElementRef nativeElement.getElementsByClassName returns collection but it is inaccessible

I'm following this tutorial about Ionic and directives and everything works fine except when I try to get the FAB element using ElementRef's nativeElement.getElementsByClassName, like this:
this.fab = this.element.nativeElement.getElementsByClassName('fab')[0]
That returns undefined. The problem is when I remove the index and print the whole HTMLCollection using console.log, it shows me a complete list with all the FAB's inside the element.
Running
console.log(this.element.nativeElement.getElementsByClassName('fab'),
this.element.nativeElement.getElementsByClassName('fab')[0]);
on ngOnInit gives the following result:
What am I doing wrong here? Every part of the code related to the problem is equal to the tutorial and it's a quite recent video...
I think the reason here is that those elements are not present while you asking for them with that line:
console.log(this.element.nativeElement.getElementsByClassName('fab'),
this.element.nativeElement.getElementsByClassName('fab')[0]);
There is simple example which shows where problem can be:
console.log(document.getElementsByClassName('fab'), document.getElementsByClassName('fab')[0]);
const el1 = document.createElement('div');
el1.setAttribute('class', 'fab');
const el2 = document.createElement('div');
el2.setAttribute('class', 'fab');
setTimeout(() => {
this.abc.nativeElement.appendChild(el1);
this.abc.nativeElement.appendChild(el2);
}, 2000);
Elements are added after 2 seconds and console log is same like yours, but when you click on HTMLCollection it will evaluate and shows you those elements - of course if you click after 2 seconds(when elements are present).
If those element are really present when you asking for them console log should look more like:
HTMLCollection(2) [div.fab, div.fab]
Also, note that this little i in Google Chrome console inform you that value is evaluted just now - at the moment when you click on it.

puppeteer element.click() not working and not throwing an error

I have a situation where a button, on a form, that is animated into view, if the element.click() happens while the animation is in progress, it doesn't work.
element.click() doesn't throw an error, doesn't return a failed status (it returns undefined) it just silently doesn't work.
I have tried ensuring the element being clicked is not disabled, and is displayed (visible) but even though both those tests succeed, the click fails.
If I wait 0.4s before clicking, it works because the animation has finished.
I don't want to have to add delays (which are unreliable, and a bodge to be frank), if I can detect when a click worked, and if not automatically retry.
Is there a generic way to detect if a click() has actually been actioned so I can use a retry loop until it does?
I have determined what is happening, and why I don't get an error, and how to work around the issue.
The main issue is with the way element.click() works. Using DEBUG="puppeteer:*" I was able to see what is going on internally. What element.click() actually does is:-
const box = element.boundingBox();
const x = box.x + (box.width/2);
const y = box.y + (box.height/2);
page.mouse.move(x,y);
page.mouse.down();
sleep(delay);
page.mouse.up();
The problem is that because the view (div) is animating the element's boundingBox() is changing, and between the time of asking for the box position, and completing the click() the element has moved or is not clickable.
An error isn't thrown (promise rejected) because its just a mouse click on a point in the viewport, and not linked to any element. The mouse event is sent, just that nothing responds to it.
One workaround is to add a sufficient delay to allow the animation to finish. Another is to disable animations during tests.
The solution for me was to wait for the position of the element to settle at its destination position, that is I spin on querying the boundingBox() and wait for the x,y to report the elements previously determined position.
In my case, this is as simple as adding at 10,10 to my test script just before the click, or specifically
test-id "form1.button3" at 10,10 click
And in action it works as follows, in this case, the view is being animated back in from the left.
00.571 [selector.test,61] at 8,410
test-id "main.add" info tag button displayed at -84,410 size 116,33 enabled not selected check "Add"
test-id "main.add" info tag button displayed at -11,410 size 116,33 enabled not selected check "Add"
test-id "main.add" info tag button displayed at 8,410 size 116,33 enabled not selected check "Add"
00.947 [selector.test,61] click
It wouldn't work for an element that was continually moving or for an element that is covered by something else. For those cases, try page.evaluate(el => el.click(), element).
Generic click with timeout function inspired by Andrea's answer. This one returns as soon as the element is clickable, so won't slow down tests.
click: async function (page, selector, timeout = 30000) {
await page.waitForSelector(selector, { visible: true, timeout })
let error;
while (timeout > 0) {
try {
await page.click(selector);
return;
} catch (e) {
await page.waitFor(100);
timeout -= 100;
error = e;
}
}
throw error;
}
The page.click() returns a promise, make sure to handle it as such, but also note that you may have issues if you are not referencing it as an xpath. That's what I had to do in order to get it working. I've tried using querySelectors and interacting with the objects that way, but I ran into issues.
page.evaluate(()=>{
await Promise.all([
page.click("a[id=tab-default-2__item]"),
//The page.waitFor is set to 15000 for my personal use.
//Feel free to play around with this.
page.waitFor(15000)
]);
});
I hope this helps.
i use a helper function to handle click
click: async function (page, selector) {
//selector must to exists
await page.waitForSelector(selector, {visible: true, timeout: 30000})
//give time to extra rendering time
await page.waitFor(500)
try {
await page.click(selector)
} catch (error) {
console.log("error clicking " + selector + " : " + error ;
}
}
using page.waitFor(500) is a VERY BAD PRACTICE in a THEORICAL WORLD, but it remove a lot of false positive in the practical with complex interfaces.
i prefer to wait 500ms more than obtain a false positive.
I've just had a very similar problem: I had Puppeteer script that used to work and now suddenly click stopped working. The culprit turned out to be zoom level. It started working again once I switched zoom to 100%. Apparently, Puppeteer does not adjust click coordinates to zoom level.
For me below code did the trick
const element = await page.$('[name="submit"]')
await this.page.evaluate(ele => ele.click(), element);