/* lib/terminal/test/application/browser - The functions necessary to run browser test automation. */ import common from "../../../common/common.js";
import error from "../../utilities/error.js";
import humanTime from "../../utilities/humanTime.js";
import ipList from "../../utilities/ipList.js";
import log from "../../utilities/log.js";
import network from "../../server/transmission/network.js";
import node from "../../utilities/node.js";
import remove from "../../commands/library/remove.js";
import time from "../../utilities/time.js";
import transmit_http from "../../server/transmission/transmit_http.js";
import transmit_ws from "../../server/transmission/transmit_ws.js";
import uiDefault from "../../../common/uiDefault.js";
import vars from "../../utilities/vars.js";
import filePathDecode from "./browserUtilities/file_path_decode.js";
import machines from "./browserUtilities/machines.js";
import test_delete from "../samples/browser_delete.js";
import test_device from "../samples/browser_device.js";
import test_self from "../samples/browser_self.js";
import test_user from "../samples/browser_user.js";
let finished = false, tests;
const defaultCommand = vars.environment.command, defaultAddresses = vars.network.addresses, defaultStorage = vars.path.settings, /**
     * Methods associated with the browser test automation logic.
     * ```typescript
     * interface module_test_browserApplication {
     *     all_order  : test_browserMode[];         // The order in which test suites should be executed in the case of running all test suites.
     *     args       : config_test_browserExecute; // Default configuration object available to the entire test browser library. Over written by a configuration object of same type passed into the *methods.execute* method.
     *     exitMessage: string;                     // Stores an exit message string for availability to the entirety of the test browser application because this messaging is generated by one method and distributed by another.
     *     exitSummary: () => string[];             // Generates messaging, including browser.exitMessage, summarizing executing data associated with test operations on the given device.
     *     fail       : boolean;                    // Stores the pass/fail status for messaging to the callback.
     *     index      : number;                     // Stores the current test item index number.
     *     ip         : string;                     // Stores the IP address of the target machine for the current test index.
     *     methods: {
     *         close           : (data:service_testBrowser) => void;        // Sends a single that tests are complete and the respective browser window should close on the local device.
     *         delay           : (config:config_test_browserDelay) => void; // Provides a single point of logic to handle delays regardless of the cause, duration, or associated messaging.
     *         execute         : (args:config_test_browserExecute) => void; // Entry point to browser test automation that prepares the environment on the local device and tells the remote machines to reset their environments.
     *         exit            : (data:service_testBrowser) => void;        // Closes out testing on the local device and informs remote machines that testing has concluded with the corresponding messaging and a single to close their respective browser window.
     *         iterate         : (index:number) => void;                    // Validates the next browser test is properly formed and then either sends it to a browser on the local device or to the correct machine.
     *         reset           : () => void;                                // Sends a reset request to remote machines informing them to reset their environment and prepare to listen for incoming test items. Method executed from *methods.execute*.
     *         "reset-complete": (item:service_testBrowser) => void;        // Determines if the test environment is ready both locally and with remote agents.
     *         result          : (item:service_testBrowser) => void;        // Evaluation result provided by a browser and transforms that data into messaging for a human to read.
     *         route           : (socketData:socketData) => void;           // Entry point to the browser test automation library on all remote machines. Tasks are routed to the correct method based upon the action specified.
     *         send            : (testItem:service_testBrowser) => void;    // Encapsulates the transmission logic to send tests to the local browser.
     *         sendAction      : (action:test_browserAction, machine:string, exitMessage?:string) => void; // a convenience method to send arbitrary test actions to/from remotes.
     *     };
     *     name        : string; // indicates identity of the local machine
     *     port        : number; // Stores the port number of the target machine for the current test index.
     *     remote: {
     *         count: number;
     *         total: number;
     *     };                    // Counts the number of remote agents ready to receive tests.
     *     suites: {
     *         [key:string]: test_browserItem[];
     *     };                    // All the test lists.
     * }
     * ``` */ browser = {
    all_order: [
        "self",
        "delete",
        "device",
        "user"
    ],
    args: {
        callback: function terminal_test_application_browser_callback() {
            return;
        },
        demo: false,
        mode: "self",
        noClose: false
    },
    exitMessage: "",
    exitSummary: function terminal_test_application_browser_exitSummary() {
        const socketList = transmit_ws.socketMap[vars.identity.hashDevice], output = [
            browser.exitMessage,
            "",
            `${vars.text.underline}Network Transmissions${vars.text.none}`,
            `${vars.text.angry}*${vars.text.none} ${vars.text.cyan}HTTP Receive${vars.text.none} - ${common.commas(vars.network.count.http.receive)}`,
            `${vars.text.angry}*${vars.text.none} ${vars.text.cyan}HTTP Send${vars.text.none}    - ${common.commas(vars.network.count.http.send)}`,
            `${vars.text.angry}*${vars.text.none} ${vars.text.cyan}WS   Receive${vars.text.none} - ${common.commas(vars.network.count.ws.receive)}`,
            `${vars.text.angry}*${vars.text.none} ${vars.text.cyan}WS   Send${vars.text.none}    - ${common.commas(vars.network.count.ws.send)}`,
            `${vars.text.angry}*${vars.text.none} ${vars.text.cyan + vars.text.bold + vars.text.underline}Total${vars.text.none}        - ${common.commas(vars.network.count.http.receive + vars.network.count.http.send + vars.network.count.ws.receive + vars.network.count.ws.send)}`,
            "",
            `${vars.text.underline}Network Transmission Size (Bytes)${vars.text.none}`,
            `${vars.text.angry}*${vars.text.none} ${vars.text.cyan}HTTP Receive${vars.text.none} - ${common.commas(vars.network.size.http.receive)}`,
            `${vars.text.angry}*${vars.text.none} ${vars.text.cyan}HTTP Send${vars.text.none}    - ${common.commas(vars.network.size.http.send)}`,
            `${vars.text.angry}*${vars.text.none} ${vars.text.cyan}WS   Receive${vars.text.none} - ${common.commas(vars.network.size.ws.receive)}`,
            `${vars.text.angry}*${vars.text.none} ${vars.text.cyan}WS   Send${vars.text.none}    - ${common.commas(vars.network.size.ws.send)}`,
            `${vars.text.angry}*${vars.text.none} ${vars.text.cyan + vars.text.bold + vars.text.underline}Total${vars.text.none}        - ${common.commas(vars.network.size.http.receive + vars.network.size.http.send + vars.network.size.ws.receive + vars.network.size.ws.send)}`,
            "",
            `${vars.text.underline}Open Sockets${vars.text.none}`
        ], padding = function terminal_test_application_browser_exitSummary_padding(input, long) {
            let diff = longest[long] - input.length;
            if (long === 0) {
                input = vars.text.cyan + input + vars.text.none;
            }
            if (diff > 0) {
                do {
                    input = `${input} `;
                    diff = diff - 1;
                }while (diff > 0)
            }
            return input;
        }, len = socketList === undefined ? 0 : socketList.length, longest = [
            0,
            0
        ];
        let index = 0;
        if (len > 0) {
            do {
                if (socketList[index].type.length > longest[0]) {
                    longest[0] = socketList[index].type.length;
                }
                if (socketList[index].status.length > longest[1]) {
                    longest[1] = socketList[index].status.length;
                }
                index = index + 1;
            }while (index < len)
            index = 0;
            do {
                output.push(`${vars.text.angry}*${vars.text.none} ${padding(socketList[index].type, 0)} - ${padding(socketList[index].status, 1)} - ${socketList[index].name}`);
                index = index + 1;
            }while (index < len)
        }
        return output;
    },
    fail: false,
    index: -1,
    ip: "",
    methods: {
        close: function terminal_test_application_browser_close(data) {
            browser.methods.sendAction("close", browser.name, data.exit);
            browser.exitMessage = data.exit;
            log(browser.exitSummary());
        },
        delay: function terminal_test_application_browser_delay(config) {
            const wait = config.browser === true ? 0 : config.delay, seconds = (config.delay / 1000).toString(), plural = config.delay === 1000 ? "" : "s";
            if (config.delay > 0 && config.message !== "demo") {
                log([
                    `${humanTime(false)}Delaying for ${vars.text.cyan + seconds + vars.text.none} second${plural}: ${vars.text.cyan + config.message + vars.text.none}`
                ]);
            }
            setTimeout(config.action, wait);
        },
        execute: function terminal_test_application_browser_execute(args) {
            const remote = function terminal_test_application_browser_execute_remoteServer() {
                const list = ipList(null, true, ` ${vars.text.angry}*${vars.text.none} `);
                list.splice(0, 0, `${vars.text.cyan}Environment ready. Listening for instructions on these addresses:${vars.text.none}`);
                log(list);
            }, agents = function terminal_test_application_browser_execute_agents() {
                const list = Object.keys(machines), listLength = list.length;
                let index = 0;
                browser.remote.total = listLength;
                log([
                    `${humanTime(false)}Preparing remote machines`
                ]);
                vars.test.browser.test = {
                    interaction: null,
                    machine: null,
                    name: "",
                    unit: null
                };
                do {
                    if (list[index] === browser.name) {
                        browser.methods.reset();
                    } else {
                        transmit_ws.open.service({
                            callback: function terminal_test_application_browser_execute_agents_socketCallback(socket) {
                                log([
                                    `${humanTime(false)}Socket established to remote ${socket.hash}.`
                                ]);
                            },
                            handler: transmit_ws.clientReceiver,
                            hash: list[index],
                            ip: machines[list[index]].ip,
                            port: machines[list[index]].port.ws,
                            socketType: "testRemote"
                        });
                    }
                    index = index + 1;
                }while (index < listLength)
            };
            log.title(`Browser Tests - ${args.mode}`, true);
            browser.args = args;
            if (args.mode === "self") {
                tests = test_self;
            } else if (args.mode === "remote") {
                vars.settings.secure = true;
            } else if (args.mode === "all") {
                vars.settings.secure = true;
                tests = browser.suites[browser.all_order[0]];
            } else {
                vars.settings.secure = true;
                tests = browser.suites[args.mode];
            }
            vars.environment.command = "test_browser";
            vars.network.addresses = {
                IPv4: args.mode === "self" ? [
                    "127.0.0.1"
                ] : [
                    machines[browser.name].ip
                ],
                IPv6: []
            };
            vars.path.settings = vars.path.testStorage;
            vars.test.browser = {
                action: args.mode === "self" ? "result" : "reset",
                exit: browser.name,
                index: 0,
                result: [],
                test: args.mode === "self" ? tests[0] : null
            };
            vars.test.type = `browser_${args.mode}`;
            if (args.mode === "self") {
                browser.name = "self";
            }
            transmit_http.server({
                browser: false,
                host: "",
                port: -1,
                test: true
            }, {
                agent: "",
                agentType: "device",
                callback: args.mode === "remote" ? remote : args.mode === "self" ? browser.methods.reset : agents
            });
        },
        exit: function terminal_test_application_browser_exit(data) {
            if (finished === true) {
                return;
            }
            finished = true;
            if (browser.args.mode === "remote") {
                browser.exitMessage = data.exit;
                log(browser.exitSummary());
            } else {
                const summary = browser.exitSummary(), closing = browser.args.noClose === true ? function terminal_test_application_browser_exit_noClose() {
                    log(summary, true);
                } : function terminal_test_application_browser_exit_closing() {
                    browser.methods.sendAction("close", browser.name, browser.exitMessage);
                    browser.methods.delay({
                        action: function terminal_test_application_browser_exit_closing_delay() {
                            browser.index = -1;
                            vars.environment.command = defaultCommand;
                            vars.network.addresses = defaultAddresses;
                            vars.path.settings = defaultStorage;
                            vars.test.browser = null;
                            browser.args.callback(`Browser ${browser.args.mode} Test`, summary, browser.fail);
                        },
                        browser: false,
                        delay: 1000,
                        message: "Closing out the test environment."
                    });
                };
                summary.push("\u0007");
                if (browser.args.mode !== "self") {
                    const agents = transmit_ws.getSocketList("testRemote");
                    agents.forEach(function terminal_test_application_browser_exit_agents(socketItem) {
                        const action = browser.args.noClose === true ? "exit" : "close";
                        browser.methods.sendAction(action, socketItem.hash, browser.exitMessage);
                    });
                }
                closing();
            }
        },
        iterate: function terminal_test_application_browser_iterate(index) {
            // not writing to settings
            if (finished === true) {
                return;
            }
            let delayMessage = "", delayBrowser = false;
            const logs = [
                `Test ${index + 1} malformed: ${vars.text.angry + tests[index].name + vars.text.none}`,
                ""
            ], wait = function terminal_test_application_browser_iterate_wait() {
                const interactionLength = tests[index].interaction === null ? 0 : tests[index].interaction.length;
                let a = interactionLength, value = 0, count = 0;
                if (a > 0) {
                    do {
                        a = a - 1;
                        if (tests[index].interaction[a].event === "wait") {
                            value = Number(tests[index].interaction[a].value);
                            if (isNaN(value) === false) {
                                count = count + value;
                            }
                        }
                    }while (a > 0)
                }
                value = 2000;
                if (interactionLength > 0 && tests[index].interaction !== null && tests[index].interaction[0].event === "refresh" && tests[index + 1] !== undefined && tests[index + 1].machine !== browser.name && count < value) {
                    delayMessage = "Providing remote machine browser time before a refresh.";
                    return value;
                }
                if (browser.args.demo === true && count < 501) {
                    return 500;
                }
                if (count > 0) {
                    delayBrowser = true;
                }
                return count;
            }(), waitText = function terminal_test_application_browser_iterate_waitText(machine) {
                return delayMessage === "" && wait > 0 ? `Pausing for 'wait' event in browser on machine ${machine}.` : delayMessage;
            }, // determine if non-interactive events have required matching data properties
            validate = function terminal_test_application_browser_iterate_validate() {
                let a = 0;
                const length = tests[index].interaction === null ? 0 : tests[index].interaction.length, eventName = function terminal_test_application_browser_iterate_validate_eventName(property) {
                    return `   ${vars.text.angry}*${vars.text.none} Interaction ${a + 1} has event ${vars.text.cyan}setValue${vars.text.none} but no ${vars.text.angry + property + vars.text.none} property.`;
                };
                if (tests[index].delay === undefined && (tests[index].unit === null || tests[index].unit !== null && tests[index].unit.length < 1)) {
                    logs.push("Test does not contain points of evaluation in either a delay case or the unit array.");
                    return false;
                }
                if (length > 0) {
                    do {
                        if ((tests[index].interaction[a].event === "setValue" || tests[index].interaction[a].event === "keydown" || tests[index].interaction[a].event === "keyup") && tests[index].interaction[a].value === undefined) {
                            logs.push(eventName("value"));
                        } else if (tests[index].interaction[a].event === "move" && tests[index].interaction[a].coords === undefined) {
                            logs.push(eventName("coords"));
                        }
                        a = a + 1;
                    }while (a < length)
                }
                if (logs.length < 3) {
                    return true;
                }
                return false;
            };
            if (validate() === true) {
                vars.test.browser = {
                    action: "result",
                    exit: "",
                    index: index,
                    result: [],
                    test: tests[index]
                };
                browser.methods.delay({
                    action: function terminal_test_application_browser_iterate_selfDelay() {
                        browser.methods.send(vars.test.browser);
                    },
                    browser: delayBrowser,
                    delay: wait,
                    message: waitText(tests[index].machine)
                });
            } else {
                log(logs, true);
                if (browser.args.noClose === false) {
                    process.exit(1);
                }
            }
        },
        reset: function terminal_test_application_browser_reset() {
            const timeStore = time("Resetting Test Environment", false, 0), launch = function terminal_test_application_browser_reset_launch() {
                const start = function terminal_test_application_browser_reset_launch_start() {
                    const keyword = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open", port = vars.network.ports.http === 443 ? "" : `:${String(vars.network.ports.http)}`, verboseFlag = browser.args.mode !== "remote" && vars.settings.verbose === true ? "test_browser_verbose" : "test_browser", path = `https://${vars.network.domain[0] + port}/?${verboseFlag}`, // execute a browser by file path to the browser binary
                    browserCommand = process.argv.length > 0 && (process.argv[0].indexOf("\\") > -1 || process.argv[0].indexOf("/") > -1) ? function terminal_test_application_browser_reset_launch_start_browserCommand() {
                        if (process.platform === "win32") {
                            // yes, this is ugly.  Windows old cmd shell doesn't play well with file paths
                            process.argv[0] = `${process.argv[0].replace(/\\/g, "\"\\\"").replace("\"\\", "\\") + "\""}`;
                        } else {
                            process.argv[0] = `"${process.argv[0]}"`;
                        }
                        if (process.argv.length > 1) {
                            return `${keyword} ${process.argv[0]} ${path} "${process.argv.slice(1).join(" ")}"`;
                        }
                        return `${keyword} ${process.argv[0]} ${path}`;
                    }() : `${keyword} ${path}`, child = function terminal_test_application_browser_reset_launch_start_child(errs, stdout, stderr) {
                        if (errs !== null) {
                            error([
                                "Error opening browser in test automation."
                            ], errs);
                            return;
                        }
                        if (stdout !== "") {
                            log([
                                stdout
                            ]);
                        }
                        if (stderr !== "") {
                            log([
                                stderr.toString()
                            ]);
                        }
                    };
                    node.child_process.exec(browserCommand, {
                        cwd: vars.path.project
                    }, child);
                };
                log([
                    "",
                    "",
                    timeStore[0]
                ]);
                vars.agents.device = {};
                vars.agents.user = {};
                transmit_ws.socketMap = {};
                remove(`${vars.path.project}lib${vars.path.sep}terminal${vars.path.sep}test${vars.path.sep}application${vars.path.sep}documentation`, [], null);
                // close sockets
                {
                    const types = Object.keys(transmit_ws.socketStore);
                    let sockets = null, socketIndex = 0, typeIndex = types.length;
                    do {
                        typeIndex = typeIndex - 1;
                        if (types[typeIndex] !== "testRemote") {
                            sockets = transmit_ws.getSocketList(types[typeIndex]);
                            socketIndex = sockets.length;
                            if (socketIndex > 0) {
                                do {
                                    socketIndex = socketIndex - 1;
                                    sockets[socketIndex].destroy();
                                }while (socketIndex > 0)
                                transmit_ws.socketStore[types[typeIndex]] = {};
                            }
                        }
                    }while (typeIndex > 0)
                }
                if (browser.args.mode === "remote" || browser.args.mode === "all") {
                    const ui = uiDefault;
                    vars.agents = {
                        device: {},
                        user: {}
                    };
                    vars.identity = {
                        hashDevice: "",
                        hashUser: "",
                        nameDevice: "",
                        nameUser: "",
                        secretDevice: "",
                        secretUser: ""
                    };
                    vars.settings.message = [];
                    vars.settings.queue = {
                        device: {},
                        user: {}
                    };
                    vars.settings.ui = ui;
                    transmit_ws.socketMap = {};
                    browser.methods.sendAction("close", browser.name);
                    browser.methods.delay({
                        action: start,
                        browser: false,
                        delay: 2000,
                        message: "Delaying to close any open browsers."
                    });
                } else {
                    start();
                }
            };
            remove(vars.path.settings, [
                `${vars.path.settings}temp.txt`
            ], launch);
        },
        "reset-complete": function terminal_test_application_browser_resetComplete(item) {
            browser.remote.count = browser.remote.count + 1;
            if (browser.args.mode === "remote") {
                item.test = {
                    interaction: null,
                    machine: "self",
                    name: "",
                    unit: null
                };
                item.exit = browser.name;
                browser.methods.send(item);
            } else if (browser.remote.count < browser.remote.total + 1) {
                const complete = browser.remote.count === browser.remote.total, color = complete === false ? vars.text.angry : vars.text.bold + vars.text.green, count = color + String(browser.remote.count) + vars.text.none, total = vars.text.bold + vars.text.green + String(browser.remote.total) + vars.text.none, machine = item.exit === "self" ? "local" : item.exit, logs = [
                    `${humanTime(false)}Machine ${count} of ${total} is ready: ${vars.text.bold + vars.text.green + machine + vars.text.none}.`
                ];
                if (browser.remote.count < 2) {
                    logs.splice(0, 0, "");
                }
                if (complete === true) {
                    logs.push("");
                    logs.push("");
                    if (browser.args.mode === "all") {
                        logs.push(`Test list: ${browser.all_order[0]}`);
                    }
                }
                log(logs);
                if (complete === true) {
                    vars.test.browser = {
                        action: "result",
                        exit: "",
                        index: 0,
                        result: [],
                        test: tests[0]
                    };
                    browser.index = -1;
                    browser.all_order.splice(0, 1);
                    browser.methods.send(vars.test.browser);
                }
            }
        },
        result: function terminal_test_application_browser_result(item) {
            if (finished === true) {
                return;
            }
            const index = item.index;
            if (browser.index < index) {
                let a = 0, falseFlag = false;
                const result = item.result, length = result.length, delay = tests[index].unit === null || tests[index].unit.length === 0, quote = `${vars.text.angry}"${vars.text.none}`, completion = function terminal_test_application_browser_result_completion(pass) {
                    const plural = tests.length === 1 ? "" : "s", totalTests = function terminal_test_application_browser_result_completion_total() {
                        // gathers a total count of tests
                        const suites = browser.args.mode === "all" ? Object.keys(browser.suites) : [
                            browser.args.mode
                        ], output = [
                            0,
                            0
                        ];
                        let suiteLen = suites.length, aa = 0;
                        do {
                            suiteLen = suiteLen - 1;
                            aa = browser.suites[suites[suiteLen]].length;
                            output[0] = output[0] + aa;
                            do {
                                aa = aa - 1;
                                output[1] = output[1] + (browser.suites[suites[suiteLen]][aa].unit === null ? 0 : browser.suites[suites[suiteLen]][aa].unit.length);
                                if (browser.suites[suites[suiteLen]][aa].delay !== undefined) {
                                    output[1] = output[1] + 1;
                                }
                            }while (aa > 0)
                        }while (suiteLen > 0)
                        return output;
                    }(), passPlural = totalTests[0] === 1 ? "" : "s", exitMessage = pass === true ? `${humanTime(false) + vars.text.green + vars.text.bold}Passed${vars.text.none} all ${totalTests[1]} evaluations from ${totalTests[0]} test${passPlural}.` : `${humanTime(false) + vars.text.angry}Failed${vars.text.none} on test ${vars.text.angry + String(index + 1) + vars.text.none}: "${vars.text.cyan + tests[index].name + vars.text.none}" out of ${tests.length} total test${plural} and ${totalTests[1]} evaluations.`;
                    browser.exitMessage = exitMessage;
                    browser.methods.exit(null);
                    browser.fail = true;
                }, summary = function terminal_test_application_browser_result_summary(pass) {
                    const resultString = pass === true ? `${vars.text.green}Passed` : `${vars.text.angry}Failed`;
                    return `${humanTime(false) + resultString} ${browser.args.mode} ${index + 1}: ${vars.text.none + tests[index].name}`;
                }, buildNode = function terminal_test_application_Browser_result_buildNode(config, elementOnly) {
                    let b = 0;
                    const node = config.node, property = config.target, nodeLength = node.length, propertyLength = property.length, output = config.target[0] === "window" ? [] : [
                        "document"
                    ];
                    if (nodeLength > 0) {
                        do {
                            output.push(".");
                            output.push(node[b][0]);
                            if (node[b][1] !== null) {
                                output.push("(\"");
                                output.push(node[b][1]);
                                output.push("\")");
                            }
                            if (node[b][2] !== null) {
                                output.push("[");
                                output.push(node[b][2].toString());
                                output.push("]");
                            }
                            b = b + 1;
                        }while (b < nodeLength)
                    }
                    if (config.type === "element" || elementOnly === true) {
                        return output.join("");
                    }
                    if (config.type === "attribute") {
                        output.push(".");
                        output.push("getAttribute(\"");
                        output.push(config.target[0]);
                        output.push("\")");
                    } else if (config.type === "property") {
                        b = 0;
                        do {
                            output.push(".");
                            output.push(config.target[b]);
                            b = b + 1;
                        }while (b < propertyLength)
                    }
                    return output.join("");
                }, testString = function terminal_test_application_browser_result_testString(pass, config) {
                    const valueStore = config.value, valueType = typeof valueStore, value = valueStore === null ? "null" : valueType === "string" ? `"${valueStore}"` : String(valueStore), star = `   ${vars.text.angry}*${vars.text.none} `, resultString = pass === true ? `${vars.text.green}Passed:` : config === tests[index].delay ? `${vars.text.angry}Failed (delay timeout):` : `${vars.text.angry}Failed:`, qualifier = config.qualifier === "begins" ? pass === true ? "begins with" : `${vars.text.angry}does not begin with${vars.text.none}` : config.qualifier === "contains" ? pass === true ? "contains" : `${vars.text.angry}does not contain${vars.text.none}` : config.qualifier === "ends" ? pass === true ? "ends with" : `${vars.text.angry}does not end with${vars.text.none}` : config.qualifier === "greater" ? pass === true ? "is greater than" : `${vars.text.angry}is not greater than${vars.text.none}` : config.qualifier === "is" ? pass === true ? "is" : `${vars.text.angry}is not${vars.text.none}` : config.qualifier === "lesser" ? pass === true ? "is less than" : `${vars.text.angry}is not less than${vars.text.none}` : config.qualifier === "not" ? pass === true ? "is not" : `${vars.text.angry}is${vars.text.none}` : pass === true ? "does not contain" : `${vars.text.angry}contains${vars.text.none}`, nodeString = `${vars.text.none} ${buildNode(config, false)} ${qualifier} ${quote + value.replace(/^"/, "").replace(/"$/, "") + quote}`;
                    return star + resultString + nodeString;
                }, failureMessage = function terminal_test_application_browser_result_failureMessage() {
                    if (result[a][2] === "error") {
                        const error = result[a][1].replace("{\"file\":", `{\n    "${vars.text.cyan}file${vars.text.none}"   :`).replace(",\"column\":", `,\n    "${vars.text.cyan}column${vars.text.none}" :`).replace(",\"line\":", `,\n    "${vars.text.cyan}line${vars.text.none}"   :`).replace(",\"message\":", `,\n    "${vars.text.cyan}message${vars.text.none}":`).replace(",\"stack\":\"", `,\n    "${vars.text.cyan}stack${vars.text.none}"  :\n        `).replace(/\\n/g, "\n        ").replace(/@http/g, "  -  http").replace(/\s*"\s*\}$/, "\n}");
                        failure.push(`     ${vars.text.angry}JavaScript Error${vars.text.none}\n${error}`);
                    } else if (result[a][1].indexOf("Bad test. ") === 0) {
                        const segments = result[a][1].split(": [");
                        failure.push(`     ${segments[0].replace("Bad test.", `${vars.text.angry}Bad test.${vars.text.none}`)}.`);
                        if (segments.length > 1) {
                            failure.push(`     Provided: ${vars.text.angry}[${segments[1] + vars.text.none}`);
                        }
                        failure.push(`     ${vars.text.cyan + result[a][2] + vars.text.none}`);
                    } else if (delay === false && result[a][2] === buildNode(tests[index].unit[a], true) || delay === true && result[a][2] === buildNode(tests[index].delay, true)) {
                        failure.push(`     ${vars.text.green}Actual value:${vars.text.none}\n        ${vars.text.angry + result[a][1].replace(/^"/, "").replace(/"$/, "").replace(/\\"/g, "\"") + vars.text.none}`);
                    } else if (delay === false && tests[index].unit[a].value === null || delay === true && tests[index].delay.value === null) {
                        failure.push(`     DOM node is not null: ${vars.text.cyan + result[a][2] + vars.text.none}`);
                    } else if (delay === false && tests[index].unit[a].value === undefined || delay === true && tests[index].delay.value === undefined) {
                        failure.push(`     DOM node is not undefined: ${vars.text.cyan + result[a][2] + vars.text.none}`);
                    } else {
                        failure.push(`     DOM node is ${result[a][1]}: ${vars.text.cyan + result[a][2] + vars.text.none}`);
                    }
                }, failure = [];
                browser.index = index;
                if (result[0][0] === false && result[0][1] === "delay timeout") {
                    failure.push(testString(false, tests[index].delay));
                    if (tests[index].delay.type === "element") {
                        const qualifier = tests[index].delay.qualifier === "not" ? " not" : "";
                        failure.push(`     DOM node is${qualifier} ${tests[index].delay.value}: ${vars.text.cyan + result[1][1] + vars.text.none}`);
                    } else {
                        failure.push(`     ${vars.text.green}Actual value:${vars.text.none}\n${quote + vars.text.cyan + result[1][1].replace(/^"/, "").replace(/"$/, "").replace(/\\"/g, "\"") + vars.text.none + quote}`);
                    }
                    falseFlag = true;
                } else if (result[0][0] === false && result[0][1].indexOf("event error ") === 0) {
                    failure.push(`${vars.text.angry}Failed: event node is ${result[0][1].replace("event error ", "")}`);
                    failure.push(`     Specified event node is: ${vars.text.cyan + result[0][2] + vars.text.none}`);
                    falseFlag = true;
                } else if (delay === true) {
                    failure.push(testString(result[a][0], tests[index].delay));
                    if (result[a][0] === false) {
                        failureMessage();
                        falseFlag = true;
                    }
                } else {
                    do {
                        failure.push(testString(result[a][0], tests[index].unit[a]));
                        if (result[a][0] === false) {
                            failureMessage();
                            falseFlag = true;
                        }
                        a = a + 1;
                    }while (a < length)
                }
                if (falseFlag === true) {
                    failure.splice(0, 0, summary(false));
                    log(failure);
                    completion(false);
                    return;
                }
                log([
                    summary(true)
                ]);
                if (index + 1 < tests.length) {
                    browser.methods.iterate(index + 1);
                } else if (browser.args.mode === "all" && browser.all_order.length > 0) {
                    tests = browser.suites[browser.all_order[0]];
                    browser.remote.count = 0;
                    vars.test.browser = {
                        action: "reset",
                        exit: browser.name,
                        index: 0,
                        result: [],
                        test: null
                    };
                    browser.methods.sendAction("reset", "VM1");
                    browser.methods.sendAction("reset", "VM2");
                    browser.methods.sendAction("reset", "VM3");
                    browser.methods.sendAction("reset", "VM4");
                    browser.methods.reset();
                } else {
                    completion(true);
                }
            }
        },
        route: function terminal_test_application_browser_route(socketData) {
            const data = socketData.data;
            if (vars.settings.verbose === true) {
                log([
                    `On terminal receiving test index ${data.index}`
                ]);
            }
            if (data.action !== "nothing") {
                if (browser.methods[data.action] === undefined) {
                    error([
                        `Unsupported action in browser test automation: ${data.action}`
                    ], null);
                } else if (browser.args.mode === "remote" && data.action === "result") {
                    if (data.result.length > 0) {
                        data.test = {
                            interaction: null,
                            machine: "self",
                            name: `Report result to test ${data.index}.`,
                            unit: null
                        };
                    }
                    browser.methods.send(data);
                } else {
                    browser.methods[data.action](data);
                }
            } else if (data.exit !== null) {
                log([
                    data.exit
                ]);
            }
        // close
        // * tells the test browser to close
        // * from browser.exit on primary computer sent to remotes
        // -
        // exit
        // * data will only be routed to the exit method from the network if the agent is in mode "remote" and the test is executing with option "no_close"
        // * from browser.exit of agent in modes "device" or "user"
        // -
        // request
        // * sends a test from control computer to a specified remote
        // * from browser.iterate
        // * sends an unused HTTP response
        // -
        // reset-browser
        // * indicates, on a remote, the environment is reset and the browser is ready on the local computer
        // * from the browser
        // * send an HTTP request to data.transfer identifiers
        // -
        // reset-complete
        // * on primary computer confirms all the remote computers are reset and their web browsers are ready to execute
        // * from resetResponse of remote sent to primary computer
        // * executes local reset-request thus beginning test iteration
        // -
        // reset-request
        // * resets the environment on remotes
        // * from primary computer in browser.execute to the remotes
        // * generates an HTTP response
        // -
        // respond
        // * converts an action 'request' into a test for the browser of a specified remote
        // * from browser.request of remote
        // * sends an HTTP request to browser.ip with the test result
        // -
        // result
        // * response to test completion
        // * from browsers whether local or remote
        // * calls browser.iterate
        },
        send: function terminal_test_application_browser_send(testItem) {
            if (testItem.test.machine === browser.name) {
                // self
                const browsers = transmit_ws.getSocketList("browser"), len = browsers.length;
                if (len > 0) {
                    testItem.test = filePathDecode(testItem.test, "");
                    network.send({
                        data: testItem,
                        service: "test-browser"
                    }, {
                        device: browsers[len - 1].hash,
                        user: "browser"
                    });
                }
            } else {
                // remote
                transmit_ws.queue({
                    data: testItem,
                    service: "test-browser"
                }, transmit_ws.socketStore.testRemote[testItem.test.machine], 1);
            }
            // Once a reset test is sent it is necessary to eliminate the event portion of the test.
            // This ensures the test available to the page upon page refresh for test unit evaluation without further executing the refresh event.
            if (testItem.test !== null && testItem.test.interaction !== null && testItem.test.interaction.length > 0 && testItem.test.interaction[0].event === "refresh") {
                vars.test.browser.test.interaction = null;
            }
        },
        sendAction: function terminal_test_application_browser_sendAction(action, machine, exitMessage) {
            const service = {
                action: action,
                exit: typeof exitMessage === "string" ? exitMessage : "",
                index: -1,
                result: [],
                test: {
                    interaction: null,
                    machine: machine,
                    name: "",
                    unit: null
                }
            };
            browser.methods.send(service);
        }
    },
    name: function terminal_test_application_browser_name() {
        const keys = Object.keys(machines);
        let ipIndex = 0, machineIndex = keys.length;
        do {
            machineIndex = machineIndex - 1;
            ipIndex = vars.network.addresses.IPv4.length;
            do {
                ipIndex = ipIndex - 1;
                if (machines[keys[machineIndex]].ip === vars.network.addresses.IPv4[ipIndex]) {
                    return keys[machineIndex];
                }
            }while (ipIndex > 0)
        }while (machineIndex > 0)
    }(),
    port: 0,
    remote: {
        count: 0,
        total: 1
    },
    suites: {
        delete: test_delete,
        device: test_device,
        self: test_self,
        user: test_user
    }
};
export default browser;
