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
fix(runner-ct): viewport scaling during screenshots #16543
Conversation
Thanks for taking the time to open a PR!
|
Test summaryRun details
View run in Cypress Dashboard ➡️ This comment has been generated by cypress-bot as a result of this project's GitHub integration settings. You can manage this integration in this project's settings in the Cypress Dashboard |
148d72f
to
0d7d37d
Compare
|
||
const iframe = document.querySelector<HTMLIFrameElement>('.aut-iframe') | ||
|
||
iframe.classList.remove('aut-iframe-screenshotting') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be OK temporarily, but the iframe code lives entirely inside runner-ct
, so we can refactor it to be rendered via React.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice thing about this PR is we now have some Percy screenshots that are correct, so we can safely refactor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really nice to see these fixes. I have several questions about what this is doing.
|
||
const iframe = document.querySelector<HTMLIFrameElement>('.aut-iframe') | ||
|
||
iframe.classList.remove('aut-iframe-screenshotting') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be OK temporarily, but the iframe code lives entirely inside runner-ct
, so we can refactor it to be rendered via React.
.aut-iframe-screenshotting { | ||
height: min(100vh, 100%) !important; | ||
overflow: scroll !important; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What styles is this conflicting with? I particularly don't understand the min(100vh, 100%)
. Is the 100% the intrinsic content size, not the iframe parent's size?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figured this out just by creating lots of use cases and observing the output. Here's the use case this solves. Consider this example (note the viewport - 200x2000, kind of unrealistic use case, who has a device this shape? But still, good to test these things).
Without this CSS, the screenshot is like this. Note the default behvior of cy.screenshot
, which is cy.screenshot({ capture: 'fullPage' })
(as opposed to cy.screenshot({ capture: 'viewport' })
.
This is the desired output - scroll down the page, stitch together the screenshot:
We need overflow: scroll
to be applied. If overflow: scroll
is not applied, Cypress tries to scroll, take a screenshot, and stitch them together. That's why you end up with that weird duplicating behavior in the first image above.
scroll: overflow
is only applied if you have a static height set - height: 100%
won't work. By doing min(100%, 100vh)
it will apply height 100vh
to components with a height greater than the viewport (100%). This supports the desired behavior.
I have some ideas on a much less hacky way to do this moving forward, I will send you a message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this detailed explanation.
transform: `scale(${screenshotting ? 1 : scale})`, | ||
transformOrigin: `${screenshotting ? 'top left' : ''}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
transform: !screenshotting ? `scale(${scale}` : undefined
And I don't think you'll need transformOrigin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You were right - I didn't need transformOrigin
. Nice catch.
Not sold on the transform
refactor. Isn't an explicit scale(1)
much more clear? Also, I think that if X then Y else Z
is more clear than if not X then Z else Y
syntax - ie, putting a negative at the start of the ternary feels a little weird, at least to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why emit HTML when you could not?
I suggested the ternary be written like that because I don't like having a falsy case be in the middle (the true case). Just a weird style preference of mine.
@@ -16,4 +16,5 @@ | |||
.size-container { | |||
overflow: auto; | |||
box-shadow: shadow(m); | |||
max-width: 100%; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we want to limit against the viewportWidth
? Can't this lead to situations where viewportWidth
is larger than the window size, and the iframe will end up being smaller (or is viewportWidth
the actual current browser size, in which case I'm not sure why this style is necessary)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. This might not be the right behavior. I added this to match the existing behavior in both the e2e runner and fix the regression reported by this comment.
I pulled his project and ran it. It looks like this in my browser.
Normal viewport:
Narrow viewport:
For reference, in CT runner it looks like this:
Without max-width: 100%
we get the following screenshot. This just seems wrong (and a regression?). It should be centered, right? Or is this expected behavior? 🤔
With max-width: 100%
:
E2E runner gives this:
So, I think this change makes sense - we should match what E2E does. You may notice that the header bar is missing in CT - I believe the user has some global CSS which he has not injected into the component, that is present when visiting the website. It's missing in both the CT runner and screenshot, so I think that's correct - the screenshot correctly reflects what is shown in the CT runner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Released in This comment thread has been locked. If you are still experiencing this issue after upgrading to |
User facing changelog
cy.screenshot
was incorrectly capturing parts of the runner UI during screenshots.Additional details
A lack of thorough testing around the
cy.screenshot
feature in CT runner led to some bugs and incorrect behavior. Runner CT now matches the behavior specified in the Cypress documentation and E2E runner, including:capture
option tocy.screenshot
correctly (bothfullPage
, which is the default, andviewport
).<iframe>
) correctly during screenshot capturecypress/packages/runner-ct/cypress/component/screenshot.spec.tsx
Line 57 in 8700ecf
Several things contributed to the problems:
scale
applied the AUT during screenshotting.scroll: overflow
) during screenshot. That's why we had the problem in this ticket - the driver was trying to scroll to capture the entire viewport, but could not, sinceoverflow: scroll
was not set.How has the user experience changed?
Figure 1: Regular 500x500 viewport
Notes: no change. The basic use case was fine.
Before:
After:
Figure 2: 750x750 viewport
Notes: depending on the size of the runner during the time of screenshot, this was okay. If you resize the browser window, though, certain sizings give you the gray background.
Before:
After:
Figure 3: component 1500px x 1000px, viewport 850x1000.
cy.screenshot({ capture: 'viewport' })
This is how it looks in the runner UI:
Notes: "before" screenshot is completely wrong. It's capturing the entire page, not just the viewport, and there is a lot of the gray background showing. "after" is correct. Since we are specifying
viewport
, it is now correctly NOT capturing the hidden part (outside viewport).Before:
After:
Figure 4: component 1500px x 1000px, viewport 850x1000.
cy.screenshot({ capture: 'fullPage' })
Notes: it correctly scrolls and captures the entire page now.
Before:
After:
Figure 5: Regression from user project
See bug report here: #16478
When the browser is this size:
After:
Another bug was if I make the runner CT really small, like this:
Produced:
This isn't what the e2e runner was doing:
Now it's working correctly in CT:
PR Tasks
cypress-documentation
?type definitions
?cypress.schema.json
?