Initial commit

This commit is contained in:
zhongjin
2020-06-15 10:58:47 +08:00
commit 4f1dfe7564
8590 changed files with 1516878 additions and 0 deletions
@@ -0,0 +1,167 @@
var Backbone = require('backbone');
var moment = require('moment');
var TimeSeriesQueryModel = require('builder/editor/widgets/time-series-query-model');
describe('editor/widgets/time-series-query-model', function () {
beforeEach(function () {
var configModel = new Backbone.Model({
user_name: '/u/pepe',
sql_api_template: 'SELECT * from test;',
api_key: '1238asd234239asd89asd123'
});
var querySchemaModel = new Backbone.Model({
status: 'fetched'
});
querySchemaModel.getColumnType = function () {
return 'date';
};
this.model = new TimeSeriesQueryModel({
column: 'cartodb_id'
}, {
configModel: configModel,
querySchemaModel: querySchemaModel
});
});
it('should call _queryData when change:column', function () {
spyOn(this.model, '_queryData');
this.model._initBinds();
this.model.trigger('change:column');
expect(this.model._queryData).toHaveBeenCalled();
});
it('should call _onQuerySchemaStatusChanged when _querySchemaModel change:status', function () {
spyOn(this.model, '_onQuerySchemaStatusChanged');
this.model._initBinds();
this.model._querySchemaModel.trigger('change:status');
expect(this.model._onQuerySchemaStatusChanged).toHaveBeenCalled();
});
describe('._onQuerySchemaStatusChanged', function () {
it('should call ._queryData if _querySchemaModel is fetched', function () {
spyOn(this.model, '_queryData');
this.model._querySchemaModel.set('status', 'unfetched');
this.model._onQuerySchemaStatusChanged();
expect(this.model._queryData).not.toHaveBeenCalled();
this.model._querySchemaModel.set('status', 'fetched');
this.model._onQuerySchemaStatusChanged();
expect(this.model._queryData).toHaveBeenCalled();
});
});
describe('._queryData', function () {
it('should set the status to fetching', function () {
spyOn(this.model._SQL, 'execute');
this.model.set('status', 'unfetched');
this.model._queryData();
expect(this.model.get('status')).toEqual('fetching');
});
it('should call _SQL.execute', function () {
spyOn(this.model._SQL, 'execute');
this.model._queryData();
expect(this.model._SQL.execute).toHaveBeenCalled();
});
});
describe('._onQuerySuccess', function () {
it('should set the status to fetched', function () {
this.model._onQuerySuccess({});
expect(this.model.get('status')).toEqual('fetched');
});
it('should call ._calculateBuckets', function () {
spyOn(this.model, '_calculateBuckets');
this.model._onQuerySuccess({});
expect(this.model._calculateBuckets).toHaveBeenCalled();
});
});
describe('._onQueryError', function () {
it('should set the status to unavailable', function () {
this.model._onQueryError({});
expect(this.model.get('status')).toEqual('unavailable');
});
});
describe('._calculateBuckets', function () {
it('sets the buckets with the correct values', function () {
this.model._calculateBuckets(1483228800000, 1451606400000); // 2016-01-01 to 2017-01-01
var expected = [
{ bins: 527041, val: 'minute', label: 'Minutes' },
{ bins: 8785, val: 'hour', 'label': 'Hours' },
{ bins: 367, val: 'day', label: 'Days' },
{ bins: 54, val: 'week', label: 'Weeks' },
{ bins: 13, val: 'month', label: 'Months' },
{ bins: 5, val: 'quarter', label: 'Quarters' },
{ bins: 2, val: 'year', label: 'Years' },
{ bins: 1, val: 'decade', label: 'Decades' }
];
expect(this.model.get('buckets')).toEqual(expected);
});
it('should get the right decades value', function () {
this.model._calculateBuckets(946684800000, 283996800000); // 1979 to 2000
var expectedDecades = 4; // 70s, 80s, 90s, 00s
var buckets = this.model.get('buckets');
expect(buckets[7].val).toEqual('decade');
expect(buckets[7].bins).toBe(expectedDecades);
});
});
describe('._calculateDecadesDiff', function () {
function fromYear (year) {
var dateString = year.toString() + '-01-01';
return moment(dateString);
}
function assertDecadeDiff (startYear, endYear, expectedDiff) {
var diff = this.model._calculateDecadesDiff(fromYear(startYear), fromYear(endYear));
expect(diff).toBe(expectedDiff);
}
it('should return proper values', function () {
var assertDiff = assertDecadeDiff.bind(this);
assertDiff(1970, 1970, 1);
assertDiff(1970, 1979, 1);
assertDiff(1970, 1980, 2);
assertDiff(1970, 1999, 3);
assertDiff(1970, 2000, 4);
assertDiff(1500, 1999, 50);
});
});
describe('.getFilteredBuckets', function () {
it('should return the buckets below the limit', function () {
this.model._calculateBuckets(1483228800000, 1451606400000);
var filteredBuckets = this.model.getFilteredBuckets(367); // Maximum 367 bins
var expected = [
{ bins: 367, val: 'day', label: 'Days' },
{ bins: 54, val: 'week', label: 'Weeks' },
{ bins: 13, val: 'month', label: 'Months' },
{ bins: 5, val: 'quarter', label: 'Quarters' },
{ bins: 2, val: 'year', label: 'Years' },
{ bins: 1, val: 'decade', label: 'Decades' }
];
expect(filteredBuckets).toEqual(expected);
});
});
describe('.getPreferredBucket', function () {
it('should return the bucket closer to the max bins', function () {
this.model._calculateBuckets(1483228800000, 1451606400000);
var preferredBucket = this.model.getPreferredBucket(367); // Maximum 367 bins
var expected = { bins: 367, val: 'day', label: 'Days' };
expect(preferredBucket).toEqual(expected);
});
});
});
@@ -0,0 +1,182 @@
var $ = require('jquery');
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var EditorWidgetView = require('builder/editor/widgets/widget-view');
var WidgetDefinitionModel = require('builder/data/widget-definition-model');
var WidgetsService = require('builder/editor/widgets/widgets-service');
var Router = require('builder/routes/router');
var FactoryModals = require('../../factories/modals');
var ENTER_KEY_CODE = 13;
var WIDGET_DEFINITION = {
type: 'formula',
title: 'AVG districts homes',
column: 'areas',
operation: 'avg',
layer_id: 'l-1',
source: 'a0'
};
var LAYER_DEFINITION = {
id: 'l-1',
options: {
type: 'CartoDB',
color: 'red',
name: 'Layer Name',
table_name: 'table_name'
}
};
var ANALYSIS_NODE_SOURCE = {
get: function () {
return 'Source';
},
isSourceType: function () {
return true;
},
getColor: function () {
return '#fabada';
}
};
var ANALYSIS_NODE_NO_SOURCE = {
get: function () {
return 'Sampling';
},
isSourceType: function () {
return false;
},
getColor: function () {
return '#fabada';
}
};
describe('editor/widgets/widget-view', function () {
var layerDefinitionModel;
var stackLayoutModel;
var widgetDefinitionModel;
beforeEach(function () {
jasmine.clock().install();
spyOn(Router, 'navigate');
widgetDefinitionModel = new WidgetDefinitionModel(WIDGET_DEFINITION, {
configModel: {},
mapId: 'm-123'
});
layerDefinitionModel = new LayerDefinitionModel(LAYER_DEFINITION, {
parse: true,
configModel: {}
});
layerDefinitionModel.findAnalysisDefinitionNodeModel = function () {
return ANALYSIS_NODE_SOURCE;
};
stackLayoutModel = jasmine.createSpyObj('stackLayoutModel', ['nextStep']);
spyOn(EditorWidgetView.prototype, '_renameWidget');
this.view = new EditorWidgetView({
modals: FactoryModals.createModalService(),
userActions: {},
model: widgetDefinitionModel,
layer: layerDefinitionModel,
stackLayoutModel: stackLayoutModel,
querySchemaModel: {}
});
this.view.render();
});
afterEach(function () {
jasmine.clock().uninstall();
this.view.remove();
});
describe('.render', function () {
it('should render correctly', function () {
expect(this.view.$el.html()).toContain(WIDGET_DEFINITION.title);
expect(this.view.$el.text()).toContain(WIDGET_DEFINITION.source);
expect(this.view.$('.js-toggle-menu').length).toBe(1);
expect(this.view.$el.html()).toContain(LAYER_DEFINITION.options.table_name);
});
});
describe('when node is an analysis', function () {
beforeEach(function () {
layerDefinitionModel.findAnalysisDefinitionNodeModel = function () {
return ANALYSIS_NODE_NO_SOURCE;
};
this.view.render();
});
describe('.render', function () {
it('should render properly', function () {
expect(this.view.$el.html()).toContain('CDB-IconFont-ray');
expect(this.view.$el.html()).toContain(LAYER_DEFINITION.options.name);
});
});
});
it('should have no leaks', function () {
expect(this.view).toHaveNoLeaks();
});
it('should contain js-widgetItem class', function () {
expect(this.view.$el.hasClass('js-widgetItem')).toBeTruthy();
});
it('should add a data attribute with the model cid', function () {
expect(this.view.$el.data('model-cid')).toBe(widgetDefinitionModel.cid);
});
it('should show context menu', function () {
this.view.$('.js-toggle-menu').click();
expect(this.view._contextMenuFactory.getContextMenu().$('[data-val="rename-widget"]').length).toBe(1);
expect(this.view._contextMenuFactory.getContextMenu().$('[data-val="delete-widget"]').length).toBe(1);
document.body.removeChild(this.view._contextMenuFactory.getContextMenu().el);
});
it('should rename', function () {
var event = $.Event('keyup');
event.which = ENTER_KEY_CODE;
this.view.$('.js-toggle-menu').click();
this.view._contextMenuFactory.getContextMenu().$('[data-val="rename-widget"]').click();
this.view.$('.js-input').val('foo').trigger('keyup');
this.view.$('.js-input').trigger(event);
expect(EditorWidgetView.prototype._renameWidget).toHaveBeenCalled();
});
describe('when clicking on remove', function () {
it('should remove widget with WidgetsService', function () {
spyOn(WidgetsService, 'removeWidget');
this.view.$('.js-toggle-menu').click();
this.view._contextMenuFactory.getContextMenu().$('[data-val="delete-widget"]').click();
expect(WidgetsService.removeWidget).toHaveBeenCalledWith(widgetDefinitionModel);
});
});
it('should edit widget with WidgetsService', function () {
spyOn(WidgetsService, 'editWidget');
this.view.$('.js-title').trigger('click');
jasmine.clock().tick(201);
expect(WidgetsService.editWidget).toHaveBeenCalledWith(widgetDefinitionModel);
});
describe('._onDestroy', function () {
it('should call Router.goToWidgetList', function () {
spyOn(Router, 'goToWidgetList');
this.view._onDestroy();
expect(Router.goToWidgetList).toHaveBeenCalled();
});
it('should call .clean', function () {
spyOn(this.view, 'clean');
this.view._onDestroy();
expect(this.view.clean).toHaveBeenCalled();
});
});
});
@@ -0,0 +1,103 @@
var Backbone = require('backbone');
var WidgetsFormBaseSchemaModel = require('builder/editor/widgets/widgets-form/schema/widgets-form-base-schema-model');
describe('editor/widgets/widgets-form/schema/widgets-form-base-schema-model', function () {
var userModel = {
featureEnabled: function (whatever) {
return true;
}
};
it('should transform sync values to values that the form interprets as expected', function () {
var model = new WidgetsFormBaseSchemaModel({
sync_on_bbox_change: false
}, { parse: true, userModel: userModel });
expect(model.get('sync_on_bbox_change')).toEqual('false');
model = new WidgetsFormBaseSchemaModel({
sync_on_bbox_change: true
}, { parse: true, userModel: userModel });
expect(model.get('sync_on_bbox_change')).toEqual('true');
});
describe('.changeWidgetDefinitionModel', function () {
beforeEach(function () {
this.widgetDefinitionModel = new Backbone.Model();
spyOn(this.widgetDefinitionModel, 'set');
});
it('should set the expected values for the sync values', function () {
var model = new WidgetsFormBaseSchemaModel({}, {userModel: userModel});
model.set({
sync_on_bbox_change: ''
});
model.changeWidgetDefinitionModel(this.widgetDefinitionModel);
expect(this.widgetDefinitionModel.set).toHaveBeenCalled();
expect(this.widgetDefinitionModel.set.calls.argsFor(0)[0].sync_on_bbox_change).toBe(false);
model.set({
sync_on_bbox_change: 'true'
});
this.widgetDefinitionModel.set.calls.reset();
model.changeWidgetDefinitionModel(this.widgetDefinitionModel);
expect(this.widgetDefinitionModel.set).toHaveBeenCalled();
expect(this.widgetDefinitionModel.set.calls.argsFor(0)[0].sync_on_bbox_change).toBe(true);
});
it('should set the expected values for category and autostyle', function () {
this.widgetDefinitionModel.attributes.type = 'category';
this.widgetDefinitionModel.attributes.column = 'col';
var model = new WidgetsFormBaseSchemaModel({}, {userModel: userModel});
model.set({
auto_style_definition: ''
});
model.changeWidgetDefinitionModel(this.widgetDefinitionModel);
expect(this.widgetDefinitionModel.set).toHaveBeenCalled();
expect(this.widgetDefinitionModel.set.calls.argsFor(0)[0].auto_style_definition).toBe('');
model.set({
auto_style_definition: {}
});
this.widgetDefinitionModel.set.calls.reset();
model.changeWidgetDefinitionModel(this.widgetDefinitionModel);
expect(this.widgetDefinitionModel.set).toHaveBeenCalled();
var payload = this.widgetDefinitionModel.set.calls.argsFor(0)[0].auto_style_definition;
expect(payload.color.attribute).toBe('col');
expect(payload.color.quantification).toBe('category');
expect(payload.color.range.length).toBe(11);
expect(payload.color.domain).toBeTruthy();
expect(payload.color.bins).toBeFalsy();
});
it('should set the expected values for histogram and autostyle', function () {
this.widgetDefinitionModel.attributes.type = 'histogram';
this.widgetDefinitionModel.attributes.column = 'col';
var model = new WidgetsFormBaseSchemaModel({}, {userModel: userModel});
model.set({
auto_style_definition: ''
});
model.changeWidgetDefinitionModel(this.widgetDefinitionModel);
expect(this.widgetDefinitionModel.set).toHaveBeenCalled();
expect(this.widgetDefinitionModel.set.calls.argsFor(0)[0].auto_style_definition).toBe('');
model.set({
auto_style_definition: {}
});
this.widgetDefinitionModel.set.calls.reset();
model.changeWidgetDefinitionModel(this.widgetDefinitionModel);
expect(this.widgetDefinitionModel.set).toHaveBeenCalled();
var payload = this.widgetDefinitionModel.set.calls.argsFor(0)[0].auto_style_definition;
expect(payload.color.attribute).toBe('col');
expect(payload.color.quantification).toBe('quantiles');
expect(payload.color.range.length).toBe(7);
expect(payload.color.domain).toBeFalsy();
expect(payload.color.bins).toBe(7);
});
});
});
@@ -0,0 +1,66 @@
var Backbone = require('backbone');
var WidgetsFormColumnOptionsFactory = require('builder/editor/widgets/widgets-form/widgets-form-column-options-factory');
var WidgetsFormCategorySchemaModel = require('builder/editor/widgets/widgets-form/schema/widgets-form-category-schema-model');
var FactoryModals = require('../../../../factories/modals');
describe('editor/widgets/widgets-form/schema/widgets-form-category-schema-model', function () {
beforeEach(function () {
var querySchemaModel = new Backbone.Model();
var userModel = {
featureEnabled: function () {
return true;
}
};
this.widgetsFormColumnOptionsFactory = new WidgetsFormColumnOptionsFactory(querySchemaModel);
spyOn(this.widgetsFormColumnOptionsFactory, 'create').and.returnValue([{
val: 'col',
label: 'col',
type: 'number'
}, {
val: 'col2',
label: 'col2',
type: 'string'
}]);
spyOn(this.widgetsFormColumnOptionsFactory, 'unavailableColumnsHelpMessage');
this.modals = FactoryModals.createModalService();
this.model = new WidgetsFormCategorySchemaModel({
type: 'category',
layer_id: 'l-1',
column: 'col',
aggregation: 'count'
}, {
columnOptionsFactory: this.widgetsFormColumnOptionsFactory,
userModel: userModel,
configModel: {},
modals: this.modals
});
});
describe('.updateSchema', function () {
beforeEach(function () {
this.model.updateSchema();
});
it('should contain the real columns as options', function () {
expect(this.model.schema.column.options[0].label).toEqual('col');
});
it('should not contain aggregation column if the aggregation is "count"', function () {
this.model.set({aggregate: {
attribute: 'col',
operation: 'avg'
}});
expect(this.model.get('aggregation_column')).toBe('col');
this.model.set({aggregate: {
operation: 'count'
}});
expect(this.model.get('aggregation_column')).toBeUndefined();
});
});
});
@@ -0,0 +1,46 @@
var Backbone = require('backbone');
var WidgetsFormColumnOptionsFactory = require('builder/editor/widgets/widgets-form/widgets-form-column-options-factory');
var WidgetsFormFormulaSchemaModel = require('builder/editor/widgets/widgets-form/schema/widgets-form-formula-schema-model');
var FactoryModals = require('../../../../factories/modals');
describe('editor/widgets/widgets-form/schema/widgets-form-formula-schema-model', function () {
beforeEach(function () {
var userModel = {
featureEnabled: function () {
return true;
}
};
this.querySchemaModel = new Backbone.Model();
this.widgetsFormColumnOptionsFactory = new WidgetsFormColumnOptionsFactory(this.querySchemaModel);
spyOn(this.widgetsFormColumnOptionsFactory, 'create').and.returnValue([{
val: 'col',
label: 'col',
type: 'number'
}, {
val: 'col2',
label: 'col2',
type: 'string'
}]);
this.modals = FactoryModals.createModalService();
this.model = new WidgetsFormFormulaSchemaModel({
type: 'formula',
column: 'cartodb_id',
operation: 'avg'
}, {
columnOptionsFactory: this.widgetsFormColumnOptionsFactory,
userModel: userModel,
configModel: {},
modals: this.modals,
parse: true
});
});
it('should parse the aggregation correctly', function () {
var attrs = this.model.toJSON();
expect(attrs.aggregate.operator).toBe('avg');
expect(attrs.aggregate.attribute).toBe('cartodb_id');
});
});
@@ -0,0 +1,74 @@
var Backbone = require('backbone');
var WidgetsFormColumnOptionsFactory = require('builder/editor/widgets/widgets-form/widgets-form-column-options-factory');
var WidgetsFormHistogramSchemaModel = require('builder/editor/widgets/widgets-form/schema/widgets-form-histogram-schema-model');
var FactoryModals = require('../../../../factories/modals');
describe('editor/widgets/widgets-form/schema/widgets-form-histogram-schema-model', function () {
beforeEach(function () {
var userModel = {
featureEnabled: function () {
return true;
}
};
this.querySchemaModel = new Backbone.Model();
this.widgetsFormColumnOptionsFactory = new WidgetsFormColumnOptionsFactory(this.querySchemaModel);
spyOn(this.widgetsFormColumnOptionsFactory, 'create').and.returnValue([{
val: 'col',
label: 'col',
type: 'number'
}, {
val: 'col2',
label: 'col2',
type: 'string'
}]);
this.modals = FactoryModals.createModalService();
this.model = new WidgetsFormHistogramSchemaModel({
type: 'histogram'
}, {
columnOptionsFactory: this.widgetsFormColumnOptionsFactory,
userModel: userModel,
configModel: {},
modals: this.modals
});
});
describe('.updateSchema', function () {
it('column validator is correct', function () {
// Arrange
var expectedValidator = {
type: 'columnType',
columnsCollection: this.querySchemaModel.columnsCollection,
columnType: 'number'
};
// Act
this.model.updateSchema();
// Assert
expect(this.model.schema.column).toBeDefined();
expect(this.model.schema.column.validators).toBeDefined();
expect(this.model.schema.column.validators[0]).toEqual(expectedValidator);
});
it('bins validator is correct', function () {
// Arrange
var expectedValidator = {
type: 'interval',
min: 2,
max: 30
};
// Act
this.model.updateSchema();
// Assert
expect(this.model.schema.bins).toBeDefined();
expect(this.model.schema.bins.validators).toBeDefined();
expect(this.model.schema.bins.validators[0]).toEqual('required');
expect(this.model.schema.bins.validators[1]).toEqual(expectedValidator);
});
});
});
@@ -0,0 +1,204 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var QuerySchemaModel = require('builder/data/query-schema-model');
var WidgetsFormColumnOptionsFactory = require('builder/editor/widgets/widgets-form/widgets-form-column-options-factory');
var WidgetsFormTimeSeriesSchemaModel = require('builder/editor/widgets/widgets-form/schema/widgets-form-time-series-schema-model');
var FactoryModals = require('../../../../factories/modals');
describe('editor/widgets/widgets-form/schema/widgets-form-time-series-schema-model', function () {
beforeEach(function () {
var userModel = {
featureEnabled: function () {
return true;
}
};
var configModel = new ConfigModel({
user_name: 'pepe'
});
this.querySchemaModel = new QuerySchemaModel({
query: 'SELECT * FROM foobar',
status: 'fetched'
}, {
configModel: configModel
});
this.querySchemaModel.columnsCollection.reset([
{ name: 'cartodb_id', type: 'number' },
{ name: 'created_at', type: 'date' }
]);
this.widgetsFormColumnOptionsFactory = new WidgetsFormColumnOptionsFactory(this.querySchemaModel);
spyOn(this.widgetsFormColumnOptionsFactory, 'create').and.returnValue([{
val: 'col',
label: 'col',
type: 'number'
}, {
val: 'col2',
label: 'col2',
type: 'string'
}]);
this.modals = FactoryModals.createModalService();
this.model = new WidgetsFormTimeSeriesSchemaModel({
type: 'time-series',
column: 'cartodb_id'
}, {
columnOptionsFactory: this.widgetsFormColumnOptionsFactory,
querySchemaModel: this.querySchemaModel,
userModel: userModel,
configModel: configModel,
modals: this.modals
});
});
it('should call ._onColumnChanged when column changes', function () {
spyOn(this.model, '_onColumnChanged');
this.model._initBinds();
this.model.trigger('change:column');
expect(this.model._onColumnChanged).toHaveBeenCalled();
});
it('should call .updateSchema when _timeSeriesQueryModel buckets change', function () {
spyOn(this.model, 'updateSchema');
this.model._initBinds();
this.model._timeSeriesQueryModel.trigger('change:buckets');
expect(this.model.updateSchema).toHaveBeenCalled();
});
describe('.updateSchema', function () {
beforeEach(function () {
spyOn(this.model._querySchemaModel, 'isFetched').and.returnValue(true);
});
it('always have column and widget_style_definition', function () {
this.model.updateSchema();
expect(this.model.schema.column).toBeDefined();
expect(this.model.schema.widget_style_definition).toBeDefined();
});
it('should have aggregation, and timezone if column is type date', function () {
spyOn(this.model, '_getColumnType').and.returnValue('date');
this.model.updateSchema();
expect(this.model.schema.aggregation).toBeDefined();
expect(this.model.schema.timezone).toBeDefined();
expect(this.model.schema.bins).not.toBeDefined();
});
it('should have bins if column is not type date', function () {
this.model.updateSchema();
expect(this.model.schema.bins).toBeDefined();
expect(this.model.schema.aggregation).not.toBeDefined();
expect(this.model.schema.timezone).not.toBeDefined();
});
});
describe('.getFields', function () {
it('should return column', function () {
var fields = this.model.getFields();
expect(fields.data).toContain('column');
});
it('should return aggregation if column type is date', function () {
spyOn(this.model, '_getColumnType').and.returnValue('date');
var fields = this.model.getFields();
expect(fields.data).toContain('aggregation');
expect(fields.data).toContain('timezone');
expect(fields.data).not.toContain('bins');
});
it('should not return aggregation, and timezone if column type is not date', function () {
var fields = this.model.getFields();
expect(fields.data).toContain('bins');
expect(fields.data).not.toContain('aggregation');
expect(fields.data).not.toContain('timezone');
});
it('should return sync_on_bbox_change', function () {
var fields = this.model.getFields();
expect(fields.style).toContain('sync_on_bbox_change');
});
it('should return widget_style_definition', function () {
var fields = this.model.getFields();
expect(fields.style).toContain('widget_style_definition');
});
});
describe('._isDateType', function () {
it('should return true if type is date', function () {
var model = new Backbone.Model({ type: 'number' });
expect(this.model._isDateType(model)).toBe(false);
model.set('type', 'date');
expect(this.model._isDateType(model)).toBe(true);
});
});
describe('._isNumberType', function () {
it('should return true if type is number', function () {
var model = new Backbone.Model({ type: 'date' });
expect(this.model._isNumberType(model)).toBe(false);
model.set('type', 'number');
expect(this.model._isNumberType(model)).toBe(true);
});
});
describe('._isNumberOrDateType', function () {
it('should return true if type is number', function () {
var model = new Backbone.Model({ type: 'string' });
expect(this.model._isNumberOrDateType(model)).toBe(false);
model.set('type', 'number');
expect(this.model._isNumberOrDateType(model)).toBe(true);
model.set('type', 'date');
expect(this.model._isNumberOrDateType(model)).toBe(true);
});
});
describe('._prepareAttributesForWidgetDefinition', function () {
describe('column type is date', function () {
it('should set bins to undefined and assign an offset to the timezone', function () {
spyOn(this.model, '_getColumnType').and.returnValue('date');
this.model.set({
bins: '100',
timezone: 'America/Barbados'
});
var attrs = this.model._prepareAttributesForWidgetDefinition();
expect(attrs.bins).toBeUndefined();
expect(attrs.offset).toBe(-14400);
expect(attrs.timezone).toBe('America/Barbados');
});
});
describe('column type is not date', function () {
it('should set aggregation, timezone, and offset to undefined', function () {
spyOn(this.model, '_getColumnType').and.returnValue('number');
this.model.set({
aggregation: 'month',
timezone: 'Atlantic/Canary'
});
var attrs = this.model._prepareAttributesForWidgetDefinition();
expect(attrs.aggregation).toBeUndefined();
expect(attrs.timezone).toBeUndefined();
expect(attrs.offset).toBeUndefined();
});
});
});
describe('._getColumnType', function () {
it('should return the column type', function () {
this.model.set('column', 'cartodb_id');
expect(this.model._getColumnType()).toEqual('number');
});
});
});
@@ -0,0 +1,96 @@
var ConfigModel = require('builder/data/config-model');
var QuerySchemaModel = require('builder/data/query-schema-model');
var WidgetsFormColumnOptionsFactory = require('builder/editor/widgets/widgets-form/widgets-form-column-options-factory');
describe('editor/widgets/widgets-form/widgets-form-column-options-factory', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.querySchemaModel = new QuerySchemaModel({
query: 'SELECT * from foobar'
}, {
configModel: configModel
});
this.factory = new WidgetsFormColumnOptionsFactory(this.querySchemaModel);
});
describe('.unavailableColumnsHelpMessage', function () {
it('should return a string if columns are not available', function () {
this.querySchemaModel.set('status', 'fetched');
expect(this.factory.unavailableColumnsHelpMessage()).toBeUndefined();
this.querySchemaModel.set('status', 'unavailable');
expect(this.factory.unavailableColumnsHelpMessage()).toEqual(jasmine.any(String));
});
});
describe('.create', function () {
describe('when columns are not yet fetched', function () {
beforeEach(function () {
this.querySchemaModel.set('status', 'fetching');
});
it('should return a disable option with loading message', function () {
expect(this.factory.create()).toEqual([{val: jasmine.any(String)}]);
});
describe('when columns are not available for whatever reason', function () {
beforeEach(function () {
this.querySchemaModel.set('status', 'unavailable');
});
it('should return the current val', function () {
expect(this.factory.create('current')).toEqual([{val: 'current'}]);
});
});
describe('when columns are finally fetched', function () {
beforeEach(function () {
this.querySchemaModel.columnsCollection.reset([
{name: 'cartodb_id', type: 'number'},
{name: 'title', type: 'string'},
{name: 'created_at', type: 'date'}
]);
this.querySchemaModel.set('status', 'fetched');
});
it('should return the columns', function () {
expect(this.factory.create('current')).toEqual([
{
label: 'cartodb_id',
val: 'cartodb_id',
type: 'number'
}, {
label: 'title',
val: 'title',
type: 'string'
}, {
label: 'created_at',
val: 'created_at',
type: 'date'
}
]);
});
it('should return only valid columns if filter is given', function () {
var noStringsFilter = function (m) {
return m.get('type') !== 'string';
};
expect(this.factory.create('current', noStringsFilter)).toEqual([
{
label: 'cartodb_id',
val: 'cartodb_id',
type: 'number'
}, {
label: 'created_at',
val: 'created_at',
type: 'date'
}
]);
});
});
});
});
});
@@ -0,0 +1,119 @@
var $ = require('jquery');
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var QuerySchemaModel = require('builder/data/query-schema-model');
var WidgetsFormContentView = require('builder/editor/widgets/widgets-form/widgets-form-content-view');
var WidgetDefinitionModel = require('builder/data/widget-definition-model');
var Router = require('builder/routes/router');
var FactoryModals = require('../../../factories/modals');
describe('editor/widgets/widgets-form/widgets-form-content-view', function () {
beforeEach(function () {
spyOn(Router, 'navigate');
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var querySchemaModel = new QuerySchemaModel({
query: 'SELECT * FROM foobar',
status: 'fetched'
}, {
configModel: configModel
});
querySchemaModel.columnsCollection.reset([
{ name: 'cartodb_id', type: 'number' },
{ name: 'created_at', type: 'date' }
]);
var userModel = {
featureEnabled: function () {
return true;
}
};
this.modals = FactoryModals.createModalService();
var dfd = $.Deferred();
dfd.resolve();
spyOn(querySchemaModel, 'fetch').and.returnValue(dfd.promise());
this.analysisDefinitionNodesCollection = new Backbone.Collection();
var nodeDefModel = this.analysisDefinitionNodesCollection.add({
id: 'a0',
type: 'source',
table_name: 'test'
});
nodeDefModel.querySchemaModel = querySchemaModel;
nodeDefModel.isSourceType = function () {
return true;
};
this.layerDefinitionsCollection = new Backbone.Collection({
id: 'l1',
color: 'red',
name: 'layerrr'
});
var layerDefinitionModel = this.layerDefinitionsCollection.at(0);
layerDefinitionModel.getName = function () {
return this.get('name');
};
layerDefinitionModel.getTableName = function () {
return 'table name';
};
layerDefinitionModel.getAnalysisDefinitionNodeModel = function () {
return nodeDefModel;
};
layerDefinitionModel.findAnalysisDefinitionNodeModel = function () {
return nodeDefModel;
};
this.widgetDefinitionModel = new WidgetDefinitionModel({
id: 'w-456',
title: 'some title',
type: 'formula',
layer_id: 'l1',
source: 'a0',
column: 'hello',
operation: 'sum',
prefix: 'my-prefix'
}, {
configModel: configModel,
mapId: 'm-123'
});
this.stackLayoutModel = jasmine.createSpyObj('stackLayoutModel', ['prevStep']);
this.view = new WidgetsFormContentView({
userActions: {},
modals: this.modals,
widgetDefinitionModel: this.widgetDefinitionModel,
layerDefinitionsCollection: this.layerDefinitionsCollection,
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
stackLayoutModel: this.stackLayoutModel,
stackLayoutPrevStep: 0,
userModel: userModel,
configModel: configModel
});
this.view.render();
});
it('should generate the right tabs', function () {
expect(this.view.$el.text()).toContain('editor.widgets.widgets-form.data.title');
expect(this.view.$el.text()).toContain('editor.widgets.widgets-form.style.title');
});
it('should get back to the previous step if arrow is clicked', function () {
spyOn(Router, 'goToPreviousRoute');
this.view.$('.js-back').click();
expect(Router.goToPreviousRoute).toHaveBeenCalledWith({
fallback: 'widgets'
});
});
it('should not have leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
@@ -0,0 +1,252 @@
var _ = require('underscore');
var ConfigModel = require('builder/data/config-model');
var QuerySchemaModel = require('builder/data/query-schema-model');
var WidgetsFormFactory = require('builder/editor/widgets/widgets-form/widgets-form-factory');
var WidgetDefinitionModel = require('builder/data/widget-definition-model');
var FactoryModals = require('../../../factories/modals');
describe('editor/widgets/widgets-form/widgets-form-factory', function () {
var userModel = {
featureEnabled: function (whatever) {
return true;
}
};
beforeEach(function () {
this.configModel = new ConfigModel({
base_url: '/u/pepe',
user_name: 'pepe'
});
this.querySchemaModel = new QuerySchemaModel({
query: 'SELECT * FROM foobar'
}, {
configModel: this.configModel
});
this.modals = FactoryModals.createModalService();
});
describe('data form', function () {
describe('formula data form', function () {
beforeEach(function () {
this.widgetDefinitionModel = new WidgetDefinitionModel({
layer_id: 'w-456',
title: 'some title',
type: 'formula',
options: {
column: 'hello',
operation: 'sum',
prefix: 'my-prefix'
}
}, {
configModel: this.configModel,
mapId: 'm-123'
});
});
it('should generate the widget form model', function () {
var model = WidgetsFormFactory.createWidgetFormModel({
widgetDefinitionModel: this.widgetDefinitionModel,
querySchemaModel: this.querySchemaModel,
modals: this.modals,
userModel: userModel,
configModel: this.configModel
});
expect(model).toBeDefined();
});
});
describe('category data form', function () {
beforeEach(function () {
this.widgetDefinitionModel = new WidgetDefinitionModel({
layer_id: 'w-456',
title: 'some title',
type: 'category',
options: {
column: 'hello'
}
}, {
configModel: this.configModel,
mapId: 'm-123'
});
});
it('should generate the widget form model', function () {
var model = WidgetsFormFactory.createWidgetFormModel({
widgetDefinitionModel: this.widgetDefinitionModel,
querySchemaModel: this.querySchemaModel,
modals: this.modals,
userModel: userModel,
configModel: this.configModel
});
expect(model).toBeDefined();
});
});
describe('histogram data form', function () {
beforeEach(function () {
this.widgetDefinitionModel = new WidgetDefinitionModel({
layer_id: 'w-456',
title: 'some title',
type: 'histogram',
options: {
column: 'hello'
}
}, {
configModel: this.configModel,
mapId: 'm-123'
});
});
it('should generate the widget form model', function () {
var model = WidgetsFormFactory.createWidgetFormModel({
widgetDefinitionModel: this.widgetDefinitionModel,
querySchemaModel: this.querySchemaModel,
modals: this.modals,
userModel: userModel,
configModel: this.configModel
});
expect(model).toBeDefined();
});
});
describe('time-series data form', function () {
beforeEach(function () {
this.widgetDefinitionModel = new WidgetDefinitionModel({
layer_id: 'w-456',
title: 'some title',
type: 'time-series',
options: {
column: 'hello'
}
}, {
configModel: this.configModel,
mapId: 'm-123'
});
});
it('should generate the widget form model', function () {
var model = WidgetsFormFactory.createWidgetFormModel({
widgetDefinitionModel: this.widgetDefinitionModel,
querySchemaModel: this.querySchemaModel,
modals: this.modals,
userModel: userModel,
configModel: this.configModel
});
expect(model).toBeDefined();
});
});
describe('style', function () {
describe('rest style form', function () {
beforeEach(function () {
this.widgetDefinitionModel = new WidgetDefinitionModel({
layer_id: 'w-456',
title: 'some title',
type: 'category',
options: {
column: 'hello'
}
}, {
configModel: this.configModel,
mapId: 'm-123'
});
});
it('should generate the widget form model', function () {
var model = WidgetsFormFactory.createWidgetFormModel({
widgetDefinitionModel: this.widgetDefinitionModel,
querySchemaModel: this.querySchemaModel,
modals: this.modals,
userModel: userModel,
configModel: this.configModel
});
expect(model.schema.description).not.toBeDefined();
expect(model.schema.sync_on_bbox_change).toBeDefined();
});
});
describe('formula style form', function () {
beforeEach(function () {
this.widgetDefinitionModel = new WidgetDefinitionModel({
layer_id: 'w-456',
title: 'helloooo',
type: 'formula',
options: {
column: 'hello'
}
}, {
configModel: this.configModel,
mapId: 'm-123'
});
});
it('should generate the widget form model', function () {
var model = WidgetsFormFactory.createWidgetFormModel({
widgetDefinitionModel: this.widgetDefinitionModel,
querySchemaModel: this.querySchemaModel,
modals: this.modals,
userModel: userModel,
configModel: this.configModel
});
expect(model).toBeDefined();
model.updateSchema();
expect(model.schema.description).toBeDefined();
expect(model.schema.sync_on_bbox_change).toBeDefined();
});
});
});
});
describe('type validation', function () {
beforeEach(function () {
this.widgetDefinitionModel = new WidgetDefinitionModel({
layer_id: 'w-456',
title: 'some title',
type: 'time-series',
options: {
column: 'hello'
}
}, {
configModel: this.configModel,
mapId: 'm-123'
});
});
describe('data validation', function () {
it('should return the valid widget types for string columns', function () {
this.querySchemaModel.columnsCollection.reset([
{ name: 'cartodb_id', type: 'string' },
{ name: 'nobel_prizes_winners', type: 'string' },
{ name: 'year', type: 'string' }
]);
var types = WidgetsFormFactory.getDataTypes(this.querySchemaModel);
var values = _.pluck(types, 'value');
expect(values.sort()).toEqual(['category']);
});
it('should return the valid widget types for numeric columns', function () {
this.querySchemaModel.columnsCollection.reset([
{ name: 'cartodb_id', type: 'number' },
{ name: 'year', type: 'number' }
]);
var types = WidgetsFormFactory.getDataTypes(this.querySchemaModel);
var values = _.pluck(types, 'value');
expect(values.sort()).toEqual(['category', 'formula', 'histogram', 'time-series']);
});
it('should return the valid widget types for date columns', function () {
this.querySchemaModel.columnsCollection.reset([
{ name: 'year', type: 'date' }
]);
var types = WidgetsFormFactory.getDataTypes(this.querySchemaModel);
var values = _.pluck(types, 'value');
expect(values.sort()).toEqual(['category', 'time-series']);
});
});
});
});
@@ -0,0 +1,65 @@
var QuerySchemaModel = require('builder/data/query-schema-model');
var WidgetsFormFieldsView = require('builder/editor/widgets/widgets-form/widgets-form-fields-view');
var WidgetDefinitionModel = require('builder/data/widget-definition-model');
describe('editor/widgets/widgets-form/widgets-form-fields-view', function () {
beforeEach(function () {
var querySchemaModel = new QuerySchemaModel({
query: 'SELECT * FROM foobar',
status: 'fetched'
}, {
configModel: {}
});
querySchemaModel.columnsCollection.reset([
{ name: 'cartodb_id', type: 'number' },
{ name: 'created_at', type: 'date' }
]);
this.widgetDefinitionModel = new WidgetDefinitionModel({
id: 'w-456',
title: 'some title',
type: 'formula',
layer_id: 'l1',
source: 'a0',
column: 'hello',
operation: 'sum'
}, {
configModel: {},
mapId: 'm-123'
});
this.view = new WidgetsFormFieldsView({
userActions: {},
widgetDefinitionModel: this.widgetDefinitionModel,
querySchemaModel: querySchemaModel,
configModel: {},
userModel: {},
modals: {}
});
spyOn(this.view, 'render').and.callThrough();
this.view.render();
});
describe('.validateForm', function () {
it('should call _widgetFormView.validate()', function () {
spyOn(this.view._widgetFormView, 'validate');
this.view.validateForm();
expect(this.view._widgetFormView.validate).toHaveBeenCalled();
});
});
it('should call .render if _widgetFormModel change:column, change:aggregation or changeSchema is triggered', function () {
this.view._widgetFormModel.trigger('change:column');
expect(this.view.render).toHaveBeenCalled();
this.view._widgetFormModel.trigger('change:aggregation');
expect(this.view.render).toHaveBeenCalled();
this.view._widgetFormModel.trigger('changeSchema');
expect(this.view.render).toHaveBeenCalled();
});
});
@@ -0,0 +1,258 @@
var $ = require('jquery');
var _ = require('underscore');
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var QuerySchemaModel = require('builder/data/query-schema-model');
var WidgetsFormView = require('builder/editor/widgets/widgets-form/widgets-form-view');
var WidgetDefinitionModel = require('builder/data/widget-definition-model');
var UserActions = require('builder/data/user-actions');
var FactoryModals = require('../../../factories/modals');
describe('editor/widgets/widgets-form/widgets-form-view', function () {
beforeEach(function () {
this.configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.querySchemaModel = new QuerySchemaModel({
query: 'SELECT * FROM foobar',
status: 'fetched'
}, {
configModel: this.configModel
});
this.querySchemaModel.columnsCollection.reset([
{ name: 'cartodb_id', type: 'number' },
{ name: 'created_at', type: 'date' }
]);
var dfd = $.Deferred();
dfd.resolve();
spyOn(this.querySchemaModel, 'fetch').and.returnValue(dfd.promise());
this.widgetDefinitionModel = new WidgetDefinitionModel({
id: 'w-456',
title: 'some title',
type: 'formula',
layer_id: 'l1',
source: 'a0',
column: 'hello',
operation: 'sum'
}, {
configModel: this.configModel,
mapId: 'm-123'
});
this.modals = FactoryModals.createModalService();
this.userModel = {
featureEnabled: function () {
return true;
}
};
this.userActions = UserActions({
userModel: this.userModel,
analysisDefinitionsCollection: {},
analysisDefinitionNodesCollection: {},
layerDefinitionsCollection: {},
widgetDefinitionsCollection: {}
});
this.promise = $.Deferred();
spyOn(this.userActions, 'saveWidget').and.returnValue(this.promise);
this.view = new WidgetsFormView({
userActions: this.userActions,
widgetDefinitionModel: this.widgetDefinitionModel,
querySchemaModel: this.querySchemaModel,
configModel: {},
userModel: this.userModel,
modals: this.modals
});
this.view.render();
});
it('should render properly', function () {
expect(_.size(this.view._subviews)).toBe(2); // carousel and form fields
});
it('should update form model when widgetDefinitionModel rename', function () {
this.widgetDefinitionModel.set({title: 'wadus'});
expect(this.view._formView._widgetFormModel.get('title')).toBe('wadus');
});
it('should have enabler editor component for suffix and prefix, disable by default', function () {
expect(this.view.$('input[name=prefix]').length).toBe(1);
expect(this.view.$('input[name=suffix]').length).toBe(1);
expect(this.view.$('input[name=prefix]').prop('readonly')).toBe(true);
expect(this.view.$('input[name=suffix]').prop('readonly')).toBe(true);
});
it('should include widget style fields if needed', function () {
// formula widget
expect(this.view.$('div.js-input[name=widget_style_definition]').length).toBe(0);
expect(this.view.$('div.js-input[name=auto_style_definition]').length).toBe(0);
this.widgetDefinitionModel.set({type: 'category'});
this.view.render();
// category widget
expect(this.view.$('div.js-input[name=widget_style_definition]').length).toBe(1);
expect(this.view.$('div.js-input[name=auto_style_definition]').length).toBe(1);
this.widgetDefinitionModel.set({type: 'histogram'});
this.view.render();
// category widget
expect(this.view.$('div.js-input[name=widget_style_definition]').length).toBe(1);
expect(this.view.$('div.js-input[name=auto_style_definition]').length).toBe(1);
});
describe('._initBinds', function () {
it('should call ._renderFormAndValidate when change:type of _widgetDefinitionModel is triggered', function () {
spyOn(this.view, '_renderFormAndValidate');
this.view._initBinds();
this.view._widgetDefinitionModel.set('type', 'histogram');
expect(this.view._renderFormAndValidate).toHaveBeenCalled();
});
it('should call .render when change:status of _querySchemaModel is triggered', function () {
spyOn(this.view, 'render');
this.view._initBinds();
this.view._querySchemaModel.set('status', 'error');
expect(this.view.render).toHaveBeenCalled();
});
it('should call ._shouldFetchQuery when change:status of _querySchemaModel:query changes', function () {
spyOn(this.view, '_shouldFetchQuery');
this.view._initBinds();
this.view._querySchemaModel.set('query', 'SELECT * FROM somewhere;');
expect(this.view._shouldFetchQuery).toHaveBeenCalled();
});
});
describe('._getFilteredDataTypes', function () {
beforeEach(function () {
var collection = new Backbone.Collection();
var widgetDefinitionModel1 = new WidgetDefinitionModel({
title: 'some title',
type: 'formula',
layer_id: 'l1',
source: 'a0',
column: 'hello',
operation: 'sum'
}, {
collection: collection,
configModel: this.configModel,
mapId: 'm-123'
});
var widgetDefinitionModel2 = new WidgetDefinitionModel({
layer_id: 'l1',
title: 'some title',
type: 'time-series',
options: {
column: 'hello'
}
}, {
collection: collection,
configModel: this.configModel,
mapId: 'm-123'
});
var widgetDefinitionModel3 = new WidgetDefinitionModel({
title: 'some title',
type: 'formula',
layer_id: 'l1',
source: 'a0',
column: 'hello',
operation: 'sum'
}, {
collection: new Backbone.Collection(),
configModel: this.configModel,
mapId: 'm-123'
});
collection.add(widgetDefinitionModel1);
collection.add(widgetDefinitionModel2);
this.view1 = new WidgetsFormView({
userActions: this.userActions,
querySchemaModel: this.querySchemaModel,
configModel: {},
widgetDefinitionModel: widgetDefinitionModel1,
userModel: this.userModel,
modals: this.modals
});
this.view2 = new WidgetsFormView({
userActions: this.userActions,
querySchemaModel: this.querySchemaModel,
configModel: {},
widgetDefinitionModel: widgetDefinitionModel2,
userModel: this.userModel,
modals: this.modals
});
this.view3 = new WidgetsFormView({
userActions: this.userActions,
querySchemaModel: this.querySchemaModel,
configModel: {},
widgetDefinitionModel: widgetDefinitionModel3,
userModel: this.userModel,
modals: this.modals
});
});
it('should not include time-series if there is a time-series widget', function () {
var filteredDataTypes = this.view1._getFilteredDataTypes();
expect(filteredDataTypes.length).toBe(3);
});
it('should include time-series if it is a time-series widget', function () {
var filteredDataTypes = this.view2._getFilteredDataTypes();
expect(filteredDataTypes.length).toBe(4);
});
it('should include time-series if there is not a time-series widget', function () {
var filteredDataTypes = this.view3._getFilteredDataTypes();
expect(filteredDataTypes.length).toBe(4);
});
afterEach(function () {
this.view1.clean();
this.view2.clean();
this.view3.clean();
});
});
describe('._renderFormAndValidate', function () {
it('should call _renderForm and saveWidget if the form pass the validation', function () {
spyOn(this.view, '_renderForm');
spyOn(this.view._formView, 'validateForm').and.returnValue(null);
this.view._renderFormAndValidate();
expect(this.view._renderForm).toHaveBeenCalled();
expect(this.view._userActions.saveWidget).toHaveBeenCalledWith(this.widgetDefinitionModel);
});
it('should call _renderForm and not call saveWidget if the form dont pass the validation', function () {
spyOn(this.view, '_renderForm');
spyOn(this.view._formView, 'validateForm').and.returnValue('Error');
this.view._renderFormAndValidate();
expect(this.view._renderForm).toHaveBeenCalled();
expect(this.view._userActions.saveWidget).not.toHaveBeenCalled();
});
});
it('should not have leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
@@ -0,0 +1,126 @@
var Backbone = require('backbone');
var $ = require('jquery');
var ConfigModel = require('builder/data/config-model');
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var WidgetHeader = require('builder/editor/widgets/widgets-form/widget-header');
var WidgetDefinitionModel = require('builder/data/widget-definition-model');
var FactoryModals = require('../../../factories/modals');
describe('editor/widgets/widgets-form/widgets-header', function () {
var nodeDefModel;
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
nodeDefModel = new Backbone.Model({
id: 'a0',
type: 'source',
table_name: 'kvothe'
});
nodeDefModel.isSourceType = function () {
return true;
};
var layerDefinitionModel = new LayerDefinitionModel({
id: 'l1',
color: 'red',
name: 'Kvothe Kingskiller',
kind: 'carto',
table_name: 'kvothe'
}, {
parse: false,
configModel: configModel
});
layerDefinitionModel.findAnalysisDefinitionNodeModel = function () {
return nodeDefModel;
};
this.widgetDefinitionModel = new WidgetDefinitionModel({
id: 'w-456',
title: 'some title',
type: 'formula',
layer_id: 'l1',
source: 'a0',
column: 'hello',
operation: 'sum',
prefix: 'my-prefix'
}, {
configModel: configModel,
mapId: 'm-123'
});
this.modals = FactoryModals.createModalService();
this.stackLayoutModel = jasmine.createSpyObj('stackLayoutModel', ['prevStep']);
spyOn(WidgetHeader.prototype, '_confirmDeleteWidget');
spyOn(WidgetHeader.prototype, '_renameWidget');
this.view = new WidgetHeader({
userActions: {},
modals: this.modals,
model: this.widgetDefinitionModel,
layerDefinitionModel: layerDefinitionModel,
stackLayoutModel: this.stackLayoutModel,
configModel: configModel
});
this.view.render();
});
describe('.render', function () {
it('should render properly', function () {
expect(this.view.$('.js-toggle-menu').length).toBe(1);
expect(this.view.$('.js-title').length).toBe(1);
expect(this.view.$('.js-title').text()).toContain('some title');
expect(this.view.$el.html()).toContain('a0');
expect(this.view.$el.html()).toContain('kvothe');
});
});
describe('when node is an analysis', function () {
describe('.render', function () {
beforeEach(function () {
nodeDefModel.isSourceType = function () {
return false;
};
this.view.render();
});
it('should render properly', function () {
expect(this.view.$el.html()).toContain('Kvothe Kingskiller');
expect(this.view.$el.html()).toContain('CDB-IconFont-ray');
});
});
});
it('should show context menu', function () {
this.view.$('.js-toggle-menu').click();
expect(this.view._contextMenuFactory.getContextMenu().$('[data-val="rename-widget"]').length).toBe(1);
expect(this.view._contextMenuFactory.getContextMenu().$('[data-val="delete-widget"]').length).toBe(1);
});
it('should rename', function () {
var e = $.Event('keyup');
e.which = 13;
this.view.$('.js-toggle-menu').click();
this.view._contextMenuFactory.getContextMenu().$('[data-val="rename-widget"]').click();
this.view.$('.js-input').val('foo').trigger('keyup');
this.view.$('.js-input').trigger(e);
expect(WidgetHeader.prototype._renameWidget).toHaveBeenCalled();
});
it('should delete', function () {
this.view.$('.js-toggle-menu').click();
this.view._contextMenuFactory.getContextMenu().$('[data-val="delete-widget"]').click();
expect(WidgetHeader.prototype._confirmDeleteWidget).toHaveBeenCalled();
});
it('should not have leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
@@ -0,0 +1,77 @@
var $ = require('jquery');
var Backbone = require('backbone');
var WidgetsService = require('builder/editor/widgets/widgets-service');
var ModalsServiceModel = require('builder/components/modals/modals-service-model');
var Router = require('builder/routes/router');
describe('editor/widgets/widgets-service', function () {
var widgetModel;
beforeEach(function () {
spyOn(Router, 'goToWidget');
spyOn(Router, 'goToWidgetList');
widgetModel = new Backbone.Model({ id: 'wadus', name: 'wadus' });
WidgetsService._editorModel = new Backbone.Model();
WidgetsService._modals = new ModalsServiceModel();
});
describe('.editWidget', function () {
it('should set `edition` to false in _editorModel', function () {
spyOn(WidgetsService._editorModel, 'set');
WidgetsService.editWidget(widgetModel);
expect(WidgetsService._editorModel.set).toHaveBeenCalledWith('edition', false);
});
it('should call Router.goToWidget with the widgetModel id', function () {
WidgetsService.editWidget(widgetModel);
expect(Router.goToWidget).toHaveBeenCalledWith(widgetModel.get('id'));
});
it('should trigger `cancelPreviousEditions` in _editorModel', function () {
spyOn(WidgetsService._editorModel, 'trigger');
WidgetsService.editWidget(widgetModel);
expect(WidgetsService._editorModel.trigger).toHaveBeenCalledWith('cancelPreviousEditions');
});
});
describe('.removeWidget', function () {
afterEach(function () {
WidgetsService._modalView.clean();
});
describe('when the widgetDefinitionModel is defined', function () {
it('should create a delete confirmation widget template', function () {
spyOn(WidgetsService._modals, 'create').and.callThrough();
WidgetsService.removeWidget(widgetModel);
expect(WidgetsService._modals.create).toHaveBeenCalled();
expect($('.js-cancel').text().trim()).toEqual('editor.widgets.delete.cancel');
expect($('.js-confirm').text().trim()).toEqual('editor.widgets.delete.confirm');
});
it('should destroy the widget when the user clicks on ', function () {
spyOn(widgetModel, 'destroy');
WidgetsService.removeWidget(widgetModel);
$('.js-confirm').click();
expect(widgetModel.destroy).toHaveBeenCalled();
});
it('should redirect to the widget list when the user clicks on confirm', function () {
spyOn(widgetModel, 'destroy');
WidgetsService.removeWidget(widgetModel);
$('.js-confirm').click();
expect(Router.goToWidgetList).toHaveBeenCalled();
});
});
});
});
@@ -0,0 +1,236 @@
var $ = require('jquery');
var Backbone = require('backbone');
var UserActions = require('builder/data/user-actions');
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var EditorWidgetsView = require('builder/editor/widgets/widgets-view');
var WidgetDefinitionModel = require('builder/data/widget-definition-model');
var Router = require('builder/routes/router');
var FactoryModals = require('../../factories/modals');
var STATES = {
loading: 'loading',
ready: 'ready'
};
describe('editor/widgets/widgets-view', function () {
var analysisDefinitionNodeModel;
function createLayerDefinitionsCollection (layerDefinitionModel) {
var layerDefinitionsCollection = new Backbone.Collection([layerDefinitionModel]);
layerDefinitionsCollection.loadAllQueryGeometryModels = function (callback) {
callback();
};
layerDefinitionsCollection.isThereAnyGeometryData = function () {
return Promise.resolve(true);
};
return layerDefinitionsCollection;
}
beforeEach(function () {
spyOn(Router, 'navigate');
this.model = new LayerDefinitionModel({
id: 'l-100',
options: {
type: 'CartoDB'
}
}, {
parse: true,
configModel: {}
});
analysisDefinitionNodeModel = new Backbone.Model({ type: 'source' });
analysisDefinitionNodeModel.querySchemaModel = new Backbone.Model();
analysisDefinitionNodeModel.querySchemaModel.hasGeometryData = function () {
return true;
};
analysisDefinitionNodeModel.isSourceType = function () {
return true;
};
analysisDefinitionNodeModel.getColor = function () {
return '#fabada';
};
this.model.findAnalysisDefinitionNodeModel = function () {
return analysisDefinitionNodeModel;
};
spyOn(this.model, 'getAnalysisDefinitionNodeModel').and.returnValue(analysisDefinitionNodeModel);
this.widgetDefinitionsCollection = new Backbone.Collection();
this.analysisDefinitionNodesCollection = new Backbone.Collection();
this.layerDefinitionsCollection = createLayerDefinitionsCollection(this.model);
this.userActions = UserActions({
userModel: {},
analysisDefinitionsCollection: {},
analysisDefinitionNodesCollection: {},
layerDefinitionsCollection: {},
widgetDefinitionsCollection: {}
});
this.promise = $.Deferred();
spyOn(this.userActions, 'saveWidget').and.returnValue(this.promise);
this.view = new EditorWidgetsView({
modals: FactoryModals.createModalService(),
userActions: this.userActions,
userModel: {},
configModel: {},
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
widgetDefinitionsCollection: this.widgetDefinitionsCollection,
layerDefinitionsCollection: this.layerDefinitionsCollection,
stackLayoutModel: jasmine.createSpyObj('stackLayoutModel', ['nextStep'])
});
this.view.render();
});
describe('_initialize', function () {
it('should initialize a viewModel with a state', function () {
expect(this.view.viewModel).toBeDefined();
expect(this.view.viewModel.get('state')).toBeDefined();
});
describe('._initViewState', function () {
it('should initialze a viewState that indicates if there is geometry data', function () {
expect(this.view._viewState).toBeDefined();
expect(this.view._viewState.get('anyGeometryData')).toBeDefined();
});
it('should call _setViewState', function () {
spyOn(this.view, '_setViewState');
this.view._initViewState();
expect(this.view._setViewState).toHaveBeenCalled();
});
});
});
describe('._setViewState', function () {
it('should update _viewState and set if there is geometry data', function (done) {
this.view._viewState.set('anyGeometryData', false);
spyOn(this.layerDefinitionsCollection, 'isThereAnyGeometryData').and.returnValue(Promise.resolve(true));
this.view._setViewState();
setTimeout(function () {
expect(this.view._viewState.get('anyGeometryData')).toEqual(true);
done();
}.bind(this), 0);
});
});
describe('render', function () {
it('should render loading view if state is loading', function () {
this.view.viewModel.set({state: STATES.loading});
this.view.render();
expect(this.view.$('.FormPlaceholder-widget').length).toBe(4);
});
it('should not render no geometry view when _anyGeometryData is true', function () {
this.view.render();
expect(this.view.$el.text()).not.toContain('editor.widgets.no-geometry-data');
});
it('should render no geometry view if no data when no geometry data', function () {
this.view._viewState.set('anyGeometryData', false);
this.view.render();
expect(this.view.$el.text()).toContain('editor.widgets.no-geometry-data');
});
it('should render if widgets collection add one or more widgets', function () {
spyOn(this.view, 'render');
this.view._initBinds();
this.widgetDefinitionsCollection.trigger('successAdd');
expect(this.view.render).toHaveBeenCalled();
});
});
describe('sortable', function () {
beforeEach(function () {
this.widgetDefModel1 = new WidgetDefinitionModel({
id: 'widget-1',
type: 'formula',
title: 'formula example',
layer_id: 'l-100',
column: 'areas',
operation: 'avg',
order: 1
}, {
configModel: {},
mapId: 'm-123'
});
this.widgetDefModel2 = new WidgetDefinitionModel({
id: 'widget-2',
type: 'histogram',
title: 'histogram example',
layer_id: 'l-100',
column: 'population',
order: 0
}, {
configModel: {},
mapId: 'm-123'
});
this.widgetDefinitionsCollection.add(this.widgetDefModel1);
this.widgetDefinitionsCollection.add(this.widgetDefModel2);
});
it('should be initialized when view is rendered', function () {
spyOn(this.view, '_initSortable').and.callThrough();
this.view.render();
expect(this.view._initSortable).toHaveBeenCalled();
expect(this.view.$('.js-widgets').data('ui-sortable')).not.toBeUndefined();
});
it('should be show the widgets in the right order', function () {
this.view.render();
expect(this.view.$('.js-widgets li:nth-child(1) .js-title').text()).toBe('histogram example');
expect(this.view.$('.js-widgets li:nth-child(2) .js-title').text()).toBe('formula example');
});
it('should update the order of the models when sort has finished', function () {
this.view.render();
expect(this.widgetDefModel2.get('order')).toBe(0);
expect(this.widgetDefModel1.get('order')).toBe(1);
// Impossible to fake sortable behaviour so...
this.view.$('.js-widgetItem:eq(1)').insertBefore(this.view.$('.js-widgetItem:eq(0)'));
this.view._onSortableFinish();
// End of fake sortable
expect(this.widgetDefModel1.get('order')).toBe(0);
expect(this.widgetDefModel2.get('order')).toBe(1);
expect(this.userActions.saveWidget).toHaveBeenCalled();
});
});
describe('when adding a widget definition', function () {
var widgetDefModel;
beforeEach(function () {
widgetDefModel = new WidgetDefinitionModel({
id: 'widget-3',
type: 'formula',
title: 'AVG districts homes',
layer_id: 'l-100',
column: 'areas',
operation: 'avg'
}, {
configModel: {},
mapId: 'm-123'
});
this.widgetDefinitionsCollection.add(widgetDefModel);
});
});
it('should have no leaks', function () {
this.view.render();
expect(this.view).toHaveNoLeaks();
});
});