Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
spbrantan opened this issue Jul 21, 2021 · 20 comments
Labels
D-atoms help wanted Issues looking for contributions I-enhancement

Comments

@spbrantan
Copy link

🐛 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

@ghost ghost added the needs-triaging label Jul 21, 2021
@sethidden
Copy link

sethidden commented Jul 27, 2021

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?

@diemol
Copy link
Member

diemol commented Jul 27, 2021

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

@spbrantan
Copy link
Author

spbrantan commented Jul 27, 2021

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("testuser@testuser.in");;
			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
Copy link
Author

Blue Highlighted Element is accessible to webdriver

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

@diemol

screenshot2

@titusfortner
Copy link
Member

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

@twalpole
Copy link
Contributor

twalpole commented Dec 30, 2021

Using the example from #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
Copy link
Contributor

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

@titusfortner
Copy link
Member

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.

@jasonfreec
Copy link

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

@GgStormer
Copy link

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

@titusfortner
Copy link
Member

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 — #10157

@AutomatedTester
Copy link
Member

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

@GgStormer
Copy link

Still no updates here?

@titusfortner
Copy link
Member

@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.

@diemol
Copy link
Member

diemol commented Aug 10, 2022

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

@conor-f
Copy link

conor-f commented Feb 12, 2023

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.

@NikhilVerma
Copy link

NikhilVerma commented May 30, 2023

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 - vuejs/core#8444

@whimboo
Copy link
Contributor

whimboo commented Nov 10, 2023

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

@whimboo
Copy link
Contributor

whimboo commented Nov 20, 2023

One more question. Do these vue.js components use a closed Shadow DOM? If yes, I noticed #13132 by the end of last week.

@titusfortner titusfortner added the help wanted Issues looking for contributions label Dec 28, 2023
Copy link

This issue is looking for contributors.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
D-atoms help wanted Issues looking for contributions I-enhancement
Projects
None yet
Development

No branches or pull requests