添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
听话的牛肉面  ·  c# ...·  1 年前    · 
谈吐大方的啤酒  ·  python - Loading ...·  2 年前    · 

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

Note from maintainers

Following up on this suggestion by @enkelmedia , you can work around this issue:

test.beforeEach(async ({ context }) => {
  await context.addInitScript(() => {
    const originalAttachShadow = Element.prototype.attachShadow;
    Element.prototype.attachShadow = function attachShadow(options) {
      return originalAttachShadow.call(this, { ...options, mode: "open" });
  });
});

There is already a bunch of issues related to shadow DOM but as far as I could see, they deal with open shadow DOM roots.

I am in a situation where on my page, there is a custom web component with closed shadow DOM root with an iframe in it with a button in that which I want to click on.

For anyone curious, specifically, it is the Vercel Live Feedback widget shown in the Vercel's Preview environments. I want to click the button in it and then close the widget programmatically so it doesn't obscure elements in my web app and prevent locators pointed at them from working.

For now I ended up solving this issue as per the below. But I don't like this solution, because that's not how a user would do it and I prefer my tests to be as user-mimicking as possible.

await page.locator('css=vercel-live-feedback').evaluate((iframe) => iframe.remove());

I know there are selectors that can enter open shadow DOM roots ( .locator('css=vercel-live-feedback') ) but as far as I could find, there is nothing in Playwright to allow forcibly entering a closed shadow DOM root?

I am aware there will be people who don't like this and are of the opinion that closed shadow roots should not be accessible. To that I raise two motivating examples for why I think that shouldn't be the case:

  • The user's can enter the contents of the closed shadow DOM root. Why shouldn't the test be able to emulate user behavior?
  • Playwright already has mechanisms to bypass other barriers like the CSP so there is a precedent for allowing that.
  • @TomasHubelbauer There is no support for closed shadow DOM, because it is not available to JavaScript traversal.

    I'd recommend to workaround like this:

    // Expect an iframe with a specific url.
    const framePromise = page.waitForEvent('framenavigated', frame => frame.url().matches(/pattern.that.matches.the.iframe.url/));
    // Load the page and wait for the iframe to appear.
    await page.goto('your.page.url');
    const frame = await framePromise;
    // Click the button inside the iframe.
    await frame.getByRole('button', { name: 'Click me' }).click();

    Let me know if that helps.

    @dgozman I've adapted your code a little and tried it:

    test('tests dgozman iframe suggestion', async ({ page }) => {
      // Expect an iframe with a specific url.
      const framePromise = page.waitForEvent('framenavigated', {
        predicate: (frame) => frame.url().match(/vercel/),
      });
      // Load the page and wait for the iframe to appear.
      await page.goto('/');
      const frame = await framePromise;
      // Click the button inside the iframe.
      await frame.getByRole('button').last().click();
      await frame.locator('text=Disable for Session').click();
      await page.pause();
    });

    This clicks the button in the Vercel Live Feedback widget. Awesome!

    But… The button opens a Popper popover which is outside of the iframe but inside the closed shadow DOM root web component. So the button I am after clicking (Disable for Session) is again effectively inaccessible via Playwright.

  • Everything is in the vercel-live-feedback custom web component whose innards are inaccessible to me
  • iframe and div with the Popper popover are siblings
  • With your snippet I can access the frame in an alternative way and click the button in it
  • I don't see / think there is a way to click the button in the Popper popover now?
  • I understand that the closed shadow DOM root is not accessible via either CSS or JS. But I wonder if Playwright could patch browser in a way to make this behavior toggle-able for the page instead of immutable? Like I already mentioned, there is a precedent for turning off stuff like the security boundaries and I am curious if this is another boundary which could made by penetrable for the purposes of the testing.

    Having said that I am well aware the cost/benefit ratio here might be infeasibly high. If that is the case and this is not worth doing, can you think of another workaround that would let me click the Popper popover button without changes to Playwright?

    can you think of another workaround that would let me click the Popper popover button without changes to Playwright?

    Well, you can try using page.mouse.{move,down,up} at some specific coordinates, but that looses all the benefits of Playwright's auto-waiting, resilience and everything else. Apart from raw mouse, I don't see anything else that could help today.

    But I wonder if Playwright could patch browser in a way to make this behavior toggle-able for the page instead of immutable?

    This is not as straightforward, unfortunately. Simply turning closed shadow roots into open ones will probably affect the behavior of some pages, because DOM APIs will behave differently now, so it requires consideration - maybe an opt-in or something. Let me leave this issue open, perhaps it turns out to be a popular request, and we'll prioritize it. However, I would not expect a working solution in the nearest future.

    I'm not saying that this is a good idea but it works for me =D

    After reading this article https://blog.revillweb.com/open-vs-closed-shadow-dom-9f3d7427d1af , I tried the hack below when I needed to run test against some closed components. It did work.

    I basically put this code in the top of the page when the test runs:

    <script type="text/javascript">
          Element.prototype._attachShadow = Element.prototype.attachShadow;
          Element.prototype.attachShadow = function () {
              return this._attachShadow( { mode: "open" } );
    </script>

    I do understand that this is probably not the "best" approach but it got me around the problem for the moment.

    ma-pony, kingsloi, kenmadev, epicwhale, cctui-dev, vvanglro, ferenc-a, and pirate reacted with thumbs up emoji TomasHubelbauer and epicwhale reacted with heart emoji All reactions

    For closed shadow roots specifically within the use case of testing browser extensions, it seems there could be an alternate solution beyond force changing the shadow root modes from closed to open. If Playwright were able to query the DOM / execute JavaScript from the extension context , the web ext API chrome.dom.openOrClosedShadowRoot could be used to pierce directly as a step in the node querying process (Firefox has a parallel method).

    I don't know how complex this would be to implement, especially as it relates to the Devtools protocol connections. I'm happy to file this as a separate issue if the Playwright team is supportive of the direction though.

    FWIW, it looks like Puppeteer has gone down a similar path / attempted to previously as well-- I couldn't find anything in the same vein in the Playwright repo.

    puppeteer/puppeteer#4465
    puppeteer/puppeteer#2812

    Thanks @jspivack , I tried chrome.dom.openOrClosedShadowRoot and it seems to be sufficient for my current needs. Here is a demo for piercing closed shadow inside an iframe.

    It would be great if there was some way to evaluate js code inside extension's content script context using playwright. It might be possible with cdp Runtime.evaluate . But I am not sure how to fetch the content-script's id

    Update: You can also use chrome.scripting.executeScript to execute content script directly from playwright, here is another demo