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

feat: Fix detached DOM errors for all Cypress commands #24417

Merged
merged 61 commits into from
Nov 9, 2022

Conversation

BlueWinds
Copy link
Contributor

@BlueWinds BlueWinds commented Oct 26, 2022

User facing changelog

Fixed the majority of Detached DOM errors, and updates the error messages when they do occur pointing out the correct way to solve them.

Breaking Change

Cypress always requeries aliases when they are referenced. This can result in certain tests that use to pass failing. For example:

cy.findByTestId('popover')
  .findByRole('button', { expanded: true })
  .as('button')
  .click()

cy.get('@button')
  .should('have.attr', 'aria-expanded', 'false')

previously passed, because the initial button was collapsed when first queried, and then later expanded. However, in Cypress 12, this test fails because the alias is always requeried from the DOM, effectively resulting in the following execution:

cy.findByTestId('popover')
  .findByRole('button', { expanded: true })
  .click()

cy.findByTestId('popover')
  .findByRole('button', { expanded: true }) // A button which matches here (is expanded)...
  .should('have.attr', 'aria-expanded', 'false') // ...will never pass this assertion.

You can rewrite tests like this to be more specific; in our case, we changed the alias to be the first button rather than the unexpanded button.

cy.findByTestId('popover')
  .findAllByRole('button')
  .first()
  .as('button')

Breaking Change

If .invoke() is followed by additional commands or assertions, it will call the named function multiple times. This has the benefit that following assertions can more reliably use the functions return value.

If this behavior is undesirable - you want the function invoked only once - move the following commands / assertions to their own chain command. For example, rewrite

cy.get('input').invoke('val', 'text').type('newText')

to

cy.get('input').invoke('val', 'text')
cy.get('input').type('newText')

Breaking Change: .invoke() now throws an error if the function returns a promise. If you wish to call a method that returns a promise and wait for it to resolve, us .then() instead of .invoke() For example, rewrite

cy.wrap(myAPI)
  .invoke('makeARequest', 'http://example.com')
  .then(res => { ...handle response... })

to

cy.wrap(myAPI)
  .then(api => api.makeARequest('http://example.com'))
  .then(res => { ...handle response... })

Additional details

The breaking changes above actually happened in #23902, but they're far more heavily exposed here, and I've finally managed to articulate the breaking change in a user-digestable way.

Steps to test

How has the user experience changed?

In addition to the breaking changes mentioned above, queries are no longer displayed as "pending" in the command log while following commands are retrying; they resolve the first time they pass. New behavior:

new-pending-behavior

Old behavior:

old-pending-behavior

The error messages around detached DOM errors have updated significatly. Users will encounter them less frequently; the most common cases won't occur at all anymore, but when users do encounter errors, we now make specific recommendations on how to resolve the error.

There are now two detached dom error messages, fairly similar but with different verbage based on the situation in which they occur. New error messages:

detached-new-error-2
detached-new-error

Old error message:

detached-old-error

PR Tasks

@@ -3269,26 +3278,6 @@ describe('src/cy/commands/actions/click', () => {
cy.dblclick()
})

it('throws when subject is not in the document', (done) => {
Copy link
Member

Choose a reason for hiding this comment

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

same question, why remove?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Our driver codebase has a ton of duplicate code. Each command is tested as if it were entirely independent - but they're not, they share huge portions of code. Detached dom errors are part of $actionability and part of verifyUpcomingAssertions. We don't need to test it separately for every command that uses these two bits.

@@ -3708,26 +3697,6 @@ describe('src/cy/commands/actions/click', () => {
cy.rightclick()
})

it('throws when subject is not in the document', (done) => {
Copy link
Member

Choose a reason for hiding this comment

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

same question, why remove?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same reason as above.

@@ -628,25 +654,6 @@ is being covered by another element:
})
})

it('waits until input stops animating', {
Copy link
Member

Choose a reason for hiding this comment

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

Why did we remove this test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test was badly written (I'm not throwing shade, I wrote it when I was new at the company :D ); it mocks things unnecessarily, and duplicates the tests around it. "Are we using actionability? Yes? Then we don't need to assert of every quirk of actionability in every command that uses it.

expect(err.docsUrl).to.eq('https://on.cypress.io/custom-commands')

done()
})

Cypress.Commands._addQuery('get', () => {
Cypress.Commands.addQuery('get', () => {
Copy link
Member

Choose a reason for hiding this comment

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

I thought we were holding off on exposing this API?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We were holding off doing it in 10.x - didn't want to release the API until we'd completed the migration and discovered any pitfalls. That was a concern while I was working in 'develop`, but I moved to a feature branch for unrelated reasons shortly after we made the decision.

My understanding is that we'll make it fully public in 12.x.

@@ -1936,7 +1957,7 @@ export default {
> ${subjectChainToString(obj.subjectChain)}`
},
state_subject_deprecated: {
message: `${cmd('state', '\'subject\'')} has been deprecated and will be removed in a future release. Consider migrating to ${cmd('currentSubject')} instead.`,
message: `${cmd('state', '\'subject\'')} has been deprecated and will be removed in a future release. Consider migrating to ${cmd('subject')} instead.`,
Copy link
Member

Choose a reason for hiding this comment

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

Changing from cy.currentSubejct to cy.subject is breaking. Do we need to make a err message for those using who may have already updated to use cy.currentSubject to say it's been removed & they need to use cy.subject instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd prefer to mention it in the changelog as breaking and not leave behind any cruft.

As far as I know, the only consumer of state('subject') (other than us) is cypress-testing-library, and they haven't updated yet.

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.

I left some comments (mostly questions) which were answered. I tried it out with some of the examples in #7306 and it seems to work great.

I don't have much else to add, but I don't think I'm deep enough in this PR for my approve to carry as much weight as the other reviewers, so I'm going to hold off ✅ for now.

Anything else I can do to move this forward @BlueWinds? Maybe kicking off the testing library discussion internally? Do you think we have bandwidth before 12.0 to do this?

@BlueWinds
Copy link
Contributor Author

Anything else I can do to move this forward @BlueWinds? Maybe kicking off the testing library discussion internally? Do you think we have bandwidth before 12.0 to do this?

I should have the associated docs PR ready for review by end of day today, and have started talking with the community team about other plugins and how to handle working with the community over these changes. I hope to open a PR for cypress-testing-library by end of week.

With that said, I'd like to get some approvals on this and merge it in, so that we can see the results of the binary build in issue-7306 and I can work on any new tests failing there. That's my next step - approvals and merging, while working on the less technical side of the release plan.

cli/types/cypress.d.ts Outdated Show resolved Hide resolved
Co-authored-by: Chris Breiding <[email protected]>
@BlueWinds BlueWinds merged commit 45953f7 into issue-7306 Nov 9, 2022
@BlueWinds BlueWinds deleted the issue-7306-addQuery-actionability branch November 9, 2022 20:04
mjhenkes pushed a commit that referenced this pull request Nov 14, 2022
* feat: Commands.addSelector, and migrate .get() to be a selector

* Fix for failed tests

* Last test fix

* More test fixes

* Self review changes

* Remove the concept of prevSubject from selectors entirely

* Rename addSelector to addQuery

* Quick fix for last commit

* Fix TS

* Fix merge from develop

* Add types and other review updates

* Increase timeout to try fixing flakiness

* Rename addQuery to _addQuery

* Fix typo in previous commit

* Fix TS

* Include AUT assertion in cy.get()

* Fix for previous commit

* Review feedback

* Minor test improvement

* Swifter failure on sizzle syntax error

* Better solution for refetching current subject in verifyUpcomingAssertions

* Command IDs now include their chainerId

* Revert "chore: Revert "feat: _addQuery() (#23665)" (#24022)"

This reverts commit f399994.

* feat: move .contains() and .shadow() to be queries; remove cy.ng() (#23791)

* First stab at removing old .get() implementation

* Fix TS and a couple of tests

* Fix tests and TS

* Fix case-sensitivity for .contains()

* Stop TS complaining

* Rework cy-contains jquery expression

* Add comments, make ts happy

* Fix one test, review feedback

* Review updates

* Fix additional tests

* Fix accidental deletion of vital code

* One more try at getting logs right

* Fix race condition in cross-origin .contains

* Add commented out test to ensure .within() works properly with selectors

* Fix for sessions + query subject chaining

* Fix mixing .within() shadow DOM and .contains() in same chainer

* One more attempt at .within + .contains

* Fix rebase commits

* feat: addQuery Remaining Queries (#24203)

* First stab at removing old .get() implementation

* Fix TS and a couple of tests

* Fix tests and TS

* Fix case-sensitivity for .contains()

* Stop TS complaining

* Rework cy-contains jquery expression

* Add comments, make ts happy

* Fix one test, review feedback

* Review updates

* Fix additional tests

* Fix accidental deletion of vital code

* One more try at getting logs right

* Fix race condition in cross-origin .contains

* Add commented out test to ensure .within() works properly with selectors

* Fix for sessions + query subject chaining

* Fix mixing .within() shadow DOM and .contains() in same chainer

* One more attempt at .within + .contains

* Fix rebase commits

* Update many commands to be queries; improve log message around invalid subjects

* Update connectors, location, focused and window commands to queries

* Return noop to a command and not a query (to avoid implicit assertions)

* More test fixes

* Fix test failures

* Fix for weird-ass frontend-component test

* Error message improvements

* Fix for broken system test

* Update withinSubject to use subject chain

* Test clarifications

* Unbreak cypress-testing-library via withinState backwards compatibility

* Typo in last commit

* Improvement for assertion following failed traversal

* feat: Fix detached DOM errors for all Cypress commands (#24417)

* First stab at removing old .get() implementation

* Fix TS and a couple of tests

* Fix tests and TS

* Fix case-sensitivity for .contains()

* Stop TS complaining

* Rework cy-contains jquery expression

* Add comments, make ts happy

* Fix one test, review feedback

* Review updates

* Fix additional tests

* Fix accidental deletion of vital code

* One more try at getting logs right

* Fix race condition in cross-origin .contains

* Add commented out test to ensure .within() works properly with selectors

* Fix for sessions + query subject chaining

* Fix mixing .within() shadow DOM and .contains() in same chainer

* One more attempt at .within + .contains

* Fix rebase commits

* Update many commands to be queries; improve log message around invalid subjects

* Update connectors, location, focused and window commands to queries

* Return noop to a command and not a query (to avoid implicit assertions)

* More test fixes

* Fix test failures

* Fix for weird-ass frontend-component test

* Error message improvements

* Fix for broken system test

* Update withinSubject to use subject chain

* Test clarifications

* Unbreak cypress-testing-library via withinState backwards compatibility

* Typo in last commit

* Improvement for assertion following failed traversal

* WIP adding query support to

* More work on actionability + detached dom

* Fix TS, rename _addQuery to addQuery

* Another try to fix types

* Fix lint

* Fix for bad merge

* Fixes for a couple more tests

* Increase timeout 50ms -> 100ms on certain tests failing in CI

* Switch to new branch of cypress-testing-library

* Update lockfile

* Fix yarn.lock with latest version of forked testing-library

* More test fixes

* Fix TS again

* Increase test assertion timeout so it passes on slow browsers (webkit)

* Apply suggestions from code review

Co-authored-by: Emily Rohrbough <[email protected]>
Co-authored-by: Zach Bloomquist <[email protected]>

* More review changes

* Fix selectFile tests based on updated error message

* Improve types and type comments for Commands.add

* Undo change to Commands.add types

* Update yarn lockfiles again

* Remove overwriteQuery from Cy12; .focused() now respects passed in timeout

* Update cli/types/cypress.d.ts

Co-authored-by: Chris Breiding <[email protected]>

* Restore .uncheck() tests

Co-authored-by: Emily Rohrbough <[email protected]>
Co-authored-by: Zach Bloomquist <[email protected]>
Co-authored-by: Chris Breiding <[email protected]>

* Fix for hanging driver test after merge

* Fix for app component test

Co-authored-by: Emily Rohrbough <[email protected]>
Co-authored-by: Zach Bloomquist <[email protected]>
Co-authored-by: Chris Breiding <[email protected]>
yjaaidi added a commit to jscutlery/devkit that referenced this pull request May 22, 2023
Since Cypress 12, `invoke()` doesn't wait for promises to resolve and just returns the promise as is.
This was fixed by using `then()` instead.

Harness usage without chaining was not affected. i.e. `getHarness(...).then(harness => harness.selectCell(42))`.

Cf. https://docs.cypress.io/guides/references/changelog#12-0-0
Cf. cypress-io/cypress#24417

Closes #232
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.

5 participants