<commit_before>'use strict';

class ApplicationController extends ModelController {

    static create(model, view) {
        // console.log('ApplicationController.create');
        // console.log('debug:', ApplicationController.isInDebugMode());
        if (model.data.js) {
            const CustomClass = eval(model.data.js);
            if (!CustomClass) throw new Error(`custom class of "${model.getFullName()}" form does not return type`);
            return new CustomClass(model, view);
        }
        return eval(`new ${model.getClassName()}Controller(model, view)`);
    }

    constructor(model, view) {
        // console.log('ApplicationController.constructor', model, view);
        super(model);
        this.view = view;
        this.view2 = null;
        this.lastPageId = 0;
        this.menu       = null;
        this.tabWidget  = null;
        this.statusbar  = null;
        this.pages = null;
        this.modalPages = [];
        this.activePage = null;
        this.tab    = null;
    }

    static getSearchObj() {
        // console.log('ApplicationController.getSearchObj:', window.location);
        if (!window.location.search.split('?')[1]) return {};
        return window.location.search.split('?')[1].split('&').reduce((acc, item) => {
            const kv = item.split('=');
            acc[kv[0]] = kv[1];
            return acc;
        }, {});
    }

    static isInDebugMode() {
        return ApplicationController.getSearchObj()['debug'] === '1';
    }

    init() {
        // console.log('ApplicationController.init');

        // app
        this.model.on('logout' , this.listeners.logout  = this.onLogout.bind(this));
        this.model.on('request', this.listeners.request = this.onRequest.bind(this));

        this.initMenu();
        this.initStatusbar();
        this.initTab();
        this.createPages();
        if (!this.view) {
            this.pages = this.createPages2();
            this.activePage = this.pages.length ? this.pages[0] : null;
        }
    }

    getActivePageIndex = () => {
        const i = this.activePage ? this.pages.indexOf(this.activePage) : null;
        if (i === -1) throw new Error('active page not in list');
        return i;
    }

    createView(root) {
        console.log('ApplicationController.createView');
        this.view2 = ApplicationController.createReactComponent(root, ApplicationView, {ctrl: this});
    }

    getMenuItemsProp() {
        return Object.keys(this.model.data.menu).map(key => ({
            name: key,
            title: key,
            items: this.model.data.menu[key].map(item => ({
                name: item.page,
                title: item.caption
            }))
        }));
    }

    initMenu() {
        if (!this.view) return;
        this.menu = ApplicationController.createReactComponent(this.view.querySelector('.menu'), Menu, {
            items: this.getMenuItemsProp(),
            onClick: this.onMenuItemClick,
            cb: menu => this.menu = menu
        });
        // setTimeout(() => this.menu.toggleMenu('Pages'), 1000);
    }

    initStatusbar() {
        if (!this.view) return;
        this.statusbar = ApplicationController.createReactComponent(this.view.querySelector('.statusbar'), Statusbar);
    }

    initTab() {
        if (!this.view) return;
        this.tabWidget = new TabWidget(this.view.querySelector('.ApplicationView > .TabWidget'));
        this.tabWidget.init();
        this.tabWidget.on('tabClosingByUser', this.listeners.tabClosingByUser = this.onTabClosingByUser.bind(this));
        this.tabWidget.on('tabShow'         , this.listeners.tabShow          = this.onTabShow.bind(this));
        this.tabWidget.on('tabHide'         , this.listeners.tabHide          = this.onTabHide.bind(this));
    }

    createPages() {
        if (!this.view) return;
        for (const name in this.model.data.pages) {
            const page = new Page(this.model.data.pages[name], this.model, {
                // app : this.model,
                // data: this.model.data.pages[name],
                id  : `p${this.getNextPageId()}`
            });
            page.init();
            this.createPageController(page);
        }
    }
    createPages2() {
        return Object.keys(this.model.data.pages).map(name => {
            // model
            const page = new Page(this.model.data.pages[name], this.model, {
                // app : this.model,
                // data: this.model.data.pages[name],
                id  : `p${this.getNextPageId()}`
            });
            page.init();

            // controller
            const pageController = PageController.create(page, null, this);
            pageController.init();
            return pageController;
        });
    }

    deinit() {
        // app
        this.model.off('logout', this.listeners.logout);
        this.model.off('request', this.listeners.request);

        // TabWidget
        this.tabWidget.off('tabClosingByUser', this.listeners.tabClosingByUser);
        this.tabWidget.off('tabShow', this.listeners.tabShow);
        this.tabWidget.off('tabHide', this.listeners.tabHide);
        super.deinit();
    }

    createPageController(model, select = false, track = false) {
        console.log('ApplicationController.createPageController', model.getFullName());
        // const html = QForms.render(model.data.view, {model});
        // const view = $(html).get(0);

        // tab
        const [tab, root] = this.tabWidget.createTab2(null, null, null, model.id);
        if (select) this.tabWidget.selectTab(tab, track);

        // pageController
        const pageController = PageController.create(model, null, this);
        pageController.tab = tab;
        tab.pageController = pageController;
        pageController.init();
        // pageController.fill();
        pageController.createView(root);
    }
    createModalPageController(model) {
        console.log('ApplicationController.createModalPageController', model.getFullName());
        // const html = QForms.render(model.data.view, {model});
        // const view = $(html).get(0);

        const [el, root] = ModalWidget.createElementAndRoot();
        this.view.appendChild(el);
        const modal = new ModalWidget(el);

        // pageController
        const pageController = PageController.create(model, null, this);
        pageController.modal = modal;
        pageController.init();
        // pageController.fill();
        pageController.createView(root);
    }

    onPageSelected(pc) {
        console.log('ApplicationController.onPageSelected', pc.model.getName());
        const i = this.pages.indexOf(pc);
        if (i === -1) throw new Error(`no page controller ${pc.model.getName()} in pages`);
        this.activePage = pc;
        this.tab.rerender();
        // const tab = this.findTabByPageController(pageController);
        // this.tabWidget.selectTab(tab);
    }

    onTabClosingByUser(e) {
        e.tab.pageController.close();
    }

    onTabShow(e) {
        // console.log('ApplicationController.onTabShow', e.tab.pageController);
        if (e.tab.pageController) {
            e.tab.pageController.emit('show', {source: this});
        }
    }

    onTabHide(e) {
        if (e.tab.pageController) {
            e.tab.pageController.emit('hide', {source: this});
        }
    }

    onLogout(ea) {
        location.reload();
    }

    onRequest(e) {
        // console.log('onRequest', e);
        if (this.statusbar) {
            this.statusbar.setLastQueryTime(e.time);
        }
    }

    closePage(pageController) {
        console.log('ApplicationController.closePage', pageController.model.getFullName());
        /*
        if (pageController.tab) {
            this.tabWidget.closeTab(pageController.tab);
        } else if (pageController.modal) {
            pageController.modal.close();
        }
        pageController.deinit();
        pageController.model.deinit();*/
        this.pages.splice(this.pages.indexOf(pageController), 1);
        if (this.activePage === pageController) {
            this.activePage = this.pages[this.pages.length - 1];
        }
        this.view2.rerender();
        pageController.deinit();
        pageController.model.deinit();
    }

    onPageClose = i => {
        console.log('ApplicationController.onPageClose', this.pages[i].model.getFullName());
        this.closePage(this.pages[i]);
    }

    findPageControllerByPageNameAndKey(pageName, key) {
        /*// console.log('ApplicationController.findPageControllerByPageNameAndKey', pageName, key);
        for (let i = 0; i < this.tabWidget.tabList.childNodes.length; i++) {
            const tab = this.tabWidget.tabList.childNodes[i];
            if (tab.pageController.model.getName() === pageName && tab.pageController.model.getKey() === key) {
                return tab.pageController;
            }
        }*/
        return this.pages.find(({model}) => model.getName() === pageName && model.getKey() === key);
    }

    findTabByPageController(pageController) {
        // console.log('ApplicationController.findTabByPageController');
        for (let i = 0; i < this.tabWidget.tabList.childNodes.length; i++) {
            const tab = this.tabWidget.tabList.childNodes[i];
            if (tab.pageController === pageController) {
                return tab;
            }
        }
        return null;
    }

    async openPage(options) {
        console.log('ApplicationController.openPage', options);
        const name       = options.name;
        const key        = options.key || null;
        const parentPage = options.parentPage;
        const newMode    = !!options.newMode;
        const modal      = !!options.modal;

        // if this page with this key is already opened, then show it
        const pageController = this.findPageControllerByPageNameAndKey(name, key);
        // console.log('pageController:', pageController);
        if (pageController) {
            this.onPageSelected(pageController);
            return;
        }

        const parentPageName = parentPage ? parentPage.getName() : null;
        let keyParams = {};
        const params = {};
        //console.log('open ' + name + ' with key: ' + key);
        if (key) {
            keyParams = QForms.keyToParams(key);
            for (const keyName in keyParams) {
                params[keyName] = keyParams[keyName];
            }
        }

        // if page doesn't exist, create it
        const response = await this.model.request({
            action        : 'page',
            page          : name,
            newMode       : newMode,
            parentPageName: parentPageName,
            params        : params
        });

        // copy params from parent page to make possible refer to parent page params
        if (parentPage !== undefined) {
            for (const name in parentPage.params) {
                if (keyParams[name] === undefined) {
                    params[name] = parentPage.params[name];
                }
            }
        }
        const page = new Page(response.page, this.model, {
            // app           : this.model,
            // data          : response.page,
            id            : `p${this.getNextPageId()}`,
            params        : params,
            parentPageName: parentPageName,
        });
        page.init();
        const pc = new PageController(page, null, this);
        pc.init();

        if (modal) {
            this.modalPages.push(pc);
        } else {
            this.pages.push(this.activePage = pc);
        }
        this.view2.rerender();
        /*
        if (modal) {
            this.createModalPageController(page);
        } else {
            this.createPageController(page, true, parentPage !== undefined);
        }*/
    }

    getNextPageId() {
        this.lastPageId++;
        return this.lastPageId;
    }

    onMenuItemClick = async (menu, item) => {
        // console.log('ApplicationController.onMenuItemClick', menu, item);
        try {
            await this.openPage({name: item});
        } catch (err) {
            console.error(err);
            alert(err.message);
        }
    }

    // static createReactRoot(name, props, children) {
    //     console.log('ApplicationController.createReactRoot', name, props);
    //     const div = document.createElement('div');
    //     div.className = name;
    //     ReactDOM.render(React.createElement(eval(name), props, children), div);
    //     return div;
    // }

    static createReactComponent(root, type, props = {}, children) {
        // console.log('ApplicationController.createReactComponent', root, type);
        let component;
        props.cb = c => component = c;
        const reactElement = React.createElement(type, props, children);
        ReactDOM.render(reactElement, root);
        return component;
    }
    onTabCreated = tab => {
        // console.log('ApplicationController.onTabCreated', tab);
        this.tab = tab;
    }
}
<commit_msg>modal pages<commit_after>'use strict';

class ApplicationController extends ModelController {

    static create(model, view) {
        // console.log('ApplicationController.create');
        // console.log('debug:', ApplicationController.isInDebugMode());
        if (model.data.js) {
            const CustomClass = eval(model.data.js);
            if (!CustomClass) throw new Error(`custom class of "${model.getFullName()}" form does not return type`);
            return new CustomClass(model, view);
        }
        return eval(`new ${model.getClassName()}Controller(model, view)`);
    }

    constructor(model, view) {
        // console.log('ApplicationController.constructor', model, view);
        super(model);
        this.view = view;
        this.view2 = null;
        this.lastPageId = 0;
        this.menu       = null;
        this.tabWidget  = null;
        this.statusbar  = null;
        this.pages = null;
        this.modalPages = [];
        this.activePage = null;
        this.tab    = null;
    }

    static getSearchObj() {
        // console.log('ApplicationController.getSearchObj:', window.location);
        if (!window.location.search.split('?')[1]) return {};
        return window.location.search.split('?')[1].split('&').reduce((acc, item) => {
            const kv = item.split('=');
            acc[kv[0]] = kv[1];
            return acc;
        }, {});
    }

    static isInDebugMode() {
        return ApplicationController.getSearchObj()['debug'] === '1';
    }

    init() {
        // console.log('ApplicationController.init');

        // app
        this.model.on('logout' , this.listeners.logout  = this.onLogout.bind(this));
        this.model.on('request', this.listeners.request = this.onRequest.bind(this));

        this.initMenu();
        this.initStatusbar();
        this.initTab();
        this.createPages();
        if (!this.view) {
            this.pages = this.createPages2();
            this.activePage = this.pages.length ? this.pages[0] : null;
        }
    }

    getActivePageIndex = () => {
        const i = this.activePage ? this.pages.indexOf(this.activePage) : null;
        if (i === -1) throw new Error('active page not in list');
        return i;
    }

    createView(root) {
        console.log('ApplicationController.createView');
        this.view2 = ApplicationController.createReactComponent(root, ApplicationView, {ctrl: this});
    }

    getMenuItemsProp() {
        return Object.keys(this.model.data.menu).map(key => ({
            name: key,
            title: key,
            items: this.model.data.menu[key].map(item => ({
                name: item.page,
                title: item.caption
            }))
        }));
    }

    initMenu() {
        if (!this.view) return;
        this.menu = ApplicationController.createReactComponent(this.view.querySelector('.menu'), Menu, {
            items: this.getMenuItemsProp(),
            onClick: this.onMenuItemClick,
            cb: menu => this.menu = menu
        });
        // setTimeout(() => this.menu.toggleMenu('Pages'), 1000);
    }

    initStatusbar() {
        if (!this.view) return;
        this.statusbar = ApplicationController.createReactComponent(this.view.querySelector('.statusbar'), Statusbar);
    }

    initTab() {
        if (!this.view) return;
        this.tabWidget = new TabWidget(this.view.querySelector('.ApplicationView > .TabWidget'));
        this.tabWidget.init();
        this.tabWidget.on('tabClosingByUser', this.listeners.tabClosingByUser = this.onTabClosingByUser.bind(this));
        this.tabWidget.on('tabShow'         , this.listeners.tabShow          = this.onTabShow.bind(this));
        this.tabWidget.on('tabHide'         , this.listeners.tabHide          = this.onTabHide.bind(this));
    }

    createPages() {
        if (!this.view) return;
        for (const name in this.model.data.pages) {
            const page = new Page(this.model.data.pages[name], this.model, {
                // app : this.model,
                // data: this.model.data.pages[name],
                id  : `p${this.getNextPageId()}`
            });
            page.init();
            this.createPageController(page);
        }
    }
    createPages2() {
        return Object.keys(this.model.data.pages).map(name => {
            // model
            const page = new Page(this.model.data.pages[name], this.model, {
                // app : this.model,
                // data: this.model.data.pages[name],
                id  : `p${this.getNextPageId()}`
            });
            page.init();

            // controller
            const pageController = PageController.create(page, null, this);
            pageController.init();
            return pageController;
        });
    }

    deinit() {
        // app
        this.model.off('logout', this.listeners.logout);
        this.model.off('request', this.listeners.request);

        // TabWidget
        this.tabWidget.off('tabClosingByUser', this.listeners.tabClosingByUser);
        this.tabWidget.off('tabShow', this.listeners.tabShow);
        this.tabWidget.off('tabHide', this.listeners.tabHide);
        super.deinit();
    }

    createPageController(model, select = false, track = false) {
        console.log('ApplicationController.createPageController', model.getFullName());
        // const html = QForms.render(model.data.view, {model});
        // const view = $(html).get(0);

        // tab
        const [tab, root] = this.tabWidget.createTab2(null, null, null, model.id);
        if (select) this.tabWidget.selectTab(tab, track);

        // pageController
        const pageController = PageController.create(model, null, this);
        pageController.tab = tab;
        tab.pageController = pageController;
        pageController.init();
        // pageController.fill();
        pageController.createView(root);
    }
    createModalPageController(model) {
        console.log('ApplicationController.createModalPageController', model.getFullName());
        // const html = QForms.render(model.data.view, {model});
        // const view = $(html).get(0);

        const [el, root] = ModalWidget.createElementAndRoot();
        this.view.appendChild(el);
        const modal = new ModalWidget(el);

        // pageController
        const pageController = PageController.create(model, null, this);
        pageController.modal = modal;
        pageController.init();
        // pageController.fill();
        pageController.createView(root);
    }

    onPageSelected(pc) {
        console.log('ApplicationController.onPageSelected', pc.model.getName());
        const i = this.pages.indexOf(pc);
        if (i === -1) throw new Error(`no page controller ${pc.model.getName()} in pages`);
        this.activePage = pc;
        this.tab.rerender();
        // const tab = this.findTabByPageController(pageController);
        // this.tabWidget.selectTab(tab);
    }

    onTabClosingByUser(e) {
        e.tab.pageController.close();
    }

    onTabShow(e) {
        // console.log('ApplicationController.onTabShow', e.tab.pageController);
        if (e.tab.pageController) {
            e.tab.pageController.emit('show', {source: this});
        }
    }

    onTabHide(e) {
        if (e.tab.pageController) {
            e.tab.pageController.emit('hide', {source: this});
        }
    }

    onLogout(ea) {
        location.reload();
    }

    onRequest(e) {
        // console.log('onRequest', e);
        if (this.statusbar) {
            this.statusbar.setLastQueryTime(e.time);
        }
    }

    closePage(pageController) {
        console.log('ApplicationController.closePage', pageController.model.getFullName());
        /*
        if (pageController.tab) {
            this.tabWidget.closeTab(pageController.tab);
        } else if (pageController.modal) {
            pageController.modal.close();
        }
        pageController.deinit();
        pageController.model.deinit();*/
        if (this.pages.indexOf(pageController) > -1) {
            this.pages.splice(this.pages.indexOf(pageController), 1);
            if (this.activePage === pageController) {
                this.activePage = this.pages[this.pages.length - 1];
            }
        } else if (this.modalPages.indexOf(pageController) > -1) {
            this.modalPages.splice(this.modalPages.indexOf(pageController), 1);
        } else {
            throw new Error('page not found');
        }

        this.view2.rerender();
        pageController.deinit();
        pageController.model.deinit();
    }

    onPageClose = i => {
        console.log('ApplicationController.onPageClose', this.pages[i].model.getFullName());
        this.closePage(this.pages[i]);
    }

    findPageControllerByPageNameAndKey(pageName, key) {
        /*// console.log('ApplicationController.findPageControllerByPageNameAndKey', pageName, key);
        for (let i = 0; i < this.tabWidget.tabList.childNodes.length; i++) {
            const tab = this.tabWidget.tabList.childNodes[i];
            if (tab.pageController.model.getName() === pageName && tab.pageController.model.getKey() === key) {
                return tab.pageController;
            }
        }*/
        return this.pages.find(({model}) => model.getName() === pageName && model.getKey() === key);
    }

    findTabByPageController(pageController) {
        // console.log('ApplicationController.findTabByPageController');
        for (let i = 0; i < this.tabWidget.tabList.childNodes.length; i++) {
            const tab = this.tabWidget.tabList.childNodes[i];
            if (tab.pageController === pageController) {
                return tab;
            }
        }
        return null;
    }

    async openPage(options) {
        console.log('ApplicationController.openPage', options);
        const name       = options.name;
        const key        = options.key || null;
        const parentPage = options.parentPage;
        const newMode    = !!options.newMode;
        const modal      = !!options.modal;

        // if this page with this key is already opened, then show it
        const pageController = this.findPageControllerByPageNameAndKey(name, key);
        // console.log('pageController:', pageController);
        if (pageController) {
            this.onPageSelected(pageController);
            return;
        }

        const parentPageName = parentPage ? parentPage.getName() : null;
        let keyParams = {};
        const params = {};
        //console.log('open ' + name + ' with key: ' + key);
        if (key) {
            keyParams = QForms.keyToParams(key);
            for (const keyName in keyParams) {
                params[keyName] = keyParams[keyName];
            }
        }

        // if page doesn't exist, create it
        const response = await this.model.request({
            action        : 'page',
            page          : name,
            newMode       : newMode,
            parentPageName: parentPageName,
            params        : params
        });

        // copy params from parent page to make possible refer to parent page params
        if (parentPage !== undefined) {
            for (const name in parentPage.params) {
                if (keyParams[name] === undefined) {
                    params[name] = parentPage.params[name];
                }
            }
        }
        const page = new Page(response.page, this.model, {
            // app           : this.model,
            // data          : response.page,
            id            : `p${this.getNextPageId()}`,
            params        : params,
            parentPageName: parentPageName,
        });
        page.init();
        const pc = new PageController(page, null, this);
        pc.init();

        if (modal) {
            this.modalPages.push(pc);
        } else {
            this.pages.push(this.activePage = pc);
        }
        this.view2.rerender();
        /*
        if (modal) {
            this.createModalPageController(page);
        } else {
            this.createPageController(page, true, parentPage !== undefined);
        }*/
    }

    getNextPageId() {
        this.lastPageId++;
        return this.lastPageId;
    }

    onMenuItemClick = async (menu, item) => {
        // console.log('ApplicationController.onMenuItemClick', menu, item);
        try {
            await this.openPage({name: item});
        } catch (err) {
            console.error(err);
            alert(err.message);
        }
    }

    // static createReactRoot(name, props, children) {
    //     console.log('ApplicationController.createReactRoot', name, props);
    //     const div = document.createElement('div');
    //     div.className = name;
    //     ReactDOM.render(React.createElement(eval(name), props, children), div);
    //     return div;
    // }

    static createReactComponent(root, type, props = {}, children) {
        // console.log('ApplicationController.createReactComponent', root, type);
        let component;
        props.cb = c => component = c;
        const reactElement = React.createElement(type, props, children);
        ReactDOM.render(reactElement, root);
        return component;
    }
    onTabCreated = tab => {
        // console.log('ApplicationController.onTabCreated', tab);
        this.tab = tab;
    }
}
<|endoftext|>


<commit_before>dojo.provide("dojox.lang.tests.docs");

dojo.require("dojox.lang.docs");
dojo.require("dijit.ColorPalette");

tests.register("dojox.lang.tests.docs", [
	function notReady(t){
		t.is(!dijit.ColorPalette.description, true);
	},
	function pastClassHasSchema(t){
		dojox.lang.docs.init();
		t.is(!!dijit.ColorPalette.description, true);
		dojox.lang.docs.init(); // make sure it can be called twice without any problems
		t.is(!!dijit.ColorPalette.properties.defaultTimeout.description, true);
		t.is(dijit.ColorPalette.properties.defaultTimeout.type, "number");
		t.is(dijit.ColorPalette.methods.onChange.parameters[0].type, "string");
		t.is(dijit.ColorPalette.methods.onChange.parameters[0].name, "color");
		t.is(dijit.ColorPalette["extends"], dijit._Widget);
	},
	function futureClassHasSchema(t){
		dojo.require("dijit.Dialog");
		t.is(!!dijit.Dialog.description, true);
		t.is(!!dijit.Dialog.properties.duration.description, true);
		t.is(dijit.Dialog.properties.duration.type, "integer");
	},
	function testSchema(t){

	}
]);
<commit_msg>Fixes unit test to expect the correct documentation<commit_after>dojo.provide("dojox.lang.tests.docs");

dojo.require("dojox.lang.docs");
dojo.require("dijit.ColorPalette");

tests.register("dojox.lang.tests.docs", [
	function notReady(t){
		t.is(!dijit.ColorPalette.description, true);
	},
	function pastClassHasSchema(t){
		dojox.lang.docs.init();
		t.is(!!dijit.ColorPalette.description, true);
		dojox.lang.docs.init(); // make sure it can be called twice without any problems
		t.is(!!dijit.ColorPalette.properties.defaultTimeout.description, true);
		t.is(dijit.ColorPalette.properties.defaultTimeout.type, "number");
		t.is(dijit.ColorPalette.methods.onChange.parameters[0].type, "string");
		t.is(dijit.ColorPalette.methods.onChange.parameters[0].name, "color");
		t.is(dijit.ColorPalette["extends"], dijit._Widget);
	},
	function futureClassHasSchema(t){
		dojo.require("dijit.Dialog");
		t.is(!!dijit.Dialog.description, true);
		t.is(!!dijit.Dialog.properties.autofocus.description, true);
		t.is(dijit.Dialog.properties.autofocus.type, "boolean");
	},
	function testSchema(t){

	}
]);
<|endoftext|>


<commit_before>    var config = require('../config');
    var Datastore = require('nedb');
    //Persistent database
    var db = {};

    db.series = new Datastore({
      filename: config.database.series_path,
      autoload: true
    });

    db.comics = new Datastore({
      filename: config.database.comics_path,
      autoload: true
    });

    db.users = new Datastore({
      filename: config.database.users_path,
      autoload: true
    });

    db.comics.ensureIndex({
      fieldName: 'relative_path',
      unique: true
    }, function(err) {
      if (err) console.log(err);
    });

    db.series.ensureIndex({
      fieldName: 'title',
      unique: true
    }, function(err) {
      if (err) console.log(err);
    });

    //Expects array of data to insert into the db and a callback that is executed when the process has finished
    var comics_dbInsert = function(data, callback) {
      db.comics.insert(data, function(err, newDoc) {

        if (err) {
          if (callback) callback(err, null);
          return "The comics could not be inserted into the db";
        }
        if (callback) callback(null, newDoc);
      });
    };

    var comics_dbFind = function(query, callback) {
      console.log('Querying comics database for ' + query);
      db.comics.find(query, function(err, docs) {
        if (err) {
          console.log(err);
          if (callback) callback(err, null);
        }
        if (callback) callback(null, docs);
      });
    };

    //Expects array of data to insert into the db and a callback that is executed when the process has finished
    var series_dbInsert = function(data, callback) {
      db.series.insert(data, function(err, newDoc) {
        if (err) {
          //console.log("Error - data not inserted into comics db");
          if (callback) callback(err, null);
          return "The series could not be added to the db";
        }
        if (callback) callback(null, newDoc);
      });
    };

    module.exports = {
      comics_dbInsert: comics_dbInsert,
      comics_dbFind: comics_dbFind,
      series_dbInsert: series_dbInsert
    };

    //TODO: More CRUD operations here :D<commit_msg>Added a query function for the series database<commit_after>    var config = require('../config');
    var Datastore = require('nedb');
    //Persistent database
    var db = {};

    db.series = new Datastore({
      filename: config.database.series_path,
      autoload: true
    });

    db.comics = new Datastore({
      filename: config.database.comics_path,
      autoload: true
    });

    db.users = new Datastore({
      filename: config.database.users_path,
      autoload: true
    });

    db.comics.ensureIndex({
      fieldName: 'relative_path',
      unique: true
    }, function(err) {
      if (err) console.log(err);
    });

    db.series.ensureIndex({
      fieldName: 'title',
      unique: true
    }, function(err) {
      if (err) console.log(err);
    });

    //Expects array of data to insert into the db and a callback that is executed when the process has finished
    var comics_dbInsert = function(data, callback) {
      db.comics.insert(data, function(err, newDoc) {

        if (err) {
          if (callback) callback(err, null);
          return "The comics could not be inserted into the db";
        }
        if (callback) callback(null, newDoc);
      });
    };

    var comics_dbFind = function(query, callback) {
      //console.log('Querying comics database for ' + query);
      db.comics.find(query, function(err, docs) {
        if (err) {
          console.log(err);
          if (callback) callback(err, null);
        }
        if (callback) callback(null, docs);
      });
    };

    //Expects array of data to insert into the db and a callback that is executed when the process has finished
    var series_dbInsert = function(data, callback) {
      db.series.insert(data, function(err, newDoc) {
        if (err) {
          //console.log("Error - data not inserted into comics db");
          if (callback) callback(err, null);
          return "The series could not be added to the db";
        }
        if (callback) callback(null, newDoc);
      });
    };

    var series_dbFind = function(query, callback) {
      //console.log('Querying comics database for ' + query);
      db.series.find(query, function(err, docs) {
        if (err) {
          console.log(err);
          if (callback) callback(err, null);
        }
        if (callback) callback(null, docs);
      });
    };

    module.exports = {
      comics_dbInsert: comics_dbInsert,
      comics_dbFind: comics_dbFind,
      series_dbInsert: series_dbInsert,
      series_dbFind: series_dbFind
    };

    //TODO: More CRUD operations here :D<|endoftext|>


<commit_before>"use strict";

var fs = require("fs");
var environment = process.env.NODE_ENV;

var config = require(__main_root + 'config/database.js')[environment];
var Sequelize = require("sequelize");
var sequelizeInstance = new Sequelize(config.database, config.username, config.password, config);
var db = {};
db.models = {};

fs.readdirSync(__main_root + 'models/')
    .filter(function (file) {
        return (file.indexOf(".") !== 0) && (file !== "index.js");
    }).forEach(function (file) {
        var model = sequelizeInstance.import(__main_root + 'models/' + file);
        db.models[model.name] = model;
    });

/*
 Object.keys(db).forEach(function (modelName) {
 if ("associate" in db[modelName]) {
 db[modelName].associate(db);
 }
 });
 */

db.instance = sequelizeInstance;
db.Sequelize = Sequelize;

module.exports = db;<commit_msg>Code cleanup<commit_after>'use strict';

var fs = require('fs');
var environment = process.env.NODE_ENV;

var config = require(__main_root + 'config/database.js')[environment];
var Sequelize = require('sequelize');
var sequelizeInstance = new Sequelize(config.database, config.username, config.password, config);
var db = {models: {}};

fs.readdirSync(__main_root + 'models/')
    .filter(function (file) {
        return (file.indexOf('.') !== 0);
    }).forEach(function (file) {
        var model = sequelizeInstance.import(__main_root + 'models/' + file);
        db.models[model.name] = model;
    });

db.instance = sequelizeInstance;

module.exports = db;<|endoftext|>


<commit_before>'use strict';

const log = require('winston');
const config = require('config');
const nodemailer = require('nodemailer');
const Email = require('../../src/email/email');
const Mongo = require('../../src/dao/mongo');
const PGP = require('../../src/service/pgp');
const PublicKey = require('../../src/service/public-key');

describe('Public Key Integration Tests', function() {
  this.timeout(20000);

  let sandbox;
  let publicKey;
  let email;
  let mongo;
  let pgp;
  let sendEmailStub;
  let publicKeyArmored;
  let publicKeyArmored2;
  let mailsSent;

  const DB_TYPE = 'publickey';
  const primaryEmail = 'test1@example.com';
  const primaryEmail2 = 'test2@example.com';
  const origin = {host: 'localhost', protocol: 'http'};

  before(async () => {
    publicKeyArmored = require('fs').readFileSync(`${__dirname}/../key3.asc`, 'utf8');
    publicKeyArmored2 = require('fs').readFileSync(`${__dirname}/../key4.asc`, 'utf8');
    sinon.stub(log, 'info');
    mongo = new Mongo();
    await mongo.init(config.mongo);
  });

  beforeEach(async () => {
    sandbox = sinon.sandbox.create();

    await mongo.clear(DB_TYPE);
    mailsSent = [];
    sendEmailStub = sinon.stub().returns(Promise.resolve({response: '250'}));
    sendEmailStub.withArgs(sinon.match(recipient => {
      mailsSent[mailsSent.length] = {to: recipient.to.address};
      return true;
    }), sinon.match(params => {
      mailsSent[mailsSent.length - 1].params = params;
      expect(params.nonce).to.exist;
      expect(params.keyId).to.exist;
      return true;
    }));
    sandbox.stub(nodemailer, 'createTransport').returns({
      templateSender: () => sendEmailStub
    });
    email = new Email(nodemailer);
    email.init({
      host: 'localhost',
      auth: {user: 'user', pass: 'pass'},
      sender: {name: 'Foo Bar', email: 'foo@bar.com'}
    });
    pgp = new PGP();
    publicKey = new PublicKey(pgp, mongo, email);
  });

  afterEach(() => {
    sandbox.restore();
  });

  after(async () => {
    await mongo.clear(DB_TYPE);
    await mongo.disconnect();
    log.info.restore();
  });

  describe('put', () => {
    it('should persist key and send verification email with primaryEmail', async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      expect(mailsSent.length).to.equal(1);
      expect(mailsSent[0].to).to.equal(primaryEmail);
      expect(mailsSent[0].params.keyId).to.exist;
      expect(mailsSent[0].params.nonce).to.exist;
    });
    it('should persist key and send verification email without primaryEmail', async () => {
      await publicKey.put({publicKeyArmored, origin});
      expect(mailsSent.length).to.equal(4);
    });

    it('should work twice if not yet verified', async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      expect(mailsSent.length).to.equal(1);
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      expect(mailsSent.length).to.equal(2);
    });

    it('should throw 304 if key already exists', async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      await publicKey.verify(mailsSent[0].params);
      try {
        await publicKey.put({publicKeyArmored, primaryEmail, origin});
        expect(false).to.be.true;
      } catch (e) {
        expect(e.status).to.equal(304);
      }
    });
  });

  describe('_purgeOldUnverified', () => {
    let key;

    beforeEach(async () => {
      key = pgp.parseKey(publicKeyArmored);
    });

    it('should work for no keys', async () => {
      const r = await publicKey._purgeOldUnverified();
      expect(r.deletedCount).to.equal(0);
    });

    it('should not remove a current unverified key', async () => {
      await publicKey._persisKey(key);
      const r = await publicKey._purgeOldUnverified();
      expect(r.deletedCount).to.equal(0);
    });

    it('should not remove a current verified key', async () => {
      key.userIds[0].verified = true;
      await publicKey._persisKey(key);
      const r = await publicKey._purgeOldUnverified();
      expect(r.deletedCount).to.equal(0);
    });

    it('should not remove an old verified key', async () => {
      key.uploaded.setDate(key.uploaded.getDate() - 31);
      key.userIds[0].verified = true;
      await publicKey._persisKey(key);
      const r = await publicKey._purgeOldUnverified();
      expect(r.deletedCount).to.equal(0);
    });

    it('should remove an old unverified key', async () => {
      key.uploaded.setDate(key.uploaded.getDate() - 31);
      await publicKey._persisKey(key);
      const r = await publicKey._purgeOldUnverified();
      expect(r.deletedCount).to.equal(1);
    });

    it('should not remove a verified key with no uploaded attribute', async () => {
      key.userIds[0].verified = true;
      await publicKey._persisKey(key);
      const r = await publicKey._purgeOldUnverified();
      expect(r.deletedCount).to.equal(0);
    });
  });

  describe('verify', () => {
    it('should update the document', async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      const emailParams = mailsSent[0].params;
      await publicKey.verify(emailParams);
      const gotten = await mongo.get({keyId: emailParams.keyId}, DB_TYPE);
      expect(gotten.userIds[0].verified).to.be.true;
      expect(gotten.userIds[0].nonce).to.be.null;
      expect(gotten.userIds[1].verified).to.be.false;
      expect(gotten.userIds[1].nonce).to.exist;
    });

    it('should not find the document', async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      const emailParams = mailsSent[0].params;
      try {
        await publicKey.verify({keyId: emailParams.keyId, nonce: 'fake_nonce'});
        expect(true).to.be.false;
      } catch (e) {
        expect(e.status).to.equal(404);
      }
      const gotten = await mongo.get({keyId: emailParams.keyId}, DB_TYPE);
      expect(gotten.userIds[0].verified).to.be.false;
      expect(gotten.userIds[0].nonce).to.equal(emailParams.nonce);
      expect(gotten.userIds[1].verified).to.be.false;
      expect(gotten.userIds[1].nonce).to.exist;
    });

    it('should not verify a second key for already verified user id of another key', async () => {
      await publicKey.put({publicKeyArmored, primaryEmail: primaryEmail2, origin});
      expect(mailsSent.length).to.equal(1);
      await publicKey.put({publicKeyArmored: publicKeyArmored2, primaryEmail: primaryEmail2, origin});
      expect(mailsSent.length).to.equal(2);
      await publicKey.verify(mailsSent[1].params);

      try {
        await publicKey.verify(mailsSent[0].params);
        expect(true).to.be.false;
      } catch (e) {
        expect(e.status).to.equal(304);
      }
      const gotten = await mongo.get({keyId: mailsSent[0].params.keyId}, DB_TYPE);
      expect(gotten.userIds[1].email).to.equal(primaryEmail2);
      expect(gotten.userIds[1].verified).to.be.false;
      expect(gotten.userIds[1].nonce).to.equal(mailsSent[0].params.nonce);
    });

    it('should be able to verify multiple user ids', async () => {
      await publicKey.put({publicKeyArmored, origin});
      expect(mailsSent.length).to.equal(4);
      await publicKey.verify(mailsSent[0].params);
      await publicKey.verify(mailsSent[1].params);
      await publicKey.verify(mailsSent[2].params);
      await publicKey.verify(mailsSent[3].params);
      const gotten = await mongo.get({keyId: mailsSent[0].params.keyId}, DB_TYPE);
      expect(gotten.userIds[0].verified).to.be.true;
      expect(gotten.userIds[1].verified).to.be.true;
      expect(gotten.userIds[2].verified).to.be.true;
      expect(gotten.userIds[3].verified).to.be.true;
    });
  });

  describe('getVerified', () => {
    let key;

    describe('should find a verified key', () => {
      beforeEach(async () => {
        key = pgp.parseKey(publicKeyArmored);
        await publicKey.put({publicKeyArmored, primaryEmail, origin});
        await publicKey.verify(mailsSent[0].params);
      });

      it('by fingerprint', async () => {
        const verified = await publicKey.getVerified({fingerprint: key.fingerprint});
        expect(verified).to.exist;
      });

      it('by all userIds', async () => {
        const verified = await publicKey.getVerified({userIds: key.userIds});
        expect(verified).to.exist;
      });

      it('by verified userId', async () => {
        const verified = await publicKey.getVerified({userIds: [key.userIds[0]]});
        expect(verified).to.exist;
      });

      it('by unverified userId', async () => {
        const verified = await publicKey.getVerified({userIds: [key.userIds[1]]});
        expect(verified).to.not.exist;
      });

      it('by keyId', async () => {
        const verified = await publicKey.getVerified({keyId: key.keyId});
        expect(verified).to.exist;
      });

      it('by all params', async () => {
        const verified = await publicKey.getVerified(key);
        expect(verified).to.exist;
      });
    });

    describe('should not find an unverified key', () => {
      beforeEach(async () => {
        key = pgp.parseKey(publicKeyArmored);
        key.userIds[0].verified = false;
        await mongo.create(key, DB_TYPE);
      });

      it('by fingerprint', async () => {
        const verified = await publicKey.getVerified({fingerprint: key.fingerprint});
        expect(verified).to.not.exist;
      });

      it('by userIds', async () => {
        const verified = await publicKey.getVerified({userIds: key.userIds});
        expect(verified).to.not.exist;
      });

      it('by keyId', async () => {
        const verified = await publicKey.getVerified({keyId: key.keyId});
        expect(verified).to.not.exist;
      });

      it('by all params', async () => {
        const verified = await publicKey.getVerified(key);
        expect(verified).to.not.exist;
      });
    });
  });

  describe('get', () => {
    let emailParams;

    beforeEach(async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      emailParams = mailsSent[0].params;
    });

    it('should return verified key by key id', async () => {
      await publicKey.verify(emailParams);
      const key = await publicKey.get({keyId: emailParams.keyId});
      expect(key.publicKeyArmored).to.exist;
    });

    it('should return verified key by key id (uppercase)', async () => {
      await publicKey.verify(emailParams);
      const key = await publicKey.get({keyId: emailParams.keyId.toUpperCase()});
      expect(key.publicKeyArmored).to.exist;
    });

    it('should return verified key by fingerprint', async () => {
      await publicKey.verify(emailParams);
      const fingerprint = pgp.parseKey(publicKeyArmored).fingerprint;
      const key = await publicKey.get({fingerprint});
      expect(key.publicKeyArmored).to.exist;
    });

    it('should return verified key by fingerprint (uppercase)', async () => {
      await publicKey.verify(emailParams);
      const fingerprint = pgp.parseKey(publicKeyArmored).fingerprint.toUpperCase();
      const key = await publicKey.get({fingerprint});
      expect(key.publicKeyArmored).to.exist;
    });

    it('should return verified key by email address', async () => {
      await publicKey.verify(emailParams);
      const key = await publicKey.get({email: primaryEmail});
      expect(key.publicKeyArmored).to.exist;
    });

    it('should return verified key by email address (uppercase)', async () => {
      await publicKey.verify(emailParams);
      const key = await publicKey.get({email: primaryEmail.toUpperCase()});
      expect(key.publicKeyArmored).to.exist;
    });

    it('should throw 404 for unverified key', async () => {
      try {
        await publicKey.get({keyId: emailParams.keyId});
        expect(false).to.be.true;
      } catch (e) {
        expect(e.status).to.equal(404);
      }
    });
  });

  describe('requestRemove', () => {
    let keyId;

    beforeEach(async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      keyId = mailsSent[0].params.keyId;
    });

    it('should work for verified key', async () => {
      await publicKey.verify(mailsSent[0].params);
      await publicKey.requestRemove({keyId, origin});
      expect(mailsSent.length).to.equal(5);
    });

    it('should work for unverified key', async () => {
      await publicKey.requestRemove({keyId, origin});
      expect(mailsSent.length).to.equal(5);
    });

    it('should work by email address', async () => {
      await publicKey.requestRemove({email: primaryEmail, origin});
      expect(mailsSent.length).to.equal(2);
    });

    it('should throw 404 for no key', async () => {
      await mongo.remove({keyId}, DB_TYPE);
      try {
        await publicKey.requestRemove({keyId, origin});
        expect(false).to.be.true;
      } catch (e) {
        expect(e.status).to.equal(404);
      }
    });
  });

  describe('verifyRemove', () => {
    let keyId;

    beforeEach(async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      keyId = mailsSent[0].params.keyId;
      await publicKey.requestRemove({keyId, origin});
    });

    it('should remove key', async () => {
      await publicKey.verifyRemove(mailsSent[1].params);
      const key = await mongo.get({keyId}, DB_TYPE);
      expect(key).to.not.exist;
    });

    it('should throw 404 for no key', async () => {
      await mongo.remove({keyId}, DB_TYPE);
      try {
        await publicKey.verifyRemove(mailsSent[1].params);
        expect(false).to.be.true;
      } catch (e) {
        expect(e.status).to.equal(404);
      }
    });
  });
});
<commit_msg>Delete redundant test<commit_after>'use strict';

const log = require('winston');
const config = require('config');
const nodemailer = require('nodemailer');
const Email = require('../../src/email/email');
const Mongo = require('../../src/dao/mongo');
const PGP = require('../../src/service/pgp');
const PublicKey = require('../../src/service/public-key');

describe('Public Key Integration Tests', function() {
  this.timeout(20000);

  let sandbox;
  let publicKey;
  let email;
  let mongo;
  let pgp;
  let sendEmailStub;
  let publicKeyArmored;
  let publicKeyArmored2;
  let mailsSent;

  const DB_TYPE = 'publickey';
  const primaryEmail = 'test1@example.com';
  const primaryEmail2 = 'test2@example.com';
  const origin = {host: 'localhost', protocol: 'http'};

  before(async () => {
    publicKeyArmored = require('fs').readFileSync(`${__dirname}/../key3.asc`, 'utf8');
    publicKeyArmored2 = require('fs').readFileSync(`${__dirname}/../key4.asc`, 'utf8');
    sinon.stub(log, 'info');
    mongo = new Mongo();
    await mongo.init(config.mongo);
  });

  beforeEach(async () => {
    sandbox = sinon.sandbox.create();

    await mongo.clear(DB_TYPE);
    mailsSent = [];
    sendEmailStub = sinon.stub().returns(Promise.resolve({response: '250'}));
    sendEmailStub.withArgs(sinon.match(recipient => {
      mailsSent[mailsSent.length] = {to: recipient.to.address};
      return true;
    }), sinon.match(params => {
      mailsSent[mailsSent.length - 1].params = params;
      expect(params.nonce).to.exist;
      expect(params.keyId).to.exist;
      return true;
    }));
    sandbox.stub(nodemailer, 'createTransport').returns({
      templateSender: () => sendEmailStub
    });
    email = new Email(nodemailer);
    email.init({
      host: 'localhost',
      auth: {user: 'user', pass: 'pass'},
      sender: {name: 'Foo Bar', email: 'foo@bar.com'}
    });
    pgp = new PGP();
    publicKey = new PublicKey(pgp, mongo, email);
  });

  afterEach(() => {
    sandbox.restore();
  });

  after(async () => {
    await mongo.clear(DB_TYPE);
    await mongo.disconnect();
    log.info.restore();
  });

  describe('put', () => {
    it('should persist key and send verification email with primaryEmail', async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      expect(mailsSent.length).to.equal(1);
      expect(mailsSent[0].to).to.equal(primaryEmail);
      expect(mailsSent[0].params.keyId).to.exist;
      expect(mailsSent[0].params.nonce).to.exist;
    });
    it('should persist key and send verification email without primaryEmail', async () => {
      await publicKey.put({publicKeyArmored, origin});
      expect(mailsSent.length).to.equal(4);
    });

    it('should work twice if not yet verified', async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      expect(mailsSent.length).to.equal(1);
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      expect(mailsSent.length).to.equal(2);
    });

    it('should throw 304 if key already exists', async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      await publicKey.verify(mailsSent[0].params);
      try {
        await publicKey.put({publicKeyArmored, primaryEmail, origin});
        expect(false).to.be.true;
      } catch (e) {
        expect(e.status).to.equal(304);
      }
    });
  });

  describe('_purgeOldUnverified', () => {
    let key;

    beforeEach(async () => {
      key = pgp.parseKey(publicKeyArmored);
    });

    it('should work for no keys', async () => {
      const r = await publicKey._purgeOldUnverified();
      expect(r.deletedCount).to.equal(0);
    });

    it('should not remove a current unverified key', async () => {
      await publicKey._persisKey(key);
      const r = await publicKey._purgeOldUnverified();
      expect(r.deletedCount).to.equal(0);
    });

    it('should not remove a current verified key', async () => {
      key.userIds[0].verified = true;
      await publicKey._persisKey(key);
      const r = await publicKey._purgeOldUnverified();
      expect(r.deletedCount).to.equal(0);
    });

    it('should not remove an old verified key', async () => {
      key.uploaded.setDate(key.uploaded.getDate() - 31);
      key.userIds[0].verified = true;
      await publicKey._persisKey(key);
      const r = await publicKey._purgeOldUnverified();
      expect(r.deletedCount).to.equal(0);
    });

    it('should remove an old unverified key', async () => {
      key.uploaded.setDate(key.uploaded.getDate() - 31);
      await publicKey._persisKey(key);
      const r = await publicKey._purgeOldUnverified();
      expect(r.deletedCount).to.equal(1);
    });
  });

  describe('verify', () => {
    it('should update the document', async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      const emailParams = mailsSent[0].params;
      await publicKey.verify(emailParams);
      const gotten = await mongo.get({keyId: emailParams.keyId}, DB_TYPE);
      expect(gotten.userIds[0].verified).to.be.true;
      expect(gotten.userIds[0].nonce).to.be.null;
      expect(gotten.userIds[1].verified).to.be.false;
      expect(gotten.userIds[1].nonce).to.exist;
    });

    it('should not find the document', async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      const emailParams = mailsSent[0].params;
      try {
        await publicKey.verify({keyId: emailParams.keyId, nonce: 'fake_nonce'});
        expect(true).to.be.false;
      } catch (e) {
        expect(e.status).to.equal(404);
      }
      const gotten = await mongo.get({keyId: emailParams.keyId}, DB_TYPE);
      expect(gotten.userIds[0].verified).to.be.false;
      expect(gotten.userIds[0].nonce).to.equal(emailParams.nonce);
      expect(gotten.userIds[1].verified).to.be.false;
      expect(gotten.userIds[1].nonce).to.exist;
    });

    it('should not verify a second key for already verified user id of another key', async () => {
      await publicKey.put({publicKeyArmored, primaryEmail: primaryEmail2, origin});
      expect(mailsSent.length).to.equal(1);
      await publicKey.put({publicKeyArmored: publicKeyArmored2, primaryEmail: primaryEmail2, origin});
      expect(mailsSent.length).to.equal(2);
      await publicKey.verify(mailsSent[1].params);

      try {
        await publicKey.verify(mailsSent[0].params);
        expect(true).to.be.false;
      } catch (e) {
        expect(e.status).to.equal(304);
      }
      const gotten = await mongo.get({keyId: mailsSent[0].params.keyId}, DB_TYPE);
      expect(gotten.userIds[1].email).to.equal(primaryEmail2);
      expect(gotten.userIds[1].verified).to.be.false;
      expect(gotten.userIds[1].nonce).to.equal(mailsSent[0].params.nonce);
    });

    it('should be able to verify multiple user ids', async () => {
      await publicKey.put({publicKeyArmored, origin});
      expect(mailsSent.length).to.equal(4);
      await publicKey.verify(mailsSent[0].params);
      await publicKey.verify(mailsSent[1].params);
      await publicKey.verify(mailsSent[2].params);
      await publicKey.verify(mailsSent[3].params);
      const gotten = await mongo.get({keyId: mailsSent[0].params.keyId}, DB_TYPE);
      expect(gotten.userIds[0].verified).to.be.true;
      expect(gotten.userIds[1].verified).to.be.true;
      expect(gotten.userIds[2].verified).to.be.true;
      expect(gotten.userIds[3].verified).to.be.true;
    });
  });

  describe('getVerified', () => {
    let key;

    describe('should find a verified key', () => {
      beforeEach(async () => {
        key = pgp.parseKey(publicKeyArmored);
        await publicKey.put({publicKeyArmored, primaryEmail, origin});
        await publicKey.verify(mailsSent[0].params);
      });

      it('by fingerprint', async () => {
        const verified = await publicKey.getVerified({fingerprint: key.fingerprint});
        expect(verified).to.exist;
      });

      it('by all userIds', async () => {
        const verified = await publicKey.getVerified({userIds: key.userIds});
        expect(verified).to.exist;
      });

      it('by verified userId', async () => {
        const verified = await publicKey.getVerified({userIds: [key.userIds[0]]});
        expect(verified).to.exist;
      });

      it('by unverified userId', async () => {
        const verified = await publicKey.getVerified({userIds: [key.userIds[1]]});
        expect(verified).to.not.exist;
      });

      it('by keyId', async () => {
        const verified = await publicKey.getVerified({keyId: key.keyId});
        expect(verified).to.exist;
      });

      it('by all params', async () => {
        const verified = await publicKey.getVerified(key);
        expect(verified).to.exist;
      });
    });

    describe('should not find an unverified key', () => {
      beforeEach(async () => {
        key = pgp.parseKey(publicKeyArmored);
        key.userIds[0].verified = false;
        await mongo.create(key, DB_TYPE);
      });

      it('by fingerprint', async () => {
        const verified = await publicKey.getVerified({fingerprint: key.fingerprint});
        expect(verified).to.not.exist;
      });

      it('by userIds', async () => {
        const verified = await publicKey.getVerified({userIds: key.userIds});
        expect(verified).to.not.exist;
      });

      it('by keyId', async () => {
        const verified = await publicKey.getVerified({keyId: key.keyId});
        expect(verified).to.not.exist;
      });

      it('by all params', async () => {
        const verified = await publicKey.getVerified(key);
        expect(verified).to.not.exist;
      });
    });
  });

  describe('get', () => {
    let emailParams;

    beforeEach(async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      emailParams = mailsSent[0].params;
    });

    it('should return verified key by key id', async () => {
      await publicKey.verify(emailParams);
      const key = await publicKey.get({keyId: emailParams.keyId});
      expect(key.publicKeyArmored).to.exist;
    });

    it('should return verified key by key id (uppercase)', async () => {
      await publicKey.verify(emailParams);
      const key = await publicKey.get({keyId: emailParams.keyId.toUpperCase()});
      expect(key.publicKeyArmored).to.exist;
    });

    it('should return verified key by fingerprint', async () => {
      await publicKey.verify(emailParams);
      const fingerprint = pgp.parseKey(publicKeyArmored).fingerprint;
      const key = await publicKey.get({fingerprint});
      expect(key.publicKeyArmored).to.exist;
    });

    it('should return verified key by fingerprint (uppercase)', async () => {
      await publicKey.verify(emailParams);
      const fingerprint = pgp.parseKey(publicKeyArmored).fingerprint.toUpperCase();
      const key = await publicKey.get({fingerprint});
      expect(key.publicKeyArmored).to.exist;
    });

    it('should return verified key by email address', async () => {
      await publicKey.verify(emailParams);
      const key = await publicKey.get({email: primaryEmail});
      expect(key.publicKeyArmored).to.exist;
    });

    it('should return verified key by email address (uppercase)', async () => {
      await publicKey.verify(emailParams);
      const key = await publicKey.get({email: primaryEmail.toUpperCase()});
      expect(key.publicKeyArmored).to.exist;
    });

    it('should throw 404 for unverified key', async () => {
      try {
        await publicKey.get({keyId: emailParams.keyId});
        expect(false).to.be.true;
      } catch (e) {
        expect(e.status).to.equal(404);
      }
    });
  });

  describe('requestRemove', () => {
    let keyId;

    beforeEach(async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      keyId = mailsSent[0].params.keyId;
    });

    it('should work for verified key', async () => {
      await publicKey.verify(mailsSent[0].params);
      await publicKey.requestRemove({keyId, origin});
      expect(mailsSent.length).to.equal(5);
    });

    it('should work for unverified key', async () => {
      await publicKey.requestRemove({keyId, origin});
      expect(mailsSent.length).to.equal(5);
    });

    it('should work by email address', async () => {
      await publicKey.requestRemove({email: primaryEmail, origin});
      expect(mailsSent.length).to.equal(2);
    });

    it('should throw 404 for no key', async () => {
      await mongo.remove({keyId}, DB_TYPE);
      try {
        await publicKey.requestRemove({keyId, origin});
        expect(false).to.be.true;
      } catch (e) {
        expect(e.status).to.equal(404);
      }
    });
  });

  describe('verifyRemove', () => {
    let keyId;

    beforeEach(async () => {
      await publicKey.put({publicKeyArmored, primaryEmail, origin});
      keyId = mailsSent[0].params.keyId;
      await publicKey.requestRemove({keyId, origin});
    });

    it('should remove key', async () => {
      await publicKey.verifyRemove(mailsSent[1].params);
      const key = await mongo.get({keyId}, DB_TYPE);
      expect(key).to.not.exist;
    });

    it('should throw 404 for no key', async () => {
      await mongo.remove({keyId}, DB_TYPE);
      try {
        await publicKey.verifyRemove(mailsSent[1].params);
        expect(false).to.be.true;
      } catch (e) {
        expect(e.status).to.equal(404);
      }
    });
  });
});
<|endoftext|>


<commit_before>// Constructor of Heap
// - comparator - Function - given two items returns a number
// - initData - Array - the initial data in a format:
//      Object:
//        - id - String - unique id of the item
//        - value - Any - the data value
//    the contents of initData is retained
MaxHeap = function (comparator, initData) {
  if (! _.isFunction(comparator))
    throw new Error('Passed comparator is invalid, should be a comparison function');
  var self = this;
  self._comparator = comparator;

  if (Package && Package.minimongo)
    self._heapIdx = new Package.minimongo.LocalCollection._IdMap;
  else
    self._heapIdx = new IdMap;

  self._heap = [];

  if (_.isArray(initData))
    self._initFromData(initData);
};

_.extend(MaxHeap.prototype, {
  _initFromData: function (data) {
    var self = this;

    self._heap = _.clone(data);

    _.each(data, function (o, i) {
      self._heapIdx.set(o.id, i);
    });

    for (var i = parentIdx(data.length); i >= 0; i--)
      self._downHeap(i);
  },

  _downHeap: function (idx) {
    var self = this;

    while (leftChildIdx(idx) < self.size()) {
      var left = leftChildIdx(idx);
      var right = rightChildIdx(idx);
      var largest = idx;

      if (left < self.size() &&
          self._comparator(self._get(left), self._get(largest)) > 0) {
        largest = left;
      }
      if (right < self.size() &&
          self._comparator(self._get(right), self._get(largest)) > 0) {
        largest = right;
      }

      if (largest === idx)
        break;

      self._swap(largest, idx);
      idx = largest;
    }
  },

  _upHeap: function (idx) {
    var self = this;
    var value = self._get(idx);

    while (idx > 0) {
      var parent = parentIdx(idx);
      if (self._comparator(self._get(parent), value) < 0) {
        self._swap(parent, idx)
        idx = parent;
      } else {
        break;
      }
    }
  },

  // Internal: gets raw data object placed on idxth place in heap
  _get: function (idx) {
    var self = this;
    return self._heap[idx].value;
  },

  _swap: function (idxA, idxB) {
    var self = this;
    var A = self._heap[idxA];
    var B = self._heap[idxB];

    self._heapIdx.set(A.id, idxB);
    self._heapIdx.set(B.id, idxA);

    self._heap[idxA] = B;
    self._heap[idxB] = A;
  },

  get: function (id) {
    var self = this;
    if (! self.has(id))
      return null;
    return self._get(self._heapIdx.get(id));
  },
  set: function (id, value) {
    var self = this;

    if (self.has(id)) {
      if (self.get(id) === value)
        return;
      else
        self.remove(id);
    }

    self._heapIdx.set(id, self._heap.length);
    self._heap.push({ id: id, value: value });
    self._upHeap(self._heap.length - 1);
  },
  remove: function (id) {
    var self = this;

    if (self.has(id)) {
      var last = self._heap.length - 1;
      var idx = self._heapIdx.get(id);

      if (idx !== last) {
        self._swap(idx, last);
        self._heap.pop();
        self._downHeap(idx);
      } else {
        self._heap.pop();
      }

      self._heapIdx.remove(id);
    }
  },
  has: function (id) {
    var self = this;
    return self._heapIdx.has(id);
  },
  empty: function (id) {
    var self = this;
    return !self.size();
  },
  clear: function () {
    var self = this;
    self._heap = [];
    self._heapIdx.clear();
  },
  forEach: function (iterator) {
    var self = this;
    _.each(self._heap, function (obj) {
      return iterator(obj.value, obj.id);
    });
  },
  size: function () {
    var self = this;
    return self._heap.length;
  },
  setDefault: function (id, def) {
    var self = this;
    if (self.has(id))
      return self.get(id);
    self.set(id, def);
    return def;
  },
  clone: function () {
    var self = this;
    var clone = new MaxHeap(self._comparator);
    clone._heap = EJSON.clone(self._heap);
    clone._heapIdx = self._heapIdx.clone();
    return clone;
  },

  maxElementId: function () {
    var self = this;
    return self.size() ? self._heap[0].id : null;
  }
});

function leftChildIdx (i) { return i * 2 + 1; }
function rightChildIdx (i) { return i * 2 + 2; }
function parentIdx (i) { return (i - 1) >> 1; }

<commit_msg>More comments on Heap implementation, linear initialization doesn't retain anything but the references to the values.<commit_after>// Constructor of Heap
// - comparator - Function - given two items returns a number
// - initData - Array - the initial data in a format:
//      Object:
//        - id - String - unique id of the item
//        - value - Any - the data value
//    the contents of initData is retained
MaxHeap = function (comparator, initData) {
  if (! _.isFunction(comparator))
    throw new Error('Passed comparator is invalid, should be a comparison function');
  var self = this;

  // a C-style comparator that is given two values and returns a number,
  // negative if the first value is less than the second, positive if the second
  // value is greater than the first and zero if they are equal.
  self._comparator = comparator;

  // _heapIdx maps an id to an index in the Heap array the corresponding value
  // is located on.
  if (Package && Package.minimongo)
    self._heapIdx = new Package.minimongo.LocalCollection._IdMap;
  else
    self._heapIdx = new IdMap;

  // The Heap data-structure implemented as a 0-based contiguous array where
  // every item on index idx is a node in a complete binary tree. Every node can
  // have leaves on indexes idx*2+1 and idx*2+2, except for the lists. Every
  // node has a parent on index (idx-1)/2;
  self._heap = [];

  // If the initial array is passed, we can build the heap in linear time
  // complexity (O(N)) compared to linearithmetic time complexity (O(nlogn)) if
  // we push elements one by one.
  if (_.isArray(initData))
    self._initFromData(initData);
};

_.extend(MaxHeap.prototype, {
  // Builds a new heap in-place in linear time based on passed data
  _initFromData: function (data) {
    var self = this;

    self._heap = _.map(data, function (o) {
      return { id: EJSON.clone(o.id), value: o.value };
    });

    _.each(data, function (o, i) {
      self._heapIdx.set(o.id, i);
    });

    for (var i = parentIdx(data.length); i >= 0; i--)
      self._downHeap(i);
  },

  _downHeap: function (idx) {
    var self = this;

    while (leftChildIdx(idx) < self.size()) {
      var left = leftChildIdx(idx);
      var right = rightChildIdx(idx);
      var largest = idx;

      if (left < self.size() &&
          self._comparator(self._get(left), self._get(largest)) > 0) {
        largest = left;
      }
      if (right < self.size() &&
          self._comparator(self._get(right), self._get(largest)) > 0) {
        largest = right;
      }

      if (largest === idx)
        break;

      self._swap(largest, idx);
      idx = largest;
    }
  },

  _upHeap: function (idx) {
    var self = this;
    var value = self._get(idx);

    while (idx > 0) {
      var parent = parentIdx(idx);
      if (self._comparator(self._get(parent), value) < 0) {
        self._swap(parent, idx)
        idx = parent;
      } else {
        break;
      }
    }
  },

  // Internal: gets raw data object placed on idxth place in heap
  _get: function (idx) {
    var self = this;
    return self._heap[idx].value;
  },

  _swap: function (idxA, idxB) {
    var self = this;
    var A = self._heap[idxA];
    var B = self._heap[idxB];

    self._heapIdx.set(A.id, idxB);
    self._heapIdx.set(B.id, idxA);

    self._heap[idxA] = B;
    self._heap[idxB] = A;
  },

  get: function (id) {
    var self = this;
    if (! self.has(id))
      return null;
    return self._get(self._heapIdx.get(id));
  },
  set: function (id, value) {
    var self = this;

    if (self.has(id)) {
      if (self.get(id) === value)
        return;
      else
        self.remove(id);
    }

    self._heapIdx.set(id, self._heap.length);
    self._heap.push({ id: id, value: value });
    self._upHeap(self._heap.length - 1);
  },
  remove: function (id) {
    var self = this;

    if (self.has(id)) {
      var last = self._heap.length - 1;
      var idx = self._heapIdx.get(id);

      if (idx !== last) {
        self._swap(idx, last);
        self._heap.pop();
        self._downHeap(idx);
      } else {
        self._heap.pop();
      }

      self._heapIdx.remove(id);
    }
  },
  has: function (id) {
    var self = this;
    return self._heapIdx.has(id);
  },
  empty: function (id) {
    var self = this;
    return !self.size();
  },
  clear: function () {
    var self = this;
    self._heap = [];
    self._heapIdx.clear();
  },
  forEach: function (iterator) {
    var self = this;
    _.each(self._heap, function (obj) {
      return iterator(obj.value, obj.id);
    });
  },
  size: function () {
    var self = this;
    return self._heap.length;
  },
  setDefault: function (id, def) {
    var self = this;
    if (self.has(id))
      return self.get(id);
    self.set(id, def);
    return def;
  },
  clone: function () {
    var self = this;
    var clone = new MaxHeap(self._comparator);
    clone._heap = EJSON.clone(self._heap);
    clone._heapIdx = self._heapIdx.clone();
    return clone;
  },

  maxElementId: function () {
    var self = this;
    return self.size() ? self._heap[0].id : null;
  }
});

function leftChildIdx (i) { return i * 2 + 1; }
function rightChildIdx (i) { return i * 2 + 2; }
function parentIdx (i) { return (i - 1) >> 1; }

<|endoftext|>


<commit_before><commit_msg>fix: include server error message in tm merge notification (#705)<commit_after><|endoftext|>


<commit_before>describe('Orbit', function() {
  var plugin;
  var $html;
  var template = `

      <button class="orbit-previous">Previous Slide◀</button>
      <button class="orbit-next">Next Slide▶</button>

        Slide #1 content.

        Slide #2 content.

        Slide #3 content.

        Slide #4 content.

      <button class="is-active" data-slide="0">First slide details.Current Slide</button>
      <button data-slide="1">Second slide details.</button>
      <button data-slide="2">Third slide details.</button>
      <button data-slide="3">Fourth slide details.</button>

`;
  Foundation.Orbit.defaults.useMUI = false;

  afterEach(function() {
    plugin.destroy();
    $html.remove();
  });

  describe('constructor()', function() {
    it('stores the element and plugin options', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.$element.should.be.an('object');
      plugin.$wrapper.should.be.an('object');
      plugin.$slides.should.be.an('object');
      plugin.options.should.be.an('object');
    });
  });

  describe('init()', function() {
    it('shows the first slide', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.find('.orbit-slide').eq(0).should.be.visible;
    });
    it('hides all slides except the first one', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.find('.orbit-slide').each(function(index) {
        if (index === 0) {return;} // Not for the first one as this is visible
        $(this).should.be.hidden;
      });
    });
    it('makes slide with is-active class active initially', function() {
      $html = $(template).appendTo('body');
      $html.find('.orbit-slide.is-active').removeClass('is-active');
      $html.find('.orbit-slide').eq(2).addClass('is-active');
      plugin = new Foundation.Orbit($html, {});

      $html.find('.orbit-slide').eq(2).should.be.visible;
    });
  });

  describe('loadBullets()', function() {
    it('stores the bullets', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.$bullets.should.be.an('object');
    });
  });

  describe('changeSlide()', function() {
    it('changes slides to the next one for ltr is true', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.changeSlide(true);

      $html.find('.orbit-slide').eq(0).should.be.hidden;
      $html.find('.orbit-slide').eq(1).should.be.visible;
    });
    it('changes slides to the last one for ltr is false', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.changeSlide(false);

      $html.find('.orbit-slide').eq(0).should.not.have.class('is-active');
      $html.find('.orbit-slide').eq(-1).should.have.class('is-active');
    });
    it('changes slides to the chosen one', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.changeSlide(true, $html.find('.orbit-slide').eq(2), 2);

      $html.find('.orbit-slide').eq(0).should.be.hidden;
      $html.find('.orbit-slide').eq(2).should.be.visible;
    });
    it('toggles ARIA attributes', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.changeSlide(true);

      $html.find('.orbit-slide').eq(0).should.not.have.attr('aria-live');
      $html.find('.orbit-slide').eq(1).should.have.attr('aria-live', 'polite');
    });
    it('fires beforeslidechange.zf.orbit event', function(done) {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.on('beforeslidechange.zf.orbit', function() {
        // Old slide should still be active
        $html.find('.orbit-slide').eq(0).should.be.visible;
        done();
      });
      plugin.changeSlide(true);
    });
    it('fires slidechange.zf.orbit event', function(done) {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.on('slidechange.zf.orbit', function() {
        // New slide is already active
        $html.find('.orbit-slide').eq(1).should.be.visible;
        done();
      });
      plugin.changeSlide(true);
    });
  });

  describe('updateBullets()', function() {
    it('updates the bullets', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.changeSlide(true);

      $html.find('.orbit-bullets [data-slide]').eq(0).should.not.have.class('is-active');
      $html.find('.orbit-bullets [data-slide]').eq(1).should.have.class('is-active');
    });
  });

  describe('events()', function() {
    it('changes slides on bullet click', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.find('.orbit-bullets [data-slide]').eq(2).trigger('click');

      $html.find('.orbit-slide').eq(0).should.be.hidden;
      $html.find('.orbit-slide').eq(2).should.be.visible;
    });
    it('changes slides to the previous one', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.find('.orbit-previous').trigger('click');

      $html.find('.orbit-slide').eq(0).should.be.hidden;
      $html.find('.orbit-slide').eq(-1).should.be.visible;
    });
    it('changes slides to the next one', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.find('.orbit-next').trigger('click');

      $html.find('.orbit-slide').eq(0).should.be.hidden;
      $html.find('.orbit-slide').eq(1).should.be.visible;
    });
  });

  describe('geoSync()', function() {
    it('changes slides automatically based on timerDelay option', function(done) {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {timerDelay: 200});

      setTimeout(function() {
        $html.find('.orbit-slide').eq(0).should.be.hidden;
        $html.find('.orbit-slide').eq(1).should.be.visible;
        done();
      }, 201);
    });
  });
});<commit_msg>Added util to mock keyboard events for orbit unit tests.<commit_after>describe('Orbit', function() {
  var plugin;
  var $html;
  var template = `

      <button class="orbit-previous">Previous Slide◀</button>
      <button class="orbit-next">Next Slide▶</button>

        Slide #1 content.

        Slide #2 content.

        Slide #3 content.

        Slide #4 content.

      <button class="is-active" data-slide="0">First slide details.Current Slide</button>
      <button data-slide="1">Second slide details.</button>
      <button data-slide="2">Third slide details.</button>
      <button data-slide="3">Fourth slide details.</button>

`;
  Foundation.Orbit.defaults.useMUI = false;

  afterEach(function() {
    plugin.destroy();
    $html.remove();
  });

  describe('constructor()', function() {
    it('stores the element and plugin options', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.$element.should.be.an('object');
      plugin.$wrapper.should.be.an('object');
      plugin.$slides.should.be.an('object');
      plugin.options.should.be.an('object');
    });
  });

  describe('init()', function() {
    it('shows the first slide', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.find('.orbit-slide').eq(0).should.be.visible;
    });
    it('hides all slides except the first one', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.find('.orbit-slide').each(function(index) {
        if (index === 0) {return;} // Not for the first one as this is visible
        $(this).should.be.hidden;
      });
    });
    it('makes slide with is-active class active initially', function() {
      $html = $(template).appendTo('body');
      $html.find('.orbit-slide.is-active').removeClass('is-active');
      $html.find('.orbit-slide').eq(2).addClass('is-active');
      plugin = new Foundation.Orbit($html, {});

      $html.find('.orbit-slide').eq(2).should.be.visible;
    });
  });

  describe('loadBullets()', function() {
    it('stores the bullets', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.$bullets.should.be.an('object');
    });
  });

  describe('changeSlide()', function() {
    it('changes slides to the next one for ltr is true', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.changeSlide(true);

      $html.find('.orbit-slide').eq(0).should.be.hidden;
      $html.find('.orbit-slide').eq(1).should.be.visible;
    });
    it('changes slides to the last one for ltr is false', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.changeSlide(false);

      $html.find('.orbit-slide').eq(0).should.not.have.class('is-active');
      $html.find('.orbit-slide').eq(-1).should.have.class('is-active');
    });
    it('changes slides to the chosen one', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.changeSlide(true, $html.find('.orbit-slide').eq(2), 2);

      $html.find('.orbit-slide').eq(0).should.be.hidden;
      $html.find('.orbit-slide').eq(2).should.be.visible;
    });
    it('toggles ARIA attributes', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.changeSlide(true);

      $html.find('.orbit-slide').eq(0).should.not.have.attr('aria-live');
      $html.find('.orbit-slide').eq(1).should.have.attr('aria-live', 'polite');
    });
    it('fires beforeslidechange.zf.orbit event', function(done) {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.on('beforeslidechange.zf.orbit', function() {
        // Old slide should still be active
        $html.find('.orbit-slide').eq(0).should.be.visible;
        done();
      });
      plugin.changeSlide(true);
    });
    it('fires slidechange.zf.orbit event', function(done) {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.on('slidechange.zf.orbit', function() {
        // New slide is already active
        $html.find('.orbit-slide').eq(1).should.be.visible;
        done();
      });
      plugin.changeSlide(true);
    });
  });

  describe('updateBullets()', function() {
    it('updates the bullets', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      plugin.changeSlide(true);

      $html.find('.orbit-bullets [data-slide]').eq(0).should.not.have.class('is-active');
      $html.find('.orbit-bullets [data-slide]').eq(1).should.have.class('is-active');
    });
  });

  describe('events()', function() {
    it('changes slides on bullet click', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.find('.orbit-bullets [data-slide]').eq(2).trigger('click');

      $html.find('.orbit-slide').eq(0).should.be.hidden;
      $html.find('.orbit-slide').eq(2).should.be.visible;
    });
    it('changes slides to the previous one', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.find('.orbit-previous').trigger('click');

      $html.find('.orbit-slide').eq(0).should.be.hidden;
      $html.find('.orbit-slide').eq(-1).should.be.visible;
    });
    it('changes slides to the next one', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});

      $html.find('.orbit-next').trigger('click');

      $html.find('.orbit-slide').eq(0).should.be.hidden;
      $html.find('.orbit-slide').eq(1).should.be.visible;
    });
  });

  describe('geoSync()', function() {
    it('changes slides automatically based on timerDelay option', function(done) {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {timerDelay: 200});

      setTimeout(function() {
        $html.find('.orbit-slide').eq(0).should.be.hidden;
        $html.find('.orbit-slide').eq(1).should.be.visible;
        done();
      }, 201);
    });
  });

  describe('keyboard events', function() {
    it('moves switches to next slide using ARROW_RIGHT', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});
      let spy = sinon.spy(plugin, 'changeSlide');

      plugin.$wrapper.focus()
        .trigger(window.mockKeyboardEvent('ARROW_RIGHT'));

      sinon.assert.calledWith(spy, true);
    });
    it('moves switches to previous slide using ARROW_LEFT', function() {
      $html = $(template).appendTo('body');
      plugin = new Foundation.Orbit($html, {});
      let spy = sinon.spy(plugin, 'changeSlide');

      plugin.$wrapper.focus()
        .trigger(window.mockKeyboardEvent('ARROW_LEFT'));

      sinon.assert.calledWith(spy, false);
    });
  });
});<|endoftext|>


<commit_before>module.exports = function (grunt) {
    grunt.initConfig({
        "pkg": grunt.file.readJSON("package.json"),
        "meta": {
            "paths": {
                "source": "Source",
                "dist": "Distribution"
            }
        },
        "tslint": {
            "options": {
                "configuration": grunt.file.readJSON("tslint.json")
            },
            "files": {
                "src": ["<%= meta.paths.source %>/<%= pkg.name %>.ts"]
            }
        },
        "typescript": {
            "base": {
                "src": "<%= meta.paths.source %>/<%= pkg.name %>.ts",
                "dest": "<%= meta.paths.source %>/<%= pkg.name %>.js"
            }
        },
        "clean": ["<%= meta.paths.dist %>"],
        "copy": {
            "default": {
                "files": [{
                    "src": "<%= meta.paths.source %>/<%= pkg.name %>.ts",
                    "dest": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.ts"
                }, {
                    "src": "<%= meta.paths.source %>/References/*.ts",
                    "dest": "<%= meta.paths.dist %>/",
                    "expand": true,
                    "flatten": true
                }, {
                    "src": "README.md",
                    "dest": "<%= meta.paths.dist %>/"
                }, {
                    "src": "README.md",
                    "dest": "<%= meta.paths.source %>/"
                }, {
                    "src": "LICENSE.txt",
                    "dest": "<%= meta.paths.dist %>/"
                }, {
                    "src": "LICENSE.txt",
                    "dest": "<%= meta.paths.source %>/"
                }]
            }
        },
        "uglify": {
            "options": {
                "compress": true
            },
            "dist": {
                "files": {
                    "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.min.js": ["<%= meta.paths.source %>/<%= pkg.name %>.js"],
                }
            }
        },
        "preprocess": {
            "dist": {
                "src": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.ts",
                "dest": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.ts"
            }
        },
        "mocha_phantomjs": {
            "all": ["Tests/*.html"]
        }
    });

    grunt.loadNpmTasks("grunt-contrib-clean");
    grunt.loadNpmTasks("grunt-contrib-copy");
    grunt.loadNpmTasks("grunt-contrib-uglify");
    grunt.loadNpmTasks("grunt-mocha-phantomjs");
    grunt.loadNpmTasks("grunt-preprocess");
    grunt.loadNpmTasks("grunt-tslint");
    grunt.loadNpmTasks("grunt-typescript");
    grunt.registerTask("default", [
        "tslint", "typescript", "clean", "copy", "uglify", "preprocess", "mocha_phantomjs"
    ]);
};<commit_msg>Added .js with source maps to Distribution<commit_after>module.exports = function (grunt) {
    grunt.initConfig({
        "pkg": grunt.file.readJSON("package.json"),
        "meta": {
            "paths": {
                "source": "Source",
                "dist": "Distribution"
            }
        },
        "tslint": {
            "options": {
                "configuration": grunt.file.readJSON("tslint.json")
            },
            "files": {
                "src": ["<%= meta.paths.source %>/<%= pkg.name %>.ts"]
            }
        },
        "typescript": {
            "base": {
                "src": "<%= meta.paths.source %>/<%= pkg.name %>.ts",
                "dest": "<%= meta.paths.source %>/<%= pkg.name %>.js"
            }
        },
        "clean": ["<%= meta.paths.dist %>"],
        "copy": {
            "default": {
                "files": [{
                    "src": "<%= meta.paths.source %>/<%= pkg.name %>.js",
                    "dest": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.js"
                }, {
                    "src": "<%= meta.paths.source %>/<%= pkg.name %>.ts",
                    "dest": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.ts"
                }, {
                    "src": "<%= meta.paths.source %>/References/*.ts",
                    "dest": "<%= meta.paths.dist %>/",
                    "expand": true,
                    "flatten": true
                }, {
                    "src": "README.md",
                    "dest": "<%= meta.paths.dist %>/"
                }, {
                    "src": "README.md",
                    "dest": "<%= meta.paths.source %>/"
                }, {
                    "src": "LICENSE.txt",
                    "dest": "<%= meta.paths.dist %>/"
                }, {
                    "src": "LICENSE.txt",
                    "dest": "<%= meta.paths.source %>/"
                }]
            }
        },
        "uglify": {
            "options": {
                "compress": true,
                "screwIE8": true,
                "sourceMap": true
            },
            "dist": {
                "files": {
                    "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.min.js": ["<%= meta.paths.source %>/<%= pkg.name %>.js"],
                }
            }
        },
        "preprocess": {
            "dist": {
                "src": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.ts",
                "dest": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.ts"
            }
        },
        "mocha_phantomjs": {
            "all": ["Tests/*.html"]
        }
    });

    grunt.loadNpmTasks("grunt-contrib-clean");
    grunt.loadNpmTasks("grunt-contrib-copy");
    grunt.loadNpmTasks("grunt-contrib-uglify");
    grunt.loadNpmTasks("grunt-mocha-phantomjs");
    grunt.loadNpmTasks("grunt-preprocess");
    grunt.loadNpmTasks("grunt-tslint");
    grunt.loadNpmTasks("grunt-typescript");
    grunt.registerTask("default", [
        "tslint", "typescript", "clean", "copy", "uglify", "preprocess", "mocha_phantomjs"
    ]);
};<|endoftext|>


<commit_before>jest.dontMock(pathToFile('components/chat_notifications_toggler.js.jsx'));
jest.dontMock(pathToFile('mixins/dropdown_toggler.js.jsx'));
jest.dontMock(pathToFile('mixins/local_storage.js'));

describe('ChatNotificationsToggler', function() {
  var chatRooms;
  var ChatNotificationsStore;

  beforeEach(function() {
    global.localStorage = {};
    global.moment = require.requireActual('moment');

    ChatNotificationsStore = require(pathToFile('stores/chat_notifications_store.js'));

    chatRooms = {
      foo: {
        updated: moment().unix(),
        last_read_at: 123
      },
      bar: {
        updated: moment().unix(),
        last_read_at: 123
      },
      baz: {
        updated: moment().unix() - 100,
        last_read_at: moment().unix()
      }
    };

    ChatNotificationsStore.mostRecentlyUpdatedChatRoom
      .mockReturnValue(chatRooms.foo);

    ChatNotificationsStore.getChatRooms.mockReturnValue(chatRooms);

    // Not sure if this kind of mock is an antipattern.
    // Having to do this to get tests to pass might suggest that
    // we need to move the getUnreadCount functions to the components
    // rather than keeping them in the store.
    ChatNotificationsStore.getUnreadCount.mockImplementation(function(acknowledgedAt) {
      var count = _.countBy(
        chatRooms,
        function(entry) {
          var updated = entry.updated > entry.last_read_at;

          if (acknowledgedAt) {
            return updated && entry.updated > acknowledgedAt;
          }

          return updated;
        }
      );

      return count.true || 0;
    });
  });

  it('instantiates a dropdown toggler with default state', function() {
    var Toggler = require(pathToFile('components/chat_notifications_toggler.js.jsx'));

    var toggler = TestUtils.renderIntoDocument(
      <Toggler iconClass="icon icon-bubbles" href="/chat" label="bubbles" />
    );

    expect(toggler.state.chatRooms).toEqual(null);
    expect(toggler.state.acknowledgedAt).toEqual(0);
    expect(toggler.props.title).toEqual('');
  });

  it('acknowledges a click', function() {
    jest.dontMock('moment');

    var Toggler = require(pathToFile('components/chat_notifications_toggler.js.jsx'));
    var toggler = TestUtils.renderIntoDocument(
      <Toggler iconClass="icon icon-bubbles" href="/chat" label="bubbles" />
    );

    toggler.acknowledge();

    expect(toggler.state.acknowledgedAt).toBeCloseTo(moment().unix(), 2);
  });

  it('adds the bg-red class if it has notifications', function() {
    jest.dontMock(appFile('mixins/dropdown_toggler.js.jsx'));
    jest.dontMock(appFile('components/ui/jewel.js.jsx'));

    var Toggler = require(pathToFile('components/chat_notifications_toggler.js.jsx'));
    var toggler = TestUtils.renderIntoDocument(
      <Toggler iconClass="icon icon-bubbles" href="/chat" label="bubbles" />
    );

    expect(toggler.badgeCount()).toEqual(2);

    var badge = TestUtils.scryRenderedDOMComponentsWithClass(
      toggler,
      'bg-red'
    );

    expect(badge.length).toEqual(1);
  });

  it('clears the badge count based on acknowledgedAt', function() {
    var Toggler = require(pathToFile('components/chat_notifications_toggler.js.jsx'));
    var toggler = TestUtils.renderIntoDocument(
      <Toggler iconClass="icon icon-bubbles" href="/chat" label="bubbles" />
    );

    expect(toggler.badgeCount()).toEqual(2);

    toggler.acknowledge();

    expect(toggler.badgeCount()).toEqual(0);
  });
});
<commit_msg>Fix brittle time-based test.<commit_after>jest.dontMock(pathToFile('components/chat_notifications_toggler.js.jsx'));
jest.dontMock(pathToFile('mixins/dropdown_toggler.js.jsx'));
jest.dontMock(pathToFile('mixins/local_storage.js'));

describe('ChatNotificationsToggler', function() {
  var chatRooms;
  var ChatNotificationsStore;

  beforeEach(function() {
    global.localStorage = {};
    global.moment = require.requireActual('moment');

    ChatNotificationsStore = require(pathToFile('stores/chat_notifications_store.js'));

    chatRooms = {
      foo: {
        updated: moment().unix(),
        last_read_at: 123
      },
      bar: {
        updated: moment().unix(),
        last_read_at: 123
      },
      baz: {
        updated: moment().unix() - 100,
        last_read_at: moment().unix()
      }
    };

    ChatNotificationsStore.mostRecentlyUpdatedChatRoom
      .mockReturnValue(chatRooms.foo);

    ChatNotificationsStore.getChatRooms.mockReturnValue(chatRooms);

    // Not sure if this kind of mock is an antipattern.
    // Having to do this to get tests to pass might suggest that
    // we need to move the getUnreadCount functions to the components
    // rather than keeping them in the store.
    ChatNotificationsStore.getUnreadCount.mockImplementation(function(acknowledgedAt) {
      var count = _.countBy(
        chatRooms,
        function(entry) {
          var updated = entry.updated > entry.last_read_at;

          if (acknowledgedAt) {
            return updated && entry.updated > acknowledgedAt;
          }

          return updated;
        }
      );

      return count.true || 0;
    });
  });

  it('instantiates a dropdown toggler with default state', function() {
    var Toggler = require(pathToFile('components/chat_notifications_toggler.js.jsx'));

    var toggler = TestUtils.renderIntoDocument(
      <Toggler iconClass="icon icon-bubbles" href="/chat" label="bubbles" />
    );

    expect(toggler.state.chatRooms).toEqual(null);
    expect(toggler.state.acknowledgedAt).toEqual(0);
    expect(toggler.props.title).toEqual('');
  });

  it('acknowledges a click', function() {
    jest.dontMock('moment');

    var Toggler = require(pathToFile('components/chat_notifications_toggler.js.jsx'));
    var toggler = TestUtils.renderIntoDocument(
      <Toggler iconClass="icon icon-bubbles" href="/chat" label="bubbles" />
    );

    expect(toggler.state.acknowledgedAt).toEqual(0)
    toggler.acknowledge()
    expect(toggler.state.acknowledgedAt).not.toEqual(0)
  });

  it('adds the bg-red class if it has notifications', function() {
    jest.dontMock(appFile('mixins/dropdown_toggler.js.jsx'));
    jest.dontMock(appFile('components/ui/jewel.js.jsx'));

    var Toggler = require(pathToFile('components/chat_notifications_toggler.js.jsx'));
    var toggler = TestUtils.renderIntoDocument(
      <Toggler iconClass="icon icon-bubbles" href="/chat" label="bubbles" />
    );

    expect(toggler.badgeCount()).toEqual(2);

    var badge = TestUtils.scryRenderedDOMComponentsWithClass(
      toggler,
      'bg-red'
    );

    expect(badge.length).toEqual(1);
  });

  it('clears the badge count based on acknowledgedAt', function() {
    var Toggler = require(pathToFile('components/chat_notifications_toggler.js.jsx'));
    var toggler = TestUtils.renderIntoDocument(
      <Toggler iconClass="icon icon-bubbles" href="/chat" label="bubbles" />
    );

    expect(toggler.badgeCount()).toEqual(2);

    toggler.acknowledge();

    expect(toggler.badgeCount()).toEqual(0);
  });
});
<|endoftext|>