<commit_before>'use strict';
/*global beforeEach, afterEach, stub*/
var DocumentDB = require('documentdb').DocumentClient;
var DoQmentDB  = require('..');
var stub       = require('sinon').stub;
var should     = require('should');
var Promise    = require('bluebird');

// Helpers: Mocks, and DocumentDB behavior
var DB_MOCK   = { _self: '/self/db',  _colls: '/colls' };
var COLL_MOCK = { _self: '/self/col', _docs:  '/docs'  };
var DOC_MOCK  = { _self: '/self/doc', _id:    '54123'  };
function toArray(args) {
  return { toArray: function(fb) { fb.apply(null, args);} }
}
function applyCallback(o1, o2, cb) { return (cb||o2)() }

// Assertions helpers
function assertCalled(q, done, toCalled, withArgs) {
  q.then(function(res) {
    (toCalled.called).should.eql(true);
    // Test `calledWith` with the given arguments.
    if(withArgs) {
      toCalled.calledWith.apply(toCalled, withArgs).should.eql(true);
    }
    done();
  });
}

describe('DoqmentDB', function() {
  describe('DatabaseManager', function() {

    describe('creating `new` DatabaseManager', function() {
      it('should get a not DocumentDB instance(connection) and throw', function() {
        (function() {
          new DoQmentDB({});
        }).should.throw();
      });

      it('should get DocumentDB instance(connection) and return an instance', function() {
        (new DoQmentDB(new DocumentDB('name', {}), 'dbName'))
          .should.be.an.instanceOf(Object)
      });
    });

    // findOrCreate behavior
    describe('`using` dynamically', function() {
      var connection = new DocumentDB('host', { masterKey: 'key' });
      var dbManager  = new DoQmentDB(connection, '');

      beforeEach(function() {
        stub(DocumentDB.prototype, 'createDatabase', function(obj, fb) {fb()});
        var stub1 = stub(DocumentDB.prototype, 'queryDatabases');
        stub1.onCall(0).returns(toArray([null, []]));
        stub1.onCall(1).returns(toArray([null, [{}]]));
      });

      describe('working with non-existing(in Azure) database', function() {
        it('should call `createDatabase` with database name', function(done) {
          dbManager.manager.findOrCreate({ id: dbManager.database })
            .then(function(result) {
              DocumentDB.prototype.createDatabase.calledWith({ id: dbManager.database })
                .should.eql(true);
              done();
            });
        });
      });

      describe('working with existing(in Azure) database', function() {
        it('should return the db and not call `createDatabase`', function(done) {
          dbManager.manager.findOrCreate({ id: dbManager.database })
            .then(function(result) {
              DocumentDB.prototype.createDatabase.calledTwice.should.eql(false);
              done();
            });
        });
      });

      afterEach(function() {
        DocumentDB.prototype.createDatabase.restore();
        DocumentDB.prototype.queryDatabases.restore();
      });
    });

    // Public api, .find(), .create(), ...
    describe('Public API', function() {
      var connection = new DocumentDB('host', { masterKey: 'key' });
      var dbManager  = new DoQmentDB(connection, '');
      var queryStub, readStub;

      beforeEach(function() {
        // _getDatabase
        stub(DocumentDB.prototype,  'queryDatabases')
          .returns(toArray([null, DB_MOCK]));
        // (query|read)Collections
        queryStub = stub(DocumentDB.prototype, 'queryCollections');
        readStub = stub(DocumentDB.prototype,  'readCollections');
        queryStub.returns(toArray([null, [COLL_MOCK]]));
        readStub.returns(toArray([null, [1, 2]]));
      });

      describe('.getDatabase()', function() {
        it('should return the used collection, call `queryCollection`', function(done) {
          dbManager.getDatabase()
            .then(function(db) {
              db.should.eql(DB_MOCK);
              DocumentDB.prototype.queryDatabases.called.should.eql(true);
              done();
            });
        });
      });

      describe('.find()', function() {
        it('should get object properties and call `queryCollections`', function(done) {
          assertCalled(dbManager.find({ id: 'name' }), done, queryStub);
        });

        //TODO: calledWith assertion

        it('should get an empty object and call `readCollections`', function(done) {
          assertCalled(dbManager.find({}), done, readStub);
        });

        it('should get an `undefined` params and call `readCollections`', function(done) {
          assertCalled(dbManager.find(undefined), done, readStub);
        });
      });

      describe('.findById()', function() {
        it('should get id as a string and call `queryCollection`', function(done) {
          assertCalled(dbManager.findById('foo'), done, queryStub);
        });
      });

      describe('.findAndCreate()', function() {
        var createStub;
        beforeEach(function() {
          createStub = stub(DocumentDB.prototype, 'createCollection', applyCallback);
          queryStub.returns(toArray([null, []]));
        });
        it('should call `createCollection` if it\'s not exist', function(done) {
          var users = { id: '31' };
          var args  = [DB_MOCK._self, users];
          assertCalled(dbManager.findOrCreate(users), done, createStub, args);
        });
        afterEach(function() {
          createStub.restore();
        });
      });

      describe('.insert() | .create()', function() {
        var createStub;
        beforeEach(function() {
          createStub = stub(DocumentDB.prototype, 'createCollection', applyCallback);
        });

        it('should get name and call `createCollection`', function(done) {
          var name = 'foo';
          var args = [DB_MOCK._self,  { id: name }];
          assertCalled(dbManager.create(name), done, createStub, args);
        });

        it('should have aliases', function() {
          dbManager.create.should.eql(dbManager.insert);
        });

        afterEach(function() {
          createStub.restore();
        });
      });

      describe('.remove()', function() {
        var removeStub;
        beforeEach(function() {
          removeStub = stub(DocumentDB.prototype, 'deleteCollection', applyCallback);
          queryStub.returns(toArray([null, [COLL_MOCK]]));
        });

        it('should get collection id and call `deleteCollection`', function(done) {
          assertCalled(dbManager.remove('id'), done, removeStub, [COLL_MOCK._self]);
        });
      });

      describe('.use()', function() {
        it('should return an object immediately(not async operation)', function() {
          dbManager.use('users').should.be.type('object');
        });

        it('should return instance of collectionManager', function() {
          dbManager.use('users').constructor.name.should.eql('Collection');
        });
      });

      describe('CollectionManager', function() {
        var users = dbManager.use('users');
        var readStub, queryStub;

        beforeEach(function() {
          readStub = stub(DocumentDB.prototype, 'readDocuments');
          queryStub = stub(DocumentDB.prototype, 'queryDocuments');
          queryStub.returns(toArray([null, [DOC_MOCK]]));
          readStub.returns(toArray([null, [1, 2]]));
        });

        describe('.getCollection()', function() {
          it('should return the used collection, call `queryCollection`', function(done) {
            users.getCollection()
              .then(function(coll) {
                coll.should.eql(COLL_MOCK);
                DocumentDB.prototype.queryCollections.called.should.eql(true);
                done();
              });
          });
        });

        describe('.find()', function() {
          it('should get empty object and call `readDocuments`', function(done) {
            assertCalled(users.find({}), done, readStub);
          });

          it('should get undefined param and call `readDocuments`', function(done) {
            assertCalled(users.find(undefined), done, readStub);
          });

          it('should get object params and call `queryDocuments`', function(done) {
            assertCalled(users.find({ id: 12 }), done, queryStub);
          });
        });

        describe('.fineOne', function() {
          it('should return the first member in the results', function(done) {
            Promise.props({
              all: users.find({ id: 1 }),
              one: users.findOne({ id: 1 })
            }).then(function(res) {
              res.one.should.eql(res.all[0]);
              done();
            });
          });
        });

        describe('.findById()', function() {
          it('should get id as a string and call `queryDocuments`', function(done) {
            assertCalled(users.findById('foo'), done, queryStub);
          });
        });

        describe('.findOneAndRemove', function() {
          var removeStub;
          beforeEach(function() {
            removeStub = stub(DocumentDB.prototype, 'deleteDocument', applyCallback);
          });

          it('should get the first result and call `deleteDocument`', function(done) {
            assertCalled(users.findOneAndRemove({ id: 2 }), done, removeStub, [DOC_MOCK._self]);
          });

          afterEach(function() {
            removeStub.restore();
          });
        });

        describe('.findOneAndModify', function() {
          var updateStub;
          beforeEach(function() {
            updateStub = stub(DocumentDB.prototype, 'replaceDocument', applyCallback);
          });

          it('should get the first result and call `replaceDocument` extened', function(done) {
            var update = users.findOneAndModify({ id: 2 }, { name: 3 });
            assertCalled(update, done, updateStub, [DOC_MOCK._self]);
          });

          afterEach(function() {
            updateStub.restore();
          });
        });

        describe('.create() | .insert()', function() {
          var createStub;
          beforeEach(function() {
            createStub = stub(DocumentDB.prototype, 'createDocument', applyCallback);
          });

          it('should not get an object params and complete it to empty object', function(done) {
            assertCalled(users.create(), done, createStub, [COLL_MOCK._self, {}]);
          });

          it('should get an object params and call `createDocument` with it', function(done) {
            var o1 = { id: 1, name: 'Ariel M.' };
            assertCalled(users.create(o1), done, createStub, [COLL_MOCK._self, o1]);
          });

          afterEach(function() {
            createStub.restore();
          });
        });

        describe('Groups', function() {
          var results = [{_self: 1}, {_self: 2}, {_self: 3}, {_self: 4}];
          beforeEach(function() {
            readStub.returns(toArray([null, results]));
          });

          // #1
          describe('.findAndRemove()', function() {
            var removeStub;
            beforeEach(function() {
              removeStub = stub(DocumentDB.prototype, 'deleteDocument', applyCallback);
            });

            it('should call `deleteDocument` to each result', function(done) {
              users.findAndRemove({})
                .then(function() {
                  removeStub.callCount.should.eql(results.length);
                  done()
                });
            });

            afterEach(function() {
              removeStub.restore();
            });
          });

          // #2
          describe('.findAndModify()', function() {
            var updateStub;
            beforeEach(function() {
              updateStub = stub(DocumentDB.prototype, 'replaceDocument', applyCallback);
            });

            it('should call `replaceDocument` with each result', function(done) {
              users.findAndModify({}, {})
                .then(function() {
                  updateStub.callCount.should.eql(results.length);
                  done();
                });
            });

            it('should have an aliases `update`', function() {
              users.update.should.eql(users.findAndModify);
            });

            afterEach(function() {
              updateStub.restore();
            });
          });
        });

        afterEach(function() {
          readStub.restore();
          queryStub.restore();
        });
      });

      afterEach(function() {
        queryStub.restore();
        readStub.restore();
        DocumentDB.prototype.queryDatabases.restore();
      });
    });
  });
});<commit_msg>test(findOrCreate): collectionManager<commit_after>'use strict';
/*global beforeEach, afterEach, stub*/
var DocumentDB = require('documentdb').DocumentClient;
var DoQmentDB  = require('..');
var stub       = require('sinon').stub;
var should     = require('should');
var Promise    = require('bluebird');

// Helpers: Mocks, and DocumentDB behavior
var DB_MOCK   = { _self: '/self/db',  _colls: '/colls' };
var COLL_MOCK = { _self: '/self/col', _docs:  '/docs'  };
var DOC_MOCK  = { _self: '/self/doc', _id:    '54123'  };
function toArray(args) {
  return { toArray: function(fb) { fb.apply(null, args);} }
}
function applyCallback(o1, o2, cb) { return (cb||o2)() }

// Assertions helpers
function assertCalled(q, done, toCalled, withArgs) {
  q.then(function(res) {
    (toCalled.called).should.eql(true);
    // Test `calledWith` with the given arguments.
    if(withArgs) {
      toCalled.calledWith.apply(toCalled, withArgs).should.eql(true);
    }
    done();
  });
}

describe('DoqmentDB', function() {
  describe('DatabaseManager', function() {

    describe('creating `new` DatabaseManager', function() {
      it('should get a not DocumentDB instance(connection) and throw', function() {
        (function() {
          new DoQmentDB({});
        }).should.throw();
      });

      it('should get DocumentDB instance(connection) and return an instance', function() {
        (new DoQmentDB(new DocumentDB('name', {}), 'dbName'))
          .should.be.an.instanceOf(Object)
      });
    });

    // findOrCreate behavior
    describe('`using` dynamically', function() {
      var connection = new DocumentDB('host', { masterKey: 'key' });
      var dbManager  = new DoQmentDB(connection, '');

      beforeEach(function() {
        stub(DocumentDB.prototype, 'createDatabase', function(obj, fb) {fb()});
        var stub1 = stub(DocumentDB.prototype, 'queryDatabases');
        stub1.onCall(0).returns(toArray([null, []]));
        stub1.onCall(1).returns(toArray([null, [{}]]));
      });

      describe('working with non-existing(in Azure) database', function() {
        it('should call `createDatabase` with database name', function(done) {
          dbManager.manager.findOrCreate({ id: dbManager.database })
            .then(function(result) {
              DocumentDB.prototype.createDatabase.calledWith({ id: dbManager.database })
                .should.eql(true);
              done();
            });
        });
      });

      describe('working with existing(in Azure) database', function() {
        it('should return the db and not call `createDatabase`', function(done) {
          dbManager.manager.findOrCreate({ id: dbManager.database })
            .then(function(result) {
              DocumentDB.prototype.createDatabase.calledTwice.should.eql(false);
              done();
            });
        });
      });

      afterEach(function() {
        DocumentDB.prototype.createDatabase.restore();
        DocumentDB.prototype.queryDatabases.restore();
      });
    });

    // Public api, .find(), .create(), ...
    describe('Public API', function() {
      var connection = new DocumentDB('host', { masterKey: 'key' });
      var dbManager  = new DoQmentDB(connection, '');
      var queryStub, readStub;

      beforeEach(function() {
        // _getDatabase
        stub(DocumentDB.prototype,  'queryDatabases')
          .returns(toArray([null, DB_MOCK]));
        // (query|read)Collections
        queryStub = stub(DocumentDB.prototype, 'queryCollections');
        readStub = stub(DocumentDB.prototype,  'readCollections');
        queryStub.returns(toArray([null, [COLL_MOCK]]));
        readStub.returns(toArray([null, [1, 2]]));
      });

      describe('.getDatabase()', function() {
        it('should return the used collection, call `queryCollection`', function(done) {
          dbManager.getDatabase()
            .then(function(db) {
              db.should.eql(DB_MOCK);
              DocumentDB.prototype.queryDatabases.called.should.eql(true);
              done();
            });
        });
      });

      describe('.find()', function() {
        it('should get object properties and call `queryCollections`', function(done) {
          assertCalled(dbManager.find({ id: 'name' }), done, queryStub);
        });

        //TODO: calledWith assertion

        it('should get an empty object and call `readCollections`', function(done) {
          assertCalled(dbManager.find({}), done, readStub);
        });

        it('should get an `undefined` params and call `readCollections`', function(done) {
          assertCalled(dbManager.find(undefined), done, readStub);
        });
      });

      describe('.findById()', function() {
        it('should get id as a string and call `queryCollection`', function(done) {
          assertCalled(dbManager.findById('foo'), done, queryStub);
        });
      });

      describe('.findAndCreate()', function() {
        var createStub;
        beforeEach(function() {
          createStub = stub(DocumentDB.prototype, 'createCollection', applyCallback);
          queryStub.returns(toArray([null, []]));
        });
        it('should call `createCollection` if it\'s not exist', function(done) {
          var users = { id: '31' };
          var args  = [DB_MOCK._self, users];
          assertCalled(dbManager.findOrCreate(users), done, createStub, args);
        });
        afterEach(function() {
          createStub.restore();
        });
      });

      describe('.insert() | .create()', function() {
        var createStub;
        beforeEach(function() {
          createStub = stub(DocumentDB.prototype, 'createCollection', applyCallback);
        });

        it('should get name and call `createCollection`', function(done) {
          var name = 'foo';
          var args = [DB_MOCK._self,  { id: name }];
          assertCalled(dbManager.create(name), done, createStub, args);
        });

        it('should have aliases', function() {
          dbManager.create.should.eql(dbManager.insert);
        });

        afterEach(function() {
          createStub.restore();
        });
      });

      describe('.remove()', function() {
        var removeStub;
        beforeEach(function() {
          removeStub = stub(DocumentDB.prototype, 'deleteCollection', applyCallback);
          queryStub.returns(toArray([null, [COLL_MOCK]]));
        });

        it('should get collection id and call `deleteCollection`', function(done) {
          assertCalled(dbManager.remove('id'), done, removeStub, [COLL_MOCK._self]);
        });
      });

      describe('.use()', function() {
        it('should return an object immediately(not async operation)', function() {
          dbManager.use('users').should.be.type('object');
        });

        it('should return instance of collectionManager', function() {
          dbManager.use('users').constructor.name.should.eql('Collection');
        });
      });

      describe('CollectionManager', function() {
        var users = dbManager.use('users');
        var readStub, queryStub;

        beforeEach(function() {
          readStub = stub(DocumentDB.prototype, 'readDocuments');
          queryStub = stub(DocumentDB.prototype, 'queryDocuments');
          queryStub.returns(toArray([null, [DOC_MOCK]]));
          readStub.returns(toArray([null, [1, 2]]));
        });

        describe('.getCollection()', function() {
          it('should return the used collection, call `queryCollection`', function(done) {
            users.getCollection()
              .then(function(coll) {
                coll.should.eql(COLL_MOCK);
                DocumentDB.prototype.queryCollections.called.should.eql(true);
                done();
              });
          });
        });

        describe('.find()', function() {
          it('should get empty object and call `readDocuments`', function(done) {
            assertCalled(users.find({}), done, readStub);
          });

          it('should get undefined param and call `readDocuments`', function(done) {
            assertCalled(users.find(undefined), done, readStub);
          });

          it('should get object params and call `queryDocuments`', function(done) {
            assertCalled(users.find({ id: 12 }), done, queryStub);
          });
        });

        describe('.fineOne', function() {
          it('should return the first member in the results', function(done) {
            Promise.props({
              all: users.find({ id: 1 }),
              one: users.findOne({ id: 1 })
            }).then(function(res) {
              res.one.should.eql(res.all[0]);
              done();
            });
          });
        });

        describe('.findById()', function() {
          it('should get id as a string and call `queryDocuments`', function(done) {
            assertCalled(users.findById('foo'), done, queryStub);
          });
        });

        describe('.findOneAndRemove', function() {
          var removeStub;
          beforeEach(function() {
            removeStub = stub(DocumentDB.prototype, 'deleteDocument', applyCallback);
          });

          it('should get the first result and call `deleteDocument`', function(done) {
            assertCalled(users.findOneAndRemove({ id: 2 }), done, removeStub, [DOC_MOCK._self]);
          });

          afterEach(function() {
            removeStub.restore();
          });
        });

        describe('.findOneAndModify', function() {
          var updateStub;
          beforeEach(function() {
            updateStub = stub(DocumentDB.prototype, 'replaceDocument', applyCallback);
          });

          it('should get the first result and call `replaceDocument` extened', function(done) {
            var update = users.findOneAndModify({ id: 2 }, { name: 3 });
            assertCalled(update, done, updateStub, [DOC_MOCK._self]);
          });

          afterEach(function() {
            updateStub.restore();
          });
        });

        describe('.create() | .insert()', function() {
          var createStub;
          beforeEach(function() {
            createStub = stub(DocumentDB.prototype, 'createDocument', applyCallback);
          });

          it('should not get an object params and complete it to empty object', function(done) {
            assertCalled(users.create(), done, createStub, [COLL_MOCK._self, {}]);
          });

          it('should get an object params and call `createDocument` with it', function(done) {
            var o1 = { id: 1, name: 'Ariel M.' };
            assertCalled(users.create(o1), done, createStub, [COLL_MOCK._self, o1]);
          });

          afterEach(function() {
            createStub.restore();
          });
        });

        describe('Groups', function() {
          var results = [{_self: 1}, {_self: 2}, {_self: 3}, {_self: 4}];
          beforeEach(function() {
            readStub.returns(toArray([null, results]));
          });

          // #1
          describe('.findAndRemove()', function() {
            var removeStub;
            beforeEach(function() {
              removeStub = stub(DocumentDB.prototype, 'deleteDocument', applyCallback);
            });

            it('should call `deleteDocument` to each result', function(done) {
              users.findAndRemove({})
                .then(function() {
                  removeStub.callCount.should.eql(results.length);
                  done()
                });
            });

            afterEach(function() {
              removeStub.restore();
            });
          });

          // #2
          describe('.findAndModify()', function() {
            var updateStub;
            beforeEach(function() {
              updateStub = stub(DocumentDB.prototype, 'replaceDocument', applyCallback);
            });

            it('should call `replaceDocument` with each result', function(done) {
              users.findAndModify({}, {})
                .then(function() {
                  updateStub.callCount.should.eql(results.length);
                  done();
                });
            });

            it('should have an aliases `update`', function() {
              users.update.should.eql(users.findAndModify);
            });

            afterEach(function() {
              updateStub.restore();
            });
          });
        });

        describe('.findOrCreate()', function() {
          var createStub;
          beforeEach(function() {
            createStub = stub(DocumentDB.prototype, 'createDocument', applyCallback);
          });

          it('should call `createDocument` if it not exist', function(done) {
            queryStub.returns(toArray([null, []]));
            assertCalled(users.findOrCreate({id:1}), done, createStub);
          });

          it('should return the result if it exist', function(done) {
            var user = { id: 3 };
            queryStub.returns(toArray([null, [user]]));
            users.findOrCreate(user)
              .then(function(res) {
                res.should.eql(user);
                createStub.called.should.eql(false);
                done();
              })
          });

          afterEach(function() {
            createStub.restore();
          });
        });

        afterEach(function() {
          readStub.restore();
          queryStub.restore();
        });
      });

      afterEach(function() {
        queryStub.restore();
        readStub.restore();
        DocumentDB.prototype.queryDatabases.restore();
      });
    });
  });
});<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.menu',
  name: 'AbstractMenu',

  methods: [
    function setMenuId(id) {
      if (window.location.hash.substr(1) != id){
        window.location.hash = this.id;
      }
    },
    function launch(X, menu) {
      this.setMenuId(menu.id);
      X.stack.push(this.createView(X, menu));
    }
  ]
});
<commit_msg>set menu only items to change browser hash<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.menu',
  name: 'AbstractMenu',

  methods: [
    function setMenuId(id) {
      if (window.location.hash.substr(1) != id){
        window.location.hash = id;
      }
    },
    function launch(X, menu) {
      this.setMenuId(menu.id);
      X.stack.push(this.createView(X, menu));
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

 foam.CLASS({
  package: 'foam.u2.view',
  name: 'ScrollTableView',
  extends: 'foam.u2.Element',

  imports: [
    'memento?',
    'stack'
  ],

  exports: [
    'as summaryView',
    'dblclick',
    'memento'
  ],

  requires: [
    'foam.dao.FnSink',
    'foam.mlang.sink.Count',
    'foam.u2.view.TableView',
    'foam.comics.v2.DAOControllerConfig',
    'foam.nanos.controller.Memento'
  ],

  css: `
    ^ {
      overflow: scroll;
      padding-bottom: 20px;
    }
    ^table {
      position: relative;
    }
    ^table  .foam-u2-view-TableView-thead {
      z-index: 1;
      overflow: visible;
    }
  `,

  constants: [
    {
      type: 'Float',
      name: 'MIN_TOP_PAGE_PROGRESS',
      documentation: `
        If the "top" page isn't scrolled by at least this amount, make
        the top page the previous page.
        i.e. If the page that's currently in view is only scrolled 10% of the way,
        we consider the page that's above it to be the "top" page. If the page
        that's on screen is scrolled 51% of the way, then it is considered the top
        page.
      `,
      value: 0.5
    },
    {
      type: 'Integer',
      name: 'NUM_PAGES_TO_RENDER',
      value: 3
    },
    {
      type: 'Integer',
      name: 'TABLE_HEAD_HEIGHT',
      value: 51
    }
  ],

  properties: [
    {
      class: 'foam.dao.DAOProperty',
      name: 'data'
    },
    'columns',
    {
      class: 'FObjectArray',
      of: 'foam.core.Action',
      name: 'contextMenuActions'
    },
    {
      class: 'Int',
      name: 'daoCount'
    },
    'selection',
    'disableUserSelection',
    {
      class: 'Boolean',
      name: 'editColumnsEnabled',
      documentation: `
        Set to true if users should be allowed to choose which columns to use.
      `,
      value: true
    },
    {
      class: 'Int',
      name: 'rowHeight',
      documentation: 'The height of one row of the table in px.',
      value: 49
    },
    {
      name: 'table_',
      documentation: `
        A reference to the table element we use in various calculations.
      `
    },
    {
      type: 'Int',
      name: 'scrollHeight',
      expression: function(daoCount, rowHeight) {
        return rowHeight * daoCount + this.TABLE_HEAD_HEIGHT;
      }
    },
    {
      type: 'Int',
      name: 'pageSize',
      value: 30,
      documentation: 'The number of items in each "page". There are three pages.'
    },
    {
      type: 'Boolean',
      name: 'enableDynamicTableHeight',
      value: true,
    },
    {
      class: 'Boolean',
      name: 'multiSelectEnabled',
      documentation: 'Pass through to UnstyledTableView.'
    },
    {
      class: 'Map',
      name: 'selectedObjects',
      documentation: `
        The objects selected by the user when multi-select support is enabled.
        It's a map where the key is the object id and the value is the object.
        Here we simply bind it to the selectedObjects property on TableView.
      `
    },
    {
      class: 'Int',
      name: 'scrollPos_'
    },
    {
      class: 'Int',
      name: 'numPages_',
      expression: function(daoCount, pageSize) {
        return Math.ceil(daoCount / pageSize);
      }
    },
    {
      class: 'Int',
      name: 'currentTopPage_',
      expression: function(numPages_, scrollPos_, scrollHeight) {
        var scrollPercent = scrollPos_ / scrollHeight;
        var topPage = Math.floor(scrollPercent * numPages_);
        var topPageProgress = (scrollPercent * numPages_) % 1;
        if ( topPageProgress < this.MIN_TOP_PAGE_PROGRESS ) topPage = topPage - 1;
        return Math.min(Math.max(0, numPages_ - this.NUM_PAGES_TO_RENDER), Math.max(0, topPage));
      }
    },
    {
      class: 'Map',
      name: 'renderedPages_'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.comics.v2.DAOControllerConfig',
      name: 'config',
      factory: function() {
        return this.DAOControllerConfig.create({ dao: this.data });
      }
    },
    {
      name: 'dblClickListenerAction',
      factory: () => {
        return function(obj, id) {
          if ( ! this.stack ) return;

          this.stack.push({
            class: 'foam.comics.v2.DAOSummaryView',
            data: obj,
            config: this.config,
            idOfRecord: id
          }, this);
        }
      }
    },
    'currentMemento',
    {
      class: 'Boolean',
      name: 'isInit'
    }
  ],

  reactions: [
    ['', 'propertyChange.currentTopPage_', 'updateRenderedPages_'],
    ['', 'propertyChange.table_',          'updateRenderedPages_']
  ],

  methods: [
    function init() {
      this.onDetach(this.data$proxy.listen(this.FnSink.create({ fn: this.updateCount })));
      this.updateCount();

      if ( this.memento )
        this.currentMemento$ = this.memento.tail$;
      else if ( this.memento != undefined )
        this.currentMemento$ = this.memento$;
    },

    function initE() {
      if ( this.currentMemento ) {
        var mementoHead = this.currentMemento.head;

        var of = this.data.of || this.config.of;
        if ( mementoHead === 'Create' && of ) {
          this.stack.push({
            class: 'foam.comics.v2.DAOCreateView',
            data: ((this.config.factory && this.config.factory$cls) ||  this.data.of).create({ mode: 'create'}, this),
            config$: this.config$,
            of: of
          }, this.__subContext__);
          return;
        }

        var id = mementoHead;
        if ( of ) {
          id = of.ID.fromString(mementoHead);
        }

        this.stack.push({
          class: 'foam.comics.v2.DAOSummaryView',
          data: null,
          config: this.config,
          idOfRecord: id
        }, this);
        return;
      }

      this.
        addClass(this.myClass()).
        on('scroll', this.onScroll).
        start(this.TableView, {
          data: foam.dao.NullDAO.create({of: this.data.of}),
          columns: this.columns,
          contextMenuActions: this.contextMenuActions,
          selection$: this.selection$,
          editColumnsEnabled: this.editColumnsEnabled,
          disableUserSelection: this.disableUserSelection,
          multiSelectEnabled: this.multiSelectEnabled,
          selectedObjects$: this.selectedObjects$
        }, this.table_$).
          addClass(this.myClass('table')).
          style({
            height: this.scrollHeight$.map(h => h + 'px')
          }).
        end();

      /*
        to be used in cases where we don't want the whole table to
        take the whole page (i.e. we need multiple tables)
        and enableDynamicTableHeight can be switched off
      */
      if ( this.enableDynamicTableHeight ) {
        this.onDetach(this.onload.sub(this.updateTableHeight));
        window.addEventListener('resize', this.updateTableHeight);
        this.onDetach(() => {
          window.removeEventListener('resize', this.updateTableHeight);
        });
      }
    }
  ],

  listeners: [
    {
      name: 'refresh',
      isFramed: true,
      code: function() {
        Object.keys(this.renderedPages_).forEach(i => {
          this.renderedPages_[i].remove();
          delete this.renderedPages_[i];
        });
        this.updateRenderedPages_();
        if ( this.el() && ! this.isInit && this.memento.paramsObj.r ) {
          var scroll = this.memento.paramsObj.r * this.rowHeight;
          scroll = scroll >= this.rowHeight && scroll < this.scrollHeight ? scroll : 0;

          document.getElementById(this.id).scrollTop = scroll;

          this.isInit = true;
        } else if ( this.el() ) this.el().scrollTop = 0;
      }
    },
    {
      name: 'updateCount',
      isFramed: true,
      code: function() {
        return this.data$proxy.select(this.Count.create()).then((s) => {
          this.daoCount = s.value;
          this.refresh();
        });
      }
    },
    {
      name: 'updateRenderedPages_',
      isFramed: true,
      code: function() {
        if ( ! this.table_ ) return;

        // Remove any pages that are no longer on screen to save on
        // the amount of DOM we add to the page.
        Object.keys(this.renderedPages_).forEach(i => {
          if ( i >= this.currentTopPage_ && i < this.currentTopPage_ + this.NUM_PAGES_TO_RENDER ) return;
          this.renderedPages_[i].remove();
          delete this.renderedPages_[i];
        });

        // Add any pages that are not already rendered.
        for ( var i = 0; i < Math.min(this.numPages_, this.NUM_PAGES_TO_RENDER) ; i++) {
          var page = this.currentTopPage_ + i;
          if ( this.renderedPages_[page] ) continue;
          var dao = this.data$proxy.limit(this.pageSize).skip(page * this.pageSize);
          var tbody = this.table_.slotE_(this.table_.rowsFrom(dao));
          tbody.style({
            position: 'absolute',
            width: '100%',
            top: this.TABLE_HEAD_HEIGHT + page * this.pageSize * this.rowHeight + 'px'
          });
          this.table_.add(tbody);
          this.renderedPages_[page] = tbody;
        }
      }
    },
    {
      name: 'onScroll',
      isFramed: true,
      code: function(e) {
        this.scrollPos_ = e.target.scrollTop;
        this.memento.paramsObj.r = this.scrollPos_ >= this.rowHeight && this.scrollPos_ < this.scrollHeight ? Math.floor( this.scrollPos_  / this.rowHeight) : 0;
        this.memento.paramsObj = foam.Object.clone(this.memento.paramsObj);
      }
    },
    {
      name: 'updateTableHeight',
      code: function() {
        // Find the distance from the top of the table to the top of the screen.
        var distanceFromTop = this.el().getBoundingClientRect().y;

        // Calculate the remaining space we have to make use of.
        var remainingSpace = window.innerHeight - distanceFromTop;

        // TODO: Do we want to do this?
        // Leave space for the footer.
        remainingSpace -= 44;

        this.style({ height: `${remainingSpace}px` });
      }
    },
    function dblclick(obj, id) {
      this.dblClickListenerAction(obj, id);
    }
  ]
});
<commit_msg>Fixing current memento being null in detail view 01282020 (#4698)<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

 foam.CLASS({
  package: 'foam.u2.view',
  name: 'ScrollTableView',
  extends: 'foam.u2.Element',

  imports: [
    'memento?',
    'stack'
  ],

  exports: [
    'as summaryView',
    'dblclick',
    'memento'
  ],

  requires: [
    'foam.dao.FnSink',
    'foam.mlang.sink.Count',
    'foam.u2.view.TableView',
    'foam.comics.v2.DAOControllerConfig',
    'foam.nanos.controller.Memento'
  ],

  css: `
    ^ {
      overflow: scroll;
      padding-bottom: 20px;
    }
    ^table {
      position: relative;
    }
    ^table  .foam-u2-view-TableView-thead {
      z-index: 1;
      overflow: visible;
    }
  `,

  constants: [
    {
      type: 'Float',
      name: 'MIN_TOP_PAGE_PROGRESS',
      documentation: `
        If the "top" page isn't scrolled by at least this amount, make
        the top page the previous page.
        i.e. If the page that's currently in view is only scrolled 10% of the way,
        we consider the page that's above it to be the "top" page. If the page
        that's on screen is scrolled 51% of the way, then it is considered the top
        page.
      `,
      value: 0.5
    },
    {
      type: 'Integer',
      name: 'NUM_PAGES_TO_RENDER',
      value: 3
    },
    {
      type: 'Integer',
      name: 'TABLE_HEAD_HEIGHT',
      value: 51
    }
  ],

  properties: [
    {
      class: 'foam.dao.DAOProperty',
      name: 'data'
    },
    'columns',
    {
      class: 'FObjectArray',
      of: 'foam.core.Action',
      name: 'contextMenuActions'
    },
    {
      class: 'Int',
      name: 'daoCount'
    },
    'selection',
    'disableUserSelection',
    {
      class: 'Boolean',
      name: 'editColumnsEnabled',
      documentation: `
        Set to true if users should be allowed to choose which columns to use.
      `,
      value: true
    },
    {
      class: 'Int',
      name: 'rowHeight',
      documentation: 'The height of one row of the table in px.',
      value: 49
    },
    {
      name: 'table_',
      documentation: `
        A reference to the table element we use in various calculations.
      `
    },
    {
      type: 'Int',
      name: 'scrollHeight',
      expression: function(daoCount, rowHeight) {
        return rowHeight * daoCount + this.TABLE_HEAD_HEIGHT;
      }
    },
    {
      type: 'Int',
      name: 'pageSize',
      value: 30,
      documentation: 'The number of items in each "page". There are three pages.'
    },
    {
      type: 'Boolean',
      name: 'enableDynamicTableHeight',
      value: true,
    },
    {
      class: 'Boolean',
      name: 'multiSelectEnabled',
      documentation: 'Pass through to UnstyledTableView.'
    },
    {
      class: 'Map',
      name: 'selectedObjects',
      documentation: `
        The objects selected by the user when multi-select support is enabled.
        It's a map where the key is the object id and the value is the object.
        Here we simply bind it to the selectedObjects property on TableView.
      `
    },
    {
      class: 'Int',
      name: 'scrollPos_'
    },
    {
      class: 'Int',
      name: 'numPages_',
      expression: function(daoCount, pageSize) {
        return Math.ceil(daoCount / pageSize);
      }
    },
    {
      class: 'Int',
      name: 'currentTopPage_',
      expression: function(numPages_, scrollPos_, scrollHeight) {
        var scrollPercent = scrollPos_ / scrollHeight;
        var topPage = Math.floor(scrollPercent * numPages_);
        var topPageProgress = (scrollPercent * numPages_) % 1;
        if ( topPageProgress < this.MIN_TOP_PAGE_PROGRESS ) topPage = topPage - 1;
        return Math.min(Math.max(0, numPages_ - this.NUM_PAGES_TO_RENDER), Math.max(0, topPage));
      }
    },
    {
      class: 'Map',
      name: 'renderedPages_'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.comics.v2.DAOControllerConfig',
      name: 'config',
      factory: function() {
        return this.DAOControllerConfig.create({ dao: this.data });
      }
    },
    {
      name: 'dblClickListenerAction',
      factory: () => {
        return function(obj, id) {
          if ( ! this.stack ) return;

          this.stack.push({
            class: 'foam.comics.v2.DAOSummaryView',
            data: obj,
            config: this.config,
            idOfRecord: id
          }, this);
        }
      }
    },
    'currentMemento',
    {
      class: 'Boolean',
      name: 'isInit'
    }
  ],

  reactions: [
    ['', 'propertyChange.currentTopPage_', 'updateRenderedPages_'],
    ['', 'propertyChange.table_',          'updateRenderedPages_']
  ],

  methods: [
    function init() {
      this.onDetach(this.data$proxy.listen(this.FnSink.create({ fn: this.updateCount })));
      this.updateCount();

      if ( this.memento )
        this.currentMemento$ = this.memento.tail$;
      else if ( this.memento != undefined )
        this.currentMemento$ = this.memento$;
    },

    function initE() {
      if ( this.currentMemento ) {
        var mementoHead = this.currentMemento.head;

        var of = this.data.of || this.config.of;
        if ( mementoHead === 'Create' && of ) {
          this.stack.push({
            class: 'foam.comics.v2.DAOCreateView',
            data: ((this.config.factory && this.config.factory$cls) ||  this.data.of).create({ mode: 'create'}, this),
            config$: this.config$,
            of: of
          }, this.__subContext__);
          return;
        }

        var id = mementoHead;
        if ( of ) {
          id = of.ID.fromString(mementoHead);
        }

        this.stack.push({
          class: 'foam.comics.v2.DAOSummaryView',
          data: null,
          config: this.config,
          idOfRecord: id
        }, this);
        return;
      }

      this.
        addClass(this.myClass()).
        on('scroll', this.onScroll).
        start(this.TableView, {
          data: foam.dao.NullDAO.create({of: this.data.of}),
          columns: this.columns,
          contextMenuActions: this.contextMenuActions,
          selection$: this.selection$,
          editColumnsEnabled: this.editColumnsEnabled,
          disableUserSelection: this.disableUserSelection,
          multiSelectEnabled: this.multiSelectEnabled,
          selectedObjects$: this.selectedObjects$
        }, this.table_$).
          addClass(this.myClass('table')).
          style({
            height: this.scrollHeight$.map(h => h + 'px')
          }).
        end();

      /*
        to be used in cases where we don't want the whole table to
        take the whole page (i.e. we need multiple tables)
        and enableDynamicTableHeight can be switched off
      */
      if ( this.enableDynamicTableHeight ) {
        this.onDetach(this.onload.sub(this.updateTableHeight));
        window.addEventListener('resize', this.updateTableHeight);
        this.onDetach(() => {
          window.removeEventListener('resize', this.updateTableHeight);
        });
      }
    }
  ],

  listeners: [
    {
      name: 'refresh',
      isFramed: true,
      code: function() {
        Object.keys(this.renderedPages_).forEach(i => {
          this.renderedPages_[i].remove();
          delete this.renderedPages_[i];
        });
        this.updateRenderedPages_();
        if ( this.el() && ! this.isInit && this.memento && this.memento.paramsObj.r ) {
          var scroll = this.memento.paramsObj.r * this.rowHeight;
          scroll = scroll >= this.rowHeight && scroll < this.scrollHeight ? scroll : 0;

          document.getElementById(this.id).scrollTop = scroll;

          this.isInit = true;
        } else if ( this.el() ) this.el().scrollTop = 0;
      }
    },
    {
      name: 'updateCount',
      isFramed: true,
      code: function() {
        return this.data$proxy.select(this.Count.create()).then((s) => {
          this.daoCount = s.value;
          this.refresh();
        });
      }
    },
    {
      name: 'updateRenderedPages_',
      isFramed: true,
      code: function() {
        if ( ! this.table_ ) return;

        // Remove any pages that are no longer on screen to save on
        // the amount of DOM we add to the page.
        Object.keys(this.renderedPages_).forEach(i => {
          if ( i >= this.currentTopPage_ && i < this.currentTopPage_ + this.NUM_PAGES_TO_RENDER ) return;
          this.renderedPages_[i].remove();
          delete this.renderedPages_[i];
        });

        // Add any pages that are not already rendered.
        for ( var i = 0; i < Math.min(this.numPages_, this.NUM_PAGES_TO_RENDER) ; i++) {
          var page = this.currentTopPage_ + i;
          if ( this.renderedPages_[page] ) continue;
          var dao = this.data$proxy.limit(this.pageSize).skip(page * this.pageSize);
          var tbody = this.table_.slotE_(this.table_.rowsFrom(dao));
          tbody.style({
            position: 'absolute',
            width: '100%',
            top: this.TABLE_HEAD_HEIGHT + page * this.pageSize * this.rowHeight + 'px'
          });
          this.table_.add(tbody);
          this.renderedPages_[page] = tbody;
        }
      }
    },
    {
      name: 'onScroll',
      isFramed: true,
      code: function(e) {
        this.scrollPos_ = e.target.scrollTop;
        if ( this.memento ) {
          this.memento.paramsObj.r = this.scrollPos_ >= this.rowHeight && this.scrollPos_ < this.scrollHeight ? Math.floor( this.scrollPos_  / this.rowHeight) : 0;
          this.memento.paramsObj = foam.Object.clone(this.memento.paramsObj);
        }
      }
    },
    {
      name: 'updateTableHeight',
      code: function() {
        // Find the distance from the top of the table to the top of the screen.
        var distanceFromTop = this.el().getBoundingClientRect().y;

        // Calculate the remaining space we have to make use of.
        var remainingSpace = window.innerHeight - distanceFromTop;

        // TODO: Do we want to do this?
        // Leave space for the footer.
        remainingSpace -= 44;

        this.style({ height: `${remainingSpace}px` });
      }
    },
    function dblclick(obj, id) {
      this.dblClickListenerAction(obj, id);
    }
  ]
});
<|endoftext|>


<commit_before>/* global describe, it, beforeEach */
/* jslint node: true, esnext: true */
"use strict";

const chai = require('chai'),
	assert = chai.assert,
	expect = chai.expect,
	should = chai.should(),
	events = require('events'),
	stepPassThrough = require('kronos-step-passthrough'),
	testStep = require('kronos-test-step'),
	flow = require('../lib/flow'),
	step = require('kronos-step');

// ---------------------------
// Create a mock manager
// ---------------------------
const manager = testStep.managerMock;

// register the flow
require('../index').registerWithManager(manager);

/*
// register the passthroughStep
stepPassThrough.registerWithManager(manager);

// create 5 copies and register them also
for (let i = 1; i < 6; i++) {
	manager.registerStepImplementation(manager.getStepInstance({
		"type": "kronos-step-passthrough",
		"name": "Step_" + i
	}));
}
*/

/**
 * A dummy step for flow testing. The step will execute the stop
 * and start command after the given amaount of time
 * @param stepName The name of this step
 * @param time The time in millisecond it will last until the promise is fullfilled
 */
manager.registerStepImplementation(Object.assign({}, step.Step, {
	"name": "slow-start",

	initialize(manager, scopeReporter, name, stepConfiguration, endpoints, props) {
		props.time = {
			value: stepConfiguration.time
		};
		//console.log(`DummyStep: time: ${stepConfiguration.time}`);
	},

	_start() {
		const self = this;
		return new Promise(function (fulfill, reject) {
			setTimeout(() => {
					//console.log(`Started the step '${self.name}'`);
					fulfill(self);
				},
				self.time);
		});
	},

	// TODO why does "can be stopped while starting" not work
	__stop() {
		const self = this;
		return new Promise(function (fulfill, reject) {
			setTimeout(() => {
				//console.log(`Stopped the step '${self.name}'`);
				fulfill(self);
			}, self.time);
		});
	}
}));

describe('livecycle', function () {

	// load the content of the flow definition
	flow.loadFlows(manager, manager.scopeReporter, {
		"myFlowName": {
			"type": "kronos-flow",
			"steps": {
				"slowInbound": {
					"type": "slow-start",
					"time": 10
				},
				"normal": {
					"type": "slow-start",
					"time": 5
				},
				"slowOutbound": {
					"type": "slow-start",
					"time": 70
				}
			}
		}
	});

	const testFlow = manager.getFlow("myFlowName");

	testStep.checkStepLivecycle(manager, testFlow);
});
<commit_msg>test: reenable static part too<commit_after>/* global describe, it, beforeEach */
/* jslint node: true, esnext: true */
"use strict";

const chai = require('chai'),
	assert = chai.assert,
	expect = chai.expect,
	should = chai.should(),
	events = require('events'),
	stepPassThrough = require('kronos-step-passthrough'),
	testStep = require('kronos-test-step'),
	flow = require('../lib/flow'),
	step = require('kronos-step');

// ---------------------------
// Create a mock manager
// ---------------------------
const manager = testStep.managerMock;

// register the flow
require('../index').registerWithManager(manager);

/*
// register the passthroughStep
stepPassThrough.registerWithManager(manager);

// create 5 copies and register them also
for (let i = 1; i < 6; i++) {
	manager.registerStepImplementation(manager.getStepInstance({
		"type": "kronos-step-passthrough",
		"name": "Step_" + i
	}));
}
*/

/**
 * A dummy step for flow testing. The step will execute the stop
 * and start command after the given amaount of time
 * @param stepName The name of this step
 * @param time The time in millisecond it will last until the promise is fullfilled
 */
manager.registerStepImplementation(Object.assign({}, step.Step, {
	"name": "slow-start",

	initialize(manager, scopeReporter, name, stepConfiguration, endpoints, props) {
		props.time = {
			value: stepConfiguration.time
		};
		//console.log(`DummyStep: time: ${stepConfiguration.time}`);
	},

	_start() {
		const self = this;
		return new Promise(function (fulfill, reject) {
			setTimeout(() => {
					//console.log(`Started the step '${self.name}'`);
					fulfill(self);
				},
				self.time);
		});
	},

	// TODO why does "can be stopped while starting" not work
	__stop() {
		const self = this;
		return new Promise(function (fulfill, reject) {
			setTimeout(() => {
				//console.log(`Stopped the step '${self.name}'`);
				fulfill(self);
			}, self.time);
		});
	}
}));

describe('flow', function () {
	// load the content of the flow definition
	flow.loadFlows(manager, manager.scopeReporter, {
		"myFlowName": {
			"type": "kronos-flow",
			"steps": {
				"slowInbound": {
					"type": "slow-start",
					"time": 10
				},
				"normal": {
					"type": "slow-start",
					"time": 5
				},
				"slowOutbound": {
					"type": "slow-start",
					"time": 70
				}
			}
		}
	});

	const f = manager.getFlow("myFlowName");

	describe('static', function () {
		testStep.checkStepStatic(manager, f);
	});

	describe('livecycle', function () {
		testStep.checkStepLivecycle(manager, f);
	});
});
<|endoftext|>


<commit_before>"use strict";

describe("gpf.http.mock", function () {

    var baseUrl;

    before(function () {
        baseUrl = "http://localhost:" + config.httpPort + "/echo/";
    });

    it("exposes a method to mock requests", function () {
        assert(typeof gpf.http.mock === "function");
    });

    it("exposes a method to remove one specific mocking", function () {
        assert(typeof gpf.http.mock.remove === "function");
    });

    it("exposes a method to reset all mocking", function () {
        assert(typeof gpf.http.mock.reset === "function");
    });

    describe("Simple mocking example", function () {

        var mockId;

        beforeEach(function () {
            mockId = gpf.http.mock({
                method: gpf.http.methods.get,
                url: /echo\/\?status=([0-9]+)/,
                response: function (request, status) {
                    if (status === "400") {
                        return; // Don't mock
                    }
                    return {
                        status: parseInt(status, 10) + 1,
                        headers: {
                            "x-mock": true
                        },
                        responseText: "It works"
                    };
                }
            });
        });

        afterEach(function () {
            if (mockId) {
                gpf.http.mock.remove(mockId);
            }

        });

        it("matches request URL and method", function (done) {
            gpf.http.get(baseUrl + "?status=200").then(function (response) {
                assert(response.status === 201);
                assert(response.headers["x-mock"] === true);
                assert(response.responseText === "It works");
                done();
            })["catch"](done);
        });

        if (config.httpPort === 0) {
            return;
        }

        it("matches request URL and method (non matching URL)", function (done) {
            gpf.http.get(baseUrl).then(function (response) {
                assert(response.status === 200);
                assert(response.headers["x-mock"] === undefined);
                done();
            })["catch"](done);
        });

        it("matches request URL and method (non matching method)", function (done) {
            gpf.http.put(baseUrl, "?status=200").then(function (response) {
                assert(response.status === 200);
                assert(response.headers["x-mock"] === undefined);
                done();
            })["catch"](done);
        });

        it("may decide to not mock", function (done) {
            gpf.http.get(baseUrl + "?status=400").then(function (response) {
                assert(response.status === 400);
                assert(response.headers["x-mock"] === undefined);
                done();
            })["catch"](done);
        });

        it("does not mock once removed", function (done) {
            gpf.http.mock.remove(mockId);
            mockId = undefined;
            gpf.http.get(baseUrl + "?status=200").then(function (response) {
                assert(response.status === 200);
                assert(response.headers["x-mock"] === undefined);
                done();
            })["catch"](done);
        });

        it("does not mock once removed (reset)", function (done) {
            gpf.http.mock.reset();
            mockId = undefined;
            gpf.http.get(baseUrl + "?status=200").then(function (response) {
                assert(response.status === 200);
                assert(response.headers["x-mock"] === undefined);
                done();
            })["catch"](done);
        });

        it("does not fail if removed twice", function () {
            gpf.http.mock.reset();
            gpf.http.mock.remove(mockId);
            mockId = undefined;
        });

    });

    describe("Advanced mocking example", function () {

        var mockIdForResponse3;

        beforeEach(function () {

            function getResponse (index) {
                return function () {
                    return {
                        status: 200,
                        headers: {},
                        responseText: "answer-" + index
                    };
                };
            }

            gpf.http.mock({
                method: gpf.http.methods.get,
                url: /echo\/lifo/,
                response: getResponse(1)
            });
            gpf.http.mock({
                method: gpf.http.methods.get,
                url: /echo\/lifo/,
                response: getResponse(2)
            });
            mockIdForResponse3 = gpf.http.mock({
                method: gpf.http.methods.get,
                url: /echo\/lifo/,
                response: getResponse(3)
            });
            gpf.http.mock({
                method: gpf.http.methods.put,
                url: /echo\/lifo/,
                response: getResponse(4)
            });
            gpf.http.mock({
                method: gpf.http.methods.get,
                url: /echo\/redirect/,
                response: function (request) {
                    return gpf.http.get(request.url.replace("/redirect", "/lifo"));
                }
            });
        });

        afterEach(function () {
            gpf.http.mock.reset();
        });

        it("uses LIFO priority", function (done) {
            gpf.http.get(baseUrl + "lifo").then(function (response) {
                assert(response.status === 200);
                assert(response.responseText === "answer-3");
                done();
            })["catch"](done);
        });

        it("preserves the order when removing a mocked request", function (done) {
            gpf.http.mock.remove(mockIdForResponse3);
            gpf.http.get(baseUrl + "lifo").then(function (response) {
                assert(response.status === 200);
                assert(response.responseText === "answer-2");
                done();
            })["catch"](done);
        });

        it("allows redirection", function (done) {
            gpf.http.get(baseUrl + "redirect").then(function (response) {
                assert(response.status === 200);
                assert(response.responseText === "answer-3");
                done();
            })["catch"](done);
        });

    });

});

<commit_msg>Exclude from performance measurement (#311)<commit_after>"use strict";

describe("gpf.http.mock", function () {

    var baseUrl;

    before(function () {
        baseUrl = "http://localhost:" + config.httpPort + "/echo/";
    });

    it("exposes a method to mock requests", function () {
        assert(typeof gpf.http.mock === "function");
    });

    it("exposes a method to remove one specific mocking", function () {
        assert(typeof gpf.http.mock.remove === "function");
    });

    it("exposes a method to reset all mocking", function () {
        assert(typeof gpf.http.mock.reset === "function");
    });

    describe("Simple mocking example", function () {

        var mockId;

        beforeEach(function () {
            mockId = gpf.http.mock({
                method: gpf.http.methods.get,
                url: /echo\/\?status=([0-9]+)/,
                response: function (request, status) {
                    if (status === "400") {
                        return; // Don't mock
                    }
                    return {
                        status: parseInt(status, 10) + 1,
                        headers: {
                            "x-mock": true
                        },
                        responseText: "It works"
                    };
                }
            });
        });

        afterEach(function () {
            if (mockId) {
                gpf.http.mock.remove(mockId);
            }
        });

        it("matches request URL and method", function (done) {
            gpf.http.get(baseUrl + "?status=200").then(function (response) {
                assert(response.status === 201);
                assert(response.headers["x-mock"] === true);
                assert(response.responseText === "It works");
                done();
            })["catch"](done);
        });

        if (config.httpPort === 0 || config.performance) {
            return;
        }

        it("matches request URL and method (non matching URL)", function (done) {
            gpf.http.get(baseUrl).then(function (response) {
                assert(response.status === 200);
                assert(response.headers["x-mock"] === undefined);
                done();
            })["catch"](done);
        });

        it("matches request URL and method (non matching method)", function (done) {
            gpf.http.put(baseUrl, "?status=200").then(function (response) {
                assert(response.status === 200);
                assert(response.headers["x-mock"] === undefined);
                done();
            })["catch"](done);
        });

        it("may decide to not mock", function (done) {
            gpf.http.get(baseUrl + "?status=400").then(function (response) {
                assert(response.status === 400);
                assert(response.headers["x-mock"] === undefined);
                done();
            })["catch"](done);
        });

        it("does not mock once removed", function (done) {
            gpf.http.mock.remove(mockId);
            mockId = undefined;
            gpf.http.get(baseUrl + "?status=200").then(function (response) {
                assert(response.status === 200);
                assert(response.headers["x-mock"] === undefined);
                done();
            })["catch"](done);
        });

        it("does not mock once removed (reset)", function (done) {
            gpf.http.mock.reset();
            mockId = undefined;
            gpf.http.get(baseUrl + "?status=200").then(function (response) {
                assert(response.status === 200);
                assert(response.headers["x-mock"] === undefined);
                done();
            })["catch"](done);
        });

        it("does not fail if removed twice", function () {
            gpf.http.mock.reset();
            gpf.http.mock.remove(mockId);
            mockId = undefined;
        });

    });

    describe("Advanced mocking example", function () {

        var mockIdForResponse3;

        beforeEach(function () {

            function getResponse (index) {
                return function () {
                    return {
                        status: 200,
                        headers: {},
                        responseText: "answer-" + index
                    };
                };
            }

            gpf.http.mock({
                method: gpf.http.methods.get,
                url: /echo\/lifo/,
                response: getResponse(1)
            });
            gpf.http.mock({
                method: gpf.http.methods.get,
                url: /echo\/lifo/,
                response: getResponse(2)
            });
            mockIdForResponse3 = gpf.http.mock({
                method: gpf.http.methods.get,
                url: /echo\/lifo/,
                response: getResponse(3)
            });
            gpf.http.mock({
                method: gpf.http.methods.put,
                url: /echo\/lifo/,
                response: getResponse(4)
            });
            gpf.http.mock({
                method: gpf.http.methods.get,
                url: /echo\/redirect/,
                response: function (request) {
                    return gpf.http.get(request.url.replace("/redirect", "/lifo"));
                }
            });
        });

        afterEach(function () {
            gpf.http.mock.reset();
        });

        it("uses LIFO priority", function (done) {
            gpf.http.get(baseUrl + "lifo").then(function (response) {
                assert(response.status === 200);
                assert(response.responseText === "answer-3");
                done();
            })["catch"](done);
        });

        it("preserves the order when removing a mocked request", function (done) {
            gpf.http.mock.remove(mockIdForResponse3);
            gpf.http.get(baseUrl + "lifo").then(function (response) {
                assert(response.status === 200);
                assert(response.responseText === "answer-2");
                done();
            })["catch"](done);
        });

        it("allows redirection", function (done) {
            gpf.http.get(baseUrl + "redirect").then(function (response) {
                assert(response.status === 200);
                assert(response.responseText === "answer-3");
                done();
            })["catch"](done);
        });

    });

});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2012 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var Property = {
    __proto__: ModelProto,

    name:  'Property',
    plural:'Properties',
    help:  "Describes a properties of a modelled entity.",

    ids: [ 'name' ],

    tableProperties: [
      'name',
      'label',
      'type',
      'required',
      'defaultValue'
    ],

    properties: [
       {
           name:  'name',
           type:  'String',
           required: true,
           displayWidth: 30,
           displayHeight: 1,
           defaultValue: '',
           help: 'The coding identifier for the property.'
       },
       {
           name: 'label',
           type: 'String',
           required: false,
           displayWidth: 70,
           displayHeight: 1,
           defaultValueFn: function() { return this.name.labelize(); },
           help: 'The display label for the property.'
       },
       {
           name: 'tableLabel',
           type: 'String',
           displayWidth: 70,
           displayHeight: 1,
           defaultValueFn: function() { return this.name.labelize(); },
           help: 'The table display label for the entity.'
       },
       {
           name: 'type',
           type: 'String',
           required: true,
           // todo: curry arguments
           view: {
              create: function() { return ChoiceView.create({choices: [
                 'Array',
                 'Boolean',
                 'Color',
                 'Date',
                 'DateTime',
                 'Email',
                 'Enum',
                 'Float',
                 'Function',
                 'Image',
                 'Integer',
                 'Object',
                 'Password',
                 'String',
                 'String[]',
                 'URL'
              ]});}
           },
           defaultValue: 'String',
           help: 'The type of the property.'
       },
       {
          name: 'javaType',
          type: 'String',
          required: false,
          defaultValueFn: function() { return this.type; },
          help: 'The java type that represents the type of this property.'
       },
       {
          name: 'javascriptType',
          type: 'String',
          required: false,
          defaultValueFn: function() { return this.type; },
          help: 'The javascript type that represents the type of this property.'
       },
       {
           name:  'shortName',
           type:  'String',
           required: true,
           displayWidth: 10,
           displayHeight: 1,
           defaultValue: '',
           help: 'A short alternate name to be used for compact encoding.'
       },
       {
           name: 'aliases',
           type: 'Array[String]',
           view: 'StringArrayView',
           defaultValue: [],
           help: 'Alternate names for this property.'
       },
       {
          name:  'mode',
          type:  'String',
          defaultValue: 'read-write',
          view: {
             create: function() { return ChoiceView.create({choices:[
                       "read-only", "read-write", "final"
              ]}); } }
       },
       {
           name: 'subType',
           label: 'Sub-Type',
           type: 'String',
           displayWidth: 30,
          // todo: keyView of Models
           required: true,
           help: 'The type of the property.'
       },
       {
           name: 'units',
           type: 'String',
           required: true,
           displayWidth: 70,
           displayHeight: 1,
           defaultValue: '',
           help: 'The units of the property.'
       },
       {
           name: 'required',
           type: 'Boolean',
           view: 'BooleanView',
           defaultValue: true,
           help: 'Indicates if the property is a required field.'
       },
       {
           name: 'hidden',
           type: 'Boolean',
           view: 'BooleanView',
           defaultValue: false,
           help: 'Indicates if the property is hidden.'
       },
       {
           name: 'transient',
           type: 'Boolean',
           view: 'BooleanView',
           defaultValue: false,
           help: 'Indicates if the property is transient.'
       },
       {
           name: 'displayWidth',
           type: 'int',
           displayWidth: 8,
           displayHeight: 1,
           defaultValue: '30',
           help: 'The display width of the property.'
       },
       {
           name: 'displayHeight',
           type: 'int',
           displayWidth: 8,
           displayHeight: 1,
           defaultValue: 1,
           help: 'The display height of the property.'
       },
       {
           name: 'view',
           type: 'view',
           defaultValue: 'TextFieldView',
           help: 'View component for the property.'
       },
       {
           model_: 'FunctionProperty',
           name: 'detailViewPreRow',
           defaultValue: function() { return ""; },
           help: 'Inject HTML before row in DetailView.'
       },
       {
           model_: 'FunctionProperty',
           name: 'detailViewPostRow',
           defaultValue: function() { return ""; },
           help: 'Inject HTML before row in DetailView.'
       },
       {
           name: 'defaultValue',
           type: 'String',
           required: false,
           displayWidth: 70,
           displayHeight: 1,
           defaultValue: '',
           help: 'The property\'s default value.'
       },
       {
           name: 'defaultValueFn',
           label: 'Default Value Function',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           rows:3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'The property\'s default value function.'
       },
       {
           name: 'valueFactory',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           rows:3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'Factory for creating initial value when new object instantiated.'
       },
       {
           name: 'getter',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'The property\'s default value function.'
       },
       {
           name: 'preSet',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'An adapter function called before normal setter logic.'
       },
       {
           name: 'postSet',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'A function called after normal setter logic, but before property change event fired.'
       },
       {
           name: 'setter',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'The property\'s default value function.'
       },
       {
           name: 'tableFormatter',
           label: 'Table View Cell Formatter',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           rows:3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'Function to format value for display in TableView.'
       },
       {
           name: 'summaryFormatter',
           label: 'Summary View Cell Formatter',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           rows:3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'Function to format value for display in SummaryView.'
       },
       {
           name: 'tableWidth',
           type: 'String',
           required: false,
           defaultValue: '',
           help: 'Table View Column Width.'
       },
       {
           name: 'help',
           label: 'Help Text',
           type: 'String',
           required: false,
           displayWidth: 70,
           displayHeight: 6,
           view: 'TextAreaView',
           defaultValue: '',
           help: 'Help text associated with the property.'
       },
       {
           name: 'prototag',
           label: 'Protobuf tag',
           type: 'Integer',
           required: false,
           help: 'The protobuf tag number for this field.'
       },
       {
           name: 'actionFactory',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           rows:3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'Factory to create the action objects for taking this property from value A to value B'
       },
       {
           name: 'compareProperty',
           type: 'Function',
           view: 'FunctionView',
           defaultValue: function(o1, o2) {
             return (o1.localeCompare || o1.compareTo).call(o1, o2);
           },
           help: 'Comparator function.'
       }
    ],

    methods: {
      f: function(obj) { return obj[this.name] || obj; },
      compare: function(o1, o2) {
        return this.compareProperty(this.f(o1), this.f(o2));
      },
      toSQL: function() { return this.name; },
      toMQL: function() { return this.name; }
    },

    getProperty: function(name) {
        for ( var i = 0 ; i < this.properties.length ; i++ ) {
            var p = this.properties[i];

            if ( p.name === name ) return p;
        }

        document.writeln("couldn't find: " + name);
        return null;
    },

    templates: [
       {
          model_: 'Template',
          name: 'closureSource',
          description: 'Closure Externs JavaScript Source',
          template:
             '/**\n' +
             ' * @type {<%= this.javascriptType %>}\n' +
             ' */\n' +
             '<%= arguments[1] %>.prototype.<%= this.name %> = undefined;'
       }
    ],

    toString: function() { return "Property"; }
};

Model.methods = {
    hashCode:       ModelProto.hashCode,
    buildPrototype: ModelProto.buildPrototype,
    getPrototype:   ModelProto.getPrototype,
    isSubModel:     ModelProto.isSubModel,
    isInstance:     ModelProto.isInstance
};

// This is the coolest line of code that I've ever written
// or ever will write. Oct. 4, 2011 -- KGR
Model = Model.create(Model);
Model.model_ = Model;

Property = Model.create(Property);
<commit_msg>Shorted long Property property names.<commit_after>/**
 * @license
 * Copyright 2012 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var Property = {
    __proto__: ModelProto,

    name:  'Property',
    plural:'Properties',
    help:  "Describes a properties of a modelled entity.",

    ids: [ 'name' ],

    tableProperties: [
      'name',
      'label',
      'type',
      'required',
      'defaultValue'
    ],

    properties: [
       {
           name:  'name',
           type:  'String',
           required: true,
           displayWidth: 30,
           displayHeight: 1,
           defaultValue: '',
           help: 'The coding identifier for the property.'
       },
       {
           name: 'label',
           type: 'String',
           required: false,
           displayWidth: 70,
           displayHeight: 1,
           defaultValueFn: function() { return this.name.labelize(); },
           help: 'The display label for the property.'
       },
       {
           name: 'tableLabel',
           type: 'String',
           displayWidth: 70,
           displayHeight: 1,
           defaultValueFn: function() { return this.name.labelize(); },
           help: 'The table display label for the entity.'
       },
       {
           name: 'type',
           type: 'String',
           required: true,
           // todo: curry arguments
           view: {
              create: function() { return ChoiceView.create({choices: [
                 'Array',
                 'Boolean',
                 'Color',
                 'Date',
                 'DateTime',
                 'Email',
                 'Enum',
                 'Float',
                 'Function',
                 'Image',
                 'Integer',
                 'Object',
                 'Password',
                 'String',
                 'String[]',
                 'URL'
              ]});}
           },
           defaultValue: 'String',
           help: 'The type of the property.'
       },
       {
          name: 'javaType',
          type: 'String',
          required: false,
          defaultValueFn: function() { return this.type; },
          help: 'The java type that represents the type of this property.'
       },
       {
          name: 'javascriptType',
          type: 'String',
          required: false,
          defaultValueFn: function() { return this.type; },
          help: 'The javascript type that represents the type of this property.'
       },
       {
           name:  'shortName',
           type:  'String',
           required: true,
           displayWidth: 10,
           displayHeight: 1,
           defaultValue: '',
           help: 'A short alternate name to be used for compact encoding.'
       },
       {
           name: 'aliases',
           type: 'Array[String]',
           view: 'StringArrayView',
           defaultValue: [],
           help: 'Alternate names for this property.'
       },
       {
          name:  'mode',
          type:  'String',
          defaultValue: 'read-write',
          view: {
             create: function() { return ChoiceView.create({choices:[
                       "read-only", "read-write", "final"
              ]}); } }
       },
       {
           name: 'subType',
           label: 'Sub-Type',
           type: 'String',
           displayWidth: 30,
          // todo: keyView of Models
           required: true,
           help: 'The type of the property.'
       },
       {
           name: 'units',
           type: 'String',
           required: true,
           displayWidth: 70,
           displayHeight: 1,
           defaultValue: '',
           help: 'The units of the property.'
       },
       {
           name: 'required',
           type: 'Boolean',
           view: 'BooleanView',
           defaultValue: true,
           help: 'Indicates if the property is a required field.'
       },
       {
           name: 'hidden',
           type: 'Boolean',
           view: 'BooleanView',
           defaultValue: false,
           help: 'Indicates if the property is hidden.'
       },
       {
           name: 'transient',
           type: 'Boolean',
           view: 'BooleanView',
           defaultValue: false,
           help: 'Indicates if the property is transient.'
       },
       {
           name: 'displayWidth',
           type: 'int',
           displayWidth: 8,
           displayHeight: 1,
           defaultValue: '30',
           help: 'The display width of the property.'
       },
       {
           name: 'displayHeight',
           type: 'int',
           displayWidth: 8,
           displayHeight: 1,
           defaultValue: 1,
           help: 'The display height of the property.'
       },
       {
           name: 'view',
           type: 'view',
           defaultValue: 'TextFieldView',
           help: 'View component for the property.'
       },
       {
           model_: 'FunctionProperty',
           name: 'detailViewPreRow',
           defaultValue: function() { return ""; },
           help: 'Inject HTML before row in DetailView.'
       },
       {
           model_: 'FunctionProperty',
           name: 'detailViewPostRow',
           defaultValue: function() { return ""; },
           help: 'Inject HTML before row in DetailView.'
       },
       {
           name: 'defaultValue',
           type: 'String',
           required: false,
           displayWidth: 70,
           displayHeight: 1,
           defaultValue: '',
           help: 'The property\'s default value.'
       },
       {
           name: 'defaultValueFn',
           label: 'Default Value Function',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           rows:3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'The property\'s default value function.'
       },
       {
           name: 'valueFactory',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           rows:3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'Factory for creating initial value when new object instantiated.'
       },
       {
           name: 'getter',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'The property\'s default value function.'
       },
       {
           name: 'preSet',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'An adapter function called before normal setter logic.'
       },
       {
           name: 'postSet',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'A function called after normal setter logic, but before property change event fired.'
       },
       {
           name: 'setter',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'The property\'s default value function.'
       },
       {
           name: 'tableFormatter',
           label: 'Table Cell Formatter',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           rows:3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'Function to format value for display in TableView.'
       },
       {
           name: 'summaryFormatter',
           label: 'Summary Formatter',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           rows:3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'Function to format value for display in SummaryView.'
       },
       {
           name: 'tableWidth',
           type: 'String',
           required: false,
           defaultValue: '',
           help: 'Table View Column Width.'
       },
       {
           name: 'help',
           label: 'Help Text',
           type: 'String',
           required: false,
           displayWidth: 70,
           displayHeight: 6,
           view: 'TextAreaView',
           defaultValue: '',
           help: 'Help text associated with the property.'
       },
       {
           name: 'prototag',
           label: 'Protobuf tag',
           type: 'Integer',
           required: false,
           help: 'The protobuf tag number for this field.'
       },
       {
           name: 'actionFactory',
           type: 'Function',
           required: false,
           displayWidth: 70,
           displayHeight: 3,
           rows:3,
           view: 'FunctionView',
           defaultValue: '',
           help: 'Factory to create the action objects for taking this property from value A to value B'
       },
       {
           name: 'compareProperty',
           type: 'Function',
           view: 'FunctionView',
           defaultValue: function(o1, o2) {
             return (o1.localeCompare || o1.compareTo).call(o1, o2);
           },
           help: 'Comparator function.'
       }
    ],

    methods: {
      f: function(obj) { return obj[this.name] || obj; },
      compare: function(o1, o2) {
        return this.compareProperty(this.f(o1), this.f(o2));
      },
      toSQL: function() { return this.name; },
      toMQL: function() { return this.name; }
    },

    getProperty: function(name) {
        for ( var i = 0 ; i < this.properties.length ; i++ ) {
            var p = this.properties[i];

            if ( p.name === name ) return p;
        }

        document.writeln("couldn't find: " + name);
        return null;
    },

    templates: [
       {
          model_: 'Template',
          name: 'closureSource',
          description: 'Closure Externs JavaScript Source',
          template:
             '/**\n' +
             ' * @type {<%= this.javascriptType %>}\n' +
             ' */\n' +
             '<%= arguments[1] %>.prototype.<%= this.name %> = undefined;'
       }
    ],

    toString: function() { return "Property"; }
};

Model.methods = {
    hashCode:       ModelProto.hashCode,
    buildPrototype: ModelProto.buildPrototype,
    getPrototype:   ModelProto.getPrototype,
    isSubModel:     ModelProto.isSubModel,
    isInstance:     ModelProto.isInstance
};

// This is the coolest line of code that I've ever written
// or ever will write. Oct. 4, 2011 -- KGR
Model = Model.create(Model);
Model.model_ = Model;

Property = Model.create(Property);
<|endoftext|>


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

describe('field', function () {

	var fieldsSelected = [];

	before(function () {
		requireWithCoverage('field');

		mongoose.Query.prototype.select = function (field) {
			if (field) {
				fieldsSelected.push(field);
			}
		};
	});

	beforeEach(function () {
		fieldsSelected = [];
	});

	it ('should return a query when created', function () {
		var query = kitteh
			.find()
			.field(null);

		(query instanceof mongoose.Query).should.equals(true);
	});

	it ('should only select fields when multiple fields are supplied', function () {
		var options = {
			filters : {
				field : ['birthday', 'name']
			}
		};

		var query = kitteh
			.find()
			.field(options);

		should.exist(query);
		fieldsSelected.should.have.length(2);
	});

	it ('should only select one field when one field is supplied', function () {
		var options = {
			filters : {
				field : ['name']
			}
		};

		var query = kitteh
			.find()
			.field(options);

		should.exist(query);
		fieldsSelected.should.have.length(1);
		fieldsSelected[0].should.equals('name');
	});

	it ('should select all model fields when options are null', function () {
		var query = kitteh
			.find()
			.field(null);

		should.exist(query);
		fieldsSelected.should.have.length(0);
	});

	it ('should select all model fields when options contain filters, but not field', function () {
		var query = kitteh
			.find()
			.field({ filters : {} });

		should.exist(query);
		fieldsSelected.should.have.length(0);
	});

});<commit_msg>getting field.js up to 100% coverage<commit_after>var mongoose = require('mongoose');

describe('field', function () {

	var fieldsSelected = [];

	before(function () {
		requireWithCoverage('field');

		mongoose.Query.prototype.select = function (field) {
			if (field) {
				fieldsSelected.push(field);
			}
		};
	});

	beforeEach(function () {
		fieldsSelected = [];
	});

	it ('should return a query when created', function () {
		var query = kitteh
			.find()
			.field(null);

		(query instanceof mongoose.Query).should.equals(true);
	});

	it ('should only select fields when multiple fields are supplied', function () {
		var options = {
			filters : {
				field : ['birthday', 'name']
			}
		};

		var query = kitteh
			.find()
			.field(options);

		should.exist(query);
		fieldsSelected.should.have.length(2);
	});

	it ('should only select one field when one field is supplied', function () {
		var options = {
			filters : {
				field : ['name']
			}
		};

		var query = kitteh
			.find()
			.field(options);

		should.exist(query);
		fieldsSelected.should.have.length(1);
		fieldsSelected[0].should.equals('name');
	});

	it ('should select one field when one field is supplied and not an array', function () {
		var options = {
			filters : {
				field : 'name'
			}
		};

		var query = kitteh
			.find()
			.field(options);

		should.exist(query);
		fieldsSelected.should.have.length(1);
		fieldsSelected[0].should.equals('name');
	});

	it ('should select all fields when one field is supplied and not an array but does not exist in schema', function () {
		var options = {
			filters : {
				field : 'notinschema'
			}
		};

		var query = kitteh
			.find()
			.field(options);

		should.exist(query);
		fieldsSelected.should.have.length(0);
	});

	it ('should select all model fields when options are null', function () {
		var query = kitteh
			.find()
			.field(null);

		should.exist(query);
		fieldsSelected.should.have.length(0);
	});

	it ('should select all model fields when options contain filters, but not field', function () {
		var query = kitteh
			.find()
			.field({ filters : {} });

		should.exist(query);
		fieldsSelected.should.have.length(0);
	});

});<|endoftext|>