From f2052959bf09ff5142366ef1ad2cf843d8107293 Mon Sep 17 00:00:00 2001 From: kobenguyent Date: Mon, 2 Sep 2024 12:00:24 +0200 Subject: [PATCH] feat: soft assert --- .github/workflows/softExpectHelper.yml | 34 ++ docs/helpers/SoftExpectHelper.md | 357 ++++++++++++++++++ lib/helper/SoftExpectHelper.js | 381 ++++++++++++++++++++ test/helper/SoftExpect_test.js | 479 +++++++++++++++++++++++++ 4 files changed, 1251 insertions(+) create mode 100644 .github/workflows/softExpectHelper.yml create mode 100644 docs/helpers/SoftExpectHelper.md create mode 100644 lib/helper/SoftExpectHelper.js create mode 100644 test/helper/SoftExpect_test.js diff --git a/.github/workflows/softExpectHelper.yml b/.github/workflows/softExpectHelper.yml new file mode 100644 index 000000000..141c056b8 --- /dev/null +++ b/.github/workflows/softExpectHelper.yml @@ -0,0 +1,34 @@ +name: Soft Expect Helper Tests + +on: + push: + branches: + - 3.x + pull_request: + branches: + - '**' + +env: + CI: true + # Force terminal colors. @see https://www.npmjs.com/package/colors + FORCE_COLOR: 1 + +jobs: + build: + + runs-on: ubuntu-22.04 + + strategy: + matrix: + node-version: [20.x] + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - name: npm install + run: npm i --force + - name: run unit tests + run: ./node_modules/.bin/mocha test/helper/SoftExpect_test.js --timeout 5000 diff --git a/docs/helpers/SoftExpectHelper.md b/docs/helpers/SoftExpectHelper.md new file mode 100644 index 000000000..ab4e62da0 --- /dev/null +++ b/docs/helpers/SoftExpectHelper.md @@ -0,0 +1,357 @@ +--- +permalink: /helpers/SoftExpectHelper +editLink: false +sidebar: auto +title: SoftExpectHelper +--- + + + +## SoftAssertHelper + +**Extends ExpectHelper** + +SoftAssertHelper is a utility class for performing soft assertions. +Unlike traditional assertions that stop the execution on failure, +soft assertions allow the execution to continue and report all failures at the end. + +### Examples + +Zero-configuration when paired with other helpers like REST, Playwright: + +```js +// inside codecept.conf.js +{ + helpers: { + Playwright: {...}, + SoftExpectHelper: {}, + } +} +``` + +```js +// in scenario +I.softExpectEqual('a', 'b') +I.flushSoftAssertions() // Throws an error if any soft assertions have failed. The error message contains all the accumulated failures. +``` + +## Methods + +### flushSoftAssertions + +Throws an error if any soft assertions have failed. +The error message contains all the accumulated failures. + +- Throws **[Error][1]** If there are any soft assertion failures. + +### softAssert + +Performs a soft assertion by executing the provided assertion function. +If the assertion fails, the error is caught and stored without halting the execution. + +#### Parameters + +- `assertionFn` **[Function][2]** The assertion function to execute. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectAbove + +Softly asserts that the target data is above a specified value. + +#### Parameters + +- `targetData` **any** The data to check. +- `aboveThan` **any** The value that the target data should be above. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectBelow + +Softly asserts that the target data is below a specified value. + +#### Parameters + +- `targetData` **any** The data to check. +- `belowThan` **any** The value that the target data should be below. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectContain + +Softly asserts that a value contains the expected value. + +#### Parameters + +- `actualValue` **any** The actual value. +- `expectedValueToContain` **any** The value that should be contained within the actual value. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectDeepEqual + +Softly asserts that two values are deeply equal. + +#### Parameters + +- `actualValue` **any** The actual value. +- `expectedValue` **any** The expected value. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectDeepEqualExcluding + +Softly asserts that two objects are deeply equal, excluding specified fields. + +#### Parameters + +- `actualValue` **[Object][4]** The actual object. +- `expectedValue` **[Object][4]** The expected object. +- `fieldsToExclude` **[Array][5]<[string][3]>** The fields to exclude from the comparison. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectDeepIncludeMembers + +Softly asserts that an array (superset) deeply includes all members of another array (set). + +#### Parameters + +- `superset` **[Array][5]** The array that should contain the expected members. +- `set` **[Array][5]** The array with members that should be included. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectDeepMembers + +Softly asserts that two arrays have deep equality, considering members in any order. + +#### Parameters + +- `actualValue` **[Array][5]** The actual array. +- `expectedValue` **[Array][5]** The expected array. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectEmpty + +Softly asserts that the target data is empty. + +#### Parameters + +- `targetData` **any** The data to check. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectEndsWith + +Softly asserts that a value ends with the expected value. + +#### Parameters + +- `actualValue` **any** The actual value. +- `expectedValueToEndWith` **any** The value that the actual value should end with. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectEqual + +Softly asserts that two values are equal. + +#### Parameters + +- `actualValue` **any** The actual value. +- `expectedValue` **any** The expected value. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectEqualIgnoreCase + +Softly asserts that two values are equal, ignoring case. + +#### Parameters + +- `actualValue` **[string][3]** The actual string value. +- `expectedValue` **[string][3]** The expected string value. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectFalse + +Softly asserts that the target data is false. + +#### Parameters + +- `targetData` **any** The data to check. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectHasAProperty + +Softly asserts that the target data has a property with the specified name. + +#### Parameters + +- `targetData` **any** The data to check. +- `propertyName` **[string][3]** The property name to check for. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectHasProperty + +Softly asserts that the target data has the specified property. + +#### Parameters + +- `targetData` **any** The data to check. +- `propertyName` **[string][3]** The property name to check for. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion + fails. + +### softExpectJsonSchema + +Softly asserts that the target data matches the given JSON schema. + +#### Parameters + +- `targetData` **any** The data to validate. +- `jsonSchema` **[Object][4]** The JSON schema to validate against. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectJsonSchemaUsingAJV + +Softly asserts that the target data matches the given JSON schema using AJV. + +#### Parameters + +- `targetData` **any** The data to validate. +- `jsonSchema` **[Object][4]** The JSON schema to validate against. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. +- `ajvOptions` **[Object][4]** Options to pass to AJV. + +### softExpectLengthAboveThan + +Softly asserts that the length of the target data is above a specified value. + +#### Parameters + +- `targetData` **any** The data to check. +- `lengthAboveThan` **[number][6]** The length that the target data should be above. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectLengthBelowThan + +Softly asserts that the length of the target data is below a specified value. + +#### Parameters + +- `targetData` **any** The data to check. +- `lengthBelowThan` **[number][6]** The length that the target data should be below. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectLengthOf + +Softly asserts that the target data has a specified length. + +#### Parameters + +- `targetData` **any** The data to check. +- `length` **[number][6]** The expected length. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectMatchesPattern + +Softly asserts that a value matches the expected pattern. + +#### Parameters + +- `actualValue` **any** The actual value. +- `expectedPattern` **any** The pattern the value should match. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectNotContain + +Softly asserts that a value does not contain the expected value. + +#### Parameters + +- `actualValue` **any** The actual value. +- `expectedValueToNotContain` **any** The value that should not be contained within the actual value. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectNotDeepEqual + +Softly asserts that two values are not deeply equal. + +#### Parameters + +- `actualValue` **any** The actual value. +- `expectedValue` **any** The expected value. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectNotEndsWith + +Softly asserts that a value does not end with the expected value. + +#### Parameters + +- `actualValue` **any** The actual value. +- `expectedValueToNotEndWith` **any** The value that the actual value should not end with. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectNotEqual + +Softly asserts that two values are not equal. + +#### Parameters + +- `actualValue` **any** The actual value. +- `expectedValue` **any** The expected value. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectNotStartsWith + +Softly asserts that a value does not start with the expected value. + +#### Parameters + +- `actualValue` **any** The actual value. +- `expectedValueToNotStartWith` **any** The value that the actual value should not start with. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectStartsWith + +Softly asserts that a value starts with the expected value. + +#### Parameters + +- `actualValue` **any** The actual value. +- `expectedValueToStartWith` **any** The value that the actual value should start with. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectToBeA + +Softly asserts that the target data is of a specific type. + +#### Parameters + +- `targetData` **any** The data to check. +- `type` **[string][3]** The expected type (e.g., 'string', 'number'). +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectToBeAn + +Softly asserts that the target data is of a specific type (alternative for articles). + +#### Parameters + +- `targetData` **any** The data to check. +- `type` **[string][3]** The expected type (e.g., 'string', 'number'). +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +### softExpectTrue + +Softly asserts that the target data is true. + +#### Parameters + +- `targetData` **any** The data to check. +- `customErrorMsg` **[string][3]** A custom error message to display if the assertion fails. + +[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error + +[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function + +[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String + +[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object + +[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array + +[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number diff --git a/lib/helper/SoftExpectHelper.js b/lib/helper/SoftExpectHelper.js new file mode 100644 index 000000000..326a8698c --- /dev/null +++ b/lib/helper/SoftExpectHelper.js @@ -0,0 +1,381 @@ +const ExpectHelper = require('./ExpectHelper') + +/** + * SoftAssertHelper is a utility class for performing soft assertions. + * Unlike traditional assertions that stop the execution on failure, + * soft assertions allow the execution to continue and report all failures at the end. + * + * ### Examples + * + * Zero-configuration when paired with other helpers like REST, Playwright: + * + * ```js + * // inside codecept.conf.js + * { + * helpers: { + * Playwright: {...}, + * SoftExpectHelper: {}, + * } + * } + * ``` + * + * ```js + * // in scenario + * I.softExpectEqual('a', 'b') + * I.flushSoftAssertions() // Throws an error if any soft assertions have failed. The error message contains all the accumulated failures. + * ``` + * + * ## Methods + */ +class SoftAssertHelper extends ExpectHelper { + constructor() { + super() + this.errors = [] + } + + /** + * Performs a soft assertion by executing the provided assertion function. + * If the assertion fails, the error is caught and stored without halting the execution. + * + * @param {Function} assertionFn - The assertion function to execute. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softAssert(assertionFn, customErrorMsg = '') { + try { + assertionFn() + } catch (error) { + this.errors.push({ customErrorMsg, error }) + } + } + + /** + * Throws an error if any soft assertions have failed. + * The error message contains all the accumulated failures. + * + * @throws {Error} If there are any soft assertion failures. + */ + flushSoftAssertions() { + if (this.errors.length > 0) { + let errorMessage = 'Soft assertions failed:\n' + this.errors.forEach((err, index) => { + errorMessage += `\n[${index + 1}] ${err.customErrorMsg}\n${err.error.message}\n` + }) + this.errors = [] + throw new Error(errorMessage) + } + } + + /** + * Softly asserts that two values are equal. + * + * @param {*} actualValue - The actual value. + * @param {*} expectedValue - The expected value. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softExpectEqual(actualValue, expectedValue, customErrorMsg = '') { + this.softAssert(() => this.expectEqual(actualValue, expectedValue, customErrorMsg), customErrorMsg) + } + + /** + * Softly asserts that two values are not equal. + * + * @param {*} actualValue - The actual value. + * @param {*} expectedValue - The expected value. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softExpectNotEqual(actualValue, expectedValue, customErrorMsg = '') { + this.softAssert(() => this.expectNotEqual(actualValue, expectedValue, customErrorMsg), customErrorMsg) + } + + /** + * Softly asserts that two values are deeply equal. + * + * @param {*} actualValue - The actual value. + * @param {*} expectedValue - The expected value. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softExpectDeepEqual(actualValue, expectedValue, customErrorMsg = '') { + this.softAssert(() => this.expectDeepEqual(actualValue, expectedValue, customErrorMsg), customErrorMsg) + } + + /** + * Softly asserts that two values are not deeply equal. + * + * @param {*} actualValue - The actual value. + * @param {*} expectedValue - The expected value. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softExpectNotDeepEqual(actualValue, expectedValue, customErrorMsg = '') { + this.softAssert(() => this.expectNotDeepEqual(actualValue, expectedValue, customErrorMsg), customErrorMsg) + } + + /** + * Softly asserts that a value contains the expected value. + * + * @param {*} actualValue - The actual value. + * @param {*} expectedValueToContain - The value that should be contained within the actual value. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softExpectContain(actualValue, expectedValueToContain, customErrorMsg = '') { + this.softAssert(() => this.expectContain(actualValue, expectedValueToContain, customErrorMsg), customErrorMsg) + } + + /** + * Softly asserts that a value does not contain the expected value. + * + * @param {*} actualValue - The actual value. + * @param {*} expectedValueToNotContain - The value that should not be contained within the actual value. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softExpectNotContain(actualValue, expectedValueToNotContain, customErrorMsg = '') { + this.softAssert(() => this.expectNotContain(actualValue, expectedValueToNotContain, customErrorMsg), customErrorMsg) + } + + /** + * Softly asserts that a value starts with the expected value. + * + * @param {*} actualValue - The actual value. + * @param {*} expectedValueToStartWith - The value that the actual value should start with. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softExpectStartsWith(actualValue, expectedValueToStartWith, customErrorMsg = '') { + this.softAssert(() => this.expectStartsWith(actualValue, expectedValueToStartWith, customErrorMsg), customErrorMsg) + } + + /** + * Softly asserts that a value does not start with the expected value. + * + * @param {*} actualValue - The actual value. + * @param {*} expectedValueToNotStartWith - The value that the actual value should not start with. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softExpectNotStartsWith(actualValue, expectedValueToNotStartWith, customErrorMsg = '') { + this.softAssert( + () => this.expectNotStartsWith(actualValue, expectedValueToNotStartWith, customErrorMsg), + customErrorMsg, + ) + } + + /** + * Softly asserts that a value ends with the expected value. + * + * @param {*} actualValue - The actual value. + * @param {*} expectedValueToEndWith - The value that the actual value should end with. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softExpectEndsWith(actualValue, expectedValueToEndWith, customErrorMsg = '') { + this.softAssert(() => this.expectEndsWith(actualValue, expectedValueToEndWith, customErrorMsg), customErrorMsg) + } + + /** + * Softly asserts that a value does not end with the expected value. + * + * @param {*} actualValue - The actual value. + * @param {*} expectedValueToNotEndWith - The value that the actual value should not end with. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softExpectNotEndsWith(actualValue, expectedValueToNotEndWith, customErrorMsg = '') { + this.softAssert( + () => this.expectNotEndsWith(actualValue, expectedValueToNotEndWith, customErrorMsg), + customErrorMsg, + ) + } + + /** + * Softly asserts that the target data matches the given JSON schema. + * + * @param {*} targetData - The data to validate. + * @param {Object} jsonSchema - The JSON schema to validate against. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softExpectJsonSchema(targetData, jsonSchema, customErrorMsg = '') { + this.softAssert(() => this.expectJsonSchema(targetData, jsonSchema, customErrorMsg), customErrorMsg) + } + + /** + * Softly asserts that the target data matches the given JSON schema using AJV. + * + * @param {*} targetData - The data to validate. + * @param {Object} jsonSchema - The JSON schema to validate against. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + * @param {Object} [ajvOptions={ allErrors: true }] - Options to pass to AJV. + */ + softExpectJsonSchemaUsingAJV(targetData, jsonSchema, customErrorMsg = '', ajvOptions = { allErrors: true }) { + this.softAssert( + () => this.expectJsonSchemaUsingAJV(targetData, jsonSchema, customErrorMsg, ajvOptions), + customErrorMsg, + ) + } + + /** + * Softly asserts that the target data has the specified property. + * + * @param {*} targetData - The data to check. + * @param {string} propertyName - The property name to check for. + * @param {string} [customErrorMsg=''] - A custom error message to display if the assertion + fails. */ softExpectHasProperty(targetData, propertyName, customErrorMsg = '') { + this.softAssert(() => this.expectHasProperty(targetData, propertyName, customErrorMsg), customErrorMsg) + } + + /** + Softly asserts that the target data has a property with the specified name. + @param {*} targetData - The data to check. + @param {string} propertyName - The property name to check for. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. + */ + softExpectHasAProperty(targetData, propertyName, customErrorMsg = '') { + this.softAssert(() => this.expectHasAProperty(targetData, propertyName, customErrorMsg), customErrorMsg) + } + + /** + Softly asserts that the target data is of a specific type. + @param {*} targetData - The data to check. + @param {string} type - The expected type (e.g., 'string', 'number'). + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectToBeA(targetData, type, customErrorMsg = '') { + this.softAssert(() => this.expectToBeA(targetData, type, customErrorMsg), customErrorMsg) + } + + /** + Softly asserts that the target data is of a specific type (alternative for articles). + @param {*} targetData - The data to check. + @param {string} type - The expected type (e.g., 'string', 'number'). + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectToBeAn(targetData, type, customErrorMsg = '') { + this.softAssert(() => this.expectToBeAn(targetData, type, customErrorMsg), customErrorMsg) + } + + /* + Softly asserts that the target data matches the specified regular expression. + @param {*} targetData - The data to check. + @param {RegExp} regex - The regular expression to match. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectMatchRegex(targetData, regex, customErrorMsg = '') { + this.softAssert(() => this.expectMatchRegex(targetData, regex, customErrorMsg), customErrorMsg) + } + + /** + Softly asserts that the target data has a specified length. + @param {*} targetData - The data to check. + @param {number} length - The expected length. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectLengthOf(targetData, length, customErrorMsg = '') { + this.softAssert(() => this.expectLengthOf(targetData, length, customErrorMsg), customErrorMsg) + } + + /** + + Softly asserts that the target data is empty. + @param {*} targetData - The data to check. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectEmpty(targetData, customErrorMsg = '') { + this.softAssert(() => this.expectEmpty(targetData, customErrorMsg), customErrorMsg) + } + + /** + + Softly asserts that the target data is true. + @param {*} targetData - The data to check. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectTrue(targetData, customErrorMsg = '') { + this.softAssert(() => this.expectTrue(targetData, customErrorMsg), customErrorMsg) + } + + /** + + Softly asserts that the target data is false. + @param {*} targetData - The data to check. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectFalse(targetData, customErrorMsg = '') { + this.softAssert(() => this.expectFalse(targetData, customErrorMsg), customErrorMsg) + } + + /** + + Softly asserts that the target data is above a specified value. + @param {*} targetData - The data to check. + @param {*} aboveThan - The value that the target data should be above. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectAbove(targetData, aboveThan, customErrorMsg = '') { + this.softAssert(() => this.expectAbove(targetData, aboveThan, customErrorMsg), customErrorMsg) + } + + /** + + Softly asserts that the target data is below a specified value. + @param {*} targetData - The data to check. + @param {*} belowThan - The value that the target data should be below. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectBelow(targetData, belowThan, customErrorMsg = '') { + this.softAssert(() => this.expectBelow(targetData, belowThan, customErrorMsg), customErrorMsg) + } + + /** + + Softly asserts that the length of the target data is above a specified value. + @param {*} targetData - The data to check. + @param {number} lengthAboveThan - The length that the target data should be above. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectLengthAboveThan(targetData, lengthAboveThan, customErrorMsg = '') { + this.softAssert(() => this.expectLengthAboveThan(targetData, lengthAboveThan, customErrorMsg), customErrorMsg) + } + + /** + Softly asserts that the length of the target data is below a specified value. + @param {*} targetData - The data to check. + @param {number} lengthBelowThan - The length that the target data should be below. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectLengthBelowThan(targetData, lengthBelowThan, customErrorMsg = '') { + this.softAssert(() => this.expectLengthBelowThan(targetData, lengthBelowThan, customErrorMsg), customErrorMsg) + } + + /** + Softly asserts that two values are equal, ignoring case. + @param {string} actualValue - The actual string value. + @param {string} expectedValue - The expected string value. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectEqualIgnoreCase(actualValue, expectedValue, customErrorMsg = '') { + this.softAssert(() => this.expectEqualIgnoreCase(actualValue, expectedValue, customErrorMsg), customErrorMsg) + } + + /** + Softly asserts that two arrays have deep equality, considering members in any order. + @param {Array} actualValue - The actual array. + @param {Array} expectedValue - The expected array. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectDeepMembers(actualValue, expectedValue, customErrorMsg = '') { + this.softAssert(() => this.expectDeepMembers(actualValue, expectedValue, customErrorMsg), customErrorMsg) + } + + /** + Softly asserts that an array (superset) deeply includes all members of another array (set). + @param {Array} superset - The array that should contain the expected members. + @param {Array} set - The array with members that should be included. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectDeepIncludeMembers(superset, set, customErrorMsg = '') { + this.softAssert(() => this.expectDeepIncludeMembers(superset, set, customErrorMsg), customErrorMsg) + } + + /** + Softly asserts that two objects are deeply equal, excluding specified fields. + @param {Object} actualValue - The actual object. + @param {Object} expectedValue - The expected object. + @param {Array} fieldsToExclude - The fields to exclude from the comparison. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectDeepEqualExcluding(actualValue, expectedValue, fieldsToExclude, customErrorMsg = '') { + this.softAssert( + () => this.expectDeepEqualExcluding(actualValue, expectedValue, fieldsToExclude, customErrorMsg), + customErrorMsg, + ) + } + + /** + Softly asserts that a value matches the expected pattern. + @param {*} actualValue - The actual value. + @param {*} expectedPattern - The pattern the value should match. + @param {string} [customErrorMsg=''] - A custom error message to display if the assertion fails. */ + softExpectMatchesPattern(actualValue, expectedPattern, customErrorMsg = '') { + this.softAssert(() => this.expectMatchesPattern(actualValue, expectedPattern, customErrorMsg), customErrorMsg) + } +} +module.exports = SoftAssertHelper diff --git a/test/helper/SoftExpect_test.js b/test/helper/SoftExpect_test.js new file mode 100644 index 000000000..d6f4af136 --- /dev/null +++ b/test/helper/SoftExpect_test.js @@ -0,0 +1,479 @@ +const path = require('path') + +let expect +import('chai').then((chai) => { + expect = chai.expect +}) + +const SoftAssertHelper = require('../../lib/helper/SoftExpectHelper') + +global.codeceptjs = require('../../lib') + +let I + +const goodApple = { + skin: 'thin', + colors: ['red', 'green', 'yellow'], + taste: 10, +} +const badApple = { + colors: ['brown'], + taste: 0, + worms: 2, +} +const fruitSchema = { + title: 'fresh fruit schema v1', + type: 'object', + required: ['skin', 'colors', 'taste'], + properties: { + colors: { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + type: 'string', + }, + }, + skin: { + type: 'string', + }, + taste: { + type: 'number', + minimum: 5, + }, + }, +} + +describe('Soft Expect Helper', function () { + this.timeout(3000) + this.retries(1) + + before(() => { + global.codecept_dir = path.join(__dirname, '/../data') + + I = new SoftAssertHelper() + }) + + describe('#softExpectEqual', () => { + it('should not show error', () => { + I.softExpectEqual('a', 'a') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectEqual('a', 'b') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain("expected 'a' to equal 'b'") + } + }) + }) + + describe('#softExpectNotEqual', () => { + it('should not show error', () => { + I.softExpectNotEqual('a', 'b') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectNotEqual('a', 'a') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain("expected 'a' to not equal 'a'") + } + }) + }) + + describe('#softExpectContain', () => { + it('should not show error', () => { + I.softExpectContain('abc', 'a') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectContain('abc', 'd') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain("expected 'abc' to include 'd'") + } + }) + }) + + describe('#softExpectNotContain', () => { + it('should not show error', () => { + I.softExpectNotContain('abc', 'd') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectNotContain('abc', 'a') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain("expected 'abc' to not include 'a'") + } + }) + }) + + describe('#softExpectStartsWith', () => { + it('should not show error', () => { + I.softExpectStartsWith('abc', 'a') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectStartsWith('abc', 'b') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected abc to start with b') + } + }) + }) + + describe('#softExpectNotStartsWith', () => { + it('should not show error', () => { + I.softExpectNotStartsWith('abc', 'b') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectNotStartsWith('abc', 'a') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected abc not to start with a') + } + }) + }) + + describe('#softExpectEndsWith', () => { + it('should not show error', () => { + I.softExpectEndsWith('abc', 'c') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectEndsWith('abc', 'd') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected abc to end with d') + } + }) + }) + + describe('#softExpectNotEndsWith', () => { + it('should not show error', () => { + I.softExpectNotEndsWith('abc', 'd') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectNotEndsWith('abc', 'd') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected abc not to end with c') + } + }) + }) + + describe('#softExpectJsonSchema', () => { + it('should not show error', () => { + I.softExpectJsonSchema(goodApple, fruitSchema) + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectJsonSchema(badApple, fruitSchema) + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected value to match json-schema') + } + }) + }) + + describe('#softExpectHasProperty', () => { + it('should not show error', () => { + I.softExpectHasProperty(goodApple, 'skin') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectHasProperty(badApple, 'skin') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected { Object (colors, taste') + } + }) + }) + + describe('#softExpectHasAProperty', () => { + it('should not show error', () => { + I.softExpectHasAProperty(goodApple, 'skin') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectHasAProperty(badApple, 'skin') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected { Object (colors, taste') + } + }) + }) + + describe('#softExpectToBeA', () => { + it('should not show error', () => { + I.softExpectToBeA(goodApple, 'object') + I.flushSoftAssertions() + }) + }) + + describe('#softExpectToBeAn', () => { + it('should not show error', () => { + I.softExpectToBeAn(goodApple, 'object') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectToBeAn(badApple, 'skin') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected { Object (colors, taste') + } + }) + }) + + describe('#softExpectMatchRegex', () => { + it('should not show error', () => { + I.softExpectMatchRegex('goodApple', /good/) + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectMatchRegex('Apple', /good/) + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('to match /good/') + } + }) + }) + + describe('#softExpectLengthOf', () => { + it('should not show error', () => { + I.softExpectLengthOf('good', 4) + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectLengthOf('Apple', 4) + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('to have a length') + } + }) + }) + + describe('#softExpectTrue', () => { + it('should not show error', () => { + I.softExpectTrue(true) + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectTrue(false) + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected false to be true') + } + }) + }) + + describe('#softExpectEmpty', () => { + it('should not show error', () => { + I.softExpectEmpty('') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectEmpty('false') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain("expected 'false' to be empty") + } + }) + }) + + describe('#softExpectFalse', () => { + it('should not show error', () => { + I.softExpectFalse(false) + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectFalse(true) + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected true to be false') + } + }) + }) + + describe('#softExpectAbove', () => { + it('should not show error', () => { + I.softExpectAbove(2, 1) + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectAbove(1, 2) + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected 1 to be above 2') + } + }) + }) + + describe('#softExpectBelow', () => { + it('should not show error', () => { + I.softExpectBelow(1, 2) + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectBelow(2, 1) + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected 2 to be below 1') + } + }) + }) + + describe('#softExpectLengthAboveThan', () => { + it('should not show error', () => { + I.softExpectLengthAboveThan('hello', 4) + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectLengthAboveThan('hello', 5) + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('to have a length above 5') + } + }) + }) + + describe('#softExpectLengthBelowThan', () => { + it('should not show error', () => { + I.softExpectLengthBelowThan('hello', 6) + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectLengthBelowThan('hello', 4) + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('to have a length below 4') + } + }) + }) + + describe('#softExpectLengthBelowThan', () => { + it('should not show error', () => { + I.softExpectEqualIgnoreCase('hEllo', 'hello') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectEqualIgnoreCase('hEllo', 'hell0') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected hEllo to equal hell0') + } + }) + }) + + describe('#softExpectDeepMembers', () => { + it('should not show error', () => { + I.softExpectDeepMembers([1, 2, 3], [1, 2, 3]) + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectDeepMembers([1, 2, 3], [3]) + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected [ 1, 2, 3 ] to have the same members') + } + }) + }) + + describe('#softExpectDeepIncludeMembers', () => { + it('should not show error', () => { + I.softExpectDeepIncludeMembers([3, 4, 5, 6], [3, 4, 5]) + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectDeepIncludeMembers([3, 4, 5], [3, 4, 5, 6]) + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected [ 3, 4, 5 ] to be a superset of [ 3, 4, 5, 6 ]') + } + }) + }) + + describe('#softExpectDeepEqualExcluding', () => { + it('should not show error', () => { + I.softExpectDeepEqualExcluding({ a: 1, b: 2 }, { b: 2, a: 1, c: 3 }, 'c') + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectDeepEqualExcluding({ a: 1, b: 2 }, { b: 2, a: 1, c: 3 }, 'a') + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain('expected { b: 2 } to deeply equal') + } + }) + }) + + describe('#softExpectLengthBelowThan', () => { + it('should not show error', () => { + I.softExpectMatchesPattern('123', /123/) + I.flushSoftAssertions() + }) + + it('should show error', () => { + try { + I.softExpectMatchesPattern('123', /1235/) + I.flushSoftAssertions() + } catch (e) { + expect(e.message).to.contain("didn't match target /1235/") + } + }) + }) +})