428 lines
13 KiB
JavaScript
428 lines
13 KiB
JavaScript
var Backbone = require('backbone');
|
|
var _ = require('underscore');
|
|
var carto = require('../../../../../src/api/v4/index');
|
|
var createEngine = require('../../../fixtures/engine.fixture.js');
|
|
|
|
function createHistogramInternalModelMock (options) {
|
|
options = options || {};
|
|
_.extend({
|
|
data: null,
|
|
nulls: null
|
|
}, options);
|
|
var internalModelMock = {
|
|
set: function () {},
|
|
get: function () {},
|
|
getUnfilteredDataModel: function () {},
|
|
getUnfilteredData: function () {
|
|
return [
|
|
{
|
|
freq: 23
|
|
}, {
|
|
freq: 46
|
|
}, {
|
|
}
|
|
];
|
|
},
|
|
getCurrentOffset: function () {
|
|
return 7200;
|
|
}
|
|
};
|
|
spyOn(internalModelMock, 'set');
|
|
spyOn(internalModelMock, 'get').and.callFake(function (key) {
|
|
if (key === 'data') {
|
|
return options.data;
|
|
}
|
|
if (key === 'nulls') {
|
|
return options.nulls;
|
|
}
|
|
if (key === 'totalAmount') {
|
|
return 7654;
|
|
}
|
|
});
|
|
spyOn(internalModelMock, 'getUnfilteredDataModel').and.returnValue({
|
|
get: function (key) {
|
|
if (key === 'nulls') {
|
|
return 12;
|
|
}
|
|
if (key === 'totalAmount') {
|
|
return 707;
|
|
}
|
|
}
|
|
});
|
|
_.extend(internalModelMock, Backbone.Events);
|
|
|
|
return internalModelMock;
|
|
}
|
|
|
|
function createSourceMock () {
|
|
return new carto.source.Dataset('ne_10m_populated_places_simple');
|
|
}
|
|
|
|
describe('api/v4/dataview/time-series', function () {
|
|
var source = createSourceMock();
|
|
|
|
describe('initialization', function () {
|
|
it('source must be provided', function () {
|
|
var test = function () {
|
|
new carto.dataview.TimeSeries(); // eslint-disable-line no-new
|
|
};
|
|
|
|
expect(test).toThrowError(Error, 'Source property is required.');
|
|
});
|
|
|
|
it('column must be provided', function () {
|
|
var test = function () {
|
|
new carto.dataview.TimeSeries(source); // eslint-disable-line no-new
|
|
};
|
|
|
|
expect(test).toThrowError(Error, 'Column property is required.');
|
|
});
|
|
|
|
it('options set to default if not provided', function () {
|
|
var column = 'population';
|
|
|
|
var dataview = new carto.dataview.TimeSeries(source, column);
|
|
|
|
expect(dataview._aggregation).toEqual(carto.dataview.timeAggregation.AUTO);
|
|
expect(dataview._offset).toBe(0);
|
|
expect(dataview._localTimezone).toBe(false);
|
|
});
|
|
|
|
it('options set to the provided value', function () {
|
|
var dataview = new carto.dataview.TimeSeries(source, 'population', {
|
|
aggregation: carto.dataview.timeAggregation.QUARTER,
|
|
offset: -7,
|
|
useLocalTimezone: true
|
|
});
|
|
|
|
expect(dataview._aggregation).toEqual(carto.dataview.timeAggregation.QUARTER);
|
|
expect(dataview._offset).toBe(-7 * 3600); // Internaly stored in seconds
|
|
expect(dataview._localTimezone).toBe(true);
|
|
});
|
|
|
|
it('throw error if aggregation is not a proper value', function () {
|
|
var test = function () {
|
|
new carto.dataview.TimeSeries(source, 'population', { // eslint-disable-line no-new
|
|
aggregation: 'terasecond'
|
|
});
|
|
};
|
|
|
|
expect(test).toThrowError(Error, 'Time aggregation must be a valid value. Use carto.dataview.timeAggregation.');
|
|
});
|
|
|
|
it('throw error if offset is not a valid hour', function () {
|
|
var test = function () {
|
|
new carto.dataview.TimeSeries(source, 'population', { // eslint-disable-line no-new
|
|
offset: 34
|
|
});
|
|
};
|
|
|
|
expect(test).toThrowError(Error, 'Offset must an integer value between -12 and 14.');
|
|
|
|
test = function () {
|
|
new carto.dataview.TimeSeries(source, 'population', { // eslint-disable-line no-new
|
|
offset: 10.45
|
|
});
|
|
};
|
|
|
|
expect(test).toThrowError(Error, 'Offset must an integer value between -12 and 14.');
|
|
});
|
|
|
|
it('throw error if localTimezone is not a binary value', function () {
|
|
var test = function () {
|
|
new carto.dataview.TimeSeries(source, 'population', { // eslint-disable-line no-new
|
|
useLocalTimezone: 'Los Angeles'
|
|
});
|
|
};
|
|
|
|
expect(test).toThrowError(Error, 'useLocalTimezone must be a boolean value.');
|
|
});
|
|
});
|
|
|
|
describe('.getData', function () {
|
|
var dataview;
|
|
|
|
beforeEach(function () {
|
|
dataview = new carto.dataview.TimeSeries(source, 'population');
|
|
});
|
|
|
|
it('returns null if there is no internalModel', function () {
|
|
var data = dataview.getData();
|
|
|
|
expect(data).toBeNull();
|
|
});
|
|
|
|
it('returns parsed data from the internal model', function () {
|
|
dataview._internalModel = createHistogramInternalModelMock({
|
|
data: [
|
|
{
|
|
freq: 35
|
|
}, {
|
|
freq: 50
|
|
}, {
|
|
}
|
|
],
|
|
nulls: 42
|
|
});
|
|
|
|
var data = dataview.getData();
|
|
|
|
expect(data.bins.length).toBe(3);
|
|
expect(data.bins[0].freq).toBe(35);
|
|
expect(data.bins[1].freq).toBe(50);
|
|
expect(data.bins[2].freq).toBeUndefined();
|
|
expect(data.bins[0].normalized).toBe(0.7);
|
|
expect(data.bins[1].normalized).toBe(1);
|
|
expect(data.bins[2].normalized).toBe(0);
|
|
expect(data.nulls).toBe(42);
|
|
expect(data.totalAmount).toBe(7654);
|
|
expect(data.offset).toBe(2);
|
|
});
|
|
|
|
it('returns nulls as 0 in case the internal model has no nulls', function () {
|
|
dataview._internalModel = createHistogramInternalModelMock({
|
|
data: [
|
|
{
|
|
freq: 35
|
|
}, {
|
|
freq: 50
|
|
}, {
|
|
}
|
|
],
|
|
nulls: undefined
|
|
});
|
|
|
|
var data = dataview.getData();
|
|
|
|
expect(data.bins.length).toBe(3);
|
|
expect(data.bins[0].freq).toBe(35);
|
|
expect(data.bins[1].freq).toBe(50);
|
|
expect(data.bins[2].freq).toBeUndefined();
|
|
expect(data.bins[0].normalized).toBe(0.7);
|
|
expect(data.bins[1].normalized).toBe(1);
|
|
expect(data.bins[2].normalized).toBe(0);
|
|
expect(data.nulls).toBe(0);
|
|
expect(data.totalAmount).toBe(7654);
|
|
});
|
|
|
|
it('returns null if internal model has no data', function () {
|
|
dataview._internalModel = createHistogramInternalModelMock();
|
|
|
|
var data = dataview.getData();
|
|
|
|
expect(data).toBe(null);
|
|
});
|
|
});
|
|
|
|
describe('.setAggregation', function () {
|
|
var dataview;
|
|
|
|
beforeEach(function () {
|
|
dataview = new carto.dataview.TimeSeries(source, 'population');
|
|
});
|
|
|
|
it('should validate aggregation', function () {
|
|
var test = function () {
|
|
dataview.setAggregation('terasecond');
|
|
};
|
|
|
|
expect(test).toThrowError(Error, 'Time aggregation must be a valid value. Use carto.dataview.timeAggregation.');
|
|
});
|
|
|
|
it('should set aggregation to internal model as well', function () {
|
|
dataview._internalModel = createHistogramInternalModelMock();
|
|
|
|
dataview.setAggregation(carto.dataview.timeAggregation.HOUR);
|
|
|
|
expect(dataview._internalModel.set).toHaveBeenCalledWith('aggregation', carto.dataview.timeAggregation.HOUR);
|
|
expect(dataview.getAggregation()).toBe('hour');
|
|
|
|
// Clean
|
|
dataview._internalModel = null;
|
|
});
|
|
|
|
it('should Trigger a aggregationChanged event when the aggregation are changed', function () {
|
|
var aggregationChangedSpy = jasmine.createSpy('aggregationChangedSpy');
|
|
dataview.on('aggregationChanged', aggregationChangedSpy);
|
|
|
|
expect(aggregationChangedSpy).not.toHaveBeenCalled();
|
|
dataview.$setEngine(createEngine());
|
|
dataview.setAggregation(carto.dataview.timeAggregation.HOUR);
|
|
|
|
expect(aggregationChangedSpy).toHaveBeenCalledWith(carto.dataview.timeAggregation.HOUR);
|
|
});
|
|
});
|
|
|
|
describe('.setOffset', function () {
|
|
var dataview;
|
|
|
|
beforeEach(function () {
|
|
dataview = new carto.dataview.TimeSeries(source, 'population');
|
|
});
|
|
|
|
it('should validate offset', function () {
|
|
var test = function () {
|
|
dataview.setOffset(32);
|
|
};
|
|
|
|
expect(test).toThrowError(Error, 'Offset must an integer value between -12 and 14.');
|
|
|
|
test = function () {
|
|
dataview.setOffset(10.7);
|
|
};
|
|
|
|
expect(test).toThrowError(Error, 'Offset must an integer value between -12 and 14.');
|
|
});
|
|
|
|
it('should set offset to internal model as well translated to seconds', function () {
|
|
dataview._internalModel = createHistogramInternalModelMock();
|
|
|
|
dataview.setOffset(-5);
|
|
|
|
expect(dataview._internalModel.set).toHaveBeenCalledWith('offset', -5 * 3600);
|
|
expect(dataview.getOffset()).toBe(-5);
|
|
|
|
// Clean
|
|
dataview._internalModel = null;
|
|
});
|
|
});
|
|
|
|
describe('.useLocalTimezone', function () {
|
|
var dataview;
|
|
|
|
beforeEach(function () {
|
|
dataview = new carto.dataview.TimeSeries(source, 'population');
|
|
});
|
|
|
|
it('should validate localTimezone', function () {
|
|
var test = function () {
|
|
dataview.useLocalTimezone('Compton');
|
|
};
|
|
|
|
expect(test).toThrowError(Error, 'useLocalTimezone must be a boolean value.');
|
|
});
|
|
|
|
it('should set localTimezone to internal model as well', function () {
|
|
dataview._internalModel = createHistogramInternalModelMock();
|
|
|
|
dataview.useLocalTimezone(true);
|
|
|
|
expect(dataview._internalModel.set).toHaveBeenCalledWith('localTimezone', true);
|
|
expect(dataview.isUsingLocalTimezone()).toBe(true);
|
|
|
|
// Clean
|
|
dataview._internalModel = null;
|
|
});
|
|
});
|
|
|
|
describe('.$setEngine', function () {
|
|
var engine;
|
|
var dataview;
|
|
|
|
beforeEach(function () {
|
|
dataview = new carto.dataview.TimeSeries(source, 'population');
|
|
engine = createEngine();
|
|
});
|
|
|
|
it('creates the internal model', function () {
|
|
dataview.disable(); // To test that it passes the ._enabled property to the internal model
|
|
dataview.setAggregation(carto.dataview.timeAggregation.WEEK);
|
|
dataview.setOffset(6);
|
|
dataview.useLocalTimezone(true);
|
|
dataview.$setEngine(engine);
|
|
|
|
var internalModel = dataview.$getInternalModel();
|
|
expect(internalModel.get('source')).toBe(dataview._source.$getInternalModel());
|
|
expect(internalModel.get('column')).toEqual(dataview._column);
|
|
expect(internalModel.get('aggregation')).toBe('week');
|
|
expect(internalModel.get('localTimezone')).toBe(true);
|
|
expect(internalModel.get('offset')).toBe(6 * 3600);
|
|
expect(internalModel.isEnabled()).toBe(false);
|
|
expect(internalModel._engine).toBe(engine);
|
|
});
|
|
|
|
it('calling twice to $setEngine does not create another internalModel', function () {
|
|
spyOn(dataview, '_createInternalModel').and.callThrough();
|
|
|
|
dataview.$setEngine(engine);
|
|
dataview.$setEngine(engine);
|
|
|
|
expect(dataview._createInternalModel.calls.count()).toBe(1);
|
|
});
|
|
|
|
describe('spatial filters', function () {
|
|
it('creates the internal model with BoundingBox filter if provided', function () {
|
|
var filter = new carto.filter.BoundingBox();
|
|
dataview.addFilter(filter);
|
|
dataview.$setEngine(engine);
|
|
|
|
var internalModel = dataview.$getInternalModel();
|
|
expect(internalModel._bboxFilter).toBeDefined();
|
|
expect(internalModel.syncsOnBoundingBoxChanges()).toBe(true);
|
|
});
|
|
|
|
it('allows removing a BoundingBox filter', function () {
|
|
var filter = new carto.filter.BoundingBox();
|
|
dataview.addFilter(filter);
|
|
dataview.$setEngine(engine);
|
|
expect(dataview.hasFilter(filter)).toBe(true);
|
|
|
|
dataview.removeFilter(filter);
|
|
|
|
expect(dataview.hasFilter(filter)).toBe(false);
|
|
var internalModel = dataview.$getInternalModel();
|
|
expect(internalModel._bboxFilter).toBeNull();
|
|
expect(internalModel.syncsOnBoundingBoxChanges()).toBe(false);
|
|
});
|
|
|
|
it('creates the internal model with Circle filter if provided', function () {
|
|
var filter = new carto.filter.Circle();
|
|
dataview.addFilter(filter);
|
|
dataview.$setEngine(engine);
|
|
|
|
var internalModel = dataview.$getInternalModel();
|
|
expect(internalModel._circleFilter).toBeDefined();
|
|
expect(internalModel.syncsOnCircleChanges()).toBe(true);
|
|
});
|
|
|
|
it('allows removing a Circle filter', function () {
|
|
var filter = new carto.filter.Circle();
|
|
dataview.addFilter(filter);
|
|
dataview.$setEngine(engine);
|
|
expect(dataview.hasFilter(filter)).toBe(true);
|
|
|
|
dataview.removeFilter(filter);
|
|
|
|
expect(dataview.hasFilter(filter)).toBe(false);
|
|
var internalModel = dataview.$getInternalModel();
|
|
expect(internalModel._circleFilter).toBeNull();
|
|
expect(internalModel.syncsOnCircleChanges()).toBe(false);
|
|
});
|
|
|
|
it('creates the internal model with Polygon filter if provided', function () {
|
|
var filter = new carto.filter.Polygon();
|
|
dataview.addFilter(filter);
|
|
dataview.$setEngine(engine);
|
|
|
|
var internalModel = dataview.$getInternalModel();
|
|
expect(internalModel._polygonFilter).toBeDefined();
|
|
expect(internalModel.syncsOnPolygonChanges()).toBe(true);
|
|
});
|
|
|
|
it('allows removing a Polygon filter', function () {
|
|
var filter = new carto.filter.Polygon();
|
|
dataview.addFilter(filter);
|
|
dataview.$setEngine(engine);
|
|
expect(dataview.hasFilter(filter)).toBe(true);
|
|
|
|
dataview.removeFilter(filter);
|
|
|
|
expect(dataview.hasFilter(filter)).toBe(false);
|
|
var internalModel = dataview.$getInternalModel();
|
|
expect(internalModel._polygonFilter).toBeNull();
|
|
expect(internalModel.syncsOnPolygonChanges()).toBe(false);
|
|
});
|
|
});
|
|
});
|
|
});
|