3

I need to input text into a field in a table that doesn't have the input tag. This XPath points out the field uniquely

//div[@id="contentBody"]//div[@row="0"]/div[contains(@class, "l4") and contains(@class, "r4")]

(one can add a trailing /div to this XPath, it makes no difference)

I can use this XPath to read this field but when I try to insert some text in the same field I get an error message:

InvalidElementStateException: Message: invalid element state: Element must be user-editable in order to clear it.

This is the HTML for the cell

<div class="slick-cell l4 r4  alignRight uppercase highint editable-cell active selected" aria-describedby="inforDataGrid731693C5" tabindex="-1" role="gridcell">
    <div class="edit-cell">2,0</div></div>

I found this question (which, BTW, does seem to be erroneously marked as a duplicate)

Element must be user-editable in order to clear it exception in selenium

The comments there seem to make sense for my situation: my XPath points to a div, not an input field (but as you can see, the div's class is "edit-cell" so...) but I don't think I have the option to point an input field. How do I work around this - how to find which element to target, so I can set its text successfully, without taht exception?

Todor Minakov
  • 19,097
  • 3
  • 55
  • 60

2 Answers2

1

The conclusion you came to is the corrected one - you cannot send input to a <div> tag, that's not how browsers usually work (an exception to the rule is given in "edit 2"). The code exception states it clearly - "Element must be user-editable in order to clear it". Regardless that the div has a class value of "edit-cell" - that just a name chosen by a developer, for illustrative proposed; it does not imply the div will be able to take input.

Most probably the UI used by the application uses this <div> for stylized presentation of the values set by the user; there is a hidden <input> element that takes the actual keyboard entries, and a javascript code updates the <div> with them.
You'll have to find that input tag, and target it for your changes.

edit1: Finding input tags by their values with JS.

Here's how to find the input tag - in the browser, (by hand) set the value to a string that would be unique for the page - for instance, "myMegaSpecialString". Then run the following js in the console:

[...document.getElementsByTagName("input")].forEach(function(el){ if (el.value == "myMegaSpecialString"){console.log(el);} })

It will print the <input> element(s) that has that "special" value.

edit2: Finding any tags by their values with JS.

According to the discussion in the comments, no <input> element was found to have this value. As I was rightfully corrected by @Andersson, under special circumstances other elements but input can receive, khm, input - they should have the contenteditable attribute, and it's inherited from parent elements - e.g. if set on the <body> tag, you can type over any element in the page.

So - a modified version of the javascript to locate "who holds our value"; thankfully, getElementsByTagName() supports wildcard for argument - that'll match any element:

[...document.getElementsByTagName("*")].forEach(function(el){ if (el.value == "myMegaSpecialString"){console.log(el);} })

It might be a bit slower - but it must print the element(s) with value property equal to "myMegaSpecialString".

edit3: Finding any tags by their text content, with JS.

The quest continues - the text you are entering does not turn up in any element's value property? No problem, let's look for it in the elements textual content.
This might be the case when you are actually editing the content of a <span>, <div> or similar tags (fingers crossed... :)).

This is a variation of the previous version - manually set the text to a value that is unique across the page (to limit the output), and run this in the console:

[...document.getElementsByTagName("*")].forEach(function(el){ if (el.textContent.indexOf("myMegaSpecialString") !== -1){console.log(el);} })

, which will (should? might? I'm not certain in anything anymore :D) print in the console all DOM elements which have that string as part of their text.

Breakdown what is what in the javascript, for future changes:

  • document.getElementsByTagName("*")] returns a all elements with that tag name as an array; the argument asterisk (*) - any tag name.
  • forEach - a loop over the collection elements, passing each one to the anonymous function in it.
  • el.textContent - returns the text content of the node (and its child nodes).
  • indexOf() - returns the position of the argument in the string, -1 if it is not present (used indexOf() vs contains() for compatibility, the later comes in ES6).
  • if the condition returns true, console.log(el) will print it in the console.
Todor Minakov
  • 19,097
  • 3
  • 55
  • 60
  • 2
    Note that actually `div` (`p`, `h1`, etc) CAN behave as `input` field and be handled with the same approaches (e.g. using `clear()`, `sendKeys()`...), but it should have `contenteditable` attribute set to `true`. I don't see that attribute in any `div` of HTML sample shared by OP, so yes it seem that OP is just trying to handle wrong element... – Andersson Jan 10 '19 at 08:44
  • Found 16 inputs on the page but none seem related to this field. Note that it is a table with plenty of cell (6 x 15 = 90 cells). Would that require 90 inputs? Any other suggestions how to find this field? (I used the element picket in Chrome's developer tools). –  Jan 10 '19 at 12:01
  • 1
    @EmLi I've updated the answer with sample steps how to find such an `` tag. – Todor Minakov Jan 10 '19 at 12:59
  • @hensti / @EmLi - try to find the element with any tag name, don't limit it to `input` tags; added that to the answer. – Todor Minakov Jan 10 '19 at 21:33
  • What do you mean? Every `p`, `a`, `li`, `em` and so on? –  Jan 10 '19 at 23:17
  • Yes, all of them. – Todor Minakov Jan 11 '19 at 04:33
  • Still can't find it. I used https://stackoverflow.com/questions/54142971/find-all-html-tags-used-on-a-webpage/54143056 to find all the tags and run your script with each of them, to no avail (the result was always _undefined_). However, running your script with `input` and a value from one of the other 16 inputs on the page returned a value that seemed correct. Could it be done the other way around, that is enter the _myMegaSpecialString_ and seach for its parent tag instead? Thank you. –  Jan 12 '19 at 08:07
  • 1
    If the entered text is not stored in in an `input` tag, but another type, then it may very well be not in a "value" property, but a part of the text content of the other type. Hold, I'll update the answer... :) – Todor Minakov Jan 12 '19 at 08:30
  • 1
    @EmLi there's an update how to find any element, that has our text in its content. See how invested I am already? :D – Todor Minakov Jan 12 '19 at 09:05
  • Now I found something but I am not sure what I found. I expanded all nodes of the return value (see here https://stackoverflow.com/questions/10464844/is-there-a-way-to-auto-expand-objects-in-chrome-dev-tools) from your JS-snippet and saved the result to a file. I then opened this file in a text editor and searched for my string (ABC123). The string occurred 19 times in the result, but they all seemed to different levels of `div`s containing the div that is visible and unworkable. I also looked for `contenteditable`, to no avail. –  Jan 12 '19 at 11:39
  • And now the real fun starts - start sending `Input Text` to reach, and see what doesn't throw the exception, and sticks. :) – Todor Minakov Jan 12 '19 at 11:45
  • However, I found something interesting. Look at this gist https://gist.github.com/l-g/9e7cd150ff6a7ab5df2b2a0a6382f076 line 259, character 554 and on (search for `inforDataGrid185841 .l0 { } .inforDataGrid185841 .r0 { }`). There seems to be CSS-styles (I am not good at this so I am just guessing) for every cell in the table I am working on. As you can see from the XPath in my question, every cell is specified by one row and two column CSS properties, something like `
    ` and on line 259 the CSS properties for this cell seems to be defined. Can I do something with this?
    –  Jan 12 '19 at 11:48
  • Does your `Input Text` suggestion really make sense? This is a tree so one level up (or so), I am gonna try to write to the row containing the cell I want to input text. Another level up I am gonna write to the whole table and then to the whole section of the page and so on. THat can't be right, can it? –  Jan 12 '19 at 11:50
  • I cannot know without seeing the app and the actual DOM structure, which you have in front of you. For all I know, there could be a key up eventHadler defined at the `` tag tracking the exact coordinates of the mouse triangulated with the Moon phase. So at this stage, I would try ***everything***, and its cousin. But that's just me, an anonymous internet person; you approach it as you deem fit :) – Todor Minakov Jan 12 '19 at 12:04
  • Did you have a look at the Gist I posted? –  Jan 12 '19 at 12:19
-1

This error message...

InvalidElementStateException: Message: invalid element state: Element must be user-editable in order to clear it.

...implies that a WebElement is in a state in which actions cannot be performed on it. Examples would include an element being obscured by another when clicking, or perhaps not being visible on the DOM.

Solution

As the element is a JavaScript enabled element you need to induce WebDriverWait to be element to be clickable and you can use either of the following solutions:

  • Wait Until Element Is Visible:

    Wait Until Element Is Visible    xpath=//div[contains(@class, 'slick-cell') and starts-with(@aria-describedby,'inforDataGrid')]/div[@class='edit-cell']    20  seconds
    Set Focus To Element    xpath=//div[contains(@class, 'slick-cell') and starts-with(@aria-describedby,'inforDataGrid')]/div[@class='edit-cell']
    # invoke click
    
  • Wait Until Element Is Enabled:

    Wait Until Element Is Enabled    xpath=//div[contains(@class, 'slick-cell') and starts-with(@aria-describedby,'inforDataGrid')]/div[@class='edit-cell']    20  seconds
    Set Focus To Element    xpath=//div[contains(@class, 'slick-cell') and starts-with(@aria-describedby,'inforDataGrid')]/div[@class='edit-cell']
    # invoke click
    
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • I tried both your solutions but still get the same error message. Since my test fails Chrome is left open and there is no problem manually clicking in this field and then enter a value. Any other suggestions? –  Jan 09 '19 at 14:15
  • What do you mean by `invoke click`? –  Jan 09 '19 at 14:15
  • Can you apply both the `Waits` one after another and try to click? – undetected Selenium Jan 09 '19 at 14:19
  • Should I send a `click element` before I send `input text`? I will try both waits. –  Jan 09 '19 at 14:22
  • Yes, send a _click_ first before sending _text_ – undetected Selenium Jan 09 '19 at 14:25
  • Now I have two different "wait", then "set focus" and "click" before I try "input text". Still the same error. Robot Framework automatically takes a screen shot when a test fails and when I look at that I can clearly see that the cell I want to input (change) value in is active with the cursor visible –  Jan 09 '19 at 14:28
  • "sending text"? I use `input text`, should I use something else? –  Jan 09 '19 at 14:35
  • Well, there seems to some default text present as **2,0**. Can you try to _clear_ it off? – undetected Selenium Jan 09 '19 at 14:42
  • It also fails with the same error message. Interestingly enough, for wait, focus and click it doesn't matter if I use the sample xpath or if I add a trailing div to it but when I use the xpath with a trailing div for input or clear, I get a different error message "Element with locator ... not found". When the test fails, if I press some keys on the keybord, characters are inserted in this field. It is very much in focus. –  Jan 09 '19 at 14:56
  • Can I use Get Element Attribute (or something else) to get some properties that might be relevant to figure out this problem? –  Jan 09 '19 at 15:10
  • @EmLi Please try sending text in upper case once e.g. "EMLI" – undetected Selenium Jan 09 '19 at 16:09
  • 2
    That's not what that error message means. It means that the element is not editable so it cannot be cleared. `.clear()` would work on an `INPUT` for instance but not a `DIV` because `INPUT`s can be edited where a `DIV` typically cannot. – JeffC Jan 09 '19 at 20:26
  • Same error message. Just curious, what did you expect to happen? –  Jan 09 '19 at 22:18