You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a test tries to interact with a website that contains DocumentFragments, the browser module will error out or timeout. The error that is returned is usually something like:
ERRO[0000] Uncaught (in promise) GoError: waiting for selector "//p[@id=\"inDocFrag\"]": DOMException: Failed to execute 'evaluate' on 'Document': The node provided is '#document-fragment', which is not a valid context node type.
at XPathQueryEngine.queryAll (__xk6_browser_evaluation_script__:131:25)
at InjectedScript._queryEngineAll (__xk6_browser_evaluation_script__:158:42)
at InjectedScript._querySelectorRecursively (__xk6_browser_evaluation_script__:214:20)
at InjectedScript._exploreShadowDOM (__xk6_browser_evaluation_script__:243:38)
at InjectedScript._exploreShadowDOM (__xk6_browser_evaluation_script__:256:35)
at InjectedScript._exploreShadowDOM (__xk6_browser_evaluation_script__:256:35)
at InjectedScript._exploreShadowDOM (__xk6_browser_evaluation_script__:256:35)
at InjectedScript._querySelectorRecursively (__xk6_browser_evaluation_script__:228:34)
at InjectedScript.querySelectorAll (__xk6_browser_evaluation_script__:651:25)
at predicate (__xk6_browser_evaluation_script__:921:29)
at github.com/grafana/xk6-browser/browser.mapPage.func16 (native)
at file:///Users/ankuragarwal/go/src/github.com/grafana/xk6-browser/e2e/shadow-and-doc-frags.js:25:23(20) executor=shared-iterations scenario=browser
I've seen this occur with waitFor and waitForSelector APIs. Other APIs that work with the same underlying injected script will also suffer from the same issue, i.e. any API that needs to run an XPath query against the DOM.
xk6-browser version
v1.4.1
OS
NA
Chrome version
NA
Docker version and image (if applicable)
NA
Steps to reproduce the problem
Use the following html file:
HTML file
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport" content="width=device-width, initial-scale=1.0"><title>DocumentFragment and ShadowRoot Test page</title></head><body><h2>DocumentFragment and ShadowRoot Test page</h2><divid="docFrag"></div><!-- Element that will host the Shadow DOM --><divid="shadowHost"></div><script>functionaddDocFrag(){constcontainer=document.getElementById('docFrag');constfragment=document.createDocumentFragment();// Add some additional text in a paragraphconstparagraph=document.createElement('p');paragraph.id='inDocFrag';// Set the id of the divparagraph.textContent='This text is added via a document fragment!';fragment.appendChild(paragraph);// Append the fragment to the containercontainer.appendChild(fragment);}functionaddShadowDom(){constshadowHost=document.getElementById('shadowHost');// When mode is set to closed, we cannot access internals with JS.// We will need to create a custom element that exposes these// internals with getters and setters.constshadowRoot=shadowHost.attachShadow({mode: 'open'});// Create a DocumentFragment to add to the Shadow DOMconstfragment=document.createDocumentFragment();// Add some styled content to the fragmentconststyleElement=document.createElement('style');styleElement.textContent=` p { color: blue; font-weight: bold; } `;fragment.appendChild(styleElement);constparagraphElement=document.createElement('p');paragraphElement.id='inShadowRootDocFrag';paragraphElement.textContent='This is inside Shadow DOM, added via a DocumentFragment!';fragment.appendChild(paragraphElement);// Append the DocumentFragment to the Shadow DOM.shadowRoot.appendChild(fragment);}functiondone(){// Create a new div element which will reside in the original Document.constdoneDiv=document.createElement('div');doneDiv.id='done';doneDiv.textContent="All additions to page completed (i'm in the original document)";// Append it to the original Document.document.body.appendChild(doneDiv);}addDocFrag();addShadowDom();done();</script></body></html>
Run this test with ./k6 run test.js:
Test file
import{browser}from'k6/experimental/browser';import{check}from'k6';exportconstoptions={scenarios: {browser: {executor: 'shared-iterations',options: {browser: {type: 'chromium',},},},},thresholds: {checks: ['rate==1.0'],},};exportdefaultasyncfunction(){constpage=browser.newPage();awaitpage.goto('http://localhost:81/static/doc-frag.html');page.waitForSelector('//p[@id="inDocFrag"]',{timeout: 10000,state: 'attached',});check(page,{'inDocFrag': p=>p.locator('//p[@id="inDocFrag"]').innerText()=='This text is added via a document fragment!',});page.waitForSelector('//p[@id="inShadowRootDocFrag"]',{timeout: 10000,state: 'attached',});check(page,{'inShadowRootDocFrag': p=>p.locator('//p[@id="inShadowRootDocFrag"]').innerText()=='This is inside Shadow DOM, added via a DocumentFragment!',});page.waitForSelector('//div[@id="done"]',{timeout: 10000,state: 'attached',});check(page,{'done': p=>p.locator('//div[@id="done"]').innerText()=="All additions to page completed (i'm in the original document)",});page.close();}
Which will result in the error specified in the summary.
Expected behaviour
The test passes and all checks pass:
✓ inDocFrag
✓ inShadowRootDocFrag
✓ done
Actual behaviour
Errors on the first waitForSelector with the error in the summary.
The content you are editing has changed. Please copy your edits and refresh the page.
The stack trace points here in the injected_script.js file.
After close inspection of the error it turns out that the root in question when this error occurs is a DocumentFragment. Looking at what DocumentFragment implements its clear that it:
Doesn't implement evaluate which is what XPathQueryEngine works with;
therefore doesn't work with XPath selectors.
The possible ways forward were:
Can we convert the XPath selector to a CSS selector? This likely requires an external dependency.
Can we convert the DocumentFragment into a Document?
After some exploration it turns out that we can convert DocumentFragment into a Document without adding any external dependencies.
ERRO[0004] Uncaught (in promise) GoError: waiting for"//div[@id=\"splash-screen\"][@class=\"animate finished\"]": Promise was collected
running at github.com/grafana/xk6-browser/common.(*Locator).WaitFor-fm (native)
So a promise isn't being handled and was lost.
Debugging the injected_script.js file, it would seem that we're not handling errors here, and so it is being swallowed which prevents us from understanding the cause of the error. We should try/catch in the polling function and reject when an error occurs.
Brief summary
When a test tries to interact with a website that contains
DocumentFragment
s, the browser module will error out or timeout. The error that is returned is usually something like:I've seen this occur with
waitFor
andwaitForSelector
APIs. Other APIs that work with the same underlying injected script will also suffer from the same issue, i.e. any API that needs to run an XPath query against the DOM.xk6-browser version
v1.4.1
OS
NA
Chrome version
NA
Docker version and image (if applicable)
NA
Steps to reproduce the problem
Use the following html file:
HTML file
Run this test with
./k6 run test.js
:Test file
Which will result in the error specified in the summary.
Expected behaviour
The test passes and all checks pass:
✓ inDocFrag ✓ inShadowRootDocFrag ✓ done
Actual behaviour
Errors on the first
waitForSelector
with the error in the summary.Tasks
The text was updated successfully, but these errors were encountered: