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

Remove puppeteer test delays #2110

Closed
raineorshine opened this issue Jul 6, 2024 · 19 comments · Fixed by #2515
Closed

Remove puppeteer test delays #2110

raineorshine opened this issue Jul 6, 2024 · 19 comments · Fixed by #2515
Labels
test Testing and project configuration

Comments

@raineorshine
Copy link
Contributor

raineorshine commented Jul 6, 2024

  1. Disable all CSS transitions on autofocus, thoughts, modals, and alerts during tests.

    Having to wait for the animations slows down snapshot tests considerably.

    The work has been started in Convert test animation durations to PandaCSS #2343. Take a look at how the PandaCSS _test condition is utilized to conditionally set the animation durations to 0s. Any remaining CSS transitions that slow down the tests (namely modals and alerts) need to be converted.

  2. Once animations are disabled, remove the artificial delays that were added to the puppeteer tests due to the need to wait for the animations.

    If a delay cannot be removed but can be reduced, reduce it as much as possible. Instead of sleep, consider waiting for an element to become visible.

    Make sure there are no intermittent failures by running the tests multiple times.

@mstrofbass
Copy link
Collaborator

mstrofbass commented Oct 3, 2024

@raineorshine a couple of questions on this.

  1. Do the CSSTransition React components need any modification under this issue?
  2. Is there a better way to identify the particular locations that need to be updated besides searching for all references to the transition CSS property?
  3. Should all instances of the transition property that have non-zero timings be set to 0 or will there be some transitions that still need a delay? If the latter, how do I identify those?
  4. It's not clear to me which transitions should be shared. More particularly, some transition timings are the same between distinct CSS elements and it's not immediately clear whether they should use the same setting or they should be unqiue values.

I think (4) is most easily solvable by me just doing distinct settings for each and then letting you review the changes and tell me which can be combined (if any) since that puts the onus on me to find them first.

@raineorshine
Copy link
Contributor Author

I think this answers a couple questions: All transitions should be disabled during the tests. That is the simplest and most effective approach. There is never a case where an automated test would cover the animation itself, just the beginning and end states. I originally wrote this issue before the PandaCSS conversion (which is still in progress). The _test conditions makes this much cleaner.

As for shared transitions, I agree let's keep them separate for now (except the ones that have already been combined). We can combine later in a refactor, but I would say that’s a separate scope from this work.

@mstrofbass
Copy link
Collaborator

One clarification needed:

The CSSTransition classes are React components that take an integer timeout value in milliseconds. The PandaCSS tokens are string values and include the units (e.g., 300ms, 0.1s). We can still use the PandaCSS tokens and apply them to the CSSTransition classes by (1) excluding the units and (2) using parseInt, but it's a bit ugly and not consistent. Is there a better way to handle the CSSTransition classes?

Example PandaCSS:

 durations: {
          highlightPulseDuration: {
            value: {
              base: '500ms',
              _test: '0s',
            },
          },
          sideMenuSlideDuration: {
            value: {
              base: '300',
              _test: '0',
            },
          },
        },

@raineorshine
Copy link
Contributor Author

raineorshine commented Oct 8, 2024

Good question. I think we want to use the panda config as the source of truth. Otherwise we would have, for example, highlightPulseDuration in the panda config referencing HIGHLIGHT_PULSLE_DURATION in constants.ts or somewhere else, which seems unnecessarily indirect.

If the panda config is the source of truth, then unfortunately we have to assume the durations are specified as a string in seconds or milliseconds. We could have a convention of only specifying durations in milliseconds, but that would require a custom eslint plugin to enforce in an Open Source context where many different developers with different levels of familiarity are expected to contribute to the project. As ugly as it is to define durations as a string and then parse them out again to use them in CSSTransition, it's actually pretty straightforward with a helper function:

const toMilliseconds = (str: string) => 
  str.endsWith('ms') ? parseInt(str, 10) : 
  str.endsWith('s') ? parseInt(str, 10) * 1000 : 
  parseInt(str, 10)

Then you can use any of the duration tokens in JS:

toMilliseconds(token('durations.highlightPulseDuration'))

@raineorshine
Copy link
Contributor Author

raineorshine commented Oct 8, 2024

The pattern of zero'ing out a duration for the tests in the panda config is so common that I would recommend a small helper function:

const duration = (str: string) => ({
  value: {
    base: str,
    _test: '0s',
  }
})

Then the duration definitions will look like this:

durations: {
  highlightPulseDuration: duration('500ms'),
  hoverPulseDuration: duration('300ms'),
  ...
}

Given that these are only defined in a map within the panda config, we can go even further. I'll leave the definition up to you:

durations: testableDurations({
  highlightPulseDuration: '500ms',
  hoverPulseDuration: '300ms',
  ...
})

@mstrofbass
Copy link
Collaborator

I am running into two problems with this.

Problem #1: The render thought tests are throwing an error when running the removeHUD helper. In particular, it appears that when trying to remove the hamburger menu it is throwing this error:

stdout | src/e2e/puppeteer/__tests__/render-thoughts2.ts > Font Size: 18 (default) > one thought
JSHandle@object
Error: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
    at removeChild (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-C5OBVPJO.js?v=3327da07:8456:26)
    at commitDeletionEffectsOnFiber (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-C5OBVPJO.js?v=3327da07:17510:21)
    at recursivelyTraverseDeletionEffects (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-C5OBVPJO.js?v=3327da07:17486:13)
    at commitDeletionEffectsOnFiber (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-C5OBVPJO.js?v=3327da07:17610:15)
    at commitDeletionEffects (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-C5OBVPJO.js?v=3327da07:17477:13)
    at recursivelyTraverseMutationEffects (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-C5OBVPJO.js?v=3327da07:17674:17)
    at commitMutationEffectsOnFiber (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-C5OBVPJO.js?v=3327da07:17727:15)
    at recursivelyTraverseMutationEffects (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-C5OBVPJO.js?v=3327da07:17685:15)
    at commitMutationEffectsOnFiber (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-C5OBVPJO.js?v=3327da07:17727:15)
    at recursivelyTraverseMutationEffects (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-C5OBVPJO.js?v=3327da07:17685:15)

stdout | src/e2e/puppeteer/__tests__/render-thoughts2.ts > Font Size: 18 (default) > one thought
The above error occurred in the <Context.Provider> component:

    at Transition2 (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-XSC42GO2.js?v=3327da07:114:30)
    at CSSTransition2 (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-XSC42GO2.js?v=3327da07:531:35)
    at HamburgerMenu (https://host.docker.internal:2552/src/components/HamburgerMenu.tsx:65:60)
    at div
    at AppComponent (https://host.docker.internal:2552/src/components/AppComponent.tsx:83:20)
    at div
    at TouchMonitor (https://host.docker.internal:2552/src/components/TouchMonitor.tsx:10:25)
    at ErrorBoundary (https://host.docker.internal:2552/node_modules/.vite/deps/react-error-boundary.js?v=c8bfa926:18:5)
    at ErrorBoundaryContainer (https://host.docker.internal:2552/src/components/ErrorBoundaryContainer.tsx:128:35)
    at Provider (https://host.docker.internal:2552/node_modules/.vite/deps/react-redux.js?v=f30efa63:1097:3)
    at DndProvider2 (https://host.docker.internal:2552/node_modules/.vite/deps/chunk-K3TNDYUV.js?v=3327da07:1257:9)
    at DragAndDropContext (https://host.docker.internal:2552/src/components/DragAndDropContext.tsx:23:31)
    at App

React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.

The comments in the remove helper indicate that removing a non-existent element should result in a NOOP so I'm a bit unclear what might be going on here. Or why CSS transition timings might have an impact. Have you seen this before? All I can find is a thread about Google Translate modifying the page and causing this error.

Problem #2: The following error is cropping up in the url.ts tests and as best I can tell it's complaining because the browser is no longer reporting the https and url as an error of some sort?

image

@raineorshine
Copy link
Contributor Author

To clarify, is Problem 1 occurring in main? Was it introduced in a recent PR, or has it been there for a while?

One possible explanation is that by the time remove gets called, React has already re-rendered the component, so the DOM node may no longer be connected to the DOM. That’s just a guess though.

Problem 2 looks like the spellchecker to be honest. I'm not sure why that is happening now, but maybe there is a way to disable it in the puppeteer config.

@mstrofbass
Copy link
Collaborator

No, problem 1 is not occurring in main but I've opted to keep the problematic duration changes for the end so I'll debug a bit more when I circle back.

I did some digging on #2 and I do not believe there is even a way to disable it in Chromium. I have a StackOverflow question pending about it but my suggestion is to just not do anything until I hear back unless it starts killing PR checks.

@mstrofbass
Copy link
Collaborator

It looks like you discovered a slightly bigger problem:

It appears that semantic tokens do not return the raw value like a token does but instead returns a variable:

E.g.,

token('durations.noteColorTransitionDuration') === "var(--durations-note-color-transition-duration)"

Tokens themselves do not allow for selectors like base and _test so I do not know what to do about the CSSTransitions. Maybe there's another PandaCSS solution but I'm not familiar enough.

We may have to split out the CSSTransition durations from the raw CSS transitions. 😭

My current, half-baked thought is having a file that:

  1. exports raw CSS transitions that get added as semantic tokens; and
  2. a function like getTransition(transition: str) that will check if we're in the e2e tests and return an integer accordingly.

@mstrofbass
Copy link
Collaborator

Is there a possible solution where, when we are in the e2e tests, we do something like this?

https://stackoverflow.com/a/11132887/7020955

@raineorshine
Copy link
Contributor Author

It appears that semantic tokens do not return the raw value like a token does but instead returns a variable:

Hmmm, that is annoying. I was hoping Panda could be the source of truth, but css variables are not so usable in JS land.

Is there a possible solution where, when we are in the e2e tests, we do something like this?

stackoverflow.com/a/11132887/7020955

This is a great idea, but CSSTransition uses setTimeout internally to swap the CSS class. So we need to actually set the duration to 0 in JS.

@raineorshine
Copy link
Contributor Author

My current, half-baked thought is having a file that:

  1. exports raw CSS transitions that get added as semantic tokens; and
  2. a function like getTransition(transition: str) that will check if we're in the e2e tests and return an integer accordingly.

If Panda cannot be the source of truth for durations, then they must be defined in JS. You can then import them into the Panda config. I think that's what you're suggesting in (1) but I could be wrong. Then getTransition can be used to zero them out at runtime.

@raineorshine
Copy link
Contributor Author

Then you can use any of the duration tokens in JS:

toMilliseconds(token('durations.highlightPulseDuration'))

My bad, I suggested this originally without testing ☹️

@raineorshine
Copy link
Contributor Author

raineorshine commented Oct 19, 2024

I did some digging on #2 and I do not believe there is even a way to disable it in Chromium. I have a StackOverflow question pending about it but my suggestion is to just not do anything until I hear back unless it starts killing PR checks.

FYI: This failed one of the CI runs on main so I disabled spell check in b6e15e5.

UPDATE: CI seems to be passing consistently now:

image

@mstrofbass
Copy link
Collaborator

I did some digging on #2 and I do not believe there is even a way to disable it in Chromium. I have a StackOverflow question pending about it but my suggestion is to just not do anything until I hear back unless it starts killing PR checks.

FYI: This failed one of the CI runs on main so I disabled spell check in b6e15e5.

UPDATE: CI seems to be passing consistently now:

Your solution makes sense; I'm not getting any bites on an answer so I think that's what we have to do for now.

@mstrofbass
Copy link
Collaborator

I submitted a PR with enough sleep calls removed that the test length is down from 60 seconds on my machine to < 40 seconds. We should get that merged IMO and then I'll have a go at the ones that are causing more difficulty.

Since the tests run in parallel, the total time is basically the time of the longest test suite, so at this point there's not a huge amount of value in spending a bunch of time trying to shorten tests that are < 20 seconds unless people are frequently running individual tests.

Regardless, the hamburger menu animation adds a significant amount of time to the tests overall since the longest running test suites have like ten individual tests. However, we could probably break those test suites down into multiple tests suites, let them run in parallel, and then get significant gains without any additional work.

My proposal would be to:

  1. Merge Remove Puppeteer Test Delays Part 1 #2491
  2. Give me a small amount of time to work on removing the hamburger animation sleep
  3. Give me a small amount of time to work on removing the other sleeps
  4. Call this done and add new issues to this same milestone for handling later.

@raineorshine
Copy link
Contributor Author

That sounds like a good plan! Thanks for breaking it down like that.

@mstrofbass
Copy link
Collaborator

There are two sleeps remaining and I think we should save those for later. They are:

cursor.ts - This appears to be needed for waiting until the cursor position is saved to persistent state prior to refreshing. There's probably a way to verify the state before refreshing but it's going to require some additional investigation into the architecture that I think is better done later.

dragAndDrapThought.ts - This sleep is needed so the notes have time to update after dropping. I made it so the sleep call only fires on drop, which means it only applies to a subset of the tests. Obv we can optimize this and probably get rid of the sleep altogether but, at this point, we're going to get more speed increases by splitting the drag and drop tests into two separate files that run in parallel.

@raineorshine
Copy link
Contributor Author

Perfect. I agree with your judgments!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
test Testing and project configuration
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants