5

What are the differences are there between using either of these methods:


cy.get('.wtv').find('.sub-wtv');

cy.get('.wtv').within(() => {cy.get('.sub-wtv');});

From the documentation
https://docs.cypress.io/api/commands/find.html
https://docs.cypress.io/api/commands/within.html

They both let us work with a "sub DOM", in which we can do whatever we would like, like searching a specific element and assert it.

Tim
  • 2,563
  • 1
  • 23
  • 31
notihs
  • 654
  • 7
  • 19
  • 1
    If you chain something else off these commands, e.g `.should(...)`, they will provide different subjects. `cy.get('.wtv').find('.sub-wtv')` will pass **sub-wtv** but `cy.get('.wtv').within(() => {cy.get('.sub-wtv');})` will pass **wtv**. – Ackroydd Nov 19 '20 at 21:30
  • @Ackroydd it depends. if you use `.should()` inside within like `cy.get('.sub-wtv').should();` My question is more turned to understand which advantages one of the methods has over the other, and all I can see is that for 90% of the cases, none :P I would say that we should use one over the other just for those 10% edge cases in which only one of them is able to provide what we really want. It still seems to me that both of them work as a redudancy of each other (and that you can use whatever you would like the best, in most cases :) ) – notihs Nov 20 '20 at 11:00
  • Looks like you are missing the point about the different subjects. – Ackroydd Nov 20 '20 at 11:49

2 Answers2

6

.find() is used for single search of an element, but only limits your actions to that element

.within() lets you to change the scope of searching the sub elements and call them directly with cy.get('subelementSelector'), and also work with them. The down side is, you can`t call elements from outside the scope of the parent element.

The third way is then. cy.get('elementSelector').then(element=>{//some code}) - this allows you to pass the element to a function for usage. You can search sub elements within with cy.get(element).find('subelementSelector'). Also you can use the usual commands for elements located outside the parent element scope. This has longer sintax, but greater scope.

Edit: To clarify .find() - allows a single usage of an element .within(passedFunction()=>{}) - changes the scope for DOM elements of the passedFunction to just child elements .then(element=>{}) - doesn't change the scope, but creates a JQ variable of the variable, that is available to use in the then function cy.get('parentSelector childSelector') - is the css way of getting the same result as .find()

notihs
  • 654
  • 7
  • 19
Rosen Mihaylov
  • 1,363
  • 2
  • 10
  • 2
    Another way is to just combine the selectors into a single `.get` call. `cy.get('.wtv .sub-wtv')` – Cory Danielson Nov 19 '20 at 18:45
  • Yeah The way @CoryDanielson has said works like `.find()`, but with less code – Rosen Mihaylov Nov 19 '20 at 19:08
  • 1
    @RosenMihaylov from your explanation am I assuming correctly that doing `cy.get().find().then()` is exactly the same as `cy.get().within()`? We are working on the same subDOM in both, right? :) – notihs Nov 19 '20 at 19:12
  • `(cy.get().find().then()` would not change the scope but will allow you to use the sub element in the then function, `.then()` doesnt change the scope - it just returns as a JQ variable the thing it recieves. And if you wana chain that jq element with cypress command you need to use `cy.wrap()` or `cy.get()`. in a `.then(element =>{})` scope you still need to use `cy.get(element).find(subelement)`, but the upside is you have the element as a variable. In a ..`.within()` scope - you directly use `cy.get(subelement)`. From the listed commands, only `.within` changes the scope – Rosen Mihaylov Nov 19 '20 at 19:17
  • Final question then (if I may :D ) - if i do use `cy.get('.wtv').within()` and inside within i do something like `cy.get('.sub-wtv')` all the following commands inside that within will be working with which DOM? the `'.wtv'.within()`one or the `.sub-wtv` ? or if i want to use the `.sub-wtv` I must do another `within()` [one within with another within inside?] – notihs Nov 19 '20 at 19:26
  • @CoryDanielson yes, your solution avoids using either BUT if we are talking about multiple levels doing something like what you suggest can easily become a pain and dangerously close to xPath (which in most cases we should avoid) – notihs Nov 19 '20 at 19:29
  • 1
    Every other command will work normally, you will only be limited for the elements you have access to, while wroking in the `.within()` So if your selector `cy.get('.sub-wtv')` is correct and a part of the parent - it will yeld the normal element. And you can use another `.within()` that will narrow the scope even more and only the inner one will be active in the inner within. Thou at that point The test would become unreadable and bettter to use the sugestion @CoryDanielson made in the first place – Rosen Mihaylov Nov 19 '20 at 19:30
  • just to finalize this discussion, can you confirm that performing .find() is way faster than doing .within() - in my case .find() takes +/- 1 ms to execute while .within() takes around 100ms – notihs Nov 20 '20 at 11:02
  • About which is faster - haven`t tried to find out. I use whichever is better suited for the test. – Rosen Mihaylov Nov 20 '20 at 11:34
1

Find:

Get the descendent DOM element/s of a specific selector. find() method always chains with other methods that return DOM elements and never get chain-ed with "cy" object.

 .find(selector)
 .find(selector, options)

 cy.get('.article').find('footer') // Yield 'footer' within '.article'

Within:

It sorts those situation where we want to search web-element/s inside a parent web-element. It scopes all subsequent cy commands to within parent element. Useful when working within a particular group of elements such as form. This is written with a call back function eg. within(callbackFn).

 .within(callbackFn)
 .within(options, callbackFn)

 cy.get('.list').within(($list) => {}) // Yield the `.list` and scope all commands within it

   // validate placeholder attributes
   cy.get('.query-form').within(() => {
   cy.get('input:first').should('have.attr', 'placeholder', 'Email')
   cy.get('input:last').should('have.attr', 'placeholder', 'Password')
     })
Atul KS
  • 908
  • 11
  • 21