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

e2e testing Polymer application / shadowDom #830

Closed
eglital opened this Issue Oct 26, 2017 · 44 comments

Comments

@eglital
Copy link

eglital commented Oct 26, 2017

Is there a way to use Cypress to test Polymer (that uses shadow DOM) applications? Any suggestions or best practices? Especially if shadow roots are deeply nested inside each other? Because I don't think querying in each shadow root deep into a page is a solution...
Thanks!

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Oct 26, 2017

Shadow DOM is a particular problem the same way that iframes are.

Can you reference any API's from selenium based frameworks that you've used before and that you like?

We could take some inspiration from other players to then create out own.

@StefanGussner

This comment has been minimized.

@chachra

This comment has been minimized.

Copy link

chachra commented Oct 26, 2017

Would be good if cy.get() just worked even when there is a shadow dom. Someone else's solution here: https://gist.github.com/ChadKillingsworth/d4cb3d30b9d7fbc3fd0af93c2a133a53

@chachra

This comment has been minimized.

Copy link

chachra commented Oct 26, 2017

@ChadKillingsworth

This comment has been minimized.

Copy link

ChadKillingsworth commented Oct 26, 2017

That's my gist and has become the authoritative source for the subject.

Traversing into a shadowRoot should never be accidental - it should be an explicit operation.

There are two basic primitives that are needed:

  • select the shadowRoot of an element directly
  • select elements which exist in the shadow tree of an element (but not in any nested shadow trees)

Both of these operations should be available in selenium directly, but until that time testing frameworks should offer helpers to assist with this.

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Oct 26, 2017

I believe the design of these API's are similar to iframes in that there is an "enter" and "exit" mode. I agree, should be explicit.

Selenium is irrelevant to us because Cypress has native access to the DOM and can query and drill into things at will without serialization.

@ChadKillingsworth

This comment has been minimized.

Copy link

ChadKillingsworth commented Oct 27, 2017

Thinking of shadowDom similar to iFrame is the wrong mental model - "enter" and "exit" modes are just going to create pain for devs.

You really need something more like:

cy.get('my-element').shadowRoot.get('button').click()

There's not much more to it.

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Oct 27, 2017

I see. That may work well.

Likely would be .shadowRoot()

@eglital

This comment has been minimized.

Copy link
Author

eglital commented Oct 27, 2017

I just started to use Cypress a couple days ago, so I might not completely get all concepts. So far I added a function:

Cypress.Commands.add("shadowDomElement", {prevSubject: true},
function(subject, selectors) {
      var currentElement = subject[0];
      for (var i = 0; i < selectors.length; i++) {
        currentElement = currentElement.shadowRoot;
        currentElement = currentElement.querySelector(selectors[i]);
        if (!currentElement) {
          break;
        }
      }
      return currentElement;
});

which basically just takes an array of selectors and returns the last element in that array, but I am not sure if that's a good approach..

The way I use it is:

cy.get('app-shell').shadowDomElement(['page-app', 'page-cluster-reporting', 'page-cluster-reporting-select-plan', 'h2'])
        .then((res) => { expect(Cypress.$(res).text()).to.contain('Select Response Plan') })

which seems to be working but I don't think I can perform actions like click() the same way:

cy.get('app-shell').shadowDomElement(['page-app', 'page-cluster-reporting', 'page-cluster-reporting-select-plan', 'paper-button[id=confirm]'])
   .then((res) => { Cypress.$(res).click() })

which doesn't seem to be doing anything. Anything I am missing?

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Oct 27, 2017

When you use Cypress.$(...) you're simply wrapping the DOM element into a jquery object. Calling .click() on that is using jquery's click, and not the Cypress click.

You could simply change all of the Cypress.$(...) into cy.wrap which then assumes the element and enables you to continue to chain off of it and call cy commands...

Cypress.Commands.add("shadowDomElement", {prevSubject: true},
function(subject, selectors) {
      var currentElement = subject[0];
      for (var i = 0; i < selectors.length; i++) {
        currentElement = currentElement.shadowRoot;
        currentElement = currentElement.querySelector(selectors[i]);
        if (!currentElement) {
          break;
        }
      }
      
      // wrap this guy back into cypress so you can continue chaining off of it
      return cy.wrap(currentElement);
});
cy.get('app-shell').shadowDomElement(['page-app', 'page-cluster-reporting', 'page-cluster-reporting-select-plan', 'h2']).should('contain', 'Select Response Plan')
cy.get('app-shell').shadowDomElement(['page-app', 'page-cluster-reporting', 'page-cluster-reporting-select-plan', 'paper-button[id=confirm]']).click()
@eglital

This comment has been minimized.

Copy link
Author

eglital commented Oct 27, 2017

screen shot 2017-10-27 at 11 02 07 am
I tried that and I am getting RangeError: Maximum call stack size exceeded on the wrap command.

@brian-mann

This comment has been minimized.

Copy link
Member

brian-mann commented Oct 27, 2017

I think the problem is that many of our internal methods that calculate things like visibility are failing on shadow root elements. So probably just need to take this into account and add a bunch of test cases.

I'm 100% certain all this is solvable, but unfortunately I wouldn't say its a priority for our internal team. We are focusing on higher value features that are applicable to a bigger audience. Would be happy to direct as needed / provide feedback / accept PR's.

If this is something that your company is interested in getting in sooner rather than later than we can talk about prioritizing this work.

@chachra

This comment has been minimized.

Copy link

chachra commented Oct 27, 2017

@brian-mann thanks. We got shady dom working in our code. And can test for assertions etc. But cannot seem to now click, hover etc. even when shadow dom is not in play (using 'shady' dom).

@iexplore

This comment has been minimized.

Copy link

iexplore commented Dec 27, 2017

I got the same "stack overflow" result on the second shadowRoot. (So the first worked.)
Too bad this isn't working, it might be a minor problem. It prevents Cypress from being used with apps build in/with Polymer and web components in general.

@AndreasGalster

This comment has been minimized.

Copy link

AndreasGalster commented Jan 5, 2018

Is there any way to speed this up? Donations?

Firefox is finally shipping shadow DOM in March, Edge excluded we can expect for shadow DOM to become mainstream by 2019 for frameworks using or supporting webcomponents (pretty much every framework has PRs to support webcomponents aside from framework specific components)

@bahmutov

This comment has been minimized.

Copy link
Collaborator

bahmutov commented Jan 5, 2018

We are testing TodoMVC made with Polymer here http://todomvc.com/examples/polymer/ just fine. In my humble opinion, shadow DOM might be available in the browsers, but does not mean frameworks would be using it widely.

As far as speeding it up - Cypress is OSS and every contribution is welcome. For us as a company we unfortunately have to prioritize based on what we think has the largest impact. So shadow dom did not make to our https://docs.cypress.io/guides/references/roadmap.html yet

@iexplore

This comment has been minimized.

Copy link

iexplore commented Jan 6, 2018

I looked at it and you are testing with the older version version of Polymer (1.x) and WebComponentsjs 0.7.18 not 1.x. This is not using shadowDom nor the newer polyfill of webcomponentjs. In this version you access everthing without using shadowRoot.

@estene

This comment has been minimized.

Copy link

estene commented Jan 22, 2018

Any updates on this? How can we get this moving forward?

@edudojevic

This comment has been minimized.

Copy link

edudojevic commented Jan 29, 2018

+1 To move this higher in priority. @brian-mann I would like to introduce Cypress at the company where I work right now where Polymer is used. However, this is not possible until this feature is added.

@trescenzi

This comment has been minimized.

Copy link

trescenzi commented Feb 9, 2018

I'm looking to test my web components which are ostensibly based off of SkateJS so they have a lot of shadowDOM within them. Are there any updates on what a desired API might look like? I'd be open to contributing if there was more information about what a generally desired solution to this looks like.

It sounds like people generally like the idea of an array of selectors where each item in the array represents a different document, breaking on shadowRoots. It seems reasonable to me to extend this into the multiple querying commands, get, children, find and if passed an array they traverse down into shadowRoots.

Then there's the problems mentioned above with stuff like isVisible and isAttached/Detached. Those don't seem like they need API discussion if I'm not mistake, more just that they need work and test cases.

@AndreasGalster

This comment has been minimized.

Copy link

AndreasGalster commented Mar 25, 2018

Just a friendly reminder for everyone watching to give their thumbs up on the first post so it's easier for the team to gauge interest for a roadmap :).

@bennypowers

This comment has been minimized.

Copy link

bennypowers commented Mar 25, 2018

This issue should be renamed to e2e application with shadowDom. It pertains to any app using shadowDom, not just apps that specifically use the polymer library.

Examples:
• Apps that use lit-element, or other shadowDom-based custom elements libraries
• Apps that define shadomDom boundaries using plain old vanilla JS
• Future apps that affect built in element shadowDom

@jennifer-shehane jennifer-shehane changed the title e2e testing Polymer application e2e testing Polymer application / shadowDom Mar 26, 2018

@egucciar

This comment has been minimized.

Copy link

egucciar commented Apr 4, 2018

Thanks so much @ValerieThoma . So far i have given a try to implement some Cypress custom commands but will need to iterate more before thinking about touching the core. Currently I've implemented shadowGet, shadowFind, and shadowClick. Will let you know what else i discover along the way and wont hesitate to continue to provide feedback so that the eventual tools can be incorporated back to Cypress.

@egucciar

This comment has been minimized.

Copy link

egucciar commented May 22, 2018

@brian-mann can you please advise if it would be easier for us to change the isAttached behaviour to account for shadowDom than it would be to re-implement all the cypress commands ourselves?

@Georgegriff

This comment has been minimized.

Copy link

Georgegriff commented Jul 3, 2018

I'm very new to Cypress and i'm been experimenting with E2E testing with Shadow DOM.
Was testing out an alternative approach to @ChadKillingsworth gist, which uses a library i put together that i've been experimenting with for automation https://www.npmjs.com/package/query-selector-shadow-dom that lets you query without needing to specify the full shadow root path.

I wrote some code to load my lib and find my element in the dom, which worked but cypress throws an error claiming that the element is no longer in the dom, i suspect this is because it is in the shadow dom.
image

Test code

describe('navigation link', function() {

    it('can visit home', function() {
        cy.visit('./test.html');
        cy.shadowGet('a.go-to').click();
    })
})

Hacky hour 1 or trying to work out how Cypress works and how can inject scripts that work with the DOM that led to the screenshot

Cypress.Commands.add("shadowGet", (selector) => {
    return cy.window().then((win) => {
        return new Promise((resolve) => {
            if (win.querySelectorShadowDom) {
                const element = win.querySelectorShadowDom.querySelectorAllDeep(selector);
                return resolve(element);
            } else {
                const script = document.createElement('script');
                script.onload = () => {
                    const element = win.querySelectorShadowDom.querySelectorDeep(selector);
                    return resolve(element);
                }
                script.src = "node_modules/query-selector-shadow-dom/dist/querySelectorShadowDom.js";
                win.document.head.appendChild(script);
            }
        });

    });
});
@egucciar

This comment has been minimized.

Copy link

egucciar commented Jul 28, 2018

Hi all. I started working on this feature --> egucciar#1

Lots left to do, so just posting here to let people know.

@Akshaya0309

This comment has been minimized.

Copy link

Akshaya0309 commented Oct 7, 2018

Hi All,
I have also the same issue.
Do we have any update on that?

@Snapu

This comment has been minimized.

Copy link

Snapu commented Nov 21, 2018

+1
Web components are becoming the standard for building micro frontends. This topic should get a higher priority.

@egucciar

This comment has been minimized.

Copy link

egucciar commented Nov 21, 2018

@Snapu not to disagree but as a devil's advocate approach one could be using webcomponents without using shadowDom.

But as someone whose written over 100 tests against shadowDom apps using entirely custom commands (and therefore spending lots of time trying to figure out how to make those commands behave similar to cypress ones, which was a very difficult process since custom commands don't come with a lot of documentation explaining the cypress conventions for retryability and such), native support for cypress and shadowDom would be beyond awesome

I even had started implementing this in a feature branch and it wasn't really even that difficult. I was able to get a large amount of native coverage in a few short hours. I began to get stuck because I wanted to be able to discuss with Cypress team what would be the best path to proceed for acceptance as I didn't want an all or nothing approach and be able to release working code more quickly. But the cypress team is inaccessible from that perspective unfortunately I was never able to get feedback or guidance when asking questions.

It would be awesome if cypress team could implement shadowDom support because it's really not much work and only requires a few small shifts in perspective of what they do today. But I could see if no one has shadowDom experience right now it could be difficult as shadowDom does have a lot of differences from the actual Dom.

@Georgegriff

This comment has been minimized.

Copy link

Georgegriff commented Nov 21, 2018

It's possible to create userland code that queries Shadow Dom using standard query selector syntax, I've done it, have a library on npm for it and integrated with puppeteer. The issues I had with cypress were even though my library could find the Shadow Dom nodes in cypress it does some checks on the visibility of the JS node that I provided (which was in the shadow dom) and errors out

@egucciar

This comment has been minimized.

Copy link

egucciar commented Nov 21, 2018

That's correct, it's possible and achievable to make custom commands. As I said I have over 100 tests therefore have a fleet of custom commands for querying and actionability (clicking, selecting from a select box, chai jQuery should, event trigger, input) it's not open source, because I did not want the burden of supporting the library for a wide audience but I can gladly share code snippets.

@Snapu

This comment has been minimized.

Copy link

Snapu commented Nov 23, 2018

@egucciar In the end I did the same. Instead of wrapping the shadow dom I have a set of custom commands that operate inside the shadow dom and return back the original element so I can chain these custom commands like shadowContains, shadowType, shadowClick, etc.

@Ycaglayan

This comment has been minimized.

Copy link

Ycaglayan commented Dec 21, 2018

That's correct, it's possible and achievable to make custom commands. As I said I have over 100 tests therefore have a fleet of custom commands for querying and actionability (clicking, selecting from a select box, chai jQuery should, event trigger, input) it's not open source, because I did not want the burden of supporting the library for a wide audience but I can gladly share code snippets.

@egucciar Some teams in our organization want to go from Selenium to Cypress but they are doubting to make the move as the applications are running with Polymer. Could you send/post the code snippets that you use for the most used shadowDom commands, so I can do some pilots?

@egucciar

This comment has been minimized.

Copy link

egucciar commented Dec 21, 2018

@Ycaglayan here's a gist with the full current source code as of today + Readme + spec file (just needs an index.html with a <cypress-spec> element to work, or you can modify the spec....). This works with cypress v 3.1.3, but be aware theres a bug in cypress 3.1.2 that affects our commands so ensure youre higher or below :)

just ask me if any questions. start with shadowCommands.js as the main file. Good luck!

https://gist.github.com/egucciar/5f31c84f19190b5e64737a9d80eb7a9d

@Ycaglayan

This comment has been minimized.

Copy link

Ycaglayan commented Jan 2, 2019

@egucciar Thanks a million and the best wishes for 2019!!

ps.:Sorry for the late reply, was offline for the last couple of days. :)

@c-cpt

This comment has been minimized.

Copy link

c-cpt commented Jan 6, 2019

@egucciar That works great! Thank you.

@egucciar

This comment has been minimized.

Copy link

egucciar commented Jan 6, 2019

No problem, I forgot to mention for cypress contains selector functionalities I use the jquery contains selector (e.g. shadowGet('*:contains("some text")'). Since this feature is not documented but diverges from the regular cypress way of doing things I figured it is worth mentioning. It was nice to be able to use jQuery for this and why jQuery is used and not native Dom querying.

@codepleb

This comment has been minimized.

Copy link

codepleb commented Mar 5, 2019

@egucciar Can you tell me how to use your work? Or sources on how to make that a reality? I don't really get it from your tl;dr discussion.

Thanks for the community work btw.

@egucciar

This comment has been minimized.

Copy link

egucciar commented Mar 5, 2019

@codepleb I have the Gist up which you can use as a reference on how to design your own custom shadowDom commands.

@codepleb

This comment has been minimized.

Copy link

codepleb commented Mar 6, 2019

@egucciar Thx for the response, but I don't get it.

import { registerShadowCommands } from '@updater/cypress-helpers';

What is this line? Where do I get this dependency from? It doesn't seem to exist on npm.
Or do I need to put the bare files you provided into my support folder and import it from there?

@egucciar

This comment has been minimized.

Copy link

egucciar commented Mar 6, 2019

You are free to choose how you want to use the commands. The implementation for registerShadowCommands is provided so if you want to use that you would just move the implementation into your support folder or whatever you choose to do and import. Or you could refactor the code to fit your style. It's really up to you.

I would suggest taking this at face value. There is no dependency on our private packages in this gist, but I lifted all the docs as is.

@jennifer-shehane

This comment has been minimized.

Copy link
Member

jennifer-shehane commented Mar 6, 2019

We're going to close this issue in favor of #144 - which is Shadow Dom support. So please follow that issue.

No longer need to keep up with 2 large issues for the same feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.