<commit_before>var _ = require('underscore');
var CategoryDataviewModel = require('../../../src/dataviews/category-dataview-model.js');
var WindshaftFiltersCategory = require('../../../src/windshaft/filters/category');

describe('dataviews/category-dataview-model', function () {
  beforeEach(function () {
    var map = jasmine.createSpyObj('map', ['getViewBounds', 'bind', 'reload']);
    map.getViewBounds.and.returnValue([[1, 2], [3, 4]]);
    var windshaftMap = jasmine.createSpyObj('windhsaftMap', ['bind']);
    this.model = new CategoryDataviewModel(null, {
      map: map,
      windshaftMap: windshaftMap,
      layer: jasmine.createSpyObj('layer', ['get']),
      filter: new WindshaftFiltersCategory()
    });
  });

  it('should define several internal models/collections', function () {
    expect(this.model._data).toBeDefined();
    expect(this.model._searchModel).toBeDefined();
    expect(this.model.filter).toBeDefined();
  });

  describe('binds', function () {
    beforeEach(function () {
      this.model.set({
        url: 'http://heytest.io'
      });
      // Simulating first interaction with client.js
      this.model._onChangeBinds();
    });

    describe('url', function () {
      beforeEach(function () {
        spyOn(this.model, 'fetch');
        spyOn(this.model._searchModel, 'fetch');
        spyOn(this.model._rangeModel, 'fetch');
      });

      it('should set search url when it changes', function () {
        expect(this.model._searchModel.get('url')).toBe('http://heytest.io');
        expect(this.model._searchModel.url()).toBe('http://heytest.io/search?q=');
      });

      it('should set rangeModel url when it changes', function () {
        expect(this.model._rangeModel.get('url')).toBe('http://heytest.io');
        expect(this.model._rangeModel.url()).toBe('http://heytest.io');
      });
    });

    describe('boundingBox', function () {
      it('should set search boundingBox when it changes', function () {
        expect(this.model._searchModel.get('boundingBox')).toBe('2,1,4,3');
        this.model.set('boundingBox', 'hey');
        expect(this.model._searchModel.get('boundingBox')).toBe('hey');
      });

      it('should fetch itself if bounding box changes only when search is not applied', function () {
        spyOn(this.model, '_fetch');
        spyOn(this.model, 'isSearchApplied').and.returnValue(true);
        this.model.set('boundingBox', 'comeon');
        expect(this.model._fetch).not.toHaveBeenCalled();
      });
    });

    describe('search events dispatcher', function () {
      it('should trigger search related events', function () {
        var eventNames = ['loading', 'sync', 'error'];
        _.each(eventNames, function (eventName) {
          _.bind(eventDispatcher, this)(this.model._searchModel, eventName);
        }, this);
      });

      it('should trigger a change:searchData when search model is fetched', function () {
        _.bind(eventDispatcher, this)(this.model._searchModel, 'change:data', 'change:searchData');
      });
    });

    describe('range model', function () {
      it('should set totalCount when rangeModel has changed', function () {
        expect(this.model.get('totalCount')).toBeUndefined();
        this.model._rangeModel.set({ totalCount: 1000 });
        expect(this.model.get('totalCount')).toBe(1000);
      });

      it('should set categoriesCount when rangeModel has changed', function () {
        expect(this.model.get('categoriesCount')).toBeUndefined();
        this.model._rangeModel.set({ categoriesCount: 123 });
        expect(this.model.get('categoriesCount')).toBe(123);
      });
    });
  });

  describe('search model helpers', function () {
    it('should clean search properly', function () {
      spyOn(this.model._searchModel, 'resetData');
      this.model.cleanSearch();
      expect(this.model._searchModel.resetData).toHaveBeenCalled();
    });

    describe('setupSearch', function () {
      beforeEach(function () {
        spyOn(this.model._searchModel, 'setData').and.callThrough();
      });

      it('should not setup search if search is already applied', function () {
        spyOn(this.model, 'isSearchApplied').and.returnValue(true);
        this.model.setupSearch();
        expect(this.model._searchModel.setData).not.toHaveBeenCalled();
      });

      it('should setup search if it is gonna be enabled', function () {
        spyOn(this.model, 'isSearchApplied').and.returnValue(false);
        _parseData(this.model, _generateData(3));
        this.model.filter.accept(['4', '5', '6']);
        this.model.setupSearch();
        expect(this.model._searchModel.setData).toHaveBeenCalled();
        expect(this.model.getSearchCount()).toBe(3);
      });
    });
  });

  it('should refresh its own data only if the search is not applied', function () {
    spyOn(this.model, '_fetch');
    spyOn(this.model._searchModel, 'fetch');
    this.model.refresh();
    expect(this.model._fetch.calls.count()).toEqual(1);
    expect(this.model._fetch).toHaveBeenCalled();
    expect(this.model._searchModel.fetch).not.toHaveBeenCalled();
    spyOn(this.model, 'isSearchApplied').and.returnValue(true);
    this.model.refresh();
    expect(this.model._searchModel.fetch).toHaveBeenCalled();
    expect(this.model._fetch.calls.count()).toEqual(1);
  });

  describe('parseData', function () {
    it('should provide data as an object', function () {
      _parseData(this.model, _generateData(10));
      var data = this.model.get('data');
      expect(data).toBeDefined();
      expect(data.length).toBe(10);
    });

    it('should complete data with accepted items (if they are not present already) when has ownFilter set', function () {
      this.model.set('ownFilter', true);
      this.model.filter.accept(['9', '10', '11']);
      _parseData(this.model, _generateData(8));
      var data = this.model.get('data');
      expect(data.length).toBe(11);

      this.model.filter.accept(['2']);
      // The '2' should not be repeated in the data array
      _parseData(this.model, _generateData(8));
      data = this.model.get('data');
      expect(data.length).toBe(11);
    });
  });

  describe('.parse', function () {
    it('should change internal data collection when parse is called', function () {
      var resetSpy = jasmine.createSpy('reset');
      this.model._data.bind('reset', resetSpy);

      _parseData(this.model, _generateData(2));
      expect(resetSpy).toHaveBeenCalled();
    });
  });

  it('should have defined "_onFilterChanged" method', function () {
    expect(this.model._onFilterChanged).toBeDefined();
  });
});

function eventDispatcher (originModel, eventName, triggerName) {
  var spyObj = jasmine.createSpy(eventName);
  this.model.bind(triggerName || eventName, spyObj);
  originModel.trigger(eventName);
  expect(spyObj).toHaveBeenCalled();
}

function _generateData (n) {
  return _.times(n, function (i) {
    return {
      category: i,
      value: 2
    };
  });
}

function _parseData (model, categories) {
  model.sync = function (method, model, options) {
    options.success({
      'categories': categories
    });
  };
  model.fetch();
}
<commit_msg>Spec added for category-model<commit_after>var _ = require('underscore');
var CategoryDataviewModel = require('../../../src/dataviews/category-dataview-model.js');
var WindshaftFiltersCategory = require('../../../src/windshaft/filters/category');

describe('dataviews/category-dataview-model', function () {
  beforeEach(function () {
    var map = jasmine.createSpyObj('map', ['getViewBounds', 'bind', 'reload']);
    map.getViewBounds.and.returnValue([[1, 2], [3, 4]]);
    var windshaftMap = jasmine.createSpyObj('windhsaftMap', ['bind']);
    this.model = new CategoryDataviewModel(null, {
      map: map,
      windshaftMap: windshaftMap,
      layer: jasmine.createSpyObj('layer', ['get']),
      filter: new WindshaftFiltersCategory()
    });
  });

  it('should define several internal models/collections', function () {
    expect(this.model._data).toBeDefined();
    expect(this.model._searchModel).toBeDefined();
    expect(this.model.filter).toBeDefined();
  });

  describe('binds', function () {
    beforeEach(function () {
      this.model.set({
        url: 'http://heytest.io'
      });
      // Simulating first interaction with client.js
      this.model._onChangeBinds();
    });

    describe('url', function () {
      beforeEach(function () {
        spyOn(this.model, 'fetch');
        spyOn(this.model._searchModel, 'fetch');
        spyOn(this.model._rangeModel, 'fetch');
      });

      it('should set search url when it changes', function () {
        expect(this.model._searchModel.get('url')).toBe('http://heytest.io');
        expect(this.model._searchModel.url()).toBe('http://heytest.io/search?q=');
      });

      it('should set rangeModel url when it changes', function () {
        expect(this.model._rangeModel.get('url')).toBe('http://heytest.io');
        expect(this.model._rangeModel.url()).toBe('http://heytest.io');
      });
    });

    describe('boundingBox', function () {
      it('should set search boundingBox when it changes', function () {
        expect(this.model._searchModel.get('boundingBox')).toBe('2,1,4,3');
        this.model.set('boundingBox', 'hey');
        expect(this.model._searchModel.get('boundingBox')).toBe('hey');
      });

      it('should fetch itself if bounding box changes only when search is not applied', function () {
        spyOn(this.model, '_fetch');
        spyOn(this.model, 'isSearchApplied').and.returnValue(true);
        this.model.set('boundingBox', 'comeon');
        expect(this.model._fetch).not.toHaveBeenCalled();
      });
    });

    describe('search events dispatcher', function () {
      it('should trigger search related events', function () {
        var eventNames = ['loading', 'sync', 'error'];
        _.each(eventNames, function (eventName) {
          _.bind(eventDispatcher, this)(this.model._searchModel, eventName);
        }, this);
      });

      it('should trigger a change:searchData when search model is fetched', function () {
        _.bind(eventDispatcher, this)(this.model._searchModel, 'change:data', 'change:searchData');
      });
    });

    describe('range model', function () {
      it('should set totalCount when rangeModel has changed', function () {
        expect(this.model.get('totalCount')).toBeUndefined();
        this.model._rangeModel.set({ totalCount: 1000 });
        expect(this.model.get('totalCount')).toBe(1000);
      });

      it('should set categoriesCount when rangeModel has changed', function () {
        expect(this.model.get('categoriesCount')).toBeUndefined();
        this.model._rangeModel.set({ categoriesCount: 123 });
        expect(this.model.get('categoriesCount')).toBe(123);
      });
    });
  });

  describe('search model helpers', function () {
    it('should clean search properly', function () {
      spyOn(this.model._searchModel, 'resetData');
      this.model.cleanSearch();
      expect(this.model._searchModel.resetData).toHaveBeenCalled();
    });

    describe('setupSearch', function () {
      beforeEach(function () {
        spyOn(this.model._searchModel, 'setData').and.callThrough();
      });

      it('should not setup search if search is already applied', function () {
        spyOn(this.model, 'isSearchApplied').and.returnValue(true);
        this.model.setupSearch();
        expect(this.model._searchModel.setData).not.toHaveBeenCalled();
      });

      it('should setup search if it is gonna be enabled', function () {
        spyOn(this.model, 'isSearchApplied').and.returnValue(false);
        _parseData(this.model, _generateData(3));
        this.model.filter.accept(['4', '5', '6']);
        this.model.setupSearch();
        expect(this.model._searchModel.setData).toHaveBeenCalled();
        expect(this.model.getSearchCount()).toBe(3);
      });
    });
  });

  it('should refresh its own data only if the search is not applied', function () {
    spyOn(this.model, '_fetch');
    spyOn(this.model._searchModel, 'fetch');
    this.model.refresh();
    expect(this.model._fetch.calls.count()).toEqual(1);
    expect(this.model._fetch).toHaveBeenCalled();
    expect(this.model._searchModel.fetch).not.toHaveBeenCalled();
    spyOn(this.model, 'isSearchApplied').and.returnValue(true);
    this.model.refresh();
    expect(this.model._searchModel.fetch).toHaveBeenCalled();
    expect(this.model._fetch.calls.count()).toEqual(1);
  });

  describe('parseData', function () {
    it('should provide data as an object', function () {
      _parseData(this.model, _generateData(10));
      var data = this.model.get('data');
      expect(data).toBeDefined();
      expect(data.length).toBe(10);
    });

    it('should complete data with accepted items (if they are not present already) when has ownFilter set', function () {
      this.model.set('ownFilter', true);
      this.model.filter.accept(['9', '10', '11']);
      _parseData(this.model, _generateData(8));
      var data = this.model.get('data');
      expect(data.length).toBe(11);

      this.model.filter.accept(['2']);
      // The '2' should not be repeated in the data array
      _parseData(this.model, _generateData(8));
      data = this.model.get('data');
      expect(data.length).toBe(11);
    });
  });

  describe('.parse', function () {
    it('should change internal data collection when parse is called', function () {
      var resetSpy = jasmine.createSpy('reset');
      this.model._data.bind('reset', resetSpy);

      _parseData(this.model, _generateData(2));
      expect(resetSpy).toHaveBeenCalled();
    });

    it('should cast any category value to string', function () {
      _parseData(this.model, _.map([null, undefined, 0, 'hello', false], function (v) {
        return {
          category: v,
          value: 1
        };
      }));
      var data = this.model.get('data');
      var areNamesString = _.every(data, function (obj) {
        return obj.name;
      });
      expect(areNamesString).toBeTruthy();
    });
  });

  it('should have defined "_onFilterChanged" method', function () {
    expect(this.model._onFilterChanged).toBeDefined();
  });
});

function eventDispatcher (originModel, eventName, triggerName) {
  var spyObj = jasmine.createSpy(eventName);
  this.model.bind(triggerName || eventName, spyObj);
  originModel.trigger(eventName);
  expect(spyObj).toHaveBeenCalled();
}

function _generateData (n) {
  return _.times(n, function (i) {
    return {
      category: i,
      value: 2
    };
  });
}

function _parseData (model, categories) {
  model.sync = function (method, model, options) {
    options.success({
      'categories': categories
    });
  };
  model.fetch();
}
<|endoftext|>


<commit_before>var SyncError = require('logux-sync').SyncError
var TestTime = require('logux-core').TestTime
var BaseSync = require('logux-sync').BaseSync
var TestPair = require('logux-sync').TestPair

var messages = require('../badge/en')
var styles = require('../badge/default')
var badge = require('../badge')

var OPTS = { messages: messages, styles: styles }

function findBadgeNode () {
  return document.querySelector('div')
}

function getBadgeMessage () {
  return findBadgeNode().childNodes[0].innerHTML
}

function wait (ms) {
  return new Promise(function (resolve) {
    setTimeout(resolve, ms)
  })
}

function merge (a, b) {
  var result = { }
  for (var i in a) result[i] = a[i]
  for (var j in b) result[j] = b[j]
  return result
}

function createTest () {
  var pair = new TestPair()
  pair.leftSync = new BaseSync('client', TestTime.getLog(), pair.left)
  pair.leftSync.catch(function () {})
  return pair.left.connect().then(function () {
    return pair
  })
}

afterEach(function () {
  var node = findBadgeNode()
  if (node) document.body.removeChild(node)
})

it('should inject base widget styles', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    expect(findBadgeNode().style.position).toEqual('fixed')
    expect(findBadgeNode().childNodes[0].style.display).toEqual('table-cell')
  })
})

it('should handle synchronized state', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, merge(OPTS, { duration: 10 }))

    test.leftSync.connected = true
    test.leftSync.setState('synchronized')
    expect(findBadgeNode().style.display).toEqual('none')

    test.leftSync.connected = false
    test.leftSync.setState('wait')
    test.leftSync.setState('connecting')
    test.leftSync.connected = true
    test.leftSync.setState('synchronized')
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.synchronized)
    return wait(10)
  }).then(function () {
    expect(findBadgeNode().style.display).toEqual('none')
  })
})

it('should handle disconnected state', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    test.leftSync.connected = true
    test.leftSync.setState('connected')
    test.leftSync.connected = false
    test.leftSync.setState('disconnected')
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.disconnected)
  })
})

it('should handle wait state', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    test.leftSync.connected = false
    test.leftSync.setState('disconnected')
    test.leftSync.setState('wait')
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.wait)
  })
})

it('should handle sending state', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    test.leftSync.connected = false
    test.leftSync.setState('disconnected')
    test.leftSync.setState('sending')
    expect(findBadgeNode().style.display).toEqual('none')

    test.leftSync.connected = false
    test.leftSync.setState('wait')
    test.leftSync.setState('sending')
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.sending)
  })
})

it('should handle connecting state', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    test.leftSync.connected = false
    test.leftSync.setState('disconnected')
    test.leftSync.setState('connecting')
    expect(findBadgeNode().style.display).toEqual('none')

    test.leftSync.connected = false
    test.leftSync.setState('wait')
    test.leftSync.setState('connecting')
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.connecting)
  })
})

it('should handle error', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    test.leftSync.emitter.emit('error', { type: 'any error' })
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.error)
  })
})

it('should handle server errors', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    var errorOPTS = {
      testError: 'testError',
      supported: [1, 2, 3],
      used: [1, 2, 3]
    }

    var error = new SyncError(test.leftSync, 'wrong-format')
    test.leftSync.emitter.emit('error', error)
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.error)

    error = new SyncError(test.leftSync, 'wrong-subprotocol', errorOPTS)
    test.leftSync.emitter.emit('error', error)
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.protocolError)

    error = new SyncError(test.leftSync, 'wrong-protocol', errorOPTS)
    test.leftSync.emitter.emit('error', error)
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.protocolError)
  })
})

it('should handle client error', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    var error = new SyncError(test.leftSync, 'test', 'type', true)
    test.leftSync.emitter.emit('clientError', error)

    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.error)
  })
})

it('should handle bottom and left side of position setting', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, merge(OPTS, { position: 'bottom-left' }))

    expect(findBadgeNode().style.bottom).toEqual('0px')
    expect(findBadgeNode().style.left).toEqual('0px')
  })
})

it('should handle top and right side of position setting', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, merge(OPTS, { position: 'top-right' }))

    expect(findBadgeNode().style.top).toEqual('0px')
    expect(findBadgeNode().style.right).toEqual('0px')
  })
})

it('should handle center/middle position setting', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, merge(OPTS, { position: 'center-middle' }))

    expect(findBadgeNode().style.top).toEqual('50%')
    expect(findBadgeNode().style.left).toEqual('50%')
    expect(findBadgeNode().style.transform).toEqual('translate(-50%, -50%)')
  })
})

it('should return unbind function and remove badge from DOM', function () {
  return createTest().then(function (test) {
    var unbind = badge({ sync: test.leftSync }, OPTS)
    unbind()

    test.leftSync.emitter.emit('error', { type: 'wrong-protocol' })
    expect(findBadgeNode()).toEqual(null)
  })
})
<commit_msg>Normalize test names<commit_after>var SyncError = require('logux-sync').SyncError
var TestTime = require('logux-core').TestTime
var BaseSync = require('logux-sync').BaseSync
var TestPair = require('logux-sync').TestPair

var messages = require('../badge/en')
var styles = require('../badge/default')
var badge = require('../badge')

var OPTS = { messages: messages, styles: styles }

function findBadgeNode () {
  return document.querySelector('div')
}

function getBadgeMessage () {
  return findBadgeNode().childNodes[0].innerHTML
}

function wait (ms) {
  return new Promise(function (resolve) {
    setTimeout(resolve, ms)
  })
}

function merge (a, b) {
  var result = { }
  for (var i in a) result[i] = a[i]
  for (var j in b) result[j] = b[j]
  return result
}

function createTest () {
  var pair = new TestPair()
  pair.leftSync = new BaseSync('client', TestTime.getLog(), pair.left)
  pair.leftSync.catch(function () {})
  return pair.left.connect().then(function () {
    return pair
  })
}

afterEach(function () {
  var node = findBadgeNode()
  if (node) document.body.removeChild(node)
})

it('injects base widget styles', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    expect(findBadgeNode().style.position).toEqual('fixed')
    expect(findBadgeNode().childNodes[0].style.display).toEqual('table-cell')
  })
})

it('handles synchronized state', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, merge(OPTS, { duration: 10 }))

    test.leftSync.connected = true
    test.leftSync.setState('synchronized')
    expect(findBadgeNode().style.display).toEqual('none')

    test.leftSync.connected = false
    test.leftSync.setState('wait')
    test.leftSync.setState('connecting')
    test.leftSync.connected = true
    test.leftSync.setState('synchronized')
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.synchronized)
    return wait(10)
  }).then(function () {
    expect(findBadgeNode().style.display).toEqual('none')
  })
})

it('handles disconnected state', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    test.leftSync.connected = true
    test.leftSync.setState('connected')
    test.leftSync.connected = false
    test.leftSync.setState('disconnected')
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.disconnected)
  })
})

it('handles wait state', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    test.leftSync.connected = false
    test.leftSync.setState('disconnected')
    test.leftSync.setState('wait')
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.wait)
  })
})

it('handles sending state', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    test.leftSync.connected = false
    test.leftSync.setState('disconnected')
    test.leftSync.setState('sending')
    expect(findBadgeNode().style.display).toEqual('none')

    test.leftSync.connected = false
    test.leftSync.setState('wait')
    test.leftSync.setState('sending')
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.sending)
  })
})

it('handles connecting state', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    test.leftSync.connected = false
    test.leftSync.setState('disconnected')
    test.leftSync.setState('connecting')
    expect(findBadgeNode().style.display).toEqual('none')

    test.leftSync.connected = false
    test.leftSync.setState('wait')
    test.leftSync.setState('connecting')
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.connecting)
  })
})

it('handles error', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    test.leftSync.emitter.emit('error', { type: 'any error' })
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.error)
  })
})

it('handles server errors', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    var errorOPTS = {
      testError: 'testError',
      supported: [1, 2, 3],
      used: [1, 2, 3]
    }

    var error = new SyncError(test.leftSync, 'wrong-format')
    test.leftSync.emitter.emit('error', error)
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.error)

    error = new SyncError(test.leftSync, 'wrong-subprotocol', errorOPTS)
    test.leftSync.emitter.emit('error', error)
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.protocolError)

    error = new SyncError(test.leftSync, 'wrong-protocol', errorOPTS)
    test.leftSync.emitter.emit('error', error)
    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.protocolError)
  })
})

it('handles client error', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, OPTS)

    var error = new SyncError(test.leftSync, 'test', 'type', true)
    test.leftSync.emitter.emit('clientError', error)

    expect(findBadgeNode().style.display).toEqual('block')
    expect(findBadgeNode().style.backgroundImage).toEqual('url(IMAGE_MOCK)')
    expect(getBadgeMessage()).toEqual(messages.error)
  })
})

it('handles bottom and left side of position setting', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, merge(OPTS, { position: 'bottom-left' }))

    expect(findBadgeNode().style.bottom).toEqual('0px')
    expect(findBadgeNode().style.left).toEqual('0px')
  })
})

it('handles top and right side of position setting', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, merge(OPTS, { position: 'top-right' }))

    expect(findBadgeNode().style.top).toEqual('0px')
    expect(findBadgeNode().style.right).toEqual('0px')
  })
})

it('handles center/middle position setting', function () {
  return createTest().then(function (test) {
    badge({ sync: test.leftSync }, merge(OPTS, { position: 'center-middle' }))

    expect(findBadgeNode().style.top).toEqual('50%')
    expect(findBadgeNode().style.left).toEqual('50%')
    expect(findBadgeNode().style.transform).toEqual('translate(-50%, -50%)')
  })
})

it('returns unbind function and remove badge from DOM', function () {
  return createTest().then(function (test) {
    var unbind = badge({ sync: test.leftSync }, OPTS)
    unbind()

    test.leftSync.emitter.emit('error', { type: 'wrong-protocol' })
    expect(findBadgeNode()).toEqual(null)
  })
})
<|endoftext|>


<commit_before>describe('programmatic use', function () {
  it('should start', function(done) {
    this.timeout(20000);
    var timedout;
    var selenium = require('./index.js');
    var proc = selenium.start({ stdio: 'pipe' });

    proc.stdout.on('data', function(data) {
      var line = data.toString().trim();
      if (line.indexOf('Started SocketListener on 0.0.0.0:4444') > -1) {
        proc.kill();
        clearTimeout(timedout);
        done();
      }
    });

    timedout = setTimeout(function() {
      proc.kill();
      done(new Error('Server never started'));
    }, 20000);
  })
});<commit_msg>selenium 2.43.1 outputs info log on stderr, joke?<commit_after>describe('programmatic use', function () {
  it('should start', function(done) {
    this.timeout(20000);
    var timedout;
    var selenium = require('./index.js');
    var proc = selenium.start({ stdio: 'pipe' });

    // selenium 2.43.1 now outputs its info log on stderr
    ['stderr', 'stdout'].forEach(function(output) {
      proc[output].on('data', function seleniumSays(data) {
        var line = data.toString().trim();
        if (line.indexOf('Started SocketListener on 0.0.0.0:4444') > -1) {
          proc.kill();
          clearTimeout(timedout);
          done();
        }
      })
    });

    timedout = setTimeout(function() {
      proc.kill();
      done(new Error('Server never started'));
    }, 20000);
  })
});<|endoftext|>


<commit_before>import {Pos} from "../model"
import {toDOM, renderNodeToDOM} from "../serialize/dom"

import {elt} from "../dom"

const nonEditable = {html_block: true, html_tag: true, horizontal_rule: true}

function options(path, ranges) {
  return {
    onRender(node, dom, offset) {
      if (!node.isInline && offset != null)
        dom.setAttribute("pm-path", offset)
      if (nonEditable.hasOwnProperty(node.type.name))
        dom.contentEditable = false
      return dom
    },
    renderInlineFlat(node, dom, offset) {
      ranges.advanceTo(new Pos(path, offset))
      let end = new Pos(path, offset + node.offset)
      let nextCut = ranges.nextChangeBefore(end)

      let inner = dom, wrapped
      for (let i = 0; i < node.styles.length; i++) inner = inner.firstChild

      if (dom.nodeType != 1) {
        dom = elt("span", null, dom)
        if (!nextCut) wrapped = dom
      }
      if (!wrapped && (nextCut || ranges.current.length)) {
        wrapped = inner == dom ? (dom = elt("span", null, inner))
                               : inner.parentNode.appendChild(elt("span", null, inner))
      }

      dom.setAttribute("pm-span", offset + "-" + end.offset)
      if (!node.isText)
        dom.setAttribute("pm-span-atom", "true")

      let inlineOffset = 0
      while (nextCut) {
        let size = nextCut - offset
        let split = splitSpan(wrapped, size)
        if (ranges.current.length)
          split.className = ranges.current.join(" ")
        split.setAttribute("pm-span-offset", inlineOffset)
        inlineOffset += size
        offset += size
        ranges.advanceTo(new Pos(path, offset))
        if (!(nextCut = ranges.nextChangeBefore(end)))
          wrapped.setAttribute("pm-span-offset", inlineOffset)
      }

      if (ranges.current.length)
        wrapped.className = ranges.current.join(" ")
      return dom
    },
    document: document,
    path: path
  }
}

function splitSpan(span, at) {
  let textNode = span.firstChild, text = textNode.nodeValue
  let newNode = span.parentNode.insertBefore(elt("span", null, text.slice(0, at)), span)
  textNode.nodeValue = text.slice(at)
  return newNode
}

export function draw(pm, doc) {
  pm.content.textContent = ""
  pm.content.appendChild(toDOM(doc, options([], pm.ranges.activeRangeTracker())))
}

function deleteNextNodes(parent, at, amount) {
  for (let i = 0; i < amount; i++) {
    let prev = at
    at = at.nextSibling
    parent.removeChild(prev)
  }
  return at
}

export function redraw(pm, dirty, doc, prev) {
  let ranges = pm.ranges.activeRangeTracker()
  let path = []

  function scan(dom, node, prev) {
    let status = [], inPrev = [], inNode = []
    for (let i = 0, j = 0; i < prev.length && j < node.width; i++) {
      let cur = prev.child(i), dirtyStatus = dirty.get(cur)
      status.push(dirtyStatus)
      let matching = dirtyStatus ? -1 : node.children.indexOf(cur, j)
      if (matching > -1) {
        inNode[i] = matching
        inPrev[matching] = i
        j = matching + 1
      }
    }

    if (node.isTextblock) {
      let needsBR = node.length == 0 ||
          node.lastChild.type == node.type.schema.nodes.hard_break
      let last = dom.lastChild, hasBR = last && last.nodeType == 1 && last.hasAttribute("pm-force-br")
      if (needsBR && !hasBR)
        dom.appendChild(elt("br", {"pm-force-br": "true"}))
      else if (!needsBR && hasBR)
        dom.removeChild(last)
    }

    let domPos = dom.firstChild, j = 0
    let block = node.isTextblock
    for (let i = 0, offset = 0; i < node.length; i++) {
      let child = node.child(i)
      if (!block) path.push(i)
      let found = inPrev[i]
      let nodeLeft = true
      if (found > -1) {
        domPos = deleteNextNodes(dom, domPos, found - j)
        j = found
      } else if (!block && j < prev.length && inNode[j] == null &&
                 status[j] != 2 && child.sameMarkup(prev.child(j))) {
        scan(domPos, child, prev.child(j))
      } else {
        dom.insertBefore(renderNodeToDOM(child, options(path, ranges), block ? offset : i), domPos)
        nodeLeft = false
      }
      if (nodeLeft) {
        if (block)
          domPos.setAttribute("pm-span", offset + "-" + (offset + child.offset))
        else
          domPos.setAttribute("pm-path", i)
        domPos = domPos.nextSibling
        j++
      }
      if (block) offset += child.offset
      else path.pop()
    }
    deleteNextNodes(dom, domPos, prev.length - j)
  }
  scan(pm.content, doc, prev)
}
<commit_msg>Check against null, not -1, in redraw test for found equivalent<commit_after>import {Pos} from "../model"
import {toDOM, renderNodeToDOM} from "../serialize/dom"

import {elt} from "../dom"

const nonEditable = {html_block: true, html_tag: true, horizontal_rule: true}

function options(path, ranges) {
  return {
    onRender(node, dom, offset) {
      if (!node.isInline && offset != null)
        dom.setAttribute("pm-path", offset)
      if (nonEditable.hasOwnProperty(node.type.name))
        dom.contentEditable = false
      return dom
    },
    renderInlineFlat(node, dom, offset) {
      ranges.advanceTo(new Pos(path, offset))
      let end = new Pos(path, offset + node.offset)
      let nextCut = ranges.nextChangeBefore(end)

      let inner = dom, wrapped
      for (let i = 0; i < node.styles.length; i++) inner = inner.firstChild

      if (dom.nodeType != 1) {
        dom = elt("span", null, dom)
        if (!nextCut) wrapped = dom
      }
      if (!wrapped && (nextCut || ranges.current.length)) {
        wrapped = inner == dom ? (dom = elt("span", null, inner))
                               : inner.parentNode.appendChild(elt("span", null, inner))
      }

      dom.setAttribute("pm-span", offset + "-" + end.offset)
      if (!node.isText)
        dom.setAttribute("pm-span-atom", "true")

      let inlineOffset = 0
      while (nextCut) {
        let size = nextCut - offset
        let split = splitSpan(wrapped, size)
        if (ranges.current.length)
          split.className = ranges.current.join(" ")
        split.setAttribute("pm-span-offset", inlineOffset)
        inlineOffset += size
        offset += size
        ranges.advanceTo(new Pos(path, offset))
        if (!(nextCut = ranges.nextChangeBefore(end)))
          wrapped.setAttribute("pm-span-offset", inlineOffset)
      }

      if (ranges.current.length)
        wrapped.className = ranges.current.join(" ")
      return dom
    },
    document: document,
    path: path
  }
}

function splitSpan(span, at) {
  let textNode = span.firstChild, text = textNode.nodeValue
  let newNode = span.parentNode.insertBefore(elt("span", null, text.slice(0, at)), span)
  textNode.nodeValue = text.slice(at)
  return newNode
}

export function draw(pm, doc) {
  pm.content.textContent = ""
  pm.content.appendChild(toDOM(doc, options([], pm.ranges.activeRangeTracker())))
}

function deleteNextNodes(parent, at, amount) {
  for (let i = 0; i < amount; i++) {
    let prev = at
    at = at.nextSibling
    parent.removeChild(prev)
  }
  return at
}

export function redraw(pm, dirty, doc, prev) {
  let ranges = pm.ranges.activeRangeTracker()
  let path = []

  function scan(dom, node, prev) {
    let status = [], inPrev = [], inNode = []
    for (let i = 0, j = 0; i < prev.length && j < node.width; i++) {
      let cur = prev.child(i), dirtyStatus = dirty.get(cur)
      status.push(dirtyStatus)
      let matching = dirtyStatus ? -1 : node.children.indexOf(cur, j)
      if (matching > -1) {
        inNode[i] = matching
        inPrev[matching] = i
        j = matching + 1
      }
    }

    if (node.isTextblock) {
      let needsBR = node.length == 0 ||
          node.lastChild.type == node.type.schema.nodes.hard_break
      let last = dom.lastChild, hasBR = last && last.nodeType == 1 && last.hasAttribute("pm-force-br")
      if (needsBR && !hasBR)
        dom.appendChild(elt("br", {"pm-force-br": "true"}))
      else if (!needsBR && hasBR)
        dom.removeChild(last)
    }

    let domPos = dom.firstChild, j = 0
    let block = node.isTextblock
    for (let i = 0, offset = 0; i < node.length; i++) {
      let child = node.child(i)
      if (!block) path.push(i)
      let found = inPrev[i]
      let nodeLeft = true
      if (found != null) {
        domPos = deleteNextNodes(dom, domPos, found - j)
        j = found
      } else if (!block && j < prev.length && inNode[j] == null &&
                 status[j] != 2 && child.sameMarkup(prev.child(j))) {
        scan(domPos, child, prev.child(j))
      } else {
        dom.insertBefore(renderNodeToDOM(child, options(path, ranges), block ? offset : i), domPos)
        nodeLeft = false
      }
      if (nodeLeft) {
        if (block)
          domPos.setAttribute("pm-span", offset + "-" + (offset + child.offset))
        else
          domPos.setAttribute("pm-path", i)
        domPos = domPos.nextSibling
        j++
      }
      if (block) offset += child.offset
      else path.pop()
    }
    deleteNextNodes(dom, domPos, prev.length - j)
  }
  scan(pm.content, doc, prev)
}
<|endoftext|>


<commit_before>var Openphacts = require("../../../src/OPS.js");
jasmine.getEnv().defaultTimeoutInterval = 30000;

describe("Concept Wiki", function() {
  var searcher, appID, appKey, appUrl;

  beforeEach(function() {
        appID = process.env['app_id'];
        appKey = process.env['app_key'];
        appUrl = process.env['app_url'];
        searcher = new ConceptWikiSearch(appUrl, appID, appKey);
  });

  describe("search by tag", function() {

    it("and return a response for compounds", function() {
	    var this_success = null;
      var this_status = null;
      var this_result = null;

      var callback=function(success, status, response){
        this_success = success;
        this_status = status;
        this_result = searcher.parseResponse(response);
      };

      waitsFor(function() {
        return this_result != null;
      });

      runs(function() {
        expect(this_result[0]).toBeDefined();
        expect(this_result[0].uri).toBeDefined();
        expect(this_result[0].prefLabel).toBeDefined();
        expect(this_result[0].match).toBeDefined();
      });
      searcher.byTag('Aspirin', '20', '4', '07a84994-e464-4bbf-812a-a4b96fa3d197', callback);
    });
    it("and return a response for targets", function() {
      var this_success = null;
      var this_status = null;
      var this_result = null;

      var callback=function(success, status, response){
        this_success = success;
        this_status = status;
        this_result = searcher.parseResponse(response);
      };

      waitsFor(function() {
        return this_result != null;
      });

      runs(function() {
        expect(this_result[0]).toBeDefined();
        expect(this_result[0].uri).toBeDefined();
        expect(this_result[0].prefLabel).toBeDefined();
        expect(this_result[0].match).toBeDefined();
      });
      searcher.byTag('Aspirin', '20', '3', 'eeaec894-d856-4106-9fa1-662b1dc6c6f1', callback);
    });
    it("and handle errors", function() {
      var this_success = null;
      var this_status = null;
      var this_result = null;

      var callback=function(success, status, response){
        this_success = success;
        this_status = status;
      };

      waitsFor(function() {
        return this_status != null;
      });

      runs(function() {
        expect(this_success).toEqual(false);
        expect(this_status).toEqual(500);
      });
      searcher.byTag('Aspirin', '20', '4', '07a84994-e464-4b96fa3d197', callback);
    });
  });
  describe("search for compounds", function() {

    it("and return a response for compounds", function() {
      var this_success = null;
      var this_status = null;
      var this_result = null;

      var callback=function(success, status, response){
        this_success = success;
        this_status = status;
        this_result = searcher.parseResponse(response);
      };

      waitsFor(function() {
        return this_result != null;
      });

      runs(function() {
        expect(this_success).toEqual(true);
        expect(this_status).toEqual(200);
        expect(this_result[0]).toBeDefined();
      });
      searcher.findCompounds('Aspirin', '20', '4', callback);
    });
    it("and handle errors", function() {
      var this_success = null;
      var this_status = null;
      var this_result = null;

      var callback=function(success, status, response){
        this_success = success;
        this_status = status;
      };

      waitsFor(function() {
        return this_status != null;
      });

      runs(function() {
        expect(this_success).toEqual(false);
        expect(this_status).toEqual(500);
      });
      searcher.findCompounds('Aspirin', '-1', '-1', callback);
    });
 });

  describe("search for targets", function() {

    it("and return a response for targets", function() {
      var this_success = null;
      var this_status = null;
      var this_result = null;

      var callback=function(success, status, response){
        this_success = success;
        this_status = status;
        this_result = searcher.parseResponse(response);
      };

      waitsFor(function() {
        return this_result != null;
      });

      runs(function() {
        expect(this_success).toEqual(true);
        expect(this_status).toEqual(200);
        expect(this_result[0]).toBeDefined();
        expect(this_result[0].uri).toBeDefined();
        expect(this_result[0].prefLabel).toBeDefined();
        expect(this_result[0].match).toBeDefined();
      });
      searcher.findTargets('PDE5', '20', '3', callback);
    });
    it("and handle errors", function() {
      var callback=function(success, status){
        expect(this_success).toEqual(false);
        expect(this_status).toEqual(500);
      };
      var this_success = null;
      var this_status = null;
      var this_result = null;

      var callback=function(success, status, response){
        this_success = success;
        this_status = status;
      };

      waitsFor(function() {
        return this_status != null;
      });

      runs(function() {
        expect(this_success).toEqual(false);
        expect(this_status).toEqual(500);
      });
      searcher.findTargets('PDE5', '-1', '-1', callback);
    });
    it("and handle an empty response for compounds", function() {
      var this_success = null;
      var this_status = null;
      var this_result = null;

      var callback=function(success, status, response){
        this_success = success;
        this_status = status;
        this_result = searcher.parseResponse(response);
      };

      waitsFor(function() {
        return this_result != null;
      });

      runs(function() {
        expect(this_success).toEqual(true);
        expect(this_status).toEqual(200);
      });
      searcher.findCompounds('Sorafenib', '20', '4', callback);
    });
 });
  describe("find a single concept", function() {

    it("and return a response", function() {
      var this_success = null;
      var this_status = null;
      var this_result = null;

      var callback=function(success, status, response){
        this_success = success;
        this_status = status;
        this_result = searcher.parseFindConceptResponse(response);
      };

      waitsFor(function() {
        return this_result != null;
      });

      runs(function() {
        expect(this_success).toEqual(true);
        expect(this_status).toEqual(200);
        expect(this_result.altLabels).toBeDefined();
        expect(this_result.prefLabel).toBeDefined();
        expect(this_result.definition).toBeDefined();
      });
      searcher.findConcept('38932552-111f-4a4e-a46a-4ed1d7bdf9d5', 4, callback);
    });
    it("and handle errors", function() {
      var this_success = null;
      var this_status = null;
      var this_result = null;

      var callback=function(success, status, response){
        this_success = success;
        this_status = status;
      };

      waitsFor(function() {
        return this_status != null;
      });

      runs(function() {
        expect(this_success).toEqual(false);
        expect(this_status).toEqual(500);
      });
      searcher.findConcept('07a84994-e464-4b96fa3d197', 4, callback);
    });
  });
  describe("free text search", function() {

    it("and return a response", function() {
      var this_success = null;
      var this_status = null;
      var this_response = null;

      var callback=function(success, status, response){
        this_success = success;
        this_status = status;
        this_response = response;
      };

      waitsFor(function() {
        return this_response != null;
      });

      runs(function() {
        expect(this_success).toEqual(true);
        expect(this_status).toEqual(200);
        expect(this_response).not.toBeNull();
      });
      searcher.freeText('Aspirin', null, null, callback);
    });
  });
});
<commit_msg>CW no longer used<commit_after><|endoftext|>


<commit_before>// Copyright 2019-2020, University of Colorado Boulder

/**
 * Puts a Node on a rectangular background, dynamically sized to fit the Node.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */

import merge from '../../phet-core/js/merge.js';
import Node from '../../scenery/js/nodes/Node.js';
import Rectangle from '../../scenery/js/nodes/Rectangle.js';
import sceneryPhet from './sceneryPhet.js';

class BackgroundNode extends Node {

  /**
   * @param {Node} node - the Node that will be put on the background
   * @param {Object} [options]
   */
  constructor( node, options ) {

    options = merge( {
      xMargin: 2, // set the x margin between the Node content and background edge
      yMargin: 2, // set the y margin between the Node content and background edge

      // Options passed to the background Rectangle
      backgroundOptions: {
        fill: 'white',
        opacity: 0.75
      }
    }, options );

    super();

    // @public (read-only) {Rectangle} - translucent rectangle
    this.background = new Rectangle( 0, 0, 1, 1, options.backgroundOptions );

    // size the rectangle to fit the node
    node.boundsProperty.lazyLink( () => {
      this.background.setRect( 0, 0, node.width + 2 * options.xMargin, node.height + 2 * options.yMargin );
      node.center = this.background.center;
    } );

    assert && assert( !options.children, 'BackgroundNode sets children' );
    options.children = [ this.background, node ];
    this.mutate( options );
  }
}

sceneryPhet.register( 'BackgroundNode', BackgroundNode );
export default BackgroundNode;<commit_msg>made size update run on construction and check if bounds are finite, see https://github.com/phetsims/scenery-phet/issues/603<commit_after>// Copyright 2019-2020, University of Colorado Boulder

/**
 * Puts a Node on a rectangular background, dynamically sized to fit the Node.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */

import merge from '../../phet-core/js/merge.js';
import Node from '../../scenery/js/nodes/Node.js';
import Rectangle from '../../scenery/js/nodes/Rectangle.js';
import sceneryPhet from './sceneryPhet.js';

class BackgroundNode extends Node {

  /**
   * @param {Node} node - the Node that will be put on the background
   * @param {Object} [options]
   */
  constructor( node, options ) {

    options = merge( {
      xMargin: 2, // set the x margin between the Node content and background edge
      yMargin: 2, // set the y margin between the Node content and background edge

      // Options passed to the background Rectangle
      backgroundOptions: {
        fill: 'white',
        opacity: 0.75
      }
    }, options );

    super();

    // @public (read-only) {Rectangle} - translucent rectangle
    this.background = new Rectangle( 0, 0, 1, 1, options.backgroundOptions );

    // size the rectangle to fit the node
    node.boundsProperty.link( bounds => {
      if ( !bounds.isEmpty() ) {
        this.background.setRect( 0, 0, node.width + 2 * options.xMargin, node.height + 2 * options.yMargin );
        node.center = this.background.center;
      }
    } );

    assert && assert( !options.children, 'BackgroundNode sets children' );
    options.children = [ this.background, node ];
    this.mutate( options );
  }
}

sceneryPhet.register( 'BackgroundNode', BackgroundNode );
export default BackgroundNode;<|endoftext|>


<commit_before>'use strict'

const test = require('tape')
const protochain = require('./')

test('protochain', t => {
  t.test('finds correct prototype chain', t => {
    const obj = {}
    strictEqualArray(t, protochain(obj), [Object.prototype])
    strictEqualArray(t, protochain(Object.create(obj)), [obj, Object.prototype])
    strictEqualArray(t, protochain(new Error('message')), [Error.prototype, Object.prototype])
    strictEqualArray(t, protochain(new TypeError('message')), [TypeError.prototype, Error.prototype, Object.prototype])
    /* eslint-disable no-new-wrappers */
    strictEqualArray(t, protochain(new String()), [String.prototype, Object.prototype])
    strictEqualArray(t, protochain(new Number()), [Number.prototype, Object.prototype])
    /* eslint-enable no-new-wrappers */
    strictEqualArray(t, protochain(new RegExp('abc')), [RegExp.prototype, Object.prototype])
    strictEqualArray(t, protochain(new Date()), [Date.prototype, Object.prototype])
    t.end()
  })

  t.test('null prototype is handled correctly', t => {
    const noProtoObject = Object.create(null)
    strictEqualArray(t, protochain(noProtoObject), [])
    strictEqualArray(t, protochain(Object.create(noProtoObject)), [noProtoObject])
    t.end()
  })

  t.test('falsey valueOf', t => {
    const obj = {
      valueOf () {
        return false
      }
    }

    strictEqualArray(t, protochain(obj), [Object.prototype])
    t.end()
  })

  t.test('non-object values cooerce to object counterparts correctly', t => {
    strictEqualArray(t, protochain('abc'), [String.prototype, Object.prototype])
    strictEqualArray(t, protochain(123), [Number.prototype, Object.prototype])
    strictEqualArray(t, protochain(/abc/), [RegExp.prototype, Object.prototype])
    strictEqualArray(t, protochain(true), [Boolean.prototype, Object.prototype])
    t.test('falsey values with prototypes', t => {
      strictEqualArray(t, protochain(NaN), [Number.prototype, Object.prototype])
      strictEqualArray(t, protochain(false), [Boolean.prototype, Object.prototype])
      strictEqualArray(t, protochain(''), [String.prototype, Object.prototype])
      strictEqualArray(t, protochain(0), [Number.prototype, Object.prototype])
      t.end()
    })
    t.end()
  })

  t.test('null values produce empty list', t => {
    strictEqualArray(t, protochain(), [])
    strictEqualArray(t, protochain(undefined), [])
    strictEqualArray(t, protochain(null), [])
    t.end()
  })

  t.test('ES5 inheritance', t => {
    function Person () {}
    function FancyPerson () {
      Person.call(this)
    }
    FancyPerson.prototype = Object.create(Person.prototype)

    strictEqualArray(t, protochain(new Person()), [Person.prototype, Object.prototype])
    strictEqualArray(t, protochain(new FancyPerson()), [FancyPerson.prototype, Person.prototype, Object.prototype])
    t.end()
  })

  t.test('ES6 inheritance', t => {
    // note this will be compiled to ES5 in the test
    class Person {}
    class FancyPerson extends Person {}

    strictEqualArray(t, protochain(new Person()), [Person.prototype, Object.prototype])
    strictEqualArray(t, protochain(new FancyPerson()), [FancyPerson.prototype, Person.prototype, Object.prototype])
    t.end()
  })

  // new native types which may not be supported
  if (typeof Symbol !== 'undefined') {
    t.test('symbol support', t => {
      const foo = Symbol('foo')
      strictEqualArray(t, protochain(foo), [Symbol.prototype, Object.prototype])
      t.end()
    })
  }

  if (typeof Promise !== 'undefined') {
    t.test('promise support', t => {
      const foo = Promise.resolve()
      strictEqualArray(t, protochain(foo), [Promise.prototype, Object.prototype])
      t.end()
    })
  }

  if (typeof Map !== 'undefined') {
    t.test('collections support', t => {
      strictEqualArray(t, protochain(new Map()), [Map.prototype, Object.prototype])
      strictEqualArray(t, protochain(new Set()), [Set.prototype, Object.prototype])
      strictEqualArray(t, protochain(new WeakMap()), [WeakMap.prototype, Object.prototype])
      strictEqualArray(t, protochain(new WeakSet()), [WeakSet.prototype, Object.prototype])
      t.end()
    })
  }

  if (typeof Uint8Array !== 'undefined') {
    t.test('typed array support', t => {
      const TypedArray = Object.getPrototypeOf(Int8Array.prototype)
      strictEqualArray(t, protochain(new Int8Array()), [Int8Array.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Uint8Array()), [Uint8Array.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Uint8ClampedArray()), [Uint8ClampedArray.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Int16Array()), [Int16Array.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Int32Array()), [Int32Array.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Uint32Array()), [Uint32Array.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Float32Array()), [Float32Array.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Float64Array()), [Float64Array.prototype, TypedArray, Object.prototype])
      t.end()
    })
  }

  t.end()
})

function strictEqualArray (t, a, b) {
  a.forEach((item, index) => t.strictEqual(a[index], b[index], `strictEqual at index ${index}`))
  t.equal(a.length, b.length, 'same length')
}
<commit_msg>Add test for functions.<commit_after>'use strict'

const test = require('tape')
const protochain = require('./')

test('protochain', t => {
  t.test('finds correct prototype chain', t => {
    const obj = {}
    strictEqualArray(t, protochain(obj), [Object.prototype])
    strictEqualArray(t, protochain(Object.create(obj)), [obj, Object.prototype])
    strictEqualArray(t, protochain(new Error('message')), [Error.prototype, Object.prototype])
    strictEqualArray(t, protochain(new TypeError('message')), [TypeError.prototype, Error.prototype, Object.prototype])
    /* eslint-disable no-new-wrappers */
    strictEqualArray(t, protochain(new String()), [String.prototype, Object.prototype])
    strictEqualArray(t, protochain(new Number()), [Number.prototype, Object.prototype])
    /* eslint-enable no-new-wrappers */
    /* eslint-disable no-new-func */
    strictEqualArray(t, protochain(new Function()), [Function.prototype, Object.prototype])
    /* eslint-enable no-new-func */
    strictEqualArray(t, protochain(new RegExp('abc')), [RegExp.prototype, Object.prototype])
    strictEqualArray(t, protochain(new Date()), [Date.prototype, Object.prototype])
    t.end()
  })

  t.test('null prototype is handled correctly', t => {
    const noProtoObject = Object.create(null)
    strictEqualArray(t, protochain(noProtoObject), [])
    strictEqualArray(t, protochain(Object.create(noProtoObject)), [noProtoObject])
    t.end()
  })

  t.test('falsey valueOf', t => {
    const obj = {
      valueOf () {
        return false
      }
    }

    strictEqualArray(t, protochain(obj), [Object.prototype])
    t.end()
  })

  t.test('non-object values cooerce to object counterparts correctly', t => {
    strictEqualArray(t, protochain(function () {}), [Function.prototype, Object.prototype])
    strictEqualArray(t, protochain('abc'), [String.prototype, Object.prototype])
    strictEqualArray(t, protochain(123), [Number.prototype, Object.prototype])
    strictEqualArray(t, protochain(/abc/), [RegExp.prototype, Object.prototype])
    strictEqualArray(t, protochain(true), [Boolean.prototype, Object.prototype])
    t.test('falsey values with prototypes', t => {
      strictEqualArray(t, protochain(NaN), [Number.prototype, Object.prototype])
      strictEqualArray(t, protochain(false), [Boolean.prototype, Object.prototype])
      strictEqualArray(t, protochain(''), [String.prototype, Object.prototype])
      strictEqualArray(t, protochain(0), [Number.prototype, Object.prototype])
      t.end()
    })
    t.end()
  })

  t.test('null values produce empty list', t => {
    strictEqualArray(t, protochain(), [])
    strictEqualArray(t, protochain(undefined), [])
    strictEqualArray(t, protochain(null), [])
    t.end()
  })

  t.test('ES5 inheritance', t => {
    function Person () {}
    function FancyPerson () {
      Person.call(this)
    }
    FancyPerson.prototype = Object.create(Person.prototype)

    strictEqualArray(t, protochain(new Person()), [Person.prototype, Object.prototype])
    strictEqualArray(t, protochain(new FancyPerson()), [FancyPerson.prototype, Person.prototype, Object.prototype])
    t.end()
  })

  t.test('ES6 inheritance', t => {
    // note this will be compiled to ES5 in the test
    class Person {}
    class FancyPerson extends Person {}

    strictEqualArray(t, protochain(new Person()), [Person.prototype, Object.prototype])
    strictEqualArray(t, protochain(new FancyPerson()), [FancyPerson.prototype, Person.prototype, Object.prototype])
    t.end()
  })

  // new native types which may not be supported
  if (typeof Symbol !== 'undefined') {
    t.test('symbol support', t => {
      const foo = Symbol('foo')
      strictEqualArray(t, protochain(foo), [Symbol.prototype, Object.prototype])
      t.end()
    })
  }

  if (typeof Promise !== 'undefined') {
    t.test('promise support', t => {
      const foo = Promise.resolve()
      strictEqualArray(t, protochain(foo), [Promise.prototype, Object.prototype])
      t.end()
    })
  }

  if (typeof Map !== 'undefined') {
    t.test('collections support', t => {
      strictEqualArray(t, protochain(new Map()), [Map.prototype, Object.prototype])
      strictEqualArray(t, protochain(new Set()), [Set.prototype, Object.prototype])
      strictEqualArray(t, protochain(new WeakMap()), [WeakMap.prototype, Object.prototype])
      strictEqualArray(t, protochain(new WeakSet()), [WeakSet.prototype, Object.prototype])
      t.end()
    })
  }

  if (typeof Uint8Array !== 'undefined') {
    t.test('typed array support', t => {
      const TypedArray = Object.getPrototypeOf(Int8Array.prototype)
      strictEqualArray(t, protochain(new Int8Array()), [Int8Array.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Uint8Array()), [Uint8Array.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Uint8ClampedArray()), [Uint8ClampedArray.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Int16Array()), [Int16Array.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Int32Array()), [Int32Array.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Uint32Array()), [Uint32Array.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Float32Array()), [Float32Array.prototype, TypedArray, Object.prototype])
      strictEqualArray(t, protochain(new Float64Array()), [Float64Array.prototype, TypedArray, Object.prototype])
      t.end()
    })
  }

  t.end()
})

function strictEqualArray (t, a, b) {
  a.forEach((item, index) => t.strictEqual(a[index], b[index], `strictEqual at index ${index}`))
  t.equal(a.length, b.length, 'same length')
}
<|endoftext|>


<commit_before>/*!
 * Copyright 2013 Apereo Foundation (AF) Licensed under the
 * Educational Community 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://opensource.org/licenses/ECL-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 events = require('events');

/**
 * ### Events
 *
 * * `ready` - Emitted when server start-up has completed and the application is ready to start accepting requests
 */
var emitter = module.exports = new events.EventEmitter();<commit_msg>Add new line to EOF<commit_after>/*!
 * Copyright 2013 Apereo Foundation (AF) Licensed under the
 * Educational Community 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://opensource.org/licenses/ECL-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 events = require('events');

/**
 * ### Events
 *
 * * `ready` - Emitted when server start-up has completed and the application is ready to start accepting requests
 */
var emitter = module.exports = new events.EventEmitter();
<|endoftext|>


<commit_before>var MELBPARKING = MELBPARKING || {};

(function() {

  MELBPARKING.D3LeafletLayer = {

    init: function(currentDate) {

        //SVG Display
        var svg = d3.select(MELBPARKING.Map.map.getPanes().overlayPane).append("svg"),
            g = svg.append("g").attr("class", "leaflet-zoom-hide");

        d3.json("data/ParkingLocations.json", function(collection) {

          MELBPARKING.Map.parkingSliderControl.init(refreshTime);

          MELBPARKING.DataProcessor.initStats();

          for (var i = 0; i < collection.features.length; i++) {

            var parkingTimeArray = MELBPARKING.DataProcessor.parkingSpotTimeArray(collection.features[i]);
            collection.features[i].properties.parkingData = parkingTimeArray;

          }

          var transform = d3.geo.transform({point: projectPoint}),
              path = d3.geo.path().projection(transform);

          var feature = g.selectAll("path")
              .data(collection.features)
            .enter().append("path")
            .on({
              "mouseover": function(d) { MELBPARKING.Map.info.update(d); },
              "mouseout":  function(d) { MELBPARKING.Map.info.clearInfo(); },
              "click":  function(d) { console.log(d); },
            });

          MELBPARKING.Map.map.on("viewreset", reset);
          reset();
          refreshTime();
          MELBPARKING.Map.parkingDayStats.updateDayStats();

          MELBPARKING.Map.parkingTimeGraph.update(JSON.parse(JSON.stringify(MELBPARKING.DataProcessor.dayStats)));

        });

    },

    reset: function() {

      var bounds = path.bounds(collection),
                topLeft = bounds[0],
                bottomRight = bounds[1];

            svg .attr("width", bottomRight[0] - topLeft[0])
                .attr("height", bottomRight[1] - topLeft[1])
                .style("left", topLeft[0] + "px")
                .style("top", topLeft[1] + "px");

            g   .attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");

            feature.attr("d", path);

    },

    refreshTime: function() {

      var newDateObj = new Date(currentDate.getTime() + $( "#slider" ).slider( "value" )*60000);
          MELBPARKING.Map.parkingTimeControl.updateTime(newDateObj);

          var minute = $( "#slider" ).slider( "value" );

          //Updating Parking Stats Control
          MELBPARKING.Map.parkingDayStats.updateStats(MELBPARKING.DataProcessor.dayStats[minute]);

          d3.select(".leaflet-overlay-pane svg").selectAll("path").attr("class", function (d) {

                  var key = d.properties.bayId;
                  var bayStatus;

                  switch (d.properties.parkingData[minute]) {
                    case 0:
                      bayStatus = 'parking-no-monitoring';
                      break;
                    case 1:
                      bayStatus = 'parking-empty';
                      break;
                    case 2:
                      bayStatus = 'parking-taken';
                      break;
                    case 3:
                      bayStatus = 'parking-will-violate';
                      break;
                    case 4:
                      bayStatus = 'parking-in-violation';
                      break;
                  }

                  return bayStatus;

                });

    },
    projectPoint: function(x, y) {
      var point = MELBPARKING.Map.map.latLngToLayerPoint(new L.LatLng(y, x));
      this.stream.point(point.x, point.y);
    }

  };

}());<commit_msg>Refacted all var names to point to self<commit_after>var MELBPARKING = MELBPARKING || {};

(function() {

  MELBPARKING.D3LeafletLayer = {

    init: function(currentDate) {

        var self = this;

        this._currentDate = currentDate;

        //SVG Display
        this._svg = d3.select(MELBPARKING.Map.map.getPanes().overlayPane).append("svg");
        this._g = this._svg.append("g").attr("class", "leaflet-zoom-hide");

        d3.json("data/ParkingLocations.json", function(collection) {

          self._collection = collection;

          MELBPARKING.Map.parkingSliderControl.init(self.refreshTime);

          MELBPARKING.DataProcessor.initStats();

          for (var i = 0; i < collection.features.length; i++) {

            var parkingTimeArray = MELBPARKING.DataProcessor.parkingSpotTimeArray(collection.features[i]);
            collection.features[i].properties.parkingData = parkingTimeArray;

          }

          var transform = d3.geo.transform({point: self.projectPoint});
          self._path = d3.geo.path().projection(transform);

          self._feature = self._g.selectAll("path")
              .data(collection.features)
            .enter().append("path")
            .on({
              "mouseover": function(d) { MELBPARKING.Map.info.update(d); },
              "mouseout":  function(d) { MELBPARKING.Map.info.clearInfo(); },
              "click":  function(d) { console.log(d); },
            });

          MELBPARKING.Map.map.on("viewreset", self.reset);
          self.reset();
          self.refreshTime();
          MELBPARKING.Map.parkingDayStats.updateDayStats();

          MELBPARKING.Map.parkingTimeGraph.update(JSON.parse(JSON.stringify(MELBPARKING.DataProcessor.dayStats)));

        });

    },

    reset: function() {

      var bounds = this._path.bounds(this._collection),
                topLeft = bounds[0],
                bottomRight = bounds[1];

            this._svg .attr("width", bottomRight[0] - topLeft[0])
                .attr("height", bottomRight[1] - topLeft[1])
                .style("left", topLeft[0] + "px")
                .style("top", topLeft[1] + "px");

            this._g.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");

            this._feature.attr("d", this._path);

    },

    refreshTime: function() {

      //TODO:
      //Dont do this
      var currentDate = MELBPARKING.D3LeafletLayer._currentDate;

      var newDateObj = new Date(currentDate.getTime() + $( "#slider" ).slider( "value" )*60000);
          MELBPARKING.Map.parkingTimeControl.updateTime(newDateObj);

          var minute = $( "#slider" ).slider( "value" );

          //Updating Parking Stats Control
          MELBPARKING.Map.parkingDayStats.updateStats(MELBPARKING.DataProcessor.dayStats[minute]);

          d3.select(".leaflet-overlay-pane svg").selectAll("path").attr("class", function (d) {

                  var key = d.properties.bayId;
                  var bayStatus;

                  switch (d.properties.parkingData[minute]) {
                    case 0:
                      bayStatus = 'parking-no-monitoring';
                      break;
                    case 1:
                      bayStatus = 'parking-empty';
                      break;
                    case 2:
                      bayStatus = 'parking-taken';
                      break;
                    case 3:
                      bayStatus = 'parking-will-violate';
                      break;
                    case 4:
                      bayStatus = 'parking-in-violation';
                      break;
                  }

                  return bayStatus;

                });

    },
    projectPoint: function(x, y) {
      var point = MELBPARKING.Map.map.latLngToLayerPoint(new L.LatLng(y, x));
      this.stream.point(point.x, point.y);
    }

  };

}());<|endoftext|>


<commit_before>const test = require('ava');
const random = require('./helpers/random');
const all = require('./products');

test('all()', t => {
  t.true(all.length < 0);
});

test('random()', t => {
  t.truthy(random(all));
  t.not(random(all), random(all));
});
<commit_msg>🤖(fix): 2017-7-17 09:59:31<commit_after>const test = require('ava');
const random = require('./helpers/random');
const all = require('./products');

test('all()', t => {
  t.true(all.length > 0);
});

test('random()', t => {
  t.truthy(random(all));
  t.not(random(all), random(all));
});
<|endoftext|>


<commit_before>var fs = require('fs'),
Channel = require('../channel').Channel,
Hoptoad = require('../hoptoad'),

env = require('../../environment');

var MAX_PRELOAD_BYTES = 1024 * 8;

if(!env.logs.channels.disabled)
	fs.mkdir(env.logs.channels.root, 0666, Hoptoad.notifyCallback);

if(!Channel.prototype.send_initial_data) throw "Channel.send_initial_data function no longer exists - need to refactor channel logger!";

function ChannelLogger(channel){
	this.channel = channel;
	this._suspendChannel();
	var self = this;
	this.openFile();
	channel.on('msg', function(msg){
		self.logMessage(msg)
	});
}

var proto = ChannelLogger.prototype;

proto._suspendChannel = function(){
	var chan = this.channel;
	chan.bufferedParameters = [];

	chan.send_initial_data = function(){
		chan.bufferedParameters.push(Array.prototype.slice.call(arguments));
	}
}

proto._onLogLoaded = function(msgs){
	var chan = this.channel;

	if(msgs) chan.messages.prepend(msgs);
	var send = Channel.prototype.send_initial_data;

	chan.bufferedParameters.forEach(function(args){
		send.apply(chan, args);
	});

	delete chan.send_initial_data;
	delete chan.bufferedParameters;
}

proto._onNewFile = function(){
	var info = {
		name: this.channel.name,
		created: new Date().getTime()
	}
	this.log.write("//" + JSON.stringify(info) + "\n");
	this._onLogLoaded();
}

proto._onExistingFile = function(size){
	var read_size = Math.min(size, MAX_PRELOAD_BYTES),
	    self = this,
	    buf;

	fs.open(this.filePath, 'r', null, opened);

	function opened(err, fd){
		if(err) return self._onLogLoaded();
		buf = new Buffer(read_size);
		fs.read(fd, buf, 0, read_size, size - read_size, done_reading);
	}

	function done_reading(err, bytesRead){
		if(!err){
			lines = buf.toString('utf8').split("\n");
			// We read the file in an arbitrary spot, so the first line is likely only half there
			lines.shift();
			msgs = lines.map(function(line){
				var p = line.indexOf(':');
				if(p > 0) return JSON.parse(line.substr(p+1));
				else return null;
			}).filter(function(m){return m != null});
		}
		self._onLogLoaded(msgs);
	}
}

proto._onLoad = function(){
	var log = this.log;
	this.buffer.forEach(function(line){
		log.write(line);
	});
	delete this.buffer;
	this.loaded = true;
}

proto.openFile = function(){
	var self = this;
	this.filePath = env.logs.channels.root + '/' + this.hashChannelName() + ".log";
	this.log = fs.createWriteStream(this.filePath, {flags:'a'});
	this.loaded = false;
	this.buffer = [];

	fs.stat(this.filePath, function(err, stat){
		if(err || stat.size == 0) self._onNewFile();
		else self._onExistingFile(stat.size);
		self._onLoad();
	});
}

proto.hashChannelName = function(){
	return Channel.hashName(Channel.generalizeName(this.channel.name));
}

proto.logMessage = function(msg){
	var line = msg.t + ':' + JSON.stringify(msg) + "\n";
	if(this.loaded)
		this.log.write(line);
	else
		this.buffer.push(line);
}

module.exports = ChannelLogger;
<commit_msg>Up the channel log preload amount to 32K<commit_after>var fs = require('fs'),
Channel = require('../channel').Channel,
Hoptoad = require('../hoptoad'),

env = require('../../environment');

var MAX_PRELOAD_BYTES = 1024 * 32;

if(!env.logs.channels.disabled)
	fs.mkdir(env.logs.channels.root, 0666, Hoptoad.notifyCallback);

if(!Channel.prototype.send_initial_data) throw "Channel.send_initial_data function no longer exists - need to refactor channel logger!";

function ChannelLogger(channel){
	this.channel = channel;
	this._suspendChannel();
	var self = this;
	this.openFile();
	channel.on('msg', function(msg){
		self.logMessage(msg)
	});
}

var proto = ChannelLogger.prototype;

proto._suspendChannel = function(){
	var chan = this.channel;
	chan.bufferedParameters = [];

	chan.send_initial_data = function(){
		chan.bufferedParameters.push(Array.prototype.slice.call(arguments));
	}
}

proto._onLogLoaded = function(msgs){
	var chan = this.channel;

	if(msgs) chan.messages.prepend(msgs);
	var send = Channel.prototype.send_initial_data;

	chan.bufferedParameters.forEach(function(args){
		send.apply(chan, args);
	});

	delete chan.send_initial_data;
	delete chan.bufferedParameters;
}

proto._onNewFile = function(){
	var info = {
		name: this.channel.name,
		created: new Date().getTime()
	}
	this.log.write("//" + JSON.stringify(info) + "\n");
	this._onLogLoaded();
}

proto._onExistingFile = function(size){
	var read_size = Math.min(size, MAX_PRELOAD_BYTES),
	    self = this,
	    buf;

	fs.open(this.filePath, 'r', null, opened);

	function opened(err, fd){
		if(err) return self._onLogLoaded();
		buf = new Buffer(read_size);
		fs.read(fd, buf, 0, read_size, size - read_size, done_reading);
	}

	function done_reading(err, bytesRead){
		if(!err){
			lines = buf.toString('utf8').split("\n");
			// We read the file in an arbitrary spot, so the first line is likely only half there
			lines.shift();
			msgs = lines.map(function(line){
				var p = line.indexOf(':');
				if(p > 0) return JSON.parse(line.substr(p+1));
				else return null;
			}).filter(function(m){return m != null});
		}
		self._onLogLoaded(msgs);
	}
}

proto._onLoad = function(){
	var log = this.log;
	this.buffer.forEach(function(line){
		log.write(line);
	});
	delete this.buffer;
	this.loaded = true;
}

proto.openFile = function(){
	var self = this;
	this.filePath = env.logs.channels.root + '/' + this.hashChannelName() + ".log";
	this.log = fs.createWriteStream(this.filePath, {flags:'a'});
	this.loaded = false;
	this.buffer = [];

	fs.stat(this.filePath, function(err, stat){
		if(err || stat.size == 0) self._onNewFile();
		else self._onExistingFile(stat.size);
		self._onLoad();
	});
}

proto.hashChannelName = function(){
	return Channel.hashName(Channel.generalizeName(this.channel.name));
}

proto.logMessage = function(msg){
	var line = msg.t + ':' + JSON.stringify(msg) + "\n";
	if(this.loaded)
		this.log.write(line);
	else
		this.buffer.push(line);
}

module.exports = ChannelLogger;
<|endoftext|>


<commit_before>module.exports = {
	"Auth": function(browser) {
		browser.url("http://localhost:5050");
		browser.expect.element("input[name=\"username\"]").to.be.present.before(2000);
		browser.setValue("input[name=\"username\"]", "agersant");
		browser.setValue("input[name=\"password\"]", "very_secret");
		browser.click("input[type=\"submit\"]");
		browser.expect.element("main menu").to.be.present.before(2000);
	},

	"Top-level is here": function(browser) {
		browser.expect.element(".directory").to.be.present.before(2000);
		browser.expect.element(".directory").text.to.equal("test_music");
	},

	"Explorer content": function(browser) {
		browser.click(".directory");
		browser.expect.element(".explorerView").to.be.present.before(2000);
		browser.expect.element("li:nth-of-type(1) .directory").text.to.contain("Khemmis");
		browser.expect.element("li:nth-of-type(2) .directory").text.to.contain("Tobokegao");
	},

	"Discography content": function(browser) {
		browser.click(".directory");
		browser.expect.element(".discographyView").to.be.present.before(2000);
		browser.expect.element(".album .cover").to.be.present;
		browser.expect.element(".details .title").text.to.equal("Hunted");
		browser.expect.element(".details .year").text.to.equal("2016");
	},

	"Album content": function(browser) {
		browser.click(".discographyView li");
		browser.expect.element(".albumView").to.be.present.before(2000);
		browser.expect.element(".artist").text.to.equal("Khemmis");
		browser.expect.element(".title").text.to.equal("Hunted");
		browser.expect.element(".trackList").to.be.present;
		browser.expect.element(".trackList li:nth-of-type(5)").to.be.present;
	},

	"Breadcrumbs": function(browser) {
		browser.expect.element("breadcrumbs li:nth-of-type(4)").to.be.present;
		browser.click("breadcrumbs li");
		browser.expect.element(".explorerView").to.be.present.before(2000);
		browser.end();
	},
};
<commit_msg>Added tests for recent albums and random albums<commit_after>module.exports = {
	"Auth": function(browser) {
		browser.url("http://localhost:5050");
		browser.expect.element("input[name=\"username\"]").to.be.present.before(2000);
		browser.setValue("input[name=\"username\"]", "agersant");
		browser.setValue("input[name=\"password\"]", "very_secret");
		browser.click("input[type=\"submit\"]");
		browser.expect.element("main menu").to.be.present.before(2000);
	},

	"Top-level is here": function(browser) {
		browser.expect.element(".directory").to.be.present.before(2000);
		browser.expect.element(".directory").text.to.equal("test_music");
	},

	"Explorer content": function(browser) {
		browser.click(".directory");
		browser.expect.element(".explorerView").to.be.present.before(2000);
		browser.expect.element("li:nth-of-type(1) .directory").text.to.contain("Khemmis");
		browser.expect.element("li:nth-of-type(2) .directory").text.to.contain("Tobokegao");
	},

	"Discography content": function(browser) {
		browser.click(".directory");
		browser.expect.element(".discographyView").to.be.present.before(2000);
		browser.expect.element(".album .cover").to.be.present;
		browser.expect.element(".details .title").text.to.equal("Hunted");
		browser.expect.element(".details .year").text.to.equal("2016");
	},

	"Album content": function(browser) {
		browser.click(".discographyView li");
		browser.expect.element(".albumView").to.be.present.before(2000);
		browser.expect.element(".artist").text.to.equal("Khemmis");
		browser.expect.element(".title").text.to.equal("Hunted");
		browser.expect.element(".trackList").to.be.present;
		browser.expect.element(".trackList li:nth-of-type(5)").to.be.present;
	},

	"Breadcrumbs": function(browser) {
		browser.expect.element("breadcrumbs li:nth-of-type(4)").to.be.present;
		browser.click("breadcrumbs li");
		browser.expect.element(".explorerView").to.be.present.before(2000);
	},

	"Random": function(browser) {
		browser.url("http://localhost:5050#random");
		browser.expect.element("browser h2").text.to.contain("Random").before(2000);
		browser.expect.element(".discographyView").to.be.present;
		browser.expect.element(".discographyView li:nth-of-type(2)").to.be.present;
	},

	"Recent": function(browser) {
		browser.url("http://localhost:5050#recent");
		browser.expect.element("browser h2").text.to.contain("Recent").before(2000);
		browser.expect.element(".discographyView").to.be.present;
		browser.expect.element(".discographyView li:nth-of-type(2)").to.be.present;
		browser.end();
	},
};
<|endoftext|>