This article assumes you know how to do basic end to end testing with cypress for more information see https://www.cypress.io/ to get started.
Shadow DOM also seen as #shadow-root, sounds scary and can really cause issues when performing an end to end(e2e) test.
So what is it?
The shadow DOM simply put is a DOM within a DOM, nested you could say. This DOM allows styles, scripts and other things to be scoped within it, without affecting the parent DOM.
AWS Amplify uses the shadow DOM(a lot) when used to create a signing up/in page. Let’s take a look:
We can see 3 individual shadow DOMs here. This can cause an issue when trying to perform an e2e test with cypress, as cypress can only see the root DOM. These other DOMs are invisible to it.
So what does that mean?
In this case the shadow DOM contains all the elements required to sign in:
Email input field
Password input field
Sign in button
So if I want to write a test that checks my sign in process works when it should, I need to be able to have cypress focus on each of these elements and perform an action such as type() or click().
These fields being within a shadow DOM mean Cypress cannot focus on them and if Cypress can’t focus on them, it can’t perform any actions on them. My e2e tests are doomed to fail from the start!
Or are they?
Let’s take a more in-depth look at this sign in form and try to find the email input field.
Now we’ve delved into the shadow DOM, and there’s even more of them inside of it. It’s quite the climb to find that email field we need to select and type in.
If the shadow DOM’s didn’t exist in this situation we would be able to use a very straight forward selector in Cypress to allows to focus and acton on the email input:
The input has an id of “email’ then we can just add
This would type out ‘email@example.com’ in the email input field.
So how do we access those elements nested within the shadow DOM?
Luckily Cypress has recently (June 2020) added the experimental feature “.shadow()”. This allows us to open up and access the shadow DOM with Cypress.
To enable this we first have to edit the cypress.JSON config file found at the root level of your app. You will need to add:
to the config object. This then enables the global use of shadow() so you don’t need to require/import it later on.
Let’s use it!
With the example above we can see we have entered a shadow DOM a total of 4 times! This is shown by the arrows pointing down next to the “#shadow-root (open)”, if it is still pointing sideways then you haven’t accessed that particular one.
You can see above each time we encounter an opened shadow DOM we have used the .shadow() method to allow us to enter each of these stages.
Running this will now allow us to focus on and type within the email input field.
You can repeat this for all the remaining tests you need to do.
That’s all there is to it!
if you have any questions please let me know mark@loupetestwarecom