<commit_before>var util = require('util');
var vm = require('vm');
require('./cycle');
//var ut = require('./env/utils.js');

process.on('uncaughtException', (err) => {
    util.log('!!! Uncaught Exception !!!');
    util.log(util.inspect(err));
});

util.log("mailware-jail, a malware sandbox ver. 0.13");
util.log("------------------------");

// commandline overwrites config.json values
var argv = require('minimist')(process.argv.slice(2), { "boolean" : true });
var _exit = false;

if (argv.h || argv.help) {
    util.log("Usage: node jailme.js  [[-e file1] [-e file2] .. ] [-c ./config.json] \\");
    util.log("\t\t[-o ofile] [-b id] \\");
    util.log("\t\t[-s odir] [--down] [malware1 [malware2] .. ]");
    util.log("\t-c config .. use alternative config file, preceed with ./");
    util.log("\t-e ifile ... js that simulates specific environment");
    util.log("\t-o ofile ... name of the file where sandbox shall be dumped at the end");
    util.log("\t-s odir  ... output directory for generated files (malware payload)");
    util.log("\t-b id    ... browser type, use -b list for possible values");
    util.log("\t-t msecs ... number of miliseconds before terminating execution, default 1 minute");
    util.log("\t--trace  ... print stack trace with every log line");
    util.log("\t--down   ... allow downloading malware payloads from remote servers");
    util.log("\t--h404   ... on download return always HTTP/404");
    util.log("\tmalware  ... js with the malware code");
    util.log("If no arguments are specified the default values are taken from config.json");
    _exit = true;
}

var config_name = "./config.json";

if (typeof argv.c === 'string') {
    config_name = argv.c
}

var config = require(config_name);

if (typeof argv.b === 'string') {
    if (argv.b === 'list') {
        var a = require('./env/agents.js')._agents;
        util.log("Possible -b values:", Object.keys(a));
        _exit = true;
    } else {
        config.browser_type = argv.b
    }
}
if (_exit)
    return;

util.log("Arguments: " + process.argv.slice(2).join(" "));

function parse_argv_array(a, b) {
    if (a) {
        if (Array.isArray(a) && a.length > 0)
            return a;
        else if (typeof a === 'string')
            return [a];
        else if (typeof a === 'boolean')
            return [];
    }
    return b;
}
config.sandbox_sequence = parse_argv_array(argv.e, config.sandbox_sequence);
util.log("Sandbox environment sequence: " + config.sandbox_sequence);

config.malware_files = parse_argv_array(argv._, config.malware_files);
util.log("Malware files: " + config.malware_files);

if (typeof argv.t === 'number')
    config.timeout = argv.t;
if (typeof config.timeout !== 'number')
    config.timeout = 1000*60;
util.log("Execution timeout set to: " + config.timeout / 1000 + " seconds");
if (typeof argv.o === 'string')
    config.context_dump_after = argv.o
util.log("Output file for sandbox dump: " + config.context_dump_after);
if (typeof argv.s === 'string')
    config.save_files = argv.s
util.log("Output directory for generated files: " + config.save_files);
//if (process.platform === "win32") {
//    var rl = require("readline").createInterface({
//        input: process.stdin,
//        output: process.stdout
//    });
//
//    rl.on("SIGINT", function() {
//        console.log("here");
//        process.emit("SIGINT");
//    });
//}
//
//process.on("SIGINT", function() {
//    //graceful shutdown
//    console.log("here");
//    process.exit();
//});

var _proxy = function(o, verbose = false, what = undefined) {
    var util_log = mylog;
    ret = new Proxy(o, {
        get: function(target, name, receiver) {
            if (verbose) {
                if (typeof name === 'symbol')
                    vname = name.toString();
                else
                    vname = name;
                if (what) {
                    var msg = what + "[" + vname + "]";
                } else {
                    var msg = "Proxy.get: " + _truncateOutput(target.toString(), 50) + "[" + vname + "]";
                }
            }
            if (name in target) {
                if (msg)
                    util_log(msg + " => " + Reflect.get(target, name));
                return Reflect.get(target, name, receiver);
            }
            if (typeof name === 'string') {
                lcname = name.toLowerCase();
                if (lcname in target) {
                    if (msg)
                        util_log(msg + " => " + Reflect.get(target, name));
                    return Reflect.get(target, lcname, receiver);
                }
            }
            ret = Reflect.get(target, name, receiver);
            if (typeof name !== 'symbol') {
                vname = name.toString();
                if (typeof ret === 'undefined') {
                    util_log(">>> " + target._name + "[" + vname + "] not defined");
                    //throw new TypeError(">>> FIXME: " + target + "[" + vname + "] not defined");
                } else {
                    util_log(">>> FIXME: " + target._name + "[" + vname + "] reflected to " + typeof ret);
                }
            }

            return ret;
        },
        set: function(target, name, value) {
            if (verbose) {
                if (typeof name === 'symbol')
                    vname = name.toString();
                else
                    vname = name;
                util_log("Proxy.set: " + _truncateOutput(target.toString(), 50) + "[" + vname + "]");
            }
            if (name in target)
                return Reflect.set(target, name, value);
            if (typeof name === 'string') {
                lcname = name.toLowerCase();
                if (lcname in target)
                    return Reflect.set(target, lcname, value);
            }
            return Reflect.set(target, name, value);
        },
        construct: function(target, args, newTarget) {
            if (verbose) {
                util_log("Proxy.construct: " + target + "(" + _truncateOutput(args.join(", "), 50) + ")");
            }
            return _proxy(Reflect.construct(target, args, newTarget));
        },
        apply: function(target, that, args) {
            if (verbose) {
                util_log("Proxy.apply: " + target + "(" + _truncateOutput(args.join(", "), 50) + ")");
            }
            return Reflect.apply(target, that, args);
        }
    })
    return ret;
}

var sandbox = {};
// too ambitious
// var sandbox = _proxy({});
if (argv.down)
    sandbox._download = "Yes";
else
    sandbox._download = "No";
if (argv.h404)
    sandbox._download = "Return HTTP/404";
util.log("Download from remote server: " + sandbox._download);

var _show_stack;
if (argv.trace)
    _show_stack = true;
else
    _show_stack = false;

var _dont_log = false;

function mylog() {
    //if (arguments[0].startsWith("function"))
    //    debugger;
    if (_dont_log)
        return;
    util.log.apply(util, arguments);
    if (_show_stack) {
            var err = new Error();
        var st = err.stack;
        var st1 = st.split("\n");
        var st2 = st1.slice(2, st1.length - 5).join(", ");
        st2 = st2.replace(/\s+/g, ' ');
        util.log(" >" + st2)
    }
}

sandbox.toString = () => { return "sandbox"; }
sandbox._object_id = 1;
sandbox.util_log = mylog
sandbox._iconv = require("iconv-lite");

//require('http');
// FIXME: proxy?
//sandbox.Object = Object
//sandbox.Error = Error
//sandbox.Proxy = Proxy
//sandbox.Reflect = Reflect
//sandbox.unescape = unescape
//sandbox.Date = Date
//sandbox.String = String
sandbox._options = (typeof config.options === 'undefined') ? {} : config.options;
sandbox._browser_type = config.browser_type;
sandbox._date = Date;
sandbox.require = require;
sandbox._setTimeout = setTimeout;
sandbox._clearTimeout = clearTimeout;
sandbox._setInterval = setInterval;
sandbox._clearInterval = clearInterval;
sandbox._decodeHTML = require("entities").decodeHTML;
sandbox._inspect = util.inspect;
sandbox._proxy_verbose = false;
sandbox._sync_request = require("sync-request");
sandbox._data = {}
sandbox._sc = ">>> Silencing catch ";
sandbox._browser_api = {
    'Array': Array,
    'Boolean': Boolean,
    'Date': Date,
    'Error': Error,
    'Math': Math,
    'Number': Number,
    'Object': Object,
    'RegExp': RegExp,
    'String': String,
    'Node': {},
    'Element': undefined,
    'HTMLElement': undefined,
    'JSON': JSON,
    'Function': Function
}

fs = require('fs');
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

var ctx = vm.createContext(sandbox);
var i, fc;

function run_in_ctx(files, no_log = false, catch_catch = false) {
    try {
        for (i = 0; i < files.length; i++) {
            util.log(" => Executing: " + files[i] + ((no_log) ? " quitely" : " verbosely") + ((catch_catch) ? ", reporting silent catches" : ""));
            fc = fs.readFileSync(files[i], 'utf8');
            _dont_log = no_log;
            sandbox._script_name = files[i];
            // log every exception caught by the script itself
            if (catch_catch) {
                fc = fc.replace(/\bcatch\b\s*\((.*?)\)\s*{/g, 'catch($1) { util_log(_sc + _inspect($1));');
                let fname = files[i].replace(/\//g,"_");
                let fname2 = config.save_files + fname;
                util.log("Saving: " + fname2);
                fs.writeFileSync(fname2, fc);
                let r = /\\u([\d\w]{4})|\\x([\d\w]{2})/gi;
                let x = fc.replace(r, function (match, p1, p2) {
                     let grp = (p1) ? p1 : p2;
                     let ret = String.fromCharCode(parseInt(grp, 16));
                     switch(ret) {
                        case '\\':
                            ret = '\\\\';
                            break;
                        case '\'':
                            ret = '\'\'';
                            break;
                        case '\"':
                            ret = '\"\"';
                            break;
                     }
                     return ret;
                });
                fname2 = config.save_files + "tr_"+fname;
                util.log("Saving: " + fname2);
                fs.writeFileSync(fname2, x);
            }
            if ("WScript" in sandbox) {
                sandbox.WScript.scriptfullname = files[i];
                sandbox.WScript.arguments = [files[i], "xyz"];
            }
            vm.runInContext(fc, ctx, {
                filename: files[i],
                timeout: config.timeout
            });
            _dont_log = false;
        }
    } catch (err) {
        _dont_log = false;
        util.log("Exception occured: " + typeof err + " " + util.inspect(err));
        //if (typeof err.stack !== 'undefined')
        //    util.log("Stack: " + err.stack);
        return false;
    } finally {
        _dont_log = false;
    }
    return true;
}

util.log("==> Preparing Sandbox environment.");
if (!run_in_ctx(config.sandbox_sequence, true)) return;

//process.on('SIGINT', function () {
//    util.log('Ctrl-C...');
//    process.exit(2);
//});

// clean up
sandbox.require = undefined;

// Run the malware
util.log("==> Executing malware file(s). =========================================");
process.exitCode = run_in_ctx(config.malware_files, false, true) ? 0 : 1;
sandbox._config = config;
sandbox._arguments = process.argv.slice(2).join(" ");
exiting = false;

process.on('beforeExit', (code) => {
    if (exiting)
        return;
    exiting = true;
    util.log("==> Cleaning up sandbox.");
    sandbox._decodeHTML = undefined;
    sandbox._my_path = undefined;
    sandbox._browser_api = undefined;
    sandbox.util_log.toString = () => {
        return "util_log"
    }
    sandbox._iconv = undefined;
    sandbox._date = undefined;
    sandbox.require = undefined;
    sandbox._setTimeout = undefined;
    sandbox._clearTimeout = undefined;
    sandbox._setInterval = undefined;
    sandbox._clearInterval = undefined;
    sandbox._decodeHTML = undefined;
    sandbox._inspect = undefined;
    sandbox._pw32 = undefined;
    sandbox.Base64 = undefined;
    sandbox.console = undefined;

    util.log("==> Script execution finished, dumping sandbox environment to a file.");

    var out; // = util.inspect(sandbox);
    out = JSON.stringify(JSON.decycle(sandbox), function(key, value) {
        // convert functions to string
        if (typeof value === 'function') {
            try {
                return value.toString();
            } catch (err) {
                util.log("Exception occured: " + typeof err + " ==> " + util.inspect(err));
                util.log("Object details: " + typeof value + " ==> " + util.inspect(value));
                return "!!! Exception occured !!!";
            }
        }
        return value;
    }, 4);

    fs.writeFileSync(config.context_dump_after, out);
    util.log("The sandbox context has been  saved to: " + config.context_dump_after);

    p = sandbox._data["eval_calls"];
    var ec = 1
    for (let key in p) {
        if (p.hasOwnProperty(key)) {
            fname = config.save_files + "eval" + ec++ + ".js";
            util.log("Saving: " + fname);
            fs.writeFileSync(fname, p[key]["report_catch"]);
        }
    }

    var name, p, fname;
    p = sandbox._wscript_saved_files;
    for (let key in p) {
        if (p.hasOwnProperty(key)) {
            name = key.replace(/\/|\\|%|:/gi, "_");
            fname = config.save_files + name;
            util.log("Saving: " + fname);
            fs.writeFile(fname, p[key]);
        }
    }
})
<commit_msg>New version - 0.14<commit_after>var util = require('util');
var vm = require('vm');
require('./cycle');
//var ut = require('./env/utils.js');

process.on('uncaughtException', (err) => {
    util.log('!!! Uncaught Exception !!!');
    util.log(util.inspect(err));
});

util.log("mailware-jail, a malware sandbox ver. 0.14");
util.log("------------------------");

// commandline overwrites config.json values
var argv = require('minimist')(process.argv.slice(2), { "boolean" : true });
var _exit = false;

if (argv.h || argv.help) {
    util.log("Usage: node jailme.js  [[-e file1] [-e file2] .. ] [-c ./config.json] \\");
    util.log("\t\t[-o ofile] [-b id] \\");
    util.log("\t\t[-s odir] [--down] [malware1 [malware2] .. ]");
    util.log("\t-c config .. use alternative config file, preceed with ./");
    util.log("\t-e ifile ... js that simulates specific environment");
    util.log("\t-o ofile ... name of the file where sandbox shall be dumped at the end");
    util.log("\t-s odir  ... output directory for generated files (malware payload)");
    util.log("\t-b id    ... browser type, use -b list for possible values");
    util.log("\t-t msecs ... number of miliseconds before terminating execution, default 1 minute");
    util.log("\t--trace  ... print stack trace with every log line");
    util.log("\t--down   ... allow downloading malware payloads from remote servers");
    util.log("\t--h404   ... on download return always HTTP/404");
    util.log("\tmalware  ... js with the malware code");
    util.log("If no arguments are specified the default values are taken from config.json");
    _exit = true;
}

var config_name = "./config.json";

if (typeof argv.c === 'string') {
    config_name = argv.c
}

var config = require(config_name);

if (typeof argv.b === 'string') {
    if (argv.b === 'list') {
        var a = require('./env/agents.js')._agents;
        util.log("Possible -b values:", Object.keys(a));
        _exit = true;
    } else {
        config.browser_type = argv.b
    }
}
if (_exit)
    return;

util.log("Arguments: " + process.argv.slice(2).join(" "));

function parse_argv_array(a, b) {
    if (a) {
        if (Array.isArray(a) && a.length > 0)
            return a;
        else if (typeof a === 'string')
            return [a];
        else if (typeof a === 'boolean')
            return [];
    }
    return b;
}
config.sandbox_sequence = parse_argv_array(argv.e, config.sandbox_sequence);
util.log("Sandbox environment sequence: " + config.sandbox_sequence);

config.malware_files = parse_argv_array(argv._, config.malware_files);
util.log("Malware files: " + config.malware_files);

if (typeof argv.t === 'number')
    config.timeout = argv.t;
if (typeof config.timeout !== 'number')
    config.timeout = 1000*60;
util.log("Execution timeout set to: " + config.timeout / 1000 + " seconds");
if (typeof argv.o === 'string')
    config.context_dump_after = argv.o
util.log("Output file for sandbox dump: " + config.context_dump_after);
if (typeof argv.s === 'string')
    config.save_files = argv.s
util.log("Output directory for generated files: " + config.save_files);
//if (process.platform === "win32") {
//    var rl = require("readline").createInterface({
//        input: process.stdin,
//        output: process.stdout
//    });
//
//    rl.on("SIGINT", function() {
//        console.log("here");
//        process.emit("SIGINT");
//    });
//}
//
//process.on("SIGINT", function() {
//    //graceful shutdown
//    console.log("here");
//    process.exit();
//});

var _proxy = function(o, verbose = false, what = undefined) {
    var util_log = mylog;
    ret = new Proxy(o, {
        get: function(target, name, receiver) {
            if (verbose) {
                if (typeof name === 'symbol')
                    vname = name.toString();
                else
                    vname = name;
                if (what) {
                    var msg = what + "[" + vname + "]";
                } else {
                    var msg = "Proxy.get: " + _truncateOutput(target.toString(), 50) + "[" + vname + "]";
                }
            }
            if (name in target) {
                if (msg)
                    util_log(msg + " => " + Reflect.get(target, name));
                return Reflect.get(target, name, receiver);
            }
            if (typeof name === 'string') {
                lcname = name.toLowerCase();
                if (lcname in target) {
                    if (msg)
                        util_log(msg + " => " + Reflect.get(target, name));
                    return Reflect.get(target, lcname, receiver);
                }
            }
            ret = Reflect.get(target, name, receiver);
            if (typeof name !== 'symbol') {
                vname = name.toString();
                if (typeof ret === 'undefined') {
                    util_log(">>> " + target._name + "[" + vname + "] not defined");
                    //throw new TypeError(">>> FIXME: " + target + "[" + vname + "] not defined");
                } else {
                    util_log(">>> FIXME: " + target._name + "[" + vname + "] reflected to " + typeof ret);
                }
            }

            return ret;
        },
        set: function(target, name, value) {
            if (verbose) {
                if (typeof name === 'symbol')
                    vname = name.toString();
                else
                    vname = name;
                util_log("Proxy.set: " + _truncateOutput(target.toString(), 50) + "[" + vname + "]");
            }
            if (name in target)
                return Reflect.set(target, name, value);
            if (typeof name === 'string') {
                lcname = name.toLowerCase();
                if (lcname in target)
                    return Reflect.set(target, lcname, value);
            }
            return Reflect.set(target, name, value);
        },
        construct: function(target, args, newTarget) {
            if (verbose) {
                util_log("Proxy.construct: " + target + "(" + _truncateOutput(args.join(", "), 50) + ")");
            }
            return _proxy(Reflect.construct(target, args, newTarget));
        },
        apply: function(target, that, args) {
            if (verbose) {
                util_log("Proxy.apply: " + target + "(" + _truncateOutput(args.join(", "), 50) + ")");
            }
            return Reflect.apply(target, that, args);
        }
    })
    return ret;
}

var sandbox = {};
// too ambitious
// var sandbox = _proxy({});
if (argv.down)
    sandbox._download = "Yes";
else
    sandbox._download = "No";
if (argv.h404)
    sandbox._download = "Return HTTP/404";
util.log("Download from remote server: " + sandbox._download);

var _show_stack;
if (argv.trace)
    _show_stack = true;
else
    _show_stack = false;

var _dont_log = false;

function mylog() {
    //if (arguments[0].startsWith("function"))
    //    debugger;
    if (_dont_log)
        return;
    util.log.apply(util, arguments);
    if (_show_stack) {
            var err = new Error();
        var st = err.stack;
        var st1 = st.split("\n");
        var st2 = st1.slice(2, st1.length - 5).join(", ");
        st2 = st2.replace(/\s+/g, ' ');
        util.log(" >" + st2)
    }
}

sandbox.toString = () => { return "sandbox"; }
sandbox._object_id = 1;
sandbox.util_log = mylog
sandbox._iconv = require("iconv-lite");

//require('http');
// FIXME: proxy?
//sandbox.Object = Object
//sandbox.Error = Error
//sandbox.Proxy = Proxy
//sandbox.Reflect = Reflect
//sandbox.unescape = unescape
//sandbox.Date = Date
//sandbox.String = String
sandbox._options = (typeof config.options === 'undefined') ? {} : config.options;
sandbox._browser_type = config.browser_type;
sandbox._date = Date;
sandbox.require = require;
sandbox._setTimeout = setTimeout;
sandbox._clearTimeout = clearTimeout;
sandbox._setInterval = setInterval;
sandbox._clearInterval = clearInterval;
sandbox._decodeHTML = require("entities").decodeHTML;
sandbox._inspect = util.inspect;
sandbox._proxy_verbose = false;
sandbox._sync_request = require("sync-request");
sandbox._data = {}
sandbox._sc = ">>> Silencing catch ";
sandbox._browser_api = {
    'Array': Array,
    'Boolean': Boolean,
    'Date': Date,
    'Error': Error,
    'Math': Math,
    'Number': Number,
    'Object': Object,
    'RegExp': RegExp,
    'String': String,
    'Node': {},
    'Element': undefined,
    'HTMLElement': undefined,
    'JSON': JSON,
    'Function': Function
}

fs = require('fs');
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

var ctx = vm.createContext(sandbox);
var i, fc;

function run_in_ctx(files, no_log = false, catch_catch = false) {
    try {
        for (i = 0; i < files.length; i++) {
            util.log(" => Executing: " + files[i] + ((no_log) ? " quitely" : " verbosely") + ((catch_catch) ? ", reporting silent catches" : ""));
            fc = fs.readFileSync(files[i], 'utf8');
            _dont_log = no_log;
            sandbox._script_name = files[i];
            // log every exception caught by the script itself
            if (catch_catch) {
                fc = fc.replace(/\bcatch\b\s*\((.*?)\)\s*{/g, 'catch($1) { util_log(_sc + _inspect($1));');
                let fname = files[i].replace(/\//g,"_");
                let fname2 = config.save_files + fname;
                util.log("Saving: " + fname2);
                fs.writeFileSync(fname2, fc);
                let r = /\\u([\d\w]{4})|\\x([\d\w]{2})/gi;
                let x = fc.replace(r, function (match, p1, p2) {
                     let grp = (p1) ? p1 : p2;
                     let ret = String.fromCharCode(parseInt(grp, 16));
                     switch(ret) {
                        case '\\':
                            ret = '\\\\';
                            break;
                        case '\'':
                            ret = '\'\'';
                            break;
                        case '\"':
                            ret = '\"\"';
                            break;
                     }
                     return ret;
                });
                fname2 = config.save_files + "tr_"+fname;
                util.log("Saving: " + fname2);
                fs.writeFileSync(fname2, x);
            }
            if ("WScript" in sandbox) {
                sandbox.WScript.scriptfullname = files[i];
                sandbox.WScript.arguments = [files[i], "xyz"];
            }
            vm.runInContext(fc, ctx, {
                filename: files[i],
                timeout: config.timeout
            });
            _dont_log = false;
        }
    } catch (err) {
        _dont_log = false;
        util.log("Exception occured: " + typeof err + " " + util.inspect(err));
        //if (typeof err.stack !== 'undefined')
        //    util.log("Stack: " + err.stack);
        return false;
    } finally {
        _dont_log = false;
    }
    return true;
}

util.log("==> Preparing Sandbox environment.");
if (!run_in_ctx(config.sandbox_sequence, true)) return;

//process.on('SIGINT', function () {
//    util.log('Ctrl-C...');
//    process.exit(2);
//});

// clean up
sandbox.require = undefined;

// Run the malware
util.log("==> Executing malware file(s). =========================================");
process.exitCode = run_in_ctx(config.malware_files, false, true) ? 0 : 1;
sandbox._config = config;
sandbox._arguments = process.argv.slice(2).join(" ");
exiting = false;

process.on('beforeExit', (code) => {
    if (exiting)
        return;
    exiting = true;
    util.log("==> Cleaning up sandbox.");
    sandbox._decodeHTML = undefined;
    sandbox._my_path = undefined;
    sandbox._browser_api = undefined;
    sandbox.util_log.toString = () => {
        return "util_log"
    }
    sandbox._iconv = undefined;
    sandbox._date = undefined;
    sandbox.require = undefined;
    sandbox._setTimeout = undefined;
    sandbox._clearTimeout = undefined;
    sandbox._setInterval = undefined;
    sandbox._clearInterval = undefined;
    sandbox._decodeHTML = undefined;
    sandbox._inspect = undefined;
    sandbox._pw32 = undefined;
    sandbox.Base64 = undefined;
    sandbox.console = undefined;

    util.log("==> Script execution finished, dumping sandbox environment to a file.");

    var out; // = util.inspect(sandbox);
    out = JSON.stringify(JSON.decycle(sandbox), function(key, value) {
        // convert functions to string
        if (typeof value === 'function') {
            try {
                return value.toString();
            } catch (err) {
                util.log("Exception occured: " + typeof err + " ==> " + util.inspect(err));
                util.log("Object details: " + typeof value + " ==> " + util.inspect(value));
                return "!!! Exception occured !!!";
            }
        }
        return value;
    }, 4);

    fs.writeFileSync(config.context_dump_after, out);
    util.log("The sandbox context has been  saved to: " + config.context_dump_after);

    p = sandbox._data["eval_calls"];
    var ec = 1
    for (let key in p) {
        if (p.hasOwnProperty(key)) {
            fname = config.save_files + "eval" + ec++ + ".js";
            util.log("Saving: " + fname);
            fs.writeFileSync(fname, p[key]["report_catch"]);
        }
    }

    var name, p, fname;
    p = sandbox._wscript_saved_files;
    for (let key in p) {
        if (p.hasOwnProperty(key)) {
            name = key.replace(/\/|\\|%|:/gi, "_");
            fname = config.save_files + name;
            util.log("Saving: " + fname);
            fs.writeFile(fname, p[key]);
        }
    }
})
<|endoftext|>


<commit_before>import { app, BrowserWindow, Menu, crashReporter, shell } from 'electron';
import restify from 'restify';
import parse from './parse';
import SequelizeManager from './sequelizeManager';

const sequelizeManager = new SequelizeManager();
const ipcMain = require('electron').ipcMain;

let menu;
let template;
let mainWindow = null;

crashReporter.start();

if (process.env.NODE_ENV === 'development') {
  require('electron-debug')();
}

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

app.on('ready', () => {
  mainWindow = new BrowserWindow({
    show: false,
    width: 1024,
    height: 728
  });

  mainWindow.loadURL(`file://${__dirname}/app/app.html`);

  mainWindow.webContents.on('did-finish-load', () => {
    mainWindow.show();
    mainWindow.focus();
  });

  mainWindow.on('closed', () => {
    mainWindow = null;
  });

  const server = restify.createServer();
  server.use(restify.queryParser());
  server.use(restify.CORS({
    origins: ['*'],   // defaults to ['*']
    credentials: false,                 // defaults to false
    headers: ['Access-Control-Allow-Origin']  // sets expose-headers
  }));

  server.listen(5000, () => {
  });

  mainWindow.webContents.on('did-finish-load', () => {
    ipcMain.on('connect', (event, payload) => {
      const usr = 'root';
      const psw = 'MyPassword1';
      const db = 'fake_plotly';
      const prt = 3306;
      const engine = 'mysql';

      sequelizeManager.login(usr, psw, db, prt, engine).then(msg => {
        event.sender.send('channel', { log: `logged in, ${msg}` });
      }).catch(error => {
         event.sender.send('channel', { error });
      });
    });

    ipcMain.on('receive', (event, payload) => {
      const statement = payload.statement;
      sequelizeManager.connection.query(statement).spread((rows, metadata) => {
        event.sender.send('channel', {rows, metadata, error: ''});
      }).catch(error => {
        event.sender.send('channel', { error });
      });
    });

    server.get('/query', (req, res) => {
      const statement = req.query.statement;
      mainWindow.webContents.send('channel', { log: statement });
      sequelizeManager.connection.query(statement).spread((rows, metadata) => {
        // Send back to app
        mainWindow.webContents.send('channel', { rows, metadata, error: '' });
        // Send back to plotly 2.0
        res.send(parse(rows));
      }).catch(error => {
        event.sender.send('channel', { error });
        res.send({ error });
      });
    });
  });

  if (process.env.NODE_ENV === 'development') {
    mainWindow.openDevTools();
  }

  if (process.platform === 'darwin') {
    template = [{
      label: 'Electron',
      submenu: [{
        label: 'About ElectronReact',
        selector: 'orderFrontStandardAboutPanel:'
      }, {
        type: 'separator'
      }, {
        label: 'Services',
        submenu: []
      }, {
        type: 'separator'
      }, {
        label: 'Hide ElectronReact',
        accelerator: 'Command+H',
        selector: 'hide:'
      }, {
        label: 'Hide Others',
        accelerator: 'Command+Shift+H',
        selector: 'hideOtherApplications:'
      }, {
        label: 'Show All',
        selector: 'unhideAllApplications:'
      }, {
        type: 'separator'
      }, {
        label: 'Quit',
        accelerator: 'Command+Q',
        click() {
          app.quit();
        }
      }]
    }, {
      label: 'Edit',
      submenu: [{
        label: 'Undo',
        accelerator: 'Command+Z',
        selector: 'undo:'
      }, {
        label: 'Redo',
        accelerator: 'Shift+Command+Z',
        selector: 'redo:'
      }, {
        type: 'separator'
      }, {
        label: 'Cut',
        accelerator: 'Command+X',
        selector: 'cut:'
      }, {
        label: 'Copy',
        accelerator: 'Command+C',
        selector: 'copy:'
      }, {
        label: 'Paste',
        accelerator: 'Command+V',
        selector: 'paste:'
      }, {
        label: 'Select All',
        accelerator: 'Command+A',
        selector: 'selectAll:'
      }]
    }, {
      label: 'View',
      submenu: (process.env.NODE_ENV === 'development') ? [{
        label: 'Reload',
        accelerator: 'Command+R',
        click() {
          mainWindow.restart();
        }
      }, {
        label: 'Toggle Full Screen',
        accelerator: 'Ctrl+Command+F',
        click() {
          mainWindow.setFullScreen(!mainWindow.isFullScreen());
        }
      }, {
        label: 'Toggle Developer Tools',
        accelerator: 'Alt+Command+I',
        click() {
          mainWindow.toggleDevTools();
        }
      }] : [{
        label: 'Toggle Full Screen',
        accelerator: 'Ctrl+Command+F',
        click() {
          mainWindow.setFullScreen(!mainWindow.isFullScreen());
        }
      }]
    }, {
      label: 'Window',
      submenu: [{
        label: 'Minimize',
        accelerator: 'Command+M',
        selector: 'performMiniaturize:'
      }, {
        label: 'Close',
        accelerator: 'Command+W',
        selector: 'performClose:'
      }, {
        type: 'separator'
      }, {
        label: 'Bring All to Front',
        selector: 'arrangeInFront:'
      }]
    }, {
      label: 'Help',
      submenu: [{
        label: 'Learn More',
        click() {
          shell.openExternal('http://electron.atom.io');
        }
      }, {
        label: 'Documentation',
        click() {
          shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme');
        }
      }, {
        label: 'Community Discussions',
        click() {
          shell.openExternal('https://discuss.atom.io/c/electron');
        }
      }, {
        label: 'Search Issues',
        click() {
          shell.openExternal('https://github.com/atom/electron/issues');
        }
      }]
    }];

    menu = Menu.buildFromTemplate(template);
    Menu.setApplicationMenu(menu);
  } else {
    template = [{
      label: '&File',
      submenu: [{
        label: '&Open',
        accelerator: 'Ctrl+O'
      }, {
        label: '&Close',
        accelerator: 'Ctrl+W',
        click() {
          mainWindow.close();
        }
      }]
    }, {
      label: '&View',
      submenu: (process.env.NODE_ENV === 'development') ? [{
        label: '&Reload',
        accelerator: 'Ctrl+R',
        click() {
          mainWindow.restart();
        }
      }, {
        label: 'Toggle &Full Screen',
        accelerator: 'F11',
        click() {
          mainWindow.setFullScreen(!mainWindow.isFullScreen());
        }
      }, {
        label: 'Toggle &Developer Tools',
        accelerator: 'Alt+Ctrl+I',
        click() {
          mainWindow.toggleDevTools();
        }
      }] : [{
        label: 'Toggle &Full Screen',
        accelerator: 'F11',
        click() {
          mainWindow.setFullScreen(!mainWindow.isFullScreen());
        }
      }]
    }, {
      label: 'Help',
      submenu: [{
        label: 'Learn More',
        click() {
          shell.openExternal('http://electron.atom.io');
        }
      }, {
        label: 'Documentation',
        click() {
          shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme');
        }
      }, {
        label: 'Community Discussions',
        click() {
          shell.openExternal('https://discuss.atom.io/c/electron');
        }
      }, {
        label: 'Search Issues',
        click() {
          shell.openExternal('https://github.com/atom/electron/issues');
        }
      }]
    }];
    menu = Menu.buildFromTemplate(template);
    mainWindow.setMenu(menu);
  }
});
<commit_msg>Takes out hardcoded credentials<commit_after>import { app, BrowserWindow, Menu, crashReporter, shell } from 'electron';
import restify from 'restify';
import parse from './parse';
import SequelizeManager from './sequelizeManager';

const sequelizeManager = new SequelizeManager();
const ipcMain = require('electron').ipcMain;

let menu;
let template;
let mainWindow = null;

crashReporter.start();

if (process.env.NODE_ENV === 'development') {
  require('electron-debug')();
}

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

app.on('ready', () => {
  mainWindow = new BrowserWindow({
    show: false,
    width: 1024,
    height: 728
  });

  mainWindow.loadURL(`file://${__dirname}/app/app.html`);

  mainWindow.webContents.on('did-finish-load', () => {
    mainWindow.show();
    mainWindow.focus();
  });

  mainWindow.on('closed', () => {
    mainWindow = null;
  });

  const server = restify.createServer();
  server.use(restify.queryParser());
  server.use(restify.CORS({
    origins: ['*'],   // defaults to ['*']
    credentials: false,                 // defaults to false
    headers: ['Access-Control-Allow-Origin']  // sets expose-headers
  }));

  server.listen(5000, () => {
  });

  mainWindow.webContents.on('did-finish-load', () => {
    ipcMain.on('connect', (event, payload) => {
      const usr = payload.username;
      const psw = payload.password;
      const db = payload.database;
      const prt = payload.portnb;
      const engine = payload.engine;

      sequelizeManager.login(usr, psw, db, prt, engine).then(msg => {
        event.sender.send('channel', { log: `logged in, ${msg}` });
      }).catch(error => {
         event.sender.send('channel', { error });
      });
    });

    ipcMain.on('receive', (event, payload) => {
      const statement = payload.statement;
      sequelizeManager.connection.query(statement).spread((rows, metadata) => {
        event.sender.send('channel', {rows, metadata, error: ''});
      }).catch(error => {
        event.sender.send('channel', { error });
      });
    });

    server.get('/query', (req, res) => {
      const statement = req.query.statement;
      mainWindow.webContents.send('channel', { log: statement });
      sequelizeManager.connection.query(statement).spread((rows, metadata) => {
        // Send back to app
        mainWindow.webContents.send('channel', { rows, metadata, error: '' });
        // Send back to plotly 2.0
        res.send(parse(rows));
      }).catch(error => {
        event.sender.send('channel', { error });
        res.send({ error });
      });
    });
  });

  if (process.env.NODE_ENV === 'development') {
    mainWindow.openDevTools();
  }

  if (process.platform === 'darwin') {
    template = [{
      label: 'Electron',
      submenu: [{
        label: 'About ElectronReact',
        selector: 'orderFrontStandardAboutPanel:'
      }, {
        type: 'separator'
      }, {
        label: 'Services',
        submenu: []
      }, {
        type: 'separator'
      }, {
        label: 'Hide ElectronReact',
        accelerator: 'Command+H',
        selector: 'hide:'
      }, {
        label: 'Hide Others',
        accelerator: 'Command+Shift+H',
        selector: 'hideOtherApplications:'
      }, {
        label: 'Show All',
        selector: 'unhideAllApplications:'
      }, {
        type: 'separator'
      }, {
        label: 'Quit',
        accelerator: 'Command+Q',
        click() {
          app.quit();
        }
      }]
    }, {
      label: 'Edit',
      submenu: [{
        label: 'Undo',
        accelerator: 'Command+Z',
        selector: 'undo:'
      }, {
        label: 'Redo',
        accelerator: 'Shift+Command+Z',
        selector: 'redo:'
      }, {
        type: 'separator'
      }, {
        label: 'Cut',
        accelerator: 'Command+X',
        selector: 'cut:'
      }, {
        label: 'Copy',
        accelerator: 'Command+C',
        selector: 'copy:'
      }, {
        label: 'Paste',
        accelerator: 'Command+V',
        selector: 'paste:'
      }, {
        label: 'Select All',
        accelerator: 'Command+A',
        selector: 'selectAll:'
      }]
    }, {
      label: 'View',
      submenu: (process.env.NODE_ENV === 'development') ? [{
        label: 'Reload',
        accelerator: 'Command+R',
        click() {
          mainWindow.restart();
        }
      }, {
        label: 'Toggle Full Screen',
        accelerator: 'Ctrl+Command+F',
        click() {
          mainWindow.setFullScreen(!mainWindow.isFullScreen());
        }
      }, {
        label: 'Toggle Developer Tools',
        accelerator: 'Alt+Command+I',
        click() {
          mainWindow.toggleDevTools();
        }
      }] : [{
        label: 'Toggle Full Screen',
        accelerator: 'Ctrl+Command+F',
        click() {
          mainWindow.setFullScreen(!mainWindow.isFullScreen());
        }
      }]
    }, {
      label: 'Window',
      submenu: [{
        label: 'Minimize',
        accelerator: 'Command+M',
        selector: 'performMiniaturize:'
      }, {
        label: 'Close',
        accelerator: 'Command+W',
        selector: 'performClose:'
      }, {
        type: 'separator'
      }, {
        label: 'Bring All to Front',
        selector: 'arrangeInFront:'
      }]
    }, {
      label: 'Help',
      submenu: [{
        label: 'Learn More',
        click() {
          shell.openExternal('http://electron.atom.io');
        }
      }, {
        label: 'Documentation',
        click() {
          shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme');
        }
      }, {
        label: 'Community Discussions',
        click() {
          shell.openExternal('https://discuss.atom.io/c/electron');
        }
      }, {
        label: 'Search Issues',
        click() {
          shell.openExternal('https://github.com/atom/electron/issues');
        }
      }]
    }];

    menu = Menu.buildFromTemplate(template);
    Menu.setApplicationMenu(menu);
  } else {
    template = [{
      label: '&File',
      submenu: [{
        label: '&Open',
        accelerator: 'Ctrl+O'
      }, {
        label: '&Close',
        accelerator: 'Ctrl+W',
        click() {
          mainWindow.close();
        }
      }]
    }, {
      label: '&View',
      submenu: (process.env.NODE_ENV === 'development') ? [{
        label: '&Reload',
        accelerator: 'Ctrl+R',
        click() {
          mainWindow.restart();
        }
      }, {
        label: 'Toggle &Full Screen',
        accelerator: 'F11',
        click() {
          mainWindow.setFullScreen(!mainWindow.isFullScreen());
        }
      }, {
        label: 'Toggle &Developer Tools',
        accelerator: 'Alt+Ctrl+I',
        click() {
          mainWindow.toggleDevTools();
        }
      }] : [{
        label: 'Toggle &Full Screen',
        accelerator: 'F11',
        click() {
          mainWindow.setFullScreen(!mainWindow.isFullScreen());
        }
      }]
    }, {
      label: 'Help',
      submenu: [{
        label: 'Learn More',
        click() {
          shell.openExternal('http://electron.atom.io');
        }
      }, {
        label: 'Documentation',
        click() {
          shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme');
        }
      }, {
        label: 'Community Discussions',
        click() {
          shell.openExternal('https://discuss.atom.io/c/electron');
        }
      }, {
        label: 'Search Issues',
        click() {
          shell.openExternal('https://github.com/atom/electron/issues');
        }
      }]
    }];
    menu = Menu.buildFromTemplate(template);
    mainWindow.setMenu(menu);
  }
});
<|endoftext|>


<commit_before>'use strict';

const Promise = require('bluebird');
const mongodb = require('../..');
const MongoClient = mongodb.MongoClient;
const path = require('path');
const fs = require('fs');
const chai = require('chai');
const expect = chai.expect;
const EJSON = require('mongodb-extjson');

// mlaunch init --replicaset --arbiter  --name rs --hostname localhost --port 31000 --binarypath /Users/mbroadst/Downloads/mongodb-osx-x86_64-enterprise-3.7.3-411-g91e4266/bin

chai.use(require('chai-subset'));
chai.config.includeStack = true;
chai.config.showDiff = true;
chai.config.truncateThreshold = 0;

const testContext = {
  dbName: 'transaction-tests',
  collectionName: 'test'
};

function isPlainObject(value) {
  return value !== null && typeof value === 'object' && Array.isArray(value) === false;
}

/**
 * Finds placeholder values in a deeply nested object.
 *
 * NOTE: This also mutates the object, by removing the values for comparison
 *
 * @param {Object} input the object to find placeholder values in
 */
function findPlaceholders(value, parent) {
  return Object.keys(value).reduce((result, key) => {
    if (isPlainObject(value[key])) {
      return result.concat(
        findPlaceholders(value[key], [value, key]).map(x => {
          if (x.path.startsWith('$')) {
            x.path = key;
          } else {
            x.path = `${key}.${x.path}`;
          }

          return x;
        })
      );
    }

    if (value[key] === null) {
      delete value[key];
      result.push({ path: key, type: null });
    } else if (value[key] === 42 || value[key] === '42') {
      if (key.startsWith('$number') || value[key] === 42) {
        result.push({ path: key, type: 'number' });
      } else {
        result.push({ path: key, type: 'string' });
      }

      // NOTE: fix this, it just passes the current example
      delete parent[0][parent[1]];
    } else if (value[key] === '') {
      result.push({ path: key, type: 'string' });
    }

    return result;
  }, []);
}

function translateClientOptions(options) {
  Object.keys(options).forEach(key => {
    if (key === 'readConcernLevel') {
      options.readConcern = { level: options.readConcernLevel };
      delete options[key];
    }
  });

  return options;
}

describe('Transactions (spec)', function() {
  const testSuites = fs
    .readdirSync(`${__dirname}/spec/transactions`)
    .filter(x => x.indexOf('.json') !== -1)
    .map(x =>
      Object.assign(JSON.parse(fs.readFileSync(`${__dirname}/spec/transactions/${x}`)), {
        name: path.basename(x, '.json')
      })
    );

  after(() => testContext.client.close());
  before(function() {
    // create a shared client for admin tasks
    const config = this.configuration;
    testContext.url = `mongodb://${config.host}:${config.port}/${testContext.dbName}?replicaSet=${
      config.replicasetName
    }`;

    testContext.client = new MongoClient(testContext.url);
    return testContext.client.connect();
  });

  testSuites.forEach(testSuite => {
    describe(testSuite.name, {
      metadata: { requires: { topology: ['replicaset', 'mongos'], mongodb: '>=3.7.x' } },
      test: function() {
        beforeEach(() => {
          const db = testContext.client.db();
          const coll = db.collection(testContext.collectionName);

          return coll
            .drop()
            .catch(err => {
              if (!err.message.match(/ns not found/)) throw err;
            })
            .then(() => db.createCollection(testContext.collectionName, { w: 'majority' }))
            .then(() => {
              if (testSuite.data && Array.isArray(testSuite.data) && testSuite.data.length > 0) {
                return coll.insert(testSuite.data, { w: 'majority' });
              }
            });
        });

        testSuite.tests.forEach(testData => {
          afterEach(() => {
            if (testContext.testClient) {
              return testContext.testClient.close().then(() => {
                delete testContext.testClient;
              });
            }
          });

          const maybeSkipIt = testData.skipReason ? it.skip : it;
          maybeSkipIt(testData.description, function() {
            const commandEvents = [];
            const clientOptions = translateClientOptions(
              Object.assign({ monitorCommands: true }, testData.clientOptions)
            );

            return MongoClient.connect(testContext.url, clientOptions).then(client => {
              testContext.testClient = client;
              client.on('commandStarted', event => {
                if (
                  event.databaseName === testContext.dbName ||
                  ['startTransaction', 'commitTransaction', 'abortTransaction'].includes(
                    event.commandName
                  )
                ) {
                  // console.dir(event, { depth: null });
                  commandEvents.push(event);
                }
              });

              const sessionOptions = Object.assign({}, testData.transactionOptions);

              testData.sessionOptions = testData.sessionOptions || {};
              const session0 = client.startSession(
                Object.assign({}, sessionOptions, testData.sessionOptions.session0)
              );
              const session1 = client.startSession(
                Object.assign({}, sessionOptions, testData.sessionOptions.session1)
              );

              return testOperations(client, testData, { session0, session1 })
                .catch(err => {
                  // If the driver throws an exception / returns an error while executing this series
                  // of operations, store the error message.
                  // console.log('error occurred during series of operations');
                  // console.dir(err);
                  // operationError = err;
                  throw err;
                })
                .then(() => {
                  session0.endSession();
                  session1.endSession();

                  if (
                    testData.expectations &&
                    Array.isArray(testData.expectations) &&
                    testData.expectations.length > 0
                  ) {
                    const actualEvents = normalizeCommandShapes(commandEvents);
                    const rawExpectedEvents = testData.expectations.map(x =>
                      linkSessionData(x.command_started_event, { session0, session1 })
                    );

                    const expectedEventPlaceholders = rawExpectedEvents.map(event =>
                      findPlaceholders(event.command)
                    );

                    const expectedEvents = normalizeCommandShapes(rawExpectedEvents);
                    expect(actualEvents).to.have.length(expectedEvents.length);

                    expectedEvents.forEach((expected, idx) => {
                      const actual = actualEvents[idx];
                      const placeHolders = expectedEventPlaceholders[idx]; // eslint-disable-line

                      expect(actual.commandName).to.equal(expected.commandName);
                      expect(actual.databaseName).to.equal(expected.databaseName);

                      const actualCommand = actual.command;
                      const expectedCommand = expected.command;

                      // handle validation of placeholder values
                      // placeHolders.forEach(placeholder => {
                      //   const parsedActual = EJSON.parse(JSON.stringify(actualCommand), {
                      //     relaxed: true
                      //   });

                      //   if (placeholder.type === null) {
                      //     expect(parsedActual).to.not.have.all.nested.property(placeholder.path);
                      //   } else if (placeholder.type === 'string') {
                      //     expect(parsedActual).nested.property(placeholder.path).to.exist;
                      //     expect(parsedActual)
                      //       .nested.property(placeholder.path)
                      //       .to.have.length.greaterThan(0);
                      //   } else if (placeholder.type === 'number') {
                      //     expect(parsedActual).nested.property(placeholder.path).to.exist;
                      //     expect(parsedActual)
                      //       .nested.property(placeholder.path)
                      //       .to.be.greaterThan(0);
                      //   }
                      // });

                      // compare the command
                      expect(actualCommand).to.containSubset(expectedCommand);
                    });
                  }

                  if (testData.outcome) {
                    if (testData.outcome.collection) {
                      // use the client without transactions to verify
                      return testContext.client
                        .db()
                        .collection(testContext.collectionName)
                        .find({})
                        .toArray()
                        .then(docs => {
                          expect(docs).to.eql(testData.outcome.collection.data);
                        });
                    }
                  }
                });
            });
          });
        });
      }
    });
  });
});

function linkSessionData(command, context) {
  const session = context[command.command.lsid];
  const result = Object.assign({}, command);
  result.command.lsid = JSON.parse(EJSON.stringify(session.id));
  return result;
}

function normalizeCommandShapes(commands) {
  return commands.map(command =>
    JSON.parse(
      EJSON.stringify({
        command: command.command,
        commandName: command.command_name ? command.command_name : command.commandName,
        databaseName: command.database_name ? command.database_name : command.databaseName
      })
    )
  );
}

function extractCrudResult(result, operation) {
  if (Array.isArray(result) || !isPlainObject(result)) {
    return result;
  }

  if (result.value) {
    // some of our findAndModify results return more than just an id, so we need to pluck
    const resultKeys = Object.keys(operation.result);
    if (resultKeys.length === 1 && resultKeys[0] === '_id') {
      return { _id: result.value._id };
    }

    return result.value;
  }

  return Object.keys(operation.result).reduce((crudResult, key) => {
    if (result.hasOwnProperty(key) && result[key] != null) {
      // FIXME(major): update crud results are broken and need to be changed
      crudResult[key] = key === 'upsertedId' ? result[key]._id : result[key];
    }

    return crudResult;
  }, {});
}

function isTransactionCommand(command) {
  return ['startTransaction', 'commitTransaction', 'abortTransaction'].includes(command);
}

function extractBulkRequests(requests) {
  return requests.map(request => ({ [request.name]: request.arguments }));
}

/**
 *
 * @param {Object} operation the operation definition from the spec test
 * @param {Object} obj the object to call the operation on
 * @param {Object} context a context object containing sessions used for the test
 */
function testOperation(operation, obj, context) {
  // console.log('testing operation: ', operation.name);

  const opOptions = {};
  const args = [];
  if (operation.arguments) {
    Object.keys(operation.arguments).forEach(key => {
      if (['filter', 'fieldName', 'document', 'documents'].includes(key)) {
        return args.unshift(operation.arguments[key]);
      }

      if (key === 'requests') return args.unshift(extractBulkRequests(operation.arguments[key]));
      if (key === 'update' || key === 'replacement') return args.push(operation.arguments[key]);
      if (key === 'session') {
        if (isTransactionCommand(operation.name)) return;
        opOptions.session = context[operation.arguments.session];
        return;
      }

      if (key === 'returnDocument') {
        opOptions.returnOriginal = operation.arguments[key] === 'Before' ? true : false;
        return;
      }

      opOptions[key] = operation.arguments[key];
    });
  }

  if (args.length === 0 && !isTransactionCommand(operation.name)) {
    args.push({});
  }

  if (Object.keys(opOptions).length > 0) {
    // NOTE: this is awful, but in order to provide options for some methods we need to add empty
    //       query objects.
    if (operation.name === 'distinct') {
      args.push({});
    }

    args.push(opOptions);
  }

  let opPromise;
  if (operation.name === 'find') {
    // `find` creates a cursor, so we need to call `toArray` on it
    const cursor = obj[operation.name].apply(obj, args);
    opPromise = cursor.toArray();
  } else if (operation.name === 'startTransaction') {
    // `startTansaction` can throw, so we need to make sure we wrap it in a promise
    opPromise = Promise.try(() => obj[operation.name].apply(obj, args));
  } else {
    opPromise = obj[operation.name].apply(obj, args);
  }

  if (operation.result) {
    if (operation.result.errorContains || operation.result.errorCodeName) {
      return opPromise
        .then(() => {
          throw new Error('expected an error!');
        })
        .catch(err => {
          if (operation.result.errorContains) {
            expect(err).to.match(new RegExp(operation.result.errorContains, 'i'));
          } else {
            expect(err.codeName).to.equal(operation.result.errorCodeName);
          }
        });
    }

    return opPromise.then(opResult => {
      const actual = extractCrudResult(opResult, operation);
      expect(actual).to.eql(operation.result);
    });
  }

  return opPromise;
}

function testOperations(client, testData, context) {
  const coll = client.db().collection('test');
  return testData.operations.reduce((combined, operation) => {
    if (isTransactionCommand(operation.name)) {
      const session = context[operation.arguments.session];
      return combined.then(() => testOperation(operation, session, context));
    }

    return combined.then(() => testOperation(operation, coll, context));
  }, Promise.resolve());
}
<commit_msg>test(txns): support the new `options` keyword for txn tests<commit_after>'use strict';

const Promise = require('bluebird');
const mongodb = require('../..');
const MongoClient = mongodb.MongoClient;
const path = require('path');
const fs = require('fs');
const chai = require('chai');
const expect = chai.expect;
const EJSON = require('mongodb-extjson');

// mlaunch init --replicaset --arbiter  --name rs --hostname localhost --port 31000 --binarypath /Users/mbroadst/Downloads/mongodb-osx-x86_64-enterprise-3.7.3-411-g91e4266/bin

chai.use(require('chai-subset'));
chai.config.includeStack = true;
chai.config.showDiff = true;
chai.config.truncateThreshold = 0;

const testContext = {
  dbName: 'transaction-tests',
  collectionName: 'test'
};

function isPlainObject(value) {
  return value !== null && typeof value === 'object' && Array.isArray(value) === false;
}

/**
 * Finds placeholder values in a deeply nested object.
 *
 * NOTE: This also mutates the object, by removing the values for comparison
 *
 * @param {Object} input the object to find placeholder values in
 */
function findPlaceholders(value, parent) {
  return Object.keys(value).reduce((result, key) => {
    if (isPlainObject(value[key])) {
      return result.concat(
        findPlaceholders(value[key], [value, key]).map(x => {
          if (x.path.startsWith('$')) {
            x.path = key;
          } else {
            x.path = `${key}.${x.path}`;
          }

          return x;
        })
      );
    }

    if (value[key] === null) {
      delete value[key];
      result.push({ path: key, type: null });
    } else if (value[key] === 42 || value[key] === '42') {
      if (key.startsWith('$number') || value[key] === 42) {
        result.push({ path: key, type: 'number' });
      } else {
        result.push({ path: key, type: 'string' });
      }

      // NOTE: fix this, it just passes the current example
      delete parent[0][parent[1]];
    } else if (value[key] === '') {
      result.push({ path: key, type: 'string' });
    }

    return result;
  }, []);
}

function translateClientOptions(options) {
  Object.keys(options).forEach(key => {
    if (key === 'readConcernLevel') {
      options.readConcern = { level: options.readConcernLevel };
      delete options[key];
    }
  });

  return options;
}

describe('Transactions (spec)', function() {
  const testSuites = fs
    .readdirSync(`${__dirname}/spec/transactions`)
    .filter(x => x.indexOf('.json') !== -1)
    .map(x =>
      Object.assign(JSON.parse(fs.readFileSync(`${__dirname}/spec/transactions/${x}`)), {
        name: path.basename(x, '.json')
      })
    );

  after(() => testContext.client.close());
  before(function() {
    // create a shared client for admin tasks
    const config = this.configuration;
    testContext.url = `mongodb://${config.host}:${config.port}/${testContext.dbName}?replicaSet=${
      config.replicasetName
    }`;

    testContext.client = new MongoClient(testContext.url);
    return testContext.client.connect();
  });

  testSuites.forEach(testSuite => {
    describe(testSuite.name, {
      metadata: { requires: { topology: ['replicaset', 'mongos'], mongodb: '>=3.7.x' } },
      test: function() {
        beforeEach(() => {
          const db = testContext.client.db();
          const coll = db.collection(testContext.collectionName);

          return coll
            .drop()
            .catch(err => {
              if (!err.message.match(/ns not found/)) throw err;
            })
            .then(() => db.createCollection(testContext.collectionName, { w: 'majority' }))
            .then(() => {
              if (testSuite.data && Array.isArray(testSuite.data) && testSuite.data.length > 0) {
                return coll.insert(testSuite.data, { w: 'majority' });
              }
            });
        });

        testSuite.tests.forEach(testData => {
          afterEach(() => {
            if (testContext.testClient) {
              return testContext.testClient.close().then(() => {
                delete testContext.testClient;
              });
            }
          });

          const maybeSkipIt = testData.skipReason ? it.skip : it;
          maybeSkipIt(testData.description, function() {
            const commandEvents = [];
            const clientOptions = translateClientOptions(
              Object.assign({ monitorCommands: true }, testData.clientOptions)
            );

            return MongoClient.connect(testContext.url, clientOptions).then(client => {
              testContext.testClient = client;
              client.on('commandStarted', event => {
                if (
                  event.databaseName === testContext.dbName ||
                  ['startTransaction', 'commitTransaction', 'abortTransaction'].includes(
                    event.commandName
                  )
                ) {
                  // console.dir(event, { depth: null });
                  commandEvents.push(event);
                }
              });

              const sessionOptions = Object.assign({}, testData.transactionOptions);

              testData.sessionOptions = testData.sessionOptions || {};
              const session0 = client.startSession(
                Object.assign({}, sessionOptions, testData.sessionOptions.session0)
              );
              const session1 = client.startSession(
                Object.assign({}, sessionOptions, testData.sessionOptions.session1)
              );

              return testOperations(client, testData, { session0, session1 })
                .catch(err => {
                  // If the driver throws an exception / returns an error while executing this series
                  // of operations, store the error message.
                  // console.log('error occurred during series of operations');
                  // console.dir(err);
                  // operationError = err;
                  throw err;
                })
                .then(() => {
                  session0.endSession();
                  session1.endSession();

                  if (
                    testData.expectations &&
                    Array.isArray(testData.expectations) &&
                    testData.expectations.length > 0
                  ) {
                    const actualEvents = normalizeCommandShapes(commandEvents);
                    const rawExpectedEvents = testData.expectations.map(x =>
                      linkSessionData(x.command_started_event, { session0, session1 })
                    );

                    const expectedEventPlaceholders = rawExpectedEvents.map(event =>
                      findPlaceholders(event.command)
                    );

                    const expectedEvents = normalizeCommandShapes(rawExpectedEvents);
                    expect(actualEvents).to.have.length(expectedEvents.length);

                    expectedEvents.forEach((expected, idx) => {
                      const actual = actualEvents[idx];
                      const placeHolders = expectedEventPlaceholders[idx]; // eslint-disable-line

                      expect(actual.commandName).to.equal(expected.commandName);
                      expect(actual.databaseName).to.equal(expected.databaseName);

                      const actualCommand = actual.command;
                      const expectedCommand = expected.command;

                      // handle validation of placeholder values
                      // placeHolders.forEach(placeholder => {
                      //   const parsedActual = EJSON.parse(JSON.stringify(actualCommand), {
                      //     relaxed: true
                      //   });

                      //   if (placeholder.type === null) {
                      //     expect(parsedActual).to.not.have.all.nested.property(placeholder.path);
                      //   } else if (placeholder.type === 'string') {
                      //     expect(parsedActual).nested.property(placeholder.path).to.exist;
                      //     expect(parsedActual)
                      //       .nested.property(placeholder.path)
                      //       .to.have.length.greaterThan(0);
                      //   } else if (placeholder.type === 'number') {
                      //     expect(parsedActual).nested.property(placeholder.path).to.exist;
                      //     expect(parsedActual)
                      //       .nested.property(placeholder.path)
                      //       .to.be.greaterThan(0);
                      //   }
                      // });

                      // compare the command
                      expect(actualCommand).to.containSubset(expectedCommand);
                    });
                  }

                  if (testData.outcome) {
                    if (testData.outcome.collection) {
                      // use the client without transactions to verify
                      return testContext.client
                        .db()
                        .collection(testContext.collectionName)
                        .find({})
                        .toArray()
                        .then(docs => {
                          expect(docs).to.eql(testData.outcome.collection.data);
                        });
                    }
                  }
                });
            });
          });
        });
      }
    });
  });
});

function linkSessionData(command, context) {
  const session = context[command.command.lsid];
  const result = Object.assign({}, command);
  result.command.lsid = JSON.parse(EJSON.stringify(session.id));
  return result;
}

function normalizeCommandShapes(commands) {
  return commands.map(command =>
    JSON.parse(
      EJSON.stringify({
        command: command.command,
        commandName: command.command_name ? command.command_name : command.commandName,
        databaseName: command.database_name ? command.database_name : command.databaseName
      })
    )
  );
}

function extractCrudResult(result, operation) {
  if (Array.isArray(result) || !isPlainObject(result)) {
    return result;
  }

  if (result.value) {
    // some of our findAndModify results return more than just an id, so we need to pluck
    const resultKeys = Object.keys(operation.result);
    if (resultKeys.length === 1 && resultKeys[0] === '_id') {
      return { _id: result.value._id };
    }

    return result.value;
  }

  return Object.keys(operation.result).reduce((crudResult, key) => {
    if (result.hasOwnProperty(key) && result[key] != null) {
      // FIXME(major): update crud results are broken and need to be changed
      crudResult[key] = key === 'upsertedId' ? result[key]._id : result[key];
    }

    return crudResult;
  }, {});
}

function isTransactionCommand(command) {
  return ['startTransaction', 'commitTransaction', 'abortTransaction'].includes(command);
}

function extractBulkRequests(requests) {
  return requests.map(request => ({ [request.name]: request.arguments }));
}

/**
 *
 * @param {Object} operation the operation definition from the spec test
 * @param {Object} obj the object to call the operation on
 * @param {Object} context a context object containing sessions used for the test
 */
function testOperation(operation, obj, context) {
  // console.log('testing operation: ', operation.name);

  const opOptions = {};
  const args = [];
  if (operation.arguments) {
    Object.keys(operation.arguments).forEach(key => {
      if (['filter', 'fieldName', 'document', 'documents'].includes(key)) {
        return args.unshift(operation.arguments[key]);
      }

      if (key === 'requests') return args.unshift(extractBulkRequests(operation.arguments[key]));
      if (key === 'update' || key === 'replacement') return args.push(operation.arguments[key]);
      if (key === 'session') {
        if (isTransactionCommand(operation.name)) return;
        opOptions.session = context[operation.arguments.session];
        return;
      }

      if (key === 'returnDocument') {
        opOptions.returnOriginal = operation.arguments[key] === 'Before' ? true : false;
        return;
      }

      if (key === 'options') {
        Object.assign(opOptions, operation.arguments[key]);
        return;
      }

      opOptions[key] = operation.arguments[key];
    });
  }

  if (args.length === 0 && !isTransactionCommand(operation.name)) {
    args.push({});
  }

  if (Object.keys(opOptions).length > 0) {
    // NOTE: this is awful, but in order to provide options for some methods we need to add empty
    //       query objects.
    if (operation.name === 'distinct') {
      args.push({});
    }

    args.push(opOptions);
  }

  let opPromise;
  if (operation.name === 'find') {
    // `find` creates a cursor, so we need to call `toArray` on it
    const cursor = obj[operation.name].apply(obj, args);
    opPromise = cursor.toArray();
  } else if (operation.name === 'startTransaction') {
    // `startTansaction` can throw, so we need to make sure we wrap it in a promise
    opPromise = Promise.try(() => obj[operation.name].apply(obj, args));
  } else {
    opPromise = obj[operation.name].apply(obj, args);
  }

  if (operation.result) {
    if (operation.result.errorContains || operation.result.errorCodeName) {
      return opPromise
        .then(() => {
          throw new Error('expected an error!');
        })
        .catch(err => {
          if (operation.result.errorContains) {
            expect(err).to.match(new RegExp(operation.result.errorContains, 'i'));
          } else {
            expect(err.codeName).to.equal(operation.result.errorCodeName);
          }
        });
    }

    return opPromise.then(opResult => {
      const actual = extractCrudResult(opResult, operation);
      expect(actual).to.eql(operation.result);
    });
  }

  return opPromise;
}

function testOperations(client, testData, context) {
  const coll = client.db().collection('test');
  return testData.operations.reduce((combined, operation) => {
    if (isTransactionCommand(operation.name)) {
      const session = context[operation.arguments.session];
      return combined.then(() => testOperation(operation, session, context));
    }

    return combined.then(() => testOperation(operation, coll, context));
  }, Promise.resolve());
}
<|endoftext|>


<commit_before>/*global it:true */
"use strict";

var describeWd = require("../../helpers/driverblock.js").describeForApp('UICatalog')
  , should = require('should')
  , _s = require('underscore.string')
  , assert = require('assert');

describeWd('flick gesture', function(h) {
  it('should work via webdriver method', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        h.driver.flick(0, -100, false, function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
  it('should work via mobile only method', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        h.driver.execute("mobile: flick", [{endX: 0, endY: 0}], function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
  it('should work via mobile only method with percentage', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        var opts = {startX: 0.75, startY: 0.75, endX: 0.25, endY: 0.25};
        h.driver.execute("mobile: flick", [opts], function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
});

describeWd('swipe gesture', function(h) {
  it('should work with wd function in pixels', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        h.driver.flick(0, -100, true, function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
  it('should work with wd function in percentage units', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        h.driver.flick(0, -0.5, true, function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
  it('should work with mobile function in pixels', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        var opts = {startX: 50, startY: 400, endX: 50, endY: 0, duration: 2};
        h.driver.execute("mobile: swipe", [opts], function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
  it('should work with mobile function in percent', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        var opts = {startX: 0.5, startY: 0.9, endX: 0.5, endY: 0.1, duration: 2};
        h.driver.execute("mobile: swipe", [opts], function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
});

describeWd("flick element", function(h) {
  it("slider value should change", function(done) {
    h.driver.elementsByTagName("tableCell", function(err, elements) {
      elements[1].click(function(){
        h.driver.elementByTagName("slider", function(err, element) {
          element.getAttribute("value", function(err, valueBefore) {
            element.flick(-0.5, 0, 1, function() {
              element.getAttribute("value", function(err, valueAfter) {
                assert.equal(valueBefore, "50%");
                assert.equal(valueAfter, "0%");
                done();
              });
            });
          });
        });
      });
    });
  });
  it("should work with mobile flick", function(done) {
    h.driver.elementsByTagName("tableCell", function(err, elements) {
      elements[1].click(function(){
        h.driver.elementByTagName("slider", function(err, element) {
          element.getAttribute("value", function(err, valueBefore) {
            var opts = {element: element.value, endX: -50, endY: 0};
            h.driver.execute("mobile: flick", [opts], function() {
              element.getAttribute("value", function(err, valueAfter) {
                assert.equal(valueBefore, "50%");
                assert.equal(valueAfter, "0%");
                done();
              });
            });
          });
        });
      });
    });
  });
  it("should work with mobile flick and percent", function(done) {
    h.driver.elementsByTagName("tableCell", function(err, elements) {
      elements[1].click(function(){
        h.driver.elementByTagName("slider", function(err, element) {
          element.getAttribute("value", function(err, valueBefore) {
            var opts = {element: element.value, startX: 0.5, startY: 0.0,
              endX: 0.0, endY: 0.0};
            h.driver.execute("mobile: flick", [opts], function() {
              element.getAttribute("value", function(err, valueAfter) {
                assert.equal(valueBefore, "50%");
                assert.equal(valueAfter, "0%");
                done();
              });
            });
          });
        });
      });
    });
  });
});
describeWd("swipe element", function(h) {
  it("slider value should change", function(done) {
    h.driver.elementsByTagName("tableCell", function(err, elements) {
      elements[1].click(function(){
        h.driver.elementByTagName("slider", function(err, element) {
          element.getAttribute("value", function(err, valueBefore) {
            var opts = {startX: 0.5, startY: 0.5, endX: 0.25, endY: 0.5,
              duration: 0.3, element: element.value};
            h.driver.execute("mobile: swipe", [opts], function() {
              element.getAttribute("value", function(err, valueAfter) {
                assert.equal(valueBefore, "50%");
                assert.equal(valueAfter, "20%");
                done();
              });
            });
          });
        });
      });
    });
  });
  it("slider value should change by pixels", function(done) {
    h.driver.elementsByTagName("tableCell", function(err, elements) {
      elements[1].click(function(){
        h.driver.elementByTagName("slider", function(err, element) {
          element.getAttribute("value", function(err, valueBefore) {
            var opts = {endX: 15, endY: 10, duration: 0.3, element: element.value};
            h.driver.execute("mobile: swipe", [opts], function() {
              element.getAttribute("value", function(err, valueAfter) {
                assert.equal(valueBefore, "50%");
                assert.equal(valueAfter, "5%");
                done();
              });
            });
          });
        });
      });
    });
  });
});
describeWd('complex tap', function(h) {
  it('should work with custom options', function(done) {
    var tapOpts = {
      tapCount: 1 // how many taps
      , duration: 2.3 // how long
      , touchCount: 3 // how many fingers
      , x: 100 // in pixels from left
      , y: 250 // in pixels from top
    };
    h.driver.execute("mobile: tap", [tapOpts], function(err) {
      should.not.exist(err);
      h.driver.elementByTagName("textview", function(err, el) {
        should.not.exist(err);
        el.text(function(err, text) {
          should.not.exist(err);
          _s.trim(text).should.eql("Now is the time for all good developers to come to serve their country.\n\nNow is the time for all good developers to come to serve their country.");
          done();
        });
      });
    });
  });
  it('should work in relative units', function(done) {
    var tapOpts = {
      tapCount: 1 // how many taps
      , duration: 2.3 // how long
      , touchCount: 3 // how many fingers
      , x: 0.5 // 50% from left of screen
      , y: 0.55 // 55% from top of screen
    };
    h.driver.execute("mobile: tap", [tapOpts], function(err) {
      should.not.exist(err);
      h.driver.elementByTagName("textview", function(err, el) {
        should.not.exist(err);
        el.text(function(err, text) {
          should.not.exist(err);
          _s.trim(text).should.eql("Now is the time for all good developers to come to serve their country.\n\nNow is the time for all good developers to come to serve their country.");
          done();
        });
      });
    });
  });
  it('should work with default options', function(done) {
    h.driver.execute("mobile: tap", function(err) {
      should.not.exist(err);
      h.driver.elementByTagName("textview", function(err) {
        should.exist(err);
        err.status.should.equal(7);
        done();
      });
    });
  });
});
describeWd('complex tap on element', function(h) {
  it('should work in relative units', function(done) {
    h.driver.elementsByTagName('tableCell', function(err, els) {
      should.not.exist(err);
      var el = els[4];
      var tapOpts = {
        x: 0.5 // in relative width from left
        , y: 0.5 // in relative height from top
        , element: el.value
      };
      h.driver.execute("mobile: tap", [tapOpts], function(err) {
        should.not.exist(err);
        h.driver.elementByTagName("textview", function(err, el) {
          should.not.exist(err);
          el.text(function(err, text) {
            should.not.exist(err);
            _s.trim(text).should.eql("Now is the time for all good developers to come to serve their country.\n\nNow is the time for all good developers to come to serve their country.");
            done();
          });
        });
      });
    });
  });
  it('should work in pixels', function(done) {
    h.driver.elementsByTagName('tableCell', function(err, els) {
      should.not.exist(err);
      var el = els[4];
      var tapOpts = {
        x: 150 // in pixels from left
        , y: 30 // in pixels from top
        , element: el.value
      };
      h.driver.execute("mobile: tap", [tapOpts], function(err) {
        should.not.exist(err);
        h.driver.elementByTagName("textview", function(err, el) {
          should.not.exist(err);
          el.text(function(err, text) {
            should.not.exist(err);
            _s.trim(text).should.eql("Now is the time for all good developers to come to serve their country.\n\nNow is the time for all good developers to come to serve their country.");
            done();
          });
        });
      });
    });
  });
});
<commit_msg>fix test which had wrong expectation<commit_after>/*global it:true */
"use strict";

var describeWd = require("../../helpers/driverblock.js").describeForApp('UICatalog')
  , should = require('should')
  , _s = require('underscore.string')
  , assert = require('assert');

describeWd('flick gesture', function(h) {
  it('should work via webdriver method', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        h.driver.flick(0, -100, false, function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
  it('should work via mobile only method', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        h.driver.execute("mobile: flick", [{endX: 0, endY: 0}], function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
  it('should work via mobile only method with percentage', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        var opts = {startX: 0.75, startY: 0.75, endX: 0.25, endY: 0.25};
        h.driver.execute("mobile: flick", [opts], function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
});

describeWd('swipe gesture', function(h) {
  it('should work with wd function in pixels', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        h.driver.flick(0, -100, true, function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
  it('should work with wd function in percentage units', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        h.driver.flick(0, -0.5, true, function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
  it('should work with mobile function in pixels', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        var opts = {startX: 50, startY: 400, endX: 50, endY: 0, duration: 2};
        h.driver.execute("mobile: swipe", [opts], function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
  it('should work with mobile function in percent', function(done) {
    h.driver.elementByTagName('tableCell', function(err, element) {
      element.getLocation(function(err, location) {
        var opts = {startX: 0.5, startY: 0.9, endX: 0.5, endY: 0.1, duration: 2};
        h.driver.execute("mobile: swipe", [opts], function(err) {
          should.not.exist(err);
          element.getLocation(function(err, location2) {
            assert.equal(location.x, location.x);
            assert.notEqual(location.y, location2.y);
            done();
          });
        });
      });
    });
  });
});

describeWd("flick element", function(h) {
  it("slider value should change", function(done) {
    h.driver.elementsByTagName("tableCell", function(err, elements) {
      elements[1].click(function(){
        h.driver.elementByTagName("slider", function(err, element) {
          element.getAttribute("value", function(err, valueBefore) {
            element.flick(-0.5, 0, 1, function() {
              element.getAttribute("value", function(err, valueAfter) {
                assert.equal(valueBefore, "50%");
                assert.equal(valueAfter, "0%");
                done();
              });
            });
          });
        });
      });
    });
  });
  it("should work with mobile flick", function(done) {
    h.driver.elementsByTagName("tableCell", function(err, elements) {
      elements[1].click(function(){
        h.driver.elementByTagName("slider", function(err, element) {
          element.getAttribute("value", function(err, valueBefore) {
            var opts = {element: element.value, endX: -50, endY: 0};
            h.driver.execute("mobile: flick", [opts], function() {
              element.getAttribute("value", function(err, valueAfter) {
                assert.equal(valueBefore, "50%");
                assert.equal(valueAfter, "0%");
                done();
              });
            });
          });
        });
      });
    });
  });
  it("should work with mobile flick and percent", function(done) {
    h.driver.elementsByTagName("tableCell", function(err, elements) {
      elements[1].click(function(){
        h.driver.elementByTagName("slider", function(err, element) {
          element.getAttribute("value", function(err, valueBefore) {
            var opts = {element: element.value, startX: 0.5, startY: 0.0,
              endX: 0.0, endY: 0.0};
            h.driver.execute("mobile: flick", [opts], function() {
              element.getAttribute("value", function(err, valueAfter) {
                assert.equal(valueBefore, "50%");
                assert.equal(valueAfter, "0%");
                done();
              });
            });
          });
        });
      });
    });
  });
});
describeWd("swipe element", function(h) {
  it("slider value should change", function(done) {
    h.driver.elementsByTagName("tableCell", function(err, elements) {
      elements[1].click(function(){
        h.driver.elementByTagName("slider", function(err, element) {
          element.getAttribute("value", function(err, valueBefore) {
            var opts = {startX: 0.5, startY: 0.5, endX: 0.25, endY: 0.5,
              duration: 0.3, element: element.value};
            h.driver.execute("mobile: swipe", [opts], function() {
              element.getAttribute("value", function(err, valueAfter) {
                assert.equal(valueBefore, "50%");
                assert.equal(valueAfter, "20%");
                done();
              });
            });
          });
        });
      });
    });
  });
  it("slider value should change by pixels", function(done) {
    h.driver.elementsByTagName("tableCell", function(err, elements) {
      elements[1].click(function(){
        h.driver.elementByTagName("slider", function(err, element) {
          element.getAttribute("value", function(err, valueBefore) {
            var opts = {endX: 15, endY: 10, duration: 0.3, element: element.value};
            h.driver.execute("mobile: swipe", [opts], function() {
              element.getAttribute("value", function(err, valueAfter) {
                assert.equal(valueBefore, "50%");
                assert.equal(valueAfter, "5%");
                done();
              });
            });
          });
        });
      });
    });
  });
});
describeWd('complex tap', function(h) {
  it('should work with custom options', function(done) {
    var tapOpts = {
      tapCount: 1 // how many taps
      , duration: 2.3 // how long
      , touchCount: 3 // how many fingers
      , x: 100 // in pixels from left
      , y: 250 // in pixels from top
    };
    h.driver.execute("mobile: tap", [tapOpts], function(err) {
      should.not.exist(err);
      h.driver.elementByTagName("textview", function(err, el) {
        should.not.exist(err);
        el.text(function(err, text) {
          should.not.exist(err);
          _s.trim(text).should.eql("Now is the time for all good developers to come to serve their country.\n\nNow is the time for all good developers to come to serve their country.");
          done();
        });
      });
    });
  });
  it('should work in relative units', function(done) {
    var tapOpts = {
      tapCount: 1 // how many taps
      , duration: 2.3 // how long
      , touchCount: 3 // how many fingers
      , x: 0.5 // 50% from left of screen
      , y: 0.55 // 55% from top of screen
    };
    h.driver.execute("mobile: tap", [tapOpts], function(err) {
      should.not.exist(err);
      h.driver.elementByTagName("textview", function(err, el) {
        should.not.exist(err);
        el.text(function(err, text) {
          should.not.exist(err);
          _s.trim(text).should.eql("Now is the time for all good developers to come to serve their country.\n\nNow is the time for all good developers to come to serve their country.");
          done();
        });
      });
    });
  });
  it.only('should work with default options', function(done) {
    h.driver.execute("mobile: tap", function(err) {
      should.not.exist(err);
      h.driver.elementByTagName("textview", function(err, el) {
        should.not.exist(err);
        el.text(function(err, text) {
          should.not.exist(err);
          _s.trim(text).should.eql("Now is the time for all good developers to come to serve their country.\n\nNow is the time for all good developers to come to serve their country.");
          done();
        });
      });
    });
  });
});
describeWd('complex tap on element', function(h) {
  it('should work in relative units', function(done) {
    h.driver.elementsByTagName('tableCell', function(err, els) {
      should.not.exist(err);
      var el = els[4];
      var tapOpts = {
        x: 0.5 // in relative width from left
        , y: 0.5 // in relative height from top
        , element: el.value
      };
      h.driver.execute("mobile: tap", [tapOpts], function(err) {
        should.not.exist(err);
        h.driver.elementByTagName("textview", function(err, el) {
          should.not.exist(err);
          el.text(function(err, text) {
            should.not.exist(err);
            _s.trim(text).should.eql("Now is the time for all good developers to come to serve their country.\n\nNow is the time for all good developers to come to serve their country.");
            done();
          });
        });
      });
    });
  });
  it('should work in pixels', function(done) {
    h.driver.elementsByTagName('tableCell', function(err, els) {
      should.not.exist(err);
      var el = els[4];
      var tapOpts = {
        x: 150 // in pixels from left
        , y: 30 // in pixels from top
        , element: el.value
      };
      h.driver.execute("mobile: tap", [tapOpts], function(err) {
        should.not.exist(err);
        h.driver.elementByTagName("textview", function(err, el) {
          should.not.exist(err);
          el.text(function(err, text) {
            should.not.exist(err);
            _s.trim(text).should.eql("Now is the time for all good developers to come to serve their country.\n\nNow is the time for all good developers to come to serve their country.");
            done();
          });
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>var buster = require("buster");
var assert = buster.assert;
var refute = buster.refute;

var http = require("http");
var bCapServ = require("./../../lib/buster-capture-server");
var h = require("./../test-helper");

buster.testRunner.timeout = 1000;
buster.testCase("Integration", {
    setUp: function (done) {
        var self = this;

        this.httpServer = http.createServer(function (req, res) {
            res.writeHead(h.NO_RESPONSE_STATUS_CODE);
            res.end();
        });
        this.httpServer.listen(h.SERVER_PORT, done);

        this.reqConns = [];
        this.httpServer.on("connection", function (socket) {
            self.reqConns.push(socket);
        });

        this.server = bCapServ.create();
        this.server.attach(this.httpServer);
    },

    tearDown: function (done) {
        // Ensure all connections are nuked out of orbit
        this.reqConns.forEach(function (c) { c.destroy(); });

        this.httpServer.on("close", done);
        this.httpServer.close();
    },

    "test one browser": function (done) {
        var self = this;

        h.capture(this.server, function (slave, phantom) {
            assert.equals(self.server.slaves.length, 1);
            phantom.kill(function () {
                assert.equals(self.server.slaves.length, 0);
                done();
            });
        });
    },

    "test multiple browsers": function (done) {
        var self = this;

        h.capture(this.server, function (slave, phantom) {
            assert.equals(self.server.slaves.length, 1);

            h.capture(self.server, function (slave, phantom2) {
                assert.equals(self.server.slaves.length, 2);

                phantom.kill(function () {
                    assert.equals(self.server.slaves.length, 1);

                    phantom2.kill(function () {
                        assert.equals(self.server.slaves.length, 0);
                        done();
                    });
                });
            });
        });
    }
});<commit_msg>Adding integration test for session event publishing.<commit_after>var buster = require("buster");
var assert = buster.assert;
var refute = buster.refute;

var http = require("http");
var bCapServ = require("./../../lib/buster-capture-server");
var h = require("./../test-helper");

buster.testRunner.timeout = 1000;
buster.testCase("Integration", {
    setUp: function (done) {
        var self = this;

        this.httpServer = http.createServer(function (req, res) {
            res.writeHead(h.NO_RESPONSE_STATUS_CODE);
            res.end();
        });
        this.httpServer.listen(h.SERVER_PORT, done);

        this.reqConns = [];
        this.httpServer.on("connection", function (socket) {
            self.reqConns.push(socket);
        });

        this.server = bCapServ.create();
        this.server.attach(this.httpServer);
    },

    tearDown: function (done) {
        // Ensure all connections are nuked out of orbit
        this.reqConns.forEach(function (c) { c.destroy(); });

        this.httpServer.on("close", done);
        this.httpServer.close();
    },

    "test one browser": function (done) {
        var self = this;

        h.capture(this.server, function (slave, phantom) {
            assert.equals(self.server.slaves.length, 1);
            phantom.kill(function () {
                assert.equals(self.server.slaves.length, 0);
                done();
            });
        });
    },

    "test multiple browsers": function (done) {
        var self = this;

        h.capture(this.server, function (slave, phantom) {
            assert.equals(self.server.slaves.length, 1);

            h.capture(self.server, function (slave, phantom2) {
                assert.equals(self.server.slaves.length, 2);

                phantom.kill(function () {
                    assert.equals(self.server.slaves.length, 1);

                    phantom2.kill(function () {
                        assert.equals(self.server.slaves.length, 0);
                        done();
                    });
                });
            });
        });
    },

    "test posting events from session": function (done) {
        var self = this;
        h.capture(this.server, function (slave, phantom) {
            var session = self.server.createSession({
                resourceSet: {
                    resources: {
                        "/test.js": {
                            content: 'buster.publish("/some/event", 123);'
                        }
                    },
                    load: ["/test.js"]
                }
            });

            session.subscribe("/some/event", function (data) {
                assert.equals(data, 123);
                phantom.kill(done);
            });
        });
    }
});<|endoftext|>