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

fix: running a new test after already having run tests #21087

Merged

Conversation

ryanthemanuel
Copy link
Collaborator

@ryanthemanuel ryanthemanuel commented Apr 14, 2022

User facing changelog

Users can now execute a test in open mode, go back to the specs page, add a new test, and run that newly added test without error

Additional details

Previously, we had a watcher that was only watching the routing changes in order to set the active spec. The problem is that this was caching the specs list so if we had a stale specs list it wasn't going to be able to find the newly added spec. We fix this by ensuring that any active spec management is done inside of a watchEffect so that we are watching changes to all things relevant.

How has the user experience changed?

PR Tasks

  • Have tests been added/updated?
  • [na] Has the original issue (or this PR, if no issue exists) been tagged with a release in ZenHub? (user-facing changes only)
  • [na] Has a PR for user-facing changes been opened in cypress-documentation?
  • [na] Have API changes been updated in the type definitions?
  • [na] Have new configuration options been added to the cypress.schema.json?

@cypress-bot
Copy link
Contributor

cypress-bot bot commented Apr 14, 2022

Thanks for taking the time to open a PR!

@cypress
Copy link

cypress bot commented Apr 14, 2022



Test summary

17872 0 217 0Flakiness 1


Run details

Project cypress
Status Passed
Commit 1961541
Started Apr 19, 2022 12:49 AM
Ended Apr 19, 2022 1:02 AM
Duration 13:10 💡
OS Linux Debian - 10.10
Browser Multiple

View run in Cypress Dashboard ➡️


Flakiness

cypress/e2e/commands/xhr.cy.js Flakiness
1 ... > no status when request isnt forced 404

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

@ryanthemanuel ryanthemanuel marked this pull request as ready for review April 15, 2022 15:22
Copy link
Contributor

@lmiller1990 lmiller1990 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left two comments, neither are really blockers but wanted to get some opinions - the readability of this part of the code base is really important since there's so much going on.

const specs = computed(() => {
return props.gql.currentProject?.specs ?? []
})

watchSpec(specs)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a strong opinion but I like to functions that call watch or watchEffect in the actual component file to give visibility, or you need to dig through a bunch of other files to find the watcher. It's like when you are trying to track down a useState or useEffect nested deeply in a hook in React.

I don't mind too much, but something about watchSpec(specs) is more readable to me than useUnifiedRunner(specs). The former is more clear that something reactive will happen. Happy to field opinions and not really a big deal either way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer adding 'watcher' to the composable's name in some way if more discoverability is desired. Putting the responsibility of activating the watchers on the consuming component just leaves a logic hole where there doesn't need to be one.


if (selectorPlaygroundStore.show) {
const autIframe = getAutIframeModel()
watch(() => getPathForPlatform(route.query.file as string), (newQueryFile) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have two things watching route.query.file, line 26 and line 44. I wonder if we should combine them so all the watching of the same variable is in the same function? There's an argument of "one watch per effect" so again I don't mind either way, but wanted to point this out since you might not have noticed the double watcher.

I'm guessing the perf overhead is negligible so just wanted to call this out to see if it was intentional or not.

I tried the single watcher and it seems to work just fine, too: #21112

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, moving this in here prevents the selector playground from ever opening - opening it triggers the watchEffect due to the value changing in the store, and the watchEffect closes it up right away.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I played around with combining them at one point and ran into what @marktnoonan is talking about. Specifically I think it's these tests failing on #21112: https://app.circleci.com/pipelines/github/cypress-io/cypress/36466/workflows/828e7886-b042-41de-8753-e693c6f9a292/jobs/1460784

There may be a way to tweak the logic to fix that, but ultimately, I'm ok with keeping them separate since I do like the idea of "one watch per effect"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"one watch per effect" seems like a reasonable approach to take until it becomes a performance issue. I prefer the sensible encapsulation over worrying about the lifecycle too much.

Copy link
Contributor

@marktnoonan marktnoonan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, having tested a bit this works well. The only drawback to the watchEffect that I can think of is that it runs more times than strictly necessary, since it sets values that it also watches. But there don't seem to be any negative side effects of that, and the simplicity of gathering this all into the composable is nice. I just left one comment about an unused param.

As for the problem itself:

Previously, we had a watcher that was only watching the routing changes in order to set the active spec. The problem is that this was caching the specs list so if we had a stale specs list it wasn't going to be able to find the newly added spec.

I was super curious about this because I couldn't grok why it would be happening, so I looked around a little bit. It seems that the route watcher's callback was firing twice - once with a reference to the correct specs list, exactly as we'd want, and then again with the out of date specs list, and it's that second run that triggers the redirect + error.

The watcher runs twice because when you first visit the spec runner calls getPathForPlatform for the new and old values of route.query.file. In running the watcher's callback for the old value (which is just undefined, then null), Vue is using the old value of the specs list, but somehow that callback runs second. So, before this PR, we briefly passed through a correct "spec found" state before it gets overwritten by a "not found" state and bounces us to the specs page. That's probably an oversimplification but I'm trying to avoid going down a rabbit hole.

We could fix the issue by making sure the watcher doesn't run twice with a coupe of checks on the new/old value parameters, I tried it out and it works fine, but I still feel the changes in this PR make it all more readable (to @lmiller1990's point).

I'd be fine with useUnifiedRunnerAndWatchers or something as the name to address the discoverability of these watchers.

I'm sure we can tighten this up even further at some point but it's probably not very valuable to do that unless we see perf issues.


if (selectorPlaygroundStore.show) {
const autIframe = getAutIframeModel()
watch(() => getPathForPlatform(route.query.file as string), (newQueryFile) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove this unused param:

Suggested change
watch(() => getPathForPlatform(route.query.file as string), (newQueryFile) => {
watch(() => getPathForPlatform(route.query.file as string), () => {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@tbiethman tbiethman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works great! Any of the proposed naming/API solutions would work for me, though I stated my preferences :P

@marktnoonan
Copy link
Contributor

@ryanthemanuel It looks like moving it into the watchSpecs function has introduced a problem - unless this is actually flaky, which seems unlikely for a brand new test: https://app.circleci.com/pipelines/github/cypress-io/cypress/36484/workflows/3f24cac4-46d2-424a-ae8c-b9db7a492f12/jobs/1461338

Video shows us doing the right behavior, but the order of things has changed.

@ryanthemanuel
Copy link
Collaborator Author

@ryanthemanuel It looks like moving it into the watchSpecs function has introduced a problem - unless this is actually flaky, which seems unlikely for a brand new test: app.circleci.com/pipelines/github/cypress-io/cypress/36484/workflows/3f24cac4-46d2-424a-ae8c-b9db7a492f12/jobs/1461338

Video shows us doing the right behavior, but the order of things has changed.

@marktnoonan, hmmm it does appear to be flaky (I can't get it to fail locally) but I'll see if I can figure out why.

@lmiller1990 lmiller1990 merged commit 558d921 into 10.0-release Apr 19, 2022
@lmiller1990 lmiller1990 deleted the ryanm/fix/running-new-test-after-already-running-test branch April 19, 2022 09:49
tgriesser added a commit that referenced this pull request Apr 20, 2022
…e-config

* 10.0-release:
  chore: Move component-index generation to scaffold-config package (#21090)
  fix: label text should be clickable to toggle snapshot highlight (#21122)
  feat: add next preset to webpack-dev-server-fresh (#21069)
  chore: add dev-servers as deps to server to be included in the binary (#21142)
  fix: do not highlight preExtension if selected option is renameFolder (#21121)
  fix(unify): Remove 'Reconfigure' dropdown from Testing Type chooser (#21063)
  feat(unify): launchpad header breadcrumbs and reusable tooltip component (#20648)
  test: add windows-test-kitchensink job (#20847)
  fix: aut centering and height calculation (#21019)
  chore: fix tests that fail on windows (#21055)
  fix: running a new test after already having run tests (#21087)
  fix: ct project setup framework keys for next and nuxt (#21116)
  fix: remove MountReturn from scaffolded ct support file (#21119)
  fix: do not scaffold fixtures if folder exist (#21078)
  fix: revert "fix: types for Cypress.Commands.add (#20376)" (#21104)
  chore: Update Chrome (stable) to 100.0.4896.127 and Chrome (beta) to 101.0.4951.34 (#21083)
  fix: types for Cypress.Commands.add (#20376) (#20377)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants