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

Documentation for Cypress.ensure #5054

Merged
merged 9 commits into from
Feb 15, 2023
Merged

Conversation

BlueWinds
Copy link
Contributor

This part of our API was added in Cypress 12, but never documented properly. Pulled the changes out from #5048 into its own PR.

@vercel
Copy link

vercel bot commented Feb 7, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated
cypress-documentation ✅ Ready (Inspect) Visit Preview 💬 Add your feedback Feb 15, 2023 at 8:12PM (UTC)

Comment on lines +37 to +45
:::caution

Many of these functions accept an optional `onFail` argument. This is a legacy
feature used to customize the thrown error, and may be removed in a future
release; we recommend against relying on it. If you need more control over the
error thrown, write your own `ensure` function instead.

:::

Copy link
Contributor

Choose a reason for hiding this comment

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

IMO we should just not document this, since the user can just try...catch to get the same functionality as onFail, right? Then we can just rip it out when we're done using it internally.

Suggested change
:::caution
Many of these functions accept an optional `onFail` argument. This is a legacy
feature used to customize the thrown error, and may be removed in a future
release; we recommend against relying on it. If you need more control over the
error thrown, write your own `ensure` function instead.
:::

Copy link
Member

Choose a reason for hiding this comment

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

I gave a similar suggestion to not document this #5048 (comment). The onFail ties in to how this would be removed up if this callback matched the command onFail behavior, so it's require quite a bit of user-knowledge to handle this correctly. I like @flotwig's suggestion for handling with try-catch.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason I documented it here is because people's intellisense / TS will show them that there's an additional argument, and they'll see it if they examine the code.

Undocumented arguments are the worst, so I figure a little warning "this exists but don't use it" would be preferable.

Was the response I gave to Emily, and I still feel the same way - if it exists, we should not leave it silently undocumented. I've found myself in this boat several times dealing with other projects, where I see an argument is there, but the documentation is completely silent. What does it do? Why is it there? Your own code uses it, to what purpose?

I think a warning "it exists, but don't use it" is appropriate, as well as not including it in the documented function signatures.

Comment on lines 28 to 35
Cypress.ensure.isAttached(subject, name, cy)​
Cypress.ensure.isNotDisabled(subject, name)​
Cypress.ensure.isNotHiddenByAncestors(subject, name)​
Cypress.ensure.isNotReadonly(subject, name)​
Cypress.ensure.isScrollable($el, name)​
Cypress.ensure.isStrictlyVisible(subject, name)​
Cypress.ensure.isVisible(subject, name)​
```
Copy link
Member

Choose a reason for hiding this comment

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

Why are most of the referenced as "subject" vs "$el"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

subject is what they'll see in intellesense, it's how these functions are used in the Cypress repo itself (), and the function signature of a query is (subject: Any) => Any.

The primary use of these is to call them on a subject, to ensure the subject matches the conditions, so I think they should be documented that way. $el is extremely non-descriptive in comparison, and doesn't indicate anything unless you're familiar with our convention that '$' means "A jquery object".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed the one remaining over to subject so they're all consistent.

Cypress.ensure.isChildCommand(command, args, cy)​

// Type of argument
Cypress.ensure.isType(subject, type, name, cy)​
Copy link
Member

Choose a reason for hiding this comment

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

I can't remember, but do these expect subject to be anything? We don't reference subject must in the docs and I'm thinking it might be helpful to expand above on what a subject might be for users who aren't familiar

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 reference 'subject' all over the docs.

https://docs.cypress.io/guides/core-concepts/introduction-to-cypress

How Cypress manages subjects and chains of commands

The very first paragraph of the 'core concepts' guide discusses them.

It's very important to understand the mechanism Cypress uses to chain commands together. It manages a Promise chain on your behalf, with each command yielding a 'subject' to the next command

https://docs.cypress.io/api/cypress-api/custom-commands#Arguments

prevSubject | Boolean, String or Array | false | how to handle the previously yielded subject.
In addition to controlling the command's implicit behavior you can also add declarative subject validations such as:
element: requires the previous subject be a DOM element
document: requires the previous subject be the document
window: requires the previous subject be the window

and all over that page.

Cypress.Commands.addQuery('verifyElementActionable', function (id) {
// Throws an early error if this query is chained directly off `cy`, eg.
// `cy.verifyElementActionable('foo')` would throw an error
Cypress.ensure.isChildCommand(command, args, cy)​
Copy link
Member

Choose a reason for hiding this comment

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

Here you said query's don't have a concept of parent/child commands. Is this a realistic example? Also where is command, args, and cy coming form?

Copy link
Contributor Author

@BlueWinds BlueWinds Feb 13, 2023

Choose a reason for hiding this comment

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

Fixed the example to show where they come from.

Yes it's a realistic example; it's exactly how we use this function in our own code. Queries don't by default have a concept of being child / parent - you can make them have one by using ensure.isChildCommand(). It's just not baked into the query API the way it's baked into the command API.

Copy link
Member

Choose a reason for hiding this comment

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

It's just not baked into the query API the way it's baked into the command API.

Gottchya - when you said it didn't have the concept of it, I had understood it as "parent" / "child" truely was no longer a concept vs a difference in how the concept was implemented.

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 created isChildCommand() based on our docs ("must be chained off cy" / "must not be chained off cy"). But the more I work with our command interface, the more I realize it just doesn't make sense: all uses of ensure.isChildCommand would be better served by a more specific error in the individual command.

We currently use it in two places:

  • .within(), which should actually be asserting that the subject is an element or document (.isType()).
  • .as(), which should actually be asserting that the subject is not null (or that it's after .intercept()).

In cypress-io/cypress#25738 (comment), I added it to .should() - but this is a breaking change, and we've decided to walk it back.

Basically, a generic "this must be a child command" is never the correct error to throw; we want to point users more usefully to what a specific command requires as a subject.

I've opened cypress-io/cypress#25811 to track this code work.

For this PR, I'm thinking I'll just remove the docs around .isChildCommand(), so that removing it won't be a breaking change (since we won't be documenting it as part of our public interface).

Copy link
Contributor Author

@BlueWinds BlueWinds Feb 14, 2023

Choose a reason for hiding this comment

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

tl;dr - it shouldn't still be a concept, but it leaked into queries through a new implementation. I'll get a paper towel. 😛

BlueWinds and others added 2 commits February 13, 2023 08:57
@BlueWinds BlueWinds merged commit c516f7b into main Feb 15, 2023
@BlueWinds BlueWinds deleted the issue-4881-cypress-ensure-docs branch February 15, 2023 20:19
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.

Document Cypress.ensure
3 participants