You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: refactor within to Within with begin/end pattern
Add Within() function with three signatures:
- Within(locator) to begin scoped context
- Within() to end current context
- Within(locator, fn) callback pattern (existing behavior)
Lowercase within() kept as deprecated alias with one-time warning.
switchTo() in helpers auto-ends any active Within context.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/effects.md
+18-55Lines changed: 18 additions & 55 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,11 +12,7 @@ Effects are functions that can modify scenario flow. They provide ways to handle
12
12
Effects can be imported directly from CodeceptJS:
13
13
14
14
```js
15
-
// ESM
16
-
import { tryTo, retryTo, within } from'codeceptjs/effects'
17
-
18
-
// CommonJS
19
-
const { tryTo, retryTo, within } =require('codeceptjs/effects')
15
+
import { tryTo, retryTo, Within } from'codeceptjs/effects'
20
16
```
21
17
22
18
> 📝 Note: Prior to v3.7, `tryTo` and `retryTo` were available globally via plugins. This behavior is deprecated and will be removed in v4.0.
@@ -80,71 +76,38 @@ await retryTo(tries => {
80
76
}, 3)
81
77
```
82
78
83
-
## within
79
+
## Within
84
80
85
-
The `within` effect scopes all actions inside it to a specific element on the page — useful when working with repeated UI components or narrowing interaction to a specific section.
81
+
The `Within` effect scopes actions to a specific element or iframe. It supports both a begin/end pattern and a callback pattern:
86
82
87
83
```js
88
-
import { within } from'codeceptjs/effects'
89
-
90
-
// inside a test...
91
-
awaitwithin('.js-signup-form', () => {
92
-
I.fillField('user[login]', 'User')
93
-
I.fillField('user[email]', 'user@user.com')
94
-
I.fillField('user[password]', 'user@user.com')
95
-
I.click('button')
84
+
import { Within } from'codeceptjs/effects'
85
+
86
+
// Begin/end pattern
87
+
Within('.modal')
88
+
I.see('Modal title')
89
+
I.click('Close')
90
+
Within()
91
+
92
+
// Callback pattern
93
+
Within('.modal', () => {
94
+
I.see('Modal title')
95
+
I.click('Close')
96
96
})
97
-
I.see('There were problems creating your account.')
98
97
```
99
98
100
-
> ⚠ `within` can cause problems when used incorrectly. If you see unexpected behavior, refactor to use the context parameter on individual actions instead (e.g. `I.click('Login', '.nav')`). Keep `within` for the simplest cases.
101
-
102
-
> ⚠ Since `within` returns a Promise, always `await` it when you need its return value.
103
-
104
-
### IFrames
105
-
106
-
Use a `frame` locator to scope actions inside an iframe:
99
+
See the full [Within documentation](/within) for details on iframes, page objects, and `await` usage.
> ℹ IFrames can also be accessed via `I.switchTo` command.
124
-
125
-
### Returning Values
126
-
127
-
`within` can return a value for use in the scenario:
128
-
129
-
```js
130
-
constval=awaitwithin('#sidebar', () => {
131
-
returnI.grabTextFrom({ css:'h1' })
132
-
})
133
-
I.fillField('Description', val)
134
-
```
135
-
136
-
When running steps inside a `within` block, they will be shown indented in the output.
101
+
> The lowercase `within()` is deprecated. Use `Within` instead.
137
102
138
103
## Usage with TypeScript
139
104
140
105
Effects are fully typed and work well with TypeScript:
141
106
142
107
```ts
143
-
import { tryTo, retryTo, within } from'codeceptjs/effects'
108
+
import { tryTo, retryTo, Within } from'codeceptjs/effects'
144
109
145
110
const success =awaittryTo(async () => {
146
111
awaitI.see('Element')
147
112
})
148
113
```
149
-
150
-
This documentation covers the main effects functionality while providing practical examples and important notes about deprecation and future changes. Let me know if you'd like me to expand any section or add more examples!
`within` scopes all actions inside it to a specific element on the page — useful when working with repeated UI components or narrowing interaction to a specific section.
8
+
`Within` narrows the execution context to a specific element or iframe on the page. All actions called inside a `Within` block are scoped to the matched element.
9
9
10
10
```js
11
-
within('.js-signup-form', () => {
12
-
I.fillField('user[login]', 'User')
13
-
I.fillField('user[email]', 'user@user.com')
14
-
I.fillField('user[password]', 'user@user.com')
15
-
I.click('button')
11
+
import { Within } from'codeceptjs/effects'
12
+
```
13
+
14
+
## Begin / End Pattern
15
+
16
+
The simplest way to use `Within` is the begin/end pattern. Call `Within` with a locator to start, perform actions, then call `Within()` with no arguments to end:
17
+
18
+
```js
19
+
Within('.signup-form')
20
+
I.fillField('Email', 'user@example.com')
21
+
I.fillField('Password', 'secret')
22
+
I.click('Sign Up')
23
+
Within()
24
+
```
25
+
26
+
Steps between `Within('.signup-form')` and `Within()` are scoped to `.signup-form`. After `Within()`, the context resets to the full page.
27
+
28
+
### Auto-end previous context
29
+
30
+
Starting a new `Within` automatically ends the previous one:
If you forget to call `Within()` at the end, the context is automatically cleaned up when the test finishes. However, it is good practice to always close it explicitly.
44
+
45
+
## Callback Pattern
46
+
47
+
The callback pattern wraps actions in a function. The context is automatically closed when the function returns:
48
+
49
+
```js
50
+
Within('.signup-form', () => {
51
+
I.fillField('Email', 'user@example.com')
52
+
I.fillField('Password', 'secret')
53
+
I.click('Sign Up')
54
+
})
55
+
I.see('Account created')
56
+
```
57
+
58
+
### Returning values
59
+
60
+
The callback pattern supports returning values. Use `await` on both the `Within` call and the inner action:
61
+
62
+
```js
63
+
consttext=awaitWithin('#sidebar', async () => {
64
+
returnawaitI.grabTextFrom('h1')
65
+
})
66
+
I.fillField('Search', text)
67
+
```
68
+
69
+
## When to use `await`
70
+
71
+
**Begin/end pattern** does not need `await`:
72
+
73
+
```js
74
+
Within('.form')
75
+
I.fillField('Name', 'John')
76
+
Within()
77
+
```
78
+
79
+
**Callback pattern** needs `await` when:
80
+
81
+
- The callback is `async`
82
+
- You need a return value from `Within`
83
+
84
+
```js
85
+
// async callback — await required
86
+
awaitWithin('.form', async () => {
87
+
awaitI.click('Submit')
88
+
awaitI.waitForText('Done')
16
89
})
17
-
I.see('There were problems creating your account.')
18
90
```
19
91
20
-
> ⚠ `within` can cause problems when used incorrectly. If you see unexpected behavior, refactor to use the context parameter on individual actions instead (e.g. `I.click('Login', '.nav')`). Keep `within` for the simplest cases.
21
-
> Since `within` returns a Promise, always `await` it when you need its return value.
92
+
```js
93
+
// sync callback — no await needed
94
+
Within('.form', () => {
95
+
I.fillField('Name', 'John')
96
+
I.click('Submit')
97
+
})
98
+
```
99
+
100
+
## Working with IFrames
101
+
102
+
Use the `frame` locator to scope actions inside an iframe:
103
+
104
+
```js
105
+
// Begin/end
106
+
Within({ frame:'iframe' })
107
+
I.fillField('Email', 'user@example.com')
108
+
I.click('Submit')
109
+
Within()
110
+
111
+
// Callback
112
+
Within({ frame:'#editor-frame' }, () => {
113
+
I.see('Page content')
114
+
})
115
+
```
22
116
23
-
## IFrames
117
+
### Nested IFrames
24
118
25
-
Use a `frame` locator to scope actions inside an iframe:
119
+
Pass an array of selectors to reach nested iframes:
Each selector in the array navigates one level deeper into the iframe hierarchy.
129
+
130
+
### switchTo auto-disables Within
131
+
132
+
If you call `I.switchTo()` while inside a `Within` context, the within context is automatically ended. This prevents conflicts between the two scoping mechanisms:
0 commit comments