Skip to content

Commit 0dcf3a0

Browse files
authored
.within() now throws an error if given more than one subject (#4898)
* .within() now throws error when passed more than one subject. * Add migration guide, update based on reviews
1 parent a4ad8fd commit 0dcf3a0

File tree

3 files changed

+86
-10
lines changed

3 files changed

+86
-10
lines changed

content/_changelogs/12.0.0.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ _Released MM/DD/YYYY_
1111
- Cypress now throws an error if commands are invoked from inside a `.should()`
1212
callback. This previously resulted in unusual and undefined behavior; it is
1313
now explicitly an error.
14+
- The [`.within()`](/api/commands/within) command now throws an error if given
15+
more than one DOM element as a subject. This is done for consistency - in
16+
older versions, some commands inside a `.within()` block would respect all
17+
passed in elements, while others silently discarded subjects beyond the first
18+
and `.screenshot()` would throw an error.
1419
- `Cookies.defaults` and `Cookies.preserveOnce` have been removed. Please update
1520
to use [`cy.session()`](/api/commands/session) to preserve session details
1621
between tests. Addresses

content/api/commands/within.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,17 @@ chain further commands that rely on the subject after `.within()`.
2020
**<Icon name="check-circle" color="green"></Icon> Correct Usage**
2121

2222
```javascript
23-
cy.get('.list').within(($list) => {}) // Yield the `.list` and scope all commands within it
23+
cy.get('.list')
24+
.first()
25+
.within(($list) => {}) // Yield the first `.list` and scope all commands within it
2426
```
2527

2628
**<Icon name="exclamation-triangle" color="red"></Icon> Incorrect Usage**
2729

2830
```javascript
2931
cy.within(() => {}) // Errors, cannot be chained off 'cy'
3032
cy.getCookies().within(() => {}) // Errors, 'getCookies' does not yield DOM element
33+
cy.get('div').within(($divs) => {}) // Probably errors, because get('div') yields multiple elements
3134
```
3235

3336
### Arguments
@@ -179,7 +182,8 @@ cy.get('form').within(($form) => {
179182

180183
### Requirements [<Icon name="question-circle"/>](/guides/core-concepts/introduction-to-cypress#Chains-of-Commands)
181184

182-
<List><li>`.within()` requires being chained off a previous command.</li></List>
185+
- `.within()` requires being chained off a previous command that yields exactly
186+
one DOM element.
183187

184188
### Assertions [<Icon name="question-circle"/>](/guides/core-concepts/introduction-to-cypress#Assertions)
185189

@@ -211,10 +215,11 @@ outputs the following:
211215

212216
## History
213217

214-
| Version | Changes |
215-
| --------------------------------------------- | ------------------------------------------------------- |
216-
| [< 0.3.3](/guides/references/changelog#0-3-3) | `.within()` command added |
217-
| [5.4.0](/guides/references/changelog#5-4-0) | fixed the yielded value to always be the parent element |
218+
| Version | Changes |
219+
| --------------------------------------------- | -------------------------------------------------------------------------------- |
220+
| [12.0.0](/guides/references/changelog#12-0-0) | `.within()` now throws an error when given more than one element as the subject. |
221+
| [5.4.0](/guides/references/changelog#5-4-0) | fixed the yielded value to always be the parent element |
222+
| [< 0.3.3](/guides/references/changelog#0-3-3) | `.within()` command added |
218223

219224
## See also
220225

content/guides/references/migration-guide.md

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,10 @@ command instead.
268268

269269
#### `.invoke()`
270270

271-
[`.invoke()`](/api/commands/invoke) now throws an error if the function returns
272-
a promise. If you wish to call a method that returns a promise and wait for it
273-
to resolve, use [`.then()`](/api/commands/then) instead of `.invoke()`.
271+
The [`.invoke()`](/api/commands/invoke) command now throws an error if the
272+
function returns a promise. If you wish to call a method that returns a promise
273+
and wait for it to resolve, use [`.then()`](/api/commands/then) instead of
274+
`.invoke()`.
274275

275276
```diff
276277
cy.wrap(myAPI)
@@ -295,7 +296,72 @@ assertions to their own chain. For example, rewrite
295296

296297
#### `.should()`
297298

298-
[`.should()`](/api/commands/should) now throws an error if Cypress commands are
299+
The [`.should()`](/api/commands/should) assertion now throws an error if Cypress
300+
commands are invoked from inside a `.should()` callback. This previously
301+
resulted in unusual and undefined behavior. If you wish to execute a series of
302+
commands on the yielded value, use`.then()` instead.
303+
304+
```diff
305+
cy.get('button')
306+
- .should(($button) => {
307+
308+
})
309+
+ .then(api => api.makeARequest('http://example.com'))
310+
.then(res => { ...handle response... })
311+
```
312+
313+
#### `.within()`
314+
315+
The [`.within()`](/api/commands/within) command now throws an error if it is
316+
passed multiple elements as the subject. This previously resulted in
317+
inconsistent behavior, where some commands would use all passed in elements,
318+
some would use only the first and ignore the rest, and
319+
[`.screenshot()`](/api/commands/screenshot) would throw an error if used inside
320+
a `.within()` block with multiple elements.
321+
322+
If you were relying on the old behavior, you have several options depending on
323+
the desired result.
324+
325+
The simplest option is to reduce the subject to a single element.
326+
327+
```diff
328+
cy.get('tr')
329+
+ .first() // Limit the subject to a single element before calling .within()
330+
.within(() => {
331+
cy.contains('Edit').click()
332+
})
333+
```
334+
335+
If you have multiple subjects and wish to run commands over the collection as a
336+
whole, you can alias the subject rather than use `.within()`.
337+
338+
```diff
339+
cy.get('tr')
340+
- .within(() => {
341+
- cy.get('td').should('have.class', 'foo')
342+
- cy.get('td').should('have.class', 'bar')
343+
- })
344+
+ .as('rows') // Store multiple elements as an alias
345+
346+
+cy.get('@rows').find('td').should('have.class', 'foo')
347+
+cy.get('@rows').find('td').should('have.class', 'bar')
348+
```
349+
350+
Or if you have a collection and want to run commands over every element, use
351+
`.each()` in conjunction with `.within()`.
352+
353+
```diff
354+
cy.get('tr')
355+
- .within(() => {
356+
- cy.contains('Edit').should('have.attr', 'disabled')
357+
- })
358+
+ .each($tr => {
359+
+ cy.wrap($tr).within(() => {
360+
+ cy.contains('Edit').should('have.attr', 'disabled')
361+
+ })
362+
+ })
363+
```
364+
299365
invoked from inside a `.should()` callback. This previously resulted in unusual
300366
and undefined behavior. If you wish to execute a series of commands on the
301367
yielded value, use`.then()` instead.

0 commit comments

Comments
 (0)