Selecting an element with no particular id, class, xpath, etc - javascript

I am currently working on writing the test automation for a web application that loads as an SWF file for our end-users, but a fully functioning Javascript version exists for the sole purpose of automation.
I have means to navigate through the application with keyboard shortcuts, but when it comes to executing click commands, I have no luck at all. Upon inspecting with Firebug/Firepath, the only value that I could find was an xpath (no id exists, no class, no anything really).
The next issue is the xpath itself.
It is:
Really brittle.
.//*[#id='flow']/div[1]/div/div[7]/div/div[3]/div[4]/div/div/div/div/div/div/div/div[3]/div[1]/img
This appears to be the xpath to the image that represents the button, not the button itself.
Executing .click() commands on the above type of xpath will do nothing until you manually hover your mouse over the button (regardless of moveToElement commands), where it will "click" the image but no functionality will run.
So I'm wondering after digging around in the actual JavaScript looking for identifiers, is there any way to select an element through any other properties? Or is there any way I can better "identify" a function? Perhaps find the xpath to the button that the image represents?
Using JUnit and Java, if that helps.
Thanks

Apparently, my comments answered the OP's question, so here goes, for reference sake:
If you need "the button itself", as you wrote in your question, use Inspect Element from the browser to find out what the actual element is you need and then simply remove from the right-hand side of your XPath expression enough axis steps until the part that remains selects the ancestor element that is the actual button element.
Now you should be able to send it a click event.
I'm afraid there won't be much we can do about the XPath statement to be "brittle", simply because you do not have an identifier to go on. That means that if the structure of the page changes, you will have to change the XPath (unless some of the ancestor elements have some notable identifiers).

Related

Despite having onkeydown event handlers, Firefox is telling me "Focusable elements should have interactive semantics"

I'm trying to add accessibility to my website, but I'm running into a weird issue with Firefox's accessibility scanner. I've put onclick and onkeydown handlers on my interactive elements, but onkeydown doesn't seem to be enough; Firefox is telling me "Focusable elements should have interactive semantics." The [Learn More] link provided in the notice sends me to this page, which recommends a keydown or keyup event handler... which is already in the code. What am I missing here?
Here's a sample code I used to test the issue:
<img
src="https://www.google.com/logos/doodles/2022/seasonal-holidays-2022-6753651837109831.8-ladc.gif"
alt="Google"
title="Google"
tabindex="0"
onclick="alert('Google')"
onkeydown="alert('Google')">
In Firefox 108's Developer Console, under the Accessibility tab, using "Check for issues" for all issues gives me a notice on this element, saying "Focusable elements should have interactive semantics." despite the fact that onkeydown is defined and performs the same action as onclick.
EDIT: This question is not a duplicate of What does "Focusable Elements Should Have Interactive Semantics" mean in a figure? because that question regards an incorrect use of the role and aria-label attributes in a link, whereas my problem (as explained by the answer I've marked as correct, thank you!) was completely missing the role attribute and better resolved by wrapping this in a button.
The key thing here in the error statement:
Focusable elements should have interactive semantics.
is the word "semantics".
semantics refers to what an element actually "means" or "does" in the context of the page. Specifically, we are talking about the role of an element as exposed to the operating system's underlying accessibility API. An img element (providing it as alt text as you have correctly given it) has default role img (which is obvious, I hope - that comes from the table on this page), which you can view in the ARIA specification. I won't quote anything from that as what's most important in this context is what's not there. Basically, an image is not expected to be interactive - unlike elements with different roles such as button.
If you were to actually test your page with a screenreader - something I would highly recommend every front end developer does with some regularity - you would see the problem. Yes, knowing how your application behaves, you can press a key and have the intended functionality happen. But there's nothing in your HTML that implies that anything interactive will happen here - it's just an image, they're not expected to be interactive. So a screenreader will not announce anything to the effect of "oh by the way you can click on this" (or press enter or whatever) (actually from memory NVDA says "clickable" for elements with click handlers - but other screenreaders such as Voiceover won't, so you shouldn't rely on this. Even knowing the element is "clickable", users without a mouse or who are unable to see where they're clicking will not know how to trigger this "click".).
The solution is to give your element the correct semantics. Since this does something when you click it, and that thing isn't navigation to a new page, it strikes me that the correct role would be button. So you could do this, adding the correct role attribute:
<img
src="https://www.google.com/logos/doodles/2022/seasonal-holidays-2022-6753651837109831.8-ladc.gif"
alt="Google"
title="Google"
tabindex="0"
onclick="alert('Google')"
onkeydown="alert('Google')"
role="button">
This would be a big improvement - I'd expect it to no longer bring up the error you're seeing, as well as to be much more usable to users with assistive technologies.
But, whenever you find yourself using a role which corresponds to an alternative HTML element - and in this case it will not surprise you to know that the button role is automatically applied to the HTML <button> element - you should ask yourself if there's any good reason why you can't just use the correct element.
Only you can answer that question, but I'd be very surprised if such a reason existed. At worst, using a <button> may change your styles, but I'm assuming you have control of the CSS so you can update that as needed.
You do still want the image there because that's seen by sighted users so screenreader users should be told about it the same. What I would do is wrap the image in a button, attach the onclick handler to that button, and remove both the tabindex and the onkeydown, because one of the nice things about using the correct HTML element is that browsers handle most of the behaviour for you, in this case making buttons be in the tab order and trigger their click effect whenever Enter or Space is pressed when focused.
So this would be in my opinion the best way to code this:
<button onclick="alert('Google')">
<img
src="https://www.google.com/logos/doodles/2022/seasonal-holidays-2022-6753651837109831.8-ladc.gif"
alt="Google"
title="Google"
>
<button>
and you should get everything you need, including being accessible "for free" (and the button having the image's alt text as its "accessible name"), and at worst just have to add/update some styles.

Testcafe can see button but not click on it

In a testcafe test how can I click on an element that is clearly clickable (with t.debug() I'm able to click on the element) and visible without using ClientFunction, or t.eval -- these "workarounds" recommended in testcafe's github issues do not work.
Some additional considerations:
the code I'm testing is Angular 1.7.
the Selector is verified as correct (and I tried various types of selectors)
testcafe version 1.8.4
I've tried various t.wait times before and after selection and click
I've tried changing the element type (<button> to <div>, etc)
Try waiting for the element to be visible before clicking
await element.with({ visibilityCheck: true }).with({timeout: 10000});
Here are some typical problems with unclickable elements, I know the link is for Selenium issues, but some solutions can be used regardless of the technology used.
If you already tried with various waiting to be visible/clickable solutions, the next thing that you might want to check is if you have multiple elements with the same id, one of them being invisible, so TestCafe is unable to uniquely identify the right element. In that case, you will need to improve the locator.
Another thing to consider is that the element might be out of the viewport (when not debugging). In that case, try changing the window size (or maximizing it) or moving to element.

Why does the value attribute from an input[text] is different from what the browser renders?

I'm getting this strange behaviour in a very specific set of inputs on one my applications. I create some inputs and I can see them as I created them on the Elements panel (google chrome), but the way the browser renders it is different.
Note how the input is renders with comma instead of a point, but the value attribute uses a point
When I get a referente to that element using the selector API, I get this:
A direct reference to the Dom Element will return 11,00. The tag has 11.00 and jQuery returns the 11,00. I've removed all js that interacts with this element (masks, events, etc) and the issue still happens.
I've been swearing at the DOM for a day and a half, but I know this is most probably an issue with my application. What bothers me the most is that the browser does not honor what I see in the elements panel.
This is the small piece of code that creates the element, stopped right before the tag is created. Note the variables values in the right panel:
Could someone give me a hint about what could be causing this difference in between element, view and attributes? If possible, I'd like to know what/how this is happening in depth.
Thank you in advance

Best way to identify manually selected element

I'm doing a chrome extension that allows user to click on some links. The extension should analyze the page (1 time per 1 hour, server-side) and if the link is changed - notify. The extension should work on any "normal" web page.
My chrome extension just allows to select element. All other job is done on server side.
What I tried:
ID - the most obvious way, but a lot of web pages don't use ID for elements.
a tag with exact href. But I also need some notifications for position changes. And there is a big chance that the extension will work not only with links.
e.path - Should I keep it like html.child(0).child(2).child(1)... or identify them with their classes and types?
So, is there any good way to identify an element on some unknown page? How should I serialize the identifier?
I'm ok if it will not work after any major changes to html template.
Thank you :)
Assuming I'm reading your question right: You want to select some element on a webpage and have a server re-read that element on a timer loop to see if the element (text, for example) has changed.
Something like an ID would be easiest but, barring that, you might consider XPath, a standard for navigating through XML (or HTML) documents.
W3Schools Reference on XPath
Just a note: from a design perspective, this is going to be challenging because you don't have control over other pages. There's nothing stopping a website from changing IDs, reorganizing elements, etc.

How to send keystrokes to a textbox that remains hidden until it's clicked on?

I'm trying to log into this site. But I can't even fill the textbox with the username.
I tried:
implicit and explicit waits
executing JavaScript
Structure of the textbox
The username is enclosed within a td tag, which can be located using its width attribute. The textbox itself is represented by two input tags. The first one is hidden. Both have style attributes. The first one has an initial value of display: none;, which is changed to display: inline-block; when I click on the textbox. And the style attribute of the second input tag is set to display: none;. Again, both these are toggled once the username textbox loses focus.
Waits
I tried using both implicit and explicit waits. The code implementing explicit wait resulted in a TimeoutException error:
wait = WebDriverWait(driver, 10)
elem_username = wait.until(EC.visibility_of_element_located((By.ID, 'txt_username')))
Executing JavaScript
Both the input tags have onfocus and onchange attributes. I didn't bother about the latter (my goal was to get keystrokes into the textbox first). The onfocus has a value of SetEnd(this). So, I tried to execute that.
The problem I had is that I couldn't find any documentation that could help me executing JavaScript. So I looked at a few related answers here at StackOverflow. I tried this first:
elem_username = driver.find_element_by_id('txt_username')
driver.execute_script('SetEnd(this)')
Of course, I knew it wouldn't work, because I was not referring to the element. So after browsing a few more questions, I learnt that execute_script took arguments. So I modified the code, which raised an error that said something like the script had no function like this:
driver.execute_script('arguments[0].SetEnd(this)', elem_username)
Then, I saw an answer using 'click()' inside the execute_script; so I tried that, too:
driver.execute_script('arguments[0].click()', elem_username)
The 'click()', I guess, is only for buttons. But since I had to "click" in the textbox to bring it in focus, I thought it would work. It didn't.
This is the last line I executed in all of my attempts, which, without an exception, kept raising ElementNotInteractableException error:
elem_username.send_keys('blahblahblah')
Requirements
The Q&As on this site would, of course, be excellent, if I had a bit more experience. For instance, there are several answers demonstrating explicit wait, but most of them were aimed at solving the OP's problem, and hence they included only the relevant parts of code. It was tough for me to understand them.
I want to solve this problem (logging into the site), but I also want to learn working with selenium properly. I haven't worked with it earlier in any other languages such as Java. The official documentation is good, but I couldn't solve this problem using that. So I want a more beginner-friendly tutorial.
This worked for me:
# Force the element to be displayed (noticed style.display = "none")
# Yes, I know you can see it, but selenium thinks it's not displayed.
driver.execute_script('document.getElementsByName("txt_username")[0].style.display="block"')
elem_username.send_keys('your name')
So you've outlined everything well and you seem to have a good understanding of it. The trick is to click the second input first, then send keys to the first input since it is then focused. (I did find that clicking the containing td also worked for Chrome, but browsers are different in that aspect and the intent of this page is that you click on the second input). A simple solution is as follows:
real, readonly = driver.find_elements_by_css_selector("input[onfocus*='SetEnd']")
readonly.click()
real.send_keys('hi')
If you have any questions about that, I can try to help.

Categories