Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 2x 2x 2x 2x 2x 2x 8x 8x 8x 8x 8x 4x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 1x 1x | const { suite } = require('uvu')
const assert = require('uvu/assert')
 
/**
 * Generate function to process the test data from array and execute tests.
 *
 * The generated function accepts an array, verifies the shape and content as well
 * as either executes an uvu is assert using the extracted components or fails the
 * test upon execution with exposure of the problematic test vector.
 *
 * The options object with method and trace members if present sets the assertion method and tracing mode.
 *
 * Defaults are `assert.is` as assertion method and production (non-tracing) mode.
 *
 * Any valid test vector has
 * 1. content
 * 2. is a javascript array
 * 3. has 3 or more items
 * 4. has a function under test or its string representation as first item
 * 5. has the expectation (result) as last item
 * 6. all items but the first and last are positional input parameters to the function under test
 *
 * @param options is an optional object with method and trace members
 * @returns {(function(*=): void)|*}
 */
const process = (options) => {
    return vector => {
        const trace = options && options.trace
        if (trace) console.log(vector, options)
        const default_method = assert.is
        const method = options && options.method
            ? (typeof(options.method) === 'string'
                ? eval(options.method)
                : options.method)
            : default_method
        if (vector && Array.isArray(vector) && vector.length > 2 && typeof (vector[0]) === 'function') {
            const [function_under_test, parameters, expectation] = [
                typeof(vector[0]) === 'string' ? eval(vector[0]) : vector[0], vector.slice(1, -1), vector[vector.length - 1] ]
            if (trace) {
                console.log("Parsed:")
                console.log("       method: ", method)
                console.log("          fut: ", function_under_test)
                console.log("   parameters: ", parameters)
                console.log("  expectation: ", expectation)
            }
            if (!Array.isArray(expectation)) {
                method(function_under_test(...parameters), expectation)
            } else {
                const zipper = '_'
                method(function_under_test(...parameters).join(zipper), expectation.join(zipper))
            }
        } else {
            assert.is(false, true, `test vector data problem: ${JSON.stringify(vector)}`)
        }
    }
}
 
/**
 * Test builder within suites relying on convention to use system under test object as anchor for functions under test.
 *
 * The test cases must be an array of arrays with only strings as elements.
 * The fixture file absolute path must be present as first or only element.
 * Optionally a test name can be present as second element in addition - if not the name will be derived from fixture.
 *
 * @param sut is the object with all incoming functions under test as members
 * @param executor is the test suite object
 * @param test_cases an array of pairs with the first element the fixture path and the optional second naming the test
 * @param options an optional object to pass through for assert and trace modes
 */
const build_tests = (sut, executor, test_cases, options) => {
    test_cases.forEach(tc => {
        const fixture = tc[0]
        const name = tc.length === 2
            ? tc[1]
            : fixture  // Derive test name from fixture file name
                .split('/')  // Mostly works as universal path separator
                .pop()  // use the last element only (assuming it is the file name
                .replace('.json', '')  // remove extension from fixture file name, keep the specific part (base)
                .replaceAll('_', ' ').split(' ')  // prepare title case transform - array of words
                .map(s => s.slice(0, 1).toUpperCase() + s.slice(1).toLowerCase())
                .join(' ') // back to single string
 
        executor(name, () => {
            let vectors = require(fixture)
            vectors.forEach(e => e[0] = eval(e[0]))
            vectors.forEach(process(options))
        })
    })
    return executor
}
 
module.exports = { process, build_tests }
 
  |