From e4d802a87f1a076b0aa83925c6a2d561eeec3366 Mon Sep 17 00:00:00 2001 From: ziad_elsoudy <53182896+ziad404@users.noreply.github.com> Date: Sun, 10 Aug 2025 14:54:15 +0300 Subject: [PATCH 1/9] Add best practices for element interaction patterns and command chaining --- docs/app/core-concepts/best-practices.mdx | 169 ++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/docs/app/core-concepts/best-practices.mdx b/docs/app/core-concepts/best-practices.mdx index 27b9f3e0f2..37b6ea0d92 100644 --- a/docs/app/core-concepts/best-practices.mdx +++ b/docs/app/core-concepts/best-practices.mdx @@ -951,6 +951,175 @@ cy.wait('@getUsers') // <--- wait explicitly for this route to finish cy.get('table tr').should('have.length', 2) ``` +## Element Interaction Patterns and Command Chaining + +:::danger + + **Anti-Pattern:** Using `then()` unnecessarily or re-querying the same element multiple times when chaining would work. + +::: + +:::tip + + **Best Practice:** Use command chaining when possible, and only use `then()` when you need to perform complex operations or work with the actual DOM element. + +::: + +A common question arises when performing multiple actions on the same element: should you chain commands or use `then()` to wrap all actions? Let's examine the best approaches for common interaction patterns. + +### Multiple Actions on the Same Element + +When you need to perform several actions on the same element (like focus, clear, type, and blur), you have two main approaches: + +#### Option 1: Command Chaining (Recommended) + +```js +// RECOMMENDED: Chain commands when possible +cy.get('[data-cy="input-field"]') + .focus() + .clear() + .type('new value') + .blur() +``` + +#### Option 2: Using then() (Only when necessary) + +```js +// ONLY USE when you need to work with the actual DOM element +cy.get('[data-cy="input-field"]').then(($input) => { + cy.wrap($input).focus() + cy.wrap($input).clear() + cy.wrap($input).type('new value') + cy.wrap($input).blur() +}) +``` + +### Why Command Chaining is Usually Better + +**Performance Benefits:** +- Cypress optimizes chained commands and doesn't re-query the element unnecessarily. +- The element reference is passed through the command chain automatically. +- No additional overhead from `cy.wrap()` calls. + +**Simplicity and Readability:** +- More concise and easier to read. +- Follows Cypress's natural command pattern. +- Less nested code structure. + +**Automatic Retries:** +- Each command in the chain automatically retries if the element becomes stale. +- Cypress handles DOM updates and element re-querying automatically. + +### When to Use `then()` + +Use `then()` only when you need to: + +1. **Access DOM element properties or methods directly:** +```js +cy.get('[data-cy="input-field"]').then(($input) => { + const initialValue = $input.val() + // Do something with the initial value + if (initialValue !== '') { + cy.wrap($input).clear().type('new value') + } +}) +``` + +2. **Perform conditional logic based on element state:** +```js +cy.get('[data-cy="toggle"]').then(($toggle) => { + if ($toggle.hasClass('active')) { + cy.wrap($toggle).click() + } +}) +``` + +3. **Work with multiple elements from the same query:** +```js +cy.get('[data-cy="list-item"]').then(($items) => { + const count = $items.length + cy.log(`Found ${count} items`) + + // Work with specific items + cy.wrap($items.first()).should('contain', 'First item') + cy.wrap($items.last()).should('contain', 'Last item') +}) +``` + +### Common Misconceptions + +**"Using `then()` prevents detached DOM errors"** + +This is not accurate. Cypress automatically handles element re-querying in both chaining and `then()` scenarios. The framework is designed to handle DOM updates gracefully. + +**"Element is found only once with `then()`"** + +While `then()` captures the element at that moment, Cypress commands within `then()` (like those wrapped with `cy.wrap()`) still perform their own element queries and retries as needed. + +### Best Practice Examples + +#### Form Interaction (Good) +```js +describe('form interaction', () => { + it('updates input field correctly', () => { + cy.get('[data-cy="email-input"]') + .should('be.visible') + .focus() + .clear() + .type('user@example.com') + .should('have.value', 'user@example.com') + .blur() + }) +}) +``` + +#### Conditional Actions (When `then()` is appropriate) +```js +describe('conditional interactions', () => { + it('handles different input states', () => { + cy.get('[data-cy="search-input"]').then(($input) => { + const currentValue = $input.val() + + if (currentValue) { + // Clear existing value and enter new one + cy.wrap($input).clear().type('new search term') + } else { + // Just enter the new value + cy.wrap($input).type('new search term') + } + }) + }) +}) +``` + +#### Working with Element Collections +```js +describe('element collections', () => { + it('processes multiple items', () => { + cy.get('[data-cy="product-card"]').then(($cards) => { + // Process each card + $cards.each((index, card) => { + cy.wrap(card) + .should('be.visible') + .find('[data-cy="product-title"]') + .should('not.be.empty') + }) + }) + }) +}) +``` + +### Performance Considerations + +Command chaining is typically more performant because: + +- Cypress optimizes the command queue for chained operations. +- No additional `cy.wrap()` overhead. +- Automatic smart retries and element resolution. +- Better memory management with fewer closures. + +Use `then()` judiciously and only when the additional functionality it provides is necessary for your specific use case. + ## Running Tests Intelligently As your test suite grows and takes longer to run, you may find yourself hitting From 0e56d9c971040a41a88f6fa6ed6bd6011dbc9787 Mon Sep 17 00:00:00 2001 From: ziad_elsoudy <53182896+ziad404@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:14:16 +0300 Subject: [PATCH 2/9] Resolve lint issue --- docs/app/core-concepts/best-practices.mdx | 112 ++++++++++++---------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/docs/app/core-concepts/best-practices.mdx b/docs/app/core-concepts/best-practices.mdx index 37b6ea0d92..637e64c807 100644 --- a/docs/app/core-concepts/best-practices.mdx +++ b/docs/app/core-concepts/best-practices.mdx @@ -42,16 +42,16 @@ and start testing. :::danger - **Anti-Pattern:** Sharing page objects, -using your UI to log in, and not taking shortcuts. + **Anti-Pattern:** Sharing page +objects, using your UI to log in, and not taking shortcuts. ::: :::tip - **Best Practice:** Test specs in isolation, -programmatically log into your application, and take control of your application's -state. + **Best Practice:** Test specs in +isolation, programmatically log into your application, and take control of your +application's state. ::: @@ -77,15 +77,16 @@ in our examples. :::danger - **Anti-Pattern:** Using highly brittle -selectors that are subject to change. + **Anti-Pattern:** Using highly +brittle selectors that are subject to change. ::: :::tip - **Best Practice:** Use `data-*` attributes -to provide context to your selectors and isolate them from CSS or JS changes. + **Best Practice:** Use `data-*` +attributes to provide context to your selectors and isolate them from CSS or JS +changes. ::: @@ -247,16 +248,16 @@ your application is working as expected for people with disabilities and the tec :::danger - **Anti-Pattern:** Trying to assign -the return value of Commands with `const`, `let`, or `var`. + **Anti-Pattern:** Trying to +assign the return value of Commands with `const`, `let`, or `var`. ::: :::tip - **Best Practice:** Use [aliases and closures -to access and store](/app/core-concepts/variables-and-aliases) what Commands yield -you. + **Best Practice:** Use [aliases and +closures to access and store](/app/core-concepts/variables-and-aliases) what +Commands yield you. ::: @@ -306,18 +307,20 @@ For working with either of these patterns, please read our :::danger - **Anti-Pattern:** Trying to visit -or interact with sites or servers you do not control. + **Anti-Pattern:** Trying to +visit or interact with sites or servers you do not control. ::: :::tip **Best Practice:** Only test websites -that you control. Try to avoid visiting or requiring a 3rd party server. If you choose, -you may use [`cy.request()`](/api/commands/request) to talk to 3rd party servers -via their APIs. If possible, cache results via [`cy.session()`](/api/commands/session) -to avoid repeat visits. See also reasons against [Testing Apps You Don't Control](/app/end-to-end-testing/writing-your-first-end-to-end-test#Testing-Apps-You-Dont-Control). +that you control. Try to avoid visiting or requiring a 3rd party server. If you +choose, you may use [`cy.request()`](/api/commands/request) to talk to 3rd party +servers via their APIs. If possible, cache results via +[`cy.session()`](/api/commands/session) to avoid repeat visits. See also reasons +against [Testing Apps You Don't +Control](/app/end-to-end-testing/writing-your-first-end-to-end-test#Testing-Apps-You-Dont-Control). ::: @@ -457,21 +460,22 @@ email's functionality and visual style: exposes an API to read off emails. You will then need the proper authentication credentials, which your server could provide, or you could use environment variables. Some email services already provide - Cypress plugins to access emails. + Cypress plugins to access + emails. ## Having Tests Rely On The State Of Previous Tests :::danger - **Anti-Pattern:** Coupling multiple -tests together. + **Anti-Pattern:** Coupling +multiple tests together. ::: :::tip - **Best Practice:** Tests should always -be able to be run independently from one another **and still pass**. + **Best Practice:** Tests should +always be able to be run independently from one another **and still pass**. ::: @@ -593,15 +597,15 @@ can be run independently and pass. :::danger - **Anti-Pattern:** Acting like you're -writing unit tests. + **Anti-Pattern:** Acting like +you're writing unit tests. ::: :::tip - **Best Practice:** Add multiple assertions -and don't worry about it + **Best Practice:** Add multiple +assertions and don't worry about it ::: @@ -686,8 +690,8 @@ or `afterEach` hooks to clean up state. :::tip - **Best Practice:** Clean up state **before** -tests run. + **Best Practice:** Clean up state +**before** tests run. ::: @@ -881,15 +885,16 @@ script. :::danger - **Anti-Pattern:** Waiting for arbitrary -time periods using [`cy.wait(Number)`](/api/commands/wait#Time). + **Anti-Pattern:** Waiting for +arbitrary time periods using [`cy.wait(Number)`](/api/commands/wait#Time). ::: :::tip - **Best Practice:** Use route aliases or -assertions to guard Cypress from proceeding until an explicit condition is met. + **Best Practice:** Use route aliases +or assertions to guard Cypress from proceeding until an explicit condition is +met. ::: @@ -955,13 +960,17 @@ cy.get('table tr').should('have.length', 2) :::danger - **Anti-Pattern:** Using `then()` unnecessarily or re-querying the same element multiple times when chaining would work. + **Anti-Pattern:** Using +`then()` unnecessarily or re-querying the same element multiple times when +chaining would work. ::: :::tip - **Best Practice:** Use command chaining when possible, and only use `then()` when you need to perform complex operations or work with the actual DOM element. + **Best Practice:** Use command +chaining when possible, and only use `then()` when you need to perform complex +operations or work with the actual DOM element. ::: @@ -975,11 +984,7 @@ When you need to perform several actions on the same element (like focus, clear, ```js // RECOMMENDED: Chain commands when possible -cy.get('[data-cy="input-field"]') - .focus() - .clear() - .type('new value') - .blur() +cy.get('[data-cy="input-field"]').focus().clear().type('new value').blur() ``` #### Option 2: Using then() (Only when necessary) @@ -997,16 +1002,19 @@ cy.get('[data-cy="input-field"]').then(($input) => { ### Why Command Chaining is Usually Better **Performance Benefits:** + - Cypress optimizes chained commands and doesn't re-query the element unnecessarily. - The element reference is passed through the command chain automatically. - No additional overhead from `cy.wrap()` calls. **Simplicity and Readability:** + - More concise and easier to read. - Follows Cypress's natural command pattern. - Less nested code structure. **Automatic Retries:** + - Each command in the chain automatically retries if the element becomes stale. - Cypress handles DOM updates and element re-querying automatically. @@ -1015,6 +1023,7 @@ cy.get('[data-cy="input-field"]').then(($input) => { Use `then()` only when you need to: 1. **Access DOM element properties or methods directly:** + ```js cy.get('[data-cy="input-field"]').then(($input) => { const initialValue = $input.val() @@ -1026,6 +1035,7 @@ cy.get('[data-cy="input-field"]').then(($input) => { ``` 2. **Perform conditional logic based on element state:** + ```js cy.get('[data-cy="toggle"]').then(($toggle) => { if ($toggle.hasClass('active')) { @@ -1035,11 +1045,12 @@ cy.get('[data-cy="toggle"]').then(($toggle) => { ``` 3. **Work with multiple elements from the same query:** + ```js cy.get('[data-cy="list-item"]').then(($items) => { const count = $items.length cy.log(`Found ${count} items`) - + // Work with specific items cy.wrap($items.first()).should('contain', 'First item') cy.wrap($items.last()).should('contain', 'Last item') @@ -1059,6 +1070,7 @@ While `then()` captures the element at that moment, Cypress commands within `the ### Best Practice Examples #### Form Interaction (Good) + ```js describe('form interaction', () => { it('updates input field correctly', () => { @@ -1074,12 +1086,13 @@ describe('form interaction', () => { ``` #### Conditional Actions (When `then()` is appropriate) + ```js describe('conditional interactions', () => { it('handles different input states', () => { cy.get('[data-cy="search-input"]').then(($input) => { const currentValue = $input.val() - + if (currentValue) { // Clear existing value and enter new one cy.wrap($input).clear().type('new search term') @@ -1093,6 +1106,7 @@ describe('conditional interactions', () => { ``` #### Working with Element Collections + ```js describe('element collections', () => { it('processes multiple items', () => { @@ -1143,9 +1157,9 @@ waste. :::danger - **Anti-Pattern:** Trying to start -a web server from within Cypress scripts with [`cy.exec()`](/api/commands/exec) or -[`cy.task()`](/api/commands/task). + **Anti-Pattern:** Trying to +start a web server from within Cypress scripts with +[`cy.exec()`](/api/commands/exec) or [`cy.task()`](/api/commands/task). ::: @@ -1193,8 +1207,8 @@ We have :::danger - **Anti-Pattern:** Using [cy.visit()](/api/commands/visit) -without setting a `baseUrl`. + **Anti-Pattern:** Using +[cy.visit()](/api/commands/visit) without setting a `baseUrl`. ::: From 364846af1a0bf3413bb28bed1a790205dfbd23b6 Mon Sep 17 00:00:00 2001 From: ziad_elsoudy <53182896+ziad404@users.noreply.github.com> Date: Sun, 10 Aug 2025 13:01:12 +0000 Subject: [PATCH 3/9] After running prettier --- docs/app/core-concepts/best-practices.mdx | 97 +++++++++++------------ 1 file changed, 46 insertions(+), 51 deletions(-) diff --git a/docs/app/core-concepts/best-practices.mdx b/docs/app/core-concepts/best-practices.mdx index 637e64c807..a9dde8e03e 100644 --- a/docs/app/core-concepts/best-practices.mdx +++ b/docs/app/core-concepts/best-practices.mdx @@ -42,16 +42,16 @@ and start testing. :::danger - **Anti-Pattern:** Sharing page -objects, using your UI to log in, and not taking shortcuts. + **Anti-Pattern:** Sharing page objects, +using your UI to log in, and not taking shortcuts. ::: :::tip - **Best Practice:** Test specs in -isolation, programmatically log into your application, and take control of your -application's state. + **Best Practice:** Test specs in isolation, +programmatically log into your application, and take control of your application's +state. ::: @@ -77,16 +77,15 @@ in our examples. :::danger - **Anti-Pattern:** Using highly -brittle selectors that are subject to change. + **Anti-Pattern:** Using highly brittle +selectors that are subject to change. ::: :::tip - **Best Practice:** Use `data-*` -attributes to provide context to your selectors and isolate them from CSS or JS -changes. + **Best Practice:** Use `data-*` attributes +to provide context to your selectors and isolate them from CSS or JS changes. ::: @@ -248,16 +247,16 @@ your application is working as expected for people with disabilities and the tec :::danger - **Anti-Pattern:** Trying to -assign the return value of Commands with `const`, `let`, or `var`. + **Anti-Pattern:** Trying to assign +the return value of Commands with `const`, `let`, or `var`. ::: :::tip - **Best Practice:** Use [aliases and -closures to access and store](/app/core-concepts/variables-and-aliases) what -Commands yield you. + **Best Practice:** Use [aliases and closures +to access and store](/app/core-concepts/variables-and-aliases) what Commands yield +you. ::: @@ -307,20 +306,18 @@ For working with either of these patterns, please read our :::danger - **Anti-Pattern:** Trying to -visit or interact with sites or servers you do not control. + **Anti-Pattern:** Trying to visit +or interact with sites or servers you do not control. ::: :::tip **Best Practice:** Only test websites -that you control. Try to avoid visiting or requiring a 3rd party server. If you -choose, you may use [`cy.request()`](/api/commands/request) to talk to 3rd party -servers via their APIs. If possible, cache results via -[`cy.session()`](/api/commands/session) to avoid repeat visits. See also reasons -against [Testing Apps You Don't -Control](/app/end-to-end-testing/writing-your-first-end-to-end-test#Testing-Apps-You-Dont-Control). +that you control. Try to avoid visiting or requiring a 3rd party server. If you choose, +you may use [`cy.request()`](/api/commands/request) to talk to 3rd party servers +via their APIs. If possible, cache results via [`cy.session()`](/api/commands/session) +to avoid repeat visits. See also reasons against [Testing Apps You Don't Control](/app/end-to-end-testing/writing-your-first-end-to-end-test#Testing-Apps-You-Dont-Control). ::: @@ -460,22 +457,21 @@ email's functionality and visual style: exposes an API to read off emails. You will then need the proper authentication credentials, which your server could provide, or you could use environment variables. Some email services already provide - Cypress plugins to access - emails. + Cypress plugins to access emails. ## Having Tests Rely On The State Of Previous Tests :::danger - **Anti-Pattern:** Coupling -multiple tests together. + **Anti-Pattern:** Coupling multiple +tests together. ::: :::tip - **Best Practice:** Tests should -always be able to be run independently from one another **and still pass**. + **Best Practice:** Tests should always +be able to be run independently from one another **and still pass**. ::: @@ -597,15 +593,15 @@ can be run independently and pass. :::danger - **Anti-Pattern:** Acting like -you're writing unit tests. + **Anti-Pattern:** Acting like you're +writing unit tests. ::: :::tip - **Best Practice:** Add multiple -assertions and don't worry about it + **Best Practice:** Add multiple assertions +and don't worry about it ::: @@ -690,8 +686,8 @@ or `afterEach` hooks to clean up state. :::tip - **Best Practice:** Clean up state -**before** tests run. + **Best Practice:** Clean up state **before** +tests run. ::: @@ -885,16 +881,15 @@ script. :::danger - **Anti-Pattern:** Waiting for -arbitrary time periods using [`cy.wait(Number)`](/api/commands/wait#Time). + **Anti-Pattern:** Waiting for arbitrary +time periods using [`cy.wait(Number)`](/api/commands/wait#Time). ::: :::tip - **Best Practice:** Use route aliases -or assertions to guard Cypress from proceeding until an explicit condition is -met. + **Best Practice:** Use route aliases or +assertions to guard Cypress from proceeding until an explicit condition is met. ::: @@ -960,17 +955,17 @@ cy.get('table tr').should('have.length', 2) :::danger - **Anti-Pattern:** Using -`then()` unnecessarily or re-querying the same element multiple times when -chaining would work. + **Anti-Pattern:** Using `then()` +unnecessarily or re-querying the same element multiple times when chaining would +work. ::: :::tip - **Best Practice:** Use command -chaining when possible, and only use `then()` when you need to perform complex -operations or work with the actual DOM element. + **Best Practice:** Use command chaining +when possible, and only use `then()` when you need to perform complex operations +or work with the actual DOM element. ::: @@ -1157,9 +1152,9 @@ waste. :::danger - **Anti-Pattern:** Trying to -start a web server from within Cypress scripts with -[`cy.exec()`](/api/commands/exec) or [`cy.task()`](/api/commands/task). + **Anti-Pattern:** Trying to start +a web server from within Cypress scripts with [`cy.exec()`](/api/commands/exec) or +[`cy.task()`](/api/commands/task). ::: @@ -1207,8 +1202,8 @@ We have :::danger - **Anti-Pattern:** Using -[cy.visit()](/api/commands/visit) without setting a `baseUrl`. + **Anti-Pattern:** Using [cy.visit()](/api/commands/visit) +without setting a `baseUrl`. ::: From 3485f0aec48e206c13eb03ad94d750a4b0544f13 Mon Sep 17 00:00:00 2001 From: ziad_elsoudy <53182896+ziad404@users.noreply.github.com> Date: Mon, 25 Aug 2025 08:14:10 +0000 Subject: [PATCH 4/9] Fixing the Added example --- docs/app/core-concepts/best-practices.mdx | 31 ++++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/docs/app/core-concepts/best-practices.mdx b/docs/app/core-concepts/best-practices.mdx index a9dde8e03e..5e9a9792fa 100644 --- a/docs/app/core-concepts/best-practices.mdx +++ b/docs/app/core-concepts/best-practices.mdx @@ -978,8 +978,11 @@ When you need to perform several actions on the same element (like focus, clear, #### Option 1: Command Chaining (Recommended) ```js -// RECOMMENDED: Chain commands when possible -cy.get('[data-cy="input-field"]').focus().clear().type('new value').blur() +// RECOMMENDED: Use separate cy.get() or cy.wrap() for each action command +cy.get('[data-cy="input-field"]').focus() +cy.get('[data-cy="input-field"]').clear() +cy.get('[data-cy="input-field"]').type('new value') +cy.get('[data-cy="input-field"]').blur() ``` #### Option 2: Using then() (Only when necessary) @@ -1064,21 +1067,19 @@ While `then()` captures the element at that moment, Cypress commands within `the ### Best Practice Examples -#### Form Interaction (Good) - -```js describe('form interaction', () => { - it('updates input field correctly', () => { - cy.get('[data-cy="email-input"]') - .should('be.visible') - .focus() - .clear() - .type('user@example.com') - .should('have.value', 'user@example.com') - .blur() - }) +it('updates input field correctly', () => { +cy.get('[data-cy="email-input"]').as('emailInput') +cy.get('@emailInput').should('be.visible') +cy.get('@emailInput').focus() +cy.get('@emailInput').clear() +cy.get('@emailInput').type('user@example.com') +}) + +cy.get('@emailInput').should('have.value', 'user@example.com') +cy.get('@emailInput').blur() +}) }) -``` #### Conditional Actions (When `then()` is appropriate) From 44cdd60a0b25efeea3fbcc96b2304559d0177250 Mon Sep 17 00:00:00 2001 From: ziad_elsoudy <53182896+ziad404@users.noreply.github.com> Date: Mon, 25 Aug 2025 09:50:43 +0000 Subject: [PATCH 5/9] fix: Correct syntax in best-practices.mdx code example --- docs/app/core-concepts/best-practices.mdx | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/docs/app/core-concepts/best-practices.mdx b/docs/app/core-concepts/best-practices.mdx index 5e9a9792fa..8ccd060cae 100644 --- a/docs/app/core-concepts/best-practices.mdx +++ b/docs/app/core-concepts/best-practices.mdx @@ -1068,17 +1068,15 @@ While `then()` captures the element at that moment, Cypress commands within `the ### Best Practice Examples describe('form interaction', () => { -it('updates input field correctly', () => { -cy.get('[data-cy="email-input"]').as('emailInput') -cy.get('@emailInput').should('be.visible') -cy.get('@emailInput').focus() -cy.get('@emailInput').clear() -cy.get('@emailInput').type('user@example.com') -}) - -cy.get('@emailInput').should('have.value', 'user@example.com') -cy.get('@emailInput').blur() -}) + it('updates input field correctly', () => { + cy.get('[data-cy="email-input"]').as('emailInput') + cy.get('@emailInput').should('be.visible') + cy.get('@emailInput').focus() + cy.get('@emailInput').clear() + cy.get('@emailInput').type('user@example.com') + cy.get('@emailInput').should('have.value', 'user@example.com') + cy.get('@emailInput').blur() + }) }) #### Conditional Actions (When `then()` is appropriate) From b0ba23ae394f1b641260a6d6adc114b792e5846f Mon Sep 17 00:00:00 2001 From: ziad_elsoudy <53182896+ziad404@users.noreply.github.com> Date: Mon, 25 Aug 2025 10:00:02 +0000 Subject: [PATCH 6/9] docs: Update command chaining examples to follow retry-ability best practices --- docs/app/core-concepts/best-practices.mdx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/app/core-concepts/best-practices.mdx b/docs/app/core-concepts/best-practices.mdx index 8ccd060cae..3423d61ed9 100644 --- a/docs/app/core-concepts/best-practices.mdx +++ b/docs/app/core-concepts/best-practices.mdx @@ -973,15 +973,23 @@ A common question arises when performing multiple actions on the same element: s ### Multiple Actions on the Same Element -When you need to perform several actions on the same element (like focus, clear, type, and blur), you have two main approaches: +When you need to perform several actions on the same element (like focus, clear, type, and blur), you have two main approaches. Remember that Cypress commands are retry-able, but actions like `.click()`, `.type()`, etc. should be at the end of command chains to maintain retry-ability: #### Option 1: Command Chaining (Recommended) ```js -// RECOMMENDED: Use separate cy.get() or cy.wrap() for each action command -cy.get('[data-cy="input-field"]').focus() +// RECOMMENDED: Chain commands with assertions before actions +cy.get('[data-cy="input-field"]') + .should('be.visible') + .should('not.be.disabled') + .focus() + +// Each action gets its own chain to ensure retry-ability cy.get('[data-cy="input-field"]').clear() -cy.get('[data-cy="input-field"]').type('new value') +cy.get('[data-cy="input-field"]') + .type('new value') + .should('have.value', 'new value') + cy.get('[data-cy="input-field"]').blur() ``` From 87db512b8b5e54b366b95de0a1a0bc1dcf0c2a6a Mon Sep 17 00:00:00 2001 From: ziad_elsoudy <53182896+ziad404@users.noreply.github.com> Date: Mon, 25 Aug 2025 10:00:45 +0000 Subject: [PATCH 7/9] fix: Correct indentation in best practice example for form interaction --- docs/app/core-concepts/best-practices.mdx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/app/core-concepts/best-practices.mdx b/docs/app/core-concepts/best-practices.mdx index 3423d61ed9..57547c6956 100644 --- a/docs/app/core-concepts/best-practices.mdx +++ b/docs/app/core-concepts/best-practices.mdx @@ -1076,15 +1076,15 @@ While `then()` captures the element at that moment, Cypress commands within `the ### Best Practice Examples describe('form interaction', () => { - it('updates input field correctly', () => { - cy.get('[data-cy="email-input"]').as('emailInput') - cy.get('@emailInput').should('be.visible') - cy.get('@emailInput').focus() - cy.get('@emailInput').clear() - cy.get('@emailInput').type('user@example.com') - cy.get('@emailInput').should('have.value', 'user@example.com') - cy.get('@emailInput').blur() - }) +it('updates input field correctly', () => { +cy.get('[data-cy="email-input"]').as('emailInput') +cy.get('@emailInput').should('be.visible') +cy.get('@emailInput').focus() +cy.get('@emailInput').clear() +cy.get('@emailInput').type('user@example.com') +cy.get('@emailInput').should('have.value', 'user@example.com') +cy.get('@emailInput').blur() +}) }) #### Conditional Actions (When `then()` is appropriate) From b4d3bc9ada80cb48d9c6c09744892f205b030953 Mon Sep 17 00:00:00 2001 From: ziad_elsoudy <53182896+ziad404@users.noreply.github.com> Date: Mon, 25 Aug 2025 10:28:57 +0000 Subject: [PATCH 8/9] fix: Improve formatting and readability in best practice examples for form interaction --- docs/app/core-concepts/best-practices.mdx | 24 +++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/app/core-concepts/best-practices.mdx b/docs/app/core-concepts/best-practices.mdx index 57547c6956..ea78575215 100644 --- a/docs/app/core-concepts/best-practices.mdx +++ b/docs/app/core-concepts/best-practices.mdx @@ -1075,17 +1075,21 @@ While `then()` captures the element at that moment, Cypress commands within `the ### Best Practice Examples +```javascript describe('form interaction', () => { -it('updates input field correctly', () => { -cy.get('[data-cy="email-input"]').as('emailInput') -cy.get('@emailInput').should('be.visible') -cy.get('@emailInput').focus() -cy.get('@emailInput').clear() -cy.get('@emailInput').type('user@example.com') -cy.get('@emailInput').should('have.value', 'user@example.com') -cy.get('@emailInput').blur() -}) + it('updates input field correctly', () => { + cy.get('[data-cy="email-input"]').as('emailInput') + cy.get('@emailInput') + .should('be.visible') + .focus() + cy.get('@emailInput').clear() + cy.get('@emailInput') + .type('user@example.com') + .should('have.value', 'user@example.com') + cy.get('@emailInput').blur() + }) }) +``` #### Conditional Actions (When `then()` is appropriate) @@ -1109,7 +1113,7 @@ describe('conditional interactions', () => { #### Working with Element Collections -```js +```javascript describe('element collections', () => { it('processes multiple items', () => { cy.get('[data-cy="product-card"]').then(($cards) => { From 829e5ff7e5a054f1c24f7aaf2f800765c498df16 Mon Sep 17 00:00:00 2001 From: ziad_elsoudy <53182896+ziad404@users.noreply.github.com> Date: Mon, 25 Aug 2025 10:35:51 +0000 Subject: [PATCH 9/9] fix: Simplify input field interaction example by consolidating commands --- docs/app/core-concepts/best-practices.mdx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/app/core-concepts/best-practices.mdx b/docs/app/core-concepts/best-practices.mdx index ea78575215..d74802d861 100644 --- a/docs/app/core-concepts/best-practices.mdx +++ b/docs/app/core-concepts/best-practices.mdx @@ -1079,9 +1079,7 @@ While `then()` captures the element at that moment, Cypress commands within `the describe('form interaction', () => { it('updates input field correctly', () => { cy.get('[data-cy="email-input"]').as('emailInput') - cy.get('@emailInput') - .should('be.visible') - .focus() + cy.get('@emailInput').should('be.visible').focus() cy.get('@emailInput').clear() cy.get('@emailInput') .type('user@example.com')