Source: globals/chunk.js

"use strict";

const _ = require("lodash");
const isPromise = require("is-promise");

const CONF = require("../config");

/**
 * Global function, existing in `glace` tests, which creates test chunk.
 *
 * `chunk` is independently executed part of `test`. It means that even if
 * first chunk is failed, other will be executed in any case. `test` should
 * contain as minimum one `chunk`.
 *
 * @global
 * @function
 * @arg {string} [name=null] - Name of chunk.
 * @arg {object} [opts] - Chunk options.
 * @arg {?number} [opts.retry=null] - Number of chunk retries on failure. Overrides
 * [config](GlaceConfig.html#test-chunk-retry) and [test](#test-chunk-retry) settings.
 * @arg {?number} [opts.timeout=null] - Time limit to execute chunk, **sec**. Overrides
 * [config](GlaceConfig.html#test-chunk-timeout) and [test](#test-chunk-timeout) settings.
 * @arg {function} func - Callback function with test payload. Can be `async` too.
 *
 * @example <caption><b>Anonymous chunk</b></caption>
 *
 * test("My test", () => {
 *     chunk(() => {
 *         var a = 5;
 *         expect(a).to.be.equal(2);
 *     });
 * });
 *
 * @example <caption><b>Named chunk</b></caption>
 *
 * test("My test", () => {
 *     chunk("My chunk", () => {
 *         var a = 5;
 *         expect(a).to.be.equal(2);
 *     });
 * });
 *
 * @example <caption><b>Chunk with options</b></caption>
 *
 * test("My test", () => {
 *     chunk("My chunk", { retry: 2, timeout: 1 }, () => {
 *         var a = 5;
 *         expect(a).to.be.equal(2);
 *     });
 * });
 *
 * @example <caption><b>Several chunks in test</b></caption>
 *
 * test("My test", () => {
 *     chunk("first chunk", () => {
 *         expect(2).to.be.equal(3);
 *     });
 *     chunk("second chunk", () => {
 *         expect(3).to.be.equal(3);
 *     });
 * });
 *
 * @example <caption><b>Async chunk</b></caption>
 *
 * test("My test", () => {
 *     chunk(async () => {
 *         await Promise.resolve("done!");
 *     });
 * });
 */
const chunk = (name, opts, func) => {

    if (name instanceof Function) {
        func = name;
        name = "";
        opts = {};
    }

    if (opts instanceof Function) {
        func = opts;
        opts = {};
    }

    if (name instanceof Object) {
        opts = name;
        name = "";
    }

    name = name || "";
    opts = opts || {};

    CONF.chunk.id++;
    const chunkId = CONF.test.id + "_" + CONF.chunk.id;

    if (CONF.chunk.passedIds.includes(chunkId)) return;

    if (CONF.retry.id) {
        if (!_.flatMap(CONF.retry.chunkIds).includes(chunkId)) return;
    } else {
        CONF.retry.curChunkIds.push(chunkId);
    };

    it(name, _chunkCb(name, chunkId, opts, func));
};

/**
 * Chunk callback.
 * @ignore
 */
const _chunkCb = (name, chunkId, opts, func) => function () {
    expect(CONF.test.curCase, "Oops! Chunk is used outside of test").to.exist;
    CONF.test.curCase.addChunk(name);
    CONF.chunk.curId = chunkId;

    if (opts.retry) this.retries(opts.retry);
    if (opts.timeout) this.timeout(opts.timeout * 1000);

    let result = func();

    if (isPromise(result)) {
        result = result.then(r => {
            if (r === false) CONF.test.curCase.skipChunk = name;
            return r;
        });
    } else {
        if (result === false) CONF.test.curCase.skipChunk = name;
    }

    return result;
};

module.exports = chunk;