selenium icon indicating copy to clipboard operation
selenium copied to clipboard

Webelements inside the vue.js's fragment are not visible to Selenium webdriver

Open spbrantan opened this issue 3 years ago • 15 comments

🐛 Bug Report

Elements with attribute 'fragment' are not visible. Impossible to interact with child elements.

Root Cause vue.js uses a concept called Fragments to create more than one root node in a Vue component. This is done for Accessibility support which is necessary to allow assistive technology like screen readers to interpret web pages and applications, More about this in -> https://blog.logrocket.com/fragments-in-vue-js/

So, basically they are creating HTML tag that would not read as a node by the DOM and called it fragments.

Hence the webelements inside such fragments are not visible for Webdriver. This is more or less like a iframe inside html page where we do switchToIFrame and interact with it. Only that for Fragments there are no ways to make it visible to the webdriver.

PFA Screenshot of DOM snapshot containing the Fragment.

image

To Reproduce

https://jsfiddle.net/brantansp/bwdkx1tg/3/#&togetherjs=eM6k1p6Iaz

Detailed steps to reproduce the behavior:

System.out.println(webDriver.findElement(By.xpath("((//div[@class='nested-list']/div)[1]//div)[4]")).getText()); //returns null

Expected behavior

System.out.println(webDriver.findElement(By.xpath("((//div[@class='nested-list']/div)[1]//div)[4]")).getText()); //should return value

Test script or set of commands reproducing this issue

WebDriverManager.chromedriver().setup(); WebDriver webDriver = new ChromeDriver(); System.out.println(webDriver.findElement(By.xpath("((//div[@class='nested-list']/div)[1]//div)[4]")).getText());

Environment

OS: Windows 10 x64 Browser: Chrome Browser version: 89 Browser Driver version: ChromeDriver 89 Language Bindings version: Java 3.141.59

spbrantan avatar Jul 21 '21 04:07 spbrantan

The article you posted is using vue-fragment, but I'm getting the same issue using the vue-frag library.

Reading vue-frag's documentation I saw the below excerpt:

#How does this work?

Vue associates vNodes with specific DOM references so once a component has mounted, the DOM nodes can be moved around and Vue will still be able to mutate them by reference. The Frag directive simply replaces the root element of a component in the DOM with it's children upon DOM insertion, and monkey-patches native properties like parentNode on the children to make Vue think they're still using the component root element.

Perhaps the monkey-patching behaves in a non-native way that makes Selenium interpret it wrong?

sethidden avatar Jul 27 '21 10:07 sethidden

Can you please provide a complete code snippet so we can have a look? Without any 3rd party dependencies.

diemol avatar Jul 27 '21 12:07 diemol

public static void main(String[] args) throws IOException {
		WebDriverManager.chromedriver().setup();
		WebDriver driver = new ChromeDriver();
		driver.manage().window().maximize();
		driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
		try {
			driver.navigate().to("https://oel-auto.pandodev.in");
			Thread.sleep(5000);
			driver.findElement(By.xpath("//label[text()='User name:']/preceding-sibling::input")).sendKeys("[email protected]");;
			driver.findElement(By.xpath("//label[text()='Password:']/preceding-sibling::input")).sendKeys("test@1234");;
			Thread.sleep(1000);
			driver.findElement(By.xpath("//button[contains(text(),'Log in')]")).click();;
			driver.findElement(By.xpath("//main[@class='container-fluid main-content']"));
			driver.navigate().to("https://oel-auto.pandodev.in/exim-master-indent-list");
			Thread.sleep(3000);
			driver.findElement(By.xpath("//span[@class='row-expand']")).click();
			Thread.sleep(2000);
			System.out.println(driver.findElement(By.xpath("(//div[contains(@Class,'grid-list')]//div)[4]/p")).getText()); //Blue-highlighted PFA. Returns text "FOB" as expected.
			System.out.println(driver.findElement(By.xpath("((//div[@class='nested-list']/div)[1]//div[contains(@class,'vendor')]//span)[last()]")).getText()); //Red-highlighted PFA. Returns text "HELLMAN LOGISTI..." but expected is "t320210730071752".
		}  catch (Exception e) {
			e.printStackTrace();
		} finally {
			driver.close();
			driver.quit();
		}
	}

In the above code, first sysout gives me expected output which is "FOB". But the second sysout gives me "HELLMAN LOGISTI..." but expected output is "t320210730071752".

If you check the xpath in DOM then it highlights the "t320210727141945" but since it is in vue fragment block selenium could not read that and instead returns the wrong text.

screenshot

spbrantan avatar Jul 27 '21 13:07 spbrantan

Blue Highlighted Element is accessible to webdriver

Red-Highlighted Element is not accessible to webdriver since that is generated by vue Fragment.

@diemol

screenshot2

spbrantan avatar Jul 27 '21 13:07 spbrantan

As discussed in #10157 Something in isDisplayed.js atom is returning false and I'm not sure where to start with it.

titusfortner avatar Dec 26 '21 07:12 titusfortner

Using the example from https://github.com/SeleniumHQ/selenium/issues/10157 the reason for this is the Vue fragment addons screwing with the parentNode property on the inserted template element. In that example the

<main fragment=​"13484d9094b" class=​"App---main---2ArFP">​…​</main>​ 

element has had parentNode modified to return an element which isn't actually in the render tree

<div fragment=​"13484d9094b">​</div>​

That alone wouldn't be so bad but the element returned as the parent doesn't have childNodes set

$0.parentNode.childNodes.length
0

So it's a 0 height, 0 width element with no children to overflow and therefore calculates as non-visible (positiveSize function returns false). Capybara is not affected by this because it had overridden the isDisplayed atom with it's own version which used parentElement to get the parent - which Vue hasn't monkeyed with

twalpole avatar Dec 30 '21 20:12 twalpole

This issue may only affect Vue 2 projects using vue-fragment - vue-frag appears to set childNodes on the container so the atom should correctly calculate visibility and Vue 3 appears to use a different method for implementing this functionality

twalpole avatar Dec 30 '21 21:12 twalpole

So, the problem is with how Vue is messing with the DOM, and presumably users can update their code to avoid this issue.

That said, this is probably a good opportunity to discuss slimming down the atom and we can address the issue on the Selenium side as well.

titusfortner avatar Dec 31 '21 04:12 titusfortner

Add $(element).css({"overflow": "visible", "position": "static"}) will resolve this issue. isDisplayed.js use this style to check whether element exists.

jasonfreec avatar Jan 29 '22 09:01 jasonfreec

Any updates on this issue? Should we expect this to be fixed sometime?

GgStormer avatar Feb 10 '22 10:02 GgStormer

Technically this is a bug in vue-fragment which removes ChildNodes from the container. vue-frag and Vue 3 both do this correctly.

If Selenium changed the atom to use parentElement instead of parentNode it could also solve it, but that may or may not be the right fix. @AutomatedTester was looking into this, but I should have had this discussion on the other Issue, because that's where the reproducible use case is — https://github.com/SeleniumHQ/selenium/issues/10157

titusfortner avatar Feb 10 '22 20:02 titusfortner

I am trying to reduce this down as there isn't reduced html show casing this... it might take some time unless someone with vue experience can give me a single page html with everything. Until then I will be trying to reduce down the html in #10157

AutomatedTester avatar Feb 11 '22 17:02 AutomatedTester

Still no updates here?

GgStormer avatar Mar 18 '22 17:03 GgStormer

@GgStormer please change/update your library or raise an issue with vue-fragment. This is not a defect in Selenium.

Additionally, the Selenium atom is not an easy thing to work with in its current state and it has some bigger limitations than this issue. Really it needs an overhaul and maybe a move away from Closure to TypeScript. This particular issue might be addressed as part of that move, or it might not, because moving from parentElement to parentNode might not be the right choice for it.

titusfortner avatar Mar 18 '22 17:03 titusfortner

@GgStormer did you raise an issue with vue-fragment? Could you please link it here?

diemol avatar Aug 10 '22 13:08 diemol

Facing this too. I understand that this is not an issue within Selenium, but is there any hacky workaround that could be used here when changing vue-fragment isn't an option? The workaround @jasonfreec mentioned doesn't seem to work currently, maybe something has changed in the past year.

conor-f avatar Feb 12 '23 12:02 conor-f

I forked the XPath libary and replaced the native implementation https://github.com/NikhilVerma/xpath-next this handles Vue 3 issues by ignoring the vue fragments and adds supports for shadow DOM elements

I also logged an issue here - https://github.com/vuejs/core/issues/8444

NikhilVerma avatar May 30 '23 12:05 NikhilVerma

Maybe this is related to #13132 (get-text atom)?

whimboo avatar Nov 10 '23 09:11 whimboo

One more question. Do these vue.js components use a closed Shadow DOM? If yes, I noticed https://github.com/SeleniumHQ/selenium/issues/13132 by the end of last week.

whimboo avatar Nov 20 '23 08:11 whimboo

This issue is looking for contributors.

Please comment below or reach out to us through our IRC/Slack/Matrix channels if you are interested.

github-actions[bot] avatar Dec 28 '23 17:12 github-actions[bot]