<commit_before>// File:   StanbolService.js
// Author: Sebastian Germesin
// Author: Szaby Gruenwald
//
(function(){
Zart.prototype.StanbolService = function(options) {
    var defaults = {
        url: 'http://dev.iks-project.eu:8080/',
        defaultProxyUrl : "../utils/proxy/proxy.php"
    };
    this.options = jQuery.extend(defaults, options);

    this.zart = null;
    this.name = 'stanbol';
    this.connector = new StanbolConnector(this.options);
    Zart.prototype.StanbolService.prototype.init(this);
    jQuery.ajaxSetup({
        converters: {"text application/rdf+json": function(s){return JSON.parse(s);}}
    });

};

Zart.prototype.StanbolService.prototype = {
    init: function(){
        this.namespaces = {
            semdeski : "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#",
            owl : "http://www.w3.org/2002/07/owl#",
            geonames : "http://www.geonames.org/ontology#",
            enhancer : "http://fise.iks-project.eu/ontology/",
            entityhub: "http://www.iks-project.eu/ontology/rick/model/",
            rdfs: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
            dc  : 'http://purl.org/dc/terms/',
            foaf: 'http://xmlns.com/foaf/0.1/',
            schema: 'http://schema.org/'
        };
        this.rules = [
            //rule to add backwards-relations to the triples
            //this makes querying for entities a lot easier!
            {'left' : [
                '?subject a <http://fise.iks-project.eu/ontology/EntityAnnotation>',
                '?subject enhancer:entity-type ?type',
                '?subject enhancer:confidence ?confidence',
                '?subject enhancer:entity-reference ?entity',
                '?subject dc:relation ?relation',
                '?relation a <http://fise.iks-project.eu/ontology/TextAnnotation>',
                '?relation enhancer:selected-text ?selected-text',
                '?relation enhancer:selection-context ?selection-context',
                '?relation enhancer:start ?start',
                '?relation enhancer:end ?end'
            ],
             'right' : [
                 '?entity a ?type',
                 '?entity enhancer:hasTextAnnotation ?relation',
                 '?entity enhancer:hasEntityAnnotation ?subject'
             ]
             },
             //rule to transform a Stanbol person into a Zart person
             {
                'left' : [
                    '?subject a <http://dbpedia.org/ontology/Person>',
                 ],
                 'right': function(ns){
                     return function(){
                         return jQuery.rdf.triple(this.subject.toString() +
                         ' a <http://schema.org/Person>', {
                             namespaces: ns
                         });
                     };
                 }(this.namespaces)
             }
        ];
    },
    // Zart API analyze implementation
    analyze: function(analyzable) {
        var service = this;

        var correct = analyzable instanceof this.zart.Analyzable;
        if (!correct) {throw "Invalid Analyzable passed";}

        var element = analyzable.options.element ? analyzable.options.element : jQuery('body');

        var text = service._extractText(element);

        if (text.length > 0) {
            var service = this;
            //query enhancer with extracted text
            var success = function (results) {
                var entities = service._enhancer2Entities(service, results);
                analyzable.resolve(entities);
            };
            var error = function (e) {
                analyzable.reject(e);
            };

            this.connector.engines(text, {proxyUrl: this.options.proxyUrl}, success, error);

        } else {
            console.warn("No text found in element.");
            analyzable.resolve([]);
        }

    },
    // Zart API load implementation
    // Runs a Stanbol entityhub query
    load: function(loadable){
        var correct = analyzable instanceof this.zart.Loadable;
        if (!correct) {throw "Invalid Loadable passed";}
        var service = this;
    },
    _extractText: function (element) {
        if (element.get(0) &&
            element.get(0).tagName &&
            (element.get(0).tagName == 'TEXTAREA' ||
            element.get(0).tagName == 'INPUT' && element.attr('type', 'text'))) {
            return element.get(0).val();
        }
        else {
            return element
                .text()    //get the text of element
                .replace(/\s+/g, ' ') //collapse multiple whitespaces
                .replace(/\0\b\n\r\f\t/g, '').trim(); // remove non-letter symbols
        }
    },

    _enhancer2Entities: function (service, results) {
        //transform data from Stanbol into Zart.Entities

        if (typeof jQuery.rdf !== 'function') {
            throw "RdfQuery is not loaded";
        }
        var rdf = jQuery.rdf().load(results, {});

        //execute rules here!
        if (service.rules) {
            var rules = jQuery.rdf.ruleset();
            for (var prefix in service.namespaces) {
                rules.prefix(prefix, service.namespaces[prefix]);
            }
            for (var i = 0; i < service.rules.length; i++) {
                rules.add(service.rules[i]['left'], service.rules[i]['right']);
            }
            rdf = rdf.reason(rules, 10); // execute the rules only 10 times to avoid looping
        }
        var entities = {};
        rdf.where('?subject ?property ?object').each(function() {
            var subject = this.subject.toString();
            if (!entities[subject]) {
                entities[subject] = {
                    '@subject': subject,
                    '@context': service.namespaces,
                    '@type': []
                };
            }
            var propertyUri = this.property.toString();

            var propertyCurie = jQuery.createCurie(propertyUri.substring(1, propertyUri.length - 1), {namespaces: service.namespaces});
            entities[subject][propertyCurie] = entities[subject][propertyCurie] || [];

            function getValue(rdfQueryLiteral){
                if(typeof rdfQueryLiteral.value === "string"){
                    return rdfQueryLiteral.value;
                } else if (rdfQueryLiteral.value._string){
                    return rdfQueryLiteral.toString();
                } else {
                    return rdfQueryLiteral.value
                }
            }
            entities[subject][propertyCurie].push(getValue(this.object));
        });

        _(entities).each(function(ent){
            ent["@type"] = ent["@type"].concat(ent["rdfs:type"]);
            delete ent["rdfs:type"];
            _(ent).each(function(value, property){
                if(value.length === 1){
                    ent[property] = value[0];
                }
            })
        })

        var zartEntities = [];
        jQuery.each(entities, function() {
            var entityInstance = new service.zart.Entity(this);
            entityInstance = service.zart.entities.addOrUpdate(entityInstance);
            zartEntities.push(entityInstance);
        });
        return zartEntities;
    }
};

var StanbolConnector = function(options){
    this.options = options;
    this.baseUrl = options.url.replace(/\/$/, '');
    this.enhancerUrlPrefix = "/engines";
    this.entityhubUrlPrefix = "/entityhub";
};
StanbolConnector.prototype = {
    engines: function(text, options, success, error) {
        var enhancerUrl = this.baseUrl + this.enhancerUrlPrefix;
        var proxyUrl = this._proxyUrl();
        jQuery.ajax({
            success: success,
            error: error,
            type: "POST",
            url: proxyUrl || enhancerUrl,
            data: (proxyUrl) ? {
                    proxy_url: enhancerUrl,
                    content: text,
                    verb: "POST",
                    format: options.format || "application/rdf+json"
                } : text,
            dataType: "application/rdf+json",
            contentType: proxyUrl ? undefined : "text/plain",
            accepts: {"application/rdf+json": "application/rdf+json"}

        });
    },
    queryEntityHub: function (uri, success, error) {
        var proxy = this._proxyUrl();
        var entityhub_url = this.baseUrl + this.entityhubUrlPrefix;
        if (proxy) {
            jQuery.ajax({
                async: true,
                type: "POST",
                success: success,
                error: error,
                url: proxy,
                dataType: "application/rdf+json",
                data: {
                    proxy_url: entityhub_url + "/sites/entity?id=" + escape(uri),
                    content: '',
                    verb: "GET",
                    format: "application/rdf+json"
                }
            });
        } else {
            jQuery.ajax({
                async: true,
                success: success,
                error: error,
                type: "GET",
                url: entityhub_url + "/sites/entity?id=" + escape(uri),
                data: '',
                dataType: "application/rdf+json",
                contentType: "text/plain",
                accepts: {"application/rdf+json": "application/rdf+json"}
            });
        }
    },
    findEntity: function (term, success, limit, offset, error) {
        // curl -X POST -d "name=Bishofsh&limit=10&offset=0" http://localhost:8080/entityhub/sites/find
        var proxy = this._proxyUrl();
        if (offset == null) {
            offset = 0;
        }
        if (limit == null) {
            limit = 10;
        }
        var entityhub_url = this.baseUrl + this.entityhubUrlPrefix;
        if (proxy) {
            // TODO test with proxy
            jQuery.ajax({
                async: true,
                type: "POST",
                success: callback,
                error: error,
                url: proxy,
                dataType: "application/rdf+json",
                data: {
                    proxy_url: entityhub_url + "/sites/find",
                    content: "name=" + term + "&limit=" + offset + "&limit=" + offset,
                    verb: "POST",
                    format: "application/rdf+json"
                }
            });
        } else {
            jQuery.ajax({
                async: true,
                success: function(response){
                    callback(response)
                },
                error: error,
                type: "POST",
                url: entityhub_url + "/sites/find",
                data: "name=" + term + "&limit=" + offset + "&limit=" + offset,
                dataType: "application/rdf+json",
                contentType: "text/plain",
                accepts: {"application/rdf+json": "application/rdf+json"}
            });
        }
    },
    _proxyUrl: function(){
        this.proxyUrl = "";
        if(this.baseUrl.indexOf(":") !== -1){
            return this.options.proxyUrl || this.options.defaultProxyUrl;
        } else {
            return '';
        }
    }
}
})();

<commit_msg>Implementing find, load in StanbolService.<commit_after>// File:   StanbolService.js
// Author: Sebastian Germesin
// Author: Szaby Gruenwald
//
(function(){
Zart.prototype.StanbolService = function(options) {
    var defaults = {
        url: 'http://dev.iks-project.eu:8080/',
        defaultProxyUrl : "../utils/proxy/proxy.php"
    };
    this.options = jQuery.extend(defaults, options);

    this.zart = null;
    this.name = 'stanbol';
    this.connector = new StanbolConnector(this.options);
    Zart.prototype.StanbolService.prototype.init(this);
    jQuery.ajaxSetup({
        converters: {"text application/rdf+json": function(s){return JSON.parse(s);}}
    });

};

Zart.prototype.StanbolService.prototype = {
    init: function(){
        this.namespaces = {
            semdeski : "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#",
            owl : "http://www.w3.org/2002/07/owl#",
            geonames : "http://www.geonames.org/ontology#",
            enhancer : "http://fise.iks-project.eu/ontology/",
            entityhub: "http://www.iks-project.eu/ontology/rick/model/",
            rdfs: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
            dc  : 'http://purl.org/dc/terms/',
            foaf: 'http://xmlns.com/foaf/0.1/',
            schema: 'http://schema.org/'
        };
        this.rules = [
            //rule to add backwards-relations to the triples
            //this makes querying for entities a lot easier!
            {'left' : [
                '?subject a <http://fise.iks-project.eu/ontology/EntityAnnotation>',
                '?subject enhancer:entity-type ?type',
                '?subject enhancer:confidence ?confidence',
                '?subject enhancer:entity-reference ?entity',
                '?subject dc:relation ?relation',
                '?relation a <http://fise.iks-project.eu/ontology/TextAnnotation>',
                '?relation enhancer:selected-text ?selected-text',
                '?relation enhancer:selection-context ?selection-context',
                '?relation enhancer:start ?start',
                '?relation enhancer:end ?end'
            ],
             'right' : [
                 '?entity a ?type',
                 '?entity enhancer:hasTextAnnotation ?relation',
                 '?entity enhancer:hasEntityAnnotation ?subject'
             ]
             },
             //rule to transform a Stanbol person into a Zart person
             {
                'left' : [
                    '?subject a <http://dbpedia.org/ontology/Person>',
                 ],
                 'right': function(ns){
                     return function(){
                         return jQuery.rdf.triple(this.subject.toString() +
                         ' a <http://schema.org/Person>', {
                             namespaces: ns
                         });
                     };
                 }(this.namespaces)
             }
        ];
    },
    // Zart API analyze implementation
    analyze: function(analyzable) {
        var service = this;

        var correct = analyzable instanceof this.zart.Analyzable;
        if (!correct) {throw "Invalid Analyzable passed";}

        var element = analyzable.options.element ? analyzable.options.element : jQuery('body');

        var text = service._extractText(element);

        if (text.length > 0) {
            var service = this;
            //query enhancer with extracted text
            var success = function (results) {
                var entities = service._enhancer2Entities(service, results);
                analyzable.resolve(entities);
            };
            var error = function (e) {
                analyzable.reject(e);
            };

            this.connector.engines(text, {proxyUrl: this.options.proxyUrl}, success, error);

        } else {
            console.warn("No text found in element.");
            analyzable.resolve([]);
        }

    },
    // Zart API load implementation
    // Runs a Stanbol entityhub find
    find: function(findable){
        var correct = findable instanceof this.zart.Findable;
        if (!correct) {throw "Invalid Findable passed";}
        var service = this;
        // The term to find, * as wildcard allowed
        var term = findable.options.term;
        if(!term){
            console.warn("StanbolConnector: No term to look for!");
            findable.resolve([]);
        };
        var limit = (typeof findable.options.limit === "undefined") ? 20 : findable.options.limit;
        var offset = (typeof findable.options.offset === "undefined") ? 0 : findable.options.offset;
        var success = function (results) {
            findable.resolve(results.results);
        };
        var error = function (e) {
            findable.reject(e);
        };
        this.connector.find(term, success, limit, offset, error)
    },
    _extractText: function (element) {
        if (element.get(0) &&
            element.get(0).tagName &&
            (element.get(0).tagName == 'TEXTAREA' ||
            element.get(0).tagName == 'INPUT' && element.attr('type', 'text'))) {
            return element.get(0).val();
        }
        else {
            return element
                .text()    //get the text of element
                .replace(/\s+/g, ' ') //collapse multiple whitespaces
                .replace(/\0\b\n\r\f\t/g, '').trim(); // remove non-letter symbols
        }
    },

    _enhancer2Entities: function (service, results) {
        //transform data from Stanbol into Zart.Entities

        if (typeof jQuery.rdf !== 'function') {
            throw "RdfQuery is not loaded";
        }
        var rdf = jQuery.rdf().load(results, {});

        //execute rules here!
        if (service.rules) {
            var rules = jQuery.rdf.ruleset();
            for (var prefix in service.namespaces) {
                rules.prefix(prefix, service.namespaces[prefix]);
            }
            for (var i = 0; i < service.rules.length; i++) {
                rules.add(service.rules[i]['left'], service.rules[i]['right']);
            }
            rdf = rdf.reason(rules, 10); // execute the rules only 10 times to avoid looping
        }
        var entities = {};
        rdf.where('?subject ?property ?object').each(function() {
            var subject = this.subject.toString();
            if (!entities[subject]) {
                entities[subject] = {
                    '@subject': subject,
                    '@context': service.namespaces,
                    '@type': []
                };
            }
            var propertyUri = this.property.toString();

            var propertyCurie = jQuery.createCurie(propertyUri.substring(1, propertyUri.length - 1), {namespaces: service.namespaces});
            entities[subject][propertyCurie] = entities[subject][propertyCurie] || [];

            function getValue(rdfQueryLiteral){
                if(typeof rdfQueryLiteral.value === "string"){
                    return rdfQueryLiteral.value;
                } else if (rdfQueryLiteral.value._string){
                    return rdfQueryLiteral.toString();
                } else {
                    return rdfQueryLiteral.value
                }
            }
            entities[subject][propertyCurie].push(getValue(this.object));
        });

        _(entities).each(function(ent){
            ent["@type"] = ent["@type"].concat(ent["rdfs:type"]);
            delete ent["rdfs:type"];
            _(ent).each(function(value, property){
                if(value.length === 1){
                    ent[property] = value[0];
                }
            })
        })

        var zartEntities = [];
        jQuery.each(entities, function() {
            var entityInstance = new service.zart.Entity(this);
            entityInstance = service.zart.entities.addOrUpdate(entityInstance);
            zartEntities.push(entityInstance);
        });
        return zartEntities;
    }
};

var StanbolConnector = function(options){
    this.options = options;
    this.baseUrl = options.url.replace(/\/$/, '');
    this.enhancerUrlPrefix = "/engines";
    this.entityhubUrlPrefix = "/entityhub";
};
StanbolConnector.prototype = {
    engines: function(text, options, success, error) {
        var enhancerUrl = this.baseUrl + this.enhancerUrlPrefix;
        var proxyUrl = this._proxyUrl();
        jQuery.ajax({
            success: function(response){
                success(response)
            },
            error: error,
            type: "POST",
            url: proxyUrl || enhancerUrl,
            data: (proxyUrl) ? {
                    proxy_url: enhancerUrl,
                    content: text,
                    verb: "POST",
                    format: options.format || "application/rdf+json"
                } : text,
            dataType: "application/rdf+json",
            contentType: proxyUrl ? undefined : "text/plain",
            accepts: {"application/rdf+json": "application/rdf+json"}

        });
    },
    queryEntityHub: function (uri, success, error, options) {
        var proxy = this._proxyUrl();
        var entityhub_url = this.baseUrl + this.entityhubUrlPrefix;
        if (proxy) {
            jQuery.ajax({
                async: true,
                type: "POST",
                success: function(response){
                    success(response)
                },
                error: error,
                url: proxy,
                dataType: "application/rdf+json",
                data: {
                    proxy_url: entityhub_url + "/sites/entity?id=" + escape(uri),
                    content: '',
                    verb: "GET",
                    format: "application/rdf+json"
                }
            });
        } else {
            jQuery.ajax({
                async: true,
                success: function(response){
                    success(response)
                },
                error: error,
                type: "GET",
                url: entityhub_url + "/sites/entity?id=" + escape(uri),
                data: '',
                dataType: "application/rdf+json",
                contentType: "text/plain",
                accepts: {"application/rdf+json": "application/rdf+json"}
            });
        }
    },
    find: function (term, success, limit, offset, error) {
        // curl -X POST -d "name=Bishofsh&limit=10&offset=0" http://localhost:8080/entityhub/sites/find
        var proxy = this._proxyUrl();
        if (offset == null) {
            offset = 0;
        }
        if (limit == null) {
            limit = 10;
        }
        var entityhub_url = this.baseUrl + this.entityhubUrlPrefix;
        if (proxy) {
            // TODO test with proxy
            jQuery.ajax({
                async: true,
                type: "POST",
                success: function(response){
                    success(response)
                },
                error: error,
                url: proxy,
                dataType: "application/rdf+json",
                data: {
                    proxy_url: entityhub_url + "/sites/find",
                    content: "name=" + term + "&limit=" + offset + "&limit=" + offset,
                    verb: "POST",
                    format: "application/rdf+json"
                }
            });
        } else {
            jQuery.ajax({
                async: true,
                success: function(response){
                    success(response)
                },
                error: error,
                type: "POST",
                url: entityhub_url + "/sites/find",
                data: "name=" + term + "&limit=" + offset + "&limit=" + offset,
                contentType: "text/plain",
                accepts: {"application/rdf+json": "application/rdf+json"}
            });
        }
    },
    _proxyUrl: function(){
        this.proxyUrl = "";
        if(this.baseUrl.indexOf(":") !== -1){
            return this.options.proxyUrl || this.options.defaultProxyUrl;
        } else {
            return '';
        }
    }
}
})();

<|endoftext|>


<commit_before>/* eslint-disable no-undef   */

const cukeFrmSubmit = '//form[@data-cuke="login"]';

const urlLogout = 'http://localhost:3000/logout';
const cukeLogin = '//x-cuke[@id="login"]';
const cukeInpEmail = '//input[@data-cuke="email"]';
const cukeInpPwd = '//input[@data-cuke="password"]';

const cukeAccountPage = '//x-cuke[@id="account-page"]';
const cukeAcctEmail = '//x-cuke[@id="acct-email"]';

const cukeInpName = '//input[@data-cuke="firstName"]';
const cukeInpFamilyName = '//input[@data-cuke="lastName"]';
const cukeInpPwd1 = '//input[@data-cuke="pword1"]';
const cukeInpPwd2 = '//input[@data-cuke="pword2"]';

const cukeButtonSave = '//button[@data-cuke="user-save"]';
const cukeUserRecord = '//x-cuke[@id="user-record"]';

const xCukeFirstName = '//x-cuke[@id="firstName"]';
const xCukeLastName = '//x-cuke[@id="lastName"]';
const xCukeEmail = '//x-cuke[@id="email"]';
const xCukeRole = '//x-cuke[@id="role"]';

const cukeUserListPage = '//x-cuke[@id="user-list"]';

let myEmail = '';
module.exports = function () {

//  Scenario: Log in as administrator
// ------------------------------------------------------------------------

  this.Given(/^I have opened the login page : "([^"]*)"$/, function (urlLogin) {

    browser.setViewportSize({ width: 1024, height: 480 });
    browser.url(urlLogout);
    browser.waitForVisible(cukeLogin);
    browser.url(urlLogin);

    server.call('_users.removeByEmail', 'jj@gmail.com');

  });

  this.When(/^I provide my email "([^"]*)" and password "([^"]*)"$/, function (_email, _pwd) {
    myEmail = _email;
    browser.setValue(cukeInpEmail, myEmail);
    browser.setValue(cukeInpPwd, _pwd);
  });

  this.When(/^I submit the form$/, function () {
    browser.submitForm(cukeFrmSubmit);
    browser.waitForExist(cukeAccountPage);
  });

  this.Then(/^I see my user drop\-down menu\.$/, function () {
    let idAcct = browser.getText(cukeAcctEmail);
    expect(idAcct).toEqual(myEmail);
  });
// =======================================================================

//   Scenario: Create a new user
// ------------------------------------------------------------------------

  this.Given(/^I have opened the create user page : "([^"]*)"$/, function (urlCreateUser) {
    browser.url(urlCreateUser);
  });

  this.Given(/^seen the title "([^"]*)"$/, function (title) {
    const cukeUserAddPage = '//h3[@data-cuke="user-form-title" and contains(text(), "' +
                                                                         title + '")]';
    browser.waitForExist(cukeUserAddPage);
  });

  let firstName = '';
  this.When(/^I provide the user's name "([^"]*)",$/, function (_firstname) {
    firstName = _firstname;
    browser.setValue(cukeInpName, firstName);
  });

  let lastName = '';
  this.When(/^family name "([^"]*)",$/, function (_lastname) {
    lastName = _lastname;
    browser.setValue(cukeInpFamilyName, lastName);
  });

  let eMail = '';
  this.When(/^her email "([^"]*)",$/, function (_email) {
    eMail = _email;
    browser.setValue(cukeInpEmail, eMail);
  });

  this.When(/^her password "([^"]*)",$/, function (_passwd) {
    browser.setValue(cukeInpPwd1, _passwd);
    browser.setValue(cukeInpPwd2, _passwd);
  });

  let role = '';
  this.When(/^her role "([^"]*)",$/, function (_role) {
    role = _role;

    let cukeRadioRole = '//input[@data-cuke="role" and @value="' + role + '"]';
    browser.click(cukeRadioRole);
  });

  this.When(/^I submit the create user form\.$/, function () {
    browser.click(cukeButtonSave);
    browser.waitForExist(cukeUserRecord);
    // browser.saveScreenshot('screenshot_example');
  });

  this.Then(/^her record shows the same data\.$/, function () {

    expect(browser.element(xCukeFirstName).getText()).toEqual(firstName);
    expect(browser.element(xCukeLastName).getText()).toEqual(lastName);
    expect(browser.element(xCukeEmail).getText()).toEqual(eMail);
    expect(browser.element(xCukeRole).getText()).toEqual(role);

  });
// =======================================================================

//   Scenario: Edit an existing user
// ------------------------------------------------------------------------
  this.Given(/^I have opened the list of users : "([^"]*)"$/, function (urlListUsers) {
    browser.url(urlListUsers);
    browser.waitForExist(cukeUserListPage);
  });

  let user = null;
  this.Given(/^I find and click the Edit button for user "([^"]*)",$/, function (emailUser) {
    user = emailUser;
    let cukeButtonUserEdit = '//a[@data-cuke="edit-' + user + '"]';
    browser.click(cukeButtonUserEdit);
  });

  this.Given(/^I see the user "([^"]*)" form,$/, function (title) {
    const cukeUserEditPage =
       '//h3[@data-cuke="user-form-title" and contains(text(), "' + title + '")]';
    browser.waitForExist(cukeUserEditPage);
  });

  this.When(/^I submit the edit user form\.$/, function () {
    browser.click(cukeButtonSave);
    browser.waitForExist(cukeUserListPage);
  });

  this.Then(/^the record shows the same data\.$/, function () {
    let cukeButtonUserView = '//a[@data-cuke="view-' + user + '"]';
    browser.click(cukeButtonUserView);
    expect(browser.element(xCukeFirstName).getText()).toEqual(firstName);
    expect(browser.element(xCukeLastName).getText()).toEqual(lastName);
  });

// =======================================================================

//   Scenario: Hide an existing user
// ------------------------------------------------------------------------

  let selector = '';
  let email = '';
  this.Given(/^I find and click the Remove button for user "([^"]*)",$/, function (_email) {
    email = _email;
    selector = '//a[@data-cuke="hide-' + _email + '"]';
    browser.click(selector);
  });

  this.Then(/^I no longer see that user record\.$/, function () {
    browser.waitUntil(function () {
      console.log('still there?');
      return !browser.isExisting(selector);
    }, 10000, ' what the?');
    expect(browser.isExisting(selector) ? email : 'Gone').toBe('Gone');

  });

// =======================================================================

};
<commit_msg>bug(ci): Slow down the loop check in waitUntil<commit_after>/* eslint-disable no-undef   */

const cukeFrmSubmit = '//form[@data-cuke="login"]';

const urlLogout = 'http://localhost:3000/logout';
const cukeLogin = '//x-cuke[@id="login"]';
const cukeInpEmail = '//input[@data-cuke="email"]';
const cukeInpPwd = '//input[@data-cuke="password"]';

const cukeAccountPage = '//x-cuke[@id="account-page"]';
const cukeAcctEmail = '//x-cuke[@id="acct-email"]';

const cukeInpName = '//input[@data-cuke="firstName"]';
const cukeInpFamilyName = '//input[@data-cuke="lastName"]';
const cukeInpPwd1 = '//input[@data-cuke="pword1"]';
const cukeInpPwd2 = '//input[@data-cuke="pword2"]';

const cukeButtonSave = '//button[@data-cuke="user-save"]';
const cukeUserRecord = '//x-cuke[@id="user-record"]';

const xCukeFirstName = '//x-cuke[@id="firstName"]';
const xCukeLastName = '//x-cuke[@id="lastName"]';
const xCukeEmail = '//x-cuke[@id="email"]';
const xCukeRole = '//x-cuke[@id="role"]';

const cukeUserListPage = '//x-cuke[@id="user-list"]';

let myEmail = '';
module.exports = function () {

//  Scenario: Log in as administrator
// ------------------------------------------------------------------------

  this.Given(/^I have opened the login page : "([^"]*)"$/, function (urlLogin) {

    browser.setViewportSize({ width: 1024, height: 480 });
    browser.url(urlLogout);
    browser.waitForVisible(cukeLogin);
    browser.url(urlLogin);

    server.call('_users.removeByEmail', 'jj@gmail.com');

  });

  this.When(/^I provide my email "([^"]*)" and password "([^"]*)"$/, function (_email, _pwd) {
    myEmail = _email;
    browser.setValue(cukeInpEmail, myEmail);
    browser.setValue(cukeInpPwd, _pwd);
  });

  this.When(/^I submit the form$/, function () {
    browser.submitForm(cukeFrmSubmit);
    browser.waitForExist(cukeAccountPage);
  });

  this.Then(/^I see my user drop\-down menu\.$/, function () {
    let idAcct = browser.getText(cukeAcctEmail);
    expect(idAcct).toEqual(myEmail);
  });
// =======================================================================

//   Scenario: Create a new user
// ------------------------------------------------------------------------

  this.Given(/^I have opened the create user page : "([^"]*)"$/, function (urlCreateUser) {
    browser.url(urlCreateUser);
  });

  this.Given(/^seen the title "([^"]*)"$/, function (title) {
    const cukeUserAddPage = '//h3[@data-cuke="user-form-title" and contains(text(), "' +
                                                                         title + '")]';
    browser.waitForExist(cukeUserAddPage);
  });

  let firstName = '';
  this.When(/^I provide the user's name "([^"]*)",$/, function (_firstname) {
    firstName = _firstname;
    browser.setValue(cukeInpName, firstName);
  });

  let lastName = '';
  this.When(/^family name "([^"]*)",$/, function (_lastname) {
    lastName = _lastname;
    browser.setValue(cukeInpFamilyName, lastName);
  });

  let eMail = '';
  this.When(/^her email "([^"]*)",$/, function (_email) {
    eMail = _email;
    browser.setValue(cukeInpEmail, eMail);
  });

  this.When(/^her password "([^"]*)",$/, function (_passwd) {
    browser.setValue(cukeInpPwd1, _passwd);
    browser.setValue(cukeInpPwd2, _passwd);
  });

  let role = '';
  this.When(/^her role "([^"]*)",$/, function (_role) {
    role = _role;

    let cukeRadioRole = '//input[@data-cuke="role" and @value="' + role + '"]';
    browser.click(cukeRadioRole);
  });

  this.When(/^I submit the create user form\.$/, function () {
    browser.click(cukeButtonSave);
    browser.waitForExist(cukeUserRecord);
    // browser.saveScreenshot('screenshot_example');
  });

  this.Then(/^her record shows the same data\.$/, function () {

    expect(browser.element(xCukeFirstName).getText()).toEqual(firstName);
    expect(browser.element(xCukeLastName).getText()).toEqual(lastName);
    expect(browser.element(xCukeEmail).getText()).toEqual(eMail);
    expect(browser.element(xCukeRole).getText()).toEqual(role);

  });
// =======================================================================

//   Scenario: Edit an existing user
// ------------------------------------------------------------------------
  this.Given(/^I have opened the list of users : "([^"]*)"$/, function (urlListUsers) {
    browser.url(urlListUsers);
    browser.waitForExist(cukeUserListPage);
  });

  let user = null;
  this.Given(/^I find and click the Edit button for user "([^"]*)",$/, function (emailUser) {
    user = emailUser;
    let cukeButtonUserEdit = '//a[@data-cuke="edit-' + user + '"]';
    browser.click(cukeButtonUserEdit);
  });

  this.Given(/^I see the user "([^"]*)" form,$/, function (title) {
    const cukeUserEditPage =
       '//h3[@data-cuke="user-form-title" and contains(text(), "' + title + '")]';
    browser.waitForExist(cukeUserEditPage);
  });

  this.When(/^I submit the edit user form\.$/, function () {
    browser.click(cukeButtonSave);
    browser.waitForExist(cukeUserListPage);
  });

  this.Then(/^the record shows the same data\.$/, function () {
    let cukeButtonUserView = '//a[@data-cuke="view-' + user + '"]';
    browser.click(cukeButtonUserView);
    expect(browser.element(xCukeFirstName).getText()).toEqual(firstName);
    expect(browser.element(xCukeLastName).getText()).toEqual(lastName);
  });

// =======================================================================

//   Scenario: Hide an existing user
// ------------------------------------------------------------------------

  let selector = '';
  let email = '';
  this.Given(/^I find and click the Remove button for user "([^"]*)",$/, function (_email) {
    email = _email;
    selector = '//a[@data-cuke="hide-' + _email + '"]';
    browser.click(selector);
  });

  this.Then(/^I no longer see that user record\.$/, function () {
    browser.waitUntil(function () {
      console.log('still there?');
      return !browser.isExisting(selector);
    }, 10000, ' what the?', 2000);
    expect(browser.isExisting(selector) ? email : 'Gone').toBe('Gone');

  });

// =======================================================================

};
<|endoftext|>


<commit_before>const {resolve} = require('path');
const {execFile} = require('child_process');

const tape = require('tape-catch');
const curry = require('1-liners/curry');
const plus = require('1-liners/plus');
const spawn = require('tape-spawn');

const title = curry(plus)('The CLI tool:  ');
const keywordPopularity = resolve(__dirname, '../../module/bin/keyword-popularity.js');
const $keywordPopularity = curry(execFile)(keywordPopularity);

tape(title('Prints usage'), (is) => {
  is.plan(8);

  $keywordPopularity([], (error, _, stderr) => {
    is.equal(error && error.code, 1,
      '`keyword-popularity` fails…'
    );

    is.ok(
      /^usage:/i.test(stderr),
      '…and prints usage to stderr'
    );
  });

  $keywordPopularity(['--invalid', '--options'], (error, _, stderr) => {
    is.equal(error && error.code, 1,
      '`keyword-popularity --invalid --options` fails…'
    );

    is.ok(
      /^usage:/i.test(stderr),
      '…and prints usage to stderr'
    );
  });

  $keywordPopularity(['-h'], (error, stdout) => {
    is.equal(error, null,
      '`keyword-popularity -h` succeeds…'
    );

    is.ok(
      /^usage:/i.test(stdout),
      '…and prints usage'
    );
  });

  $keywordPopularity(['--help'], (error, stdout) => {
    is.equal(error, null,
      '`keyword-popularity --help` succeeds…'
    );

    is.ok(
      /SYNOPSIS/.test(stdout),
      '…and prints manpage-like help'
    );
  });
});

const cwd = resolve(__dirname, '../mock-cwd');

tape(title('Does what it says'), (is) => {
  const run = spawn(is, `"${keywordPopularity}"`, {cwd});

  run.succeeds(
    'succeeds'
  );

  run.timeout(500);
  run.end();
});
<commit_msg>Update bin tests<commit_after>const {resolve} = require('path');
const {execFile} = require('child_process');

const tape = require('tape-catch');
const curry = require('1-liners/curry');
const plus = require('1-liners/plus');
const spawn = require('tape-spawn');

const title = curry(plus)('The CLI tool:  ');
const keywordPopularity = resolve(__dirname,
  '../../module/bin/keyword-popularity.js'
);
const $keywordPopularity = curry(execFile)(keywordPopularity);

tape(title('Prints usage'), (is) => {
  is.plan(8);

  $keywordPopularity([], (error, _, stderr) => {
    is.equal(error && error.code, 1,
      '`keyword-popularity` fails…'
    );

    is.ok(
      /^usage:/i.test(stderr),
      '…and prints usage to stderr'
    );
  });

  $keywordPopularity(['--invalid', '--options'], (error, _, stderr) => {
    is.equal(error && error.code, 1,
      '`keyword-popularity --invalid --options` fails…'
    );

    is.ok(
      /^usage:/i.test(stderr),
      '…and prints usage to stderr'
    );
  });

  $keywordPopularity(['-h'], (error, stdout) => {
    is.equal(error, null,
      '`keyword-popularity -h` succeeds…'
    );

    is.ok(
      /^usage:/i.test(stdout),
      '…and prints usage'
    );
  });

  $keywordPopularity(['--help'], (error, stdout) => {
    is.equal(error, null,
      '`keyword-popularity --help` succeeds…'
    );

    is.ok(
      /SYNOPSIS/.test(stdout),
      '…and prints manpage-like help'
    );
  });
});

tape(title('Does what it says'), (is) => {
  const keywords = [
    'cli',
    'tool',
    `very-very-unlikely-keyword-${Date.now()}`,
  ];
  const tabLength = keywords[2].length + 2;
  const veryUnlikelyKeyword = keywords[2];
  const program = spawn(is, `'${keywordPopularity}' ${keywords.join(' ')}`);

  const wordWithTab = (word) => `${word} {${tabLength - word.length}}`;

  program.succeeds(
    'succeeds'
  );

  program.stdout.match(
    new RegExp(
      '^' +
      wordWithTab('KEYWORD') +
      'POPULARITY' +
      '\\n'
    ),
    'outputs a properly-formatted header…'
  );

  program.stdout.match(
    new RegExp(
      '^.*\\n' +
      keywords.map((keyword) => (
        wordWithTab(keyword) +
        '\\d+\\n'
      )) +
      '$'
    ),
    '…followed by a table of keywords and numbers in the specified order'
  );

  program.stdout.match(
    new RegExp(`${wordWithTab(veryUnlikelyKeyword)}0\\n`),
    'prints out `0` for a very unlikely keyword'
  );

  program.timeout(500);
  program.end();
});
<|endoftext|>


<commit_before>/* global app, chai, describe, it, beforeEach, afterEach, sinon */
var expect = chai.expect;

describe("ChatView", function() {
  "use strict";
  var sandbox;

  describe("#initialize", function() {
    var call, oldtitle;

    beforeEach(function() {
      sandbox = sinon.sandbox.create();
      sandbox.stub(app.models.Call.prototype, "_startTimer");
      sandbox.stub(window, "close");
      oldtitle = document.title;

      // Although we're not testing it in this set of tests, stub the WebRTCCall
      // model's initialize function, as creating new media items
      // (e.g. PeerConnection) takes a lot of time that we don't need to spend.
      sandbox.stub(app.models.WebRTCCall.prototype, "initialize");
      var media = new app.models.WebRTCCall();
      call = new app.models.Call({}, media);

      sandbox.stub(call, "on");
    });

    afterEach(function() {
      document.title = oldtitle;
      sandbox.restore();
    });

    it("should attach a given call model", function() {
      var view = new app.views.ChatView({call: call});

      expect(view.call).to.equal(call);
    });

    it("should throw an error when no call model is given", function() {
      function shouldExplode() {
        new app.views.ChatView({});
      }
      expect(shouldExplode).to.Throw(Error, /missing parameter: call/);
    });

    it("should attach to the app user model", function() {
      new app.views.ChatView({call: call});

      sinon.assert.called(call.on);
      sinon.assert.calledWith(call.on, "change:otherUser");
    });

    it("should update the document title on change of user login details",
      function() {
        call.set("otherUser", "nick");

        new app.views.ChatView({call: call});

        call.on.args[0][1](call);

        expect(document.title).to.be.equal("nick");
      });

    it("should close the window when the call `offer-timeout` event is " +
       "triggered", function() {
        new app.views.ChatView({call: call});

        call.trigger('offer-timeout');

        call.on.args[1][1]();

        sinon.assert.calledOnce(window.close);
      });
  });
});
<commit_msg>enhanced test comments for legibility<commit_after>/* global app, chai, describe, it, beforeEach, afterEach, sinon */
var expect = chai.expect;

describe("ChatView", function() {
  "use strict";
  var sandbox;

  describe("#initialize", function() {
    var call, oldtitle;

    beforeEach(function() {
      sandbox = sinon.sandbox.create();
      sandbox.stub(app.models.Call.prototype, "_startTimer");
      sandbox.stub(window, "close");
      oldtitle = document.title;

      // Although we're not testing it in this set of tests, stub the WebRTCCall
      // model's initialize function, as creating new media items
      // (e.g. PeerConnection) takes a lot of time that we don't need to spend.
      sandbox.stub(app.models.WebRTCCall.prototype, "initialize");
      var media = new app.models.WebRTCCall();
      call = new app.models.Call({}, media);

      sandbox.stub(call, "on");
    });

    afterEach(function() {
      document.title = oldtitle;
      sandbox.restore();
    });

    it("should attach a given call model", function() {
      var view = new app.views.ChatView({call: call});

      expect(view.call).to.equal(call);
    });

    it("should throw an error when no call model is given", function() {
      function shouldExplode() {
        new app.views.ChatView({});
      }
      expect(shouldExplode).to.Throw(Error, /missing parameter: call/);
    });

    it("should attach to the app user model", function() {
      new app.views.ChatView({call: call});

      sinon.assert.called(call.on);
      sinon.assert.calledWith(call.on, "change:otherUser");
    });

    it("should update the document title on change of user login details",
      function() {
        call.set("otherUser", "nick");

        new app.views.ChatView({call: call});

        call.on.args[0][1](call);

        expect(document.title).to.be.equal("nick");
      });

    it("should close the window when the call `offer-timeout` event is " +
       "triggered", function() {
        new app.views.ChatView({call: call});

        call.trigger('offer-timeout');

        // offer-timeout is the second event triggered
        call.on.args[1][1]();

        sinon.assert.calledOnce(window.close);
      });
  });
});
<|endoftext|>


<commit_before>/**
 * @ignore
 */
exports['should correctly insert decimal128 value'] = {
  // Add a tag that our runner can trigger on
  // in this case we are setting that node needs to be higher than 0.10.X to run
  metadata: {
    requires: {
      mongodb: '>=3.3.6',
      topology: ['single']
    }
  },

  // The actual test we wish to run
  test: function(configuration, test) {
    var Decimal128 = configuration.require.Decimal128;

    var client = configuration.newDbInstance(configuration.writeConcernMax(), { poolSize: 1 });
    client.connect(function(err, client) {
      var db = client.db(configuration.database);
      var object = {
        id: 1,
        value: Decimal128.fromString('1')
      };

      db.collection('decimal128').insertOne(object, function(err, r) {
        test.equal(null, err);

        db.collection('decimal128').findOne({
          id: 1
        }, function(err, doc) {
          test.equal(null, err);
          test.ok(doc.value instanceof Decimal128);
          test.equal('1', doc.value.toString());

          client.close();
          test.done();
        });
      });
    });
  }
};
<commit_msg>test(decimal128): convert `decimal128` tests to mocha style<commit_after>'use strict';
var test = require('./shared').assert;
var setupDatabase = require('./shared').setupDatabase;

describe('Decimal128', function() {
  before(function() {
    return setupDatabase(this.configuration);
  });

  /**
   * @ignore
   */
  it('should correctly insert decimal128 value', {
    // Add a tag that our runner can trigger on
    // in this case we are setting that node needs to be higher than 0.10.X to run
    metadata: {
      requires: {
        mongodb: '>=3.3.6',
        topology: ['single']
      }
    },

    // The actual test we wish to run
    test: function(done) {
      var configuration = this.configuration;
      var Decimal128 = configuration.require.Decimal128;

      var client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1 });
      client.connect(function(err, client) {
        var db = client.db(configuration.db);
        var object = {
          id: 1,
          value: Decimal128.fromString('1')
        };

        db.collection('decimal128').insertOne(object, function(err) {
          test.equal(null, err);

          db.collection('decimal128').findOne({
            id: 1
          }, function(err, doc) {
            test.equal(null, err);
            test.ok(doc.value instanceof Decimal128);
            test.equal('1', doc.value.toString());

            client.close();
            done();
          });
        });
      });
    }
  });
});
<|endoftext|>


<commit_before>var min = Math.min;
var max = Math.max;
var abs = Math.abs;
var sqrt = Math.sqrt;

// this value helps resolve colliding state between entities
var COLLISION_OFFSET = 0.001;

// shape-to-function maps
var SHAPE_COLLIDE_MAP = {
  Rect: {
    Rect: "rectCollidesWithRect",
    Circle: "rectCollidesWithCircle"
  },
  Circle: {
    Rect: "circleCollidesWithRect",
    Circle: "circleCollidesWithCircle"
  }
};

var SHAPE_RESOLVE_MAP = {
  Rect: {
    Rect: "resolveCollidingRects",
    Circle: "resolveCollidingRectCircle"
  },
  Circle: {
    Rect: "resolveCollidingCircleRect",
    Circle: "resolveCollidingCircles"
  }
};

var SHAPE_INSIDE_MAP = {
  Rect: {
    Rect: "rectInsideRect",
    Circle: "rectInsideCircle"
  },
  Circle: {
    Rect: "circleInsideRect",
    Circle: "circleInsideCircle"
  }
};

var _genericResolution = function(genericName, lookupMap, defaultResult) {
  return function (model1, model2) {
    var fn = null;
    var result = defaultResult;
    var shape1 = model1.shape || model1;
    var shape2 = model2.shape || model2;
    var map = lookupMap[shape1.name];
    if (map) {
      fn = map[shape2.name];
    }

    if (fn) {
      result = this[fn](shape1, shape2);
    } else {
      logger.warn(genericName + " function not found for:", shape1, shape2);
    }
    return result;
  };
};

/**
 * IMPORTANT NOTES:
 * ~ by default, collisions support circles and axis-aligned rectangles only
 * ~ these are NOT continuous collision detection algorithms, meaning a large
 *   value of dt could cause entities to pass through each other - it's up to the
 *   developer to manage the time step of his/her game to prevent this behavior
 */
exports = {
  /**
   * ~ collide defines how collisions are detected
   * ~ by default, only works with circles and axis-aligned rectangles
   */
  collide: _genericResolution('Collide', SHAPE_COLLIDE_MAP, false),

  /**
   * ~ resolveCollision guarantees that two models are not colliding
   *   by pushing them apart
   * ~ shapes with fixed = true are never moved
   * ~ returns total distance moved to separate the objects
   */
  resolveCollision: _genericResolution('Resolve', SHAPE_RESOLVE_MAP, 0),

  /**
   * ~ REQUIRED
   * ~ isInside is used to determine if one entity is fully contained by another
   * ~ by default, returns a bool, and only works with circles and rects
   */
  isInside: _genericResolution('Inside', SHAPE_INSIDE_MAP, false),

  circleCollidesWithCircle: function (circ1, circ2) {
    var x1 = circ1.x;
    var y1 = circ1.y;
    var r1 = circ1.radius;
    var x2 = circ2.x;
    var y2 = circ2.y;
    var r2 = circ2.radius;
    var dx = x2 - x1;
    var dy = y2 - y1;
    var distSqrd = dx * dx + dy * dy;
    var distColl = r1 + r2;
    var distCollSqrd = distColl * distColl;
    return distSqrd <= distCollSqrd;
  },

  circleCollidesWithRect: function (circ, rect) {
    var cx = circ.x;
    var cy = circ.y;
    var cr = circ.radius;
    var rwHalf = rect.width / 2;
    var rhHalf = rect.height / 2;
    var rx = rect.x + rwHalf;
    var ry = rect.y + rhHalf;
    var dx = abs(cx - rx);
    var dy = abs(cy - ry);
    if (dx > rwHalf + cr || dy > rhHalf + cr) {
      // far case: circle's center is too far from rect's center
      return false;
    } else if (dx <= rwHalf || dy <= rhHalf) {
      // close case: circle's center is close enough to rect's center
      return true;
    } else {
      // corner case: rect corner within a radius of the circle's center
      var dcx = dx - rwHalf;
      var dcy = dy - rhHalf;
      var cornerDistSqrd = dcx * dcx + dcy * dcy;
      return cornerDistSqrd <= cr * cr;
    }
  },

  rectCollidesWithCircle: function (rect, circ) {
    return this.circleCollidesWithRect(circ, rect);
  },

  rectCollidesWithRect: function (rect1, rect2) {
    var x1 = rect1.x;
    var y1 = rect1.y;
    var xf1 = x1 + rect1.width;
    var yf1 = y1 + rect1.height;
    var x2 = rect2.x;
    var y2 = rect2.y;
    var xf2 = x2 + rect2.width;
    var yf2 = y2 + rect2.height;
    return x1 <= xf2 && xf1 >= x2 && y1 <= yf2 && yf1 >= y2;
  },

  /**
   * ~ resolveCollidingCircles forces two circles apart based on their centers
   */
  resolveCollidingCircles: function (circ1, circ2) {
    var x1 = circ1.x;
    var y1 = circ1.y;
    var r1 = circ1.radius;
    var mult1 = 0.5;
    var x2 = circ2.x;
    var y2 = circ2.y;
    var r2 = circ2.radius;
    var mult2 = 0.5;
    var dx = x2 - x1;
    var dy = y2 - y1;
    var dist = sqrt(dx * dx + dy * dy);
    var distColl = r1 + r2 + COLLISION_OFFSET;

    // if concentric, force a very small distance
    if (dist === 0) {
      dx = COLLISION_OFFSET;
      dist = COLLISION_OFFSET;
    }

    var dd = distColl - dist;

    // fixed entities cannot be moved by physics
    if (circ1.fixed && circ2.fixed) {
      dd = 0;
    } else if (circ1.fixed) {
      mult1 = 0;
      mult2 = 1;
    } else if (circ2.fixed) {
      mult1 = 1;
      mult2 = 0;
    }

    circ1.x += mult1 * dd * -(dx / dist);
    circ1.y += mult1 * dd * -(dy / dist);
    circ2.x += mult2 * dd * (dx / dist);
    circ2.y += mult2 * dd * (dy / dist);
    return dd;
  },

  /**
   * ~ resolveCollidingCircleRect forces apart a circle and rect
   * ~ good default collision behavior for landing on a platforms vs.
   *   hitting the side (missing the platform)
   */
  resolveCollidingCircleRect: function (circ, rect) {
    var cx = circ.x;
    var cy = circ.y;
    var cr = circ.radius;
    var rwHalf = rect.width / 2;
    var rhHalf = rect.height / 2;
    var rx = rect.x + rwHalf;
    var ry = rect.y + rhHalf;
    var dx = abs(cx - rx);
    var dy = abs(cy - ry);
    if (dx > rwHalf + cr || dy > rhHalf + cr || (circ.fixed && rect.fixed)) {
      // far case: circle's center too far or both entities are fixed
      return 0;
    } else if (dx <= rwHalf || dy <= rhHalf) {
      // close case: treat the circle like another rect, then resolve
      var tempHitBounds = {
        x: cx - cr,
        y: cy - cr,
        width: 2 * cr,
        height: 2 * cr,
        fixed: circ.fixed
      };
      var dd = this.resolveCollidingRects(rect, tempHitBounds);
      circ.x = tempHitBounds.x + cr;
      circ.y = tempHitBounds.y + cr;
      return dd;
    } else {
      // corner case: the two meet at a rect corner, push them away
      var mult1 = 0.5;
      var mult2 = 0.5;
      // get the right corner point
      var nx = cx < rx ? rx - rwHalf : rx + rwHalf;
      var ny = cy < ry ? ry - rhHalf : ry + rhHalf;
      dx = nx - cx;
      dy = ny - cy;
      var dist = sqrt(dx * dx + dy * dy);
      var distColl = cr + COLLISION_OFFSET;
      var dd = distColl - dist;

      // fixed entities cannot be moved by physics
      if (circ.fixed) {
        mult1 = 0;
        mult2 = 1;
      } else if (rect.fixed) {
        mult1 = 1;
        mult2 = 0;
      }

      circ.x += mult1 * dd * -(dx / dist);
      circ.y += mult1 * dd * -(dy / dist);
      rect.x += mult2 * dd * (dx / dist);
      rect.y += mult2 * dd * (dy / dist);
      return dd;
    }
  },

  resolveCollidingRectCircle: function (rect, circ) {
    return resolveCollidingCircleRect(circ, rect);
  },

  /**
   * ~ resolveCollidingRects forces two rects apart, but only in one direction
   * ~ good default collision behavior for landing on a platforms vs.
   *   hitting the side (missing the platform)
   */
  resolveCollidingRects: function (rect1, rect2) {
    var x1 = rect1.x;
    var y1 = rect1.y;
    var w1 = rect1.width;
    var h1 = rect1.height;
    var xf1 = x1 + w1;
    var yf1 = y1 + h1;
    var mult1 = 0.5;
    var x2 = rect2.x;
    var y2 = rect2.y;
    var w2 = rect2.width;
    var h2 = rect2.height;
    var xf2 = x2 + w2;
    var yf2 = y2 + h2;
    var mult2 = 0.5;

    // find shallowest collision overlap, positive value means no overlap
    var dx = 1;
    var dx1 = x1 - xf2;
    var dx2 = x2 - xf1;
    if (dx1 <= 0 && dx2 <= 0) {
      dx = max(dx1, dx2) - COLLISION_OFFSET;
    } else if (dx1 <= 0) {
      dx = dx1 - COLLISION_OFFSET;
    } else if (dx2 <= 0) {
      dx = dx2 - COLLISION_OFFSET;
    }

    var dy = 1;
    var dy1 = y1 - yf2;
    var dy2 = y2 - yf1;
    if (dy1 <= 0 && dy2 <= 0) {
      dy = max(dy1, dy2) - COLLISION_OFFSET;
    } else if (dy1 <= 0) {
      dy = dy1 - COLLISION_OFFSET;
    } else if (dy2 <= 0) {
      dy = dy2 - COLLISION_OFFSET;
    }

    // step out in only one direction, pick the smallest overlap
    if (dx <= 0 && dy <= 0) {
      if (dx > dy) {
        dy = 0;
      } else {
        dx = 0;
      }
    } else if (dx <= 0) {
      dy = 0;
    } else if (dy <= 0) {
      dx = 0;
    } else {
      // there was no collision to begin with
      dx = 0;
      dy = 0;
    }

    // fixed entities cannot be moved by physics
    if (rect1.fixed && rect2.fixed) {
      dx = 0;
      dy = 0;
    } else if (rect1.fixed) {
      mult1 = 0;
      mult2 = 1;
    } else if (rect2.fixed) {
      mult1 = 1;
      mult2 = 0;
    }

    // dx and dy are never positive; so fix signs here based on rect centers
    var cx1 = x1 + w1 / 2;
    var cx2 = x2 + w2 / 2;
    var cy1 = y1 + h1 / 2;
    var cy2 = y2 + h2 / 2;
    var sign = (dx && cx1 > cx2) || (dy && cy1 > cy2) ? -1 : 1;
    rect1.x += mult1 * sign * dx;
    rect1.y += mult1 * sign * dy;
    rect2.x += mult2 * sign * -dx;
    rect2.y += mult2 * sign * -dy;

    // one of these will always be 0, so this is also the delta distance
    return dx + dy;
  },

  /**
   * ~ circleInsideCircle returns true if circ1 is fully contained in circ2
   */
  circleInsideCircle: function (circ1, circ2) {
    var x1 = circ1.x;
    var y1 = circ1.y;
    var r1 = circ1.radius;
    var x2 = circ2.x;
    var y2 = circ2.y;
    var r2 = circ2.radius;
    var dx = x2 - x1;
    var dy = y2 - y1;
    var dist = sqrt(dx * dx + dy * dy);
    return dist <= (r2 - r1);
  },

  /**
   * ~ circleInsideRect returns true if circ is fully contained in rect
   */
  circleInsideRect: function (circ, rect) {
    var cx = circ.x;
    var cy = circ.y;
    var cr = circ.radius;
    var tempHitBounds = {
      x: cx - cr,
      y: cy - cr,
      width: 2 * cr,
      height: 2 * cr
    };
    return this.rectInsideRect(tempHitBounds, rect);
  },

  /**
   * ~ rectInsideCircle returns true if rect is fully contained in circ
   */
  rectInsideCircle: function (rect, circ) {
    var l = rect.x;
    var r = l + rect.width;
    var t = rect.y;
    var b = t + rect.height;
    return circ.contains(l, t)
        && circ.contains(r, t)
        && circ.contains(r, b)
        && circ.contains(l, b);
  },

  /**
   * ~ rectInsideRect returns true if rect1 is fully contained in rect2
   */
  rectInsideRect: function (rect1, rect2) {
    var l = rect1.x;
    var r = l + rect1.width;
    var t = rect1.y;
    var b = t + rect1.height;
    return rect2.contains(l, t)
        && rect2.contains(r, t)
        && rect2.contains(r, b)
        && rect2.contains(l, b);
  }

};
<commit_msg>collisionHelper JSDocs<commit_after>var min = Math.min;
var max = Math.max;
var abs = Math.abs;
var sqrt = Math.sqrt;

/**
 * This value helps resolve colliding state between entities
 * @constant {number}
 * @private
 */
var COLLISION_OFFSET = 0.001;

/**
 * Shape map for function look-up {@link collisionHelper#collide}
 * @constant {object}
 * @private
 */
var SHAPE_COLLIDE_MAP = {
  Rect: {
    Rect: "rectCollidesWithRect",
    Circle: "rectCollidesWithCircle"
  },
  Circle: {
    Rect: "circleCollidesWithRect",
    Circle: "circleCollidesWithCircle"
  }
};

/**
 * Shape map for function look-up {@link collisionHelper#resolveCollision}
 * @constant {object}
 * @private
 */
var SHAPE_RESOLVE_MAP = {
  Rect: {
    Rect: "resolveCollidingRects",
    Circle: "resolveCollidingRectCircle"
  },
  Circle: {
    Rect: "resolveCollidingCircleRect",
    Circle: "resolveCollidingCircles"
  }
};

/**
 * Shape map for function look-up {@link collisionHelper#isInside}
 * @constant {object}
 * @private
 */
var SHAPE_INSIDE_MAP = {
  Rect: {
    Rect: "rectInsideRect",
    Circle: "rectInsideCircle"
  },
  Circle: {
    Rect: "circleInsideRect",
    Circle: "circleInsideCircle"
  }
};

/**
 * Function factory for shape look-up
 * @function
 * @private
 */
var _genericResolution = function(genericName, lookupMap, defaultResult) {
  return function (model1, model2) {
    var fn = null;
    var result = defaultResult;
    var shape1 = model1.shape || model1;
    var shape2 = model2.shape || model2;
    var map = lookupMap[shape1.name];
    if (map) {
      fn = map[shape2.name];
    }

    if (fn) {
      result = this[fn](shape1, shape2);
    } else {
      logger.warn(genericName + " function not found for:", shape1, shape2);
    }
    return result;
  };
};

/**
 * This namespace defines the collisions used to control entities' behavior;
 * supports circles and axis-aligned rectangles only; these are NOT continuous
 * collision detection algorithms, meaning a large value of dt could cause
 * entities to pass through each other - it's up to the developer to manage the
 * time step of his/her game to prevent this behavior
 * @namespace collisionHelper
 */
exports = {
  /**
   * Defines how collisions are detected
   * @method collisionHelper#collide
   * @arg {Shape} shape1
   * @arg {Shape} shape2
   * @returns {boolean} true if collision detected
   */
  collide: _genericResolution('Collide', SHAPE_COLLIDE_MAP, false),

  /**
   * Guarantees that two models are not colliding by pushing them apart;
   * shapes with fixed=true are never moved; returns total distance moved to separate the objects
   * @method collisionHelper#resolveCollision
   * @arg {Shape} shape1
   * @arg {Shape} shape2
   * @returns {number} total distance moved
   */
  resolveCollision: _genericResolution('Resolve', SHAPE_RESOLVE_MAP, 0),

  /**
   * Used to determine if one shape is fully contained by another
   * @method collisionHelper#isInside
   * @arg {Shape} shape1
   * @arg {Shape} shape2
   * @returns {boolean} true if shape1 is inside shape2
   */
  isInside: _genericResolution('Inside', SHAPE_INSIDE_MAP, false),

  /**
   * Detect collision between two circles
   * @method collisionHelper#circleCollidesWithCircle
   * @arg {Circle} circ1
   * @arg {Circle} circ2
   * @returns {boolean} true if collision detected
   */
  circleCollidesWithCircle: function (circ1, circ2) {
    var x1 = circ1.x;
    var y1 = circ1.y;
    var r1 = circ1.radius;
    var x2 = circ2.x;
    var y2 = circ2.y;
    var r2 = circ2.radius;
    var dx = x2 - x1;
    var dy = y2 - y1;
    var distSqrd = dx * dx + dy * dy;
    var distColl = r1 + r2;
    var distCollSqrd = distColl * distColl;
    return distSqrd <= distCollSqrd;
  },

  /**
   * Detect collision between a circle and a rectangle
   * @method collisionHelper#circleCollidesWithRect
   * @arg {Circle} circ
   * @arg {Rect} rect
   * @returns {boolean} true if collision detected
   */
  circleCollidesWithRect: function (circ, rect) {
    var cx = circ.x;
    var cy = circ.y;
    var cr = circ.radius;
    var rwHalf = rect.width / 2;
    var rhHalf = rect.height / 2;
    var rx = rect.x + rwHalf;
    var ry = rect.y + rhHalf;
    var dx = abs(cx - rx);
    var dy = abs(cy - ry);
    if (dx > rwHalf + cr || dy > rhHalf + cr) {
      // far case: circle's center is too far from rect's center
      return false;
    } else if (dx <= rwHalf || dy <= rhHalf) {
      // close case: circle's center is close enough to rect's center
      return true;
    } else {
      // corner case: rect corner within a radius of the circle's center
      var dcx = dx - rwHalf;
      var dcy = dy - rhHalf;
      var cornerDistSqrd = dcx * dcx + dcy * dcy;
      return cornerDistSqrd <= cr * cr;
    }
  },

  /**
   * Detect collision between a rectangle and a circle
   * @method collisionHelper#rectCollidesWithCircle
   * @arg {Rect} rect
   * @arg {Circle} circ
   * @returns {boolean} true if collision detected
   */
  rectCollidesWithCircle: function (rect, circ) {
    return this.circleCollidesWithRect(circ, rect);
  },

  /**
   * Detect collision between two rectangles
   * @method collisionHelper#rectCollidesWithRect
   * @arg {Rect} rect1
   * @arg {Rect} rect2
   * @returns {boolean} true if collision detected
   */
  rectCollidesWithRect: function (rect1, rect2) {
    var x1 = rect1.x;
    var y1 = rect1.y;
    var xf1 = x1 + rect1.width;
    var yf1 = y1 + rect1.height;
    var x2 = rect2.x;
    var y2 = rect2.y;
    var xf2 = x2 + rect2.width;
    var yf2 = y2 + rect2.height;
    return x1 <= xf2 && xf1 >= x2 && y1 <= yf2 && yf1 >= y2;
  },

  /**
   * Resolve collision between two circles by pushing them apart relative to their centers
   * @method collisionHelper#resolveCollidingCircles
   * @arg {Circle} circ1
   * @arg {Circle} circ2
   * @returns {number} the total distance the circles moved apart
   */
  resolveCollidingCircles: function (circ1, circ2) {
    var x1 = circ1.x;
    var y1 = circ1.y;
    var r1 = circ1.radius;
    var mult1 = 0.5;
    var x2 = circ2.x;
    var y2 = circ2.y;
    var r2 = circ2.radius;
    var mult2 = 0.5;
    var dx = x2 - x1;
    var dy = y2 - y1;
    var dist = sqrt(dx * dx + dy * dy);
    var distColl = r1 + r2 + COLLISION_OFFSET;

    // if concentric, force a very small distance
    if (dist === 0) {
      dx = COLLISION_OFFSET;
      dist = COLLISION_OFFSET;
    }

    var dd = distColl - dist;

    // fixed entities cannot be moved by physics
    if (circ1.fixed && circ2.fixed) {
      dd = 0;
    } else if (circ1.fixed) {
      mult1 = 0;
      mult2 = 1;
    } else if (circ2.fixed) {
      mult1 = 1;
      mult2 = 0;
    }

    circ1.x += mult1 * dd * -(dx / dist);
    circ1.y += mult1 * dd * -(dy / dist);
    circ2.x += mult2 * dd * (dx / dist);
    circ2.y += mult2 * dd * (dy / dist);
    return dd;
  },

  /**
   * Resolve collision between circle and rectangle by pushing them apart
   * @method collisionHelper#resolveCollidingCircleRect
   * @arg {Circle} circ
   * @arg {Rect} rect
   * @returns {number} the total distance the shapes moved apart
   */
  resolveCollidingCircleRect: function (circ, rect) {
    var cx = circ.x;
    var cy = circ.y;
    var cr = circ.radius;
    var rwHalf = rect.width / 2;
    var rhHalf = rect.height / 2;
    var rx = rect.x + rwHalf;
    var ry = rect.y + rhHalf;
    var dx = abs(cx - rx);
    var dy = abs(cy - ry);
    if (dx > rwHalf + cr || dy > rhHalf + cr || (circ.fixed && rect.fixed)) {
      // far case: circle's center too far or both entities are fixed
      return 0;
    } else if (dx <= rwHalf || dy <= rhHalf) {
      // close case: treat the circle like another rect, then resolve
      var tempHitBounds = {
        x: cx - cr,
        y: cy - cr,
        width: 2 * cr,
        height: 2 * cr,
        fixed: circ.fixed
      };
      var dd = this.resolveCollidingRects(rect, tempHitBounds);
      circ.x = tempHitBounds.x + cr;
      circ.y = tempHitBounds.y + cr;
      return dd;
    } else {
      // corner case: the two meet at a rect corner, push them away
      var mult1 = 0.5;
      var mult2 = 0.5;
      // get the right corner point
      var nx = cx < rx ? rx - rwHalf : rx + rwHalf;
      var ny = cy < ry ? ry - rhHalf : ry + rhHalf;
      dx = nx - cx;
      dy = ny - cy;
      var dist = sqrt(dx * dx + dy * dy);
      var distColl = cr + COLLISION_OFFSET;
      var dd = distColl - dist;

      // fixed entities cannot be moved by physics
      if (circ.fixed) {
        mult1 = 0;
        mult2 = 1;
      } else if (rect.fixed) {
        mult1 = 1;
        mult2 = 0;
      }

      circ.x += mult1 * dd * -(dx / dist);
      circ.y += mult1 * dd * -(dy / dist);
      rect.x += mult2 * dd * (dx / dist);
      rect.y += mult2 * dd * (dy / dist);
      return dd;
    }
  },

  /**
   * Resolve collision between rectangle and circle by pushing them apart
   * @method collisionHelper#resolveCollidingRectCircle
   * @arg {Rect} rect
   * @arg {Circle} circ
   * @returns {number} the total distance the shapes moved apart
   */
  resolveCollidingRectCircle: function (rect, circ) {
    return resolveCollidingCircleRect(circ, rect);
  },

  /**
   * Resolve collision between two rectangles by pushing them apart in one direction
   * @method collisionHelper#resolveCollidingRects
   * @arg {Rect} rect1
   * @arg {Rect} rect2
   * @returns {number} the total distance the rectangles moved apart
   */
  resolveCollidingRects: function (rect1, rect2) {
    var x1 = rect1.x;
    var y1 = rect1.y;
    var w1 = rect1.width;
    var h1 = rect1.height;
    var xf1 = x1 + w1;
    var yf1 = y1 + h1;
    var mult1 = 0.5;
    var x2 = rect2.x;
    var y2 = rect2.y;
    var w2 = rect2.width;
    var h2 = rect2.height;
    var xf2 = x2 + w2;
    var yf2 = y2 + h2;
    var mult2 = 0.5;

    // find shallowest collision overlap, positive value means no overlap
    var dx = 1;
    var dx1 = x1 - xf2;
    var dx2 = x2 - xf1;
    if (dx1 <= 0 && dx2 <= 0) {
      dx = max(dx1, dx2) - COLLISION_OFFSET;
    } else if (dx1 <= 0) {
      dx = dx1 - COLLISION_OFFSET;
    } else if (dx2 <= 0) {
      dx = dx2 - COLLISION_OFFSET;
    }

    var dy = 1;
    var dy1 = y1 - yf2;
    var dy2 = y2 - yf1;
    if (dy1 <= 0 && dy2 <= 0) {
      dy = max(dy1, dy2) - COLLISION_OFFSET;
    } else if (dy1 <= 0) {
      dy = dy1 - COLLISION_OFFSET;
    } else if (dy2 <= 0) {
      dy = dy2 - COLLISION_OFFSET;
    }

    // step out in only one direction, pick the smallest overlap
    if (dx <= 0 && dy <= 0) {
      if (dx > dy) {
        dy = 0;
      } else {
        dx = 0;
      }
    } else if (dx <= 0) {
      dy = 0;
    } else if (dy <= 0) {
      dx = 0;
    } else {
      // there was no collision to begin with
      dx = 0;
      dy = 0;
    }

    // fixed entities cannot be moved by physics
    if (rect1.fixed && rect2.fixed) {
      dx = 0;
      dy = 0;
    } else if (rect1.fixed) {
      mult1 = 0;
      mult2 = 1;
    } else if (rect2.fixed) {
      mult1 = 1;
      mult2 = 0;
    }

    // dx and dy are never positive; so fix signs here based on rect centers
    var cx1 = x1 + w1 / 2;
    var cx2 = x2 + w2 / 2;
    var cy1 = y1 + h1 / 2;
    var cy2 = y2 + h2 / 2;
    var sign = (dx && cx1 > cx2) || (dy && cy1 > cy2) ? -1 : 1;
    rect1.x += mult1 * sign * dx;
    rect1.y += mult1 * sign * dy;
    rect2.x += mult2 * sign * -dx;
    rect2.y += mult2 * sign * -dy;

    // one of these will always be 0, so this is also the delta distance
    return dx + dy;
  },

  /**
   * @method collisionHelper#circleInsideCircle
   * @arg {Circle} circ1
   * @arg {Circle} circ2
   * @returns {boolean} true if circ1 is inside circ2
   */
  circleInsideCircle: function (circ1, circ2) {
    var x1 = circ1.x;
    var y1 = circ1.y;
    var r1 = circ1.radius;
    var x2 = circ2.x;
    var y2 = circ2.y;
    var r2 = circ2.radius;
    var dx = x2 - x1;
    var dy = y2 - y1;
    var dist = sqrt(dx * dx + dy * dy);
    return dist <= (r2 - r1);
  },

  /**
   * @method collisionHelper#circleInsideRect
   * @arg {Circle} circ
   * @arg {Rect} rect
   * @returns {boolean} true if circ is inside rect
   */
  circleInsideRect: function (circ, rect) {
    var cx = circ.x;
    var cy = circ.y;
    var cr = circ.radius;
    var tempHitBounds = {
      x: cx - cr,
      y: cy - cr,
      width: 2 * cr,
      height: 2 * cr
    };
    return this.rectInsideRect(tempHitBounds, rect);
  },

  /**
   * @method collisionHelper#rectInsideCircle
   * @arg {Rect} rect
   * @arg {Circle} circ
   * @returns {boolean} true if rect is inside circ
   */
  rectInsideCircle: function (rect, circ) {
    var l = rect.x;
    var r = l + rect.width;
    var t = rect.y;
    var b = t + rect.height;
    return circ.contains(l, t)
        && circ.contains(r, t)
        && circ.contains(r, b)
        && circ.contains(l, b);
  },

  /**
   * @method collisionHelper#rectInsideRect
   * @arg {Rect} rect1
   * @arg {Rect} rect2
   * @returns {boolean} true if rect1 is inside rect2
   */
  rectInsideRect: function (rect1, rect2) {
    var l = rect1.x;
    var r = l + rect1.width;
    var t = rect1.y;
    var b = t + rect1.height;
    return rect2.contains(l, t)
        && rect2.contains(r, t)
        && rect2.contains(r, b)
        && rect2.contains(l, b);
  }
};
<|endoftext|>


<commit_before>"use strict";

process.env.DEVICE = process.env.DEVICE || "selendroid";
var setup = require("../common/setup-base")
  , sessionUtils = require('../../helpers/session-utils')
  , path = require('path')
  , Q = require("q")
  , _ = require('underscore');

var desired = {
  app: path.resolve(__dirname, "../../../sample-code/apps/ApiDemos/bin/ApiDemos-debug.apk"),
  'app-package': 'com.example.android.apis',
  'app-activity': '.ApiDemos'
};

  // , appAct2 = "ApiDemos"
  // , appActFull = "com.example.android.apis.ApiDemos"

describe('selendroid - basic -', function () {

  describe('api', function () {
    var driver;
    setup(this, desired).then(function (d) { driver = d; });
    // todo: issue with find
    it('should find and click an element @skip-all-selendroid', function (done) {
      // selendroid appears to have some issues with implicit waits
      // hence the timeouts
      driver
        .waitForElementByName('App', 10000).click()
        .sleep(1000)
        .elementByLinkText("Action Bar").should.eventually.exist
        .nodeify(done);
    });

    it('should be able to get logcat log type', function (done) {
      driver.logTypes().should.eventually.include('logcat')
        .nodeify(done);
    });
    it('should be able to get logcat logs', function (done) {
      driver.log('logcat').then(function (logs) {
        logs.length.should.be.above(0);
        logs[0].message.should.not.include("\n");
        logs[0].level.should.equal("ALL");
        logs[0].timestamp.should.exist;
      }).nodeify(done);
    });

    it('should be able to proxy errors', function (done) {
      driver
        .frame(null).should.be.rejected
        .nodeify(done);
    });

    it('should be able to set location', function (done) {
      var locOpts = {latitude: "27.17", longitude: "78.04"};
      driver
        .execute("mobile: setLocation", [locOpts])
        .nodeify(done);
    });

    it('should error out nicely with incompatible commands', function (done) {
      driver
        .execute("mobile: flick", [{}])
        .catch(function (err) {
          err.cause.value.origValue.should.contain('mobile:');
          throw err;
        }).should.be.rejectedWith(/status: 9/)
        .nodeify(done);
    });

  });

  describe('uninstall app', function () {
    var driver;
    setup(this, desired).then(function (d) { driver = d; });

    it('should be able to uninstall the app', function (done) {
      driver
        .execute("mobile: removeApp", [{bundleId: desired['app-package']}])
        .nodeify(done);
    });
  });

  describe('background app', function () {
    var driver;
    setup(this, desired).then(function (d) { driver = d; });

    it("should background the app", function (done) {
      var before = new Date().getTime() / 1000;
      driver
        .execute("mobile: background", [{seconds: 3}])
        .then(function () {
          ((new Date().getTime() / 1000) - before).should.be.above(2);
          // this should not be tested
          // ((new Date().getTime() / 1000) - before).should.be.below(5);
        })
        .execute("mobile: currentActivity")
          .should.eventually.include("ApiDemos")
        .nodeify(done);
    });
  });

  describe('command timeouts', function () {
    var driver;
    setup(this, _.defaults({newCommandTimeout: 3}, desired))
     .then(function (d) { driver = d; });

    it('should die with short timeout', function (done) {
      driver
        .sleep(5000)
        .elementByName('Animation')
          .should.be.rejectedWith(/(status: (13|6))|(Not JSON response)/)
        .nodeify(done);
    });
  });

  // todo: issue with find
  describe('command timeouts @skip-all-selendroid', function () {
    var driver;
    setup(this, _.defaults({newCommandTimeout: 7}, desired))
     .then(function (d) { driver = d; });

    it('should not die if commands come in', function (done) {
      var start = Date.now();
      var find = function () {
        if ((Date.now() - start) < 5000) {
          return driver
            .elementByName('Animation').should.eventually.exist
            .sleep(500)
            .then(find);
        } else return new Q();
      };
      find().then(function () {
        return driver
          .sleep(10000)
          .elementByName('Animation').should.be.rejected;
      }).nodeify(done);
    });
  });

  describe('app activities with no dot', function () {
    var session;
    after(function () { session.tearDown(); });

    it('should not launch app', function (done) {
      session = sessionUtils.initSession(_.defaults({'app-activity': 'ApiDemos'}, desired), {'no-retry': true});
      session.setUp()
        .should.be.rejected
        .nodeify(done);
    });
  });

  describe('fully qualified app activities', function () {
    var session;
    after(function () { session.tearDown(); });

    it('should still launch app', function (done) {
      session = sessionUtils.initSession(_.defaults({'app-activity': 'com.example.android.apis.ApiDemos'}, desired));
      session.setUp()
        .nodeify(done);
    });
  });
});
<commit_msg>change selendroid proxy test to use a method that actually errors<commit_after>"use strict";

process.env.DEVICE = process.env.DEVICE || "selendroid";
var setup = require("../common/setup-base")
  , sessionUtils = require('../../helpers/session-utils')
  , path = require('path')
  , Q = require("q")
  , _ = require('underscore');

var desired = {
  app: path.resolve(__dirname, "../../../sample-code/apps/ApiDemos/bin/ApiDemos-debug.apk"),
  'app-package': 'com.example.android.apis',
  'app-activity': '.ApiDemos'
};

  // , appAct2 = "ApiDemos"
  // , appActFull = "com.example.android.apis.ApiDemos"

describe('selendroid - basic -', function () {

  describe('api', function () {
    var driver;
    setup(this, desired).then(function (d) { driver = d; });
    // todo: issue with find
    it('should find and click an element @skip-all-selendroid', function (done) {
      // selendroid appears to have some issues with implicit waits
      // hence the timeouts
      driver
        .waitForElementByName('App', 10000).click()
        .sleep(1000)
        .elementByLinkText("Action Bar").should.eventually.exist
        .nodeify(done);
    });

    it('should be able to get logcat log type', function (done) {
      driver.logTypes().should.eventually.include('logcat')
        .nodeify(done);
    });
    it('should be able to get logcat logs', function (done) {
      driver.log('logcat').then(function (logs) {
        logs.length.should.be.above(0);
        logs[0].message.should.not.include("\n");
        logs[0].level.should.equal("ALL");
        logs[0].timestamp.should.exist;
      }).nodeify(done);
    });

    it('should be able to proxy errors', function (done) {
      driver
        .elementByCss("foobar").should.be.rejected
        .nodeify(done);
    });

    it('should be able to set location', function (done) {
      var locOpts = {latitude: "27.17", longitude: "78.04"};
      driver
        .execute("mobile: setLocation", [locOpts])
        .nodeify(done);
    });

    it('should error out nicely with incompatible commands', function (done) {
      driver
        .execute("mobile: flick", [{}])
        .catch(function (err) {
          err.cause.value.origValue.should.contain('mobile:');
          throw err;
        }).should.be.rejectedWith(/status: 9/)
        .nodeify(done);
    });

  });

  describe('uninstall app', function () {
    var driver;
    setup(this, desired).then(function (d) { driver = d; });

    it('should be able to uninstall the app', function (done) {
      driver
        .execute("mobile: removeApp", [{bundleId: desired['app-package']}])
        .nodeify(done);
    });
  });

  describe('background app', function () {
    var driver;
    setup(this, desired).then(function (d) { driver = d; });

    it("should background the app", function (done) {
      var before = new Date().getTime() / 1000;
      driver
        .execute("mobile: background", [{seconds: 3}])
        .then(function () {
          ((new Date().getTime() / 1000) - before).should.be.above(2);
          // this should not be tested
          // ((new Date().getTime() / 1000) - before).should.be.below(5);
        })
        .execute("mobile: currentActivity")
          .should.eventually.include("ApiDemos")
        .nodeify(done);
    });
  });

  describe('command timeouts', function () {
    var driver;
    setup(this, _.defaults({newCommandTimeout: 3}, desired))
     .then(function (d) { driver = d; });

    it('should die with short timeout', function (done) {
      driver
        .sleep(5000)
        .elementByName('Animation')
          .should.be.rejectedWith(/(status: (13|6))|(Not JSON response)/)
        .nodeify(done);
    });
  });

  // todo: issue with find
  describe('command timeouts @skip-all-selendroid', function () {
    var driver;
    setup(this, _.defaults({newCommandTimeout: 7}, desired))
     .then(function (d) { driver = d; });

    it('should not die if commands come in', function (done) {
      var start = Date.now();
      var find = function () {
        if ((Date.now() - start) < 5000) {
          return driver
            .elementByName('Animation').should.eventually.exist
            .sleep(500)
            .then(find);
        } else return new Q();
      };
      find().then(function () {
        return driver
          .sleep(10000)
          .elementByName('Animation').should.be.rejected;
      }).nodeify(done);
    });
  });

  describe('app activities with no dot', function () {
    var session;
    after(function () { session.tearDown(); });

    it('should not launch app', function (done) {
      session = sessionUtils.initSession(_.defaults({'app-activity': 'ApiDemos'}, desired), {'no-retry': true});
      session.setUp()
        .should.be.rejected
        .nodeify(done);
    });
  });

  describe('fully qualified app activities', function () {
    var session;
    after(function () { session.tearDown(); });

    it('should still launch app', function (done) {
      session = sessionUtils.initSession(_.defaults({'app-activity': 'com.example.android.apis.ApiDemos'}, desired));
      session.setUp()
        .nodeify(done);
    });
  });
});
<|endoftext|>


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

var fs          = require('fs');
var os          = require('os');
var path        = require('path');
var crypto      = require('crypto');
var _           = require('underscore');
var Handlebars  = require('handlebars');
var async       = require('async');
var tar         = require('tar-fs');
var Docker      = require('dockerode');
var rimraf      = require('rimraf');
var yaml        = require('js-yaml');
var minimist    = require('minimist');
var colors      = require('colors');

var argv = (function(argv) {
  return {
    help: argv.h || argv.help,
    version: argv.v || argv.version,
    config: path.resolve(process.cwd(), argv.f || argv.file || '.chimera.yml'),
    project: path.resolve(process.cwd(), argv.p || argv.project || './'),
    target: argv.t || argv.target || process.env.CHIMERA_TARGET,
    verbose: argv.V || argv.verbose,
    _: argv._
  };
}(minimist(process.argv.slice(2))));
var verbose = argv.verbose ?
  function(arg) { console.log(arg); return arg;} :
  function(arg) { return arg; };
var docker;

Handlebars.registerHelper('render', function(template, data) {
  return new Handlebars.SafeString(Handlebars.compile(template)(data));
});
var dockerfile = Handlebars.compile([
  'FROM {{baseImage}}:{{tag}}',
  'COPY project/ /project',
  'WORKDIR /project',
  '',
  'ENV CHIMERA_TARGET={{name}}:{{tag}}',
  'ENV CHIMERA_TARGET_NAME={{name}}',
  'ENV CHIMERA_TARGET_TAG={{tag}}',
  '{{#each env}}',
  'ENV {{render this ../this}}',
  '{{/each}}',
  '',
  '{{#each install}}',
  'RUN {{render this ../this}}',
  '{{/each}}',
  'CMD {{script}}'
].join('\n'));

if (argv.help) {
  console.log([
    '',
    'Usage: chimera [options]',
    '       chimera generate <ci-service>',
    '',
    'Easy multi-container testing with Docker',
    '',
    'Options:',
    '',
    '  -h, --help               output usage information',
    '  -v, --version            output version',
    '  -c, --config <path>      set configuration file',
    '  -p, --project <path>     set project directory',
    '  -t, --target <image:tag> set target',
    '  -V, --verbose            verbose mode'
  ].join('\n'));
  process.exit(0);
}

if(argv.version) {
  console.log('chimera version ' + require('./package.json').version);
  process.exit(0);
}

fs.readFile(argv.config, 'utf8', function(err, raw) {
  fail(err);

  var config = yaml.safeLoad(raw);

  docker = new Docker(config.docker);

  targets(config, function(err, targets) {
    fail(err);

    switch (argv._[0]) {
    case 'generate':
      generate(targets);
      break;
    case 'run':
    case undefined:
      run(targets);
      break;
    default:
      console.error('unknown command, see --help');
    };
  });
});

function generate(targets) {
  switch (argv._[1]) {
  case 'travis':
    console.log(Handlebars.compile([
      'language: node_js',
      'sudo: required',
      'services:',
      '  - docker',
      'install:',
      '  - npm install -g chimera-cli',
      'script:',
      '  - chimera',
      'env:',
      '  matrix:',
      '{{#each this}}',
      '    - CHIMERA_TARGET={{name}}:{{tag}}',
      '{{/each}}'
    ].join('\n'))(targets));
    break;
  default:
    console.error('unknown ci service, example: chimera generate travis');
  };

};

function run(targets) {
  async.eachSeries(targets, function(target, cb) {
    console.log(('executing target ' + target.image).green);
    async.applyEachSeries([bundle, build, test, clean], target, cb);
  }, fail);
};

function targets(config, cb) {
  cb(null, _.map(config.targets, function(target, name) {
    return target.tags.map(function(tag) {
      var id = crypto.randomBytes(5).toString('hex');

      return {
        name: name,
        tag: tag,
        id: id,
        dir: path.join(os.tmpdir(), id),
        tar: path.join(os.tmpdir(), id + '.tar'),
        baseImage: target.image || name,
        image: name + '-' + tag + '-' + id,
        install: (target.install || []).concat(config.install || []),
        env: (target.env || []).concat(config.env || []),
        script: config.script.join(' && ') // TODO is this a good idea?
      };
    });
  }).reduce(function(a, b) {
    return a.concat(b);
  }).filter(function(target) {
    return !argv.target ||
      target.name.indexOf(argv.target) === 0 ||
      (target.name + ':' + target.tag).indexOf(argv.target) === 0;
  }));
}

function bundle(target, cb) {
  async.series([
    fs.mkdir.bind(fs, target.dir),
    fs.writeFile.bind(fs,
      path.join(target.dir, 'Dockerfile'), verbose(dockerfile(target))),
    fs.symlink.bind(fs, argv.project, path.join(target.dir, 'project')),
    function(cb) {
      tar.pack(target.dir, {dereference: true})
        .pipe(fs.createWriteStream(target.tar))
        .on('error', cb)
        .on('finish', cb);
    }
  ], cb);
}

function build(target, cb) {
  docker.buildImage(target.tar, {t: target.image}, function(err, res) {
    if(err) {
      return cb(err);
    }

    res.on('data', function(data) {
      var msg = JSON.parse(data);

      if(msg.error) {
        console.error(msg.error);
        cb = cb.bind(null, new Error('failed to build image ' + target.image));
      } else if(msg.stream || msg.status) {
        verbose(msg.stream || msg.status);
      }
    });
    res.on('end', cb);
  });
}

function test(target, cb) {
  docker.run(target.image, [], process.stdout, function(err, data, container) {
    if(err) {
      return cb(err);
    }
    if(data.StatusCode != 0) {
      return cb(new Error('tests failed on ' + target.image));
    }

    target.container = container.id;
    cb();
  });
}

function clean(target, cb) {
  var container = docker.getContainer(target.container);
  var image = docker.getImage(target.image);

  async.series([
    rimraf.bind(rimraf, target.dir),
    rimraf.bind(rimraf, target.tar),
    container.remove.bind(container),
    image.remove.bind(image)
  ], cb);
}

function fail(err) {
  if(err) {
    console.error(err.message.red);
    process.exit(1);
  }
}
<commit_msg>use docker-modem followProgress to handle build stream<commit_after>#!/usr/bin/env node

var fs          = require('fs');
var os          = require('os');
var path        = require('path');
var crypto      = require('crypto');
var _           = require('underscore');
var Handlebars  = require('handlebars');
var async       = require('async');
var tar         = require('tar-fs');
var Docker      = require('dockerode');
var rimraf      = require('rimraf');
var yaml        = require('js-yaml');
var minimist    = require('minimist');
var colors      = require('colors');

var argv = (function(argv) {
  return {
    help: argv.h || argv.help,
    version: argv.v || argv.version,
    config: path.resolve(process.cwd(), argv.f || argv.file || '.chimera.yml'),
    project: path.resolve(process.cwd(), argv.p || argv.project || './'),
    target: argv.t || argv.target || process.env.CHIMERA_TARGET,
    verbose: argv.V || argv.verbose,
    _: argv._
  };
}(minimist(process.argv.slice(2))));
var verbose = argv.verbose ?
  function(arg) { console.log(arg); return arg;} :
  function(arg) { return arg; };
var docker;

Handlebars.registerHelper('render', function(template, data) {
  return new Handlebars.SafeString(Handlebars.compile(template)(data));
});
var dockerfile = Handlebars.compile([
  'FROM {{baseImage}}:{{tag}}',
  'COPY project/ /project',
  'WORKDIR /project',
  '',
  'ENV CHIMERA_TARGET={{name}}:{{tag}}',
  'ENV CHIMERA_TARGET_NAME={{name}}',
  'ENV CHIMERA_TARGET_TAG={{tag}}',
  '{{#each env}}',
  'ENV {{render this ../this}}',
  '{{/each}}',
  '',
  '{{#each install}}',
  'RUN {{render this ../this}}',
  '{{/each}}',
  'CMD {{script}}'
].join('\n'));

if (argv.help) {
  console.log([
    '',
    'Usage: chimera [options]',
    '       chimera generate <ci-service>',
    '',
    'Easy multi-container testing with Docker',
    '',
    'Options:',
    '',
    '  -h, --help               output usage information',
    '  -v, --version            output version',
    '  -c, --config <path>      set configuration file',
    '  -p, --project <path>     set project directory',
    '  -t, --target <image:tag> set target',
    '  -V, --verbose            verbose mode'
  ].join('\n'));
  process.exit(0);
}

if(argv.version) {
  console.log('chimera version ' + require('./package.json').version);
  process.exit(0);
}

fs.readFile(argv.config, 'utf8', function(err, raw) {
  fail(err);

  var config = yaml.safeLoad(raw);

  docker = new Docker(config.docker);

  targets(config, function(err, targets) {
    fail(err);

    switch (argv._[0]) {
    case 'generate':
      generate(targets);
      break;
    case 'run':
    case undefined:
      run(targets);
      break;
    default:
      console.error('unknown command, see --help');
    };
  });
});

function generate(targets) {
  switch (argv._[1]) {
  case 'travis':
    console.log(Handlebars.compile([
      'language: node_js',
      'sudo: required',
      'services:',
      '  - docker',
      'install:',
      '  - npm install -g chimera-cli',
      'script:',
      '  - chimera',
      'env:',
      '  matrix:',
      '{{#each this}}',
      '    - CHIMERA_TARGET={{name}}:{{tag}}',
      '{{/each}}'
    ].join('\n'))(targets));
    break;
  default:
    console.error('unknown ci service, example: chimera generate travis');
  };
};

function run(targets) {
  async.eachSeries(targets, function(target, cb) {
    console.log(('executing target ' + target.image).green);
    async.applyEachSeries([bundle, build, test, clean], target, cb);
  }, fail);
};

function targets(config, cb) {
  cb(null, _.map(config.targets, function(target, name) {
    return target.tags.map(function(tag) {
      var id = crypto.randomBytes(5).toString('hex');

      return {
        name: name,
        tag: tag,
        id: id,
        dir: path.join(os.tmpdir(), id),
        tar: path.join(os.tmpdir(), id + '.tar'),
        baseImage: target.image || name,
        image: name + '-' + tag + '-' + id,
        install: (target.install || []).concat(config.install || []),
        env: (target.env || []).concat(config.env || []),
        script: config.script.join(' && ') // TODO is this a good idea?
      };
    });
  }).reduce(function(a, b) {
    return a.concat(b);
  }).filter(function(target) {
    return !argv.target ||
      target.name.indexOf(argv.target) === 0 ||
      (target.name + ':' + target.tag).indexOf(argv.target) === 0;
  }));
}

function bundle(target, cb) {
  async.series([
    fs.mkdir.bind(fs, target.dir),
    fs.writeFile.bind(fs,
      path.join(target.dir, 'Dockerfile'), verbose(dockerfile(target))),
    fs.symlink.bind(fs, argv.project, path.join(target.dir, 'project')),
    function(cb) {
      tar.pack(target.dir, {dereference: true})
        .pipe(fs.createWriteStream(target.tar))
        .on('error', cb)
        .on('finish', cb);
    }
  ], cb);
}

function build(target, cb) {
  docker.buildImage(target.tar, {t: target.image}, function(err, stream) {
    if(err) {
      return cb(err);
    }

    docker.modem.followProgress(stream, cb, verbose);
  });
}

function test(target, cb) {
  docker.run(target.image, [], process.stdout, function(err, data, container) {
    if(err) {
      return cb(err);
    }
    if(data.StatusCode != 0) {
      return cb(new Error('tests failed on ' + target.image));
    }

    target.container = container.id;
    cb();
  });
}

function clean(target, cb) {
  var container = docker.getContainer(target.container);
  var image = docker.getImage(target.image);

  async.series([
    rimraf.bind(rimraf, target.dir),
    rimraf.bind(rimraf, target.tar),
    container.remove.bind(container),
    image.remove.bind(image)
  ], cb);
}

function fail(err) {
  if(err) {
    console.error(err.message.red);
    process.exit(1);
  }
}
<|endoftext|>