<commit_before>if (typeof require != "undefined") {
    require.paths.unshift(__dirname + "/../lib/");
    require.paths.unshift(__dirname + "/../deps/buster-util/lib/");
    var testCase = require("test_case").testCase;
    var assert = require("assert");
    var buster = { format: require("ascii_format") };
}

(function () {
    function F() {}

    var create = Object.create || function (object) {
        F.prototype = object;
        return new F();
    };

    testCase("AsciiFormatTest", {
        "should format strings with quotes": function () {
            assert.equal('"A string"', buster.format.ascii("A string"));
        },

        "should format booleans without quotes": function () {
            assert.equal("true", buster.format.ascii(true));
            assert.equal("false", buster.format.ascii(false));
        },

        "should format null and undefined without quotes": function () {
            assert.equal("null", buster.format.ascii(null));
            assert.equal("undefined", buster.format.ascii(undefined));
        },

        "should format numbers without quotes": function () {
            assert.equal("3", buster.format.ascii(3));
            assert.equal("3987.56", buster.format.ascii(3987.56));
            assert.equal("-980", buster.format.ascii(-980.0));
            assert.equal("NaN", buster.format.ascii(NaN));
            assert.equal("Infinity", buster.format.ascii(Infinity));
            assert.equal("-Infinity", buster.format.ascii(-Infinity));
        },

        "should format regexp using toString": function () {
            assert.equal("/[a-zA-Z0-9]+\\.?/", buster.format.ascii(/[a-zA-Z0-9]+\.?/));
        },

        "should format functions with name": function () {
            assert.equal("function doIt() {}", buster.format.ascii(function doIt() {}));
        },

        "should format functions without name": function () {
            assert.equal("function () {}", buster.format.ascii(function () {}));
        },

        "should format functions with display name": function () {
            function doIt() {}
            doIt.displayName = "ohHai";

            assert.equal("function ohHai() {}", buster.format.ascii(doIt));
        },

        "should shorten functions with long bodies": function () {
            function doIt() {
                var i;
                function hey() {}
                for (; i < 10; i++) {
                }
            }

            assert.equal("function doIt() {}", buster.format.ascii(doIt));
        },

        "should format functions with no name or display name": function () {
            function doIt() {}
            doIt.name = "";

            assert.equal("function doIt() {}", buster.format.ascii(doIt));
        },

        "should format arrays": function () {
            function ohNo() { return "Oh yes!"; }

            var array = ["String", 123, /a-z/, null];

            var str = buster.format.ascii(array);
            assert.equal('["String", 123, /a-z/, null]', str);

            str = buster.format.ascii([ohNo, array]);
            assert.equal('[function ohNo() {}, ["String", 123, /a-z/, null]]', str);
        },

        "should not trip on circular arrays": function () {
            var array = ["String", 123, /a-z/];
            array.push(array);

            var str = buster.format.ascii(array);
            assert.equal('["String", 123, /a-z/, [Circular]]', str);
        },

        "should format object": function () {
            var object = {
                id: 42,
                hello: function () {},
                prop: "Some",
                more: "properties",
                please: "Gimme some more",
                "oh hi": 42,
                seriously: "many properties"
            };

            var expected = "{\n  hello: function () {},\n  id: 42,\n  " +
                "more: \"properties\",\n  \"oh hi\": 42,\n  please: " +
                "\"Gimme some more\",\n  prop: \"Some\"," +
                "\n  seriously: \"many properties\"\n}";

            assert.equal(expected, buster.format.ascii(object));
        },

        "should format short object on one line": function () {
            var object = {
                id: 42,
                hello: function () {},
                prop: "Some"
            };

            var expected = "{ hello: function () {}, id: 42, prop: \"Some\" }";
            assert.equal(expected, buster.format.ascii(object));
        },

        "should format nested object": function () {
            var object = {
                id: 42,
                hello: function () {},
                prop: "Some",
                obj: {
                    num: 23,
                    string: "Here you go you little mister"
                }
            };

            var expected = "{\n  hello: function () {},\n  id: 42,\n  obj" +
                ": { num: 23, string: \"Here you go you little mister\"" +
                " },\n  prop: \"Some\"\n}";

            assert.equal(expected, buster.format.ascii(object));
        },

        "should include constructor if known and not Object": function () {
            function Person(name) {
                this.name = name;
            }

            var person = new Person("Christian");

            assert.equal("[Person] { name: \"Christian\" }", buster.format.ascii(person));
        },

        "should not include one letter constructors": function () {
            function F(name) {
                this.name = name;
            }

            var person = new F("Christian");

            assert.equal("{ name: \"Christian\" }", buster.format.ascii(person));
        },

        "should include one letter constructors when configured to do so": function () {
            function C(name) {
                this.name = name;
            }

            var person = new C("Christian");
            var formatter = create(buster.format);
            formatter.excludeConstructors = [];

            assert.equal("[C] { name: \"Christian\" }", formatter.ascii(person));
        },

        "should exclude constructors when configured to do so": function () {
            function Person(name) {
                this.name = name;
            }

            var person = new Person("Christian");
            var formatter = create(buster.format);
            formatter.excludeConstructors = ["Person"];

            assert.equal("{ name: \"Christian\" }", formatter.ascii(person));
        },

        "should exclude constructors by pattern when configured to do so": function () {
            function Person(name) {
                this.name = name;
            }

            function Ninja(name) {
                this.name = name;
            }

            function Pervert(name) {
                this.name = name;
            }

            var person = new Person("Christian");
            var ninja = new Ninja("Haruhachi");
            var pervert = new Pervert("Mr. Garrison");
            var formatter = create(buster.format);
            formatter.excludeConstructors = [/^Per/];

            assert.equal("{ name: \"Christian\" }", formatter.ascii(person));
            assert.equal("[Ninja] { name: \"Haruhachi\" }", formatter.ascii(ninja));
            assert.equal("{ name: \"Mr. Garrison\" }", formatter.ascii(pervert));
        },

        "should not trip on circular formatting": function () {
            var object = {};
            object.foo = object;

            assert.equal("{ foo: [Circular] }", buster.format.ascii(object));
        },

        "should not trip on indirect circular formatting": function () {
            var object = { someProp: {} };
            object.someProp.foo = object;

            assert.equal("{ someProp: { foo: [Circular] } }", buster.format.ascii(object));
        }
    });

    if (typeof document != "undefined") {
        testCase("AsciiFormatDOMElementTest", {
            "should format dom element": function () {
                var element = document.createElement("div");

                assert.equal("

", buster.format.ascii(element));
            },

            "should format dom element with attributes": function () {
                var element = document.createElement("div");
                element.className = "hey there";
                element.id = "ohyeah";

                assert.equal("

",
                             buster.format.ascii(element));
            },

            "should format dom element with content": function () {
                var element = document.createElement("div");
                element.innerHTML = "Oh hi!";

                assert.equal("
Oh hi!
",
                             buster.format.ascii(element));
            },

            "should truncate dom element content": function () {
                var element = document.createElement("div");
                element.innerHTML = "Oh hi! I'm Christian, and this is a lot of content";

                assert.equal("
Oh hi! I'm Christian
",
                             buster.format.ascii(element));
            },

            "should include attributes and truncated content": function () {
                var element = document.createElement("div");
                element.id = "anid";
                element.lang = "en"
                element.innerHTML = "Oh hi! I'm Christian, and this is a lot of content";

                assert.equal("
Oh hi! I'm Christian
",
                             buster.format.ascii(element));
            }
        });
    }
}());<commit_msg>Better test<commit_after>if (typeof require != "undefined") {
    require.paths.unshift(__dirname + "/../lib/");
    require.paths.unshift(__dirname + "/../deps/buster-util/lib/");
    var testCase = require("test_case").testCase;
    var assert = require("assert");
    var buster = { format: require("ascii_format") };
}

(function () {
    function F() {}

    var create = Object.create || function (object) {
        F.prototype = object;
        return new F();
    };

    testCase("AsciiFormatTest", {
        "should format strings with quotes": function () {
            assert.equal('"A string"', buster.format.ascii("A string"));
        },

        "should format booleans without quotes": function () {
            assert.equal("true", buster.format.ascii(true));
            assert.equal("false", buster.format.ascii(false));
        },

        "should format null and undefined without quotes": function () {
            assert.equal("null", buster.format.ascii(null));
            assert.equal("undefined", buster.format.ascii(undefined));
        },

        "should format numbers without quotes": function () {
            assert.equal("3", buster.format.ascii(3));
            assert.equal("3987.56", buster.format.ascii(3987.56));
            assert.equal("-980", buster.format.ascii(-980.0));
            assert.equal("NaN", buster.format.ascii(NaN));
            assert.equal("Infinity", buster.format.ascii(Infinity));
            assert.equal("-Infinity", buster.format.ascii(-Infinity));
        },

        "should format regexp using toString": function () {
            assert.equal("/[a-zA-Z0-9]+\\.?/", buster.format.ascii(/[a-zA-Z0-9]+\.?/));
        },

        "should format functions with name": function () {
            assert.equal("function doIt() {}", buster.format.ascii(function doIt() {}));
        },

        "should format functions without name": function () {
            assert.equal("function () {}", buster.format.ascii(function () {}));
        },

        "should format functions with display name": function () {
            function doIt() {}
            doIt.displayName = "ohHai";

            assert.equal("function ohHai() {}", buster.format.ascii(doIt));
        },

        "should shorten functions with long bodies": function () {
            function doIt() {
                var i;
                function hey() {}
                for (; i < 10; i++) {
                }
            }

            assert.equal("function doIt() {}", buster.format.ascii(doIt));
        },

        "should format functions with no name or display name": function () {
            function doIt() {}
            doIt.name = "";

            assert.equal("function doIt() {}", buster.format.ascii(doIt));
        },

        "should format arrays": function () {
            function ohNo() { return "Oh yes!"; }

            var array = ["String", 123, /a-z/, null];

            var str = buster.format.ascii(array);
            assert.equal('["String", 123, /a-z/, null]', str);

            str = buster.format.ascii([ohNo, array]);
            assert.equal('[function ohNo() {}, ["String", 123, /a-z/, null]]', str);
        },

        "should not trip on circular arrays": function () {
            var array = ["String", 123, /a-z/];
            array.push(array);

            var str = buster.format.ascii(array);
            assert.equal('["String", 123, /a-z/, [Circular]]', str);
        },

        "should format object": function () {
            var object = {
                id: 42,
                hello: function () {},
                prop: "Some",
                more: "properties",
                please: "Gimme some more",
                "oh hi": 42,
                seriously: "many properties"
            };

            var expected = "{\n  hello: function () {},\n  id: 42,\n  " +
                "more: \"properties\",\n  \"oh hi\": 42,\n  please: " +
                "\"Gimme some more\",\n  prop: \"Some\"," +
                "\n  seriously: \"many properties\"\n}";

            assert.equal(expected, buster.format.ascii(object));
        },

        "should format short object on one line": function () {
            var object = {
                id: 42,
                hello: function () {},
                prop: "Some"
            };

            var expected = "{ hello: function () {}, id: 42, prop: \"Some\" }";
            assert.equal(expected, buster.format.ascii(object));
        },

        "should format nested object": function () {
            var object = {
                id: 42,
                hello: function () {},
                prop: "Some",
                obj: {
                    num: 23,
                    string: "Here you go you little mister"
                }
            };

            var expected = "{\n  hello: function () {},\n  id: 42,\n  obj" +
                ": { num: 23, string: \"Here you go you little mister\"" +
                " },\n  prop: \"Some\"\n}";

            assert.equal(expected, buster.format.ascii(object));
        },

        "should include constructor if known and not Object": function () {
            function Person(name) {
                this.name = name;
            }

            var person = new Person("Christian");

            assert.equal("[Person] { name: \"Christian\" }", buster.format.ascii(person));
        },

        "should not include one letter constructors": function () {
            function F(name) {
                this.name = name;
            }

            var person = new F("Christian");

            assert.equal("{ name: \"Christian\" }", buster.format.ascii(person));
        },

        "should include one letter constructors when configured to do so": function () {
            function C(name) {
                this.name = name;
            }

            var person = new C("Christian");
            var formatter = create(buster.format);
            formatter.excludeConstructors = [];

            assert.equal("[C] { name: \"Christian\" }", formatter.ascii(person));
        },

        "should exclude constructors when configured to do so": function () {
            function Person(name) {
                this.name = name;
            }

            var person = new Person("Christian");
            var formatter = create(buster.format);
            formatter.excludeConstructors = ["Person"];

            assert.equal("{ name: \"Christian\" }", formatter.ascii(person));
        },

        "should exclude constructors by pattern when configured to do so": function () {
            function Person(name) {
                this.name = name;
            }

            function Ninja(name) {
                this.name = name;
            }

            function Pervert(name) {
                this.name = name;
            }

            var person = new Person("Christian");
            var ninja = new Ninja("Haruhachi");
            var pervert = new Pervert("Mr. Garrison");
            var formatter = create(buster.format);
            formatter.excludeConstructors = [/^Per/];

            assert.equal("{ name: \"Christian\" }", formatter.ascii(person));
            assert.equal("[Ninja] { name: \"Haruhachi\" }", formatter.ascii(ninja));
            assert.equal("{ name: \"Mr. Garrison\" }", formatter.ascii(pervert));
        },

        "should not trip on circular formatting": function () {
            var object = {};
            object.foo = object;

            assert.equal("{ foo: [Circular] }", buster.format.ascii(object));
        },

        "should not trip on indirect circular formatting": function () {
            var object = { someProp: {} };
            object.someProp.foo = object;

            assert.equal("{ someProp: { foo: [Circular] } }", buster.format.ascii(object));
        }
    });

    if (typeof document != "undefined") {
        testCase("AsciiFormatDOMElementTest", {
            "should format dom element": function () {
                var element = document.createElement("div");

                assert.equal("

", buster.format.ascii(element));
            },

            "should format dom element with attributes": function () {
                var element = document.createElement("div");
                element.className = "hey there";
                element.id = "ohyeah";
                var str = buster.format.ascii(element);

                assert.ok(/
<\/div>/.test(str));
                assert.ok(/class="hey there"/.test(str));
                assert.ok(/id="ohyeah"/.test(str));
            },

            "should format dom element with content": function () {
                var element = document.createElement("div");
                element.innerHTML = "Oh hi!";

                assert.equal("
Oh hi!
",
                             buster.format.ascii(element));
            },

            "should truncate dom element content": function () {
                var element = document.createElement("div");
                element.innerHTML = "Oh hi! I'm Christian, and this is a lot of content";

                assert.equal("
Oh hi! I'm Christian
",
                             buster.format.ascii(element));
            },

            "should include attributes and truncated content": function () {
                var element = document.createElement("div");
                element.id = "anid";
                element.lang = "en"
                element.innerHTML = "Oh hi! I'm Christian, and this is a lot of content";
                var str = buster.format.ascii(element);

                assert.ok(/
Oh hi! I'm Christian<\/div>/.test(str));
                assert.ok(/lang="en"/.test(str));
                assert.ok(/id="anid"/.test(str));
            }
        });
    }
}());<|endoftext|>


<commit_before>var Mechanize = require('../lib/mechanize');

Mechanize.newAgent().
  get({uri: 'http://www.google.com'}, function (err, page) {
    //console.log(require('sys').inspect(page));
    console.log(require('sys').inspect(page.response.headers['content-type']));
    console.log(page.responseHeaderCharset);
  });
<commit_msg>log page contents<commit_after>var Mechanize = require('../lib/mechanize');

Mechanize.newAgent().
  get({uri: 'http://www.google.com'}, function (err, page) {
    console.log(require('sys').inspect(page));
  });
<|endoftext|>


<commit_before>/*
 * bucketdb
 *
 * Copyright(c) 2015 André König <andre.koenig@posteo.de>
 * MIT Licensed
 *
 */

/**
 * @author André König <andre.koenig@posteo.de>
 *
 */

'use strict';

var fs = require('fs');
var path = require('path');

var storage = require('../')(require('path').join(process.cwd(), 'database'));

var expect = require('expect.js');

describe('The storage layer should provide a factory that is capable', function suite () {

    var db = path.join(process.cwd(), 'bucketdb');

    before(storage.destroy);

    it('to create a LevelDB instance', function test (done) {
        storage('foo');

        function onStat (err) {
            expect(err).to.be(null);

            done();
        }

        fs.stat(db, onStat);
    });
});
<commit_msg>specs: test fix<commit_after>/*
 * bucketdb
 *
 * Copyright(c) 2015 André König <andre.koenig@posteo.de>
 * MIT Licensed
 *
 */

/**
 * @author André König <andre.koenig@posteo.de>
 *
 */

'use strict';

var fs = require('fs');
var path = require('path');

var storage = require('../')(require('path').join(process.cwd(), 'database'));

var expect = require('expect.js');

describe('The storage layer should provide a factory that is capable', function suite () {

    var db = path.join(process.cwd(), 'database');

    before(storage.destroy);

    it('to create a LevelDB instance', function test (done) {
        storage('foo');

        function onStat (err) {
            expect(err).to.be(null);

            done();
        }

        fs.stat(db, onStat);
    });
});
<|endoftext|>


<commit_before>#! /usr/bin/env node

// Create a new, empty imp project, or reinitialize and existing one

var program = require("commander");
var prompt = require("cli-prompt");
var fs = require("fs");

var ImpConfig = require("../lib/impConfig.js");
var config = new ImpConfig();

var imp;

program
    .option("-f, --force", "Overwrites existing .impconfig file")
    .option("-k, --keep [options]", "Prevents code files from being overwritten during initialization")
    .option("-g, --global", "Uses your global API key and prevents a local API key from being written to .impconfig")

    .on("--help", function() {
        console.log("  Usage:");
        console.log("");
        console.log("    imp new -k\t\tAgent and device code files will not be overwritten");
        console.log("    imp new -k device\tDevice code file will not be overwritten");
        console.log("    imp new -k agent\tAgent code file will not be overwritten");
    });

program.parse(process.argv);

function apiKeyPrompt(apiKey, next) {
    if ("global" in program) {
        // If 'apiKey' isn't set in the global config, log error and return
        if (!config.getGlobal("apiKey")) {
            console.log("ERROR: Global Build API key is not set - run 'imp setup' then try 'imp init' again");
            return;
        }

        imp = config.createImpWithConfig();
        imp.getDevices({ "device_id" : "garbage" }, function(err, data) {
            if (err) {
                // Clear API key and try again
                imp.apiKey = null;
                console.log("ERROR: Invalid Build API key");
                apiKeyPrompt(apiKey, next);
                return;
            }

            next();
        });

        return;
    }

    var promptText = "Build API key";
    if (apiKey) {
        promptText += " (" + apiKey + "): ";
    } else {
        promptText += ": ";
    }

    prompt(promptText, function(val) {
        if (apiKey && !val) val = apiKey;
        config.setLocal("apiKey", val);

        imp = config.createImpWithConfig();
        imp.getDevices({ "device_id" : "garbage" }, function(err, data) {
            if (err) {
                // Clear API key and try again
                imp.apiKey = null;
                console.log("ERROR: Invalid Build API key");
                apiKeyPrompt(apiKey, next);
                return;
            }

            next();
        });
    });
}

function modelPrompt(next) {
    prompt("Model ID or name: ", function(val) {
        if (!val) {
            modelPrompt(next);
            return;
        }

        // Try to get model by ID
        imp.getModel(val, function(err, data) {
            if (!err) {
                prompt("Found an existing model: '" + data.model.name + "'. Use this? (y/n) ", function(confirm) {
                    if (confirm && confirm.toLowerCase()[0] != "y") {
                        modelPrompt(next);
                        return;
                    }

                    config.setLocal("modelId", data.model.id);
                    config.setLocal("modelName", data.model.name);
                    next();
                    return;
                });
            } else {
                // An error means no model_id match was found
                imp.getModels({ "name": val }, function(err, data) {
                    if (err) {
                        console.log("ERROR: Could not match the model ID");
                        return;
                    }

                    // See if we found a matching result
                    var foundMatch = false;
                    for (var i = 0 ; i < data.models.length ; i++) {
                        if (data.models[i].name.toLowerCase() == val.toLowerCase()) {
                            foundMatch = true;
                            break;
                        }
                    }

                    if (foundMatch) {
                        prompt("Found an existing model: '" + data.models[i].name + "'. Use this? (y/n) ", function(confirm){
                            if (confirm && confirm.toLowerCase()[0] != "y") {
                                modelPrompt(next);
                                return;
                            }

                            config.setLocal("modelId", data.models[i].id);
                            config.setLocal("modelName", data.models[i].name);
                            next();
                            return;
                        });
                    } else {
                        prompt("Create model '" + val + "'? (y/n) ", function(confirm) {
                            if (confirm && confirm.toLowerCase()[0] != "y") {
                                modelPrompt(next);
                                return;
                            }

                            imp.createModel(val, function(err, data) {
                                if (err) {
                                    console.log("ERROR: Could not create model");
                                    return;
                                }

                                config.setLocal("modelName", data.model.name);
                                config.setLocal("modelId", data.model.id);
                                next();
                            });

                            return;
                        });
                    }
                });
            }
        });
    });
}

function getDevices(next) {
    var modelId = config.getLocal("modelId");
    var modelName = config.getLocal("modelName");
    if (modelId == null) {
        next();
        return;
    }

    imp.getDevices({ "model_id": modelId }, function(err, data) {
        if (err) {
            console.log("WARNING: Could not fetch devices assigned to model '" + modelName + "'");
            next();
        }

        var devices = [];
        for (var i = 0 ; i < data.devices.length ; i++) {
            if (data.devices[i].model_id == modelId) {
                devices.push(data.devices[i].id);
            }
        }

        // Save current list of model's devices locally
        config.setLocal("devices", devices);

        // Display number of devices (if any) associated with this model
        var devicesText = devices.length == 1 ? "device" : "devices";
        var devicesLen = devices.length == 0 ? "no" : devices.len;
        console.log("Found " + devicesLen + " " + devicesText + " associated with model '" + modelName + "'");
        next();
    });
}

function fileNamePrompt(next) {
    var modelName = config.getLocal("modelName");
    var baseFileName = modelName.split(" ").join("_").toLowerCase();
    var defaultDeviceFileName = config.getLocal("deviceFile") || (baseFileName + ".device.nut");
    var defaultAgentFileName = config.getLocal("agentFile") || (baseFileName + ".agent.nut");

    prompt.multi([
        {
            label: "Device code file (" + defaultDeviceFileName + ")",
            key: "deviceFile"
        },
        {
            label: "Agent code file (" + defaultAgentFileName + ")",
            key: "agentFile"
        }
    ], function(data){
        config.setLocal("deviceFile", data.deviceFile || defaultDeviceFileName);
        config.setLocal("agentFile", data.agentFile || defaultAgentFileName);
        next();
    });
}

function finalize() {
    var deviceCode = "";
    var agentCode = "";
    var modelId = config.getLocal("modelId");
    var modelName = config.getLocal("modelName");
    var agentFile = config.getLocal("agentFile");
    var deviceFile = config.getLocal("deviceFile");

    if (modelId != null) {
        imp.getModelRevisions(modelId, null, function(err, data) {
            if (err) {
                console.log("ERROR: Could not fetch code revisions for model '" + modelName + "'");
                return;
            }

            if (data.revisions.length == 0) {
                config.saveLocalConfig(function(err) {
                    if (err) {
                        console.log("ERROR: " + err);
                        return;
                    }

                    console.log("Model '" + modelName + "' initialized. To add a device run: 'imp devices -a <deviceID>'");
                });

                return;
            }

            imp.getModelRevision(modelId, data.revisions[0].version, function(err, data) {
                if (err) {
                    console.log("ERROR: Could not fetch the latest build for model '" + modelName + "'");
                    return;
                }

                deviceCode = data.revision.device_code;
                agentCode = data.revision.agent_code;

                if ("keep" in program && keep === true) {
                    // Don't overwrite any saved code
                } else if ("keep" in program && program.keep == "device") {
                    // Only overwrite the agent code
                    fs.writeFile(agentFile, agentCode);
                } else if ("keep" in program && program.keep == "agent") {
                    // Only overwrite the device code
                    fs.writeFile(deviceFile, deviceCode);
                } else {
                    // Overwrite both agent code and device code
                    fs.writeFile(deviceFile, deviceCode);
                    fs.writeFile(agentFile, agentCode);
                }

                config.saveLocalConfig(function(err) {
                    if (err) {
                        console.log("ERROR: " + err);
                        return;
                    }

                    console.log("Model '" + modelName + "' initialized. To add a device run: 'imp devices -a <deviceID>'");
                });
            });
        });
    } else {
        imp.createModel(modelName, function(err, data) {
            if (err) {
                console.log("ERROR: Could not create model");
                return;
            }

            config.setLocal("modelId", data.model.id);

            fs.writeFile(deviceFile, deviceCode);
            fs.writeFile(agentFile, agentCode);

            config.saveLocalConfig(function(err) {
                if (err) {
                    console.log("ERROR: " + err);
                    return;
                }

                console.log("Model '" + modelName + "' initialized. To add a device run: 'imp devices -a <deviceId>'");
            });
        });
    }
}

config.init(null, function() {
    // Make sure this folder doesn't already have a config file
    if (this.getLocalConfig() && !("force" in program)) {
        console.log("ERROR: .impconfig already exists. Specify '-f' to re-initialize model");
        return;
    }

    apiKeyPrompt(this.get("apiKey"), function() {
        modelPrompt(function() {
            getDevices(function() {
                fileNamePrompt(function() {
                    finalize();
                });
            });
        });
    });
}.bind(config));
<commit_msg>Update imp-init.js<commit_after>#! /usr/bin/env node

// Create a new, empty imp project, or reinitialize and existing one

var program = require("commander");
var prompt = require("cli-prompt");
var fs = require("fs");

var ImpConfig = require("../lib/impConfig.js");
var config = new ImpConfig();

var imp;

program
    .option("-f, --force", "Overwrites existing .impconfig file")
    .option("-g, --global", "Uses your global API key and prevents a local API key from being written to .impconfig")
    .option("-k, --keep [options]", "Prevents code files from being overwritten during initialization")
    .option("-t, --title <projectName>", "Create a new project and model with the supplied name")

    .on("--help", function() {
        console.log("  Usage:");
        console.log("");
        console.log("    imp init -g\t\tAgent and device code files will not be overwritten");
        console.log("    imp init -k\t\tAgent and device code files will not be overwritten");
        console.log("    imp init -k device\tDevice code file will not be overwritten");
        console.log("    imp init -k agent\tAgent code file will not be overwritten");
    });

program.parse(process.argv);

function apiKeyPrompt(apiKey, next) {
    if ("global" in program) {
        // If 'apiKey' isn't set in the global config, log error and return
        if (!config.getGlobal("apiKey")) {
            console.log("ERROR: Global Build API key is not set. Run 'imp setup' then try 'imp new' again");
            return;
        }

        imp = config.createImpWithConfig();
        // Use a false value to test the API key
        imp.getDevices({ "device_id" : "garbage" }, function(err, data) {
            if (err) {
                // API key is wrong, so ask again
                imp.apiKey = null;
                console.log("ERROR: Invalid Build API key");
                apiKeyPrompt(apiKey, next);
                return;
            }

            next();
        });

        return;
    }

    // Display/request local Build API key
    var promptText = "Build API key";
    if (apiKey) {
        promptText += " (" + apiKey + "): ";
    } else {
        promptText += ": ";
    }

    prompt(promptText, function(val) {
        if (apiKey && !val) val = apiKey;
        config.setLocal("apiKey", val);
        imp = config.createImpWithConfig();

        // Use a false value to test the API key
        imp.getDevices({ "device_id" : "garbage" }, function(err, data) {
            if (err) {
                // API key is wrong, so ask again
                imp.apiKey = null;
                console.log("ERROR: Invalid Build API key");
                apiKeyPrompt(apiKey, next);
                return;
            }

            next();
        });
    });
}

function modelPrompt(next) {
    prompt("Model ID or name: ", function(val) {
        if (!val) {
            modelPrompt(next);
            return;
        }

        // Assume the user has passed in an ID - check if it exists
        imp.getModel(val, function(err, data) {
            if (!err) {
                // The user DID supply a model ID and it matches an existing one
                prompt("There is an existing model, '" + data.model.name + "', with the ID you supplied. Use this? (y/n) ", function(confirm) {
                    if (confirm && confirm.toLowerCase()[0] != "y") {
                        // User doesn't want to use the existing model,
                        // so force them to choose a new name
                        modelPrompt(next);
                        return;
                    }

                    config.setLocal("modelId", data.model.id);
                    config.setLocal("modelName", data.model.name);
                    next();
                    return;
                });
            } else {
                // An error means there was no match to a model ID,
                // so perhaps a name was supplied: compare it to the list of models
                imp.getModels({ "name": val }, function(err, data) {
                    if (err) {
                        console.log("ERROR: Could not get a list of your models from the impCloud");
                        return;
                    }

                    // See if the supplied model name matches an existing one
                    var foundMatch = false;
                    for (var i = 0 ; i < data.models.length ; i++) {
                        if (data.models[i].name.toLowerCase() == val.toLowerCase()) {
                            foundMatch = true;
                            break;
                        }
                    }

                    if (foundMatch) {
                        // The user has supplied a name that already exists
                        prompt("There is an existing model, '" + data.models[i].name + "', with the ID you supplied. Use this? (y/n) ", function(confirm){
                            if (confirm && confirm.toLowerCase()[0] != "y") {
                                // User doesn't want to use the existing model,
                                // so force them to choose a new name
                                modelPrompt(next);
                                return;
                            }

                            config.setLocal("modelId", data.models[i].id);
                            config.setLocal("modelName", data.models[i].name);
                            next();
                            return;
                        });
                    } else {
                        // The user has supplied a name that doesn't exist, so
                        // create a new model with that name
                        prompt("Create model '" + val + "'? (y/n) ", function(confirm) {
                            if (confirm && confirm.toLowerCase()[0] != "y") {
                                modelPrompt(next);
                                return;
                            }

                            imp.createModel(val, function(err, data) {
                                if (err) {
                                    console.log("ERROR: Could not create model '" + data.model.name + "'");
                                    return;
                                }

                                config.setLocal("modelName", data.model.name);
                                config.setLocal("modelId", data.model.id);
                                next();
                            });

                            return;
                        });
                    }
                });
            }
        });
    });
}

function newModelPrompt(next) {
    // We have a model name, parsed as 'program.title'
    imp.getModels({ "name": program.title }, function(err, data) {
        if (err) {
            console.log("ERROR: Could not get a list of your models from the impCloud");
            return;
        }

        // See if the supplied model name matches an existing one
        var foundMatch = false;
        for (var i = 0 ; i < data.models.length ; i++) {
            if (data.models[i].name.toLowerCase() == program.title.toLowerCase()) {
                foundMatch = true;
                break;
            }
        }

        if (foundMatch) {
            // The user has supplied a name that already exists
            prompt("You already have a model with the name '" + data.models[i].name + "'. Use this? (y/n) ", function(confirm) {
                if (confirm && confirm.toLowerCase()[0] != "y") {
                    // User doesn't want to use the existing model, so get a new name
                    modelPrompt(next);
                    return;
                }

                config.setLocal("modelId", data.models[i].id);
                config.setLocal("modelName", data.models[i].name);
                next();
                return;
            });
        } else {
            // The user has supplied a name that doesn't exist, so
            // create a new model with that name
            imp.createModel(program.title, function(err, data) {
                if (err) {
                    console.log("ERROR: Could not create model '" + program.title + "'");
                    return;
                }

                config.setLocal("modelName", data.model.name);
                config.setLocal("modelId", data.model.id);
                next();
            });

            return;
        }
    });
}

function getDevices(next) {
    var modelId = config.getLocal("modelId");
    var modelName = config.getLocal("modelName");
    if (modelId == null) {
        next();
        return;
    }

    imp.getDevices({ "model_id": modelId }, function(err, data) {
        if (err) {
            console.log("ERROR: Could not fetch a list of devices assigned to model '" + modelName + "'");
            next();
        }

        var devices = [];
        for (var i = 0 ; i < data.devices.length ; i++) {
            if (data.devices[i].model_id == modelId) {
                devices.push(data.devices[i].id);
            }
        }

        // Save current list of model's devices locally
        config.setLocal("devices", devices);

        // Display number of devices (if any) associated with this model
        var devicesText = devices.length == 1 ? "device" : "devices";
        var devicesLen = devices.length == 0 ? "no" : devices.length;
        console.log("Found " + devicesLen + " " + devicesText + " associated with model '" + modelName + "'");
        next();
    });
}

function fileNamePrompt(next) {
    var modelName = config.getLocal("modelName");
    var baseFileName = modelName.split(" ").join("_").toLowerCase();
    var defaultDeviceFileName = config.getLocal("deviceFile") || (baseFileName + ".device.nut");
    var defaultAgentFileName = config.getLocal("agentFile") || (baseFileName + ".agent.nut");

    prompt.multi([
        {
            label: "Device code file (" + defaultDeviceFileName + ")",
            key: "deviceFile"
        },
        {
            label: "Agent code file (" + defaultAgentFileName + ")",
            key: "agentFile"
        }
    ], function(data){
        config.setLocal("deviceFile", data.deviceFile || defaultDeviceFileName);
        config.setLocal("agentFile", data.agentFile || defaultAgentFileName);
        next();
    });
}

function makeFiles() {
    var deviceCode = "# Device Code";
    var agentCode = "# Agent Code";
    var modelId = config.getLocal("modelId");
    var modelName = config.getLocal("modelName");
    var agentFile = config.getLocal("agentFile");
    var deviceFile = config.getLocal("deviceFile");

    if (modelId != null) {
        if ("keep" in program && keep === true) {
            // Don't overwrite any saved code UNLESS files don't exist
            if (!fs.existsSync(agentFile)) fs.writeFile(agentFile, agentCode);
            if (!fs.existsSync(deviceFile)) fs.writeFile(deviceFile, deviceCode);
        } else if ("keep" in program && program.keep == "device") {
            // Only overwrite the agent code
            fs.writeFile(agentFile, agentCode);
        } else if ("keep" in program && program.keep == "agent") {
            // Only overwrite the device code
            fs.writeFile(deviceFile, deviceCode);
        } else {
            // Overwrite both agent code and device code
            fs.writeFile(deviceFile, deviceCode);
            fs.writeFile(agentFile, agentCode);
        }

        config.saveLocalConfig(function(err) {
            if (err) {
                console.log("ERROR: " + err);
                return;
            }

            console.log("Model '" + modelName + "' initialized. To add a device run: 'imp devices -a <deviceID>'");
        });
    } else {
        console.log("ERROR: Could not create model's code files");
    }
}

function finalize() {
    var deviceCode = "";
    var agentCode = "";
    var modelId = config.getLocal("modelId");
    var modelName = config.getLocal("modelName");
    var agentFile = config.getLocal("agentFile");
    var deviceFile = config.getLocal("deviceFile");

    if (modelId != null) {
        imp.getModelRevisions(modelId, null, function(err, data) {
            if (err) {
                console.log("ERROR: Could not fetch code revisions for model '" + modelName + "'");
                return;
            }

            if (data.revisions.length == 0) {
                config.saveLocalConfig(function(err) {
                    if (err) {
                        console.log("ERROR: " + err);
                        return;
                    }

                    console.log("Model '" + modelName + "' initialized. To add a device run: 'imp devices -a <deviceID>'");
                });

                return;
            }

            imp.getModelRevision(modelId, data.revisions[0].version, function(err, data) {
                if (err) {
                    console.log("ERROR: Could not fetch the latest build for model '" + modelName + "'");
                    return;
                }

                deviceCode = data.revision.device_code;
                agentCode = data.revision.agent_code;

                if ("keep" in program && keep === true) {
                    // Don't overwrite any saved code
                } else if ("keep" in program && program.keep == "device") {
                    // Only overwrite the agent code
                    fs.writeFile(agentFile, agentCode);
                } else if ("keep" in program && program.keep == "agent") {
                    // Only overwrite the device code
                    fs.writeFile(deviceFile, deviceCode);
                } else {
                    // Overwrite both agent code and device code
                    fs.writeFile(deviceFile, deviceCode);
                    fs.writeFile(agentFile, agentCode);
                }

                config.saveLocalConfig(function(err) {
                    if (err) {
                        console.log("ERROR: " + err);
                        return;
                    }

                    console.log("Model '" + modelName + "' initialized. To add a device run: 'imp devices -a <deviceID>'");
                });
            });
        });
    } else {
        imp.createModel(modelName, function(err, data) {
            if (err) {
                console.log("ERROR: Could not create model");
                return;
            }

            config.setLocal("modelId", data.model.id);

            fs.writeFile(deviceFile, deviceCode);
            fs.writeFile(agentFile, agentCode);

            config.saveLocalConfig(function(err) {
                if (err) {
                    console.log("ERROR: " + err);
                    return;
                }

                console.log("Model '" + modelName + "' initialized. To add a device run: 'imp devices -a <deviceId>'");
            });
        });
    }
}

config.init(null, function() {
    // Make sure this folder doesn't already have a config file
    if (this.getLocalConfig() && !("force" in program)) {
        console.log("ERROR: .impconfig already exists. Specify '-f' to re-initialize model");
        return;
    }

    if ("title" in program) {
        // User has supplied a specific model name, so use this rather
        // than ask for one
        apiKeyPrompt(this.get("apiKey"), function() {
            newModelPrompt(function() {
                fileNamePrompt(function() {
                    makeFiles();
                });
            });
        });
    } else {
        apiKeyPrompt(this.get("apiKey"), function() {
            modelPrompt(function() {
                getDevices(function() {
                    fileNamePrompt(function() {
                        finalize();
                    });
                });
            });
        });
    }
}.bind(config));
<|endoftext|>


<commit_before>var templates = require("./templates"),
expect = require("expect.js"),
pc = require(".."),
bindable = require("bindable");

describe("basic", function() {
  var helloTemplate = pc.template(templates.hello),
  person = new bindable.Object({
    name: {
      first: "craig",
      last: "condon"
    }
  });

  it("can load the context", function() {
    var content = helloTemplate.load(person);
    expect(content.toString()).to.be("hello craig condon!");
  });

  describe("bind/unbind", function() {
    var content = helloTemplate.load(person).bind();

    it("can bind to the context", function() {
      expect(content.toString()).to.be("hello craig condon!");
      person.set("name.first", "john");
      expect(content.toString()).to.be("hello john condon!");
      person.set("name.first", "jake");
      expect(content.toString()).to.be("hello jake condon!");
      person.set("name.last", "jeff");
      expect(content.toString()).to.be("hello jake jeff!");
      person.set({ name: { first: "a", last: "b" }})
      expect(content.toString()).to.be("hello a b!");
    });

    it("can unbind the context", function() {
      person.set("name.first", "john");
      expect(content.toString()).to.be("hello john b!");
      content.unbind();
      person.set("name.first", "jake");
      expect(content.toString()).to.be("hello john b!");
    });

    it("can be re-bound", function() {
      content.bind();
      expect(content.toString()).to.be("hello jake b!");
    })

  });

});<commit_msg>check for HTML entities to be encoded<commit_after>var templates = require("./templates"),
expect = require("expect.js"),
pc = require(".."),
bindable = require("bindable");

describe("basic", function() {
  var helloTemplate = pc.template(templates.hello),
  person = new bindable.Object({
    name: {
      first: "craig",
      last: "condon"
    }
  });

  it("can load the context", function() {
    var content = helloTemplate.load(person);
    expect(content.toString()).to.be("hello craig condon!");
  });

  describe("bind/unbind", function() {
    var content = helloTemplate.load(person).bind();

    it("can bind to the context", function() {
      expect(content.toString()).to.be("hello craig condon!");
      person.set("name.first", "john");
      expect(content.toString()).to.be("hello john condon!");
      person.set("name.first", "jake");
      expect(content.toString()).to.be("hello jake condon!");
      person.set("name.last", "jeff");
      expect(content.toString()).to.be("hello jake jeff!");
      person.set({ name: { first: "a", last: "b" }})
      expect(content.toString()).to.be("hello a b!");
    });

    it("can unbind the context", function() {
      person.set("name.first", "john");
      expect(content.toString()).to.be("hello john b!");
      content.unbind();
      person.set("name.first", "jake");
      expect(content.toString()).to.be("hello john b!");
    });

    it("can be re-bound", function() {
      content.bind();
      expect(content.toString()).to.be("hello jake b!");
    });

    it("properly encodes HTML entities", function() {
      person.set("name.first", "<hello>");
      expect(content.toString()).to.be("hello <hello> b!")
    })

  });

});<|endoftext|>


<commit_before>import test from 'tape'
import sinon from 'sinon'
import { Client, https } from '../../src/index'

const moneyBirdClientV2 = new Client('CUSTOM_TOKEN', 'CUSTOM_ADMIN_ID');
const moneyBirdClientV1 = new Client('CUSTOM_TOKEN', 'CUSTOM_ADMIN_ID', 'v1');

test('new client v2 - constructor - token', (t) => {
  t.equal(moneyBirdClientV2.token, 'CUSTOM_TOKEN');
  t.end();
});

test('new client v2 - constructor - administration id', (t) => {
  t.equal(moneyBirdClientV2.administrationId, 'CUSTOM_ADMIN_ID');
  t.end();
});

test('new client v2 - constructor - version', (t) => {
  t.equal(moneyBirdClientV2.version, 'v2');
  t.end();
});

test('new client v2 - constructor - format', (t) => {
  t.equal(moneyBirdClientV2.format, 'json');
  t.end();
});

test('new client v1 - constructor - version', (t) => {
  t.equal(moneyBirdClientV1.version, 'v1');
  t.end();
});

let requestStub;
test('new client - request stubbing', (t) => {
  requestStub = sinon.stub(moneyBirdClientV2, 'request');
  requestStub.returns(Promise.resolve());
  t.end();
});

test('new client - get', (t) => {
  requestStub.reset();
  return moneyBirdClientV2.get('resource_path').then(() => {
    t.equal(moneyBirdClientV2.request.callCount, 1, 'should be called one time');
    t.equal(moneyBirdClientV2.request.getCall(0).args[0], 'get', 'first argument should be get');
    t.equal(moneyBirdClientV2.request.getCall(0).args[1], 'resource_path', 'second argument should be resource path');
    t.end();
  });
});

test('new client - post', (t) => {
  requestStub.reset();
  return moneyBirdClientV2.post('resource_path', { 'body': true }).then(() => {
    t.equal(moneyBirdClientV2.request.callCount, 1, 'should be called one time');
    t.equal(moneyBirdClientV2.request.getCall(0).args[0], 'post', 'first argument should be post');
    t.equal(moneyBirdClientV2.request.getCall(0).args[1], 'resource_path', 'second argument should be resource path');
    t.deepEqual(moneyBirdClientV2.request.getCall(0).args[2], { 'body': true }, 'third argument should be body');
    t.end();
  });
});

test('new client - put', (t) => {
  requestStub.reset();
  return moneyBirdClientV2.put('resource_path', { 'body': true }).then(() => {
    t.equal(moneyBirdClientV2.request.callCount, 1, 'should be called one time');
    t.equal(moneyBirdClientV2.request.getCall(0).args[0], 'patch', 'first argument should be patch not put');
    t.equal(moneyBirdClientV2.request.getCall(0).args[1], 'resource_path', 'second argument should be resource path');
    t.deepEqual(moneyBirdClientV2.request.getCall(0).args[2], { 'body': true }, 'third argument should be body');
    t.end();
  });
});

test('new client - patch', (t) => {
  requestStub.reset();
  return moneyBirdClientV2.patch('resource_path', { 'body': true }).then(() => {
    t.equal(moneyBirdClientV2.request.callCount, 1, 'should be called one time');
    t.equal(moneyBirdClientV2.request.getCall(0).args[0], 'patch', 'first argument should be patch');
    t.equal(moneyBirdClientV2.request.getCall(0).args[1], 'resource_path', 'second argument should be resource path');
    t.deepEqual(moneyBirdClientV2.request.getCall(0).args[2], { 'body': true }, 'third argument should be body');
    t.end();
  });
});

test('new client - delete', (t) => {
  requestStub.reset();
  return moneyBirdClientV2.delete('resource_path').then(() => {
    t.equal(moneyBirdClientV2.request.callCount, 1, 'should be called one time');
    t.equal(moneyBirdClientV2.request.getCall(0).args[0], 'delete', 'first argument should be delete');
    t.equal(moneyBirdClientV2.request.getCall(0).args[1], 'resource_path', 'second argument should be resource path');
    t.end();
  });
});

test('new client - request restore', (t) => {
  requestStub.restore();
  t.end();
});

let httpsrequest;
test('new client - https stub', (t) => {
  httpsrequest = sinon.stub(https, 'request');
  httpsrequest.returns(Promise.resolve());
  t.end();
});

test('new client - request - hostname', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('get', 'resource_path').then(() => {
    t.equal(httpsrequest.callCount, 1, 'should be called one time');
    t.equal(httpsrequest.getCall(0).args[0].hostname, 'moneybird.com', 'hostname should be moneybird.com');
    t.end();
  });
});

test('new client - request - header token', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('get', 'resource_path').then(() => {
    t.equal(httpsrequest.getCall(0).args[0].headers.Authorization, 'Bearer CUSTOM_TOKEN', 'headers.Authorization should include token');
    t.end();
  });
});

test('new client - request - path', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('get', 'resource_path').then(() => {
    t.equal(httpsrequest.getCall(0).args[0].path, '/api/v2/CUSTOM_ADMIN_ID/resource_path.json', 'path should be set to resource_path with version including format');
    t.end();
  });
});

test('new client - request - path post', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('post', 'resource_path', {}).then(() => {
    t.equal(httpsrequest.getCall(0).args[0].path, '/api/v2/CUSTOM_ADMIN_ID/resource_path.json', 'path should be set to resource_path with version including format');
    t.end();
  });
});

test('new client - request - path v1', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV1.request('post', 'resource_path', {}).then(() => {
    t.equal(httpsrequest.getCall(0).args[0].path, '/api/v1/CUSTOM_ADMIN_ID/resource_path.json', 'path should be set to resource_path with version including format');
    t.end();
  });
});

test('new client - request - port', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('get', 'resource_path').then(() => {
    t.equal(httpsrequest.getCall(0).args[0].port, 443, 'port should always be set to SSL');
    t.end();
  });
});

test('new client - request - method', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('get', 'resource_path').then(() => {
    t.equal(httpsrequest.getCall(0).args[0].method, 'GET', 'should be uppercase');
    t.end();
  });
});

test('new client - request - method post', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('post', 'resource_path', {}).then(() => {
    t.equal(httpsrequest.getCall(0).args[0].method, 'POST', 'should be uppercase');
    t.end();
  });
});

test('new client - request - body', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('get', 'resource_path').then(() => {
    t.equal(httpsrequest.getCall(0).args[0].body, null);
    t.end();
  });
});

test('new client - request - body on post', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('post', 'resource_path', { 'body': true }).then(() => {
    t.deepEqual(httpsrequest.getCall(0).args[0].body, { 'body' : true });
    t.end();
  });
});

test('new client - request - body on patch', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('patch', 'resource_path', { 'body': true }).then(() => {
    t.deepEqual(httpsrequest.getCall(0).args[0].body, { 'body' : true });
    t.end();
  });
});

test('new client - https restore', (t) => {
  httpsrequest.restore();
  t.end();
});

test('client - requestHasBody - POST', (t) => {
  t.equal(Client.requestHasBody('POST'), true);
  t.end();
});

test('client - requestHasBody - PATCH', (t) => {
  t.equal(Client.requestHasBody('PATCH'), true);
  t.end();
});

test('client - requestHasBody - GET', (t) => {
  t.equal(Client.requestHasBody('GET'), false);
  t.end();
});

test('client - requestHasBody - DELETE', (t) => {
  t.equal(Client.requestHasBody('DELETE'), false);
  t.end();
});
<commit_msg>Added tests for the format methods<commit_after>import test from 'tape'
import sinon from 'sinon'
import { Client, https } from '../../src/index'

const moneyBirdClientV2 = new Client('CUSTOM_TOKEN', 'CUSTOM_ADMIN_ID');
const moneyBirdClientV1 = new Client('CUSTOM_TOKEN', 'CUSTOM_ADMIN_ID', 'v1');

test('new client v2 - constructor - token', (t) => {
  t.equal(moneyBirdClientV2.token, 'CUSTOM_TOKEN');
  t.end();
});

test('new client v2 - constructor - administration id', (t) => {
  t.equal(moneyBirdClientV2.administrationId, 'CUSTOM_ADMIN_ID');
  t.end();
});

test('new client v2 - constructor - version', (t) => {
  t.equal(moneyBirdClientV2.version, 'v2');
  t.end();
});

test('new client v2 - constructor - format', (t) => {
  t.equal(moneyBirdClientV2.format, 'json');
  t.end();
});

test('new client v1 - constructor - version', (t) => {
  t.equal(moneyBirdClientV1.version, 'v1');
  t.end();
});

let requestStub;
test('new client - request stubbing', (t) => {
  requestStub = sinon.stub(moneyBirdClientV2, 'request');
  requestStub.returns(Promise.resolve());
  t.end();
});

test('new client - get', (t) => {
  requestStub.reset();
  return moneyBirdClientV2.get('resource_path').then(() => {
    t.equal(moneyBirdClientV2.request.callCount, 1, 'should be called one time');
    t.equal(moneyBirdClientV2.request.getCall(0).args[0], 'get', 'first argument should be get');
    t.equal(moneyBirdClientV2.request.getCall(0).args[1], 'resource_path', 'second argument should be resource path');
    t.end();
  });
});

test('new client - post', (t) => {
  requestStub.reset();
  return moneyBirdClientV2.post('resource_path', { 'body': true }).then(() => {
    t.equal(moneyBirdClientV2.request.callCount, 1, 'should be called one time');
    t.equal(moneyBirdClientV2.request.getCall(0).args[0], 'post', 'first argument should be post');
    t.equal(moneyBirdClientV2.request.getCall(0).args[1], 'resource_path', 'second argument should be resource path');
    t.deepEqual(moneyBirdClientV2.request.getCall(0).args[2], { 'body': true }, 'third argument should be body');
    t.end();
  });
});

test('new client - put', (t) => {
  requestStub.reset();
  return moneyBirdClientV2.put('resource_path', { 'body': true }).then(() => {
    t.equal(moneyBirdClientV2.request.callCount, 1, 'should be called one time');
    t.equal(moneyBirdClientV2.request.getCall(0).args[0], 'patch', 'first argument should be patch not put');
    t.equal(moneyBirdClientV2.request.getCall(0).args[1], 'resource_path', 'second argument should be resource path');
    t.deepEqual(moneyBirdClientV2.request.getCall(0).args[2], { 'body': true }, 'third argument should be body');
    t.end();
  });
});

test('new client - patch', (t) => {
  requestStub.reset();
  return moneyBirdClientV2.patch('resource_path', { 'body': true }).then(() => {
    t.equal(moneyBirdClientV2.request.callCount, 1, 'should be called one time');
    t.equal(moneyBirdClientV2.request.getCall(0).args[0], 'patch', 'first argument should be patch');
    t.equal(moneyBirdClientV2.request.getCall(0).args[1], 'resource_path', 'second argument should be resource path');
    t.deepEqual(moneyBirdClientV2.request.getCall(0).args[2], { 'body': true }, 'third argument should be body');
    t.end();
  });
});

test('new client - delete', (t) => {
  requestStub.reset();
  return moneyBirdClientV2.delete('resource_path').then(() => {
    t.equal(moneyBirdClientV2.request.callCount, 1, 'should be called one time');
    t.equal(moneyBirdClientV2.request.getCall(0).args[0], 'delete', 'first argument should be delete');
    t.equal(moneyBirdClientV2.request.getCall(0).args[1], 'resource_path', 'second argument should be resource path');
    t.end();
  });
});

test('new client - request restore', (t) => {
  requestStub.restore();
  t.end();
});

let httpsrequest;
test('new client - https stub', (t) => {
  httpsrequest = sinon.stub(https, 'request');
  httpsrequest.returns(Promise.resolve());
  t.end();
});

test('new client - request - hostname', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('get', 'resource_path').then(() => {
    t.equal(httpsrequest.callCount, 1, 'should be called one time');
    t.equal(httpsrequest.getCall(0).args[0].hostname, 'moneybird.com', 'hostname should be moneybird.com');
    t.end();
  });
});

test('new client - request - header token', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('get', 'resource_path').then(() => {
    t.equal(httpsrequest.getCall(0).args[0].headers.Authorization, 'Bearer CUSTOM_TOKEN', 'headers.Authorization should include token');
    t.end();
  });
});

test('new client - request - path', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('get', 'resource_path').then(() => {
    t.equal(httpsrequest.getCall(0).args[0].path, '/api/v2/CUSTOM_ADMIN_ID/resource_path.json', 'path should be set to resource_path with version including format');
    t.end();
  });
});

test('new client - request - path post', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('post', 'resource_path', {}).then(() => {
    t.equal(httpsrequest.getCall(0).args[0].path, '/api/v2/CUSTOM_ADMIN_ID/resource_path.json', 'path should be set to resource_path with version including format');
    t.end();
  });
});

test('new client - request - path v1', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV1.request('post', 'resource_path', {}).then(() => {
    t.equal(httpsrequest.getCall(0).args[0].path, '/api/v1/CUSTOM_ADMIN_ID/resource_path.json', 'path should be set to resource_path with version including format');
    t.end();
  });
});

test('new client - request - port', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('get', 'resource_path').then(() => {
    t.equal(httpsrequest.getCall(0).args[0].port, 443, 'port should always be set to SSL');
    t.end();
  });
});

test('new client - request - method', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('get', 'resource_path').then(() => {
    t.equal(httpsrequest.getCall(0).args[0].method, 'GET', 'should be uppercase');
    t.end();
  });
});

test('new client - request - method post', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('post', 'resource_path', {}).then(() => {
    t.equal(httpsrequest.getCall(0).args[0].method, 'POST', 'should be uppercase');
    t.end();
  });
});

test('new client - request - body', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('get', 'resource_path').then(() => {
    t.equal(httpsrequest.getCall(0).args[0].body, null);
    t.end();
  });
});

test('new client - request - body on post', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('post', 'resource_path', { 'body': true }).then(() => {
    t.deepEqual(httpsrequest.getCall(0).args[0].body, { 'body' : true });
    t.end();
  });
});

test('new client - request - body on patch', (t) => {
  httpsrequest.reset();
  return moneyBirdClientV2.request('patch', 'resource_path', { 'body': true }).then(() => {
    t.deepEqual(httpsrequest.getCall(0).args[0].body, { 'body' : true });
    t.end();
  });
});

test('new client - https restore', (t) => {
  httpsrequest.restore();
  t.end();
});

test('client - requestHasBody - POST', (t) => {
  t.equal(Client.requestHasBody('POST'), true);
  t.end();
});

test('client - requestHasBody - PATCH', (t) => {
  t.equal(Client.requestHasBody('PATCH'), true);
  t.end();
});

test('client - requestHasBody - GET', (t) => {
  t.equal(Client.requestHasBody('GET'), false);
  t.end();
});

test('client - requestHasBody - DELETE', (t) => {
  t.equal(Client.requestHasBody('DELETE'), false);
  t.end();
});

let testArgsQueryAndBody = [
  'GET',
  'contacts',
  'id',
  {
    'contact': {
      'name': ''
    }
  }, {
    'period': 'prev_year'
  }
];

/*
  With query and postbody
*/
test('client - requestHasQuery - query and body - GET', (t) => {
  t.equal(Client.requestHasQuery('GET', testArgsQueryAndBody), true);
  t.end();
});

test('client - requestHasQuery - query and body - POST', (t) => {
  t.equal(Client.requestHasQuery('POST', testArgsQueryAndBody), true);
  t.end();
});

test('client - requestHasQuery - query and body - PUT', (t) => {
  t.equal(Client.requestHasQuery('PUT', testArgsQueryAndBody), true);
  t.end();
});

test('client - requestHasQuery - query and body - PATCH', (t) => {
  t.equal(Client.requestHasQuery('PATCH', testArgsQueryAndBody), true);
  t.end();
});

test('client - requestHasQuery - query and body - DELETE', (t) => {
  t.equal(Client.requestHasQuery('DELETE', testArgsQueryAndBody), true);
  t.end();
});

/*
  With body without query
*/
let testArgsBody = [
  'POST',
  'contacts',
  'id',
  '12',
  '52',
  {
    'contact': {
      'name': ''
    }
  }
];
test('client - requestHasQuery - only body - GET', (t) => {
  t.equal(Client.requestHasQuery('GET', testArgsBody), true);
  t.end();
});

test('client - requestHasQuery - only body - POST', (t) => {
  t.equal(Client.requestHasQuery('POST', testArgsBody), false);
  t.end();
});

test('client - requestHasQuery - only body - PUT', (t) => {
  t.equal(Client.requestHasQuery('PUT', testArgsBody), true);
  t.end();
});

test('client - requestHasQuery - only body - PATCH', (t) => {
  t.equal(Client.requestHasQuery('PATCH', testArgsBody), false);
  t.end();
});

test('client - requestHasQuery - only body - DELETE', (t) => {
  t.equal(Client.requestHasQuery('DELETE', testArgsBody), true);
  t.end();
});

/*
  With only query
*/
let testArgsQuery = [
  'GET',
  'contacts',
  'id',
  {
    'query': 'name'
  }
];
test('client - requestHasQuery - only query - GET', (t) => {
  t.equal(Client.requestHasQuery('GET', testArgsQuery), true);
  t.end();
});

test('client - requestHasQuery - only query - POST', (t) => {
  t.equal(Client.requestHasQuery('POST', testArgsQuery), false);
  t.end();
});

test('client - requestHasQuery - only query - PUT', (t) => {
  t.equal(Client.requestHasQuery('PUT', testArgsQuery), true);
  t.end();
});

test('client - requestHasQuery - only query - PATCH', (t) => {
  t.equal(Client.requestHasQuery('PATCH', testArgsQuery), false);
  t.end();
});

test('client - requestHasQuery - only query - DELETE', (t) => {
  t.equal(Client.requestHasQuery('DELETE', testArgsQuery), true);
  t.end();
});

/*
 Without query or body
*/
let testArgsNoQueryOrBody = [
  'GET',
  'contacts',
  'id',
  '12'
];
test('client - requestHasQuery - no query or body - GET', (t) => {
  t.equal(Client.requestHasQuery('GET', testArgsNoQueryOrBody), false);
  t.end();
});

test('client - requestHasQuery - no query or body - PUT', (t) => {
  t.equal(Client.requestHasQuery('PUT', testArgsNoQueryOrBody), false);
  t.end();
});

test('client - requestHasQuery - no query or body - DELETE', (t) => {
  t.equal(Client.requestHasQuery('DELETE', testArgsNoQueryOrBody), false);
  t.end();
});

/*
  Test composing the endpoint
*/
let testArgsEndpointNoBody = [
  'GET',
  'contacts',
  12,
  'view',
  'something'
];

test('client - requestEndpoint - GET', (t) => {
  t.equal(Client.requestEndpoint('GET', testArgsEndpointNoBody), '/contacts/12/view/something');
  t.end();
});

let testArgsEndpointBody = [
  'POST',
  'contacts',
  12,
  'view',
  'something',
  {
    'something': {
      'yes': true
    }
  }
];

test('client - requestEndpoint - POST', (t) => {
  t.equal(Client.requestEndpoint('POST', testArgsEndpointBody), '/contacts/12/view/something');
  t.end();
});

let testArgsEndpointBodyAndQuery = [
  'POST',
  'contacts',
  12,
  'view',
  'something',
  {
    'something': {
      'yes': true
    }
  },
  {
    'period': 'prev_year'
  }
];

test('client - requestEndpoint - POST', (t) => {
  t.equal(Client.requestEndpoint('POST', testArgsEndpointBodyAndQuery), '/contacts/12/view/something');
  t.end();
});
<|endoftext|>


<commit_before>export { tester };

import * as Base64ToUint8Array from "../Unpacker/Base64ToUint8Array.js";
import * as RandTools from "../util/RandTools.js";
import * as ScriptLoader from "../ScriptLoader.js";
import * as ValueMax from "../ValueMax.js";

// This string will not fit to ASCII when base64 decoded. So can not be used to test.
//const base64EncodedString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Use all printable ASCII codes form a string of 3-divisible length.
// (So its base64-encoded codes will divisible by 4.)
let originalArray = [];
{
  const ORIGINAL_TEST_STRING_LENGTH = 3 * 4;

  const PRINTABLE_ASCII_MIN = 32;
  const PRINTABLE_ASCII_MAX = 126;

  for (let i = 0; i < ORIGINAL_TEST_STRING_LENGTH; ++i) {
    let c = RandTools.getIntInclusive(PRINTABLE_ASCII_MIN, PRINTABLE_ASCII_MAX);
    originalArray.push( String.fromCodePoint( c ) );
  }
}

const originalString = originalArray.join("");
const base64EncodedString = btoa(originalString);
const base64DecodedString = atob(base64EncodedString);

const base64EncodedStrings_extra = [
  // Test empty base64 content.
  "",
  "\n",
  "\r",
  "\r\n",
  "\n\r",

  // Test extra has 0 to 3 bytes. (They should be ignored.)
  base64EncodedString,
  base64EncodedString + base64EncodedString.slice(0, 1),
  base64EncodedString + base64EncodedString.slice(0, 2),
  base64EncodedString + base64EncodedString.slice(0, 3),

  // Test extra non-base64 codes inside. (They should be ignored.)
  base64EncodedString.slice(0, 3) + "\n"   + base64EncodedString.slice(3),
  base64EncodedString.slice(0, 3) + "\r"   + base64EncodedString.slice(3),
  base64EncodedString.slice(0, 3) + "\r\n" + base64EncodedString.slice(3),
  base64EncodedString.slice(0, 3) + "\n\r" + base64EncodedString.slice(3),

  // Test skip 3 lines. Test multiple non-base64 codes. (They should be ignored.)
  "qwerty5ASDFG7\n\n\r"
    + base64EncodedString.slice(0, 2) + "\n\n\r\r\r\n\r\n"
    + base64EncodedString.slice(2, 7) + "\n\n\r\r\r\n\r\n"
    + base64EncodedString.slice(7, 9) + "\n\n\r\r\r\n\r\n"
    + base64EncodedString.slice(9)    + "\n\n\r\r\r\n\r\n",
];

let tEncoder = new TextEncoder();
let tDecoder = new TextDecoder();

//const base64EncodedUint8Array = tDecoder.decode(base64EncodedString);
const base64DecodedUint8Array = tEncoder.encode(base64DecodedString);
const emptyUint8Array = new Uint8Array(0);

class TestCase {
  constructor(source, skipLineCount, result, suspendByteCount, note) {
    this.source = source;
    this.skipLineCount = skipLineCount;
    this.result = result;
    this.suspendByteCount = suspendByteCount;
    this.note = note;
  }
}

let testCases = [
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 0]), 1, emptyUint8Array, undefined, "Empty. Not enough lines." ),

  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 0]), 0, emptyUint8Array,         3, "Empty" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 1]), 1, emptyUint8Array,         2, "LF Empty" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 2]), 1, emptyUint8Array,         5, "CR Empty" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 3]), 1, emptyUint8Array, undefined, "CRLF Empty" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 4]), 2, emptyUint8Array,         5, "LFCR Empty" ),

  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 5]), 0, base64DecodedUint8Array,        15, "Extra 0 bytes" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 6]), 0, base64DecodedUint8Array,         7, "Extra 1 bytes" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 7]), 0, base64DecodedUint8Array,        17, "Extra 2 bytes" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 8]), 0, base64DecodedUint8Array,         8, "Extra 3 bytes" ),

  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 9]), 0, base64DecodedUint8Array,         4, "Extra LF inside" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[10]), 0, base64DecodedUint8Array,         3, "Extra CR inside" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[11]), 0, base64DecodedUint8Array,         5, "Extra CRLF inside" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[12]), 0, base64DecodedUint8Array,        11, "Extra LFCR inside" ),

  new TestCase( tEncoder.encode("\n"   + base64EncodedString), 1, base64DecodedUint8Array,         5, "Begin LF" ),
  new TestCase( tEncoder.encode("\r"   + base64EncodedString), 1, base64DecodedUint8Array,         5, "Begin CR" ),
  new TestCase( tEncoder.encode("\r\n" + base64EncodedString), 1, base64DecodedUint8Array,         5, "Begin CRLF" ),
  new TestCase( tEncoder.encode("\n\r" + base64EncodedString), 2, base64DecodedUint8Array,         5, "Begin LFCR" ),

  new TestCase( tEncoder.encode("qwerty\n"   + base64EncodedString), 1, base64DecodedUint8Array,         6, "Text LF" ),
  new TestCase( tEncoder.encode("qwerty\r"   + base64EncodedString), 1, base64DecodedUint8Array, undefined, "Text CR" ),
  new TestCase( tEncoder.encode("qwerty\r\n" + base64EncodedString), 1, base64DecodedUint8Array,      2048, "Text CRLF" ),
  new TestCase( tEncoder.encode("qwerty\n\r" + base64EncodedString), 2, base64DecodedUint8Array,         7, "Text LFCR" ),

  new TestCase( tEncoder.encode(base64EncodedStrings_extra[13]), 3, base64DecodedUint8Array, 8, "Multiple LF and CR inside" ),
];

/** Aggregate all progress about downloading, JSON parsing, characters scanning, and weights scanning.  */
class Progress extends ValueMax.Percentage.Aggregate {
  constructor() {
    let children = new Array( testCases.length );
    for (let i = 0; i < testCases.length; ++i) {
      children[ i ] = new ValueMax.Percentage.Aggregate(); // Increased when parsing the downloaded data to Uint8Array.
    }

    super(children);
  }
}

/**
 *
 * @param {ValueMax.Percentage.Aggregate} progressParent
 *   Some new progressToAdvance will be created and added to progressParent. The created progressToAdvance will be
 * increased when every time advanced. The progressParent.root_get() will be returned when every time yield.
 *
 */
function* tester( progressParent ) {
  console.log("Base64 decode testing...");

  let progress = progressParent.child_add( new Progress() );

  for (let i = 0; i < testCases.length; ++i) {
    let testCase = testCases[ i ];

    let decoder = Base64ToUint8Array.decoder_FromArrayBuffer(
      testCase.source, testCase.skipLineCount, progress.children[ i ],
      testCase.suspendByteCount );

    let r = yield* decoder;

    if ( r.toString() != testCase.result.toString() )
      throw Error( `[${i}]`
        + ` Skip ${testCase.skipLineCount} lines.`
        + ` suspendByteCount=${testCase.suspendByteCount}.`
        + ` ${testCase.note} [${r}] != [${testCase.result}]` );
  }

  console.log("Base64 decode testing... Done.");
}

/*!!! Old (2020/12/13 Remarked)
function test( progressParent ) {
  console.log("Base64 decode testing...");
  let delayMilliseconds = 1000;

  let progress = progressParent.child_add( new Progress() );
  let progressReceiver = new ValueMax.Receiver.HTMLProgress.createByTitle_or_getDummy("TestProgressBar");

  let testPromiseAll = [];
  for (let i = 0; i < testCases.length; ++i) {
    let testCase = testCases[ i ];

    let decoder = Base64ToUint8Array.decoder_FromArrayBuffer(
        testCase.source, testCase.skipLineCount, progress.children[ i ], testCase.suspendByteCount);

    let testPromise = PartTime.forOf(
      decoder,
      ( valueMax ) => { progressReceiver.setValueMax( valueMax.valuePercentage, valueMax.maxPercentage ); }, // Report progress to UI.
      delayMilliseconds
    ).then(r => {
      progressReceiver.informDone(r); // Inform UI progress done.
      if ( r.toString() != testCase.result.toString() )
        throw Error( `[${i}]`
          + ` Skip ${testCase.skipLineCount} lines.`
          + ` suspendByteCount=${testCase.suspendByteCount}.`
          + ` ${testCase.note} [${r}] != [${testCase.result}]` );
    });

    testPromiseAll.push( testPromise );
  }

  Promise.all(testPromiseAll).then(values => {
    console.log("Base64 decode testing... Done.");
  });
}
*/
<commit_msg>Prepare progressParent for every TestCase.<commit_after>export { tester };

import * as Base64ToUint8Array from "../Unpacker/Base64ToUint8Array.js";
import * as RandTools from "../util/RandTools.js";
import * as ScriptLoader from "../util/ScriptLoader.js";
import * as ValueMax from "../util/ValueMax.js";

// This string will not fit to ASCII when base64 decoded. So can not be used to test.
//const base64EncodedString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Use all printable ASCII codes form a string of 3-divisible length.
// (So its base64-encoded codes will divisible by 4.)
let originalArray = [];
{
  const ORIGINAL_TEST_STRING_LENGTH = 3 * 4;

  const PRINTABLE_ASCII_MIN = 32;
  const PRINTABLE_ASCII_MAX = 126;

  for (let i = 0; i < ORIGINAL_TEST_STRING_LENGTH; ++i) {
    let c = RandTools.getIntInclusive(PRINTABLE_ASCII_MIN, PRINTABLE_ASCII_MAX);
    originalArray.push( String.fromCodePoint( c ) );
  }
}

const originalString = originalArray.join("");
const base64EncodedString = btoa(originalString);
const base64DecodedString = atob(base64EncodedString);

const base64EncodedStrings_extra = [
  // Test empty base64 content.
  "",
  "\n",
  "\r",
  "\r\n",
  "\n\r",

  // Test extra has 0 to 3 bytes. (They should be ignored.)
  base64EncodedString,
  base64EncodedString + base64EncodedString.slice(0, 1),
  base64EncodedString + base64EncodedString.slice(0, 2),
  base64EncodedString + base64EncodedString.slice(0, 3),

  // Test extra non-base64 codes inside. (They should be ignored.)
  base64EncodedString.slice(0, 3) + "\n"   + base64EncodedString.slice(3),
  base64EncodedString.slice(0, 3) + "\r"   + base64EncodedString.slice(3),
  base64EncodedString.slice(0, 3) + "\r\n" + base64EncodedString.slice(3),
  base64EncodedString.slice(0, 3) + "\n\r" + base64EncodedString.slice(3),

  // Test skip 3 lines. Test multiple non-base64 codes. (They should be ignored.)
  "qwerty5ASDFG7\n\n\r"
    + base64EncodedString.slice(0, 2) + "\n\n\r\r\r\n\r\n"
    + base64EncodedString.slice(2, 7) + "\n\n\r\r\r\n\r\n"
    + base64EncodedString.slice(7, 9) + "\n\n\r\r\r\n\r\n"
    + base64EncodedString.slice(9)    + "\n\n\r\r\r\n\r\n",
];

let tEncoder = new TextEncoder();
let tDecoder = new TextDecoder();

//const base64EncodedUint8Array = tDecoder.decode(base64EncodedString);
const base64DecodedUint8Array = tEncoder.encode(base64DecodedString);
const emptyUint8Array = new Uint8Array(0);

class TestCase {
  constructor(source, skipLineCount, result, suspendByteCount, note) {
    this.source = source;
    this.skipLineCount = skipLineCount;
    this.result = result;
    this.suspendByteCount = suspendByteCount;
    this.note = note;
  }
}

let testCases = [
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 0]), 1, emptyUint8Array, undefined, "Empty. Not enough lines." ),

  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 0]), 0, emptyUint8Array,         3, "Empty" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 1]), 1, emptyUint8Array,         2, "LF Empty" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 2]), 1, emptyUint8Array,         5, "CR Empty" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 3]), 1, emptyUint8Array, undefined, "CRLF Empty" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 4]), 2, emptyUint8Array,         5, "LFCR Empty" ),

  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 5]), 0, base64DecodedUint8Array,        15, "Extra 0 bytes" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 6]), 0, base64DecodedUint8Array,         7, "Extra 1 bytes" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 7]), 0, base64DecodedUint8Array,        17, "Extra 2 bytes" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 8]), 0, base64DecodedUint8Array,         8, "Extra 3 bytes" ),

  new TestCase( tEncoder.encode(base64EncodedStrings_extra[ 9]), 0, base64DecodedUint8Array,         4, "Extra LF inside" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[10]), 0, base64DecodedUint8Array,         3, "Extra CR inside" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[11]), 0, base64DecodedUint8Array,         5, "Extra CRLF inside" ),
  new TestCase( tEncoder.encode(base64EncodedStrings_extra[12]), 0, base64DecodedUint8Array,        11, "Extra LFCR inside" ),

  new TestCase( tEncoder.encode("\n"   + base64EncodedString), 1, base64DecodedUint8Array,         5, "Begin LF" ),
  new TestCase( tEncoder.encode("\r"   + base64EncodedString), 1, base64DecodedUint8Array,         5, "Begin CR" ),
  new TestCase( tEncoder.encode("\r\n" + base64EncodedString), 1, base64DecodedUint8Array,         5, "Begin CRLF" ),
  new TestCase( tEncoder.encode("\n\r" + base64EncodedString), 2, base64DecodedUint8Array,         5, "Begin LFCR" ),

  new TestCase( tEncoder.encode("qwerty\n"   + base64EncodedString), 1, base64DecodedUint8Array,         6, "Text LF" ),
  new TestCase( tEncoder.encode("qwerty\r"   + base64EncodedString), 1, base64DecodedUint8Array, undefined, "Text CR" ),
  new TestCase( tEncoder.encode("qwerty\r\n" + base64EncodedString), 1, base64DecodedUint8Array,      2048, "Text CRLF" ),
  new TestCase( tEncoder.encode("qwerty\n\r" + base64EncodedString), 2, base64DecodedUint8Array,         7, "Text LFCR" ),

  new TestCase( tEncoder.encode(base64EncodedStrings_extra[13]), 3, base64DecodedUint8Array, 8, "Multiple LF and CR inside" ),
];

/**
 *
 * @param {ValueMax.Percentage.Aggregate} progressParent
 *   Some new progressToAdvance will be created and added to progressParent. The
 * created progressToAdvance will be increased when every time advanced. The
 * progressParent.root_get() will be returned when every time yield.
 *
 */
function* tester( progressParent ) {
  console.log("Base64 decode testing...");

  // 0. Prepare progressParent for every TestCase.
  for ( let i = 0; i < testCases.length; ++i ) {
    progressParent.child_add( ValueMax.Percentage.Aggregate.Pool.get_or_create_by() );
  }

  // 1. Run every TestCase.
  for ( let i = 0; i < testCases.length; ++i ) {
    let testCase = testCases[ i ];

    let decoder = Base64ToUint8Array.decoder_FromArrayBuffer(
      testCase.source, testCase.skipLineCount, progress.children[ i ],
      testCase.suspendByteCount );

    let r = yield* decoder;

    if ( r.toString() != testCase.result.toString() )
      throw Error( `[${i}]`
        + ` Skip ${testCase.skipLineCount} lines.`
        + ` suspendByteCount=${testCase.suspendByteCount}.`
        + ` ${testCase.note} [${r}] != [${testCase.result}]` );
  }

  console.log("Base64 decode testing... Done.");
}

/*!!! Old (2020/12/13 Remarked)
function test( progressParent ) {
  console.log("Base64 decode testing...");
  let delayMilliseconds = 1000;

  let progress = progressParent.child_add( new Progress() );
  let progressReceiver = new ValueMax.Receiver.HTMLProgress.createByTitle_or_getDummy("TestProgressBar");

  let testPromiseAll = [];
  for (let i = 0; i < testCases.length; ++i) {
    let testCase = testCases[ i ];

    let decoder = Base64ToUint8Array.decoder_FromArrayBuffer(
        testCase.source, testCase.skipLineCount, progress.children[ i ], testCase.suspendByteCount);

    let testPromise = PartTime.forOf(
      decoder,
      ( valueMax ) => { progressReceiver.setValueMax( valueMax.valuePercentage, valueMax.maxPercentage ); }, // Report progress to UI.
      delayMilliseconds
    ).then(r => {
      progressReceiver.informDone(r); // Inform UI progress done.
      if ( r.toString() != testCase.result.toString() )
        throw Error( `[${i}]`
          + ` Skip ${testCase.skipLineCount} lines.`
          + ` suspendByteCount=${testCase.suspendByteCount}.`
          + ` ${testCase.note} [${r}] != [${testCase.result}]` );
    });

    testPromiseAll.push( testPromise );
  }

  Promise.all(testPromiseAll).then(values => {
    console.log("Base64 decode testing... Done.");
  });
}
*/
<|endoftext|>