Skip to content

Commit

Permalink
feat: soft assertion (#4728)
Browse files Browse the repository at this point in the history
* feat: soft assertion

* update as hopeThat module in effects

* fix: docs
  • Loading branch information
kobenguyent authored Jan 10, 2025
1 parent b960479 commit c503ea2
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 0 deletions.
123 changes: 123 additions & 0 deletions lib/effects.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
const recorder = require('./recorder')
const { debug } = require('./output')
const store = require('./store')

/**
* @module hopeThat
*
* `hopeThat` is a utility function for CodeceptJS tests that allows for soft assertions.
* It enables conditional assertions without terminating the test upon failure.
* This is particularly useful in scenarios like A/B testing, handling unexpected elements,
* or performing multiple assertions where you want to collect all results before deciding
* on the test outcome.
*
* ## Use Cases
*
* - **Multiple Conditional Assertions**: Perform several assertions and evaluate all their outcomes together.
* - **A/B Testing**: Handle different variants in A/B tests without failing the entire test upon one variant's failure.
* - **Unexpected Elements**: Manage elements that may or may not appear, such as "Accept Cookie" banners.
*
* ## Examples
*
* ### Multiple Conditional Assertions
*
* Add the assertion library:
* ```js
* const assert = require('assert');
* const { hopeThat } = require('codeceptjs/effects');
* ```
*
* Use `hopeThat` with assertions:
* ```js
* const result1 = await hopeThat(() => I.see('Hello, user'));
* const result2 = await hopeThat(() => I.seeElement('.welcome'));
* assert.ok(result1 && result2, 'Assertions were not successful');
* ```
*
* ### Optional Click
*
* ```js
* const { hopeThat } = require('codeceptjs/effects');
*
* I.amOnPage('/');
* await hopeThat(() => I.click('Agree', '.cookies'));
* ```
*
* Performs a soft assertion within CodeceptJS tests.
*
* This function records the execution of a callback containing assertion logic.
* If the assertion fails, it logs the failure without stopping the test execution.
* It is useful for scenarios where multiple assertions are performed, and you want
* to evaluate all outcomes before deciding on the test result.
*
* ## Usage
*
* ```js
* const result = await hopeThat(() => I.see('Welcome'));
*
* // If the text "Welcome" is on the page, result => true
* // If the text "Welcome" is not on the page, result => false
* ```
*
* @async
* @function hopeThat
* @param {Function} callback - The callback function containing the soft assertion logic.
* @returns {Promise<boolean | any>} - Resolves to `true` if the assertion is successful, or `false` if it fails.
*
* @example
* // Multiple Conditional Assertions
* const assert = require('assert');
* const { hopeThat } = require('codeceptjs/effects');
*
* const result1 = await hopeThat(() => I.see('Hello, user'));
* const result2 = await hopeThat(() => I.seeElement('.welcome'));
* assert.ok(result1 && result2, 'Assertions were not successful');
*
* @example
* // Optional Click
* const { hopeThat } = require('codeceptjs/effects');
*
* I.amOnPage('/');
* await hopeThat(() => I.click('Agree', '.cookies'));
*/
async function hopeThat(callback) {
if (store.dryRun) return
const sessionName = 'hopeThat'

let result = false
return recorder.add(
'hopeThat',
() => {
recorder.session.start(sessionName)
store.hopeThat = true
callback()
recorder.add(() => {
result = true
recorder.session.restore(sessionName)
return result
})
recorder.session.catch(err => {
result = false
const msg = err.inspect ? err.inspect() : err.toString()
debug(`Unsuccessful assertion > ${msg}`)
recorder.session.restore(sessionName)
return result
})
return recorder.add(
'result',
() => {
store.hopeThat = undefined
return result
},
true,
false,
)
},
false,
false,
)
}

module.exports = {
hopeThat,
}
27 changes: 27 additions & 0 deletions test/unit/hopeThat_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const { expect } = require('chai')
const { hopeThat } = require('../../lib/effects')
const recorder = require('../../lib/recorder')

describe('effects', () => {
describe('hopeThat', () => {
beforeEach(() => {
recorder.start()
})

it('should execute command on success', async () => {
const ok = await hopeThat(() => recorder.add(() => 5))
expect(true).is.equal(ok)
return recorder.promise()
})

it('should execute command on fail', async () => {
const notOk = await hopeThat(() =>
recorder.add(() => {
throw new Error('Ups')
}),
)
expect(false).is.equal(notOk)
return recorder.promise()
})
})
})

0 comments on commit c503ea2

Please sign in to comment.