Initial commit

This commit is contained in:
zhongjin
2020-06-13 18:34:34 +08:00
commit 52aaa9f15d
655 changed files with 96796 additions and 0 deletions
+243
View File
@@ -0,0 +1,243 @@
var DataviewBase = require('../../../../../src/api/v4/dataview/base');
var status = require('../../../../../src/api/v4/constants').status;
var carto = require('../../../../../src/api/v4/index');
function createSourceMock () {
return new carto.source.Dataset('foo');
}
function createEngineMock () {
var engine = {
name: 'Engine mock',
reload: function () {}
};
spyOn(engine, 'reload');
return engine;
}
describe('api/v4/dataview/base', function () {
var base = new DataviewBase();
it('.getStatus should return the internal status', function () {
expect(base.getStatus()).toEqual(base._status);
});
describe('.isLoading', function () {
it('should return true if loading and false otherwise', function () {
base._status = status.NOT_LOADED;
expect(base.isLoading()).toBe(false);
base._status = status.LOADING;
expect(base.isLoading()).toBe(true);
});
it('should return true if loading and false otherwise', function () {
base._status = status.NOT_LOADED;
expect(base.isLoaded()).toBe(false);
base._status = status.LOADED;
expect(base.isLoaded()).toBe(true);
});
});
describe('.hasError', function () {
it('should return true if loading and false otherwise', function () {
base._status = status.NOT_LOADED;
expect(base.hasError()).toBe(false);
base._status = status.ERROR;
expect(base.hasError()).toBe(true);
});
});
describe('.enable', function () {
it('should enable the dataview', function () {
base.enable();
expect(base._enabled).toBe(true);
});
it('should return the dataview', function () {
expect(base.enable()).toBe(base);
});
});
describe('.disable', function () {
it('should disable the dataview', function () {
base.disable();
expect(base._enabled).toBe(false);
});
it('should return the dataview', function () {
expect(base.disable()).toBe(base);
});
});
describe('.isEnabled', function () {
it('should return true if enabled and false otherwise', function () {
base.disable();
expect(base.isEnabled()).toBe(false);
base.enable();
expect(base.isEnabled()).toBe(true);
});
});
describe('.getSource', function () {
it('should return the source object', function () {
var source = new carto.source.Dataset('table_name');
base._source = source;
expect(base.getSource()).toBe(source);
});
});
describe('.setColumn', function () {
it('should set the column name as string', function () {
var column = 'column-test';
base.setColumn(column);
expect(base._column).toBe(column);
});
it('should throw an error if the argument is not string or undefined', function () {
var requiredColumnError;
var stringColumnError;
var emptyColumnError;
try { base.setColumn(); } catch (error) { requiredColumnError = error; }
try { base.setColumn(12); } catch (error) { stringColumnError = error; }
try { base.setColumn(''); } catch (error) { emptyColumnError = error; }
expect(requiredColumnError).toEqual(jasmine.objectContaining({
message: 'Column property is required.',
type: 'dataview',
errorCode: 'validation:dataview:column-required'
}));
expect(stringColumnError).toEqual(jasmine.objectContaining({
message: 'Column property must be a string.',
type: 'dataview',
errorCode: 'validation:dataview:column-string'
}));
expect(emptyColumnError).toEqual(jasmine.objectContaining({
message: 'Column property must be not empty.',
type: 'dataview',
errorCode: 'validation:dataview:empty-column'
}));
});
it('should return the dataview', function () {
var column = 'column-test';
expect(base.setColumn(column)).toBe(base);
});
});
describe('.getColumn', function () {
it('should return the column name', function () {
var column = 'column-test2';
base._column = column;
expect(base.getColumn()).toBe(column);
});
});
describe('.getData', function () {
it('.getData should not be defined in the base dataview', function () {
expect(function () { base.getData(); }).toThrowError(Error, 'getData must be implemented by the particular dataview.');
});
});
describe('._changeProperty', function () {
it('should set internal property', function () {
base._example = 'something';
base._changeProperty('example', 'whatever');
expect(base._example).toEqual('whatever');
});
it('should trigger change is there is no internal model', function () {
var eventValue = '';
base._example = 'something';
base.on('exampleChanged', function (newValue) {
eventValue = newValue;
});
base._changeProperty('example', 'whatever');
expect(eventValue).toEqual('whatever');
});
it('should update internal model and trigger a change when the internalModel exists', function () {
var internalModelSpy = jasmine.createSpyObj('internalModelSpy', ['set']);
base._example = 'something';
base._internalModel = internalModelSpy;
spyOn(base, '_triggerChange');
base._changeProperty('example', 'whatever');
expect(internalModelSpy.set).toHaveBeenCalledWith('example', 'whatever');
expect(base._triggerChange).toHaveBeenCalled();
});
});
describe('.$setEngine', function () {
var engine;
var dataview;
beforeEach(function () {
// We use Formula for these tests. Any other dataview could be used instead.
dataview = new carto.dataview.Formula(createSourceMock(), 'population', {
operation: carto.operation.MIN
});
engine = createEngineMock();
});
it('internalModel events should be properly hooked up', function () {
dataview.$setEngine(engine);
var internalModel = dataview._internalModel;
var eventStatus = null;
var eventError = null;
var dataviewError = null;
dataview.on('statusChanged', function (newStatus, error) {
eventStatus = newStatus;
eventError = error;
});
dataview.on('error', function (error) {
dataviewError = error;
});
// Loading
internalModel.trigger('loading');
expect(dataview.getStatus()).toEqual('loading');
expect(eventStatus).toEqual('loading');
// Loaded
internalModel.trigger('loaded');
expect(dataview.getStatus()).toEqual('loaded');
expect(eventStatus).toEqual('loaded');
// Error
internalModel.trigger('statusError', internalModel, 'an error');
expect(dataview.getStatus()).toEqual('error');
expect(eventStatus).toEqual('error');
expect(eventError).toEqual('an error');
expect(dataviewError.name).toEqual('CartoError');
});
});
describe('add bbox filter', function () {
it('should check if it is a proper object', function () {
function test () {
base.addFilter('invalid_filter');
}
expect(test).toThrowError('Filter property is required.');
});
it('should throw an error if an SQL filter is passed', function () {
function test () {
var categoryFilter = new carto.filter.Category('fake_column', { in: ['category_value'] });
base.addFilter(categoryFilter);
}
expect(test).toThrowError('Filter property is required.');
});
});
});
+438
View File
@@ -0,0 +1,438 @@
var Backbone = require('backbone');
var _ = require('underscore');
var carto = require('../../../../../src/api/v4/index');
var createEngine = require('../../../fixtures/engine.fixture.js');
function createInternalModelMock () {
var internalModelMock = {
set: function () {},
get: function () {}
};
spyOn(internalModelMock, 'set');
spyOn(internalModelMock, 'get').and.callFake(function (key) {
switch (key) {
case 'count': return 42;
case 'max': return 9;
case 'min': return 1;
case 'nulls': return 0;
case 'data': return [
{
name: 'cat1',
value: 1,
agg: false
},
{
name: 'others',
value: 9,
agg: true
}
];
}
});
_.extend(internalModelMock, Backbone.Events);
return internalModelMock;
}
function createSourceMock () {
return new carto.source.Dataset('foo');
}
describe('api/v4/dataview/category', function () {
var source = createSourceMock();
describe('initialization', function () {
it('source must be provided', function () {
var error;
try { new carto.dataview.Category(); } catch (err) { error = err; } // eslint-disable-line no-new
expect(error).toEqual(jasmine.objectContaining({
message: 'Source property is required.',
type: 'dataview',
errorCode: 'validation:dataview:source-required'
}));
});
it('column must be provided', function () {
var error;
try { new carto.dataview.Category(source); } catch (err) { error = err; } // eslint-disable-line no-new
expect(error).toEqual(jasmine.objectContaining({
message: 'Column property is required.',
type: 'dataview',
errorCode: 'validation:dataview:column-required'
}));
});
it('options set to default if not provided', function () {
var column = 'population';
var dataview = new carto.dataview.Category(source, column);
expect(dataview._limit).toEqual(6);
expect(dataview._operation).toEqual(carto.operation.COUNT);
expect(dataview._operationColumn).toEqual('population');
});
it('options set to the provided value', function () {
var dataview = new carto.dataview.Category(source, 'population', {
limit: 10,
operation: carto.operation.AVG,
operationColumn: 'column-test'
});
expect(dataview._limit).toEqual(10);
expect(dataview._operation).toEqual(carto.operation.AVG);
expect(dataview._operationColumn).toEqual('column-test');
});
it('throw error if no correct operation is provided', function () {
var error;
var test = function () {
new carto.dataview.Category(source, 'population', { // eslint-disable-line no-new
operation: 'exponential'
});
};
try { test(); } catch (err) { error = err; }
expect(error).toEqual(jasmine.objectContaining({
message: 'Operation for category dataview is not valid. Use carto.operation',
type: 'dataview',
errorCode: 'validation:dataview:category-invalid-operation'
}));
});
});
describe('.setLimit', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Category(source, 'population');
});
it('checks if limit is valid', function () {
var requiredError;
var numberError;
var positiveError;
try { dataview.setLimit(); } catch (err) { requiredError = err; }
try { dataview.setLimit('12'); } catch (err) { numberError = err; }
try { dataview.setLimit(0); } catch (err) { positiveError = err; }
expect(requiredError).toEqual(jasmine.objectContaining({
message: 'Limit for category dataview is required.',
type: 'dataview',
errorCode: 'validation:dataview:category-limit-required'
}));
expect(numberError).toEqual(jasmine.objectContaining({
message: 'Limit for category dataview must be a number.',
type: 'dataview',
errorCode: 'validation:dataview:category-limit-number'
}));
expect(positiveError).toEqual(jasmine.objectContaining({
message: 'Limit for category dataview must be greater than 0.',
type: 'dataview',
errorCode: 'validation:dataview:category-limit-positive'
}));
});
it('if limit is valid, it assigns it to property, returns this and nothing else if there is no internaModel', function () {
var returnedObject = dataview.setLimit(10);
expect(dataview.getLimit()).toEqual(10);
expect(returnedObject).toBe(dataview);
});
it('sets limit in internal model if exists', function () {
var internalModelMock = createInternalModelMock();
dataview._internalModel = internalModelMock;
dataview.setLimit(1);
var operationArgs = internalModelMock.set.calls.mostRecent().args;
expect(operationArgs[0]).toEqual('categories');
expect(operationArgs[1]).toEqual(1);
});
it('should trigger a limitChanged event', function () {
var limitChangedSpy = jasmine.createSpy('operationaChangedSpy');
dataview.on('limitChanged', limitChangedSpy);
expect(limitChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setLimit(7);
expect(limitChangedSpy).toHaveBeenCalledWith(7);
});
});
describe('.setOperation', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Category(source, 'population');
});
it('checks if operation is valid', function () {
var error;
var test = function () {
dataview.setOperation('swordfish');
};
try { test(); } catch (err) { error = err; }
expect(error).toEqual(jasmine.objectContaining({
message: 'Operation for category dataview is not valid. Use carto.operation',
type: 'dataview',
errorCode: 'validation:dataview:category-invalid-operation'
}));
});
it('if operation is valid, it assigns it to property, returns this and nothing else if there is no internaModel', function () {
var returnedObject = dataview.setOperation(carto.operation.AVG);
expect(dataview.getOperation()).toEqual(carto.operation.AVG);
expect(returnedObject).toBe(dataview);
});
it('sets operation in internal model if exists', function () {
var internalModelMock = createInternalModelMock();
dataview._internalModel = internalModelMock;
dataview.setOperation(carto.operation.AVG);
var operationArgs = internalModelMock.set.calls.mostRecent().args;
expect(operationArgs[0]).toEqual('aggregation');
expect(operationArgs[1]).toEqual(carto.operation.AVG);
});
it('should trigger a operationChanged event', function () {
var operationChangedSpy = jasmine.createSpy('operationaChangedSpy');
dataview.on('operationChanged', operationChangedSpy);
expect(operationChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setOperation(carto.operation.AVG);
expect(operationChangedSpy).toHaveBeenCalledWith(carto.operation.AVG);
});
});
describe('.setOperationColumn', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Category(source, 'population');
});
it('checks if operation is valid', function () {
var requiredError;
var numberError;
var emptyError;
try { dataview.setOperationColumn(); } catch (err) { requiredError = err; }
try { dataview.setOperationColumn(12); } catch (err) { numberError = err; }
try { dataview.setOperationColumn(''); } catch (err) { emptyError = err; }
expect(requiredError).toEqual(jasmine.objectContaining({
message: 'Operation column for category dataview is required.',
type: 'dataview',
errorCode: 'validation:dataview:category-operation-required'
}));
expect(numberError).toEqual(jasmine.objectContaining({
message: 'Operation column for category dataview must be a string.',
type: 'dataview',
errorCode: 'validation:dataview:category-operation-string'
}));
expect(emptyError).toEqual(jasmine.objectContaining({
message: 'Operation column for category dataview must be not empty.',
type: 'dataview',
errorCode: 'validation:dataview:category-operation-empty'
}));
});
it('if operation is valid, it assigns it to property, returns this and nothing else if there is no internaModel', function () {
var returnedObject = dataview.setOperationColumn('columnA');
expect(dataview.getOperationColumn()).toEqual('columnA');
expect(returnedObject).toBe(dataview);
});
it('sets operation in internal model if exists', function () {
var internalModelMock = createInternalModelMock();
dataview._internalModel = internalModelMock;
dataview.setOperationColumn('columnB');
var operationArgs = internalModelMock.set.calls.mostRecent().args;
expect(operationArgs[0]).toEqual('aggregation_column');
expect(operationArgs[1]).toEqual('columnB');
});
it('should trigger a operationColumnChanged event', function () {
var operationColumnChangedSpy = jasmine.createSpy('operationaChangedSpy');
dataview.on('operationColumnChanged', operationColumnChangedSpy);
expect(operationColumnChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setOperationColumn('column2');
expect(operationColumnChangedSpy).toHaveBeenCalledWith('column2');
});
});
describe('.getData', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Category(source, 'population', {
operation: carto.operation.SUM,
operationColumn: 'column-test'
});
});
it('returns null if there is no internalModel', function () {
var data = dataview.getData();
expect(data).toBeNull();
});
it('returns data from internalModel', function () {
var internalModelMock = createInternalModelMock();
dataview._internalModel = internalModelMock;
var data = dataview.getData();
expect(data).toEqual({
count: 42,
max: 9,
min: 1,
nulls: 0,
operation: carto.operation.SUM,
categories: [
{
name: 'cat1',
value: 1,
group: false
},
{
name: 'others',
value: 9,
group: true
}
]
});
});
});
describe('.$setEngine', function () {
var engine;
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Category(source, 'population', {
operation: carto.operation.MIN,
operationColumn: 'column-test'
});
engine = createEngine();
});
it('creates the internal model', function () {
dataview.disable(); // To test that it passes the ._enabled property to the internal model
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('categories')).toEqual(dataview._limit);
expect(internalModel.get('aggregation')).toEqual(dataview._operation);
expect(internalModel.get('aggregation_column')).toEqual(dataview._operationColumn);
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);
});
});
});
});
+265
View File
@@ -0,0 +1,265 @@
var Backbone = require('backbone');
var _ = require('underscore');
var carto = require('../../../../../src/api/v4/index');
function createInternalModelMock () {
var internalModelMock = {
set: function () {},
get: function () {}
};
spyOn(internalModelMock, 'set');
spyOn(internalModelMock, 'get').and.callFake(function (key) {
if (key === 'data') {
return 1234;
}
if (key === 'nulls') {
return 42;
}
});
_.extend(internalModelMock, Backbone.Events);
return internalModelMock;
}
function createSourceMock () {
return new carto.source.Dataset('foo');
}
function createEngineMock () {
var engine = {
name: 'Engine mock',
reload: function () {}
};
spyOn(engine, 'reload');
return engine;
}
describe('api/v4/dataview/formula', function () {
var source = createSourceMock();
describe('initialization', function () {
it('source must be provided', function () {
var test = function () {
new carto.dataview.Formula(); // 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.Formula(source); // eslint-disable-line no-new
};
expect(test).toThrowError('Column property is required.');
});
it('options set to default if not provided', function () {
var column = 'population';
var dataview = new carto.dataview.Formula(source, column);
expect(dataview._operation).toEqual(carto.operation.COUNT);
});
it('options set to the provided value', function () {
var dataview = new carto.dataview.Formula(source, 'population', {
operation: carto.operation.AVG
});
expect(dataview._operation).toEqual(carto.operation.AVG);
});
it('throw error if no correct operation is provided', function () {
var test = function () {
new carto.dataview.Formula(source, 'population', { // eslint-disable-line no-new
operation: 'exponential'
});
};
expect(test).toThrowError(Error, 'Operation for formula dataview is not valid. Use carto.operation');
});
});
describe('.setOperation', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Formula(source, 'population');
});
it('checks if operation is valid', function () {
var test = function () {
dataview.setOperation('swordfish');
};
expect(test).toThrowError(Error, 'Operation for formula dataview is not valid. Use carto.operation');
});
it('if operation is valid, it assigns it to property, returns this and nothing else if there is no internaModel', function () {
var returnedObject = dataview.setOperation(carto.operation.AVG);
expect(dataview.getOperation()).toEqual(carto.operation.AVG);
expect(returnedObject).toBe(dataview);
});
it('sets operation in internal model if exists', function () {
var internalModelMock = createInternalModelMock();
dataview._internalModel = internalModelMock;
dataview.setOperation(carto.operation.AVG);
var operationArgs = internalModelMock.set.calls.mostRecent().args;
expect(operationArgs[0]).toEqual('operation');
expect(operationArgs[1]).toEqual(carto.operation.AVG);
});
it('should Trigger a operationChanged event when the operation is changed', function () {
var operationChangedSpy = jasmine.createSpy('operationaChangedSpy');
dataview.on('operationChanged', operationChangedSpy);
expect(operationChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngineMock());
dataview.setOperation(carto.operation.MAX);
expect(operationChangedSpy).toHaveBeenCalledWith(carto.operation.MAX);
});
});
describe('.getData', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Formula(source, 'population', {
operation: carto.operation.SUM
});
});
it('returns null if there is no internalModel', function () {
var data = dataview.getData();
expect(data).toBeNull();
});
it('returns data from internalModel', function () {
var internalModelMock = createInternalModelMock();
dataview._internalModel = internalModelMock;
var data = dataview.getData();
expect(data).toEqual({
nulls: 42,
operation: carto.operation.SUM,
result: 1234
});
});
});
describe('.$setEngine', function () {
var engine;
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Formula(source, 'population', {
operation: carto.operation.MIN
});
engine = createEngineMock();
});
it('creates the internal model', function () {
dataview.disable(); // To test that it passes the ._enabled property to the internal model
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('operation')).toEqual(dataview._operation);
expect(internalModel.isEnabled()).toBe(false);
expect(internalModel._engine.name).toEqual('Engine mock');
});
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);
});
});
});
});
+452
View File
@@ -0,0 +1,452 @@
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
}, {
}
];
}
};
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/histogram', function () {
var source = createSourceMock();
describe('initialization', function () {
it('source must be provided', function () {
var test = function () {
new carto.dataview.Histogram(); // 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.Histogram(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.Histogram(source, column);
expect(dataview._bins).toEqual(10);
});
it('options set to the provided value', function () {
var dataview = new carto.dataview.Histogram(source, 'population', {
bins: 808
});
expect(dataview._bins).toEqual(808);
});
it('throw error if bins is not a positive integer value', function () {
var test = function () {
new carto.dataview.Histogram(source, 'population', { // eslint-disable-line no-new
bins: 0
});
};
expect(test).toThrowError(Error, 'Bins must be a positive integer value.');
});
it('throw error if start is present but not end', function () {
var test = function () {
new carto.dataview.Histogram(source, 'population', { // eslint-disable-line no-new
start: 10
});
};
expect(test).toThrowError(Error, 'Both start and end values must be a number or null.');
});
it('throw error if end is present but not start', function () {
var test = function () {
new carto.dataview.Histogram(source, 'population', { // eslint-disable-line no-new
end: 10
});
};
expect(test).toThrowError(Error, 'Both start and end values must be a number or null.');
});
});
describe('.getData', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Histogram(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);
});
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('.setBins', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Histogram(source, 'population');
});
it('should validate bins', function () {
var test = function () {
dataview.setBins(-1);
};
expect(test).toThrowError(Error, 'Bins must be a positive integer value.');
});
it('should throw error if called with a float number', function () {
var test = function () {
dataview.setBins(15.7);
};
expect(test).toThrowError(Error, 'Bins must be a positive integer value.');
});
it('should set bins to internal model as well', function () {
dataview._internalModel = createHistogramInternalModelMock();
dataview.setBins(16);
expect(dataview._internalModel.set).toHaveBeenCalledWith('bins', 16);
expect(dataview.getBins()).toBe(16); // We assert .getBins() as well
// Clean
dataview._internalModel = null;
});
it('should Trigger a binsChanged event when the bins are changed', function () {
var binsChangedSpy = jasmine.createSpy('binsChangedSpy');
dataview.on('binsChanged', binsChangedSpy);
expect(binsChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setBins(11);
expect(binsChangedSpy).toHaveBeenCalledWith(11);
});
});
describe('.setStartEnd', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Histogram(source, 'population');
});
it('should throw an error if only start is present', function () {
var test = function () {
dataview.setStartEnd(20, null);
};
expect(test).toThrowError(Error, 'Both start and end values must be a number or null.');
});
it('should throw an error if only end is present', function () {
var test = function () {
dataview.setStartEnd(null, 30);
};
expect(test).toThrowError(Error, 'Both start and end values must be a number or null.');
});
it('should set start and end with a number', function () {
dataview._internalModel = createHistogramInternalModelMock();
dataview.setStartEnd(20, 30);
expect(dataview._internalModel.set).toHaveBeenCalledWith({ start: 20, end: 30 });
expect(dataview.getStart()).toBe(20); // We assert .getStart() as well
expect(dataview.getEnd()).toBe(30); // We assert .getEnd() as well
// Clean
dataview._internalModel = null;
});
it('should set start and end with null', function () {
dataview._internalModel = createHistogramInternalModelMock();
dataview.setStartEnd(null, null);
expect(dataview._internalModel.set).toHaveBeenCalledWith({ start: null, end: null });
expect(dataview.getStart()).toBe(undefined); // We assert .getStart() as well
expect(dataview.getEnd()).toBe(undefined); // We assert .getEnd() as well
// Clean
dataview._internalModel = null;
});
it('should trigger a startChanged event when the start is changed', function () {
var startChangedSpy = jasmine.createSpy('startChangedSpy');
dataview.on('startChanged', startChangedSpy);
expect(startChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setStartEnd(20, 30);
expect(startChangedSpy).toHaveBeenCalledWith(20);
});
it('should trigger a endChanged event when the end is changed', function () {
var endChangedSpy = jasmine.createSpy('endChangedSpy');
dataview.on('endChanged', endChangedSpy);
expect(endChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setStartEnd(20, 30);
expect(endChangedSpy).toHaveBeenCalledWith(30);
});
});
describe('.$setEngine', function () {
var engine;
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Histogram(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.setBins(15);
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('bins')).toBe(15);
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);
});
});
});
describe('.getDistributionType', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Histogram(source, 'population');
});
it('should return null if there is no internal model', function () {
var distribution = dataview.getDistributionType();
expect(distribution).toBe(null);
});
it('should call to the proper method in internal model', function () {
var internalModel = {
getData: function () {},
getDistributionType: function () {}
};
spyOn(internalModel, 'getData').and.returnValue('token');
spyOn(internalModel, 'getDistributionType').and.returnValue('a');
dataview._internalModel = internalModel;
var distribution = dataview.getDistributionType();
expect(internalModel.getDistributionType).toHaveBeenCalledWith('token');
expect(distribution).toEqual('a');
});
});
});
@@ -0,0 +1,427 @@
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);
});
});
});
});