14

I am trying to use Selenium to click on a ::after pseudo element. I realize that this cannot be done through the WebDriver directly, but cannot seem to figure out a way to do so with Javascript.

Here is what the DOM looks like:

<em class="x-btn-split" unselectable="on" id="ext-gen161">
    <button type="button" id="ext-gen33" class=" x-btn-text">
        <div class="mruIcon"></div>
        <span>Accounts</span>
    </button>
    ::after
</em>

This is what the above element looks like. The Left hand side of the object is the 'button' element and the :after element is the right hand side with the arrow which would bring down a dropdown menu when clicked. As you can see that the right hand side has no identifiers whatsoever and that is partially what is making this difficult to do.

Element to be clicked

I have seen these two links in stackoverflow and have attempted to combine the answers to form my solution, but to no avail.

Clicking an element in Selenium WebDriver using JavaScript
Locating pseudo element in Selenium WebDriver using JavaScript

Here is one my attempts:

string script = "return window.getComputedStyle(document.querySelector('#ext-gen33'),':before')";
IJavaScriptExecutor js = (IJavaScriptExecutor) Session.Driver;
js.ExecuteScript("arguments[0].click(); ", script);

In which I get this error:

System.InvalidOperationException: 'unknown error: arguments[0].click is not a function
  (Session info: chrome=59.0.3071.115)
  (Driver info: chromedriver=2.30.477700 (0057494ad8732195794a7b32078424f92a5fce41),platform=Windows NT 6.1.7601 SP1 x86_64)'

I've also tried using the Actions class in Selenium to move the mouse in reference to the left hand side, similar to this answer as well. I think it may be because I don't know what the offset is measured in and the documentation doesn't seem to give any indication. I think it is in pixels??

Actions build = new Actions(Session.Driver);
build.MoveToElement(FindElement(By.Id("ext-gen33"))).MoveByOffset(235, 15).Click().Build().Perform();

This attempt seems to click somewhere as it gives no errors, but I'm not really sure where.

I'm attempting to automate Salesforce (Service Cloud) in c# if that helps.

Maybe someone can offer a solution?

Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
Jand
  • 406
  • 2
  • 7
  • 22
  • 1
    Click the element the pseudo element is attached to – guest271314 Aug 01 '17 at 01:09
  • Can we try another approach such as image recognising the down arrow and click. We can use sikuli to get it done. We need to download the library and use it.Screen scr = new Screen(); Pattern Image1 = new Pattern("D:/>SikuliImages/SignupClick.png"); scr.Click(Image1, true); where image1 is the cropped dropdown. – santhosh kumar Aug 01 '17 at 04:03
  • @guest271314 I had tried that and it isn't that simple, then the left hand side of the button gets clicked, hence my problem. I need to click the right hand side. – Jand Aug 01 '17 at 05:04
  • @Jand It is not possible, as far as am aware of, to programmatically dispatch a `DOM` event to a CSS pseudo element or pseudo class. – guest271314 Aug 01 '17 at 05:14
  • @santhoshkumar interesting solution.The Sikuli website doesn't list that it is written in c# though. – Jand Aug 01 '17 at 05:19
  • We are having sikuli implementation for C# also. Check here dude https://sourceforge.net/projects/sikuli4net/ – santhosh kumar Aug 01 '17 at 05:22

5 Answers5

9

I've encounter the same problem while writing Selenium tests for Salesforce and managed to solve it by direct control over mouse using Actions.

Wrapper table for this button has hardcoded width of 250px, and you have spotted that. To locate where the mouse is, you can use contextClick() method instead of Click(). It simulates right mouse button so it will always open browser menu.

If you do:

Actions build = new Actions(Session.Driver);
build.MoveToElement(FindElement(By.Id("ext-gen33"))).ContextClick().Build().Perform();

you will spot that mouse moves to the middle of the WebElement, not the top left corner (I thought that it does too). Since that element width is constant, we can move mouse just by 250 / 2 - 1 to the right and it will work :) code:

Actions build = new Actions(Session.Driver);
build.MoveToElement(FindElement(By.Id("ext-gen33"))).MoveByOffset(124, 0).Click().Build().Perform();
4

For those who are trying to do this in Python, the solution is below:

elem= driver.<INSERT THE PATH TO ELEMENT HERE> ActionChains(driver).move_to_element_with_offset(elem,249,1).click().perform()

Basically here I'm finding my element in the DOM and assigning to a WebElement. The WebElement is then passed the method move_to_element_with_offset as a param.

I got the px values for the element from developer tools.

PS: use this import- from selenium.webdriver.common.action_chains import ActionChains

You can read more about Action chain class and its method move_to_element_with_offset here: http://selenium-python.readthedocs.io/api.html.

Hope this helps.

AmrithS
  • 59
  • 3
2

Maciej'a answer above worked with WebDriver, but not with the RemoteWebDriver (Selenium 3.12.0) against Firefox V.56. We needed a solution that worked for both local and remote. Ended up using keyboard shortcuts to invoke the Navigation Menu drop down. As an added benefit, this also removes the need to use offsets.

String navigationMenuDropdownShortcutKeys = Keys.chord(Keys.ESCAPE, "v");

new Actions(driver)
 .sendKeys(navigationMenuDropdownShortcutKeys)
 .perform();
perNalin
  • 161
  • 9
  • Could you please explain how esc + v selects the menu? If it works, then it will be very helpful. Please explain. – Sun Shine Jul 26 '18 at 16:54
  • It certainly works :-) try it out. Salesforce by default allows for a number of keyboard shortcuts. This simply invokes the key combination to drop down the menu. – perNalin Jul 28 '18 at 14:42
  • It is a SalesForce shortcut, got it. My question was in general, not specific to SalesForce. Thanks – Sun Shine Jul 29 '18 at 16:14
2

After going through numerous article and the blogs I figured out the way to determine how to detect the Pseudo element in the DOM in the Selenium. And validate based on the certain conditions if it is present or no.

Step 1

Find the path to the parent element which consist the pseudo element and pass under the findElement as shown below

WebElement pseudoEle = driver.findElement(path);

Step 2

String display = ((JavascriptExecutor)getWebDriver()).executeScript("return window.getComputedStyle(arguments[0], ':after').getPropertyValue('display');",pseudoEle).toString();

In the above line of code pass the desired Pseudo code in the place of ":after" (In my case I was looking for 'after') and the property value which is changing based on the pseudo code is present or no (In my case it was 'display').

Note: When the pseudo element was present javascript code return 'Block' which in turn I saved in the display field. And use it according to the scenario.

Steps to determine the right property value for your case

  1. Inspect the element.
  2. Navigate to the parent element of the pseudo code.
  3. Under the Styles tab figure out the field(Green in color) whose value change when the pseudo code is present and when not present.

I am sure this would help you to the great extent. Kindly like and support, would encourage me to post more solutions as such.

Thanks!

1

Im going to provide an alternative that may work for some scenarios, at least it did the trick for me, and is relatively easy to implement in any language using selenium via a JS script.

In my scenario there was an ::after pseudoelement containing the functionality of a button. This button was contained in a position relative to another element under it.

So I did the following:

  1. Get the element that I can, in this question scenario would be that span.
  2. Get the coordinates of the element.
  3. Calculate the coordinates realtive to that element of the pseudoelement you want to click.
  4. Click on those coordinates.

This is my code using perl, but I'm sure you can do the same in any language:

my $script="
function click_function(x, y)
{
    console.log('Clicking: ' + x + ' ' + y);

    var ev = new MouseEvent('click', {
        'view': window,
        'bubbles': true,
        'cancelable': true,
        'screenX': x,
        'screenY': y
    });

    var el = document.elementFromPoint(x, y);

    el.dispatchEvent(ev);
}

var element = document.getElementById('here_put_your_id'); //replace elementId with your element's Id.
var rect = element.getBoundingClientRect();
var elementLeft,elementTop; //x and y
var scrollTop = document.documentElement.scrollTop?
               document.documentElement.scrollTop:document.body.scrollTop;
var scrollLeft = document.documentElement.scrollLeft?            

    document.documentElement.scrollLeft:document.body.scrollLeft;
elementTop = rect.top+scrollTop;
elementLeft = rect.left+scrollLeft;
console.log('Coordiantes: ' + elementLeft + ' ' + elementTop)

click_function(elementLeft*1.88, elementTop*1.045) // here put yor relative coordiantes
    ";
    $driver->execute_script($script);
nck
  • 1,673
  • 16
  • 40