<commit_before>module('lively.ide.codeeditor.modes.Clojure').requires('lively.ide.codeeditor.ace').toRun(function() {

lively.ide.codeeditor.modes.Clojure.ReplServer = {};

Object.extend(lively.ide.codeeditor.modes.Clojure.ReplServer, {

    livelyLeinProfile: "{:plugins [[lein-pprint \"1.1.1\"]]\n"
                     + " :dependencies [[rksm/repl.utils \"0.1.1\"]\n"
                     + "                ;; for vinyasa lein\n"
                     + "                [leiningen #=(leiningen.core.main/leiningen-version)]\n"
                     + "                ;; trace execution\n"
                     + "                [org.clojure/tools.trace \"0.7.8\"]\n"
                     + "                ;; install dependencies / packages without restart\n"
                     + "                [com.cemerick/pomegranate \"0.3.0\"]\n"
                     + "                ;; cljs repl ++\n"
                     + "                [com.cemerick/piggieback \"0.1.4-SNAPSHOT\"]\n"
                     + "                ;; \"quick and dirty debugging\n"
                     + "                [spyscope \"0.1.4\"]\n"
                     + "                ;; reload namespaces, recursively tracking dependencies\n"
                     + "                [org.clojure/tools.namespace \"0.2.4\"]\n"
                     + "                ;; java reflection\n"
                     + "                [im.chit/iroh \"0.1.11\"]\n"
                     + "                ;; pretty stacktraces\n"
                     + "                [io.aviso/pretty \"0.1.8\"]\n"
                     + "                ;; inject stuff in namespace + other goodies\n"
                     + "                ;; see http://z.caudate.me/give-your-clojure-workflow-more-flow/\n"
                     + "                [im.chit/vinyasa \"0.2.2\"]]\n"
                     + " :injections [(require 'spyscope.core)\n"
                     + "              (require 'vinyasa.inject)\n"
                     + "              (require 'iroh.core)\n"
                     + "              (vinyasa.inject/in\n"
                     + "              clojure.core\n"
                     + "              [rksm.repl.utils lein search-for-symbol get-stack print-stack dumb-stack traced-fn]\n"
                     + "              [vinyasa.pull [pull pull]]\n"
                     + "              [clojure.java.shell sh]\n"
                     + "              [clojure.repl apropos dir doc find-doc source [root-cause cause]]\n"
                     + "              [clojure.tools.namespace.repl [refresh refresh]]\n"
                     + "              [clojure.pprint [pprint >pprint]]\n"
                     + "              [iroh.core .% .%> .*]\n"
                     + "              [clojure.tools.trace dotrace trace-ns untrace-ns])]}\n",

    ensure: function(options, thenDo) {
        if (!thenDo) { thenDo = options; options = {}; }
        var cmd = this.getCurrentServerCommand();
        if (cmd) {
            Functions.waitFor(5000, function() {
                return cmd.getStdout().include("nREPL server started");
            }, function(err) { thenDo(null, cmd); });
        } else this.start(options, thenDo);
    },

    getCurrentServerCommand: function() {
        var cmdQueueName = "lively.clojure.replServer";
        var q = lively.shell.commandQueue[cmdQueueName];
        var cmd = q && q[0];
        return cmd && String(cmd.getCommand()).include("+lively repl") ?
            cmd : null;
    },

    start: function(options, thenDo) {
        if (!thenDo) { thenDo = options; options = {}; }
        var port = options.port || "7888",
            self = this,
            cmdQueueName = "lively.clojure.replServer";

        Functions.composeAsync(
            function(next) { lively.require("lively.ide.CommandLineInterface").toRun(function() { next(); }); },
            this.stop.bind(this, port),
            this.ensureLivelyProfile.bind(this),
            function startServer(next) {
                var cmdString = Strings.format(
                    "lein with-profile +lively repl :headless :port %s", port);
                var cmd = lively.shell.run(cmdString, {group: cmdQueueName});
                next(null,cmd);
            },
            function waitForServerStart(cmd, next) {
                Functions.waitFor(5000, function() {
                    return cmd.getStdout().include("nREPL server started");
                }, function(err) { next(null, cmd); });
            }
        )(thenDo);
    },

    stop: function(port, thenDo) {
        port = port || "7888";
        var cmdQueueName = "lively.clojure.replServer";
        Functions.composeAsync(
            function clearLivelyClojureCommands(next) {
                var q = lively.shell.commandQueue[cmdQueueName];
                if (!q || !q[0]) return next();
                delete lively.shell.commandQueue[cmdQueueName];
                q[0].kill("SIGKILL", function(err, answer) { next(); });
            },
            function stopRunningServer(next) {
                var cmdString = Strings.format("lsof -i tcp:%s -t | xargs kill; ", port);
                lively.shell.run(cmdString, {group: cmdQueueName}, function(cmd) { next(); });
            }
        )(thenDo);
    },

    reset: function(thenDo) {
        Global.URL.nodejsBase.withFilename("ClojureServer/reset").asWebResource().beAsync()
            .post().whenDone(function(response, status) { thenDo(null,  show(status + '\n' + response)); });
    },

    ensureLivelyProfile: function(thenDo) {
        var profilesDir, livelyClojureProfileFile;
        var  livelyLeinProfile = this.livelyLeinProfile;
        Functions.composeAsync(
            function(next) {
                lively.shell.run("echo $HOME", {}, function(cmd) {
                    if (cmd.getCode()) next(cmd.resultString(true));
                    else {
                        var home = cmd.getStdout().trim()
                        profilesDir = home + "/.lein/profiles.d";
                        livelyClojureProfileFile = profilesDir + "/lively.clj";
                        next();
                    }
                });
            },
            function(next) {
                lively.shell.run("mkdir -p " + profilesDir, {}, function(cmd) { next(); });
            },
            function(next) {
                lively.ide.CommandLineInterface.writeFile(
                    livelyClojureProfileFile,
                    {content: livelyLeinProfile},
                    function() { next(); })
            }
        )(thenDo);
    }

});

Object.extend(lively.ide.codeeditor.modes.Clojure, {

    prettyPrint: function(code, thenDo) {
        // this is WORK IN PROGRESS!
        var prettify = Strings.format(
            "(require '(rewrite-clj parser printer))\n"
          + "(rewrite-clj.printer/print-edn (rewrite-clj.parser/parse-string-all \"%s\"))",
            code.replace(/"/g, '\\"'));

        // var prettify = Strings.format(
        //     "(clojure.pprint/write '%s :dispatch clojure.pprint/code-dispatch)",
        //     code);
        // var prettify = Strings.format(
        //     "(require '[fipp.edn :refer (pprint) :rename {pprint fipp}]) (fipp '%s)",
        //     code);

        lively.ide.codeeditor.modes.Clojure.doEval(
            prettify, {passError: true, resultIsJSON: false},
            function(err, prettified) {
                if (err) show(err);
                if (err) prettified = code;
                else {
                    // try { prettified = eval(prettified); } catch (e) { prettified = code; }
                    prettified = prettified.replace(/^(nil\s*)+/g, '');
                    // FIXME pretty printer seems to add additional newlines after comments
                    prettified = prettified.replace(/(;[^\n]*)[\n]+/gm, '$1\n');
                    if (!prettified.length) prettified = code;
                }
                thenDo && thenDo(err, prettified);
            });
    },

    fetchDoc: function(expr, thenDo) {
        this.doEval("(doc " + expr + ")", {prettyPrint: true}, thenDo);
    },

    doEval: function(expr, options, thenDo) {
        if (!thenDo) { thenDo = options; options = null; };
        options = options || {};
        var pp = options.prettyPrint;
        var isJSON = options.resultIsJSON;

        if (!module('lively.net.SessionTracker').isLoaded() || !lively.net.SessionTracker.isConnected()) {
            thenDo(new Error('Lively2Lively not running, cannot connect to Clojure nREPL server!'));
            return;
        }

        var sess = lively.net.SessionTracker.getSession();

        if (pp) expr = Strings.format("(with-out-str (>pprint (do %s)))", expr);

        sess.send('clojureEval', {code: expr}, function(answer) {
            if (answer.data.error) thenDo && thenDo(answer.data.error, null);
            else prepareRawResult(answer.data.result, thenDo); });

        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
        function prepareRawResult(rawResult, thenDo) {
            if (!Object.isArray(rawResult) || !rawResult.length) return rawResult;
            var errors = rawResult.pluck("ex").compact(),
                isError = !!errors.length,
                result = rawResult.pluck('value').concat(rawResult.pluck('out')).compact().join('\n'),
                err = isError ? [errors, rawResult.pluck('root-ex'), rawResult.pluck('err')].flatten().compact().invoke('trim').uniq().join('\n') : null;

            if (!isError && pp) try { result = eval(result); } catch (e) {}
            if (!isError && isJSON) try { result = JSON.parse(eval(result)); } catch (e) {
                err = e;
                result = {error: e};
            }
            thenDo && thenDo(options.passError ? err : null, err || result);
        }
    }

});

var ClojureMode = lively.ide.ace.require('ace/mode/clojure').Mode;

ClojureMode.addMethods({

    commands: {
        prettyPrint: {
            exec: function(ed) {
                var string = ed.$morph.getSelectionOrLineString();
                lively.ide.codeeditor.modes.Clojure.prettyPrint(string, function(err, string) {
                    ed.insert(string);
                    // ed.$morph.printObject(ed, err ? err : string);
                });

            }
        },

        printDoc: {
            exec: function(ed) {
                var string = ed.$morph.getSelectionOrLineString();
                lively.ide.codeeditor.modes.Clojure.fetchDoc(string, function(err, docString) {
                    ed.$morph.printObject(ed, err ? err : docString);
                });
            }
        },
    },

    keybindings: {
        "Command-Shift-/": "printDoc",
        "Tab": "prettyPrint"
    },

    keyhandler: null,

    initKeyHandler: function() {
        Properties.forEachOwn(this.keybindings, function(key, commandName) {
            if (this.commands[commandName]) {
                if (this.commands[commandName].bindKey) {
                    var bound = this.commands[commandName].bindKey;
                    if (!Object.isArray(bound)) bound = [bound];
                    bound.push(key);
                    this.commands[commandName].bindKey = bound;
                } else this.commands[commandName].bindKey = key
            }
        }, this);
        return this.keyhandler = lively.ide.ace.createKeyHandler({
            keyBindings: this.keybindings,
            commands: this.commands
        });
    },

    attach: function(ed) {
        this.initKeyHandler();
        ed.keyBinding.addKeyboardHandler(this.keyhandler);
    },

    detach: function(ed) {
        ed.keyBinding.removeKeyboardHandler(this.keyhandler);
        this.keyhandler = null;
    },

    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

    doEval: function(codeEditor, insertResult) {
        var sourceString = codeEditor.getSelectionOrLineString();
        lively.ide.codeeditor.modes.Clojure.doEval(sourceString, {prettyPrint: true}, function(err, result) {
            printResult(err, result)
        });

        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

        function printResult(err, result) {
            if (err && !Object.isString(err)) err = Objects.inspect(err, {maxDepth: 3});
            if (!insertResult && err) { codeEditor.world().alert(err); return;}
            if (result && !Object.isString(result)) result = Objects.inspect(result, {maxDepth: 3});
            if (insertResult) codeEditor.printObject(codeEditor.aceEditor, err ? err : result);
            else codeEditor.collapseSelection("end");
        }
    }
});

lively.ide.codeeditor.ModeChangeHandler.subclass('lively.ide.codeeditor.modes.Clojure.ChangeHandler',
"settings", {
    targetMode: "ace/mode/clojure"
},
"parsing", {
    parse: function(src, session) {
        var options = {};
        return null;
        // return closer.parse(src, options);
    }
},
'rendering', {

    onDocumentChange: function(evt) {
        this.updateAST(evt)
    },

    updateAST: function(evt) {
        var codeEditor = evt.codeEditor,
            session = evt.session,
            src = evt.codeEditor.textString,
            ast;

        // 1. parse
        try {
            ast = session.$ast = this.parse(src, session);
        } catch(e) { ast = session.$ast = e; }

        // 2. update lively codemarker
        var marker = this.ensureLivelyCodeMarker(session);
        marker.modeId = this.targetMode;
        marker.markerRanges.length = 0;

        // if (codeEditor.getShowWarnings()) {
        //     marker.markerRanges.pushAll(
        //         lively.ast.query.findGlobalVarRefs(ast, {jslintGlobalComment: true}).map(function(ea) {
        //             ea.cssClassName = "ace-global-var"; return ea; }));
        // }

        // if (ast.parseError && codeEditor.getShowErrors()) {
        //     ast.parseError.cssClassName = "ace-syntax-error";
        //     marker.markerRanges.push(ast.parseError);
        // }

        // marker.redraw(session);

        // 3. emit session astChange event
        var astChange = {ast: ast, docChange: evt.data, codeEditor: codeEditor};
        session._signal('astChange', astChange);
    }

});

(function registerModeHandler() {
    lively.module('lively.ide.codeeditor.DocumentChange').runWhenLoaded(function() {
        lively.ide.CodeEditor.DocumentChangeHandler.registerModeHandler(lively.ide.codeeditor.modes.Clojure.ChangeHandler);
    });
})();

}) // end of module
<commit_msg>clojure: eval queue<commit_after>module('lively.ide.codeeditor.modes.Clojure').requires('lively.ide.codeeditor.ace').toRun(function() {

lively.ide.codeeditor.modes.Clojure.ReplServer = {};

Object.extend(lively.ide.codeeditor.modes.Clojure.ReplServer, {

    livelyLeinProfile: "{:plugins [[lein-pprint \"1.1.1\"]]\n"
                     + " :dependencies [[rksm/repl.utils \"0.1.1\"]\n"
                     + "                ;; for vinyasa lein\n"
                     + "                [leiningen #=(leiningen.core.main/leiningen-version)]\n"
                     + "                ;; trace execution\n"
                     + "                [org.clojure/tools.trace \"0.7.8\"]\n"
                     + "                ;; install dependencies / packages without restart\n"
                     + "                [com.cemerick/pomegranate \"0.3.0\"]\n"
                     + "                ;; cljs repl ++\n"
                     + "                [com.cemerick/piggieback \"0.1.4-SNAPSHOT\"]\n"
                     + "                ;; \"quick and dirty debugging\n"
                     + "                [spyscope \"0.1.4\"]\n"
                     + "                ;; reload namespaces, recursively tracking dependencies\n"
                     + "                [org.clojure/tools.namespace \"0.2.4\"]\n"
                     + "                ;; java reflection\n"
                     + "                [im.chit/iroh \"0.1.11\"]\n"
                     + "                ;; pretty stacktraces\n"
                     + "                [io.aviso/pretty \"0.1.8\"]\n"
                     + "                ;; inject stuff in namespace + other goodies\n"
                     + "                ;; see http://z.caudate.me/give-your-clojure-workflow-more-flow/\n"
                     + "                [im.chit/vinyasa \"0.2.2\"]]\n"
                     + " :injections [(require 'spyscope.core)\n"
                     + "              (require 'vinyasa.inject)\n"
                     + "              (require 'iroh.core)\n"
                     + "              (vinyasa.inject/in\n"
                     + "              clojure.core\n"
                     + "              [rksm.repl.utils lein search-for-symbol get-stack print-stack dumb-stack traced-fn]\n"
                     + "              [vinyasa.pull [pull pull]]\n"
                     + "              [clojure.java.shell sh]\n"
                     + "              [clojure.repl apropos dir doc find-doc source [root-cause cause]]\n"
                     + "              [clojure.tools.namespace.repl [refresh refresh]]\n"
                     + "              [clojure.pprint [pprint >pprint]]\n"
                     + "              [iroh.core .% .%> .*]\n"
                     + "              [clojure.tools.trace dotrace trace-ns untrace-ns])]}\n",

    ensure: function(options, thenDo) {
        if (!thenDo) { thenDo = options; options = {}; }
        var cmd = this.getCurrentServerCommand();
        if (cmd) {
            Functions.waitFor(5000, function() {
                return cmd.getStdout().include("nREPL server started");
            }, function(err) { thenDo(err, cmd); });
        } else this.start(options, thenDo);
    },

    getCurrentServerCommand: function() {
        var cmdQueueName = "lively.clojure.replServer";
        var q = lively.shell.commandQueue[cmdQueueName];
        var cmd = q && q[0];
        return cmd && String(cmd.getCommand()).include("+lively repl") ?
            cmd : null;
    },

    start: function(options, thenDo) {
        if (!thenDo) { thenDo = options; options = {}; }
        var port = options.port || "7888",
            self = this,
            cmdQueueName = "lively.clojure.replServer";

        Functions.composeAsync(
            function(next) { lively.require("lively.ide.CommandLineInterface").toRun(function() { next(); }); },
            this.stop.bind(this, port),
            this.ensureLivelyProfile.bind(this),
            function startServer(next) {
                var cmdString = Strings.format(
                    "lein with-profile +lively repl :headless :port %s", port);
                var cmd = lively.shell.run(cmdString, {group: cmdQueueName});
                next(null,cmd);
            },
            function waitForServerStart(cmd, next) {
                Functions.waitFor(5000, function() {
                    return cmd.getStdout().include("nREPL server started");
                }, function(err) { next(null, cmd); });
            }
        )(thenDo);
    },

    stop: function(port, thenDo) {
        port = port || "7888";
        var cmdQueueName = "lively.clojure.replServer";
        Functions.composeAsync(
            function clearLivelyClojureCommands(next) {
                var q = lively.shell.commandQueue[cmdQueueName];
                if (!q || !q[0]) return next();
                delete lively.shell.commandQueue[cmdQueueName];
                q[0].kill("SIGKILL", function(err, answer) { next(); });
            },
            function stopRunningServer(next) {
                var cmdString = Strings.format("lsof -i tcp:%s -t | xargs kill -9 ", port);
                lively.shell.run(cmdString, {group: cmdQueueName}, function(cmd) { next(); });
            }
        )(thenDo);
    },

    reset: function(thenDo) {
        Global.URL.nodejsBase.withFilename("ClojureServer/reset").asWebResource().beAsync()
            .post().whenDone(function(response, status) { thenDo(null,  show(status + '\n' + response)); });
    },

    ensureLivelyProfile: function(thenDo) {
        var profilesDir, livelyClojureProfileFile;
        var  livelyLeinProfile = this.livelyLeinProfile;
        Functions.composeAsync(
            function(next) {
                lively.shell.run("echo $HOME", {}, function(cmd) {
                    if (cmd.getCode()) next(cmd.resultString(true));
                    else {
                        var home = cmd.getStdout().trim()
                        profilesDir = home + "/.lein/profiles.d";
                        livelyClojureProfileFile = profilesDir + "/lively.clj";
                        next();
                    }
                });
            },
            function(next) {
                lively.shell.run("mkdir -p " + profilesDir, {}, function(cmd) { next(); });
            },
            function(next) {
                lively.ide.CommandLineInterface.writeFile(
                    livelyClojureProfileFile,
                    {content: livelyLeinProfile},
                    function() { next(); })
            }
        )(thenDo);
    }

});

Object.extend(lively.ide.codeeditor.modes.Clojure, {

    prettyPrint: function(code, thenDo) {
        // this is WORK IN PROGRESS!
        var prettify = Strings.format(
            "(require '(rewrite-clj parser printer))\n"
          + "(rewrite-clj.printer/print-edn (rewrite-clj.parser/parse-string-all \"%s\"))",
            code.replace(/"/g, '\\"'));

        // var prettify = Strings.format(
        //     "(clojure.pprint/write '%s :dispatch clojure.pprint/code-dispatch)",
        //     code);
        // var prettify = Strings.format(
        //     "(require '[fipp.edn :refer (pprint) :rename {pprint fipp}]) (fipp '%s)",
        //     code);

        lively.ide.codeeditor.modes.Clojure.doEval(
            prettify, {passError: true, resultIsJSON: false},
            function(err, prettified) {
                if (err) show(err);
                if (err) prettified = code;
                else {
                    // try { prettified = eval(prettified); } catch (e) { prettified = code; }
                    prettified = prettified.replace(/^(nil\s*)+/g, '');
                    // FIXME pretty printer seems to add additional newlines after comments
                    prettified = prettified.replace(/(;[^\n]*)[\n]+/gm, '$1\n');
                    if (!prettified.length) prettified = code;
                }
                thenDo && thenDo(err, prettified);
            });
    },

    fetchDoc: function(expr, thenDo) {
        this.doEval("(doc " + expr + ")", {prettyPrint: true}, thenDo);
    },

    evalQueue: [],

    runEvalFromQueue: function() {
        var clj = this;
        var evalObject = clj.evalQueue[0];
        if (!evalObject || evalObject.isRunning) return;
        clj.runEval(evalObject, function(err, result) {
            clj.evalQueue.remove(evalObject);
            try { if (evalObject.callback) evalObject.callback(err, result); } catch (e) {
                show("error in clj eval callback: %s", e);
            }
            clj.runEvalFromQueue.bind(clj).delay(0);
        });
    },

    runEval: function(evalObject, thenDo) {
        var options = evalObject.options;
        var expr = evalObject.expr;
        var pp = options.hasOwnProperty("prettyPrint") ? options.prettyPrint : false;
        var isJSON = options.hasOwnProperty("resultIsJSON") ? options.resultIsJSON : false;
        var catchError = options.hasOwnProperty("catchError") ? options.catchError : true;

        if (!module('lively.net.SessionTracker').isLoaded() || !lively.net.SessionTracker.isConnected()) {
            thenDo(new Error('Lively2Lively not running, cannot connect to Clojure nREPL server!'));
            return;
        }
        var sess = lively.net.SessionTracker.getSession();

        if (pp) expr = Strings.format("(with-out-str (>pprint (do %s)))", expr);
        if (catchError) expr = Strings.format("(try %s (catch Exception e (clojure.repl/pst e)))", expr);

        evalObject.isRunning = true;
        var result;
        sess.send('clojureEval', {code: expr}, function(answer) {
            result = Object.merge([result || {}, answer.data]);
            if (answer.data['eval-id']) evalObject['eval-id'] = answer.data['eval-id'];
            if (answer.expectMoreResponses) return;
            evalObject.isRunning = false;
            if (result.error) thenDo && thenDo(result.error, null);
            else prepareRawResult(result.result, thenDo); });

        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
        function prepareRawResult(rawResult, thenDo) {

            if (Object.isString(rawResult) && rawResult.match(/error/i))
                rawResult = [{err: rawResult}];

            if (!Object.isArray(rawResult) || !rawResult.length) {
                console.warn("strange clj eval rawResult: %o", rawResult);
                return;
            };

            var errors = rawResult.pluck("ex").compact().concat(rawResult.pluck("err").compact()),
                isError = !!errors.length,
                result = rawResult.pluck('value').concat(rawResult.pluck('out')).compact().join('\n'),
                err;

            if (isError) {
                errors.unshift(rawResult.pluck("status").flatten().without("done"));
                var cause = rawResult.pluck('root-ex').flatten().compact();
                if (cause.length) errors.pushAll(["root-cause:"].concat(cause));
                err = errors.flatten().compact().invoke('trim').join('\n');
            }

            if (!isError && pp) try { result = eval(result); } catch (e) {}
            if (!isError && isJSON) try { result = JSON.parse(eval(result)); } catch (e) {
                err = e;
                result = {error: e};
            }
            thenDo && thenDo(options.passError ? err : null, err || result);
        }
    },

    doEval: function(expr, options, thenDo) {
        if (!thenDo) { thenDo = options; options = null; };
        this.evalQueue.push({
            expr: expr,
            options: options || {},
            isRunning: false,
            "eval-id": null,
            callback: thenDo});
        this.runEvalFromQueue();
    },

    evalInterrupt: function(thenDo) {
        var clj = this;
        var evalObject = clj.evalQueue[0];
        if (!evalObject) thenDo(new Error("no clj eval running"));
        else if (!evalObject.isRunning || !evalObject['eval-id']) cleanup();
        else {
            var sess = lively.net.SessionTracker.getSession();
            sess.send('clojureEvalInterrupt', {"eval-id": evalObject['eval-id']}, function(answer) {
                cleanup();
                thenDo(answer.error || answer.data.error, answer.data);
            });
        }

        function cleanup() {
            clj.evalQueue.remove(evalObject);
            clj.runEvalFromQueue.bind(clj).delay(0);
        }
    }

});

var ClojureMode = lively.ide.ace.require('ace/mode/clojure').Mode;

ClojureMode.addMethods({

    commands: {
        prettyPrint: {
            exec: function(ed) {
                var string = ed.$morph.getSelectionOrLineString();
                lively.ide.codeeditor.modes.Clojure.prettyPrint(string, function(err, string) {
                    ed.insert(string);
                    // ed.$morph.printObject(ed, err ? err : string);
                });

            }
        },

        printDoc: {
            exec: function(ed) {
                var string = ed.$morph.getSelectionOrLineString();
                lively.ide.codeeditor.modes.Clojure.fetchDoc(string, function(err, docString) {
                    ed.$morph.printObject(ed, err ? err : docString);
                });
            }
        },

        evalInterrupt: {
            exec: function(ed) {
                ed.$morph.setStatusMessage("Interrupting eval...");
                lively.ide.codeeditor.modes.Clojure.evalInterrupt(function(err, answer) {
                    ed.$morph.setStatusMessage(Objects.inspect(err || answer), err ? Color.red : null);
                });
            },
        }
    },

    keybindings: {
        "Command-Shift-/": "printDoc",
        "Tab": "prettyPrint"
    },

    keyhandler: null,

    initKeyHandler: function() {
        Properties.forEachOwn(this.keybindings, function(key, commandName) {
            if (this.commands[commandName]) {
                if (this.commands[commandName].bindKey) {
                    var bound = this.commands[commandName].bindKey;
                    if (!Object.isArray(bound)) bound = [bound];
                    bound.push(key);
                    this.commands[commandName].bindKey = bound;
                } else this.commands[commandName].bindKey = key
            }
        }, this);
        return this.keyhandler = lively.ide.ace.createKeyHandler({
            keyBindings: this.keybindings,
            commands: this.commands
        });
    },

    attach: function(ed) {
        this.initKeyHandler();
        ed.keyBinding.addKeyboardHandler(this.keyhandler);
    },

    detach: function(ed) {
        ed.keyBinding.removeKeyboardHandler(this.keyhandler);
        this.keyhandler = null;
    },

    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

    doEval: function(codeEditor, insertResult) {
        var sourceString = codeEditor.getSelectionOrLineString();
        lively.ide.codeeditor.modes.Clojure.doEval(sourceString, {prettyPrint: true}, function(err, result) {
            printResult(err, result)
        });

        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

        function printResult(err, result) {
            if (err && !Object.isString(err)) err = Objects.inspect(err, {maxDepth: 3});
            if (!insertResult && err) { codeEditor.world().alert(err); return;}
            if (result && !Object.isString(result)) result = Objects.inspect(result, {maxDepth: 3});
            if (insertResult) codeEditor.printObject(codeEditor.aceEditor, err ? err : result);
            else codeEditor.collapseSelection("end");
        }
    }
});

lively.ide.codeeditor.ModeChangeHandler.subclass('lively.ide.codeeditor.modes.Clojure.ChangeHandler',
"settings", {
    targetMode: "ace/mode/clojure"
},
"parsing", {
    parse: function(src, session) {
        var options = {};
        return null;
        // return closer.parse(src, options);
    }
},
'rendering', {

    onDocumentChange: function(evt) {
        this.updateAST(evt)
    },

    updateAST: function(evt) {
        var codeEditor = evt.codeEditor,
            session = evt.session,
            src = evt.codeEditor.textString,
            ast;

        // 1. parse
        try {
            ast = session.$ast = this.parse(src, session);
        } catch(e) { ast = session.$ast = e; }

        // 2. update lively codemarker
        var marker = this.ensureLivelyCodeMarker(session);
        marker.modeId = this.targetMode;
        marker.markerRanges.length = 0;

        // if (codeEditor.getShowWarnings()) {
        //     marker.markerRanges.pushAll(
        //         lively.ast.query.findGlobalVarRefs(ast, {jslintGlobalComment: true}).map(function(ea) {
        //             ea.cssClassName = "ace-global-var"; return ea; }));
        // }

        // if (ast.parseError && codeEditor.getShowErrors()) {
        //     ast.parseError.cssClassName = "ace-syntax-error";
        //     marker.markerRanges.push(ast.parseError);
        // }

        // marker.redraw(session);

        // 3. emit session astChange event
        var astChange = {ast: ast, docChange: evt.data, codeEditor: codeEditor};
        session._signal('astChange', astChange);
    }

});

(function registerModeHandler() {
    lively.module('lively.ide.codeeditor.DocumentChange').runWhenLoaded(function() {
        lively.ide.CodeEditor.DocumentChangeHandler.registerModeHandler(lively.ide.codeeditor.modes.Clojure.ChangeHandler);
    });
})();

}) // end of module
<|endoftext|>


<commit_before>
var BrowserTabManager = function( parentObj ) {

  OPromise.call( this );

  // Set up 0 mock BrowserTab objects at startup
  this.length = 0;

  this._parent = parentObj;

  // Remove all collection items and replace with browserTabs
  this.replaceTabs = function( browserTabs ) {

    for( var i = 0, l = this.length; i < l; i++ ) {
      delete this[ i ];
    }
    this.length = 0;

    if(browserTabs.length <= 0) {
      return;
    }

    for( var i = 0, l = browserTabs.length; i < l; i++ ) {
      if(this !== OEX.tabs) {
        browserTabs[ i ].properties.index = i;
      }
      this[ i ] = browserTabs[ i ];
    }
    this.length = browserTabs.length;

    // Set focused on first tab object, unless some other tab object has focused=='true'
    var focusFound = false;
    for(var i = 0, l = browserTabs.length; i < l; i++) {
      if(browserTabs[i].properties.active == true) {
        focusFound = true;
        break;
      }
    }
    if(!focusFound) {
      browserTabs[0].focus();
    }

  };

  // Add an array of browserTabs to the current collection
  this.addTab = function( browserTab, startPosition ) {
    // Extract current set of tabs in collection
    var allTabs = [];

    for(var i = 0, l = this.length; i < l; i++) {
      allTabs[ i ] = this[ i ];
      if(allTabs[ i ].properties.active == true) {
        focusFound = true;
      }
    }

    if(browserTab.properties.active == true) {
      browserTab.focus();
    }

    var position = startPosition !== undefined ? startPosition : allTabs.length;

    // Add new browserTab to allTabs array
    allTabs.splice(this !== OEX.tabs ? position : this.length, 0, browserTab);

    // Rewrite the current tabs collection in order
    for( var i = 0, l = allTabs.length; i < l; i++ ) {
      if(this !== OEX.tabs) {
        // Update all tab indexes to the current tabs collection order
        allTabs[ i ].properties.index = i;
      }
      this[ i ] = allTabs[ i ];
    }
    this.length = allTabs.length;

  };

  // Remove a browserTab from the current collection
  this.removeTab = function( browserTab ) {

    var oldCollectionLength = this.length;

    // Extract current set of tabs in collection
    var allTabs = [];
    var removeTabIndex = -1;
    for(var i = 0, l = this.length; i < l; i++) {
      allTabs[ i ] = this[ i ];
      if( allTabs[ i ].id == browserTab.id ) {
        removeTabIndex = i;
      }
    }

    // Remove browser tab
    if(removeTabIndex > -1) {
      allTabs.splice(removeTabIndex, 1);
    }

    // Rewrite the current tabs collection
    for( var i = 0, l = allTabs.length; i < l; i++ ) {
      if(this !== OEX.tabs) {
        allTabs[ i ].properties.index = i;
      }
      this[ i ] = allTabs[ i ];
    }
    this.length = allTabs.length;

    // Remove any ghost items, if any
    if(oldCollectionLength > this.length) {
      for(var i = this.length, l = oldCollectionLength; i < l; i++) {
        delete this[ i ];
      }
    }

  };

};

BrowserTabManager.prototype = Object.create( OPromise.prototype );

BrowserTabManager.prototype.create = function( browserTabProperties, before ) {

  if(before && !(before instanceof BrowserTab)) {
    throw new OError(
      "TypeMismatchError",
      "Could not create BrowserTab object. 'before' attribute provided is invalid.",
      DOMException.TYPE_MISMATCH_ERR
    );
  } else if(before) {

    if( before.closed === true ) {
      throw new OError(
        "InvalidStateError",
        "'before' BrowserTab object is in the closed state and therefore is invalid.",
        DOMException.INVALID_STATE_ERR
      );
    }

    if(before._windowParent && before._windowParent.closed === true ) {
      throw new OError(
        "InvalidStateError",
        "Parent window of 'before' BrowserTab object is in the closed state and therefore is invalid.",
        DOMException.INVALID_STATE_ERR
      );
    }

    // If we're adding this BrowserTab before an existing object then set its insert position correctly
    browserTabProperties.position = before.properties.index;

  }

  // Set parent window to create the tab in
  var windowParent = before && before._windowParent ? before._windowParent : this._parent || OEX.windows.getLastFocused();

  if(windowParent && windowParent.closed === true ) {
    throw new OError(
      "InvalidStateError",
      "Parent window of the current BrowserTab object is in the closed state and therefore is invalid.",
      DOMException.INVALID_STATE_ERR
    );
  }

  var shadowBrowserTab = new BrowserTab( browserTabProperties, windowParent );

  // Sanitized tab properties
  var createTabProperties = {
    'url': shadowBrowserTab.properties.url,
    'active': shadowBrowserTab.properties.active,
    'pinned': shadowBrowserTab.properties.pinned,
    'index': shadowBrowserTab.properties.index
  };

  // Set insert position for the new tab from 'before' attribute, if any
  if( before ) {
    createTabProperties.windowId = before._windowParent ?
                                      before._windowParent.properties.id : createTabProperties.windowId;
  }

  // Add this object to the end of the current tabs collection
  shadowBrowserTab._windowParent.tabs.addTab(shadowBrowserTab, shadowBrowserTab.properties.index);

  // unfocus all other tabs in tab's window parent collection if this tab is set to focused
  if(shadowBrowserTab.properties.active == true ) {
    for(var i = 0, l = shadowBrowserTab._windowParent.tabs.length; i < l; i++) {
      if(shadowBrowserTab._windowParent.tabs[i] !== shadowBrowserTab) {
        shadowBrowserTab._windowParent.tabs[i].properties.active = false;
      }
    }
  }

  // Add this object to the root tab manager
  OEX.tabs.addTab( shadowBrowserTab );

  // Queue platform action or fire immediately if this object is resolved
  Queue.enqueue(this, function(done) {

    chrome.tabs.create(
      createTabProperties,
      function( _tab ) {
        // Update BrowserTab properties
        for(var i in _tab) {
          if(i == 'url') continue;
          shadowBrowserTab.properties[i] = _tab[i];
        }

        // Resolve new tab, if it hasn't been resolved already
        shadowBrowserTab.resolve(true);

        done();

      }.bind(this)
    );

  }.bind(this), true);

  // return shadowBrowserTab from this function before firing these events!
  global.setTimeout(function() {

    shadowBrowserTab._windowParent.tabs.dispatchEvent(new OEvent('create', {
      "tab": shadowBrowserTab,
      "prevWindow": null,
      "prevTabGroup": null,
      "prevPosition": 0
    }));

    // Fire a create event at RootTabsManager
    OEX.tabs.dispatchEvent(new OEvent('create', {
      "tab": shadowBrowserTab,
      "prevWindow": null,
      "prevTabGroup": null,
      "prevPosition": 0
    }));

  }, 0);

  return shadowBrowserTab;

};

BrowserTabManager.prototype.getAll = function() {

  var allTabs = [];

  for(var i = 0, l = this.length; i < l; i++) {
    allTabs[ i ] = this[ i ];
  }

  return allTabs;

};

BrowserTabManager.prototype.getSelected = function() {

  for(var i = 0, l = this.length; i < l; i++) {
    if(this[i].focused == true) {
      return this[i];
    }
  }

  // default
  if(this[0]) {
    this[0].properties.active = true;
  }

  return this[0] || undefined;

};
// Alias of .getSelected()
BrowserTabManager.prototype.getFocused = BrowserTabManager.prototype.getSelected;

BrowserTabManager.prototype.close = function( browserTab ) {

  if( !browserTab || !(browserTab instanceof BrowserTab)) {
    throw new OError(
      "TypeMismatchError",
      "Expected browserTab argument to be of type BrowserTab.",
      DOMException.TYPE_MISMATCH_ERR
    );
  }

  browserTab.close();

};
<commit_msg>Ensure BrowserTab creation events fire after a 50ms delay<commit_after>
var BrowserTabManager = function( parentObj ) {

  OPromise.call( this );

  // Set up 0 mock BrowserTab objects at startup
  this.length = 0;

  this._parent = parentObj;

  // Remove all collection items and replace with browserTabs
  this.replaceTabs = function( browserTabs ) {

    for( var i = 0, l = this.length; i < l; i++ ) {
      delete this[ i ];
    }
    this.length = 0;

    if(browserTabs.length <= 0) {
      return;
    }

    for( var i = 0, l = browserTabs.length; i < l; i++ ) {
      if(this !== OEX.tabs) {
        browserTabs[ i ].properties.index = i;
      }
      this[ i ] = browserTabs[ i ];
    }
    this.length = browserTabs.length;

    // Set focused on first tab object, unless some other tab object has focused=='true'
    var focusFound = false;
    for(var i = 0, l = browserTabs.length; i < l; i++) {
      if(browserTabs[i].properties.active == true) {
        focusFound = true;
        break;
      }
    }
    if(!focusFound) {
      browserTabs[0].focus();
    }

  };

  // Add an array of browserTabs to the current collection
  this.addTab = function( browserTab, startPosition ) {
    // Extract current set of tabs in collection
    var allTabs = [];

    for(var i = 0, l = this.length; i < l; i++) {
      allTabs[ i ] = this[ i ];
      if(allTabs[ i ].properties.active == true) {
        focusFound = true;
      }
    }

    if(browserTab.properties.active == true) {
      browserTab.focus();
    }

    var position = startPosition !== undefined ? startPosition : allTabs.length;

    // Add new browserTab to allTabs array
    allTabs.splice(this !== OEX.tabs ? position : this.length, 0, browserTab);

    // Rewrite the current tabs collection in order
    for( var i = 0, l = allTabs.length; i < l; i++ ) {
      if(this !== OEX.tabs) {
        // Update all tab indexes to the current tabs collection order
        allTabs[ i ].properties.index = i;
      }
      this[ i ] = allTabs[ i ];
    }
    this.length = allTabs.length;

  };

  // Remove a browserTab from the current collection
  this.removeTab = function( browserTab ) {

    var oldCollectionLength = this.length;

    // Extract current set of tabs in collection
    var allTabs = [];
    var removeTabIndex = -1;
    for(var i = 0, l = this.length; i < l; i++) {
      allTabs[ i ] = this[ i ];
      if( allTabs[ i ].id == browserTab.id ) {
        removeTabIndex = i;
      }
    }

    // Remove browser tab
    if(removeTabIndex > -1) {
      allTabs.splice(removeTabIndex, 1);
    }

    // Rewrite the current tabs collection
    for( var i = 0, l = allTabs.length; i < l; i++ ) {
      if(this !== OEX.tabs) {
        allTabs[ i ].properties.index = i;
      }
      this[ i ] = allTabs[ i ];
    }
    this.length = allTabs.length;

    // Remove any ghost items, if any
    if(oldCollectionLength > this.length) {
      for(var i = this.length, l = oldCollectionLength; i < l; i++) {
        delete this[ i ];
      }
    }

  };

};

BrowserTabManager.prototype = Object.create( OPromise.prototype );

BrowserTabManager.prototype.create = function( browserTabProperties, before ) {

  if(before && !(before instanceof BrowserTab)) {
    throw new OError(
      "TypeMismatchError",
      "Could not create BrowserTab object. 'before' attribute provided is invalid.",
      DOMException.TYPE_MISMATCH_ERR
    );
  } else if(before) {

    if( before.closed === true ) {
      throw new OError(
        "InvalidStateError",
        "'before' BrowserTab object is in the closed state and therefore is invalid.",
        DOMException.INVALID_STATE_ERR
      );
    }

    if(before._windowParent && before._windowParent.closed === true ) {
      throw new OError(
        "InvalidStateError",
        "Parent window of 'before' BrowserTab object is in the closed state and therefore is invalid.",
        DOMException.INVALID_STATE_ERR
      );
    }

    // If we're adding this BrowserTab before an existing object then set its insert position correctly
    browserTabProperties.position = before.properties.index;

  }

  // Set parent window to create the tab in
  var windowParent = before && before._windowParent ? before._windowParent : this._parent || OEX.windows.getLastFocused();

  if(windowParent && windowParent.closed === true ) {
    throw new OError(
      "InvalidStateError",
      "Parent window of the current BrowserTab object is in the closed state and therefore is invalid.",
      DOMException.INVALID_STATE_ERR
    );
  }

  var shadowBrowserTab = new BrowserTab( browserTabProperties, windowParent );

  // Sanitized tab properties
  var createTabProperties = {
    'url': shadowBrowserTab.properties.url,
    'active': shadowBrowserTab.properties.active,
    'pinned': shadowBrowserTab.properties.pinned,
    'index': shadowBrowserTab.properties.index
  };

  // Set insert position for the new tab from 'before' attribute, if any
  if( before ) {
    createTabProperties.windowId = before._windowParent ?
                                      before._windowParent.properties.id : createTabProperties.windowId;
  }

  // Add this object to the end of the current tabs collection
  shadowBrowserTab._windowParent.tabs.addTab(shadowBrowserTab, shadowBrowserTab.properties.index);

  // unfocus all other tabs in tab's window parent collection if this tab is set to focused
  if(shadowBrowserTab.properties.active == true ) {
    for(var i = 0, l = shadowBrowserTab._windowParent.tabs.length; i < l; i++) {
      if(shadowBrowserTab._windowParent.tabs[i] !== shadowBrowserTab) {
        shadowBrowserTab._windowParent.tabs[i].properties.active = false;
      }
    }
  }

  // Add this object to the root tab manager
  OEX.tabs.addTab( shadowBrowserTab );

  // Queue platform action or fire immediately if this object is resolved
  Queue.enqueue(this, function(done) {

    chrome.tabs.create(
      createTabProperties,
      function( _tab ) {
        // Update BrowserTab properties
        for(var i in _tab) {
          if(i == 'url') continue;
          shadowBrowserTab.properties[i] = _tab[i];
        }

        // Resolve new tab, if it hasn't been resolved already
        shadowBrowserTab.resolve(true);

        done();

      }.bind(this)
    );

  }.bind(this), true);

  // return shadowBrowserTab from this function before firing these events!
  global.setTimeout(function() {

    shadowBrowserTab._windowParent.tabs.dispatchEvent(new OEvent('create', {
      "tab": shadowBrowserTab,
      "prevWindow": null,
      "prevTabGroup": null,
      "prevPosition": 0
    }));

    // Fire a create event at RootTabsManager
    OEX.tabs.dispatchEvent(new OEvent('create', {
      "tab": shadowBrowserTab,
      "prevWindow": null,
      "prevTabGroup": null,
      "prevPosition": 0
    }));

  }, 50);

  return shadowBrowserTab;

};

BrowserTabManager.prototype.getAll = function() {

  var allTabs = [];

  for(var i = 0, l = this.length; i < l; i++) {
    allTabs[ i ] = this[ i ];
  }

  return allTabs;

};

BrowserTabManager.prototype.getSelected = function() {

  for(var i = 0, l = this.length; i < l; i++) {
    if(this[i].focused == true) {
      return this[i];
    }
  }

  // default
  if(this[0]) {
    this[0].properties.active = true;
  }

  return this[0] || undefined;

};
// Alias of .getSelected()
BrowserTabManager.prototype.getFocused = BrowserTabManager.prototype.getSelected;

BrowserTabManager.prototype.close = function( browserTab ) {

  if( !browserTab || !(browserTab instanceof BrowserTab)) {
    throw new OError(
      "TypeMismatchError",
      "Expected browserTab argument to be of type BrowserTab.",
      DOMException.TYPE_MISMATCH_ERR
    );
  }

  browserTab.close();

};
<|endoftext|>


<commit_before>// load type definitions that come with Cypress module
/// <reference types="cypress" />

/**
 * @param {string} path
 */
function visitAndCheckUrl (path) {
  cy.visitIonicAndSetApiUrl(path)
  cy.wait(2000)
  cy.url().should('include', path)
}
describe('Reminders', function () {
  /**
   * @param {string} variableCategoryName
   */
  function goToCategoryInbox (variableCategoryName) {
    visitAndCheckUrl(`/#/app/reminders-inbox?variableCategoryName=${variableCategoryName}`)
    //cy.get('#TodayReminders', { timeout: 40000 }).should('exist')
    //cy.get('#TodayReminders').children().should('have.length.above', 0)
  }
    /**
     * @param {string} variableCategoryName
     */
    function goToManageReminders (variableCategoryName) {
        if(!variableCategoryName){variableCategoryName = 'Anything'}
        cy.loginWithAccessTokenIfNecessary(`/#/app/variable-list-category/${variableCategoryName}`)
    }
  /**
   * @param {string} frequency
   */
  function setFrequency (frequency) {
    cy.log(`Setting frequency to ${frequency}`)
    cy.get('#frequencySelectorMaterial').click()
      cy.get('md-option').contains(frequency).click({force: true})
  }
  /**
   * @param {number} hour
   * @param {number} minute
   * @param {string} ampm
   */
  function setReminderTime (hour, minute, ampm) {
    cy.log(`Change to ${hour}:${minute} ${ampm} (We want this to be slightly later than other reminders so its always at the top of ` +
            'the reminder inbox.  (Warning: This will fail for 15 minutes every day)')
    cy.get('#materialFirstReminderStartTime', { timeout: 30000 }).click({ force: true })
    cy.get('.dtp-picker-time').contains(hour).click()
    cy.wait(1000)
    cy.get('a').contains(ampm).click()
    cy.wait(1000)
    cy.get('.dtp-picker-time').contains(minute).click()
    cy.wait(1000)
    cy.get('.dtp-btn-ok').contains('OK').click()
    cy.wait(1000)
  }
  function deleteReminders (variableCategoryName) {
      goToManageReminders(variableCategoryName)
    cy.log('Deleting reminders...')
    cy.wait(1000)
    let reminderListSelector = '#remindersList > div'
    let deleted = false
      //cy.debug();
      cy.get("body").then($body => {
          let selector = "#showActionSheet-button > i";
          let numberOfReminders = $body.find(selector).length;
          cy.log(numberOfReminders+" reminders to delete");
          if (numberOfReminders > 0) {   //evaluates as true
              cy.get(selector, { timeout: 30000 })
                  // eslint-disable-next-line no-unused-vars
                  .each(($el, _index, _$list) => {
                      cy.log(`Deleting ${$el.text()} reminder`)
                      cy.wrap($el).click({force: true, timeout: 10000})
                      cy.clickActionSheetButtonContaining('Delete')
                      deleted = true
                  })
          }
      });

    cy.get(reminderListSelector, { timeout: 30000 })
    // eslint-disable-next-line no-unused-vars
            .each(($el, _index, _$list) => {
              let html = $el.html() // $el is a wrapped jQuery element

              if (html.indexOf('showActionSheet') !== -1 && $el.is('visible')) {
                cy.log(`Deleting ${$el.text()} reminder`)
                cy.wrap($el).click()
                cy.clickActionSheetButtonContaining('Delete')
                deleted = true
              } else {
                // It's a header
              }
            })
    if (deleted) {
      cy.log('Waiting for deletions to post...')
      cy.wait(5000)
    }
  }
    function deleteFavorites () {
        cy.loginWithAccessTokenIfNecessary(`/#/app/favorites`)
        cy.log('Deleting favorites...')
        cy.wait(1000)
        let deleted = false
        //cy.debug();
        cy.get("body").then($body => {
            let selector = "#favoriteItemSettings > i";
            let numberOfReminders = $body.find(selector).length;
            cy.log(numberOfReminders+" reminders to delete");
            if (numberOfReminders > 0) {   //evaluates as true
                cy.get(selector, { timeout: 30000 })
                    // eslint-disable-next-line no-unused-vars
                    .each(($el, _index, _$list) => {
                        cy.log(`Deleting ${$el.text()} reminder`)
                        cy.wrap($el).click({force: true, timeout: 10000})
                        cy.clickActionSheetButtonContaining('Delete')
                        deleted = true
                    })
            }
        });
    }
  /**
   * @param {string} unitName
   */
  function changeUnit (unitName) {
    cy.log(`Changing unit to ${unitName}`)
    cy.get('#unitSelectorMaterial').click()
    cy.get('md-option').contains(unitName).click({ force: true })
  }
  /**
   * @param {string} variableName
   * @param {string} [frequency]
   */
  function addReminder (variableName, frequency) {
    cy.log(`Click Add new reminder for ${variableName}`)
    cy.get('#addReminderButton').click({ force: true })
    cy.searchAndClickTopResult(variableName, true)
    cy.get('#reminder-header').contains(variableName, {matchCase: false})
    if (frequency) {
      setFrequency(frequency)
    }
  }
  /**
   * @param {string} variableName
   * @param {string} frequency
   * @param {string} variableCategoryName
   */
  function deleteRemindersAddOneAndGoToCategoryInbox (variableName, frequency, variableCategoryName) {
    deleteReminders(variableCategoryName)
    addReminder(variableName, frequency)
    saveReminderAndGoToCategoryInbox(variableCategoryName)
  }
  /**
   * @param {string} variableCategoryName
   */
  function saveReminderAndGoToCategoryInbox (variableCategoryName) {
    cy.get('#saveButton').click({ force: true })
    cy.wait(10000) // Have to wait for save to complete
    goToCategoryInbox(variableCategoryName)
  }
  it('Creates a goals reminder and skip it', function () {
    let variableName = 'Aaa Test Reminder Goal Skip'
    let variableCategoryName = 'Goals'
    let frequency = '30 minutes'

    deleteRemindersAddOneAndGoToCategoryInbox(variableName, frequency, variableCategoryName)
    cy.get('#notification-skip').click({ force: true })
    cy.get('#notification-skip').should('not.be.visible')
    deleteReminders(variableCategoryName)
  })
  it('Creates a sleep reminder and changes unit', function () {
    let variableName = 'Sleep Duration'
    let variableCategoryName = 'Sleep'

    deleteReminders(variableCategoryName)
    addReminder(variableName)
    //cy.get('#defaultValue').should('not.exist')
    let hour = 8
    let minute = 15
    let ampm = 'AM'

    setReminderTime(hour, minute, ampm)
    cy.get('#saveButton').click({ force: true })

      goToManageReminders(variableCategoryName)
    cy.log('Should not contain reminder time because the frequency is below a day')
    //let firstReminderTime = '#remindersList > div > div > div:nth-child(1) > div.col.col-70 > p';
    let firstReminderTime = '#remindersList'
    let time = `${hour}:${minute}`

    //assertReminderListContains(time);
    cy.get(firstReminderTime).should('contain', time)
    cy.wait(15000) // Have to wait for save to complete
    goToCategoryInbox(variableCategoryName)
    cy.get('#notification-settings').click({ force: true })
    cy.url().should('include', '#/app/reminder-add/')
    cy.get('#reminder-header').contains(variableName, {matchCase: false})
    changeUnit('Minutes')
    saveReminderAndGoToCategoryInbox(variableCategoryName)
    cy.log('Click Record different value/time')
    cy.get('#other-value-time-note-button').click({ force: true })
    cy.url().should('include', '#/app/measurement-add')
    //cy.get('#measurementAddCard > div', {timeout: 10000}).should('contain', variableName);
    //cy.get('#defaultValue').type("480", {force: true});
    deleteReminders(variableCategoryName)
  })
  it('Deletes reminders', function () {
    deleteReminders('Sleep')
  })
  it('Creates a food reminder and snoozes it', function () {
    let variableName = 'Aaa Test Reminder Snooze'
    let variableCategoryName = 'Foods'
    let frequency = '30 minutes'

    deleteRemindersAddOneAndGoToCategoryInbox(variableName, frequency, variableCategoryName)
    cy.get('#notification-snooze').click({ force: true })
    cy.get('#notification-snooze').should('not.be.visible')
    deleteReminders(variableCategoryName)
  })
  it.skip('Creates a symptoms reminder and tracks it', function () {
    let variableName = 'Aaa Test Reminder Variable'
    let variableCategoryName = 'Symptoms'
    let frequency = '30 minutes'
    deleteRemindersAddOneAndGoToCategoryInbox(variableName, frequency, variableCategoryName)
    cy.get('#negativeRatingOptions4').click({ force: true,timeout: 30000 })
    cy.get('#menu-item-chart-search > a').click({ force: true,timeout: 20000 })
    cy.log("waiting for notifications to post after leaving inbox state before checking history...")
    cy.wait(10000)
    cy.searchAndClickTopResult(variableName, true)
    cy.contains(`${variableName} Over Time`, {timeout: 30000})
    cy.get('#menu-more-button').click({ force: true })
    cy.clickActionSheetButtonContaining('History')
    cy.get('#historyItemTitle', { timeout: 30000 }).should('contain', `4/5 ${variableName}`)
    cy.get('#historyItemTitle').click({ force: true })
    cy.get('#menu-more-button').click({ force: true })
    cy.clickActionSheetButtonContaining('Analysis Settings')
    cy.get('#variableName', { timeout: 30000 }).should('contain', variableName)
    cy.log('Waiting for action sheet button to update...')
    cy.wait(1000)
    cy.get('#menu-more-button').click({ force: true })
    cy.clickActionSheetButtonContaining('Delete All')
    cy.get('#yesButton').click({ force: true })
    cy.wait(1000)
    deleteReminders(variableCategoryName)
  })
  it('Selects a reminder time', function () {
    cy.loginWithAccessTokenIfNecessary('/#/app/reminder-add/', false)
    setReminderTime(8, 15, 'AM')
  })
  it('Sets frequency to 30 minutes', function () {
    cy.loginWithAccessTokenIfNecessary('/#/app/reminder-add/', false)
    setFrequency('30 minutes')
  })
  it('Changes unit', function () {
    cy.loginWithAccessTokenIfNecessary('/#/app/reminder-add/', false)
    changeUnit('Minutes')
  })
    it('Adds a favorite and records a measurement with it', function () {
        deleteFavorites()
        cy.loginWithAccessTokenIfNecessary('/#/app/favorites')
        cy.log('Click add a favorite variable')
        cy.get('#addFavoriteBtn').click({ force: true })
        let variableName = 'Aaa Test Treatment'
        cy.searchAndClickTopResult(variableName, true)
        cy.get('#moreOptions').click({ force: true })
        cy.log('Assign default value to 100mg')
        cy.get('#defaultValue').type('100', { force: true })
        cy.get('#saveButton').click({ force: true })
        cy.log('Wait for favorite to save so we are not redirected back to favoriteAdd')
        cy.visitIonicAndSetApiUrl('/#/app/favorites')
        cy.log('Check that favorite was added')
        cy.get('#favoriteItemTitle').should('contain', variableName)
        cy.debug();
        cy.get('#recordDefaultValue', { timeout: 20000 }).should('contain', 'Record ')
        cy.log('Click Record 100 mg')

        cy.get('#recordDefaultValue').click({ force: true, timeout: 20000 })
        cy.get('#favoriteItemTitle').should('contain', '100 mg')
        cy.get('#favoriteItemTitle').should('contain', variableName)
        cy.log(
            'Space out clicks so the first post consistently completes before the second one.  This way we have a consistent 100 value on history page to check.')
        cy.log('Click Record 100 mg')
        //cy.get('#recordDefaultValue').click({ force: true, timeout: 20000 })
        //cy.log('Displayed value from second click (Not sure why test cant detect but it works in real life)')
        //cy.get('#favoriteItemTitle').should('contain', '200 mg')
        cy.get('#favoriteItemTitle').should('contain', variableName)
        cy.log('Click ... settings button')
        cy.get('#favoriteItemSettings', { timeout: 30000 })
            // eslint-disable-next-line no-unused-vars
            .each(($el, _index, _$list) => {
                cy.log(`Deleting ${$el.text()} reminder`)
                cy.wrap($el).click()
                cy.clickActionSheetButtonContaining('Delete')
            })
        //cy.log('Since there are no favorites, the explanation card is showing')
        //cy.get("#noFavoritesExplanation").should('exist');
        //cy.log('There is no favorites list since there are no favorites')
        //cy.get("#favoritesList").should('not.be.visible')
        cy.log('Posted value from second click')
        cy.visitIonicAndSetApiUrl('/#/app/history-all?variableCategoryName=Treatments')
        //TODO: cy.get('#historyItemTitle', { timeout: 30000 }).should('contain', '100 mg '+variableName)
    })
})
<commit_msg>it.skip('Adds a favorite and records a measurement with it<commit_after>// load type definitions that come with Cypress module
/// <reference types="cypress" />

/**
 * @param {string} path
 */
function visitAndCheckUrl (path) {
  cy.visitIonicAndSetApiUrl(path)
  cy.wait(2000)
  cy.url().should('include', path)
}
describe('Reminders', function () {
  /**
   * @param {string} variableCategoryName
   */
  function goToCategoryInbox (variableCategoryName) {
    visitAndCheckUrl(`/#/app/reminders-inbox?variableCategoryName=${variableCategoryName}`)
    //cy.get('#TodayReminders', { timeout: 40000 }).should('exist')
    //cy.get('#TodayReminders').children().should('have.length.above', 0)
  }
    /**
     * @param {string} variableCategoryName
     */
    function goToManageReminders (variableCategoryName) {
        if(!variableCategoryName){variableCategoryName = 'Anything'}
        cy.loginWithAccessTokenIfNecessary(`/#/app/variable-list-category/${variableCategoryName}`)
    }
  /**
   * @param {string} frequency
   */
  function setFrequency (frequency) {
    cy.log(`Setting frequency to ${frequency}`)
    cy.get('#frequencySelectorMaterial').click()
      cy.get('md-option').contains(frequency).click({force: true})
  }
  /**
   * @param {number} hour
   * @param {number} minute
   * @param {string} ampm
   */
  function setReminderTime (hour, minute, ampm) {
    cy.log(`Change to ${hour}:${minute} ${ampm} (We want this to be slightly later than other reminders so its always at the top of ` +
            'the reminder inbox.  (Warning: This will fail for 15 minutes every day)')
    cy.get('#materialFirstReminderStartTime', { timeout: 30000 }).click({ force: true })
    cy.get('.dtp-picker-time').contains(hour).click()
    cy.wait(1000)
    cy.get('a').contains(ampm).click()
    cy.wait(1000)
    cy.get('.dtp-picker-time').contains(minute).click()
    cy.wait(1000)
    cy.get('.dtp-btn-ok').contains('OK').click()
    cy.wait(1000)
  }
  function deleteReminders (variableCategoryName) {
      goToManageReminders(variableCategoryName)
    cy.log('Deleting reminders...')
    cy.wait(1000)
    let reminderListSelector = '#remindersList > div'
    let deleted = false
      //cy.debug();
      cy.get("body").then($body => {
          let selector = "#showActionSheet-button > i";
          let numberOfReminders = $body.find(selector).length;
          cy.log(numberOfReminders+" reminders to delete");
          if (numberOfReminders > 0) {   //evaluates as true
              cy.get(selector, { timeout: 30000 })
                  // eslint-disable-next-line no-unused-vars
                  .each(($el, _index, _$list) => {
                      cy.log(`Deleting ${$el.text()} reminder`)
                      cy.wrap($el).click({force: true, timeout: 10000})
                      cy.clickActionSheetButtonContaining('Delete')
                      deleted = true
                  })
          }
      });

    cy.get(reminderListSelector, { timeout: 30000 })
    // eslint-disable-next-line no-unused-vars
            .each(($el, _index, _$list) => {
              let html = $el.html() // $el is a wrapped jQuery element

              if (html.indexOf('showActionSheet') !== -1 && $el.is('visible')) {
                cy.log(`Deleting ${$el.text()} reminder`)
                cy.wrap($el).click()
                cy.clickActionSheetButtonContaining('Delete')
                deleted = true
              } else {
                // It's a header
              }
            })
    if (deleted) {
      cy.log('Waiting for deletions to post...')
      cy.wait(5000)
    }
  }
    function deleteFavorites () {
        cy.loginWithAccessTokenIfNecessary(`/#/app/favorites`)
        cy.log('Deleting favorites...')
        cy.wait(1000)
        let deleted = false
        //cy.debug();
        cy.get("body").then($body => {
            let selector = "#favoriteItemSettings > i";
            let numberOfReminders = $body.find(selector).length;
            cy.log(numberOfReminders+" reminders to delete");
            if (numberOfReminders > 0) {   //evaluates as true
                cy.get(selector, { timeout: 30000 })
                    // eslint-disable-next-line no-unused-vars
                    .each(($el, _index, _$list) => {
                        cy.log(`Deleting ${$el.text()} reminder`)
                        cy.wrap($el).click({force: true, timeout: 10000})
                        cy.clickActionSheetButtonContaining('Delete')
                        deleted = true
                    })
            }
        });
    }
  /**
   * @param {string} unitName
   */
  function changeUnit (unitName) {
    cy.log(`Changing unit to ${unitName}`)
    cy.get('#unitSelectorMaterial').click()
    cy.get('md-option').contains(unitName).click({ force: true })
  }
  /**
   * @param {string} variableName
   * @param {string} [frequency]
   */
  function addReminder (variableName, frequency) {
    cy.log(`Click Add new reminder for ${variableName}`)
    cy.get('#addReminderButton').click({ force: true })
    cy.searchAndClickTopResult(variableName, true)
    cy.get('#reminder-header').contains(variableName, {matchCase: false})
    if (frequency) {
      setFrequency(frequency)
    }
  }
  /**
   * @param {string} variableName
   * @param {string} frequency
   * @param {string} variableCategoryName
   */
  function deleteRemindersAddOneAndGoToCategoryInbox (variableName, frequency, variableCategoryName) {
    deleteReminders(variableCategoryName)
    addReminder(variableName, frequency)
    saveReminderAndGoToCategoryInbox(variableCategoryName)
  }
  /**
   * @param {string} variableCategoryName
   */
  function saveReminderAndGoToCategoryInbox (variableCategoryName) {
    cy.get('#saveButton').click({ force: true })
    cy.wait(10000) // Have to wait for save to complete
    goToCategoryInbox(variableCategoryName)
  }
  it('Creates a goals reminder and skip it', function () {
    let variableName = 'Aaa Test Reminder Goal Skip'
    let variableCategoryName = 'Goals'
    let frequency = '30 minutes'

    deleteRemindersAddOneAndGoToCategoryInbox(variableName, frequency, variableCategoryName)
    cy.get('#notification-skip').click({ force: true })
    cy.get('#notification-skip').should('not.be.visible')
    deleteReminders(variableCategoryName)
  })
  it('Creates a sleep reminder and changes unit', function () {
    let variableName = 'Sleep Duration'
    let variableCategoryName = 'Sleep'

    deleteReminders(variableCategoryName)
    addReminder(variableName)
    //cy.get('#defaultValue').should('not.exist')
    let hour = 8
    let minute = 15
    let ampm = 'AM'

    setReminderTime(hour, minute, ampm)
    cy.get('#saveButton').click({ force: true })

      goToManageReminders(variableCategoryName)
    cy.log('Should not contain reminder time because the frequency is below a day')
    //let firstReminderTime = '#remindersList > div > div > div:nth-child(1) > div.col.col-70 > p';
    let firstReminderTime = '#remindersList'
    let time = `${hour}:${minute}`

    //assertReminderListContains(time);
    cy.get(firstReminderTime).should('contain', time)
    cy.wait(15000) // Have to wait for save to complete
    goToCategoryInbox(variableCategoryName)
    cy.get('#notification-settings').click({ force: true })
    cy.url().should('include', '#/app/reminder-add/')
    cy.get('#reminder-header').contains(variableName, {matchCase: false})
    changeUnit('Minutes')
    saveReminderAndGoToCategoryInbox(variableCategoryName)
    cy.log('Click Record different value/time')
    cy.get('#other-value-time-note-button').click({ force: true })
    cy.url().should('include', '#/app/measurement-add')
    //cy.get('#measurementAddCard > div', {timeout: 10000}).should('contain', variableName);
    //cy.get('#defaultValue').type("480", {force: true});
    deleteReminders(variableCategoryName)
  })
  it('Deletes reminders', function () {
    deleteReminders('Sleep')
  })
  it('Creates a food reminder and snoozes it', function () {
    let variableName = 'Aaa Test Reminder Snooze'
    let variableCategoryName = 'Foods'
    let frequency = '30 minutes'

    deleteRemindersAddOneAndGoToCategoryInbox(variableName, frequency, variableCategoryName)
    cy.get('#notification-snooze').click({ force: true })
    cy.get('#notification-snooze').should('not.be.visible')
    deleteReminders(variableCategoryName)
  })
  it.skip('Creates a symptoms reminder and tracks it', function () {
    let variableName = 'Aaa Test Reminder Variable'
    let variableCategoryName = 'Symptoms'
    let frequency = '30 minutes'
    deleteRemindersAddOneAndGoToCategoryInbox(variableName, frequency, variableCategoryName)
    cy.get('#negativeRatingOptions4').click({ force: true,timeout: 30000 })
    cy.get('#menu-item-chart-search > a').click({ force: true,timeout: 20000 })
    cy.log("waiting for notifications to post after leaving inbox state before checking history...")
    cy.wait(10000)
    cy.searchAndClickTopResult(variableName, true)
    cy.contains(`${variableName} Over Time`, {timeout: 30000})
    cy.get('#menu-more-button').click({ force: true })
    cy.clickActionSheetButtonContaining('History')
    cy.get('#historyItemTitle', { timeout: 30000 }).should('contain', `4/5 ${variableName}`)
    cy.get('#historyItemTitle').click({ force: true })
    cy.get('#menu-more-button').click({ force: true })
    cy.clickActionSheetButtonContaining('Analysis Settings')
    cy.get('#variableName', { timeout: 30000 }).should('contain', variableName)
    cy.log('Waiting for action sheet button to update...')
    cy.wait(1000)
    cy.get('#menu-more-button').click({ force: true })
    cy.clickActionSheetButtonContaining('Delete All')
    cy.get('#yesButton').click({ force: true })
    cy.wait(1000)
    deleteReminders(variableCategoryName)
  })
  it('Selects a reminder time', function () {
    cy.loginWithAccessTokenIfNecessary('/#/app/reminder-add/', false)
    setReminderTime(8, 15, 'AM')
  })
  it('Sets frequency to 30 minutes', function () {
    cy.loginWithAccessTokenIfNecessary('/#/app/reminder-add/', false)
    setFrequency('30 minutes')
  })
  it('Changes unit', function () {
    cy.loginWithAccessTokenIfNecessary('/#/app/reminder-add/', false)
    changeUnit('Minutes')
  })
    it.skip('Adds a favorite and records a measurement with it', function () {
        deleteFavorites()
        cy.loginWithAccessTokenIfNecessary('/#/app/favorites')
        cy.log('Click add a favorite variable')
        cy.get('#addFavoriteBtn').click({ force: true })
        let variableName = 'Aaa Test Treatment'
        cy.searchAndClickTopResult(variableName, true)
        cy.get('#moreOptions').click({ force: true })
        cy.log('Assign default value to 100mg')
        cy.get('#defaultValue').type('100', { force: true })
        cy.get('#saveButton').click({ force: true })
        cy.log('Wait for favorite to save so we are not redirected back to favoriteAdd')
        cy.visitIonicAndSetApiUrl('/#/app/favorites')
        cy.log('Check that favorite was added')
        cy.get('#favoriteItemTitle').should('contain', variableName)
        cy.debug();
        cy.get('#recordDefaultValue', { timeout: 20000 }).should('contain', 'Record ')
        cy.log('Click Record 100 mg')

        cy.get('#recordDefaultValue').click({ force: true, timeout: 20000 })
        cy.get('#favoriteItemTitle').should('contain', '100 mg')
        cy.get('#favoriteItemTitle').should('contain', variableName)
        cy.log(
            'Space out clicks so the first post consistently completes before the second one.  This way we have a consistent 100 value on history page to check.')
        cy.log('Click Record 100 mg')
        //cy.get('#recordDefaultValue').click({ force: true, timeout: 20000 })
        //cy.log('Displayed value from second click (Not sure why test cant detect but it works in real life)')
        //cy.get('#favoriteItemTitle').should('contain', '200 mg')
        cy.get('#favoriteItemTitle').should('contain', variableName)
        cy.log('Click ... settings button')
        cy.get('#favoriteItemSettings', { timeout: 30000 })
            // eslint-disable-next-line no-unused-vars
            .each(($el, _index, _$list) => {
                cy.log(`Deleting ${$el.text()} reminder`)
                cy.wrap($el).click()
                cy.clickActionSheetButtonContaining('Delete')
            })
        //cy.log('Since there are no favorites, the explanation card is showing')
        //cy.get("#noFavoritesExplanation").should('exist');
        //cy.log('There is no favorites list since there are no favorites')
        //cy.get("#favoritesList").should('not.be.visible')
        cy.log('Posted value from second click')
        cy.visitIonicAndSetApiUrl('/#/app/history-all?variableCategoryName=Treatments')
        //TODO: cy.get('#historyItemTitle', { timeout: 30000 }).should('contain', '100 mg '+variableName)
    })
})
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// These are the cookies we expect to see along the way.
var SET_REMOVE_COOKIE = {
  name: 'testSetRemove',
  value: '42',
  domain: 'a.com',
  hostOnly: true,
  path: '/',
  secure: false,
  httpOnly: false,
  session: false,
  expirationDate: 12345678900,
  storeId: "0"
};

var OVERWRITE_COOKIE_PRE = {
  name: 'testOverwrite',
  value: '42',
  domain: 'a.com',
  hostOnly: true,
  path: '/',
  secure: false,
  httpOnly: false,
  session: false,
  expirationDate: 12345678900,
  storeId: "0"
};

var OVERWRITE_COOKIE_POST = {
  name: 'testOverwrite',
  value: '43',
  domain: 'a.com',
  hostOnly: true,
  path: '/',
  secure: false,
  httpOnly: false,
  session: false,
  expirationDate: 12345678900,
  storeId: "0"
};

chrome.test.runTests([
  function testSet() {
    var testCompleted = chrome.test.callbackAdded();
    var listener = function (info) {
      chrome.test.assertFalse(info.removed);
      chrome.test.assertEq('explicit', info.cause);
      chrome.test.assertEq(SET_REMOVE_COOKIE, info.cookie);
      testCompleted();
    };

    chrome.cookies.onChanged.addListener(listener);
    chrome.cookies.set({
      url: 'http://a.com/path',
      name: 'testSetRemove',
      value: '42',
      expirationDate: 12345678900
    }, function () {
      chrome.cookies.onChanged.removeListener(listener);
    });
  },
  function testRemove() {
    var testCompleted = chrome.test.callbackAdded();
    var listener = function (info) {
      chrome.test.assertTrue(info.removed);
      chrome.test.assertEq('explicit', info.cause);
      chrome.test.assertEq(SET_REMOVE_COOKIE, info.cookie);
      testCompleted();
    };

    chrome.cookies.onChanged.addListener(listener);
    chrome.cookies.remove({
      url: 'http://a.com/path',
      name: 'testSetRemove'
    }, function () {
      chrome.cookies.onChanged.removeListener(listener);
    });
  },
  function overwriteFirstSet() {
    var testCompleted = chrome.test.callbackAdded();
    var listener = function (info) {
      chrome.test.assertFalse(info.removed);
      chrome.test.assertEq('explicit', info.cause);
      chrome.test.assertEq(OVERWRITE_COOKIE_PRE, info.cookie);
      testCompleted();
    };

    chrome.cookies.onChanged.addListener(listener);
    chrome.cookies.set({
      url: 'http://a.com/path',
      name: 'testOverwrite',
      value: '42',
      expirationDate: 12345678900
    }, function () {
      chrome.cookies.onChanged.removeListener(listener);
    });
  },
  function overwriteSecondSet() {
    var removeCompleted = chrome.test.callbackAdded();
    var setCompleted = chrome.test.callbackAdded();
    var listenerRemove = function (info) {
      if (info.removed) {
        chrome.test.assertEq('overwrite', info.cause);
        chrome.test.assertEq(OVERWRITE_COOKIE_PRE, info.cookie);
        removeCompleted();
      }
    };
    var listenerSet = function (info) {
      if (!info.removed) {
        chrome.test.assertEq('explicit', info.cause);
        chrome.test.assertEq(OVERWRITE_COOKIE_POST, info.cookie);
        setCompleted();
      }
    };
    chrome.cookies.onChanged.addListener(listenerRemove);
    chrome.cookies.onChanged.addListener(listenerSet);
    chrome.cookies.set({
      url: 'http://a.com/path',
      name: 'testOverwrite',
      value: '43',
      expirationDate: 12345678900
    }, function () {
      chrome.cookies.onChanged.removeListener(listenerRemove);
      chrome.cookies.onChanged.removeListener(listenerSet);
    });
  },
  function overwriteExpired() {
    var setCompleted = chrome.test.callbackAdded();
    var listener = function (info) {
      chrome.test.assertTrue(info.removed);
      chrome.test.assertEq('expired_overwrite', info.cause);
      chrome.test.assertEq(OVERWRITE_COOKIE_POST, info.cookie);
      setCompleted();
    };
    chrome.cookies.onChanged.addListener(listener);
    chrome.cookies.set({
      url: 'http://a.com/path',
      name: 'testOverwrite',
      value: '43',
      expirationDate: 1
    }, function () {
      chrome.cookies.onChanged.removeListener(listener);
    });
  }
]);
<commit_msg>Correct and simplify the cookie events browser test<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// These are the cookies we expect to see along the way.
var SET_REMOVE_COOKIE = {
  name: 'testSetRemove',
  value: '42',
  domain: 'a.com',
  hostOnly: true,
  path: '/',
  secure: false,
  httpOnly: false,
  session: false,
  expirationDate: 12345678900,
  storeId: "0"
};

var OVERWRITE_COOKIE_PRE = {
  name: 'testOverwrite',
  value: '42',
  domain: 'a.com',
  hostOnly: true,
  path: '/',
  secure: false,
  httpOnly: false,
  session: false,
  expirationDate: 12345678900,
  storeId: "0"
};

var OVERWRITE_COOKIE_POST = {
  name: 'testOverwrite',
  value: '43',
  domain: 'a.com',
  hostOnly: true,
  path: '/',
  secure: false,
  httpOnly: false,
  session: false,
  expirationDate: 12345678900,
  storeId: "0"
};

chrome.test.runTests([
  function testSet() {
    chrome.test.listenOnce(chrome.cookies.onChanged, function (info) {
      chrome.test.assertFalse(info.removed);
      chrome.test.assertEq('explicit', info.cause);
      chrome.test.assertEq(SET_REMOVE_COOKIE, info.cookie);
    });
    chrome.cookies.set({
      url: 'http://a.com/path',
      name: 'testSetRemove',
      value: '42',
      expirationDate: 12345678900
    });
  },
  function testRemove() {
    chrome.test.listenOnce(chrome.cookies.onChanged, function (info) {
      chrome.test.assertTrue(info.removed);
      chrome.test.assertEq('explicit', info.cause);
      chrome.test.assertEq(SET_REMOVE_COOKIE, info.cookie);
    });
    chrome.cookies.remove({
      url: 'http://a.com/path',
      name: 'testSetRemove'
    });
  },
  function overwriteFirstSet() {
    chrome.test.listenOnce(chrome.cookies.onChanged, function (info) {
      chrome.test.assertFalse(info.removed);
      chrome.test.assertEq('explicit', info.cause);
      chrome.test.assertEq(OVERWRITE_COOKIE_PRE, info.cookie);
    });
    chrome.cookies.set({
      url: 'http://a.com/path',
      name: 'testOverwrite',
      value: '42',
      expirationDate: 12345678900
    });
  },
  function overwriteSecondSet() {
    var haveRemoved = false;
    var haveSet = false;
    var done = chrome.test.listenForever(chrome.cookies.onChanged,
      function(info) {
        if (info.removed) {
          chrome.test.assertEq('overwrite', info.cause);
          chrome.test.assertEq(OVERWRITE_COOKIE_PRE, info.cookie);
          chrome.test.assertFalse(haveRemoved);
          chrome.test.assertFalse(haveSet);
          haveRemoved = true;
        } else {
          chrome.test.assertEq('explicit', info.cause);
          chrome.test.assertEq(OVERWRITE_COOKIE_POST, info.cookie);
          chrome.test.assertTrue(haveRemoved);
          chrome.test.assertFalse(haveSet);
          haveSet = true;
        }
        if (haveRemoved && haveSet) {
          done();
        }
      });
    chrome.cookies.set({
      url: 'http://a.com/path',
      name: 'testOverwrite',
      value: '43',
      expirationDate: 12345678900
    });
  },
  function overwriteExpired() {
    chrome.test.listenOnce(chrome.cookies.onChanged, function (info) {
      chrome.test.assertTrue(info.removed);
      chrome.test.assertEq('expired_overwrite', info.cause);
      chrome.test.assertEq(OVERWRITE_COOKIE_POST, info.cookie);
    });
    chrome.cookies.set({
      url: 'http://a.com/path',
      name: 'testOverwrite',
      value: '43',
      expirationDate: 1
    });
  }
]);
<|endoftext|>