<commit_before>// # Ghost Configuration
// Setup your Ghost install for various [environments](http://support.ghost.org/config/#about-environments).

// Ghost runs in `development` mode by default. Full documentation can be found at http://support.ghost.org/config/

var path = require('path'),
    config;

config = {
    // ### Production
    // When running Ghost in the wild, use the production environment.
    // Configure your URL and mail settings here
    production: {
        url: 'http://blog.awslearn.tk:2368',
        mail: {},
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost.db')
            },
            debug: false
        },

        server: {
            host: '127.0.0.1',
            port: '2368'
        }
    },

    // ### Development **(default)**
    development: {
        // The url to use when providing links to the site, E.g. in RSS and email.
        // Change this to your Ghost blog's published URL.
        url: 'http://localhost:2368',

        // Example mail config
        // Visit http://support.ghost.org/mail for instructions
        // ```
        //  mail: {
        //      transport: 'SMTP',
        //      options: {
        //          service: 'Mailgun',
        //          auth: {
        //              user: '', // mailgun username
        //              pass: ''  // mailgun password
        //          }
        //      }
        //  },
        // ```

        // #### Database
        // Ghost supports sqlite3 (default), MySQL & PostgreSQL
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost-dev.db')
            },
            debug: false
        },
        // #### Server
        // Can be host & port (default), or socket
        server: {
            // Host to be passed to node's `net.Server#listen()`
            host: '127.0.0.1',
            // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
            port: '2368'
        },
        // #### Paths
        // Specify where your content directory lives
        paths: {
            contentPath: path.join(__dirname, '/content/')
        }
    },

    // **Developers only need to edit below here**

    // ### Testing
    // Used when developing Ghost to run tests and check the health of Ghost
    // Uses a different port number
    testing: {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost-test.db')
            },
            pool: {
                afterCreate: function (conn, done) {
                    conn.run('PRAGMA synchronous=OFF;' +
                    'PRAGMA journal_mode=MEMORY;' +
                    'PRAGMA locking_mode=EXCLUSIVE;' +
                    'BEGIN EXCLUSIVE; COMMIT;', done);
                }
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    },

    // ### Testing MySQL
    // Used by Travis - Automated testing run through GitHub
    'testing-mysql': {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'mysql',
            connection: {
                host     : '127.0.0.1',
                user     : 'root',
                password : '',
                database : 'ghost_testing',
                charset  : 'utf8'
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    },

    // ### Testing pg
    // Used by Travis - Automated testing run through GitHub
    'testing-pg': {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'pg',
            connection: {
                host     : '127.0.0.1',
                user     : 'postgres',
                password : '',
                database : 'ghost_testing',
                charset  : 'utf8'
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    }
};

module.exports = config;
<commit_msg>sssssssu<commit_after>// # Ghost Configuration
// Setup your Ghost install for various [environments](http://support.ghost.org/config/#about-environments).

// Ghost runs in `development` mode by default. Full documentation can be found at http://support.ghost.org/config/

var path = require('path'),
    config;

config = {
    // ### Production
    // When running Ghost in the wild, use the production environment.
    // Configure your URL and mail settings here
    production: {
        url: 'http://blog.awslearn.tk',
        mail: {},
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost.db')
            },
            debug: false
        },

        server: {
            host: '127.0.0.1',
            port: '2368'
        }
    },

    // ### Development **(default)**
    development: {
        // The url to use when providing links to the site, E.g. in RSS and email.
        // Change this to your Ghost blog's published URL.
        url: 'http://blog.awslearn.tk',

        // Example mail config
        // Visit http://support.ghost.org/mail for instructions
        // ```
        //  mail: {
        //      transport: 'SMTP',
        //      options: {
        //          service: 'Mailgun',
        //          auth: {
        //              user: '', // mailgun username
        //              pass: ''  // mailgun password
        //          }
        //      }
        //  },
        // ```

        // #### Database
        // Ghost supports sqlite3 (default), MySQL & PostgreSQL
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost-dev.db')
            },
            debug: false
        },
        // #### Server
        // Can be host & port (default), or socket
        server: {
            // Host to be passed to node's `net.Server#listen()`
            host: '127.0.0.1',
            // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
            port: '2368'
        },
        // #### Paths
        // Specify where your content directory lives
        paths: {
            contentPath: path.join(__dirname, '/content/')
        }
    },

    // **Developers only need to edit below here**

    // ### Testing
    // Used when developing Ghost to run tests and check the health of Ghost
    // Uses a different port number
    testing: {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost-test.db')
            },
            pool: {
                afterCreate: function (conn, done) {
                    conn.run('PRAGMA synchronous=OFF;' +
                    'PRAGMA journal_mode=MEMORY;' +
                    'PRAGMA locking_mode=EXCLUSIVE;' +
                    'BEGIN EXCLUSIVE; COMMIT;', done);
                }
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    },

    // ### Testing MySQL
    // Used by Travis - Automated testing run through GitHub
    'testing-mysql': {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'mysql',
            connection: {
                host     : '127.0.0.1',
                user     : 'root',
                password : '',
                database : 'ghost_testing',
                charset  : 'utf8'
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    },

    // ### Testing pg
    // Used by Travis - Automated testing run through GitHub
    'testing-pg': {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'pg',
            connection: {
                host     : '127.0.0.1',
                user     : 'postgres',
                password : '',
                database : 'ghost_testing',
                charset  : 'utf8'
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    }
};

module.exports = config;
<|endoftext|>


<commit_before>define(function(require, exports, module) {
    main.consumes = [
        "Plugin", "c9", "settings", "ui", "layout", "tooltip",
        "anims", "menus", "tabManager", "preferences", "save"
    ];
    main.provides = ["autosave"];
    return main;

    function main(options, imports, register) {
        var c9       = imports.c9;
        var Plugin   = imports.Plugin;
        var settings = imports.settings;
        var save     = imports.save;
        var tooltip  = imports.tooltip;
        var tabs     = imports.tabManager;
        var prefs    = imports.preferences;
        var stripws  = imports.stripws;

        /***** Initialization *****/

        var plugin = new Plugin("Ajax.org", main.consumes);
        // var emit   = plugin.getEmitter();

        var INTERVAL       = 60000;
        var CHANGE_TIMEOUT = 500;

        var docChangeTimeout   = null;
        var btnSave, autosave, saveInterval;

        var loaded = false;
        function load(){
            if (loaded) return false;
            loaded = true;

            prefs.add({
                "General" : {
                    "General" : {
                        position : 50,
                        "Enable Auto-Save" : {
                            type     : "checkbox",
                            position : 1000,
                            path     : "user/general/@autosave"
                        }
                    }
                }
            }, plugin);

            settings.on("read", function(e){
                settings.setDefaults("user/general", [["autosave", "false"]]);
                autosave = settings.getBool("user/general/@autosave");
                transformButton();
            }, plugin);

            settings.on("user/general", function(e) {
                autosave = settings.getBool("user/general/@autosave");
                transformButton();
            }, plugin);

            // when we're back online we'll trigger an autosave if enabled
            c9.on("stateChange", function(e) {
                if (e.state & c9.STORAGE && !(e.last & c9.STORAGE))
                    check();
            }, plugin);

            save.getElement("btnSave", function(btn){
                btnSave = btn;
                transformButton();
            });

            tabs.on("tabCreate", function(e){
                var tab = e.tab;
                tab.document.undoManager.on("change", function(e){
                    if (!autosave || !tab.path)
                        return;

                    clearTimeout(docChangeTimeout);
                    docChangeTimeout = setTimeout(function() {
                        stripws.disable();
                        saveTab(tab);
                    }, CHANGE_TIMEOUT);
                }, plugin);
            }, plugin);

            tabs.on("tabDestroy", function(e){
                if (!e.tab.path)
                    return;

                if (tabs.getTabs().length == 1)
                    btnSave.hide();

                saveTab(e.tab);
            }, plugin);

            save.on("beforeWarn", function(e){
                if (autosave && !e.tab.document.meta.newfile) {
                    saveTab(e.tab);
                    return false;
                }
            }, plugin);
        }

        function transformButton(){
            if (!btnSave) return;
            if (btnSave.autosave === autosave) return;

            if (autosave) {
                // Transform btnSave
                btnSave.setAttribute("caption", "");
                btnSave.setAttribute("margin", "0 20");
                btnSave.removeAttribute("tooltip");
                btnSave.removeAttribute("command");
                apf.setStyleClass(btnSave.$ext, "btnSave");

                tooltip.add(btnSave, {
                    message : "Changes to your file are automatically saved.
\
                        View all your changes through the Revision History pane. \
                        Rollback to a previous state, or make comparisons.",
                    width : "250px",
                    hideonclick : true
                }, plugin);
            }
            else {

            }

            btnSave.autosave = autosave;
        }

        /***** Helpers *****/

        function check() {
            if (!autosave) return;

            var pages = tabs.getTabs();
            for (var tab, i = 0, l = pages.length; i < l; i++) {
                if ((tab = pages[i]).document.changed && tab.path)
                    saveTab(tab)
            }
        }

        function saveTab(tab, force) {
            if (!autosave) return;

            if (!c9.has(c9.STORAGE)) {
                save.setSavingState(tab, "offline");
                return;
            }

            if (!force && (!tab.path
              || !tab.document.changed
              || tab.document.meta.newfile
              || tab.document.meta.error))
                return;

            save.save(tab, { silentsave: true, timeout: 1 }, function() {
                stripws.enable();
            });
        }

        /***** Lifecycle *****/

        plugin.on("load", function(){
            load();
        });
        plugin.on("enable", function(){
            autosave = settings.getBool("user/general/@autosave");
            transformButton();

        });
        plugin.on("disable", function(){
            autosave = false;
            transformButton();
        });
        plugin.on("unload", function(){
            if (saveInterval)
                clearInterval(saveInterval);

            loaded = false;
        });

        /***** Register and define API *****/

        /**
         * Implements auto save for Cloud9 IDE. When the user enables autosave
         * the contents of files are automatically saved about 500ms after the
         * change is made.
         * @singleton
         **/
        plugin.freezePublicAPI({ });

        register(null, {
            autosave: plugin
        });
    }
});<commit_msg>ace plugins docs<commit_after>define(function(require, exports, module) {
    main.consumes = [
        "Plugin", "c9", "settings", "ui", "layout", "tooltip",
        "anims", "menus", "tabManager", "preferences", "save",
        "ace.stripws"
    ];
    main.provides = ["autosave"];
    return main;

    function main(options, imports, register) {
        var c9       = imports.c9;
        var Plugin   = imports.Plugin;
        var settings = imports.settings;
        var save     = imports.save;
        var tooltip  = imports.tooltip;
        var tabs     = imports.tabManager;
        var prefs    = imports.preferences;
        var stripws  = imports["ace.stripws"];

        /***** Initialization *****/

        var plugin = new Plugin("Ajax.org", main.consumes);
        // var emit   = plugin.getEmitter();

        var INTERVAL       = 60000;
        var CHANGE_TIMEOUT = 500;

        var docChangeTimeout   = null;
        var btnSave, autosave, saveInterval;

        var loaded = false;
        function load(){
            if (loaded) return false;
            loaded = true;

            prefs.add({
                "General" : {
                    "General" : {
                        position : 50,
                        "Enable Auto-Save" : {
                            type     : "checkbox",
                            position : 1000,
                            path     : "user/general/@autosave"
                        }
                    }
                }
            }, plugin);

            settings.on("read", function(e){
                settings.setDefaults("user/general", [["autosave", "false"]]);
                autosave = settings.getBool("user/general/@autosave");
                transformButton();
            }, plugin);

            settings.on("user/general", function(e) {
                autosave = settings.getBool("user/general/@autosave");
                transformButton();
            }, plugin);

            // when we're back online we'll trigger an autosave if enabled
            c9.on("stateChange", function(e) {
                if (e.state & c9.STORAGE && !(e.last & c9.STORAGE))
                    check();
            }, plugin);

            save.getElement("btnSave", function(btn){
                btnSave = btn;
                transformButton();
            });

            tabs.on("tabCreate", function(e){
                var tab = e.tab;
                tab.document.undoManager.on("change", function(e){
                    if (!autosave || !tab.path)
                        return;

                    clearTimeout(docChangeTimeout);
                    docChangeTimeout = setTimeout(function() {
                        stripws.disable();
                        saveTab(tab);
                    }, CHANGE_TIMEOUT);
                }, plugin);
            }, plugin);

            tabs.on("tabDestroy", function(e){
                if (!e.tab.path)
                    return;

                if (tabs.getTabs().length == 1)
                    btnSave.hide();

                saveTab(e.tab);
            }, plugin);

            save.on("beforeWarn", function(e){
                if (autosave && !e.tab.document.meta.newfile) {
                    saveTab(e.tab);
                    return false;
                }
            }, plugin);
        }

        function transformButton(){
            if (!btnSave) return;
            if (btnSave.autosave === autosave) return;

            if (autosave) {
                // Transform btnSave
                btnSave.setAttribute("caption", "");
                btnSave.setAttribute("margin", "0 20");
                btnSave.removeAttribute("tooltip");
                btnSave.removeAttribute("command");
                apf.setStyleClass(btnSave.$ext, "btnSave");

                tooltip.add(btnSave, {
                    message : "Changes to your file are automatically saved.
\
                        View all your changes through the Revision History pane. \
                        Rollback to a previous state, or make comparisons.",
                    width : "250px",
                    hideonclick : true
                }, plugin);
            }
            else {

            }

            btnSave.autosave = autosave;
        }

        /***** Helpers *****/

        function check() {
            if (!autosave) return;

            var pages = tabs.getTabs();
            for (var tab, i = 0, l = pages.length; i < l; i++) {
                if ((tab = pages[i]).document.changed && tab.path)
                    saveTab(tab)
            }
        }

        function saveTab(tab, force) {
            if (!autosave) return;

            if (!c9.has(c9.STORAGE)) {
                save.setSavingState(tab, "offline");
                return;
            }

            if (!force && (!tab.path
              || !tab.document.changed
              || tab.document.meta.newfile
              || tab.document.meta.error))
                return;

            save.save(tab, { silentsave: true, timeout: 1 }, function() {
                stripws.enable();
            });
        }

        /***** Lifecycle *****/

        plugin.on("load", function(){
            load();
        });
        plugin.on("enable", function(){
            autosave = settings.getBool("user/general/@autosave");
            transformButton();

        });
        plugin.on("disable", function(){
            autosave = false;
            transformButton();
        });
        plugin.on("unload", function(){
            if (saveInterval)
                clearInterval(saveInterval);

            loaded = false;
        });

        /***** Register and define API *****/

        /**
         * Implements auto save for Cloud9 IDE. When the user enables autosave
         * the contents of files are automatically saved about 500ms after the
         * change is made.
         * @singleton
         **/
        plugin.freezePublicAPI({ });

        register(null, {
            autosave: plugin
        });
    }
});<|endoftext|>


<commit_before>var d3 = require('d3');

var Plotly = require('@lib/index');
var Plots = require('@src/plots/plots');
var Lib = require('@src/lib');

var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var mouseEvent = require('../assets/mouse_event');
var selectButton = require('../assets/modebar_button');
var customMatchers = require('../assets/custom_matchers');

/*
 * WebGL interaction test cases fail on the CircleCI
 * most likely due to a WebGL/driver issue
 *
 */

var PLOT_DELAY = 100;
var MOUSE_DELAY = 20;

describe('Test gl plot interactions', function() {
    'use strict';

    var gd;

    beforeEach(function() {
        jasmine.addMatchers(customMatchers);
    });

    afterEach(function() {
        var fullLayout = gd._fullLayout,
            sceneIds;

        sceneIds = Plots.getSubplotIds(fullLayout, 'gl3d');
        sceneIds.forEach(function(id) {
            fullLayout[id]._scene.destroy();
        });

        sceneIds = Plots.getSubplotIds(fullLayout, 'gl2d');
        sceneIds.forEach(function(id) {
            var scene2d = fullLayout._plots[id]._scene2d;
            scene2d.stopped = true;
            scene2d.destroy();
        });

        destroyGraphDiv();
    });

    describe('gl3d plots', function() {
        var mock = require('@mocks/gl3d_marker-arrays.json');
        var gd;

        function mouseEventScatter3d(type, opts) {
            mouseEvent(type, 605, 271, opts);
        }

        beforeEach(function(done) {
            gd = createGraphDiv();
            Plotly.plot(gd, mock.data, mock.layout).then(done);
        });

        describe('scatter3d hover', function() {
            var node, ptData;

            beforeEach(function(done) {
                gd.on('plotly_hover', function(eventData) {
                    ptData = eventData.points[0];
                });

                setTimeout(function() {
                    mouseEventScatter3d('mouseover');
                    setTimeout(done, MOUSE_DELAY);
                }, PLOT_DELAY);
            });

            it('should have', function() {
                node = d3.selectAll('canvas');
                expect(node[0].length).toEqual(1, 'one canvas node');

                node = d3.selectAll('g.hovertext');
                expect(node.size()).toEqual(1, 'one hover text group');

                node = d3.selectAll('g.hovertext').selectAll('tspan')[0];
                expect(node[0].innerHTML).toEqual('x: 140.72', 'x val on hover');
                expect(node[1].innerHTML).toEqual('y: −96.97', 'y val on hover');
                expect(node[2].innerHTML).toEqual('z: −96.97', 'z val on hover');

                expect(Object.keys(ptData)).toEqual([
                    'x', 'y', 'z',
                    'data', 'fullData', 'curveNumber', 'pointNumber'
                ], 'correct hover data fields');

                expect(ptData.x).toBe('140.72', 'x val hover data');
                expect(ptData.y).toBe('−96.97', 'y val hover data');
                expect(ptData.z).toEqual('−96.97', 'z val hover data');
                expect(ptData.curveNumber).toEqual(0, 'curveNumber hover data');
                expect(ptData.pointNumber).toEqual(2, 'pointNumber hover data');
            });

        });

        describe('scatter3d click events', function() {
            var ptData;

            beforeEach(function(done) {
                gd.on('plotly_click', function(eventData) {
                    ptData = eventData.points[0];
                });

                setTimeout(function() {

                    // N.B. gl3d click events are 'mouseover' events
                    // with button 1 pressed
                    mouseEventScatter3d('mouseover', {buttons: 1});
                    setTimeout(done, MOUSE_DELAY);
                }, PLOT_DELAY);
            });

            it('should have', function() {
                expect(Object.keys(ptData)).toEqual([
                    'x', 'y', 'z',
                    'data', 'fullData', 'curveNumber', 'pointNumber'
                ], 'correct hover data fields');

                expect(ptData.x).toBe('140.72', 'x val click data');
                expect(ptData.y).toBe('−96.97', 'y val click data');
                expect(ptData.z).toEqual('−96.97', 'z val click data');
                expect(ptData.curveNumber).toEqual(0, 'curveNumber click data');
                expect(ptData.pointNumber).toEqual(2, 'pointNumber click data');
            });
        });
    });

    describe('gl2d plots', function() {
        var mock = require('@mocks/gl2d_10.json');

        beforeEach(function(done) {
            Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(done);
        });

        it('has one *canvas* node', function() {
            var nodes = d3.selectAll('canvas');
            expect(nodes[0].length).toEqual(1);
        });
    });

    describe('gl3d modebar click handlers', function() {
        var gd, modeBar;

        beforeEach(function(done) {
            var mockData = [{
                type: 'scatter3d'
            }, {
                type: 'surface', scene: 'scene2'
            }];

            var mockLayout = {
                scene: { camera: { eye: { x: 0.1, y: 0.1, z: 1 }}},
                scene2: { camera: { eye: { x: 2.5, y: 2.5, z: 2.5 }}}
            };

            gd = createGraphDiv();
            Plotly.plot(gd, mockData, mockLayout).then(function() {
                modeBar = gd._fullLayout._modeBar;
                done();
            });
        });

        function assertScenes(cont, attr, val) {
            var sceneIds = Plots.getSubplotIds(cont, 'gl3d');

            sceneIds.forEach(function(sceneId) {
                var thisVal = Lib.nestedProperty(cont[sceneId], attr).get();
                expect(thisVal).toEqual(val);
            });
        }

        describe('button zoom3d', function() {
            it('should updates the scene dragmode and dragmode button', function() {
                var buttonTurntable = selectButton(modeBar, 'tableRotation'),
                    buttonZoom3d = selectButton(modeBar, 'zoom3d');

                assertScenes(gd._fullLayout, 'dragmode', 'turntable');
                expect(buttonTurntable.isActive()).toBe(true);
                expect(buttonZoom3d.isActive()).toBe(false);

                buttonZoom3d.click();
                assertScenes(gd.layout, 'dragmode', 'zoom');
                expect(gd.layout.dragmode).toBe(undefined);
                expect(gd._fullLayout.dragmode).toBe('zoom');
                expect(buttonTurntable.isActive()).toBe(false);
                expect(buttonZoom3d.isActive()).toBe(true);

                buttonTurntable.click();
                assertScenes(gd._fullLayout, 'dragmode', 'turntable');
                expect(buttonTurntable.isActive()).toBe(true);
                expect(buttonZoom3d.isActive()).toBe(false);
            });
        });

        describe('button pan3d', function() {
            it('should updates the scene dragmode and dragmode button', function() {
                var buttonTurntable = selectButton(modeBar, 'tableRotation'),
                    buttonPan3d = selectButton(modeBar, 'pan3d');

                assertScenes(gd._fullLayout, 'dragmode', 'turntable');
                expect(buttonTurntable.isActive()).toBe(true);
                expect(buttonPan3d.isActive()).toBe(false);

                buttonPan3d.click();
                assertScenes(gd.layout, 'dragmode', 'pan');
                expect(gd.layout.dragmode).toBe(undefined);
                expect(gd._fullLayout.dragmode).toBe('zoom');
                expect(buttonTurntable.isActive()).toBe(false);
                expect(buttonPan3d.isActive()).toBe(true);

                buttonTurntable.click();
                assertScenes(gd._fullLayout, 'dragmode', 'turntable');
                expect(buttonTurntable.isActive()).toBe(true);
                expect(buttonPan3d.isActive()).toBe(false);
            });
        });

        describe('button orbitRotation', function() {
            it('should updates the scene dragmode and dragmode button', function() {
                var buttonTurntable = selectButton(modeBar, 'tableRotation'),
                    buttonOrbit = selectButton(modeBar, 'orbitRotation');

                assertScenes(gd._fullLayout, 'dragmode', 'turntable');
                expect(buttonTurntable.isActive()).toBe(true);
                expect(buttonOrbit.isActive()).toBe(false);

                buttonOrbit.click();
                assertScenes(gd.layout, 'dragmode', 'orbit');
                expect(gd.layout.dragmode).toBe(undefined);
                expect(gd._fullLayout.dragmode).toBe('zoom');
                expect(buttonTurntable.isActive()).toBe(false);
                expect(buttonOrbit.isActive()).toBe(true);

                buttonTurntable.click();
                assertScenes(gd._fullLayout, 'dragmode', 'turntable');
                expect(buttonTurntable.isActive()).toBe(true);
                expect(buttonOrbit.isActive()).toBe(false);
            });
        });

        describe('buttons resetCameraDefault3d and resetCameraLastSave3d', function() {
            // changes in scene objects are not instantaneous
            var DELAY = 500;

            it('should update the scene camera', function(done) {
                var sceneLayout = gd._fullLayout.scene,
                    sceneLayout2 = gd._fullLayout.scene2,
                    scene = sceneLayout._scene,
                    scene2 = sceneLayout2._scene;

                expect(sceneLayout.camera.eye)
                    .toEqual({x: 0.1, y: 0.1, z: 1});
                expect(sceneLayout2.camera.eye)
                    .toEqual({x: 2.5, y: 2.5, z: 2.5});

                selectButton(modeBar, 'resetCameraDefault3d').click();
                setTimeout(function() {
                    expect(sceneLayout.camera.eye)
                        .toEqual({x: 0.1, y: 0.1, z: 1}, 'does not change the layout objects');
                    expect(scene.camera.eye)
                        .toBeCloseToArray([1.25, 1.25, 1.25], 4);
                    expect(sceneLayout2.camera.eye)
                        .toEqual({x: 2.5, y: 2.5, z: 2.5}, 'does not change the layout objects');
                    expect(scene2.camera.eye)
                        .toBeCloseToArray([1.25, 1.25, 1.25], 4);

                    selectButton(modeBar, 'resetCameraLastSave3d').click();
                    setTimeout(function() {
                        expect(sceneLayout.camera.eye)
                            .toEqual({x: 0.1, y: 0.1, z: 1}, 'does not change the layout objects');
                        expect(scene.camera.eye)
                            .toBeCloseToArray([ 0.1, 0.1, 1], 4);
                        expect(sceneLayout2.camera.eye)
                            .toEqual({x: 2.5, y: 2.5, z: 2.5}, 'does not change the layout objects');
                        expect(scene2.camera.eye)
                            .toBeCloseToArray([2.5, 2.5, 2.5], 4);

                        done();
                    }, DELAY);
                }, DELAY);
            });
        });

        describe('button hoverClosest3d', function() {
            it('should update the scene hovermode and spikes', function() {
                var buttonHover = selectButton(modeBar, 'hoverClosest3d');

                assertScenes(gd._fullLayout, 'hovermode', 'closest');
                expect(buttonHover.isActive()).toBe(true);

                buttonHover.click();
                assertScenes(gd._fullLayout, 'hovermode', false);
                assertScenes(gd._fullLayout, 'xaxis.showspikes', false);
                assertScenes(gd._fullLayout, 'yaxis.showspikes', false);
                assertScenes(gd._fullLayout, 'zaxis.showspikes', false);
                expect(buttonHover.isActive()).toBe(false);

                buttonHover.click();
                assertScenes(gd._fullLayout, 'hovermode', 'closest');
                assertScenes(gd._fullLayout, 'xaxis.showspikes', true);
                assertScenes(gd._fullLayout, 'yaxis.showspikes', true);
                assertScenes(gd._fullLayout, 'zaxis.showspikes', true);
                expect(buttonHover.isActive()).toBe(true);
            });
        });

    });
});
<commit_msg>robustify gl interaction tests:<commit_after>var d3 = require('d3');

var Plotly = require('@lib/index');
var Plots = require('@src/plots/plots');
var Lib = require('@src/lib');

var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var mouseEvent = require('../assets/mouse_event');
var selectButton = require('../assets/modebar_button');
var customMatchers = require('../assets/custom_matchers');

/*
 * WebGL interaction test cases fail on the CircleCI
 * most likely due to a WebGL/driver issue
 *
 */

var PLOT_DELAY = 200;
var MOUSE_DELAY = 20;
var MODEBAR_DELAY = 500;

describe('Test gl plot interactions', function() {
    'use strict';

    var gd;

    beforeEach(function() {
        jasmine.addMatchers(customMatchers);
    });

    afterEach(function() {
        var fullLayout = gd._fullLayout,
            sceneIds;

        sceneIds = Plots.getSubplotIds(fullLayout, 'gl3d');
        sceneIds.forEach(function(id) {
            fullLayout[id]._scene.destroy();
        });

        sceneIds = Plots.getSubplotIds(fullLayout, 'gl2d');
        sceneIds.forEach(function(id) {
            var scene2d = fullLayout._plots[id]._scene2d;
            scene2d.stopped = true;
            scene2d.destroy();
        });

        destroyGraphDiv();
    });

    function delay(done) {
        setTimeout(function() {
            done();
        }, PLOT_DELAY);
    }

    describe('gl3d plots', function() {
        var mock = require('@mocks/gl3d_marker-arrays.json');

        function mouseEventScatter3d(type, opts) {
            mouseEvent(type, 605, 271, opts);
        }

        beforeEach(function(done) {
            gd = createGraphDiv();
            Plotly.plot(gd, mock.data, mock.layout).then(function() {
                delay(done);
            });
        });

        describe('scatter3d hover', function() {

            var node, ptData;

            beforeEach(function(done) {
                gd.on('plotly_hover', function(eventData) {
                    ptData = eventData.points[0];
                });

                mouseEventScatter3d('mouseover');

                setTimeout(function() {
                    done();
                }, MOUSE_DELAY);
            });

            it('should have', function() {
                node = d3.selectAll('canvas');
                expect(node[0].length).toEqual(1, 'one canvas node');

                node = d3.selectAll('g.hovertext');
                expect(node.size()).toEqual(1, 'one hover text group');

                node = d3.selectAll('g.hovertext').selectAll('tspan')[0];
                expect(node[0].innerHTML).toEqual('x: 140.72', 'x val on hover');
                expect(node[1].innerHTML).toEqual('y: −96.97', 'y val on hover');
                expect(node[2].innerHTML).toEqual('z: −96.97', 'z val on hover');

                expect(Object.keys(ptData)).toEqual([
                    'x', 'y', 'z',
                    'data', 'fullData', 'curveNumber', 'pointNumber'
                ], 'correct hover data fields');

                expect(ptData.x).toBe('140.72', 'x val hover data');
                expect(ptData.y).toBe('−96.97', 'y val hover data');
                expect(ptData.z).toEqual('−96.97', 'z val hover data');
                expect(ptData.curveNumber).toEqual(0, 'curveNumber hover data');
                expect(ptData.pointNumber).toEqual(2, 'pointNumber hover data');
            });

        });

        describe('scatter3d click events', function() {
            var ptData;

            beforeEach(function(done) {
                gd.on('plotly_click', function(eventData) {
                    ptData = eventData.points[0];
                });

                // N.B. gl3d click events are 'mouseover' events
                // with button 1 pressed
                mouseEventScatter3d('mouseover', {buttons: 1});

                setTimeout(function() {
                    done();
                }, MOUSE_DELAY);
            });

            it('should have', function() {
                expect(Object.keys(ptData)).toEqual([
                    'x', 'y', 'z',
                    'data', 'fullData', 'curveNumber', 'pointNumber'
                ], 'correct hover data fields');

                expect(ptData.x).toBe('140.72', 'x val click data');
                expect(ptData.y).toBe('−96.97', 'y val click data');
                expect(ptData.z).toEqual('−96.97', 'z val click data');
                expect(ptData.curveNumber).toEqual(0, 'curveNumber click data');
                expect(ptData.pointNumber).toEqual(2, 'pointNumber click data');
            });
        });
    });

    describe('gl2d plots', function() {
        var mock = require('@mocks/gl2d_10.json');

        beforeEach(function(done) {
            gd = createGraphDiv();

            Plotly.plot(gd, mock.data, mock.layout).then(function() {
                delay(done);
            });
        });

        it('has one *canvas* node', function() {
            var nodes = d3.selectAll('canvas');
            expect(nodes[0].length).toEqual(1);
        });
    });

    describe('gl3d modebar click handlers', function() {
        var modeBar;

        beforeEach(function(done) {
            var mockData = [{
                type: 'scatter3d'
            }, {
                type: 'surface', scene: 'scene2'
            }];

            var mockLayout = {
                scene: { camera: { eye: { x: 0.1, y: 0.1, z: 1 }}},
                scene2: { camera: { eye: { x: 2.5, y: 2.5, z: 2.5 }}}
            };

            gd = createGraphDiv();
            Plotly.plot(gd, mockData, mockLayout).then(function() {
                modeBar = gd._fullLayout._modeBar;

                delay(done);
            });
        });

        function assertScenes(cont, attr, val) {
            var sceneIds = Plots.getSubplotIds(cont, 'gl3d');

            sceneIds.forEach(function(sceneId) {
                var thisVal = Lib.nestedProperty(cont[sceneId], attr).get();
                expect(thisVal).toEqual(val);
            });
        }

        describe('button zoom3d', function() {
            it('should updates the scene dragmode and dragmode button', function() {
                var buttonTurntable = selectButton(modeBar, 'tableRotation'),
                    buttonZoom3d = selectButton(modeBar, 'zoom3d');

                assertScenes(gd._fullLayout, 'dragmode', 'turntable');
                expect(buttonTurntable.isActive()).toBe(true);
                expect(buttonZoom3d.isActive()).toBe(false);

                buttonZoom3d.click();
                assertScenes(gd.layout, 'dragmode', 'zoom');
                expect(gd.layout.dragmode).toBe(undefined);
                expect(gd._fullLayout.dragmode).toBe('zoom');
                expect(buttonTurntable.isActive()).toBe(false);
                expect(buttonZoom3d.isActive()).toBe(true);

                buttonTurntable.click();
                assertScenes(gd._fullLayout, 'dragmode', 'turntable');
                expect(buttonTurntable.isActive()).toBe(true);
                expect(buttonZoom3d.isActive()).toBe(false);
            });
        });

        describe('button pan3d', function() {
            it('should updates the scene dragmode and dragmode button', function() {
                var buttonTurntable = selectButton(modeBar, 'tableRotation'),
                    buttonPan3d = selectButton(modeBar, 'pan3d');

                assertScenes(gd._fullLayout, 'dragmode', 'turntable');
                expect(buttonTurntable.isActive()).toBe(true);
                expect(buttonPan3d.isActive()).toBe(false);

                buttonPan3d.click();
                assertScenes(gd.layout, 'dragmode', 'pan');
                expect(gd.layout.dragmode).toBe(undefined);
                expect(gd._fullLayout.dragmode).toBe('zoom');
                expect(buttonTurntable.isActive()).toBe(false);
                expect(buttonPan3d.isActive()).toBe(true);

                buttonTurntable.click();
                assertScenes(gd._fullLayout, 'dragmode', 'turntable');
                expect(buttonTurntable.isActive()).toBe(true);
                expect(buttonPan3d.isActive()).toBe(false);
            });
        });

        describe('button orbitRotation', function() {
            it('should updates the scene dragmode and dragmode button', function() {
                var buttonTurntable = selectButton(modeBar, 'tableRotation'),
                    buttonOrbit = selectButton(modeBar, 'orbitRotation');

                assertScenes(gd._fullLayout, 'dragmode', 'turntable');
                expect(buttonTurntable.isActive()).toBe(true);
                expect(buttonOrbit.isActive()).toBe(false);

                buttonOrbit.click();
                assertScenes(gd.layout, 'dragmode', 'orbit');
                expect(gd.layout.dragmode).toBe(undefined);
                expect(gd._fullLayout.dragmode).toBe('zoom');
                expect(buttonTurntable.isActive()).toBe(false);
                expect(buttonOrbit.isActive()).toBe(true);

                buttonTurntable.click();
                assertScenes(gd._fullLayout, 'dragmode', 'turntable');
                expect(buttonTurntable.isActive()).toBe(true);
                expect(buttonOrbit.isActive()).toBe(false);
            });
        });

        describe('buttons resetCameraDefault3d and resetCameraLastSave3d', function() {
            it('should update the scene camera', function(done) {
                var sceneLayout = gd._fullLayout.scene,
                    sceneLayout2 = gd._fullLayout.scene2,
                    scene = sceneLayout._scene,
                    scene2 = sceneLayout2._scene;

                expect(sceneLayout.camera.eye)
                    .toEqual({x: 0.1, y: 0.1, z: 1});
                expect(sceneLayout2.camera.eye)
                    .toEqual({x: 2.5, y: 2.5, z: 2.5});

                selectButton(modeBar, 'resetCameraDefault3d').click();
                setTimeout(function() {
                    expect(sceneLayout.camera.eye)
                        .toEqual({x: 0.1, y: 0.1, z: 1}, 'does not change the layout objects');
                    expect(scene.camera.eye)
                        .toBeCloseToArray([1.25, 1.25, 1.25], 4);
                    expect(sceneLayout2.camera.eye)
                        .toEqual({x: 2.5, y: 2.5, z: 2.5}, 'does not change the layout objects');
                    expect(scene2.camera.eye)
                        .toBeCloseToArray([1.25, 1.25, 1.25], 4);

                    selectButton(modeBar, 'resetCameraLastSave3d').click();
                    setTimeout(function() {
                        expect(sceneLayout.camera.eye)
                            .toEqual({x: 0.1, y: 0.1, z: 1}, 'does not change the layout objects');
                        expect(scene.camera.eye)
                            .toBeCloseToArray([ 0.1, 0.1, 1], 4);
                        expect(sceneLayout2.camera.eye)
                            .toEqual({x: 2.5, y: 2.5, z: 2.5}, 'does not change the layout objects');
                        expect(scene2.camera.eye)
                            .toBeCloseToArray([2.5, 2.5, 2.5], 4);

                        done();
                    }, MODEBAR_DELAY);
                }, MODEBAR_DELAY);
            });
        });

        describe('button hoverClosest3d', function() {
            it('should update the scene hovermode and spikes', function() {
                var buttonHover = selectButton(modeBar, 'hoverClosest3d');

                assertScenes(gd._fullLayout, 'hovermode', 'closest');
                expect(buttonHover.isActive()).toBe(true);

                buttonHover.click();
                assertScenes(gd._fullLayout, 'hovermode', false);
                assertScenes(gd._fullLayout, 'xaxis.showspikes', false);
                assertScenes(gd._fullLayout, 'yaxis.showspikes', false);
                assertScenes(gd._fullLayout, 'zaxis.showspikes', false);
                expect(buttonHover.isActive()).toBe(false);

                buttonHover.click();
                assertScenes(gd._fullLayout, 'hovermode', 'closest');
                assertScenes(gd._fullLayout, 'xaxis.showspikes', true);
                assertScenes(gd._fullLayout, 'yaxis.showspikes', true);
                assertScenes(gd._fullLayout, 'zaxis.showspikes', true);
                expect(buttonHover.isActive()).toBe(true);
            });
        });

    });
});
<|endoftext|>


<commit_before>import {
  Button,
  Grid,
  Row,
  Col,
  FormGroup,
  FormControl,
  ControlLabel,
  HelpBlock,
  Panel,
  Badge,
  ProgressBar
} from 'react-bootstrap';

import Utility from '../../../../shared/util/Utility';

import * as Lessons from '../../../../shared/reducers/Lessons';
import * as Security from '../../../../shared/reducers/Security';

import FontAwesomeIcon from '@fortawesome/react-fontawesome';
import faPlay from '@fortawesome/fontawesome-free-solid/faPlay';
import faStar from '@fortawesome/fontawesome-free-solid/faStar';

import ToggleButton from 'react-toggle-button';

export const Reducers = [Lessons, Security];

export class selectScreen extends React.Component {
  constructor(props) {
    super(props);
    this.handleLanguageSelection = this.handleLanguageSelection.bind(this);
    this.handleStarredClick = this.handleStarredClick.bind(this);
    this.handleSpacedRepetition = this.handleSpacedRepetition.bind(this);
  }

  componentWillMount() {
    this.props.fetchLessons(this.props.params.type).catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchUserStarredLessons().catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchFavoriteLesson(this.props.params.type).catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchaddressedQuestionsInLessons();

    if (this.props.params.type === 'kanji') {
      this.props.setQuestionLanguage('reading');
    }
  }

  componentDidMount() {
    window.addEventListener('keydown', this.onKeys);
  }

  // Triggers when we change between play types but remain in "selection" page
  componentWillReceiveProps(nextProps) {
    if (this.props.params.type !== nextProps.params.type) {
      this.props.fetchLessons(nextProps.params.type).catch(() => this.props.verifyUserLoggedIn());
    }
  }

  componentWillUnmount() {
    window.removeEventListener('keydown', this.onKeys);
  }

  getPageHeader() {
    switch (this.props.params.type) {
      case 'quiz':
        return
Quiz
;
      case 'guess':
        return
Gissa ordet
;
      case 'translate':
        return
Översätt ordet
;
      case 'flashcards':
        return
Bildkort
;
      case 'kanji':
        return
Skriv Kanji
;
      case 'grammar':
        return
Böj verb
;
      default:
        throw new Error('No play type specified');
    }
  }

  getPageDescription() {
    switch (this.props.params.type) {
      case 'quiz':
        return Sätt dina kunskaper om Japan på prov genom att välja en av 4 svarsalternativ;
      case 'guess':
        return Välj mellan 4 svarsalternativ för den korrekta översättningen.;
      case 'translate':
        return Översätt det visade ordet i fritext.;
      case 'flashcards':
        return (
          Träna dig själv genom att använda kort, med frågan på ena sidan och rätta svaret på den andra.
        );
      case 'kanji':
        return Försök rita kanji-tecken med korrekta drag och i rätt ordning.;
      case 'grammar':
        return Böj det visade ordet i fritext på angiven verbform.;
      default:
        throw new Error('No play type specified');
    }
  }

  handleLanguageSelection(event) {
    switch (event.target.name) {
      case 'selectedLesson':
        this.props.setSelectedLesson(event.target.value);
        break;
      case 'questionType':
        this.props.setQuestionLanguage(event.target.value);
        break;
      case 'answerType':
        this.props.setAnswerLanguage(event.target.value);
        break;
      default:
        break;
    }
  }

  startLesson() {
    try {
      this.props.fetchLesson(this.props.params.type).then(() => {
        this.props.setPageByName(`/play/${this.props.params.type}`);
      });
    } catch (err) {
      this.props.verifyUserLoggedIn();
    }
  }

  handleSpacedRepetition() {
    this.props.toggleSpacedRepetition();
  }

  isSpacedRepetition() {
    return this.props.spacedRepetition && this.props.spacedRepetitionModes.includes(this.props.params.type);
  }

  handleStarredClick(lesson) {
    this.props.starredLessons.map(userLesson => userLesson.lesson.name).includes(lesson.name)
      ? this.props.removeStarredLesson(lesson.name, this.props.params.type)
      : this.props.addStarredLesson(lesson.name, this.props.params.type);
  }

  getNumberOfQuestions(lesson) {
    if (
      this.props.addressedQuestionsInLessons &&
      this.props.addressedQuestionsInLessons[lesson.name] &&
      this.props.spacedRepetitionModes.includes(this.props.params.type)
    ) {
      const { unanswered, retention, all } = this.props.addressedQuestionsInLessons[lesson.name];
      return { unanswered, retention, all };
    }
    return { unanswered: 0, retention: 0, all: 0 };
  }

  getNumberOfFavoriteQuestions() {
    if (
      this.props.favoriteLesson &&
      this.props.favoriteLesson.nuggetData &&
      this.props.spacedRepetitionModes.includes(this.props.params.type)
    ) {
      const { unanswered, retention, all } = this.props.favoriteLesson.nuggetData;
      return { unanswered, retention, all };
    }
    return { unanswered: 0, retention: 0, all: 0 };
  }

  isLessonUnfinished(lesson) {
    if (!this.props.addressedQuestionsInLessons) {
      return false;
    }
    return (
      (this.getNumberOfQuestions(lesson).unanswered < this.getNumberOfQuestions(lesson).all &&
        this.getNumberOfQuestions(lesson).unanswered !== 0) ||
      this.getNumberOfQuestions(lesson).retention > 0
    );
  }

  isLessonUnstarted(lesson) {
    if (!this.props.addressedQuestionsInLessons) {
      return false;
    }

    return this.getNumberOfQuestions(lesson).unanswered === this.getNumberOfQuestions(lesson).all;
  }

  renderLessons(lessons) {
    const mediumColumnSize = 6;
    const largeColumnSize = 4;
    const renderedLessons = lessons.map(lesson => (
      <Col
        key={lesson.name}
        xs={12}
        md={mediumColumnSize}
        lg={largeColumnSize}
      >
        <Panel>
          <Panel.Body>

                  {lesson.name}
                  {this.isSpacedRepetition() && this.isLessonUnfinished(lesson) ? (
                    <Badge className="badge--type-todo">{this.getNumberOfQuestions(lesson).retention}</Badge>
                  ) : null}
                  {this.isSpacedRepetition() && this.isLessonUnfinished(lesson) ? (
                    <Badge className="badge--type-new">{this.getNumberOfQuestions(lesson).unanswered}</Badge>
                  ) : null}

                {this.props.params.type === 'quiz' ? null : (

                    <Button
                      bsClass={
                        this.props.starredLessons.map(userLesson => userLesson.lesson.name).includes(lesson.name)
                          ? 'favorite-icon-button favorite-icon-button--active icon-button'
                          : 'favorite-icon-button icon-button'
                      }
                      onClick={e => {
                        e.stopPropagation();
                        this.handleStarredClick(lesson);
                      }}
                    >
                      <FontAwesomeIcon
                        className={'fa-fw'}
                        icon={faStar}
                      />
                    </Button>

                )}

              {this.isSpacedRepetition() ? (

                  <ProgressBar
                    now={
                      100 -
                      (this.getNumberOfQuestions(lesson).retention + this.getNumberOfQuestions(lesson).unanswered) /
                        this.getNumberOfQuestions(lesson).all *
                        100
                    }
                  />

              ) : null}

{lesson.description}

                <Button
                  onClick={e => {
                    e.stopPropagation();
                    this.props.setSelectedLesson(lesson);
                    this.startLesson();
                  }}
                  disabled={
                    this.isSpacedRepetition() &&
                    this.getNumberOfQuestions(lesson).unanswered === 0 &&
                    this.getNumberOfQuestions(lesson).retention === 0
                  }
                  bsClass={'icon-button'}
                >
                  <FontAwesomeIcon
                    className={'fa-fw'}
                    icon={faPlay}
                  />
                </Button>

          </Panel.Body>
        </Panel>
      </Col>
    ));

    const adjustedLessons = [];
    for (let i = 0; i < renderedLessons.length; i++) {
      adjustedLessons.push(renderedLessons[i]);
      if ((i + 1) % (12 / mediumColumnSize) === 0) {
        const fixer = (<div
          key={`clearfix-md ${i}`}
          className="clearfix visible-md"
        />);
        adjustedLessons.push(fixer);
      }

      if ((i + 1) % (12 / largeColumnSize) === 0) {
        const fixer = (<div
          key={`clearfix-lg ${i}`}
          className="clearfix visible-lg"
        />);
        adjustedLessons.push(fixer);
      }
    }
    return adjustedLessons;
  }

  render() {
    let lessonsUnfinished, lessonsFinished, lessonsUnstarted, lessonsAll;
    if (this.isSpacedRepetition()) {
      lessonsUnfinished = this.renderLessons(this.props.lessons.filter(lesson => this.isLessonUnfinished(lesson)));
      lessonsFinished = this.renderLessons(
        this.props.lessons.filter(lesson => !this.isLessonUnfinished(lesson) && !this.isLessonUnstarted(lesson))
      );
      lessonsUnstarted = this.renderLessons(this.props.lessons.filter(lesson => this.isLessonUnstarted(lesson)));
    } else {
      lessonsAll = this.renderLessons(this.props.lessons);
    }

    const favoriteLesson = (
      <Row>
        <Col
          xs={12}
          md={12}
          lg={12}
        >
          <Panel>
            <Panel.Body>

                    {'Blandade frågor.'}
                    {this.isSpacedRepetition() ? (
                      <Badge className="badge--type-todo">{this.getNumberOfFavoriteQuestions().retention}</Badge>
                    ) : null}
                    {this.isSpacedRepetition() ? (
                      <Badge className="badge--type-new">{this.getNumberOfFavoriteQuestions().unanswered}</Badge>
                    ) : null}

                {this.isSpacedRepetition() ? (

                    <ProgressBar
                      now={
                        this.getNumberOfFavoriteQuestions().all > 0
                          ? 100 -
                            (this.getNumberOfFavoriteQuestions().retention +
                              this.getNumberOfFavoriteQuestions().unanswered) /
                              this.getNumberOfFavoriteQuestions().all *
                              100
                          : 0
                      }
                    />

                ) : null}

                  {'Blandade frågor från alla dina favoritmarkerade lektioner.'}

                  <Button
                    onClick={e => {
                      e.stopPropagation();
                      this.props.setSelectedLesson(this.props.favoriteLesson);
                      this.startLesson();
                    }}
                    disabled={this.props.starredLessons.length === 0}
                    bsClass={'icon-button'}
                  >
                    <FontAwesomeIcon
                      className={'fa-fw'}
                      icon={faPlay}
                    />
                  </Button>

            </Panel.Body>
          </Panel>
        </Col>
      </Row>
    );

    const answerLanguages = [];
    let questionLanguages = [];
    answerLanguages.push(
      <option
        key={'reading'}
        value={'reading'}
      >
        Japanska
      </option>
    );
    answerLanguages.push(
      <option
        key={'swedish'}
        value={'swedish'}
      >
        Svenska
      </option>
    );
    /* devcode: start */
    answerLanguages.push(
      <option
        key={'english'}
        value={'english'}
      >
        Engelska
      </option>
    );
    /* devcode: end */

    if (this.props.params.type === 'kanji') {
      questionLanguages = answerLanguages.shift();
    } else {
      questionLanguages = answerLanguages;
    }

    let languageSelection;
    if (this.props.params.type === 'quiz' || this.props.params.type === 'grammar') {
      languageSelection =
;
    } else {
      languageSelection = (
        <FormGroup>
          <Row>
            <Col
              xs={12}
              sm={4}
            >
              <HelpBlock>Frågespråk</HelpBlock>
              <FormControl
                componentClass="select"
                name="questionType"
                id="questionLanguageSelection"
                onChange={this.handleLanguageSelection}
                value={this.props.questionType}
                disabled={this.props.params.type === 'kanji'}
              >
                {questionLanguages}
              </FormControl>
            </Col>
            <Col
              xs={12}
              sm={4}
            >
              <HelpBlock>Svarspråk</HelpBlock>
              <FormControl
                componentClass="select"
                name="answerType"
                id="answerLanguageSelection"
                onChange={this.handleLanguageSelection}
                value={this.props.answerType}
              >
                {answerLanguages}
              </FormControl>
            </Col>
            <Col
              xs={12}
              sm={4}
            >
              <HelpBlock>Smart inlärningsläge</HelpBlock>
              <ToggleButton
                inactiveLabel={'Av'}
                activeLabel={'På'}
                value={this.isSpacedRepetition()}
                onToggle={this.handleSpacedRepetition}
              />
            </Col>
          </Row>
        </FormGroup>
      );
    }
    return (
      <Grid>
        <Col
          xs={11}
          lg={8}
          lgOffset={2}
        >
          <ControlLabel>{this.getPageHeader()}</ControlLabel>
          <FormGroup>
            <ControlLabel>{this.getPageDescription()}</ControlLabel>
          </FormGroup>
          {this.props.params.type !== 'quiz' && this.props.params.type !== 'grammar' ? (
            <FormGroup>
              <HelpBlock> Gemensamt lektionsläge för dina favoritlektioner </HelpBlock>
              {favoriteLesson}
            </FormGroup>
          ) : null}
          {this.isSpacedRepetition() ? (
            <FormGroup>
              <HelpBlock> Pågående lektioner </HelpBlock>
              <Row>{lessonsUnfinished}</Row>

              <HelpBlock> Ej påbörjade lektioner </HelpBlock>
              <Row>{lessonsUnstarted}</Row>

              <HelpBlock> Färdiga lektioner </HelpBlock>
              <Row>{lessonsFinished}</Row>

            </FormGroup>
          ) : (
            <FormGroup>
              <HelpBlock> Välj lektion att starta </HelpBlock>
              <Row>{lessonsAll}</Row>
            </FormGroup>
          )}
          {languageSelection}

        </Col>
      </Grid>
    );
  }
}

selectScreen.defaultProps = Utility.reduxEnabledDefaultProps({}, Reducers);

selectScreen.propTypes = Utility.reduxEnabledPropTypes({}, Reducers);

export default Utility.superConnect(this, Reducers)(selectScreen);
<commit_msg>improved lifecycle usage<commit_after>import {
  Button,
  Grid,
  Row,
  Col,
  FormGroup,
  FormControl,
  ControlLabel,
  HelpBlock,
  Panel,
  Badge,
  ProgressBar
} from 'react-bootstrap';

import Utility from '../../../../shared/util/Utility';

import * as Lessons from '../../../../shared/reducers/Lessons';
import * as Security from '../../../../shared/reducers/Security';

import FontAwesomeIcon from '@fortawesome/react-fontawesome';
import faPlay from '@fortawesome/fontawesome-free-solid/faPlay';
import faStar from '@fortawesome/fontawesome-free-solid/faStar';

import ToggleButton from 'react-toggle-button';

export const Reducers = [Lessons, Security];

export class selectScreen extends React.Component {
  constructor(props) {
    super(props);
    this.handleLanguageSelection = this.handleLanguageSelection.bind(this);
    this.handleStarredClick = this.handleStarredClick.bind(this);
    this.handleSpacedRepetition = this.handleSpacedRepetition.bind(this);
  }

  componentDidMount() {
    window.addEventListener('keydown', this.onKeys);

    this.props.fetchLessons(this.props.params.type).catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchUserStarredLessons().catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchFavoriteLesson(this.props.params.type).catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchaddressedQuestionsInLessons();

    if (this.props.params.type === 'kanji') {
      this.props.setQuestionLanguage('reading');
    }
  }

  // Triggers when we change between play types but remain in "selection" page
  componentWillReceiveProps(nextProps) {
    if (this.props.params.type !== nextProps.params.type) {
      this.props.fetchLessons(nextProps.params.type).catch(() => this.props.verifyUserLoggedIn());
    }
  }

  componentWillUnmount() {
    window.removeEventListener('keydown', this.onKeys);
  }

  getPageHeader() {
    switch (this.props.params.type) {
      case 'quiz':
        return
Quiz
;
      case 'guess':
        return
Gissa ordet
;
      case 'translate':
        return
Översätt ordet
;
      case 'flashcards':
        return
Bildkort
;
      case 'kanji':
        return
Skriv Kanji
;
      case 'grammar':
        return
Böj verb
;
      default:
        throw new Error('No play type specified');
    }
  }

  getPageDescription() {
    switch (this.props.params.type) {
      case 'quiz':
        return Sätt dina kunskaper om Japan på prov genom att välja en av 4 svarsalternativ;
      case 'guess':
        return Välj mellan 4 svarsalternativ för den korrekta översättningen.;
      case 'translate':
        return Översätt det visade ordet i fritext.;
      case 'flashcards':
        return (
          Träna dig själv genom att använda kort, med frågan på ena sidan och rätta svaret på den andra.
        );
      case 'kanji':
        return Försök rita kanji-tecken med korrekta drag och i rätt ordning.;
      case 'grammar':
        return Böj det visade ordet i fritext på angiven verbform.;
      default:
        throw new Error('No play type specified');
    }
  }

  handleLanguageSelection(event) {
    switch (event.target.name) {
      case 'selectedLesson':
        this.props.setSelectedLesson(event.target.value);
        break;
      case 'questionType':
        this.props.setQuestionLanguage(event.target.value);
        break;
      case 'answerType':
        this.props.setAnswerLanguage(event.target.value);
        break;
      default:
        break;
    }
  }

  startLesson() {
    try {
      this.props.fetchLesson(this.props.params.type).then(() => {
        this.props.setPageByName(`/play/${this.props.params.type}`);
      });
    } catch (err) {
      this.props.verifyUserLoggedIn();
    }
  }

  handleSpacedRepetition() {
    this.props.toggleSpacedRepetition();
  }

  isSpacedRepetition() {
    return this.props.spacedRepetition && this.props.spacedRepetitionModes.includes(this.props.params.type);
  }

  handleStarredClick(lesson) {
    this.props.starredLessons.map(userLesson => userLesson.lesson.name).includes(lesson.name)
      ? this.props.removeStarredLesson(lesson.name, this.props.params.type)
      : this.props.addStarredLesson(lesson.name, this.props.params.type);
  }

  getNumberOfQuestions(lesson) {
    if (
      this.props.addressedQuestionsInLessons &&
      this.props.addressedQuestionsInLessons[lesson.name] &&
      this.props.spacedRepetitionModes.includes(this.props.params.type)
    ) {
      const { unanswered, retention, all } = this.props.addressedQuestionsInLessons[lesson.name];
      return { unanswered, retention, all };
    }
    return { unanswered: 0, retention: 0, all: 0 };
  }

  getNumberOfFavoriteQuestions() {
    if (
      this.props.favoriteLesson &&
      this.props.favoriteLesson.nuggetData &&
      this.props.spacedRepetitionModes.includes(this.props.params.type)
    ) {
      const { unanswered, retention, all } = this.props.favoriteLesson.nuggetData;
      return { unanswered, retention, all };
    }
    return { unanswered: 0, retention: 0, all: 0 };
  }

  isLessonUnfinished(lesson) {
    if (!this.props.addressedQuestionsInLessons) {
      return false;
    }
    return (
      (this.getNumberOfQuestions(lesson).unanswered < this.getNumberOfQuestions(lesson).all &&
        this.getNumberOfQuestions(lesson).unanswered !== 0) ||
      this.getNumberOfQuestions(lesson).retention > 0
    );
  }

  isLessonUnstarted(lesson) {
    if (!this.props.addressedQuestionsInLessons) {
      return false;
    }

    return this.getNumberOfQuestions(lesson).unanswered === this.getNumberOfQuestions(lesson).all;
  }

  renderLessons(lessons) {
    const mediumColumnSize = 6;
    const largeColumnSize = 4;
    const renderedLessons = lessons.map(lesson => (
      <Col
        key={lesson.name}
        xs={12}
        md={mediumColumnSize}
        lg={largeColumnSize}
      >
        <Panel>
          <Panel.Body>

                  {lesson.name}
                  {this.isSpacedRepetition() && this.isLessonUnfinished(lesson) ? (
                    <Badge className="badge--type-todo">{this.getNumberOfQuestions(lesson).retention}</Badge>
                  ) : null}
                  {this.isSpacedRepetition() && this.isLessonUnfinished(lesson) ? (
                    <Badge className="badge--type-new">{this.getNumberOfQuestions(lesson).unanswered}</Badge>
                  ) : null}

                {this.props.params.type === 'quiz' ? null : (

                    <Button
                      bsClass={
                        this.props.starredLessons.map(userLesson => userLesson.lesson.name).includes(lesson.name)
                          ? 'favorite-icon-button favorite-icon-button--active icon-button'
                          : 'favorite-icon-button icon-button'
                      }
                      onClick={e => {
                        e.stopPropagation();
                        this.handleStarredClick(lesson);
                      }}
                    >
                      <FontAwesomeIcon
                        className={'fa-fw'}
                        icon={faStar}
                      />
                    </Button>

                )}

              {this.isSpacedRepetition() ? (

                  <ProgressBar
                    now={
                      100 -
                      (this.getNumberOfQuestions(lesson).retention + this.getNumberOfQuestions(lesson).unanswered) /
                        this.getNumberOfQuestions(lesson).all *
                        100
                    }
                  />

              ) : null}

{lesson.description}

                <Button
                  onClick={e => {
                    e.stopPropagation();
                    this.props.setSelectedLesson(lesson);
                    this.startLesson();
                  }}
                  disabled={
                    this.isSpacedRepetition() &&
                    this.getNumberOfQuestions(lesson).unanswered === 0 &&
                    this.getNumberOfQuestions(lesson).retention === 0
                  }
                  bsClass={'icon-button'}
                >
                  <FontAwesomeIcon
                    className={'fa-fw'}
                    icon={faPlay}
                  />
                </Button>

          </Panel.Body>
        </Panel>
      </Col>
    ));

    const adjustedLessons = [];
    for (let i = 0; i < renderedLessons.length; i++) {
      adjustedLessons.push(renderedLessons[i]);
      if ((i + 1) % (12 / mediumColumnSize) === 0) {
        const fixer = (<div
          key={`clearfix-md ${i}`}
          className="clearfix visible-md"
        />);
        adjustedLessons.push(fixer);
      }

      if ((i + 1) % (12 / largeColumnSize) === 0) {
        const fixer = (<div
          key={`clearfix-lg ${i}`}
          className="clearfix visible-lg"
        />);
        adjustedLessons.push(fixer);
      }
    }
    return adjustedLessons;
  }

  render() {
    let lessonsUnfinished, lessonsFinished, lessonsUnstarted, lessonsAll;
    if (this.isSpacedRepetition()) {
      lessonsUnfinished = this.renderLessons(this.props.lessons.filter(lesson => this.isLessonUnfinished(lesson)));
      lessonsFinished = this.renderLessons(
        this.props.lessons.filter(lesson => !this.isLessonUnfinished(lesson) && !this.isLessonUnstarted(lesson))
      );
      lessonsUnstarted = this.renderLessons(this.props.lessons.filter(lesson => this.isLessonUnstarted(lesson)));
    } else {
      lessonsAll = this.renderLessons(this.props.lessons);
    }

    const favoriteLesson = (
      <Row>
        <Col
          xs={12}
          md={12}
          lg={12}
        >
          <Panel>
            <Panel.Body>

                    {'Blandade frågor.'}
                    {this.isSpacedRepetition() ? (
                      <Badge className="badge--type-todo">{this.getNumberOfFavoriteQuestions().retention}</Badge>
                    ) : null}
                    {this.isSpacedRepetition() ? (
                      <Badge className="badge--type-new">{this.getNumberOfFavoriteQuestions().unanswered}</Badge>
                    ) : null}

                {this.isSpacedRepetition() ? (

                    <ProgressBar
                      now={
                        this.getNumberOfFavoriteQuestions().all > 0
                          ? 100 -
                            (this.getNumberOfFavoriteQuestions().retention +
                              this.getNumberOfFavoriteQuestions().unanswered) /
                              this.getNumberOfFavoriteQuestions().all *
                              100
                          : 0
                      }
                    />

                ) : null}

                  {'Blandade frågor från alla dina favoritmarkerade lektioner.'}

                  <Button
                    onClick={e => {
                      e.stopPropagation();
                      this.props.setSelectedLesson(this.props.favoriteLesson);
                      this.startLesson();
                    }}
                    disabled={this.props.starredLessons.length === 0}
                    bsClass={'icon-button'}
                  >
                    <FontAwesomeIcon
                      className={'fa-fw'}
                      icon={faPlay}
                    />
                  </Button>

            </Panel.Body>
          </Panel>
        </Col>
      </Row>
    );

    const answerLanguages = [];
    let questionLanguages = [];
    answerLanguages.push(
      <option
        key={'reading'}
        value={'reading'}
      >
        Japanska
      </option>
    );
    answerLanguages.push(
      <option
        key={'swedish'}
        value={'swedish'}
      >
        Svenska
      </option>
    );
    /* devcode: start */
    answerLanguages.push(
      <option
        key={'english'}
        value={'english'}
      >
        Engelska
      </option>
    );
    /* devcode: end */

    if (this.props.params.type === 'kanji') {
      questionLanguages = answerLanguages.shift();
    } else {
      questionLanguages = answerLanguages;
    }

    let languageSelection;
    if (this.props.params.type === 'quiz' || this.props.params.type === 'grammar') {
      languageSelection =
;
    } else {
      languageSelection = (
        <FormGroup>
          <Row>
            <Col
              xs={12}
              sm={4}
            >
              <HelpBlock>Frågespråk</HelpBlock>
              <FormControl
                componentClass="select"
                name="questionType"
                id="questionLanguageSelection"
                onChange={this.handleLanguageSelection}
                value={this.props.questionType}
                disabled={this.props.params.type === 'kanji'}
              >
                {questionLanguages}
              </FormControl>
            </Col>
            <Col
              xs={12}
              sm={4}
            >
              <HelpBlock>Svarspråk</HelpBlock>
              <FormControl
                componentClass="select"
                name="answerType"
                id="answerLanguageSelection"
                onChange={this.handleLanguageSelection}
                value={this.props.answerType}
              >
                {answerLanguages}
              </FormControl>
            </Col>
            <Col
              xs={12}
              sm={4}
            >
              <HelpBlock>Smart inlärningsläge</HelpBlock>
              <ToggleButton
                inactiveLabel={'Av'}
                activeLabel={'På'}
                value={this.isSpacedRepetition()}
                onToggle={this.handleSpacedRepetition}
              />
            </Col>
          </Row>
        </FormGroup>
      );
    }
    return (
      <Grid>
        <Col
          xs={11}
          lg={8}
          lgOffset={2}
        >
          <ControlLabel>{this.getPageHeader()}</ControlLabel>
          <FormGroup>
            <ControlLabel>{this.getPageDescription()}</ControlLabel>
          </FormGroup>
          {this.props.params.type !== 'quiz' && this.props.params.type !== 'grammar' ? (
            <FormGroup>
              <HelpBlock> Gemensamt lektionsläge för dina favoritlektioner </HelpBlock>
              {favoriteLesson}
            </FormGroup>
          ) : null}
          {this.isSpacedRepetition() ? (
            <FormGroup>
              <HelpBlock> Pågående lektioner </HelpBlock>
              <Row>{lessonsUnfinished}</Row>

              <HelpBlock> Ej påbörjade lektioner </HelpBlock>
              <Row>{lessonsUnstarted}</Row>

              <HelpBlock> Färdiga lektioner </HelpBlock>
              <Row>{lessonsFinished}</Row>

            </FormGroup>
          ) : (
            <FormGroup>
              <HelpBlock> Välj lektion att starta </HelpBlock>
              <Row>{lessonsAll}</Row>
            </FormGroup>
          )}
          {languageSelection}

        </Col>
      </Grid>
    );
  }
}

selectScreen.defaultProps = Utility.reduxEnabledDefaultProps({}, Reducers);

selectScreen.propTypes = Utility.reduxEnabledPropTypes({}, Reducers);

export default Utility.superConnect(this, Reducers)(selectScreen);
<|endoftext|>


<commit_before>/*jshint esversion: 6 */
const test = require('tape');
const iris_api = require('./api.js');
const messages = require('./iris_pb.js');
const textEncoding = require('text-encoding');

const irisAddress = iris_api.defaultIrisAddress;
const testSource = 'fg-iris-nodejs-test-source1';
const testSource2 = 'fg-iris-nodejs-test-source2';

function encode(value) {
    return new textEncoding.TextEncoder('utf-8').encode(value);
}

function decode(value) {
    return new textEncoding.TextDecoder('utf-8').decode(value);
}

test('Test client', (t) => {
    const client = new iris_api(irisAddress, '');
    t.equal(client.serverAddress, irisAddress);
    t.notEqual(client.creds, null);
    t.notEqual(client.sourceHandlers, null);
    t.notEqual(client.keyHandlers, null);
    t.equal(client.connected, false);
    client.close();
    t.end();
});

test('Test connect', (t) => {
    const client = new iris_api(irisAddress, '');
    client.connect().then(() => {
        t.notEqual(client.rpc, null);
        t.notEqual(client.session, null);
        t.notEqual(client.listener, null);
        t.equal(client.connected, true);
        client.close();
        t.equal(client.listener, null);
        t.equal(client.rpc, null);
        t.equal(client.serverAddress, null);
        t.equal(client.creds, null);
        t.equal(client.sourceHandlers, null);
        t.equal(client.keyHandlers, null);
        t.equal(client.connected, false);
        t.end();
    }, (error) => {
        client.close();
        t.end(error);
    });
});

test('Test set/get/remove', (t) => {
    const client = new iris_api(irisAddress, '');

    client.connect().then(()=>{
        return client.setValue(testSource, 'key1', encode('value1'));
    }).then((response) => {
        return client.setValue(testSource, 'key2', encode('value2'));
    }).then((response) => {
        return client.setValue(testSource2, 'key1', encode('value1'));
    }).then((response) => {
        return client.getValue(testSource, 'key1');
    }).then((response) => {
        t.equal(decode(response.value), 'value1');
        return client.getValue(testSource, 'key2');
    }).then((response) => {
        t.equal(decode(response.value), 'value2');
        return client.getValue(testSource2, 'key1');
    }).then((response) => {
        t.equal(decode(response.value), 'value1');
        return client.getSources();
    }).then((sources) => {
        t.equal(sources.length, 2);
        t.notEqual(sources.indexOf(testSource), -1);
        t.notEqual(sources.indexOf(testSource2), -1);
        return client.getKeys(testSource);
    }).then((keys) => {
        t.equal(keys.length, 2);
        t.notEqual(keys.indexOf('key1'), -1);
        t.notEqual(keys.indexOf('key2'), -1);
        return client.getKeys(testSource2);
    }).then((keys) => {
        t.equal(keys.length, 1);
        t.notEqual(keys.indexOf('key1'), -1);
        t.equal(keys.indexOf('key2'), -1);
        t.end();
        client.close();
    }).catch((error) => {
        t.end(error);
        client.close();
    });
});

test('Test subscribe', (t) => {
    const client = new iris_api(irisAddress, '');
    var handlerCallCount = 0;
    var otherHandlerCallCount = 0;
    var expectedHandlerCallCount = 5;
    var handlerCallCountReached = false;
    var expectedOtherHandlerCallCount = 3;
    var otherHandlerCallCountReached = false;

    var testTimeout = setTimeout(function(){
        testTimeout = null;
        t.fail(new Error('Test timeout reached before completion.'));
        finish();
    }, 1000);

    var finish = function(error) {
        clearTimeout(testTimeout);
        testTimeout = null;
        client.unsubscribe(testSource, handler);
        client.close();
        t.equal(handlerCallCount, expectedHandlerCallCount);
        t.equal(otherHandlerCallCount, expectedOtherHandlerCallCount);

        t.end(error);
    };

    var checkCallCounts = function(){
        if (handlerCallCount == expectedHandlerCallCount &&
            otherHandlerCallCount == expectedOtherHandlerCallCount) {
            finish();
        }
    };

    var handler = function(update) {
        handlerCallCount++;
        checkCallCounts();
    };

    var otherHandler = function(update) {
        otherHandlerCallCount++;
        checkCallCounts();
    };

    resume = function() {
        client.unsubscribe(testSource, otherHandler)
        .then((response) => {
            return client.setValue(testSource, 'key1', 'value1');
        }).then((response) => {
            client.setValue(testSource, 'key1', 'value1');
        }).catch((error) => {
            finish(error);
        });
    };

    client.connect().then(() => {
        return client.subscribe(testSource, handler);
    }).then((response) => {
        return client.subscribe(testSource, otherHandler);
    }).then((response) => {
        return client.setValue(testSource, 'key1', 'value1');
    }).then((response) => {
        return client.setValue(testSource, 'key2', 'value2');
    }).then((response) => {
        return client.setValue(testSource, 'key1', 'value1');
    }).then((response) => {
        setTimeout(resume, 100);
    }).catch((error) => {
        finish(error);
    });
});

test('Test subscribeKey', (t) => {
    const client = new iris_api(irisAddress, '');
    var handlerCallCount = 0;
    var otherHandlerCallCount = 0;
    var expectedHandlerCallCount = 5;
    var handlerCallCountReached = false;
    var expectedOtherHandlerCallCount = 3;
    var otherHandlerCallCountReached = false;

    var testTimeout = setTimeout(function(){
        testTimeout = null;
        t.fail(new Error('Test timeout reached before completion.'));
        finish();
    }, 1000);

    var finish = function(error) {
        clearTimeout(testTimeout);
        testTimeout = null;

        client.unsubscribeKey(testSource, 'key1', handler);
        client.close();

        t.equal(handlerCallCount, expectedHandlerCallCount);
        t.equal(otherHandlerCallCount, expectedOtherHandlerCallCount);
        t.end(error);
    };

    var checkCallCounts = function(){
        if (handlerCallCount == expectedHandlerCallCount &&
            otherHandlerCallCount == expectedOtherHandlerCallCount) {
            finish();
        }
    };

    var handler = function(update) {
        handlerCallCount++;
        checkCallCounts();
    };

    var otherHandler = function(update) {
        otherHandlerCallCount++;
        checkCallCounts();
    };

    resume = function() {
        client.unsubscribeKey(testSource, 'key1', otherHandler)
        .then((response) => {
            return client.setValue(testSource, 'key1', 'value4');
        }).then((response) => {
            return client.setValue(testSource, 'key1', 'value5');
        }).catch((error) => {
            finish(error);
        });
    };

    client.connect().then(() => {
        return client.subscribeKey(testSource, 'key1', handler);
    }).then((response) => {
        return client.subscribeKey(testSource, 'key1', otherHandler);
    }).then((response) => {
        return client.setValue(testSource, 'key1', 'value1');
    }).then((response) => {
        return client.setValue(testSource, 'key1', 'value2');
    }).then((response) => {
        return client.setValue(testSource, 'key1', 'value3');
    }).then((response) => {
        return client.setValue(testSource, 'key2', 'value3');
    }).then((response) => {
        return client.setValue(testSource2, 'key1', 'value3');
    }).then((response) => {
        setTimeout(resume, 100);
    }).catch((error) => {
        finish(error);
    });
});

test('Test remove source', (t) => {
    const client = new iris_api(irisAddress, '');
    client.connect().then((response) => {
        return client.setValue(testSource, 'key1', 'value1');
    }).then((response) => {
        return client.setValue(testSource, 'key2', 'value2');
    }).then((response) => {
        return client.setValue(testSource2, 'key1', 'value1');
    }).then((response) => {
        return client.removeSource(testSource);
    }).then((response) => {
        return client.getValue(testSource, 'key1');
    }).then((response) => {
        t.equal(response.value, '');
        return client.getValue(testSource, 'key2');
    }).then((response) => {
        t.equal(response.value, '');
        return client.removeSource(testSource2);
    }).then((response) => {
        return client.getValue(testSource2, 'key1');
    }).then((response) => {
        t.equal(response.value, '');
        t.end();
        client.close();
    }).catch((error) => {
        t.end(error);
        client.close();
    });
});

test('Test remove value', (t) => {
    const client = new iris_api(irisAddress, '');
    client.connect().then((response) => {
        return client.setValue(testSource, 'key1', encode('value1'));
    }).then((response) => {
        return client.setValue(testSource, 'key2', encode('value2'));
    }).then((response) => {
        return client.removeValue(testSource, "key1");
    }).then((response) => {
        return client.getValue(testSource, 'key1');
    }).then((response) => {
        t.equal(response.value, '');
        return client.getValue(testSource, 'key2');
    }).then((response) => {
        t.equal(decode(response.value), 'value2');
        t.end();
        client.close();
    }).catch((error) => {
        t.end(error);
        client.close();
    });
});

test('Cleanup', (t) => {
    const client = new iris_api(irisAddress, '');
    client.connect().then(()=>{
        return client.getSources();
    }).then((sources) => {
        if(sources) {
            sources.forEach((source) => {
                client.removeSource(source).then((response) => {
                    // source was removed
                }, (error) => {
                    t.error(error);
                });
            });
        }
        client.close();
        t.end();
    }).catch((error) => {
        client.close();
        t.end(error);
    });
});<commit_msg>Using promise.all in test cleanup.<commit_after>/*jshint esversion: 6 */
const test = require('tape');
const iris_api = require('./api.js');
const messages = require('./iris_pb.js');
const textEncoding = require('text-encoding');

const irisAddress = iris_api.defaultIrisAddress;
const testSource = 'fg-iris-nodejs-test-source1';
const testSource2 = 'fg-iris-nodejs-test-source2';

function encode(value) {
    return new textEncoding.TextEncoder('utf-8').encode(value);
}

function decode(value) {
    return new textEncoding.TextDecoder('utf-8').decode(value);
}

test('Test client', (t) => {
    const client = new iris_api(irisAddress, '');
    t.equal(client.serverAddress, irisAddress);
    t.notEqual(client.creds, null);
    t.notEqual(client.sourceHandlers, null);
    t.notEqual(client.keyHandlers, null);
    t.equal(client.connected, false);
    client.close();
    t.end();
});

test('Test connect', (t) => {
    const client = new iris_api(irisAddress, '');
    client.connect().then(() => {
        t.notEqual(client.rpc, null);
        t.notEqual(client.session, null);
        t.notEqual(client.listener, null);
        t.equal(client.connected, true);
        client.close();
        t.equal(client.listener, null);
        t.equal(client.rpc, null);
        t.equal(client.serverAddress, null);
        t.equal(client.creds, null);
        t.equal(client.sourceHandlers, null);
        t.equal(client.keyHandlers, null);
        t.equal(client.connected, false);
        t.end();
    }, (error) => {
        client.close();
        t.end(error);
    });
});

test('Test set/get/remove', (t) => {
    const client = new iris_api(irisAddress, '');

    client.connect().then(()=>{
        return client.setValue(testSource, 'key1', encode('value1'));
    }).then((response) => {
        return client.setValue(testSource, 'key2', encode('value2'));
    }).then((response) => {
        return client.setValue(testSource2, 'key1', encode('value1'));
    }).then((response) => {
        return client.getValue(testSource, 'key1');
    }).then((response) => {
        t.equal(decode(response.value), 'value1');
        return client.getValue(testSource, 'key2');
    }).then((response) => {
        t.equal(decode(response.value), 'value2');
        return client.getValue(testSource2, 'key1');
    }).then((response) => {
        t.equal(decode(response.value), 'value1');
        return client.getSources();
    }).then((sources) => {
        t.equal(sources.length, 2);
        t.notEqual(sources.indexOf(testSource), -1);
        t.notEqual(sources.indexOf(testSource2), -1);
        return client.getKeys(testSource);
    }).then((keys) => {
        t.equal(keys.length, 2);
        t.notEqual(keys.indexOf('key1'), -1);
        t.notEqual(keys.indexOf('key2'), -1);
        return client.getKeys(testSource2);
    }).then((keys) => {
        t.equal(keys.length, 1);
        t.notEqual(keys.indexOf('key1'), -1);
        t.equal(keys.indexOf('key2'), -1);
        t.end();
        client.close();
    }).catch((error) => {
        t.end(error);
        client.close();
    });
});

test('Test subscribe', (t) => {
    const client = new iris_api(irisAddress, '');
    var handlerCallCount = 0;
    var otherHandlerCallCount = 0;
    var expectedHandlerCallCount = 5;
    var handlerCallCountReached = false;
    var expectedOtherHandlerCallCount = 3;
    var otherHandlerCallCountReached = false;

    var testTimeout = setTimeout(function(){
        testTimeout = null;
        t.fail(new Error('Test timeout reached before completion.'));
        finish();
    }, 1000);

    var finish = function(error) {
        clearTimeout(testTimeout);
        testTimeout = null;
        client.unsubscribe(testSource, handler);
        client.close();
        t.equal(handlerCallCount, expectedHandlerCallCount);
        t.equal(otherHandlerCallCount, expectedOtherHandlerCallCount);

        t.end(error);
    };

    var checkCallCounts = function(){
        if (handlerCallCount == expectedHandlerCallCount &&
            otherHandlerCallCount == expectedOtherHandlerCallCount) {
            finish();
        }
    };

    var handler = function(update) {
        handlerCallCount++;
        checkCallCounts();
    };

    var otherHandler = function(update) {
        otherHandlerCallCount++;
        checkCallCounts();
    };

    resume = function() {
        client.unsubscribe(testSource, otherHandler)
        .then((response) => {
            return client.setValue(testSource, 'key1', 'value1');
        }).then((response) => {
            client.setValue(testSource, 'key1', 'value1');
        }).catch((error) => {
            finish(error);
        });
    };

    client.connect().then(() => {
        return client.subscribe(testSource, handler);
    }).then((response) => {
        return client.subscribe(testSource, otherHandler);
    }).then((response) => {
        return client.setValue(testSource, 'key1', 'value1');
    }).then((response) => {
        return client.setValue(testSource, 'key2', 'value2');
    }).then((response) => {
        return client.setValue(testSource, 'key1', 'value1');
    }).then((response) => {
        setTimeout(resume, 100);
    }).catch((error) => {
        finish(error);
    });
});

test('Test subscribeKey', (t) => {
    const client = new iris_api(irisAddress, '');
    var handlerCallCount = 0;
    var otherHandlerCallCount = 0;
    var expectedHandlerCallCount = 5;
    var handlerCallCountReached = false;
    var expectedOtherHandlerCallCount = 3;
    var otherHandlerCallCountReached = false;

    var testTimeout = setTimeout(function(){
        testTimeout = null;
        t.fail(new Error('Test timeout reached before completion.'));
        finish();
    }, 1000);

    var finish = function(error) {
        clearTimeout(testTimeout);
        testTimeout = null;

        client.unsubscribeKey(testSource, 'key1', handler);
        client.close();

        t.equal(handlerCallCount, expectedHandlerCallCount);
        t.equal(otherHandlerCallCount, expectedOtherHandlerCallCount);
        t.end(error);
    };

    var checkCallCounts = function(){
        if (handlerCallCount == expectedHandlerCallCount &&
            otherHandlerCallCount == expectedOtherHandlerCallCount) {
            finish();
        }
    };

    var handler = function(update) {
        handlerCallCount++;
        checkCallCounts();
    };

    var otherHandler = function(update) {
        otherHandlerCallCount++;
        checkCallCounts();
    };

    resume = function() {
        client.unsubscribeKey(testSource, 'key1', otherHandler)
        .then((response) => {
            return client.setValue(testSource, 'key1', 'value4');
        }).then((response) => {
            return client.setValue(testSource, 'key1', 'value5');
        }).catch((error) => {
            finish(error);
        });
    };

    client.connect().then(() => {
        return client.subscribeKey(testSource, 'key1', handler);
    }).then((response) => {
        return client.subscribeKey(testSource, 'key1', otherHandler);
    }).then((response) => {
        return client.setValue(testSource, 'key1', 'value1');
    }).then((response) => {
        return client.setValue(testSource, 'key1', 'value2');
    }).then((response) => {
        return client.setValue(testSource, 'key1', 'value3');
    }).then((response) => {
        return client.setValue(testSource, 'key2', 'value3');
    }).then((response) => {
        return client.setValue(testSource2, 'key1', 'value3');
    }).then((response) => {
        setTimeout(resume, 100);
    }).catch((error) => {
        finish(error);
    });
});

test('Test remove source', (t) => {
    const client = new iris_api(irisAddress, '');
    client.connect().then((response) => {
        return client.setValue(testSource, 'key1', 'value1');
    }).then((response) => {
        return client.setValue(testSource, 'key2', 'value2');
    }).then((response) => {
        return client.setValue(testSource2, 'key1', 'value1');
    }).then((response) => {
        return client.removeSource(testSource);
    }).then((response) => {
        return client.getValue(testSource, 'key1');
    }).then((response) => {
        t.equal(response.value, '');
        return client.getValue(testSource, 'key2');
    }).then((response) => {
        t.equal(response.value, '');
        return client.removeSource(testSource2);
    }).then((response) => {
        return client.getValue(testSource2, 'key1');
    }).then((response) => {
        t.equal(response.value, '');
        t.end();
        client.close();
    }).catch((error) => {
        t.end(error);
        client.close();
    });
});

test('Test remove value', (t) => {
    const client = new iris_api(irisAddress, '');
    client.connect().then((response) => {
        return client.setValue(testSource, 'key1', encode('value1'));
    }).then((response) => {
        return client.setValue(testSource, 'key2', encode('value2'));
    }).then((response) => {
        return client.removeValue(testSource, "key1");
    }).then((response) => {
        return client.getValue(testSource, 'key1');
    }).then((response) => {
        t.equal(response.value, '');
        return client.getValue(testSource, 'key2');
    }).then((response) => {
        t.equal(decode(response.value), 'value2');
        t.end();
        client.close();
    }).catch((error) => {
        t.end(error);
        client.close();
    });
});

test('Cleanup', (t) => {
    const client = new iris_api(irisAddress, '');
    client.connect().then(()=>{
        return client.getSources();
    }).then((sources) => {
        var promises = [];
        if(sources) {
            sources.forEach((source) => {
                promises.push(client.removeSource(source));
            });
        }

        Promise.all(promises).then((response) => {
            t.end();
            client.close();
        }).catch((error) => {
            t.end();
            client.close();
        });
    }).catch((error) => {
        client.close();
        t.end(error);
    });
});<|endoftext|>