11const recorder = require ( './recorder' )
22const { debug } = require ( './output' )
33const store = require ( './store' )
4+ const event = require ( './event' )
45
56/**
6- * @module hopeThat
7- *
8- * `hopeThat` is a utility function for CodeceptJS tests that allows for soft assertions.
9- * It enables conditional assertions without terminating the test upon failure.
10- * This is particularly useful in scenarios like A/B testing, handling unexpected elements,
11- * or performing multiple assertions where you want to collect all results before deciding
12- * on the test outcome.
13- *
14- * ## Use Cases
15- *
16- * - **Multiple Conditional Assertions**: Perform several assertions and evaluate all their outcomes together.
17- * - **A/B Testing**: Handle different variants in A/B tests without failing the entire test upon one variant's failure.
18- * - **Unexpected Elements**: Manage elements that may or may not appear, such as "Accept Cookie" banners.
19- *
20- * ## Examples
21- *
22- * ### Multiple Conditional Assertions
23- *
24- * Add the assertion library:
25- * ```js
26- * const assert = require('assert');
27- * const { hopeThat } = require('codeceptjs/effects');
28- * ```
29- *
30- * Use `hopeThat` with assertions:
31- * ```js
32- * const result1 = await hopeThat(() => I.see('Hello, user'));
33- * const result2 = await hopeThat(() => I.seeElement('.welcome'));
34- * assert.ok(result1 && result2, 'Assertions were not successful');
35- * ```
36- *
37- * ### Optional Click
38- *
39- * ```js
40- * const { hopeThat } = require('codeceptjs/effects');
41- *
42- * I.amOnPage('/');
43- * await hopeThat(() => I.click('Agree', '.cookies'));
44- * ```
45- *
46- * Performs a soft assertion within CodeceptJS tests.
47- *
48- * This function records the execution of a callback containing assertion logic.
49- * If the assertion fails, it logs the failure without stopping the test execution.
50- * It is useful for scenarios where multiple assertions are performed, and you want
51- * to evaluate all outcomes before deciding on the test result.
52- *
53- * ## Usage
54- *
55- * ```js
56- * const result = await hopeThat(() => I.see('Welcome'));
57- *
58- * // If the text "Welcome" is on the page, result => true
59- * // If the text "Welcome" is not on the page, result => false
60- * ```
7+ * A utility function for CodeceptJS tests that acts as a soft assertion.
8+ * Executes a callback within a recorded session, ensuring errors are handled gracefully without failing the test immediately.
619 *
6210 * @async
6311 * @function hopeThat
64- * @param {Function } callback - The callback function containing the soft assertion logic .
65- * @returns { Promise<boolean | any> } - Resolves to `true` if the assertion is successful, or `false` if it fails .
66- *
67- * @example
68- * // Multiple Conditional Assertions
69- * const assert = require('assert');
70- * const { hopeThat } = require('codeceptjs/effects');
71- *
72- * const result1 = await hopeThat(() => I.see('Hello, user'));
73- * const result2 = await hopeThat(() => I.seeElement('.welcome'));
74- * assert.ok(result1 && result2, 'Assertions were not successful');
12+ * @param {Function } callback - The callback function containing the logic to validate .
13+ * This function should perform the desired assertion or condition check .
14+ * @returns { Promise<boolean|any> } A promise resolving to `true` if the assertion or condition was successful,
15+ * or `false` if an error occurred.
16+ *
17+ * @description
18+ * - Designed for use in CodeceptJS tests as a "soft assertion."
19+ * Unlike standard assertions, it does not stop the test execution on failure.
20+ * - Starts a new recorder session named 'hopeThat' and manages state restoration.
21+ * - Logs errors and attaches them as notes to the test, enabling post-test reporting of soft assertion failures.
22+ * - Resets the `store.hopeThat` flag after the execution, ensuring clean state for subsequent operations.
7523 *
7624 * @example
77- * // Optional Click
78- * const { hopeThat } = require('codeceptjs/effects');
25+ * const { hopeThat } = require('codeceptjs/effects')
26+ * await hopeThat(() => {
27+ * I.see('Welcome'); // Perform a soft assertion
28+ * });
7929 *
80- * I.amOnPage('/');
81- * await hopeThat(() => I.click('Agree', '.cookies'));
30+ * @throws Will handle errors that occur during the callback execution. Errors are logged and attached as notes to the test.
8231 */
8332async function hopeThat ( callback ) {
8433 if ( store . dryRun ) return
@@ -100,6 +49,9 @@ async function hopeThat(callback) {
10049 result = false
10150 const msg = err . inspect ? err . inspect ( ) : err . toString ( )
10251 debug ( `Unsuccessful assertion > ${ msg } ` )
52+ event . dispatcher . on ( event . test . after , test => {
53+ test . notes . push ( { type : 'conditionalError' , text : msg } )
54+ } )
10355 recorder . session . restore ( sessionName )
10456 return result
10557 } )
@@ -119,46 +71,33 @@ async function hopeThat(callback) {
11971}
12072
12173/**
74+ * A CodeceptJS utility function to retry a step or callback multiple times with a specified polling interval.
12275 *
123- * @module retryTo
124- *
125- * `retryTo` which retries steps a few times before failing.
126- *
127- *
128- * Use it in your tests:
129- *
130- * const { retryTo } = require('codeceptjs/effects');
131- * ```js
132- * // retry these steps 5 times before failing
133- * await retryTo((tryNum) => {
134- * I.switchTo('#editor frame');
135- * I.click('Open');
136- * I.see('Opened')
137- * }, 5);
138- * ```
139- * Set polling interval as 3rd argument (200ms by default):
140- *
141- * ```js
142- * // retry these steps 5 times before failing
143- * await retryTo((tryNum) => {
144- * I.switchTo('#editor frame');
145- * I.click('Open');
146- * I.see('Opened')
147- * }, 5, 100);
148- * ```
149- *
150- * Disables retryFailedStep plugin for steps inside a block;
151- *
152- * Use this plugin if:
153- *
154- * * you need repeat a set of actions in flaky tests
155- * * iframe was not rendered, so you need to retry switching to it
156- *
157- *
158- * #### Configuration
159- *
160- * * `pollInterval` - default interval between retries in ms. 200 by default.
76+ * @async
77+ * @function retryTo
78+ * @param {Function } callback - The function to execute, which will be retried upon failure.
79+ * Receives the current retry count as an argument.
80+ * @param {number } maxTries - The maximum number of attempts to retry the callback.
81+ * @param {number } [pollInterval=200] - The delay (in milliseconds) between retry attempts.
82+ * @returns {Promise<void|any> } A promise that resolves when the callback executes successfully, or rejects after reaching the maximum retries.
83+ *
84+ * @description
85+ * - This function is designed for use in CodeceptJS tests to handle intermittent or flaky test steps.
86+ * - Starts a new recorder session for each retry attempt, ensuring proper state management and error handling.
87+ * - Logs errors and retries the callback until it either succeeds or the maximum number of attempts is reached.
88+ * - Restores the session state after each attempt, whether successful or not.
16189 *
90+ * @example
91+ * const { hopeThat } = require('codeceptjs/effects')
92+ * await retryTo((tries) => {
93+ * if (tries < 3) {
94+ * I.see('Non-existent element'); // Simulates a failure
95+ * } else {
96+ * I.see('Welcome'); // Succeeds on the 3rd attempt
97+ * }
98+ * }, 5, 300); // Retry up to 5 times, with a 300ms interval
99+ *
100+ * @throws Will reject with the last error encountered if the maximum retries are exceeded.
162101 */
163102async function retryTo ( callback , maxTries , pollInterval = 200 ) {
164103 const sessionName = 'retryTo'
@@ -207,80 +146,32 @@ async function retryTo(callback, maxTries, pollInterval = 200) {
207146}
208147
209148/**
210- * @module tryTo
211- *
212- * `tryTo` which all failed steps won't fail a test but will return true/false.
213- * It enables conditional assertions without terminating the test upon failure.
214- * This is particularly useful in scenarios like A/B testing, handling unexpected elements,
215- * or performing multiple assertions where you want to collect all results before deciding
216- * on the test outcome.
217- *
218- * ## Use Cases
219- *
220- * - **Multiple Conditional Assertions**: Perform several assertions and evaluate all their outcomes together.
221- * - **A/B Testing**: Handle different variants in A/B tests without failing the entire test upon one variant's failure.
222- * - **Unexpected Elements**: Manage elements that may or may not appear, such as "Accept Cookie" banners.
223- *
224- * ## Examples
225- *
226- * ### Multiple Conditional Assertions
227- *
228- * Add the assertion library:
229- * ```js
230- * const assert = require('assert');
231- * const { tryTo } = require('codeceptjs/effects');
232- * ```
233- *
234- * Use `hopeThat` with assertions:
235- * ```js
236- * const result1 = await tryTo(() => I.see('Hello, user'));
237- * const result2 = await tryTo(() => I.seeElement('.welcome'));
238- * assert.ok(result1 && result2, 'Assertions were not successful');
239- * ```
240- *
241- * ### Optional Click
242- *
243- * ```js
244- * const { tryTo } = require('codeceptjs/effects');
245- *
246- * I.amOnPage('/');
247- * await tryTo(() => I.click('Agree', '.cookies'));
248- * ```
249- *
250- * This function records the execution of a callback containing assertion logic.
251- * If the assertion fails, it logs the failure without stopping the test execution.
252- * It is useful for scenarios where multiple assertions are performed, and you want
253- * to evaluate all outcomes before deciding on the test result.
254- *
255- * ## Usage
256- *
257- * ```js
258- * const result = await tryTo(() => I.see('Welcome'));
259- *
260- * // If the text "Welcome" is on the page, result => true
261- * // If the text "Welcome" is not on the page, result => false
262- * ```
149+ * A CodeceptJS utility function to attempt a step or callback without failing the test.
150+ * If the step fails, the test continues execution without interruption, and the result is logged.
263151 *
264152 * @async
265153 * @function tryTo
266- * @param {Function } callback - The callback function.
267- * @returns {Promise<boolean | any> } - Resolves to `true` if the assertion is successful, or `false` if it fails.
154+ * @param {Function } callback - The function to execute, which may succeed or fail.
155+ * This function contains the logic to be attempted.
156+ * @returns {Promise<boolean|any> } A promise resolving to `true` if the step succeeds, or `false` if it fails.
268157 *
269- * @example
270- * // Multiple Conditional Assertions
271- * const assert = require('assert');
272- * const { tryTo } = require('codeceptjs/effects');
273- *
274- * const result1 = await tryTo(() => I.see('Hello, user'));
275- * const result2 = await tryTo(() => I.seeElement('.welcome'));
276- * assert.ok(result1 && result2, 'Assertions were not successful');
158+ * @description
159+ * - Useful for scenarios where certain steps are optional or their failure should not interrupt the test flow.
160+ * - Starts a new recorder session named 'tryTo' for isolation and error handling.
161+ * - Captures errors during execution and logs them for debugging purposes.
162+ * - Ensures the `store.tryTo` flag is reset after execution to maintain a clean state.
277163 *
278164 * @example
279- * // Optional Click
280- * const { tryTo } = require('codeceptjs/effects');
165+ * const { tryTo } = require('codeceptjs/effects')
166+ * const wasSuccessful = await tryTo(() => {
167+ * I.see('Welcome'); // Attempt to find an element on the page
168+ * });
169+ *
170+ * if (!wasSuccessful) {
171+ * I.say('Optional step failed, but test continues.');
172+ * }
281173 *
282- * I.amOnPage('/');
283- * await tryTo(() => I.click('Agree', '.cookies'));
174+ * @throws Will handle errors internally, logging them and returning `false` as the result.
284175 */
285176async function tryTo ( callback ) {
286177 if ( store . dryRun ) return
0 commit comments