All files / lib/globals test.js

100% Statements 91/91
100% Branches 50/50
100% Functions 13/13
100% Lines 77/77

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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173    2x   2x 2x   2x 2x 2x 2x   2x 2x 226x 226x       226x   225x 2x 2x 2x 1x 1x 1x     2x     224x 2x 2x       223x 4x 1x   3x     222x 222x 222x   222x 222x     222x 221x 221x 221x 221x 2x 2x     1x 1x     222x   220x 220x   220x       2x 206x 206x   206x 206x       2x 207x     207x 207x 207x 207x 207x 207x   207x     208x 208x 1x   207x       2x 10x 9x 9x 2x   7x                                                                                           2x   210x 210x 210x   210x 210x   210x     2x  
"use strict";
 
const path = require("path");
 
const _ = require("lodash");
const U = require("glace-utils");
 
const CONF = require("../config");
const TestCase = require("../testing").TestCase;
const ScopeType = require("../testing").ScopeType;
const setLog = require("../utils").setLog;
 
const _test = (names => {
    return (name, fixtures, opts, func) => {
        CONF.test.id++;
        CONF.chunk.id = 0;
 
        let preloaded_passed_chunk_ids;
 
        if (CONF.filter.testIds && !CONF.filter.testIds.includes(CONF.test.id)) return;
 
        if (CONF.filter.include) {
            let isIncluded = false;
            for (const include of CONF.filter.include) {
                if (isFilterMatched(name, include.id)) {
                    preloaded_passed_chunk_ids = include.passed_chunk_ids;
                    isIncluded = true;
                    break;
                }
            }
            if (!isIncluded) return;
        }
 
        if (CONF.filter.exclude) {
            for (const exclude of CONF.filter.exclude) {
                if (isFilterMatched(name, exclude.id)) return;
            }
        }
 
        if (CONF.test.checkNames && !CONF.retry.id) {
            if (names.includes(name)) {
                throw new Error(`Test case '${name}' is added already`);
            }
            names.push(name);
        }
 
        const skip = !!opts.skip;
        const skipReason = _.isString(opts.skip) ? opts.skip : null;
        const retries = U.defVal(opts.retry, CONF.test.retries, 0);
 
        opts.chunkRetry = U.defVal(opts.chunkRetry, CONF.chunk.retries, 0);
        opts.chunkTimeout = U.defVal(opts.chunkTimeout);
 
        let testCase;
        if (!CONF.retry.id) {
            testCase = new TestCase(name, CONF.test.id);
            testCase.addPassedChunkIds(preloaded_passed_chunk_ids || []);
            CONF.test.cases.push(testCase);
            if (skip) {
                testCase.status = TestCase.SKIPPED;
                if (skipReason) testCase.addDetails(skipReason);
            }
        } else {
            testCase = CONF.test.cases.filter(t => t.id === CONF.test.id)[0];
            expect(testCase, "Oops! Testcase isn't found by id").to.exist;
        }
 
        if (testCase.status === TestCase.SKIPPED) return;
 
        if (!CONF.retry.chunkIds[retries]) CONF.retry.chunkIds[retries] = [];
        CONF.retry.curChunkIds = CONF.retry.chunkIds[retries];
 
        testFunc({ testCase, fixtures, opts, func });
    };
})([]);
 
const testFunc = ({ testCase, fixtures, opts, func }) => {
    fixtures = [initTestFixture(testCase)].concat(fixtures);
    const scopeType = new ScopeType(testCase.name).setType("test");
 
    scope(scopeType, fixtures, opts, () => {
        func();
    });
};
 
const initTestFixture = testCase => {
    return U.makeFixture({ before: beforeCb(testCase), after: afterCb });
};
 
const beforeCb = testCase => ctx => () => {
    ctx.testCase = testCase;
    ctx.testCase.reset();
    ctx.testCase.start();
    CONF.test.curCase = ctx.testCase;
    CONF.report.testDir = path.resolve(
        CONF.report.dir, "tests", U.toKebab(ctx.testCase.name));
    setLog(); // Current test case is started, need to reinit log
};
 
const afterCb = ctx => () => {
    if (ctx.testCase.errors.length) {
        ctx.testCase.end(TestCase.FAILED);
    } else {
        ctx.testCase.end(TestCase.PASSED);
    }
};
 
const isFilterMatched = (testName, filterId) => {
    if (filterId == CONF.test.id) return true;
    filterId = filterId.toString();
    if (CONF.filter.precise) {
        return testName.toLowerCase() === filterId.toLowerCase();
    } else {
        return testName.toLowerCase().includes(filterId.toLowerCase());
    }
};
 
/**
 * Global function, existing in `glace` tests, which creates test case.
 *
 * @global
 * @function
 * @arg {string} name - Name of test case. By default, should be unique in session.
 * But uniqueness check can be skipped with CLI option `--dont-check-names`.
 * @arg {object} [opts] - Options.
 * @arg {boolean|string} [opts.skip=false] - Flag to skip test or skip reason message.
 * @arg {?number} [opts.retry=null] - Number of test retries on failure. Overrides
 * [config](GlaceConfig.html#test-retry) settings.
 * @arg {?number} [opts.chunkRetry=null] - <a name="test-chunk-retry" href="#test-chunk-retry">#</a>
 * Number of chunk retries on failure. Overrides [config](GlaceConfig.html#test-chunk-retry) settings.
 * @arg {?number} [opts.chunkTimeout=null] - <a name="test-chunk-timeout" href="#test-chunk-timeout">#</a>
 * Time to execute chunk or hook, **sec**. Overrides [config](GlaceConfig.html#test-chunk-timeout) settings.
 * @arg {function[]} [fixtures] - Involved [fixtures](tutorial-common-used-funcs.html#fixtures) list.
 * @arg {function} func - Сallback function with [chunks](#chunk__anchor) and hooks.
 *
 * @example <caption><b>Simple test</b></caption>
 *
 * test("Some test", () => {
 *     chunk("Some chunk", () => {
 *         someFunc();
 *     });
 * });
 *
 * @example <caption><b>Test with retry</b></caption>
 *
 * test("Test with retry", { retry: 2 }, () => {
 *     chunk(() => {
 *         someFunc();
 *     });
 * });
 *
 * @example <caption><b>Test with fixtures</b></caption>
 *
 * test("Test with fixtures", null, [fix_func_1, fix_func_2], () => {
 *     chunk(() => {
 *         someFunc();
 *     });
 * });
 */
const test = (name, fixtures, opts, func) => {
 
    if (_.isFunction(opts)) [func, opts] = [opts];
    if (_.isPlainObject(fixtures)) [opts, fixtures] = [fixtures];
    if (_.isFunction(fixtures)) [func, fixtures] = [fixtures];
 
    fixtures = fixtures || [];
    opts = opts || {};
 
    _test(name, fixtures, opts, func);
};
 
module.exports = test;