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,140 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var AnalysisDefinitionNodessCollection = require('builder/data/analysis-definition-nodes-collection');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
var AddAnalysisView = require('builder/components/modals/add-analysis/add-analysis-view');
var UserModel = require('builder/data/user-model');
var DataServicesApiCheck = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-info');
var Router = require('builder/routes/router');
describe('components/modals/add-analysis/add-analysis-view', function () {
beforeEach(function () {
this.modalModel = new Backbone.Model();
spyOn(this.modalModel, 'destroy');
spyOn(Router, 'navigate');
var configModel = new ConfigModel({
base_url: '/u/pepe',
user_name: 'foo',
sql_api_template: 'foo',
api_key: 'foo'
});
var userModel = new UserModel({}, {
configModel: configModel
});
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodessCollection(null, {
configModel: configModel,
userModel: userModel
});
this.a0 = this.analysisDefinitionNodesCollection.add({
id: 'a0',
type: 'source',
params: {
query: 'SELECT * from alice'
},
options: {
table_name: 'alice'
}
});
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
configModel: configModel,
userModel: userModel,
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
mapId: '123',
stateDefinitionModel: new Backbone.Model()
});
this.layerA = this.layerDefinitionsCollection.add({
id: 'layerA',
kind: 'carto',
options: {
source: 'a0',
table_name: 'alice'
}
});
DataServicesApiCheck.get(configModel)._state = 'fetched';
this.queryGeometryModel = this.a0.queryGeometryModel;
spyOn(this.queryGeometryModel, 'sync');
spyOn(this.queryGeometryModel, 'fetch').and.callThrough();
this.view = new AddAnalysisView({
configModel: configModel,
userModel: userModel,
modalModel: this.modalModel,
layerDefinitionModel: this.layerA
});
this.view.render();
});
it('should have no leaks', function () {
expect(this.view).toHaveNoLeaks();
});
it('should render the loading view', function () {
expect(this.view.$el.html()).toContain('loading');
});
describe('when geometry output type is fetched', function () {
beforeEach(function () {
this.queryGeometryModel.set('simple_geom', 'polygon');
this.queryGeometryModel.sync.calls.argsFor(0)[2].success({
fields: {},
rows: []
});
});
it('should render the content view', function () {
expect(this.view.$el.html()).not.toContain('loading');
expect(this.view.$el.html()).not.toContain('error');
});
it('should render the tabs', function () {
expect(this.view.$('.js-menu li').size()).toBe(4);
expect(this.view.$('.js-menu li:eq(0) button').html()).toContain('analysis-category.all');
expect(this.view.$('.js-menu li:eq(1) button').html()).toContain('analysis-category.create-clean');
expect(this.view.$('.js-menu li:eq(2) button').html()).toContain('analysis-category.analyze-predict');
expect(this.view.$('.js-menu li:eq(3) button').html()).toContain('analysis-category.data-transformation');
});
describe('when click add when there is no selection', function () {
it('should do nothing', function () {
this.view.$('.js-add').click();
expect(this.modalModel.destroy).not.toHaveBeenCalled();
});
});
describe('when an option is selected', function () {
beforeEach(function () {
expect(this.view.$('.js-add').hasClass('is-disabled')).toBe(true);
var $el = this.view.$('[title*=georeference]').first();
expect($el.length).toEqual(1, 'should only click one item (the custom georeference one)');
$el.click();
});
it('should enable add-button', function () {
expect(this.view.$('.js-add').hasClass('is-disabled')).toBe(false);
});
describe('when click add', function () {
beforeEach(function () {
this.view.$('.js-add').click();
});
it('should destroy the modal and pass the created node model', function () {
expect(this.modalModel.destroy).toHaveBeenCalled();
expect(this.modalModel.destroy.calls.argsFor(0)).toEqual([
jasmine.objectContaining({
id: 'a1',
source: 'a0',
type: jasmine.any(String)
})
]);
});
});
});
});
});
@@ -0,0 +1,87 @@
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var CamshaftReference = require('builder/data/camshaft-reference');
var AnalysisOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/analysis-option-model');
describe('builder/components/modals/add-analysis/analysis-option-models/analysis-option-model', function () {
beforeEach(function () {
this.model = new AnalysisOptionModel({
title: 'a Buffer',
desc: 'An example of how buffer would/should behave',
type_group: 'Specs',
dummy: false
}, {
nodeAttrs: {
type: 'buffer',
radius: 123
}
});
});
describe('.acceptsGeometryTypeAsInput', function () {
it('should return true for valid geometries', function () {
expect(this.model.acceptsGeometryTypeAsInput('point')).toBe(true);
expect(this.model.acceptsGeometryTypeAsInput('polygon')).toBe(true);
expect(this.model.acceptsGeometryTypeAsInput('line')).toBe(true);
var model = new AnalysisOptionModel(null, {nodeAttrs: {type: 'trade-area'}});
expect(model.acceptsGeometryTypeAsInput('point')).toBe(true);
expect(model.acceptsGeometryTypeAsInput('polygon')).toBe(false);
});
it('should skip camshaft geometry validation if our analysis is a dummy one', function () {
this.model.set('dummy', true);
spyOn(CamshaftReference, 'isValidInputGeometryForType');
this.model.acceptsGeometryTypeAsInput('line');
expect(CamshaftReference.isValidInputGeometryForType).not.toHaveBeenCalled();
this.model.set('dummy', false);
});
});
describe('.getValidInputGeometries', function () {
it('should return valid input geometries', function () {
expect(this.model.getValidInputGeometries()).toEqual(jasmine.any(Array));
expect(this.model.getValidInputGeometries()[0]).toEqual(jasmine.any(String));
});
});
describe('.getFormAttrs', function () {
beforeEach(function () {
this.layerDefModel = new LayerDefinitionModel({
id: 'layerA',
type: 'cartoDB',
letter: 'a',
source: 'a0'
}, {
configModel: {}
});
});
describe('when given a layer with own source', function () {
it('should return attrs to create a form model for analysis', function () {
expect(this.model.getFormAttrs(this.layerDefModel)).toEqual({
id: 'a1',
source: 'a0',
type: 'buffer',
radius: 123
});
});
});
describe('when given a layer which source belongs to another layer', function () {
beforeEach(function () {
this.layerDefModel.set('source', 'b0');
});
it('should return attrs to create a form model for analysis', function () {
var attrs = this.model.getFormAttrs(this.layerDefModel);
expect(attrs.id).toEqual('a1', 'id should be adapted for layer, not continue on source node');
expect(attrs).toEqual({
id: 'a1',
source: 'b0',
type: 'buffer',
radius: 123
});
});
});
});
});
@@ -0,0 +1,31 @@
var Backbone = require('backbone');
var AnalysisOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/analysis-option-model');
var SQLFunctionOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/deprecated-sql-function-option-model');
describe('deprecated-sql-function-option-model', function () {
var model;
beforeEach(function () {
spyOn(AnalysisOptionModel.prototype, 'getFormAttrs').and.returnValue({
source: 'y0' // Return always this `source` property. We need to check that it gets deleted.
});
model = new SQLFunctionOptionModel(null, {
nodeAttrs: {
key: 'value'
}
});
});
describe('getFormAttrs', function () {
it('should get form attributes, delete source and set primary_source with the layer definition attributes', function () {
var fakeLayerDefModel = new Backbone.Model({
source: 'z0'
});
var formAttrs = model.getFormAttrs(fakeLayerDefModel);
expect(AnalysisOptionModel.prototype.getFormAttrs).toHaveBeenCalledWith(fakeLayerDefModel);
expect(formAttrs).toEqual({ primary_source: 'z0' });
});
});
});
@@ -0,0 +1,60 @@
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var GeneratedAnalysisOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/generated-analysis-option-model');
describe('builder/components/modals/add-analysis/analysis-option-models/generated-analysis-option-model', function () {
beforeEach(function () {
this.layerA = new LayerDefinitionModel({
id: 'layerA',
type: 'cartoDB',
letter: 'a',
source: 'a0'
}, {
configModel: {}
});
});
describe('when created a type with a single source', function () {
beforeEach(function () {
this.model = new GeneratedAnalysisOptionModel({
title: 'filter-category',
desc: 'An example of a generated option model'
}, {
nodeAttrs: {
type: 'filter-category'
}
});
});
describe('.getFormAttrs', function () {
it('should return attrs to create a form model for analysis', function () {
expect(this.model.getFormAttrs(this.layerA)).toEqual({
id: 'a1',
source: 'a0',
type: 'filter-category'
});
});
});
});
describe('when created a type with multiple input sources', function () {
beforeEach(function () {
this.model = new GeneratedAnalysisOptionModel({
title: 'aggregate-intersection',
desc: 'An example of a generated option model with multiple sources'
}, {
nodeAttrs: {
type: 'aggregate-intersection'
}
});
});
describe('.getFormAttrs', function () {
it('should return attrs but w/o any source set', function () {
expect(this.model.getFormAttrs(this.layerA)).toEqual({
id: 'a1',
type: 'aggregate-intersection'
});
});
});
});
});
@@ -0,0 +1,35 @@
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var MergeAnalysisOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/merge-analysis-option-model');
describe('builder/components/modals/add-analysis/analysis-option-models/merge-analysis-option-model', function () {
beforeEach(function () {
this.layerA = new LayerDefinitionModel({
id: 'layerA',
type: 'cartoDB',
letter: 'a',
source: 'a0'
}, {
configModel: {}
});
this.model = new MergeAnalysisOptionModel({
title: 'merge',
desc: 'An example of a generated option model'
}, {
nodeAttrs: {
type: 'merge'
}
});
});
describe('.getFormAttrs', function () {
it('should return attrs to create a form model for analysis', function () {
expect(this.model.getFormAttrs(this.layerA)).toEqual({
id: 'a1',
type: 'merge',
left_source: 'a0',
primary_source_name: 'left_source'
});
});
});
});
@@ -0,0 +1,101 @@
var AnalysisOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/analysis-option-model');
var AnalysisOptionView = require('builder/components/modals/add-analysis/analysis-option-view');
describe('components/modals/add-analysis/analysis-option-view', function () {
beforeEach(function () {
this.model = new AnalysisOptionModel({
title: 'Buffer',
desc: 'describes the buffer type',
type_group: 'area of influence',
link: 'https://carto.com'
}, {
nodeAttrs: {
type: 'buffer'
}
});
});
describe('when given a simple geometry type', function () {
beforeEach(function () {
this.view = new AnalysisOptionView({
model: this.model,
simpleGeometryTypeInput: 'point'
});
this.view.render();
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
it('should render the info', function () {
expect(this.view.$el.html()).toContain('Buffer');
expect(this.view.$el.html()).toContain('describes the buffer');
});
it('should not select the item if the link is clicked', function () {
expect(this.view.$('.js-more').length).toBe(1);
this.view.$('.js-more').click();
expect(this.model.get('selected')).toBeFalsy();
});
it('should render the animation', function () {
expect(this.view.$('.js-animation').length).toBe(1);
});
it('should launch the animation on hover', function () {
this.view._acceptsInputGeometry = function () { return true; };
this.view.$el.trigger('mouseenter');
expect(this.view.$('.js-animation').hasClass('has-autoplay')).toBeTruthy();
this.view.$el.trigger('mouseleave');
expect(this.view.$('.js-animation').hasClass('has-autoplay')).toBeFalsy();
});
describe('when selected', function () {
beforeEach(function () {
this.model.set('selected', true);
});
it('should highlight the item', function () {
expect(this.view.el.className).toContain('is-selected');
});
});
describe('when geometry type does not match ', function () {
beforeEach(function () {
spyOn(this.model, 'acceptsGeometryTypeAsInput').and.returnValue(false);
this.view.render();
});
it('should disable the view', function () {
expect(this.view.el.className).toContain('is-disabled');
});
it('should show alternative desc', function () {
expect(this.view.$el.html()).toContain('disabled-option-desc');
expect(this.view.$el.html()).not.toContain('describes the buffer');
});
it('should not be able to select it', function () {
this.view.$el.click();
expect(this.view.el.className).not.toContain('selected');
});
});
});
describe('when not given any geometry type', function () {
beforeEach(function () {
this.view = new AnalysisOptionView({
model: this.model
});
this.view.render();
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
});
@@ -0,0 +1,41 @@
var AnalysisOptionsCollection = require('builder/components/modals/add-analysis/analysis-options-collection');
describe('components/modals/add-analysis/analysis-options-collection', function () {
beforeEach(function () {
this.collection = new AnalysisOptionsCollection();
});
describe('when reset with some options', function () {
beforeEach(function () {
this.collection.reset([
{
title: 'A Buffer',
nodeAttrs: {
type: 'buffer'
}
}, {
title: 'A Trade-Area',
nodeAttrs: {
type: 'trade-area'
}
}
]);
});
it('should create a model for given data', function () {
expect(this.collection.length).toEqual(2);
expect(this.collection.first().get('title')).toEqual(jasmine.any(String));
expect(this.collection.first().getFormAttrs).toEqual(jasmine.any(Function));
});
it('should only allow one selection at a time', function () {
expect(this.collection.pluck('selected')).toEqual([false, false]);
this.collection.first().set('selected', true);
expect(this.collection.pluck('selected')).toEqual([true, false]);
this.collection.last().set('selected', true);
expect(this.collection.pluck('selected')).toEqual([false, true]);
});
});
});
@@ -0,0 +1,92 @@
var Backbone = require('backbone');
var _ = require('underscore');
var camshaftReferenceAnalyses = require('camshaft-reference').getVersion('latest').analyses;
var analysisOptions = require('builder/components/modals/add-analysis/analysis-options');
var DataServicesApiCheck = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-info');
describe('builder/components/modals/add-analysis/analysis-options', function () {
var configModel = new Backbone.Model({
user_name: 'foo',
sql_api_template: 'foo',
api_key: 'foo'
});
configModel.dataServiceEnabled = function () { return true; };
var userModel = new Backbone.Model();
userModel.featureEnabled = function () { return true; };
var queryGeometryModel = new Backbone.Model();
queryGeometryModel.isPolygon = function () { return true; };
DataServicesApiCheck.get(configModel)._state = 'fetched';
it('should not add generated option if feature flag is disabled', function () {
userModel.featureEnabled = function () { return false; };
configModel.dataServiceEnabled = function () { return false; };
var defaultAnalysisOptions = analysisOptions({
userModel: userModel,
configModel: configModel,
queryGeometryModel: queryGeometryModel
});
expect(defaultAnalysisOptions['generated']).toBeUndefined('should not have any generated options');
});
describe('each category (including generated)', function () {
var options = analysisOptions({
userModel: userModel,
configModel: configModel,
queryGeometryModel: queryGeometryModel
});
_.each(options, function (item, category) {
it('should have a key', function () {
expect(category).toEqual(jasmine.any(String));
});
describe('category: ' + category, function () {
describe('should have a definition', function () {
var def = options[category];
beforeEach(function () {
expect(def).toEqual(jasmine.any(Object));
});
it('should have a title', function () {
expect(def.title).toEqual(jasmine.any(String));
});
it('should have analyses', function () {
expect(def.analyses).toEqual(jasmine.any(Array));
expect(def.analyses.length).toBeGreaterThan(0);
});
describe('each analysis', function () {
def.analyses.forEach(function (d) {
describe('analysis: ' + d.title, function () {
it('should have a title', function () {
expect(d.title).toEqual(jasmine.any(String));
});
it('should have a description', function () {
expect(d.desc).toEqual(jasmine.any(String));
});
it('should have attrs to create a node from', function () {
expect(d.nodeAttrs).toEqual(jasmine.any(Object));
});
it('should have at least a type', function () {
expect(d.nodeAttrs.type).toEqual(jasmine.any(String));
if (d.dummy !== true) {
expect(camshaftReferenceAnalyses[d.nodeAttrs.type]).toBeDefined();
}
});
});
});
});
});
});
});
});
});
@@ -0,0 +1,141 @@
var Backbone = require('backbone');
var AnalysisViewPane = require('builder/components/modals/add-analysis/analysis-view-pane');
var AnalysisOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/analysis-option-model');
var Router = require('builder/routes/router');
describe('components/modals/add-analysis/analysis-view-pane', function () {
var view, analysisOptionsCollection;
var createViewFn = function (options) {
analysisOptionsCollection = new Backbone.Collection([
new AnalysisOptionModel({
category: 'create_clean',
desc: 'Use street addresses, city names, or other location text to generate point geometries.',
selected: false,
title: 'Georeference',
type: 'georeference-long-lat',
type_group: ''
}, {
nodeAttrs: {
type: 'georeference-long-lat'
}
}), new AnalysisOptionModel({
category: 'analyze_predict',
desc: 'Moran description',
selected: false,
title: 'Moran',
type: 'moran',
type_group: ''
}, {
nodeAttrs: {
type: 'moran'
}
}), new AnalysisOptionModel({
category: 'data_transformation',
desc: 'Centroid description',
selected: false,
title: 'Centroid',
type: 'centroid',
type_group: ''
}, {
nodeAttrs: {
type: 'centroid'
}
})
]);
var view = new AnalysisViewPane({
stackLayoutModel: new Backbone.Model(),
modalModel: new Backbone.Model(),
queryGeometryModel: new Backbone.Model(),
analysisOptions: {
create_clean: {
title: 'Create and clean',
analyses: [{
desc: 'Use street addresses, city names, or other location text to generate point geometries.',
nodeAttrs: {
type: 'georeference-long-lat'
},
title: 'Georeference'
}]
},
analyze_predict: {
title: 'Analyze and predict',
analyses: [{
desc: 'Moran desc',
nodeAttrs: {
type: 'moran'
},
title: 'Moran'
}]
},
data_transformation: {
title: 'Data Transformation',
analyses: [{
desc: 'Centroid desc',
nodeAttrs: {
type: 'centroid'
},
title: 'Centroid'
}]
}
},
analysisOptionsCollection: analysisOptionsCollection,
layerDefinitionModel: new Backbone.Model({
id: 'some-layer-id',
letter: 'a',
source: 'a0'
}),
collection: new Backbone.Collection([
{ selected: true,
id: 'a3',
title: 'Intersection',
category: 'analyze-predict',
type: 'intersection',
genericType: 'intersection',
analysisParams: {}
}
])
});
return view;
};
beforeEach(function () {
spyOn(Router, 'navigate');
view = createViewFn();
view.render();
});
describe('.render', function () {
it('should render properly', function () {
expect(view.$el.html()).toContain('analysis-category.create-clean');
expect(view.$el.html()).toContain('analysis-category.analyze-predict');
expect(view.$el.html()).toContain('analysis-category.data-transformation');
expect(view.$('.js-add').hasClass('is-disabled')).toBeTruthy();
});
it('should not have any leaks', function () {
expect(view).toHaveNoLeaks();
});
});
it('should go to tab item', function () {
analysisOptionsCollection.at(0).set('selected', true);
view.goToTabItem('create_clean');
expect(view.$('.js-add').hasClass('is-disabled')).toBeFalsy();
});
describe('._onAddAnalysis', function () {
it('should call goToAnalysisNode', function () {
spyOn(Router, 'goToAnalysisNode');
analysisOptionsCollection.at(0).set('selected', true);
view._onAddAnalysis();
expect(Router.goToAnalysisNode).toHaveBeenCalledWith('some-layer-id', 'a1');
});
});
});
@@ -0,0 +1,217 @@
var AddBasemapModel = require('builder/components/modals/add-basemap/add-basemap-model');
var BasemapsCollection = require('builder/editor/layers/basemap-content-views/basemaps-collection');
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
describe('components/modals/add-basemap/add-basemap-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var userModel = new UserModel({}, {
configModel: configModel
});
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
configModel: configModel,
userModel: userModel
});
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
configModel: configModel,
userModel: userModel,
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
mapId: 'm-123',
stateDefinitionModel: {}
});
this.layerDefinitionsCollection.add({
id: 'l-1',
kind: 'carto',
options: {
table_name: 'foo',
className: 'positron_rainbow',
category: 'CARTO'
}
});
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: 'https://a.example.com/{z}/{x}/{y}.png',
category: 'Custom',
className: 'httpsaexamplecomzxypng'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
this.model = new AddBasemapModel({}, {
layerDefinitionsCollection: this.layerDefinitionsCollection,
basemapsCollection: new BasemapsCollection([{
id: 'basemap-id-1',
urlTemplate: 'https://a.example.com/{z}/{x}/{y}.png',
val: 'httpsaexamplecomzxypng'
}]),
customBaselayersCollection: this.customBaselayersCollection
});
});
it('should start with tabs for current view', function () {
expect(this.model.get('contentPane')).toEqual('tabs');
});
it('should start on XYZ tab', function () {
expect(this.model.get('currentTab')).toEqual('xyz');
});
describe('.activeTabModel', function () {
it('should return the model for current tab', function () {
expect(this.model.activeTabModel()).toEqual(jasmine.any(Object));
});
});
describe('.canSaveBasemap', function () {
it('should return true if there is a layer set on current tab model and is not processing something', function () {
expect(this.model.canSaveBasemap()).toBeFalsy();
this.model.activeTabModel().set('layer', {});
expect(this.model.canSaveBasemap()).toBeTruthy();
this.model.set('contentPane', 'not tabs');
expect(this.model.canSaveBasemap()).toBeFalsy();
});
});
describe('.saveBasemap', function () {
beforeEach(function () {
var url = 'https://b.example.com/{z}/{x}/{y}.png';
this.layer = new CustomBaselayerModel({
id: 'basemap-id-2',
urlTemplate: url,
attribution: null,
maxZoom: 21,
minZoom: 0,
name: '',
tms: false,
category: 'Custom',
type: 'Tiled'
});
this.layer.set('className', this.layer._generateClassName(url));
spyOn(this.layer, 'save');
this.model.activeTabModel().set('layer', this.layer);
});
describe('when layer has already been added', function () {
beforeEach(function () {
this.model._basemapsCollection.add({
id: 'basemap-id-2',
urlTemplate: 'https://b.example.com/{z}/{x}/{y}.png',
val: 'httpsbexamplecomzxypng'
});
this.customBaselayersCollection.add(this.layer);
spyOn(this.model.activeTabModel(), 'hasAlreadyAddedLayer').and.returnValue(true);
spyOn(this.model._layerDefinitionsCollection, 'setBaseLayer');
spyOn(this.model._basemapsCollection, 'updateSelected');
this.model.saveBasemap();
});
it('should call hasAlreadyAddedLayer with baseLayers', function () {
expect(this.model.activeTabModel().hasAlreadyAddedLayer).toHaveBeenCalledWith(this.customBaselayersCollection);
});
it('should change baseLayer', function () {
expect(this.model._layerDefinitionsCollection.setBaseLayer).toHaveBeenCalledWith({
id: 'basemap-id-2',
className: 'httpsbexamplecomzxypng',
urlTemplate: 'https://b.example.com/{z}/{x}/{y}.png',
attribution: null,
maxZoom: 21,
minZoom: 0,
name: '',
tms: false,
category: 'Custom',
type: 'Tiled'
});
});
it('should update selected in basemapsCollection', function () {
expect(this.model._basemapsCollection.updateSelected).toHaveBeenCalledWith('httpsbexamplecomzxypng');
});
});
describe('when layer is new', function () {
beforeEach(function () {
spyOn(this.model._layerDefinitionsCollection, 'setBaseLayer');
spyOn(this.model._basemapsCollection, 'updateSelected');
this.model.saveBasemap();
});
it('should add layer to baselayers', function () {
expect(this.customBaselayersCollection.contains(this.layer)).toBeTruthy();
});
it('should call save on layer', function () {
expect(this.layer.save).toHaveBeenCalled();
});
describe('when save succeeds', function () {
beforeEach(function () {
this.layer.save.calls.argsFor(0)[1].success({}, {
options: {
urlTemplate: 'https://b.example.com/{z}/{x}/{y}.png',
attribution: null,
maxZoom: 21,
minZoom: 0,
name: '',
tms: false,
category: 'Custom',
type: 'Tiled',
className: 'httpsbexamplecomzxypng'
},
kind: 'tiled',
infowindow: null,
tooltip: null,
id: 'basemap-id-2',
order: 2
});
});
it('should change baseLayer', function () {
expect(this.model._layerDefinitionsCollection.setBaseLayer).toHaveBeenCalledWith({
id: 'basemap-id-2',
className: 'httpsbexamplecomzxypng',
urlTemplate: 'https://b.example.com/{z}/{x}/{y}.png',
attribution: null,
maxZoom: 21,
minZoom: 0,
name: '',
tms: false,
category: 'Custom',
type: 'Tiled'
});
});
});
describe('when save fails', function () {
beforeEach(function () {
this.layer.save.calls.argsFor(0)[1].error();
});
it('should remove layer from baselayers', function () {
expect(this.customBaselayersCollection.contains(this.layer)).toBeFalsy();
});
it('should set current view to addBasemapFailed', function () {
expect(this.model.get('contentPane')).toEqual('addBasemapFailed');
});
});
});
});
});
@@ -0,0 +1,140 @@
var AddBasemapView = require('builder/components/modals/add-basemap/add-basemap-view');
var AddBasemapModel = require('builder/components/modals/add-basemap/add-basemap-model');
var BasemapsCollection = require('builder/editor/layers/basemap-content-views/basemaps-collection');
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var ModalViewModel = require('builder/components/modals/modal-view-model');
describe('components/modals/add-basemap/add-basemap-view', function () {
beforeEach(function () {
this.createContentViewResult = {};
this.createContentView = jasmine.createSpy('createContentView').and.returnValue(this.createContentViewResult);
spyOn(ModalViewModel.prototype, 'destroy').and.callThrough();
this.modalModel = new ModalViewModel({
createContentView: this.createContentView
});
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var userModel = new UserModel({}, {
configModel: configModel
});
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
configModel: configModel,
userModel: userModel
});
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
configModel: configModel,
userModel: userModel,
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
mapId: 'm-123',
stateDefinitionModel: {}
});
this.layerDefinitionsCollection.add({
id: 'l-1',
kind: 'carto',
options: {
table_name: 'foo',
className: 'positron_rainbow',
category: 'CARTO'
}
});
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: '',
category: 'Custom'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
this.model = new AddBasemapModel({}, {
layerDefinitionsCollection: this.layerDefinitionsCollection,
basemapsCollection: new BasemapsCollection(),
customBaselayersCollection: this.customBaselayersCollection
});
this.view = new AddBasemapView({
modalModel: this.modalModel,
layerDefinitionsCollection: this.layerDefinitionsCollection,
createModel: this.model
});
this.view.render();
});
it('should render the tabs', function () {
expect(this.innerHTML()).toContain('XYZ');
});
it('should start on XYZ view', function () {
expect(this.innerHTML()).toContain('components.modals.add-basemap.xyz.insert');
});
it('should highlight the selected tab', function () {
expect(this.view.$('.Modal-navigation .is-selected button').data('name')).toBe('xyz');
expect(this.view.$('.Modal-navigation .is-selected button').data('name')).not.toBe('mapbox');
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
describe('when clicking on a tab', function () {
beforeEach(function () {
this.view.$('button[data-name="mapbox"]').click();
});
it('should change tab', function () {
expect(this.innerHTML()).toContain('components.modals.add-basemap.mapbox.insert');
});
it('should highlight new tab', function () {
expect(this.view.$('.Modal-navigation .is-selected button').data('name')).toBe('mapbox');
expect(this.view.$('.Modal-navigation .is-selected button').data('name')).not.toBe('xyz');
});
});
describe('when click OK', function () {
beforeEach(function () {
spyOn(this.view._createModel, 'canSaveBasemap').and.returnValue(true);
spyOn(this.view._createModel, 'saveBasemap');
this.view.$('.js-ok').click();
});
it('should save new basemap', function () {
expect(this.view._createModel.saveBasemap).toHaveBeenCalled();
});
describe('when save succeeds', function () {
it('should close view', function () {
this.view._createModel.trigger('saveBasemapDone');
expect(ModalViewModel.prototype.destroy).toHaveBeenCalled();
});
});
describe('when save fails', function () {
beforeEach(function () {
this.view._createModel.set('contentPane', 'addBasemapFailed');
});
it('should show an error explanation', function () {
expect(this.innerHTML()).toContain('error');
});
});
});
afterEach(function () {
this.view.clean();
});
});
@@ -0,0 +1,36 @@
var ConfigModel = require('builder/data/config-model');
var MapboxModel = require('builder/components/modals/add-basemap/mapbox/mapbox-model.js');
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
describe('editor/components/modals/add-basemap/mapbox/mapbox-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: 'https://a.tiles.mapbox.com/v4/map.id/{z}/{x}/{y}.png?access_token=',
category: 'Mapbox',
className: 'httpsatilesmapboxcomv4mapidzxypngaccess_token'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
this.model = new MapboxModel();
});
describe('.hasAlreadyAddedLayer', function () {
it('should return true if layer has already been added', function () {
this.model.set('layer', new CustomBaselayerModel({}));
expect(this.model.hasAlreadyAddedLayer(this.customBaselayersCollection)).toBeFalsy();
this.model.set('layer', this.customBaselayersCollection.at(0));
expect(this.model.hasAlreadyAddedLayer(this.customBaselayersCollection)).toBeTruthy();
});
});
});
@@ -0,0 +1,189 @@
var $ = require('jquery');
var MapboxToTileLayerFactory = require('builder/components/modals/add-basemap/mapbox/mapbox-to-tile-layer-factory');
describe('editor/components/modals/add-basemap/mapbox/mapbox-to-tile-layer-factory', function () {
beforeEach(function () {
this.factory = new MapboxToTileLayerFactory({
url: 'https://a.tiles.mapbox.com/v4/username.123abc45d/'
});
});
describe('._fixHTTPS', function () {
it('should fix mapbox https url', function () {
var url = MapboxToTileLayerFactory.prototype._fixHTTPS('http://a.tiles.mapbox.com/v4/examples.map-4l7djmvo.json', {
protocol: 'https:'
});
expect(url).toEqual('https://dnv9my2eseobd.cloudfront.net/v4/examples.map-4l7djmvo.json');
url = MapboxToTileLayerFactory.prototype._fixHTTPS('http://a.tiles.mapbox.com/v4/examples.map-4l7djmvo.json', {
protocol: 'http:'
});
expect(url).toEqual('http://a.tiles.mapbox.com/v4/examples.map-4l7djmvo.json');
});
});
describe('.createTileLayer', function () {
beforeEach(function () {
this.successSpy = jasmine.createSpy('success');
this.errorSpy = jasmine.createSpy('error');
this.callbacks = {
success: this.successSpy,
error: this.errorSpy
};
// for sure jasmine has a function for this
spyOn($, 'ajax');
});
describe('when provided an edit URL', function () {
beforeEach(function () {
this.username = 'cartodb';
this.mapID = 'map-eeoepub0';
this.mapboxID = this.username + '.' + this.mapID;
this.factory.set({
url: 'https://tiles.mapbox.com/' + this.username + '/edit/' + this.mapID + '#3/0.09/0.00'
});
this.factory.createTileLayer(this.callbacks);
});
describe('when valid URL', function () {
beforeEach(function () {
$.ajax.calls.argsFor(0)[0].success({
attribution: 'attribution str',
minzoom: 2,
maxzoom: 4
});
});
it('should call the success callback', function () {
expect(this.successSpy).toHaveBeenCalled();
});
it('should have an expected urlTemplate value on the returned layer', function () {
expect(this.successSpy).toHaveBeenCalledWith(jasmine.any(Object));
var url = 'https://a.tiles.mapbox.com/v4/' + this.mapboxID + '/{z}/{x}/{y}.png';
expect(this.successSpy.calls.argsFor(0)[0].get('urlTemplate')).toEqual(url);
});
it('should also set expected metadata', function () {
var tileLayer = this.successSpy.calls.argsFor(0)[0];
expect(tileLayer.get('attribution')).toEqual('attribution str');
expect(tileLayer.get('minZoom')).toEqual(2);
expect(tileLayer.get('maxZoom')).toEqual(4);
});
});
describe('when fails to validate URL', function () {
beforeEach(function () {
$.ajax.calls.argsFor(0)[0].error();
});
it('should call error callback with error message', function () {
expect(this.errorSpy).toHaveBeenCalled();
expect(this.errorSpy).toHaveBeenCalledWith(jasmine.any(String));
});
});
});
describe('when provided an embed URL', function () {
beforeEach(function () {
this.mapboxID = 'cartodb.map-eeoepub0';
this.factory.set({
url: 'http://a.tiles.mapbox.com/v4/' + this.mapboxID + '/page.html'
});
this.factory.createTileLayer(this.callbacks);
});
describe('when valid URL', function () {
beforeEach(function () {
$.ajax.calls.argsFor(0)[0].success({
attribution: 'attribution str',
minzoom: 2,
maxzoom: 4
});
});
it('should call the success callback', function () {
expect(this.successSpy).toHaveBeenCalled();
});
it('should have an expected urlTemplate value on the returned layer', function () {
expect(this.successSpy).toHaveBeenCalledWith(jasmine.any(Object));
var url = 'https://a.tiles.mapbox.com/v4/' + this.mapboxID + '/{z}/{x}/{y}.png';
expect(this.successSpy.calls.argsFor(0)[0].get('urlTemplate')).toEqual(url);
});
it('should also set expected metadata', function () {
var tileLayer = this.successSpy.calls.argsFor(0)[0];
expect(tileLayer.get('attribution')).toEqual('attribution str');
expect(tileLayer.get('minZoom')).toEqual(2);
expect(tileLayer.get('maxZoom')).toEqual(4);
});
});
describe('when fails to validate URL', function () {
beforeEach(function () {
$.ajax.calls.argsFor(0)[0].error();
});
it('should call error callback with error message', function () {
expect(this.errorSpy).toHaveBeenCalled();
expect(this.errorSpy).toHaveBeenCalledWith(jasmine.any(String));
});
});
});
describe('when provided a XYZ URL', function () {
beforeEach(function () {
this.mapboxID = 'cartodb.map-eeoepub0';
var self = this;
this.img = {};
spyOn(window, 'Image').and.callFake(function () {
return self.img;
});
this.factory.set({
url: 'https://a.tiles.mapbox.com/v4/' + this.mapboxID + '/{z}/{y}/{x}.png'
});
this.factory.createTileLayer(this.callbacks);
});
describe('when valid URL and tiles', function () {
beforeEach(function () {
this.img.onload();
});
it('should call the success callback', function () {
expect(this.successSpy).toHaveBeenCalled();
});
it('should have an expected urlTemplate value on the returned layer', function () {
expect(this.successSpy).toHaveBeenCalledWith(jasmine.any(Object));
var url = 'https://a.tiles.mapbox.com/v4/' + this.mapboxID + '/{z}/{y}/{x}.png';
expect(this.successSpy.calls.argsFor(0)[0].get('urlTemplate')).toEqual(url);
});
it('should also set expected metadata', function () {
var tileLayer = this.successSpy.calls.argsFor(0)[0];
expect(tileLayer.get('attribution')).toBeNull();
expect(tileLayer.get('minZoom')).toEqual(0);
expect(tileLayer.get('maxZoom')).toEqual(21);
});
});
describe('when fails to validate URL', function () {
beforeEach(function () {
this.img.onerror();
});
it('should call error callback with error message', function () {
expect(this.errorSpy).toHaveBeenCalled();
expect(this.errorSpy).toHaveBeenCalledWith(jasmine.any(String));
});
});
});
});
});
@@ -0,0 +1,78 @@
var $ = require('jquery');
var MapboxView = require('builder/components/modals/add-basemap/mapbox/mapbox-view');
var MapboxModel = require('builder/components/modals/add-basemap/mapbox/mapbox-model');
var MapboxToTileLayerFactory = require('builder/components/modals/add-basemap/mapbox/mapbox-to-tile-layer-factory');
describe('editor/components/modals/add-basemap/mapbox/mapbox-view', function () {
beforeEach(function () {
this.model = new MapboxModel();
var submitButton = $('<button class="is-disabled">Submit</button>');
var modalFooter = $('<div></div>');
this.view = new MapboxView({
model: this.model,
submitButton: submitButton,
modalFooter: modalFooter
});
this.view.render();
});
it('should render the inputs', function () {
expect(this.view.$('input').length).toEqual(1);
});
describe('when click save', function () {
beforeEach(function () {
spyOn(MapboxToTileLayerFactory.prototype, 'createTileLayer');
spyOn(this.model, 'validateInputs').and.callThrough();
this.view.$('.js-url').val('mapbox/URL');
this.view._submitButton.click();
});
it('should call validateInputs on model with current values', function () {
expect(this.model.validateInputs).toHaveBeenCalled();
expect(this.model.validateInputs).toHaveBeenCalledWith('mapbox/URL');
});
it('should show the loading message', function () {
expect(this.innerHTML()).toContain('components.modals.add-basemap.validating');
expect(this.view.$('input').length).toEqual(0);
});
describe('when layer is created', function () {
beforeEach(function () {
this.saveBasemapSpy = jasmine.createSpy('saveBasemap');
this.model.bind('saveBasemap', this.saveBasemapSpy);
this.tileLayer = jasmine.createSpy('CustomBaselayerModel');
MapboxToTileLayerFactory.prototype.createTileLayer.calls.argsFor(0)[0].success(this.tileLayer);
});
it('should set the layer on the model', function () {
expect(this.model.get('layer')).toBe(this.tileLayer);
});
it('should trigger saveBasemap event', function () {
expect(this.saveBasemapSpy).toHaveBeenCalled();
});
});
describe('when layer fails to be created', function () {
beforeEach(function () {
MapboxToTileLayerFactory.prototype.createTileLayer.calls.argsFor(0)[0].error('something failed');
});
it('should show the start view again', function () {
expect(this.view.$('input').length).toEqual(1);
});
});
});
it('should have no leaks', function () {
expect(this.view).toHaveNoLeaks();
});
afterEach(function () {
this.view.clean();
});
});
@@ -0,0 +1,69 @@
var ConfigModel = require('builder/data/config-model');
var NASAModel = require('builder/components/modals/add-basemap/nasa/nasa-model');
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
describe('components/modals/add-basemap/nasa/nasa-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: 'http://map1.vis.earthdata.nasa.gov/wmts-webmerc/MODIS_Terra_CorrectedReflectance_TrueColor/default/2016-09-20/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpeg',
category: 'NASA',
className: 'httpmap1visearthdatanasagovwmtswebmercmodis_terra_correctedreflectance_truecolordefault20160920googlemapscompatible_level9zyxjpeg'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
this.model = new NASAModel(null, {
customBaselayersCollection: this.customBaselayersCollection
});
});
describe('when model changes', function () {
beforeEach(function () {
this.model.set({
date: '2015-05-25',
layerType: 'day'
});
});
it('should set layer', function () {
expect(this.model.get('layer')).toEqual(jasmine.any(Object));
});
it('should set layer with expected params', function () {
var layer = this.model.get('layer');
expect(layer.get('urlTemplate')).toMatch('/2015-05-25/.*\{x\}');
expect(layer.get('attribution')).toEqual(jasmine.any(String));
expect(layer.get('maxZoom')).toEqual(jasmine.any(Number));
expect(layer.get('minZoom')).toEqual(jasmine.any(Number));
expect(layer.get('name')).toMatch('.* 2015-05-25');
});
});
describe('.hasAlreadyAddedLayer', function () {
beforeEach(function () {
this.customBaselayerModel = new CustomBaselayerModel({
id: 'basemap-id-2',
urlTemplate: 'http://map1.vis.earthdata.nasa.gov/wmts-webmerc/VIIRS_CityLights_2012/default/2015-05-25/GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpeg',
className: 'httpmap1visearthdatanasagovwmtswebmercviirscitylights2012default20150525googlemapscompatiblelevel8zyxjpeg',
category: 'NASA'
});
});
it('should return true if layer has already been added', function () {
this.model.set('layer', this.customBaselayerModel);
expect(this.model.hasAlreadyAddedLayer(this.customBaselayersCollection)).toBe(false);
this.model.set('layer', this.customBaselayersCollection.at(0));
expect(this.model.hasAlreadyAddedLayer(this.customBaselayersCollection)).toBe(true);
});
});
});
@@ -0,0 +1,77 @@
var $ = require('jquery');
var NASAView = require('builder/components/modals/add-basemap/nasa/nasa-view');
var NASAModel = require('builder/components/modals/add-basemap/nasa/nasa-model');
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
var ConfigModel = require('builder/data/config-model');
describe('components/modals/add-basemap/nasa/nasa-view', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: 'http://map1.vis.earthdata.nasa.gov/wmts-webmerc/MODIS_Terra_CorrectedReflectance_TrueColor/default/2016-09-20/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpeg',
category: 'NASA',
className: 'httpmap1visearthdatanasagovwmtswebmercmodis_terra_correctedreflectance_truecolordefault20160920googlemapscompatible_level9zyxjpeg'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
this.model = new NASAModel(null, {
customBaselayersCollection: this.customBaselayersCollection
});
var submitButton = $('<button class="is-disabled">Submit</button>');
this.view = new NASAView({
model: this.model,
submitButton: submitButton
});
this.view.render();
});
it('should render day option as pre-selected', function () {
expect(this.view.$('.js-day .RadioButton-input').hasClass('is-checked')).toBeTruthy();
});
it('should show date picker', function () {
expect(this.view.$('.DatePicker').length).toBe(1);
});
it('should render date of yesterday as start date', function () {
expect(this.innerHTML()).toMatch(/\d{4}-\d{2}-\d{2}/);
});
describe('when changing from day to night', function () {
beforeEach(function () {
this.view.$('.js-night').click();
});
it('should disable date picker', function () {
expect(this.view.$('.DatePicker .DatePicker-dates').hasClass('is-disabled')).toBeTruthy();
});
describe('when changing back to day', function () {
beforeEach(function () {
this.view.$('.js-day').click();
});
it('should enable date picker again', function () {
expect(this.view.$('.DatePicker .DatePicker-dates').hasClass('is-disabled')).toBeFalsy();
});
});
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
afterEach(function () {
this.view.clean();
});
});
@@ -0,0 +1,30 @@
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var TabsView = require('builder/components/modals/add-basemap/tabs-view');
describe('basemap-tabs-view', function () {
beforeEach(function () {
this.view = new TabsView({
model: new Backbone.Model({
tabs: new Backbone.Collection([])
}),
submitButton: {},
modalFooter: {}
});
this.view.model.activeTabModel = function () {
return {
createView: function () {
return new CoreView();
}
};
};
});
describe('.render', function () {
it('should have the proper class name', function () {
this.view.render();
expect(this.view.$el.hasClass('Modal-outer')).toBe(true);
});
});
});
@@ -0,0 +1,66 @@
var TileJSONLayerModel = require('builder/components/modals/add-basemap/tilejson/tilejson-layer-model');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
describe('components/modals/add-basemap/tilejson/tilejson-layer-model', function () {
beforeEach(function () {
this.model = new TileJSONLayerModel();
this.model.setUrl('/tile.json');
this.attrsExFromToTileJSONSpec = {
tilejson: '2.1.0',
name: 'compositing',
description: 'A simple, light grey world.',
version: '1.0.0',
attribution: '<a href="http://openstreetmap.org">OSM contributors</a>',
template: '{{#__teaser__}}{{NAME}}{{/__teaser__}}',
legend: 'Dangerous zones are red, safe zones are green',
scheme: 'xyz',
tiles: [
'http://localhost:8888/admin/1.0.0/world-light,broadband/{z}/{x}/{y}.png'
],
grids: [
'http://localhost:8888/admin/1.0.0/broadband/{z}/{x}/{y}.grid.json'
],
data: [
'http://localhost:8888/admin/data.geojson'
],
minzoom: 0,
maxzoom: 11,
bounds: [ -180, -85.05112877980659, 180, 85.0511287798066 ],
center: [ -76.275329586789, 39.153492567373, 8 ]
};
});
it('should have a custom URL to get JSON result', function () {
expect(this.model.url()).toEqual('/tile.json');
});
describe('.newTileLayer', function () {
describe('when have fetched model successfully', function () {
beforeEach(function () {
this.model.set(this.attrsExFromToTileJSONSpec);
});
it('should return a new tile layer', function () {
expect(this.model.newTileLayer() instanceof CustomBaselayerModel).toBeTruthy();
});
it('should return model with expected attrs', function () {
var tileLayer = this.model.newTileLayer();
expect(tileLayer.get('urlTemplate')).toEqual('http://localhost:8888/admin/1.0.0/world-light,broadband/{z}/{x}/{y}.png');
expect(tileLayer.get('maxZoom')).toEqual(11);
expect(tileLayer.get('minZoom')).toEqual(0);
expect(tileLayer.get('bounding_boxes')).toEqual([ -180, -85.05112877980659, 180, 85.0511287798066 ]);
expect(tileLayer.get('attribution')).toEqual('<a href="http://openstreetmap.org">OSM contributors</a>');
expect(tileLayer.get('name')).toEqual('compositing');
});
it('should set the TMS option based on the value of scheme', function () {
expect(this.model.newTileLayer().get('tms')).toEqual(false);
this.model.set('scheme', 'tms');
expect(this.model.newTileLayer().get('tms')).toEqual(true);
});
});
});
});
@@ -0,0 +1,51 @@
var ConfigModel = require('builder/data/config-model');
var TileJSONModel = require('builder/components/modals/add-basemap/tilejson/tilejson-model');
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
describe('components/modals/add-basemap/tilejson/tilejson-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: 'https://a.tiles.mapbox.com/v3/mapbox.geography-class/{z}/{x}/{y}.png',
category: 'TileJSON',
className: 'httpsatilesmapboxcomv3mapboxgeographyclasszxypng'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
this.model = new TileJSONModel();
});
describe('.hasAlreadyAddedLayer', function () {
beforeEach(function () {
this.customBaselayerModel = new CustomBaselayerModel({
id: 'basemap-id-2',
urlTemplate: 'https://b.tiles.mapbox.com/v3/mapbox.geography-class/{z}/{x}/{y}.png',
attribution: null,
maxZoom: 21,
minZoom: 0,
className: 'httpsbtilesmapboxcomv3mapboxgeographyclasszxypng',
name: 'Geography Class',
tms: false,
category: 'TileJSON',
type: 'Tiled'
});
});
it('should return true if layer has already been added', function () {
this.model.set('layer', this.customBaselayerModel);
expect(this.model.hasAlreadyAddedLayer(this.customBaselayersCollection)).toBe(false);
this.model.set('layer', this.customBaselayersCollection.at(0));
expect(this.model.hasAlreadyAddedLayer(this.customBaselayersCollection)).toBe(true);
});
});
});
@@ -0,0 +1,129 @@
var _ = require('underscore');
var $ = require('jquery');
var ConfigModel = require('builder/data/config-model');
var TileJSONModel = require('builder/components/modals/add-basemap/tilejson/tilejson-model');
var TileJSONView = require('builder/components/modals/add-basemap/tilejson/tilejson-view');
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
var TileJSONLayerModel = require('builder/components/modals/add-basemap/tilejson/tilejson-layer-model');
describe('components/modals/add-basemap/tilejson/tilejson-view', function () {
beforeEach(function () {
spyOn(_, 'debounce').and.callFake(function (func) {
return function () {
func.apply(this, arguments);
};
});
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: 'https://a.tiles.mapbox.com/v3/mapbox.geography-class/{z}/{x}/{y}.png',
category: 'TileJSON',
className: 'httpsatilesmapboxcomv3mapboxgeographyclasszxypng'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
this.model = new TileJSONModel(null, {
customBaselayersCollection: this.customBaselayersCollection
});
var submitButton = $('<button class="is-disabled">Submit</button>');
var tileJSONLayerModel = new TileJSONLayerModel();
this.view = new TileJSONView({
model: this.model,
customBaselayersCollection: this.customBaselayersCollection,
submitButton: submitButton,
tileJSONLayerModel: tileJSONLayerModel
});
this.view.render();
});
it('should render the set button as disabled initially', function () {
expect(this.view._submitButton.hasClass('is-disabled')).toBe(true);
});
describe('when user written a URL', function () {
describe('when URL is half-done or invalid', function () {
beforeEach(function () {
spyOn($, 'ajax');
this.view.$('.js-url')
.val('ht')
.trigger('keydown');
});
it('should indicate that it is validating URL', function () {
expect(this.innerHTML()).toMatch('js-validating.*display: inline');
});
describe('when triggers error', function () {
beforeEach(function () {
$.ajax.calls.argsFor(0)[0].error();
});
it('should show error', function () {
expect(this.view.$('.js-error').attr('class')).toContain('is-visible');
expect(this.innerHTML()).toContain('components.modals.add-basemap.tilejson.invalid');
});
it('should not indicate validating anymore', function () {
expect(this.innerHTML()).not.toMatch('js-validating.*display: inline');
});
it('should disable OK button', function () {
expect(this.view._submitButton.hasClass('is-disabled')).toBe(true);
});
});
describe('when finally written/pasted a valid URL', function () {
beforeEach(function () {
this.layer = new CustomBaselayerModel({
id: 'basemap-id-2',
urlTemplate: 'https://b.tiles.mapbox.com/v3/mapbox.geography-class/{z}/{x}/{y}.png',
attribution: null,
maxZoom: 21,
minZoom: 0,
className: 'httpsbtilesmapboxcomv3mapboxgeographyclasszxypng',
name: 'Geography Class',
tms: false,
category: 'TileJSON',
type: 'Tiled'
});
spyOn(this.view._tileJSONLayerModel, 'newTileLayer').and.returnValue(this.layer);
$.ajax.calls.argsFor(0)[0].success();
});
it('should create layer with url', function () {
expect(this.model.get('layer')).toBe(this.layer);
});
it('should enable save button', function () {
expect(this.view.$('.js-ok').attr('class')).not.toContain('is-disabled');
});
it('should hide error', function () {
expect(this.view.$('.js-error').attr('class')).not.toContain('is-visible');
});
});
});
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
afterEach(function () {
this.view.clean();
});
});
@@ -0,0 +1,95 @@
var WMSModel = require('builder/components/modals/add-basemap/wms/wms-model');
var ConfigModel = require('builder/data/config-model');
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
var SelectLayerView = require('builder/components/modals/add-basemap/wms/select-layer-view');
describe('editor/components/modals/add-basemap/wms/select-layer-view', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: 'https://a.example.com/{z}/{x}/{y}.png',
category: 'Custom',
className: 'httpsaexamplecomzxypng'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
this.layer = new CustomBaselayerModel({
id: 'basemap-id-2',
urlTemplate: 'https://a.example.com/{z}/{x}/{y}.png',
attribution: null,
maxZoom: 21,
minZoom: 0,
className: 'httpsaexamplecomzxypng',
name: 'item #2',
tms: false,
category: 'WMS',
type: 'Tiled'
});
this.customBaselayersCollection.add(this.layer);
this.model = new WMSModel(null, {
customBaselayersCollection: this.customBaselayersCollection
});
this.view = new SelectLayerView({
model: this.model,
customBaselayersCollection: this.customBaselayersCollection
});
this.view.render();
});
describe('when there are a bunch of layers and some unavailable ones', function () {
beforeEach(function () {
this.model.wmsLayersCollection.reset([{
title: 'item #1'
}, {
title: 'item #2'
}, {
title: 'item #3'
}, {
title: 'item #4'
}]);
this.view.render();
});
it('should render the amount of layers', function () {
expect(this.model.getLayers().length).toBe(4);
expect(this.model.layersAvailableCount()).toBe(3);
expect(this.innerHTML()).toContain('components.modals.add-basemap.wms.placeholder');
});
it('should render the individual items', function () {
expect(this.innerHTML()).toContain('item #1');
expect(this.innerHTML()).toContain('item #4');
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
describe('when click back', function () {
beforeEach(function () {
this.view.$('.js-back').click();
});
it('should set current view to go back to enter URL', function () {
expect(this.model.get('currentView')).toEqual('enterURL');
});
});
afterEach(function () {
this.view.clean();
});
});
@@ -0,0 +1,142 @@
var LayersCollection = require('builder/components/modals/add-basemap/wms/wms-layers-collection');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
var WMSService = require('builder/data/wms-service');
describe('editor/components/modals/add-basemap/wms/wms-layer-model', function () {
beforeEach(function () {
this.wmsService = new WMSService();
this.wmsLayersCollection = new LayersCollection(null, {
wmsService: this.wmsService
});
this.wmsLayersCollection.reset([{}]);
this.model = this.wmsLayersCollection.at(0);
});
it('should have idle state by default', function () {
expect(this.model.get('state')).toEqual('idle');
});
describe('.createProxiedLayerOrCustomBaselayerModel', function () {
beforeEach(function () {
spyOn(this.model, 'save');
});
describe('when is a WMS resource', function () {
beforeEach(function () {
this.model.createProxiedLayerOrCustomBaselayerModel();
});
it('should change state to saving', function () {
expect(this.model.get('state')).toEqual('saving');
});
it('should save the WMS model', function () {
expect(this.model.save).toHaveBeenCalled();
});
describe('when save succeeds', function () {
describe('when can create customBaselayerModel', function () {
beforeEach(function () {
this.customBaselayerModel = jasmine.createSpy('customBaselayerModel');
spyOn(this.model, '_newProxiedBaselayerModel').and.returnValue(this.customBaselayerModel);
this.model.save.calls.argsFor(0)[1].success();
});
it('should create a new tile layer', function () {
expect(this.model.get('customBaselayerModel')).toBe(this.customBaselayerModel);
});
it('should set state to saveDone', function () {
expect(this.model.get('state')).toEqual('saveDone');
});
});
describe('when could not create customBaselayerModel', function () {
beforeEach(function () {
spyOn(this.model, '_newProxiedBaselayerModel').and.throwError('meh');
this.model.save.calls.argsFor(0)[1].success();
});
it('should not have any customBaselayerModel set', function () {
expect(this.model.get('customBaselayerModel')).toBeUndefined();
});
it('should set state to saveDone', function () {
expect(this.model.get('state')).toEqual('saveFail');
});
});
});
describe('when save fails', function () {
beforeEach(function () {
this.model.save.calls.argsFor(0)[1].error();
});
it('should set state to saveFail', function () {
expect(this.model.get('state')).toEqual('saveFail');
});
});
});
describe('when is a WMTS resource with unsupported matrix set (e.g. GoogleMapsCompatible)', function () {
beforeEach(function () {
this.model.set({
type: 'wmts',
url_template: 'http://foo.com/bar/%%(z)s/%%(x)s/%%(y)s.png',
matrix_sets: ['GoogleMapsCompatible']
});
spyOn(this.model, '_byCustomURL').and.callThrough();
this.model.createProxiedLayerOrCustomBaselayerModel();
});
it('should create the tile layer with a proper XYZ URL', function () {
expect(this.model._byCustomURL).toHaveBeenCalled();
expect(this.model._byCustomURL.calls.argsFor(0)[0]).toEqual('http://foo.com/bar/{z}/{x}/{y}.png');
});
it('should return a tile layer directly instead', function () {
expect(this.model.get('customBaselayerModel') instanceof CustomBaselayerModel).toBeTruthy();
});
it('should set saveDone', function () {
expect(this.model.get('state')).toEqual('saveDone');
});
});
});
describe('._newProxiedBaselayerModel', function () {
it('should throw an error unless mapproxy id is present', function () {
expect(function () {
this.model._newProxiedBaselayerModel();
}).toThrowError();
});
describe('when mapproxy id is present', function () {
beforeEach(function () {
this.model.set({
mapproxy_id: 'abc123',
attribution: 'attribution',
name: 'tilelayer test',
bounding_boxes: [1, 2, 3, 4]
});
this.proxiedBaselayerModel = this.model._newProxiedBaselayerModel();
});
it('should return a tilelayer object', function () {
expect(this.proxiedBaselayerModel).toEqual(jasmine.any(CustomBaselayerModel));
});
it('should should have expected attrs on returned object', function () {
expect(this.proxiedBaselayerModel.get('urlTemplate')).toMatch('/abc123/wmts/');
expect(this.proxiedBaselayerModel.get('attribution')).toEqual('attribution');
expect(this.proxiedBaselayerModel.get('maxZoom')).toEqual(21);
expect(this.proxiedBaselayerModel.get('minZoom')).toEqual(0);
expect(this.proxiedBaselayerModel.get('name')).toEqual('tilelayer test');
expect(this.proxiedBaselayerModel.get('proxy')).toBeTruthy();
expect(this.proxiedBaselayerModel.get('bounding_boxes')).toEqual([1, 2, 3, 4]);
});
});
});
});
@@ -0,0 +1,103 @@
var LayerView = require('builder/components/modals/add-basemap/wms/wms-layer-view');
var LayerModel = require('builder/components/modals/add-basemap/wms/wms-layer-model');
var ConfigModel = require('builder/data/config-model');
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
var WMSService = require('builder/data/wms-service');
describe('editor/components/modals/add-basemap/wms/wms-layer-view', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.wmsService = new WMSService();
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: 'https://a.example.com/{z}/{x}/{y}.png',
category: 'Custom',
className: 'httpsaexamplecomzxypng'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
this.model = new LayerModel(null, {
wmsService: this.wmsService
});
this.view = new LayerView({
model: this.model,
customBaselayersCollection: this.customBaselayersCollection
});
this.view.render();
});
it('should render item title or name', function () {
this.model.set('name', 'Roads');
this.view.render();
expect(this.innerHTML()).toContain('Roads');
this.model.set('title', 'Title has prio');
this.view.render();
expect(this.innerHTML()).toContain('Title has prio');
});
it('should not disable any item by default', function () {
expect(this.innerHTML).not.toContain('is-disabled');
});
describe('when can not save layer', function () {
beforeEach(function () {
this.model.set('title', 'Test');
this.layer = new CustomBaselayerModel({
id: 'basemap-id-2',
urlTemplate: 'https://a.example.com/{z}/{x}/{y}.png',
attribution: null,
maxZoom: 21,
minZoom: 0,
className: 'httpsaexamplecomzxypng',
name: 'Test',
tms: false,
category: 'WMS',
type: 'Tiled'
});
this.customBaselayersCollection.add(this.layer);
this.view.render();
});
it('should disable item', function () {
expect(this.innerHTML()).toContain('is-disabled');
});
});
describe('when clicking add this', function () {
beforeEach(function () {
spyOn(this.model, 'createProxiedLayerOrCustomBaselayerModel');
spyOn(this.model, 'canSave');
});
it('should call save on model only if can save', function () {
this.model.canSave.and.returnValue(false);
this.view.$('.js-add').click();
expect(this.model.createProxiedLayerOrCustomBaselayerModel).not.toHaveBeenCalled();
this.model.canSave.and.returnValue(true);
this.view.$('.js-add').click();
expect(this.model.createProxiedLayerOrCustomBaselayerModel).toHaveBeenCalled();
});
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
afterEach(function () {
this.view.clean();
});
});
@@ -0,0 +1,29 @@
var $ = require('jquery');
var LayersCollection = require('builder/components/modals/add-basemap/wms/wms-layers-collection');
var WMSService = require('builder/data/wms-service');
describe('editor/components/modals/add-basemap/wms/wms-layers-collection', function () {
beforeEach(function () {
spyOn($, 'ajax').and.callFake(function (options) {
options.success({});
});
this.wmsService = new WMSService();
this.wmsLayersCollection = new LayersCollection(null, {
wmsService: this.wmsService
});
});
describe('.fetch', function () {
beforeEach(function () {
this.url = 'http://openlayers.org/en/v3.5.0/examples/data/ogcsample.xml';
spyOn(this.wmsLayersCollection._wmsService, 'getFetchLayersURL').and.returnValue(this.url);
this.wmsLayersCollection.fetch();
});
it('should fetch layers from given URL', function () {
expect(this.wmsLayersCollection._wmsService.getFetchLayersURL).toHaveBeenCalled();
});
});
});
@@ -0,0 +1,121 @@
var WMSModel = require('builder/components/modals/add-basemap/wms/wms-model');
var ConfigModel = require('builder/data/config-model');
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
describe('editor/components/modals/add-basemap/wms/wms-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: 'https://a.example.com/{z}/{x}/{y}.png',
category: 'Custom',
className: 'httpsaexamplecomzxypng'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
this.model = new WMSModel(null, {
customBaselayersCollection: this.customBaselayersCollection
});
});
it('should not have any layers initially', function () {
expect(this.model.wmsLayersCollection.length).toEqual(0);
});
it('should have enter URl as initial view', function () {
expect(this.model.get('currentView')).toEqual('enterURL');
});
describe('.fetchLayers', function () {
beforeEach(function () {
this.url = 'http://openlayers.org/en/v3.5.0/examples/data/ogcsample.xml';
this.model.wmsLayersCollection.sync = function (a, b, opts) {
};
spyOn(this.model.wmsLayersCollection, 'fetch').and.callThrough();
this.model.fetchLayers(this.url);
});
it('should fetch layers from given URL', function () {
expect(this.model.wmsLayersCollection.fetch).toHaveBeenCalled();
expect(this.model.wmsLayersCollection._wmsService._wms_url).toEqual(this.url);
});
it('should change view to indicate that layers are being fetched', function () {
expect(this.model.get('currentView')).toEqual('fetchingLayers');
});
describe('when fetch is done', function () {
describe('when there are layers', function () {
beforeEach(function () {
this.model.wmsLayersCollection.sync = function (a, b, opts) {
opts.success({
layers: [{}, {}, {}],
title: 'title',
type: 'type'
});
};
this.model.fetchLayers(this.url);
});
it('should change view to select layer', function () {
expect(this.model.get('currentView')).toEqual('selectLayer');
});
});
describe('when there are no layer', function () {
beforeEach(function () {
this.model.wmsLayersCollection.sync = function (a, b, opts) {
opts.success({
layers: [],
title: 'title',
type: 'type'
});
};
this.model.fetchLayers(this.url);
});
it('should change view back to enter URL', function () {
expect(this.model.get('currentView')).toEqual('enterURL');
});
it('should indicate that layers were fetched', function () {
expect(this.model.get('layersFetched')).toBeTruthy();
});
});
});
});
describe('when a layer changes state', function () {
beforeEach(function () {
this.saveBasemapSpy = jasmine.createSpy('saveBasemap');
this.model.bind('saveBasemap', this.saveBasemapSpy);
this.model.wmsLayersCollection.reset([{}, {}, {}]);
});
it('should set the current view appropriately', function () {
this.model.wmsLayersCollection.at(1).set('state', 'saving');
expect(this.model.get('currentView')).toEqual('savingLayer');
this.model.wmsLayersCollection.at(1).set('state', 'saveFail');
expect(this.model.get('currentView')).toEqual('saveFail');
});
it('should select layer if state is done', function () {
this.customBaselayerModel = {};
this.model.wmsLayersCollection.at(1).set({
state: 'saveDone',
customBaselayerModel: this.customBaselayerModel
});
expect(this.saveBasemapSpy).toHaveBeenCalled();
expect(this.model.get('layer')).toBe(this.customBaselayerModel);
});
});
});
@@ -0,0 +1,182 @@
var _ = require('underscore');
var $ = require('jquery');
var WMSView = require('builder/components/modals/add-basemap/wms/wms-view');
var WMSModel = require('builder/components/modals/add-basemap/wms/wms-model');
var ConfigModel = require('builder/data/config-model');
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
describe('editor/components/modals/add-basemap/wms/wms-view', function () {
beforeEach(function () {
spyOn(_, 'debounce').and.callFake(function (func) {
return function () {
func.apply(this, arguments);
};
});
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: 'https://a.example.com/{z}/{x}/{y}.png',
category: 'Custom',
className: 'httpsaexamplecomzxypng'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
this.model = new WMSModel(null, {
customBaselayersCollection: this.customBaselayersCollection
});
var submitButton = $('<button class="is-disabled">Submit</button>');
var modalFooter = $('<div></div>');
this.view = new WMSView({
model: this.model,
customBaselayersCollection: this.customBaselayersCollection,
submitButton: submitButton,
modalFooter: modalFooter
});
this.view.render();
});
it('should render the set button as disabled initially', function () {
expect(this.view._submitButton.hasClass('is-disabled')).toBe(true);
});
describe('when user written a URL', function () {
beforeEach(function () {
var $el = this.view.$('.js-url');
$el.val('http://openlayers.org/en/v3.5.0/examples/data/ogcsample.xml');
$el.trigger('keydown');
});
it('should enable get-layers button', function () {
expect(this.view._submitButton.hasClass('is-disabled')).toBe(false);
});
describe('when click fetch layers', function () {
beforeEach(function () {
this.model.wmsLayersCollection.sync = function (a, b, opts) {
};
spyOn(this.model, 'fetchLayers').and.callThrough();
this.view._submitButton.click();
});
it('should show fetching layers', function () {
expect(this.innerHTML()).toContain('components.modals.add-basemap.fetching');
});
it('should call fetch layers on view model', function () {
expect(this.model.fetchLayers).toHaveBeenCalled();
});
describe('when there is at least one layer fetched', function () {
beforeEach(function () {
this.model.wmsLayersCollection.reset([{ name: '' }]);
});
it('should change to select layer view', function () {
expect(this.innerHTML()).toContain('components.modals.add-basemap.wms.placeholder');
});
it('should show the search form', function () {
expect(this.innerHTML()).toContain('components.modals.add-layer.navigation.search');
});
});
describe('when there are several layers fetched', function () {
beforeEach(function () {
this.model.wmsLayersCollection.reset([
{ name: 'Bageshwar' },
{ name: 'Bagaha' },
{ name: 'Bahadurgarh' },
{ name: 'baharampur' },
{ name: 'Bahraich' },
{ name: 'Chirmiri' }
]);
});
it('should allow to search', function () {
this.view.$el.find('.js-search-input').val('Bah');
this.view.$el.find('.js-search-link').click();
expect(this.innerHTML()).toContain('Bahraich');
expect(this.innerHTML()).toContain('Bahadurgarh');
expect(this.innerHTML()).toContain('baharampur');
expect(this.innerHTML()).not.toContain('Chirmiri');
});
it('should show a no result search', function () {
this.view.$el.find('.js-search-input').val('León');
this.view.$el.find('.js-search-link').click();
expect(this.innerHTML()).toContain('components.modals.add-basemap.wms.unfortunately');
});
it('should close the search', function () {
this.view.$el.find('.js-search-input').val('Bah');
this.view.$el.find('.js-search-link').click();
this.view.$el.find('.js-clean-search').click();
expect(this.view.$el.find('.js-search-input').val()).toBeFalsy();
expect(this.innerHTML()).toContain('Bageshwar');
expect(this.innerHTML()).toContain('Bagaha');
expect(this.innerHTML()).toContain('Bahraich');
expect(this.innerHTML()).toContain('Bahadurgarh');
expect(this.innerHTML()).toContain('baharampur');
expect(this.innerHTML()).toContain('Chirmiri');
});
});
describe('when there are no layers fetched', function () {
beforeEach(function () {
this.model.wmsLayersCollection.reset();
});
it('should set back to enter url view', function () {
expect(this.innerHTML()).toContain('components.modals.add-basemap.wms.insert');
});
it('should show an error indicating URL being invalid', function () {
expect(this.innerHTML()).toContain('components.modals.add-basemap.wms.invalid');
});
it("shouldn't show the search form", function () {
expect(this.innerHTML()).not.toContain('components.modals.add-layer.navigation.search');
});
});
describe('when layer is added', function () {
beforeEach(function () {
this.model.set('currentView', 'savingLayer');
});
it('should show saving layer indicator', function () {
expect(this.innerHTML()).toContain('components.modals.add-basemap.saving');
});
describe('when layer is not saved', function () {
beforeEach(function () {
this.model.set('currentView', 'saveFail');
});
it('should show error view', function () {
expect(this.innerHTML()).toContain('error');
});
});
});
});
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
afterEach(function () {
this.view.clean();
});
});
@@ -0,0 +1,51 @@
var ConfigModel = require('builder/data/config-model');
var XYZModel = require('builder/components/modals/add-basemap/xyz/xyz-model');
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
describe('components/modals/add-basemap/xyz/xyz-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: 'https://a.example.com/{z}/{x}/{y}.png',
category: 'Custom',
className: 'httpsaexamplecomzxypng'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
this.model = new XYZModel();
});
describe('.hasAlreadyAddedLayer', function () {
beforeEach(function () {
this.customBaselayerModel = new CustomBaselayerModel({
id: 'basemap-id-2',
urlTemplate: 'https://b.example.com/{z}/{x}/{y}.png',
attribution: null,
maxZoom: 21,
minZoom: 0,
className: 'httpsbexamplecomzxypng',
name: 'item #2',
tms: false,
category: 'Custom',
type: 'Tiled'
});
});
it('should return true if layer has already been added', function () {
this.model.set('layer', this.customBaselayerModel);
expect(this.model.hasAlreadyAddedLayer(this.customBaselayersCollection)).toBe(false);
this.model.set('layer', this.customBaselayersCollection.at(0));
expect(this.model.hasAlreadyAddedLayer(this.customBaselayersCollection)).toBe(true);
});
});
});
@@ -0,0 +1,156 @@
var _ = require('underscore');
var $ = require('jquery');
var XYZView = require('builder/components/modals/add-basemap/xyz/xyz-view');
var XYZModel = require('builder/components/modals/add-basemap/xyz/xyz-model');
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
describe('components/modals/add-basemap/xyz/xyz-view', function () {
beforeEach(function () {
spyOn(_, 'debounce').and.callFake(function (func) {
return function () {
func.apply(this, arguments);
};
});
var submitButton = $('<button class="is-disabled">Submit</button>');
this.model = new XYZModel();
this.view = new XYZView({
model: this.model,
submitButton: submitButton
});
this.view.render();
});
it('should render the set button as disabled initially', function () {
expect(this.view._submitButton.hasClass('is-disabled')).toBe(true);
});
it('should change TMS checkbox when model changes', function () {
expect(this.view.$('.js-tms .Checkbox-input').hasClass('is-checked')).toBeFalsy();
this.view.model.set('tms', true);
expect(this.view.$('.js-tms .Checkbox-input').hasClass('is-checked')).toBeTruthy();
});
describe('when user written a URL', function () {
describe('when URL is half-done or invalid', function () {
beforeEach(function () {
var $el = this.view.$('.js-url');
$el.val('ht');
$el.trigger('keydown');
$el.val('htt');
$el.trigger('keydown');
});
it('should show error', function () {
expect(this.view.$('.js-error').attr('class')).toContain('is-visible');
expect(this.innerHTML()).toContain('components.modals.add-basemap.xyz.not-valid');
});
it('should disable OK button', function () {
expect(this.view._submitButton.hasClass('is-disabled')).toBe(true);
});
});
describe('when finally written/pasted a valid URL', function () {
beforeEach(function () {
this.layer = new CustomBaselayerModel({
id: 'basemap-id-2',
urlTemplate: 'https://b.example.com/{z}/{x}/{y}.png',
attribution: null,
maxZoom: 21,
minZoom: 0,
name: '',
tms: false,
category: 'Custom',
type: 'Tiled'
});
spyOn(this.layer, 'validateTemplateURL');
});
describe('when URL does not have a valid XYZ format', function () {
beforeEach(function () {
spyOn(this.view, '_byCustomURL').and.throwError();
this.view.$('.js-url')
.val('http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png')
.trigger('keydown');
});
it('should show error', function () {
expect(this.innerHTML()).toContain('components.modals.add-basemap.xyz.not-valid');
});
});
describe('when URL has a valid XYZ format', function () {
beforeEach(function () {
spyOn(this.view, '_byCustomURL').and.returnValue(this.layer);
this.view.$('.js-url')
.val('http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png')
.trigger('keydown');
});
it('should create layer with url', function () {
expect(this.view._byCustomURL).toHaveBeenCalledWith('http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png', false);
});
it('should set the layer on the view model', function () {
expect(this.model.get('layer')).toBe(this.layer);
});
it('should disable the save button', function () {
expect(this.view._submitButton.attr('class')).toContain('is-disabled');
});
it('should hide error', function () {
expect(this.view.$('.js-error').attr('class')).not.toContain('is-visible');
});
it('should validate template URL', function () {
expect(this.layer.validateTemplateURL).toHaveBeenCalledWith({
success: jasmine.any(Function),
error: jasmine.any(Function)
});
});
describe('when URL is finally validated', function () {
beforeEach(function () {
this.layer.validateTemplateURL.calls.argsFor(0)[0].success();
});
it('should enable save button', function () {
expect(this.view._submitButton.attr('class')).not.toContain('is-disabled');
});
it('should hide error', function () {
expect(this.view.$('.js-error').attr('class')).not.toContain('is-visible');
});
});
describe('when URL fails to be validated', function () {
beforeEach(function () {
this.layer.validateTemplateURL.calls.argsFor(0)[0].error();
});
it('should enable save button', function () {
expect(this.view._submitButton.attr('class')).not.toContain('is-disabled');
});
it('should show error', function () {
expect(this.view.$('.js-error').attr('class')).toContain('is-visible');
expect(this.innerHTML()).toContain('components.modals.add-basemap.xyz.couldnt-validate');
});
});
});
});
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
afterEach(function () {
this.view.clean();
});
});
@@ -0,0 +1,306 @@
var Backbone = require('backbone');
var AddLayerModel = require('builder/components/modals/add-layer/add-layer-model');
var TableModel = require('builder/data/table-model');
var UserModel = require('builder/data/user-model');
var ConfigModel = require('builder/data/config-model');
var sharedForCreateListingViewModel = require('./shared-for-create-listing-view-model.spec.js');
var sharedForCreateListingImportViewModel = require('./shared-for-import-view-model.spec.js');
var UserActions = require('builder/data/user-actions');
var MetricsTracker = require('builder/components/metrics/metrics-tracker');
describe('components/modals/add-layer/add-layer-model', function () {
beforeEach(function () {
jasmine.Ajax.install();
spyOn(MetricsTracker, 'track');
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var vizUrl = 'http(s)?://(.)+' + configModel.get('base_url') + '/api/v1/viz.*';
jasmine.Ajax.stubRequest(new RegExp(vizUrl)).andReturn({
status: 200
});
this.pollingModel = new Backbone.Model();
this.userModel = new UserModel({
username: 'pepe'
}, {
configModel: configModel
});
this.userActions = UserActions({
userModel: this.userModel,
analysisDefinitionNodesCollection: {},
analysisDefinitionsCollection: {},
layerDefinitionsCollection: {},
widgetDefinitionsCollection: {}
});
spyOn(this.userActions, 'createLayerFromTable');
this.model = new AddLayerModel(null, {
pollingModel: this.pollingModel,
configModel: configModel,
userModel: this.userModel,
userActions: this.userActions
});
});
afterEach(function () {
jasmine.Ajax.uninstall();
});
sharedForCreateListingViewModel.call(this);
sharedForCreateListingImportViewModel.call(this);
it('should have listing as default content pane', function () {
expect(this.model.get('contentPane')).toEqual('listing');
});
describe('.canFinish', function () {
describe('when listing is import', function () {
beforeEach(function () {
spyOn(this.model._uploadModel, 'isValidToUpload');
this.model.set('listing', 'import');
});
it('should return true only if upload is valid', function () {
expect(this.model.canFinish()).toBeFalsy();
this.model._uploadModel.isValidToUpload = function () { return true; };
expect(this.model.canFinish()).toBeTruthy();
});
});
describe('when listing is datasets', function () {
beforeEach(function () {
this.model.set('listing', 'datasets');
});
it('should return true if there is at least one dataset selected', function () {
expect(this.model.canFinish()).toBeFalsy();
this.model._selectedDatasetsCollection.add({});
expect(this.model.canFinish()).toBeTruthy();
});
});
});
describe('.canSelect', function () {
beforeEach(function () {
this.dataset = new Backbone.Model({
selected: false
});
});
it('should return true only if there is dataset selected or if wanting to deselect', function () {
expect(this.model.canSelect(this.dataset)).toBeTruthy();
this.model._selectedDatasetsCollection.add({ selected: true });
expect(this.model.canSelect(this.dataset)).toBeFalsy();
this.dataset.set('selected', true);
expect(this.model.canSelect(this.dataset)).toBeTruthy();
});
});
describe('.finish', function () {
describe('when an dataset is selected', function () {
describe('when dataset is a remote one (from library)', function () {
beforeEach(function () {
this.pollingModel.bind('importByUploadData', function (data) {
this.importByUploadData = data;
}, this);
this.model._tablesCollection.reset([{
id: 'abc-123',
type: 'remote',
name: 'foobar',
external_source: {
size: 1024
}
}]);
this.model._tablesCollection.at(0).set('selected', true);
this.model.finish();
});
it('should trigger a importByUploadData event', function () {
expect(this.importByUploadData).toBeTruthy();
});
it('should pass a metadata object with necessary data to import dataset', function () {
expect(this.importByUploadData).toEqual(jasmine.objectContaining({ type: 'remote' }));
expect(this.importByUploadData).toEqual(jasmine.objectContaining({ value: 'foobar' }));
expect(this.importByUploadData).toEqual(jasmine.objectContaining({ remote_visualization_id: 'abc-123' }));
expect(this.importByUploadData).toEqual(jasmine.objectContaining({ size: 1024 }));
expect(this.importByUploadData).toEqual(jasmine.objectContaining({ create_vis: false }));
});
});
describe('when dataset is not a remote one', function () {
beforeEach(function () {
this.model.bind('addLayerDone', function () {
this.addLayerDoneCalled = true;
}, this);
this.model._tablesCollection.reset([{
id: 'abc',
type: 'table',
name: 'paco',
table: {
name: 'tableName'
}
}]);
this.model._tablesCollection.at(0).set('selected', true);
this.model.finish();
});
it('should create layer from dataset', function () {
expect(this.userActions.createLayerFromTable).toHaveBeenCalled();
});
it('should create layer with expected params', function () {
var tableModel = this.userActions.createLayerFromTable.calls.argsFor(0)[0];
expect(tableModel.get('name')).toBe('tableName');
});
it('should change the content view setting', function () {
expect(this.model.get('contentPane')).toEqual('addingNewLayer');
});
describe('when adding layer succeeds', function () {
beforeEach(function () {
var createdModel = new Backbone.Model({ id: 'test' });
expect(this.addLayerDoneCalled).toBeFalsy();
this.userActions.createLayerFromTable.calls.argsFor(0)[1].success(createdModel);
});
it('should trigger addLayerDone', function () {
expect(this.addLayerDoneCalled).toBeTruthy();
});
});
describe('when adding layer fails', function () {
beforeEach(function () {
this.userActions.createLayerFromTable.calls.argsFor(0)[1].error({}, {
responseText: '{"error": ["meh"]}'
});
});
it('should change contentPane to addLayerFail', function () {
expect(this.model.get('contentPane')).toEqual('addLayerFailed');
});
});
describe('when table quota exceeded', function () {
beforeEach(function () {
this.userActions.createLayerFromTable.calls.argsFor(0)[1].error({}, {
responseText: '{"errors": ["You have reached your table quota"]}'
});
});
it('should change contentPane to datasetQuotaExceeded', function () {
expect(this.model.get('contentPane')).toEqual('datasetQuotaExceeded');
});
});
});
});
describe('.createFromScratch', function () {
beforeEach(function () {
spyOn(TableModel.prototype, 'save').and.callFake(function () {
this.set('name', 'name-just-for-testing-purposes', { silent: true });
return this;
});
});
it('should change to loading state', function () {
this.model.createFromScratch();
expect(this.model.get('contentPane')).toEqual('creatingFromScratch');
});
it('should save a new dataset table', function () {
this.model.createFromScratch();
expect(TableModel.prototype.save).toHaveBeenCalled();
});
describe('when table is successfully created', function () {
it('should add the table as new layer', function () {
this.model.createFromScratch();
TableModel.prototype.save.calls.argsFor(0)[1].success();
expect(this.userActions.createLayerFromTable).toHaveBeenCalled();
var tableModel = this.userActions.createLayerFromTable.calls.argsFor(0)[0];
expect(tableModel.get('name')).toBe('name-just-for-testing-purposes');
});
});
});
describe('when an upload is set', function () {
beforeEach(function () {
this.pollingModel.bind('importByUploadData', function (d) {
this.importByUploadData = d;
}, this);
this.model.set('listing', 'import');
this.model.finish();
});
it('should call pollingModel importByUploadData with expected data', function () {
expect(this.importByUploadData).toBeTruthy();
expect(this.importByUploadData).toEqual(jasmine.objectContaining({ state: 'idle' }));
});
});
});
describe('when listing', function () {
it('should detect which', function () {
this.model.set('listing', 'import');
expect(this.model._atImportPane()).toBe(true);
this.model.set('listing', 'datasets');
expect(this.model._atImportPane()).toBe(false);
expect(this.model._atDatasetsPane()).toBe(true);
this.model.set('listing', 'scratch');
expect(this.model._atDatasetsPane()).toBe(false);
expect(this.model._atScratchPane()).toBe(true);
});
it('should detect "twitter import pane"', function () {
this.model.set('listing', 'import');
this.model.set('activeImportPane', 'twitter');
expect(this.model._atTwitterImportPane()).toBe(true);
this.model.set('activeImportPane', '*whatever-else*');
expect(this.model._atTwitterImportPane()).toBe(false);
});
describe('and deciding on togglers', function () {
it('should decide to show "guessing toggler" if at import', function () {
this.model.set('listing', 'import');
expect(this.model.showGuessingToggler()).toBe(true);
this.model.set('listing', 'datasets');
expect(this.model.showGuessingToggler()).toBe(false);
});
it('should decide to show "privacy toggler" if at import (unless deprecated twitter)', function () {
this.model.set('listing', 'import');
this.model.set('activeImportPane', '*whatever-but-twitter*');
expect(this.model.showPrivacyToggler()).toBe(true);
this.model.set('activeImportPane', 'twitter');
var spy = spyOn(this.userModel, 'hasOwnTwitterCredentials');
spy.and.returnValue(true);
expect(this.model.showPrivacyToggler()).toBe(true);
spy.and.returnValue(false);
expect(this.model.showPrivacyToggler()).toBe(false);
});
});
});
});
@@ -0,0 +1,60 @@
var $ = require('jquery');
var _ = require('underscore');
var CreditsUsageView = require('builder/components/modals/add-layer/content/imports/import-twitter/credits-usage-view');
var getUserModelFixture = require('fixtures/builder/user-model.fixture.js');
/** Note: More behaviour is tested on its parent view (import-twitter.view.spec.js) */
describe('components/modals/add-layer/content/imports/imports-twitter/credits-usage-view', function () {
beforeEach(function () {
this.userModel = getUserModelFixture();
// extracted from `import-twitter.tpl`
this.tplForView =
'<div class="Form-rowData Form-rowData--longer CreditsUsage">' +
' <div class="UISlider CreditsUsage-slider js-slider"></div>' +
' <div class="CreditsUsage-info CDB-Text CDB-Size-medium js-info"></div>' +
'</div>';
this.createView = function (opts) {
var custom = opts || {};
var defaults = {
userModel: this.userModel,
el: $(this.tplForView)
};
return new CreditsUsageView(_.extend(defaults, custom));
};
this.view = this.createView();
});
it('should be enabled by default', function () {
expect(this.view._disabled).toBe(false);
});
it('can be created disabled or enabled', function () {
var v = this.createView({disabled: true});
expect(v._disabled).toBe(true);
v = this.createView({disabled: false});
expect(v._disabled).toBe(false);
});
describe('render', function () {
beforeEach(function () {
this.view.render();
});
it('should render properly', function () {
expect(this.view.$('.js-slider').length).toBe(1);
expect(this.view.$('.js-info').length).toBe(1);
});
it('can be rendered with a disabled or enabled slider', function () {
expect(this.view._disabled).toBe(false);
expect(this.view.$('.js-slider').slider('option', 'disabled')).toBe(false);
this.view._disabled = true;
this.view.render();
expect(this.view.$('.js-slider').slider('option', 'disabled')).toBe(true);
});
});
});
@@ -0,0 +1,176 @@
var _ = require('underscore');
var ImportTwitterView = require('builder/components/modals/add-layer/content/imports/import-twitter/import-twitter-view');
var getUserModelFixture = require('fixtures/builder/user-model.fixture.js');
var getConfigModelFixture = require('fixtures/builder/config-model.fixture.js');
describe('components/modals/add-layer/content/imports/imports-twitter/import-twitter-view', function () {
beforeEach(function () {
this.userModel = getUserModelFixture();
this.view = new ImportTwitterView({
userModel: this.userModel,
configModel: getConfigModelFixture()
});
this.view.render();
});
describe('render', function () {
it('should be rendered properly', function () {
expect(this.view.$('.ImportTwitterPanel-categories').length).toBe(1);
expect(this.view.$('.ImportTwitterPanel-datePicker').length).toBe(1);
expect(this.view.$('.ImportTwitterPanel-creditsUsage').length).toBe(1);
});
describe('twitter categories', function () {
beforeEach(function () {
this.categories = this.view.categories;
});
it('should render properly', function () {
expect(this.categories.$('.TwitterCategory').length).toBe(1);
expect(this.categories.$('.TwitterCategory label').text()).toBe(
'components.modals.add-layer.imports.twitter.category 1');
});
it('should create a new category when the before one has any character', function () {
this.categories.$('.TwitterCategory .js-terms').val('test');
this.categories.$('.TwitterCategory .js-terms').trigger('keydown');
expect(this.categories.$('.TwitterCategory').length).toBe(2);
});
it('should delete the category when it is empty and it is not the first one', function () {
this.categories.$('.TwitterCategory .js-terms').val('test');
this.categories.$('.TwitterCategory .js-terms').trigger('keydown');
expect(this.categories.$('.TwitterCategory').length).toBe(2);
this.categories.$('.TwitterCategory:eq(0) .js-terms').val('');
this.categories.$('.TwitterCategory:eq(0) .js-terms').trigger('keydown');
expect(this.categories.$('.TwitterCategory').length).toBe(1);
});
it('should render 4 categories as max', function () {
for (var i = 0, l = 10; i < l; i++) {
this.categories.$('.TwitterCategory:eq(' + i + ') .js-terms').val('test');
this.categories.$('.TwitterCategory:eq(' + i + ') .js-terms').trigger('keydown');
}
expect(this.categories.$('.TwitterCategory').length).toBe(4);
});
it('should return categories as JSON format', function () {
this.categories.$('.TwitterCategory .js-terms').val('test');
this.categories.$('.TwitterCategory .js-terms').trigger('keydown');
var categories = this.categories.getCategories();
expect(categories.length).toBe(2);
expect(categories[0].category).toBe('1');
expect(categories[0].terms.length).toBe(1);
expect(categories[0].terms[0]).toBe('test');
expect(categories[1].category).toBe('2');
expect(categories[1].terms.length).toBe(0);
});
});
describe('twitter credits', function () {
beforeEach(function () {
this.creditsUsage = this.view.creditsUsage;
var creditsInfoKey = _.keys(this.creditsUsage._subviews)[0];
this.creditsInfo = this.creditsUsage._subviews[creditsInfoKey];
});
it('should render properly', function () {
expect(this.creditsUsage.$('.CreditsUsage-slider').length).toBe(1);
expect(this.creditsUsage.$('.CreditsUsage-info').length).toBe(1);
});
it('should set as min value 1%, not 0%', function () {
this.creditsUsage.$('.js-slider').slider('value', 0);
expect(this.creditsUsage.model.get('value')).toBe(1);
});
it('should start around 80% of quota', function () {
var perValue = (this.creditsUsage.model.get('value') * 100) / this.userModel.get('twitter').quota;
expect(perValue).toBe(80);
});
it("should let choose 'no limits' when user doesn't have hard limits", function () {
this.creditsUsage.$('.js-slider').slider('value', 101);
expect(this.creditsUsage.$('.js-info').text()).toContain(
'components.modals.add-layer.imports.twitter.credits-no-limit');
});
it("shouldn't let choose 'no limits' when user has hard limits", function () {
var twitterData = this.userModel.get('twitter');
twitterData.hard_limit = true;
this.userModel.set('twitter', twitterData);
this.creditsUsage.render();
this.creditsUsage.$('.js-slider').slider('value', 101);
expect(this.creditsUsage.$('.js-info').text()).not.toContain(
'components.modals.add-layer.imports.twitter.credits-no-limit');
});
it('should disable slider when user has reached his/her limits', function () {
var twitterData = this.userModel.get('twitter');
twitterData.monthly_use = 110;
this.userModel.set('twitter', twitterData);
this.view.render();
expect(this.view.$('.js-slider').slider('option', 'disabled')).toBeTruthy();
expect(this.view.$('.js-info').text()).toContain(
'components.modals.add-layer.imports.twitter.credits-consumed');
});
it('should change credits info when credits value has changed', function () {
expect(this.creditsInfo.model.get('value')).toBe(80);
this.creditsUsage.model.set('value', 30);
expect(this.creditsInfo.model.get('value')).toBe(30);
});
it('should update model when value has changed', function (done) {
expect(this.creditsUsage.model.get('value')).toBe(80);
spyOn(this.view.model, 'validate').and.callThrough();
this.creditsUsage.model.set('value', 1);
var that = this;
_.defer(function () {
var lastCall = that.view.model.validate.calls.mostRecent();
var lastArg = lastCall.args[0].user_defined_limits.twitter_credits_limit;
expect(lastArg).toEqual(1);
done();
});
});
});
describe('enabled or disabled according to user\'s own credentials', function () {
beforeEach(function () {
this.spyOnHasOwnTwitterCredentials = spyOn(this.userModel, 'hasOwnTwitterCredentials');
this.formIsEnabled = function () {
var categories = !this.view.categories._disabled;
var datepicker = !this.view.datepicker._disabled;
var creditsUsage = !this.view.creditsUsage._disabled;
var allInputsEnabled = categories && datepicker && creditsUsage;
return allInputsEnabled;
};
});
it('should be enabled if user has his own credentials', function () {
this.spyOnHasOwnTwitterCredentials.and.returnValue(true);
this.view._initViews();
expect(this.formIsEnabled()).toBe(true);
});
it('should not be enabled if uses don\'t have his own', function () {
this.spyOnHasOwnTwitterCredentials.and.returnValue(false);
this.view._initViews();
expect(this.formIsEnabled()).toBe(false);
});
});
});
it('should not have leaks', function () {
expect(this.view).toHaveNoLeaks();
});
afterEach(function () {
this.view.clean();
});
});
@@ -0,0 +1,38 @@
var _ = require('underscore');
var TwitterCategoriesView = require('builder/components/modals/add-layer/content/imports/import-twitter/twitter-categories/twitter-categories-view');
/** Note: More behaviour, specially 'render', is tested on its parent view (import-twitter.view.spec.js) */
describe('components/modals/add-layer/content/imports/import-twitter/twitter-categories/twitter-categories-view', function () {
beforeEach(function () {
this.createView = function (opts) {
var custom = opts || {};
var defaults = {};
var view = new TwitterCategoriesView(_.extend(defaults, custom));
view.categoriesAreDisabled = function () {
var allDisabled = this.collection.every(function (category) {
return category.get('disabled');
});
return allDisabled;
};
return view;
};
this.view = this.createView();
});
it('should be enabled by default', function () {
expect(this.view._disabled).toBe(false);
expect(this.view.categoriesAreDisabled()).toBe(false);
});
it('can be created disabled or enabled', function () {
var v = this.createView({disabled: true});
expect(v._disabled).toBe(true);
expect(v.categoriesAreDisabled()).toBe(true);
v = this.createView({disabled: false});
expect(v._disabled).toBe(false);
expect(v.categoriesAreDisabled()).toBe(false);
});
});
@@ -0,0 +1,37 @@
var Backbone = require('backbone');
var TwitterCategoryView = require('builder/components/modals/add-layer/content/imports/import-twitter/twitter-categories/twitter-category-view');
/** Note: More behaviour is tested on its parent view (import-twitter.view.spec.js) */
describe('components/modals/add-layer/content/imports/import-twitter/twitter-categories/twitter-category-view', function () {
beforeEach(function () {
this.createView = function () {
var m = new Backbone.Model({
terms: [],
category: '1',
counter: 1014
});
return new TwitterCategoryView({model: m});
};
this.view = this.createView();
});
describe('render', function () {
beforeEach(function () {
this.view.render();
});
it('should render properly', function () {
expect(this.view.$('input').length).toBe(1);
});
it('can be rendered disabled or enabled', function () {
this.view.model.set('disabled', false);
this.view.render();
expect(this.view.$('input').prop('disabled')).toBe(false);
this.view.model.set('disabled', true);
this.view.render();
expect(this.view.$('input').prop('disabled')).toBe(true);
});
});
});
@@ -0,0 +1,55 @@
var Backbone = require('backbone');
var ImportsView = require('builder/components/modals/add-layer/content/imports/imports-view');
var AddLayerModel = require('builder/components/modals/add-layer/add-layer-model');
var userModel = require('fixtures/builder/user-model.fixture');
var configModel = require('fixtures/builder/config-model.fixture');
describe('components/modals/add-layer/content/imports/imports-view', function () {
var privacyModel = new Backbone.Model();
var guessingModel = new Backbone.Model();
var _userModel = userModel({
mailchimp: {
enabled: true
}
});
var _configModel = configModel({
oauth_gdrive: true
});
var createModel = new AddLayerModel({}, {
userModel: _userModel,
configModel: _configModel,
userActions: {},
pollingModel: new Backbone.Model()
});
beforeEach(function () {
this.view = new ImportsView({
userModel: _userModel,
configModel: _configModel,
createModel: createModel,
privacyModel: privacyModel,
guessingModel: guessingModel
});
spyOn(this.view, '_setUploadModel');
this.view.render();
});
it('should call _setUploadModel on change events', function () {
// Switch tabs back and forth, this used to break because backbone drops events
this.view.$('.js-gdriveTab').click();
this.view.$('.js-fileTab').click();
var selectedTab = this.view._tabPaneCollection.filter(function (e) {
return e.get('selected') === true;
})[0];
selectedTab.attributes.createContentView().trigger('change');
expect(this.view._setUploadModel).toHaveBeenCalled();
this.view.clean();
});
afterEach(function () {
// Remove dropzone leaftover elements
document.querySelector('input.dz-hidden-input').remove();
});
});
@@ -0,0 +1,110 @@
var Backbone = require('backbone');
var GuessingTogglerView = require('builder/components/modals/add-layer/footer/guessing-toggler-view');
var CreateModel = require('builder/components/modals/add-layer/add-layer-model');
var getUserModelFixture = require('fixtures/builder/user-model.fixture.js');
var getConfigModelFixture = require('fixtures/builder/config-model.fixture.js');
describe('components/modals/add-layer/footer/guessing-toggler-view', function () {
beforeEach(function () {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*/viz\?.*'))
.andReturn({ status: 200 });
this.upgradeUrl = window.upgrade_url;
window.upgrade_url = 'paco_upgrade';
this.userModel = getUserModelFixture();
this.createModel = new CreateModel({
type: 'map',
contentPane: 'listing',
listing: 'import'
}, {
configModel: getConfigModelFixture(),
userModel: this.userModel,
userActions: {},
pollingModel: new Backbone.Model()
});
this.guessingModel = new Backbone.Model({ guessing: true });
this.view = new GuessingTogglerView({
userModel: this.userModel,
guessingModel: this.guessingModel,
createModel: this.createModel,
configModel: jasmine.createSpyObj('configModel', ['get'])
});
spyOn(this.createModel, 'bind').and.callThrough();
this.spyOnHasOwnTwitterCredentials = spyOn(this.userModel, 'hasOwnTwitterCredentials');
this.view.render();
});
afterEach(function () {
jasmine.Ajax.uninstall();
});
it('should render properly', function () {
expect(this.view.$el.text()).toContain('components.modals.add-layer.footer.guessing-desc');
expect(this.view.$('.js-toggle').length).toBe(1);
expect(this.view.$('.js-toggle input').is(':checked')).toBeTruthy();
});
describe('when twitter import is active', function () {
beforeEach(function () {
this.createModel.set('activeImportPane', 'twitter');
});
it('should not render guessing button', function () {
expect(this.view.$el.text()).not.toContain('components.modals.add-layer.footer.guessing-desc');
expect(this.view.$('button').length).toBe(0);
});
it('should display how to get historical data when user has his own credentials', function () {
this.spyOnHasOwnTwitterCredentials.and.returnValue(true);
this.view.render();
expect(this.view.$el.text()).toContain('components.modals.add-layer.footer.twitter-how-to-historical');
expect(this.view.$el.text()).toContain('components.modals.add-layer.footer.contact-team');
});
it('should display a deprecated warning if user does not have his own credentials', function () {
this.spyOnHasOwnTwitterCredentials.and.returnValue(false);
this.view.render();
expect(this.view.$el.text()).toContain('components.modals.add-layer.footer.deprecated-connector');
expect(this.view.$el.text()).toContain('components.modals.add-layer.footer.twitter-contact-support');
});
});
it('should not change type and content guessing attributes when button is clicked', function () {
// Valid "upload"
var uploadModel = this.createModel.getUploadModel();
uploadModel.setUpload({
type: 'url',
value: 'https://carto.com'
});
expect(uploadModel.get('content_guessing')).toBeTruthy();
expect(uploadModel.get('type_guessing')).toBeTruthy();
expect(this.guessingModel.get('guessing')).toBeTruthy();
this.view.$('.js-toggle').click();
expect(this.guessingModel.get('guessing')).toBeFalsy();
expect(uploadModel.get('content_guessing')).toBeTruthy();
expect(uploadModel.get('type_guessing')).toBeTruthy();
});
it('should not change type and content guessing attributes when upload isn\'t valid', function () {
// Non valid "upload"
var uploadModel = this.createModel.getUploadModel();
uploadModel.setUpload({
type: 'url',
value: 'hello'
});
this.view.$('.js-toggle').click();
expect(this.guessingModel.get('guessing')).toBeFalsy();
expect(uploadModel.get('content_guessing')).toBeTruthy();
expect(uploadModel.get('type_guessing')).toBeTruthy();
});
it('should have no leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
@@ -0,0 +1,104 @@
var Backbone = require('backbone');
var PrivacyTogglerView = require('builder/components/modals/add-layer/footer/privacy-toggler-view');
var CreateModel = require('builder/components/modals/add-layer/add-layer-model');
var getUserModelFixture = require('fixtures/builder/user-model.fixture.js');
var getConfigModelFixture = require('fixtures/builder/config-model.fixture.js');
describe('components/modals/add-layer/footer/privacy-toggler-view', function () {
beforeEach(function () {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*/viz\?.*'))
.andReturn({ status: 200 });
this.upgradeUrl = window.upgrade_url;
window.upgrade_url = 'paco_upgrade';
this.userModel = getUserModelFixture();
var configModel = getConfigModelFixture();
this.model = new CreateModel({
option: 'listing',
listing: 'import'
}, {
configModel: configModel,
userModel: this.userModel,
userActions: {},
pollingModel: new Backbone.Model()
});
this.privacyModel = new Backbone.Model({ privacy: 'PUBLIC' });
this.view = new PrivacyTogglerView({
userModel: this.userModel,
privacyModel: this.privacyModel,
createModel: this.model,
configModel: configModel
});
spyOn(this.model, 'bind').and.callThrough();
this.spyOnHasOwnTwitterCredentials = spyOn(this.userModel, 'hasOwnTwitterCredentials');
this.view.render();
});
afterEach(function () {
jasmine.Ajax.uninstall();
});
it('should render properly', function () {
expect(this.view.$('.PrivacyToggler').length).toBe(1);
expect(this.view.$('.PrivacyToggler--PUBLIC').length).toBe(1);
expect(this.view.$('.PrivacyToggler').hasClass('is-disabled')).toBeFalsy();
});
it('should change privacy when it is clicked', function () {
expect(this.privacyModel.get('privacy')).toBe('PUBLIC');
expect(this.view.$('.PrivacyToggler--PUBLIC').length).toBe(1);
this.view.$('.PrivacyToggler').click();
expect(this.privacyModel.get('privacy')).toBe('PRIVATE');
expect(this.view.$('.PrivacyToggler--PRIVATE').length).toBe(1);
});
it('should point to upgrade when user can\'t change privacy', function () {
var actions = { private_tables: false };
this.userModel.set('actions', actions);
this.view.render();
expect(this.view.$('a.PrivacyToggler').length).toBe(1);
expect(this.view.$('a.PrivacyToggler').attr('href')).toBe('paco_upgrade');
expect(this.view.$('.PrivacyToggler').hasClass('is-disabled')).toBeTruthy();
});
it('should not point to upgrade if user can\'t change privacy and there is no upgrade url', function () {
window.upgrade_url = '';
var actions = { private_tables: false };
this.userModel.set('actions', actions);
this.view.render();
expect(this.view.$('a.PrivacyToggler').length).toBe(0);
expect(this.view.$('button.PrivacyToggler').length).toBe(1);
expect(this.view.$('.PrivacyToggler').hasClass('is-disabled')).toBeTruthy();
});
it('should have no leaks', function () {
expect(this.view).toHaveNoLeaks();
});
describe('when twitter import is active', function () {
beforeEach(function () {
this.model.set('activeImportPane', 'twitter');
});
it('should be displayed if user has his own credentials', function () {
this.spyOnHasOwnTwitterCredentials.and.returnValue(true);
this.view.render();
expect(this.view.$('.PrivacyToggler').length).toBe(1);
});
it('it should not be displayed if user does not have his own credentials', function () {
this.spyOnHasOwnTwitterCredentials.and.returnValue(false);
this.view.render();
expect(this.view.$('.PrivacyToggler').length).toBe(0);
});
});
afterEach(function () {
window.upgrade_url = this.upgradeUrl;
});
});
@@ -0,0 +1,65 @@
var _ = require('underscore');
var Backbone = require('backbone');
var ListingView = require('builder/components/modals/add-layer/content/listing-view');
var CreateModel = require('builder/components/modals/add-layer/add-layer-model');
var UserModel = require('builder/data/user-model');
describe('components/modals/add-layer/listing-view', function () {
beforeEach(function () {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*/viz\?.*'))
.andReturn({ status: 200 });
var configModel = jasmine.createSpyObj('configModel', ['get', 'urlVersion']);
this.userModel = new UserModel({
username: 'pepe',
actions: {
private_tables: true
}
}, {
configModel: configModel
});
this.createModel = new CreateModel({
type: 'map',
contentPane: 'listing',
listing: 'import'
}, {
configModel: configModel,
userModel: this.userModel,
userActions: {},
pollingModel: new Backbone.Model()
});
this.view = new ListingView({
userModel: this.userModel,
createModel: this.createModel,
configModel: configModel,
privacyModel: new Backbone.Model({
privacy: 'PRIVATE'
}),
guessingModel: new Backbone.Model({
guessing: true
})
});
spyOn(this.createModel._tablesCollection, 'fetch');
spyOn(this.createModel, 'bind').and.callThrough();
spyOn(this.createModel._visualizationFetchModel, 'bind').and.callThrough();
this.view.render();
});
afterEach(function () {
jasmine.Ajax.uninstall();
});
it('should render correctly', function () {
this.createModel.set('option', 'listing');
expect(_.size(this.view._subviews)).toBe(1);
});
it('should have no leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
@@ -0,0 +1,226 @@
var Backbone = require('backbone');
var CreateModel = require('builder/components/modals/add-layer/add-layer-model');
var VisualizationsFetchModel = require('builder/data/visualizations-fetch-model');
var TablesCollection = require('builder/data/tables-collection');
var UserModel = require('builder/data/user-model');
var ConfigModel = require('builder/data/config-model');
var NavigationView = require('builder/components/modals/add-layer/content/navigation-view');
var _ = require('underscore');
describe('components/modals/add-layer/navigation-view', function () {
beforeEach(function () {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*/viz\?.*'))
.andReturn({ status: 200 });
this.configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.userModel = new UserModel({
username: 'pepe',
remaining_byte_quota: 10
}, {
configModel: this.configModel
});
this.createModel = new CreateModel(
{},
{
configModel: this.configModel,
userModel: this.userModel,
userActions: {},
pollingModel: new Backbone.Model()
}
);
this.visFetchModel = new VisualizationsFetchModel({
content_type: 'datasets'
});
this.tablesCollection = new TablesCollection([], {
configModel: this.configModel
});
this.createView = function () {
this.view = new NavigationView({
configModel: this.configModel,
userModel: this.userModel,
routerModel: this.visFetchModel,
createModel: this.createModel,
tablesCollection: this.tablesCollection
});
};
this.createView();
this.createModel.set('type', 'dataset');
spyOn(this.createModel, 'bind').and.callThrough();
spyOn(this.visFetchModel, 'bind').and.callThrough();
spyOn(this.tablesCollection, 'bind').and.callThrough();
this.view.render();
});
afterEach(function () {
jasmine.Ajax.uninstall();
this.view.remove();
});
it('should render correctly', function () {
expect(this.view.$('.Filters-searchLink').length).toBe(1);
expect(this.view.$('.js-typeItem').length).toBe(3);
});
// TBD: create dataset dialog still not implemented
// it('should not render several links when create dialog type is dataset', function () {
// this.createModel = new CreateDatasetModel({},
// {js-typeItem
// visMap: {},
// configModel: this.configModel,
// userModel: this.userModel
// });
// this.createView();
// this.view.render();
// expect(this.view.$('.Filters-searchLink').length).toBe(1);
// expect(this.view.$('.Filters-typeLink').length).toBe(3);
// });
describe('when data library is enabled', function () {
describe('.render', function () {
it('should render correctly', function () {
spyOn(this.view._configModel, 'dataLibraryEnabled').and.returnValue(true);
this.view.render();
expect(this.view.$('.js-typeItem').length).toBe(4);
expect(this.innerHTML()).toContain('components.modals.add-layer.navigation.data-library');
});
});
});
describe('.initBinds', function () {
it('should render when listing state changes', function () {
spyOn(this.view, 'render');
this.view._initBinds();
this.createModel.trigger('change:listing');
expect(this.view.render).toHaveBeenCalled();
});
it('should render when router model changes', function () {
spyOn(this.view, 'render');
this.view._initBinds();
this.visFetchModel.trigger('change');
expect(this.view.render).toHaveBeenCalled();
});
it('should render when collection changes', function () {
spyOn(this.view, 'render');
this.view._initBinds();
this.tablesCollection.trigger('sync');
expect(this.view.render).toHaveBeenCalled();
});
it('should call _onChangeIsSearchEnabled when model isSearchEnabled changes', function () {
spyOn(this.view, '_onChangeIsSearchEnabled');
this.view._initBinds();
this.view.model.trigger('change:isSearchEnabled');
expect(this.view._onChangeIsSearchEnabled).toHaveBeenCalled();
});
});
it('should create from scratch when create empty button is clicked', function () {
spyOn(this.createModel, 'createFromScratch');
this.userModel.set('account_type', 'ACCOUNT');
this.view.render();
this.view.$('.js-create-empty').click();
expect(this.createModel.createFromScratch).toHaveBeenCalled();
});
it('should disable connect-dataset option when user is out of bytes quota', function () {
this.createModel.set('listing', 'datasets');
this.userModel.set('remaining_byte_quota', 0);
this.view.render();
expect(this.view.$('.js-connect').hasClass('is-disabled')).toBeTruthy();
this.view.$('.js-connect').click();
expect(this.createModel.get('listing')).not.toBe('import');
});
it('should disable connect-dataset option when user Individual and is out of datasets quota', function () {
this.createModel.set('listing', 'datasets');
this.userModel.set('account_type', 'Individual');
this.userModel.set('table_quota', 40);
this.userModel.set('table_count', 40);
this.view.render();
expect(this.view.$('.js-connect').hasClass('is-disabled')).toBeTruthy();
this.view.$('.js-connect').click();
expect(this.createModel.get('listing')).not.toBe('import');
});
it('should disable create empty dataset option when user is out of bytes quota', function () {
spyOn(this.createModel, 'createFromScratch');
this.createModel.set('listing', 'datasets');
this.userModel.set('remaining_byte_quota', 0);
this.view.render();
expect(this.view.$('.js-create-empty').hasClass('is-disabled')).toBeTruthy();
this.view.$('.js-create-empty').click();
expect(this.createModel.createFromScratch).not.toHaveBeenCalled();
});
it('should disable create empty dataset option when user Individual and is out of datasets quota', function () {
spyOn(this.createModel, 'createFromScratch');
this.createModel.set('listing', 'datasets');
this.userModel.set('account_type', 'Individual');
this.userModel.set('table_quota', 40);
this.userModel.set('table_count', 40);
this.view.render();
expect(this.view.$('.js-create-empty').hasClass('is-disabled')).toBeTruthy();
this.view.$('.js-create-empty').click();
expect(this.createModel.createFromScratch).not.toHaveBeenCalled();
});
it('should search starting for the first page', function () {
this.visFetchModel.set('page', 2);
this.view.$('.js-search-input').val('test');
this.view.$('.js-search-form').submit();
expect(this.visFetchModel.get('page')).toBe(1);
});
it('should change router model and listing model when a link is clicked', function () {
spyOn(this.view._configModel, 'dataLibraryEnabled').and.returnValue(true);
this.view.render();
var routerChanged = false;
var stateChanged = false;
function resetFlags () {
routerChanged = false;
stateChanged = false;
}
this.visFetchModel.bind('change', function (m, c) {
if (!_.isEmpty(m.changed)) routerChanged = true;
});
this.createModel.bind('change', function (m, c) {
if (!_.isEmpty(m.changed)) stateChanged = true;
});
resetFlags();
this.view.$('.js-connect').click();
expect(routerChanged).toBeFalsy();
expect(stateChanged).toBeTruthy();
resetFlags();
this.view.$('.js-library').click();
expect(routerChanged).toBeTruthy();
expect(stateChanged).toBeTruthy();
});
it('should have no leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
@@ -0,0 +1,36 @@
var Backbone = require('backbone');
/**
* Common test cases for a create listing view model.
* Expected to be called in the context of the top-describe closure of an option model, e.g.:
* describe('...', function() {
* beforeEach(function() {
* this.model = new Model({});
* });
* sharedForCreateListingViewModel.call(this);
*/
module.exports = function () {
it('should have listing attr representing current listing view', function () {
expect(this.model.get('listing')).toEqual(jasmine.any(String));
expect(this.model.get('listing')).not.toEqual(''); // should have an initial value
});
describe('.showLibrary', function () {
it('should return a boolean for if data library should be loaded', function () {
expect(this.model.showLibrary()).toEqual(jasmine.any(Boolean));
});
});
describe('.canSelect', function () {
it('should return a boolean for if user can select an item (more)', function () {
var datasetModel = new Backbone.Model();
expect(this.model.canSelect(datasetModel)).toEqual(jasmine.any(Boolean));
});
});
describe('.showDatasets', function () {
it('should return a boolean for whether to show datasets or not', function () {
expect(this.model.showDatasets()).toEqual(jasmine.any(Boolean));
});
});
};
@@ -0,0 +1,16 @@
/**
* Common test cases for a import view model in a create listing.
* Expected to be called in the context of the top-describe closure of an option model, e.g.:
* describe('...', function() {
* beforeEach(function() {
* this.model = new Model({});
* });
* sharedForImportViewModel.call(this);
*/
module.exports = function () {
describe('.setActiveImportPane', function () {
it('should be a function to set which pane is currently active', function () {
expect(this.model.setActiveImportPane).toEqual(jasmine.any(Function));
});
});
};
@@ -0,0 +1,264 @@
var _ = require('underscore');
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
var AddWidgetsView = require('builder/components/modals/add-widgets/add-widgets-view');
describe('components/modals/add-widgets/add-widgets-view', function () {
var LOADING_TITLE = 'loading-';
var view;
var configModel;
var userModel;
var analysisDefinitionNodesCollection;
var layerDefinitionsCollection;
var querySchemaModel0;
var querySchemaModel1;
var querySchemaModel2;
var modalModel;
function createViewFn (options) {
var defaultOptions;
configModel = new ConfigModel({
base_url: '/u/pepe'
});
userModel = new UserModel({}, {
configModel: configModel
});
analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
configModel: configModel,
userModel: userModel
});
layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
configModel: configModel,
userModel: userModel,
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
mapId: 'map-123',
stateDefinitionModel: {}
});
analysisDefinitionNodesCollection.add({
id: 'a0',
type: 'source',
query: 'SELECT * FROM table_name'
});
analysisDefinitionNodesCollection.add({
id: 'a1',
type: 'buffer',
radio: 300,
source: 'a0'
});
analysisDefinitionNodesCollection.add({
id: 'a2',
type: 'buffer',
radio: 300,
source: 'a1'
});
querySchemaModel0 = analysisDefinitionNodesCollection.at(0).querySchemaModel;
querySchemaModel1 = analysisDefinitionNodesCollection.at(1).querySchemaModel;
querySchemaModel2 = analysisDefinitionNodesCollection.at(2).querySchemaModel;
querySchemaModel0.set({ status: 'fetched', query: 'SELECT * FROM table_name' });
querySchemaModel1.set({ status: 'unavailable' });
querySchemaModel2.set({ query: 'SELECT * FROM test' });
querySchemaModel2.set({ status: 'fetching' });
spyOn(querySchemaModel0, 'fetch');
spyOn(querySchemaModel1, 'fetch');
spyOn(querySchemaModel2, 'fetch');
modalModel = new Backbone.Model();
defaultOptions = {
modalModel: modalModel,
userActions: {
saveWidgetOption: function (widgetOptionModels) {
return Promise.resolve(widgetOptionModels);
},
updateWidgetsOrder: function (widgetOptionModels) {
return Promise.resolve(widgetOptionModels);
},
goToEditWidget: function () {}
},
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
layerDefinitionsCollection: layerDefinitionsCollection,
widgetDefinitionsCollection: new Backbone.Collection()
};
return new AddWidgetsView(_.extend(defaultOptions, options));
}
beforeEach(function () {
view = createViewFn();
view.render();
});
it('should have no leaks', function () {
expect(view).toHaveNoLeaks();
});
it('should show loading msg until all are fetched', function () {
expect(view.$('.js-body').html()).toContain(LOADING_TITLE);
});
describe('when all layer tables are fetched', function () {
beforeEach(function () {
querySchemaModel2.set('status', 'fetched');
});
it('should render the content view', function () {
expect(view.$('.js-body').html()).not.toContain(LOADING_TITLE);
});
});
describe('when the user clicks on continue', function () {
beforeEach(function () {
spyOn(view, '_hasFetchedAllQuerySchemas').and.returnValue(true);
view.render();
});
it('should call _saveSelectedWidgets if there are selected widgets', function () {
var saveSelectedWidgetsSpy = spyOn(view, '_saveSelectedWidgets').and.callThrough();
view._optionsCollection.add(new Backbone.Model({
id: '1',
selected: true
}));
view._onContinue();
expect(saveSelectedWidgetsSpy).toHaveBeenCalled();
});
it('should not call _saveSelectedWidgets if there are not selected widgets', function () {
var saveSelectedWidgetsSpy = spyOn(view, '_saveSelectedWidgets').and.returnValue(true);
view._optionsCollection.add(new Backbone.Model({
id: '2',
selected: false
}));
view._onContinue();
expect(saveSelectedWidgetsSpy).not.toHaveBeenCalled();
});
it('should call user actions to update widgets if the _saveSelectedWidgets succeeds', function (done) {
spyOn(view, '_saveSelectedWidgets').and.returnValue(Promise.resolve());
spyOn(view._userActions, 'updateWidgetsOrder');
spyOn(view._userActions, 'goToEditWidget');
view._optionsCollection.add(new Backbone.Model({
id: '1',
selected: true
}));
view._onContinue();
setTimeout(function () {
expect(view._userActions.updateWidgetsOrder).toHaveBeenCalled();
expect(view._userActions.goToEditWidget).toHaveBeenCalled();
done();
}, 0);
});
it('should call user actions to update widgets if the _saveSelectedWidgets fails', function (done) {
spyOn(view, '_saveSelectedWidgets').and.returnValue(Promise.reject());
spyOn(view._userActions, 'updateWidgetsOrder');
spyOn(view._userActions, 'goToEditWidget');
view._optionsCollection.add(new Backbone.Model({
id: '1',
selected: true
}));
view._onContinue();
setTimeout(function () {
expect(view._userActions.updateWidgetsOrder).toHaveBeenCalled();
expect(view._userActions.goToEditWidget).not.toHaveBeenCalled();
done();
}, 0);
});
});
describe('when the selected widgets are selected to be saved', function () {
it('should call saveWidgetOption for each selectedOptionModel', function () {
var saveWidgetOptionSpy = spyOn(view._userActions, 'saveWidgetOption').and.callThrough();
var widgetModel_1 = new Backbone.Model({
id: '1',
selected: true
});
var widgetModel_2 = new Backbone.Model({
id: '2',
selected: true
});
view._saveSelectedWidgets([widgetModel_1, widgetModel_2]);
expect(saveWidgetOptionSpy).toHaveBeenCalledWith(widgetModel_1);
expect(saveWidgetOptionSpy).toHaveBeenCalledWith(widgetModel_2);
});
it('should return an async response that succeeds if all the widgets can be saved', function (done) {
var widgetModel_1 = new Backbone.Model({
id: '1',
selected: false
});
var widgetModel_2 = new Backbone.Model({
id: '2',
selected: true
});
view._saveSelectedWidgets([widgetModel_1, widgetModel_2])
.then(function (response) {
expect(response.length).toEqual(2);
done();
});
});
it('should return an async response that fails if one of the widgets can not be saved', function (done) {
var errorResponse = 'Error';
view = createViewFn({userActions: {
saveWidgetOption: function () {
return Promise.reject(errorResponse);
},
updateWidgetsOrder: function () {
return Promise.resolve({});
},
goToEditWidget: function () {}
}});
view.render();
var widgetModel_1 = new Backbone.Model({
id: '1',
selected: false
});
var widgetModel_2 = new Backbone.Model({
id: '2',
selected: true
});
view._saveSelectedWidgets([widgetModel_1, widgetModel_2])
.catch(function (response) {
expect(response).toEqual(errorResponse);
done();
});
});
});
});
@@ -0,0 +1,69 @@
var Backbone = require('backbone');
var BodyView = require('builder/components/modals/add-widgets/body-view');
var WidgetOptionModel = require('builder/components/modals/add-widgets/widget-option-model');
var widgetsTypes = require('builder/components/modals/add-widgets/widgets-types');
describe('components/modals/add-widgets/body-view', function () {
beforeEach(function () {
var layerDefinitionModel = new Backbone.Model();
layerDefinitionModel.getColor = function () { return '#fabada'; };
layerDefinitionModel.getName = function () { return 'layer'; };
layerDefinitionModel.getTableName = function () { return 'table_name'; };
var analysisDefinitionNodeModel = new Backbone.Model({
id: 'a1'
});
analysisDefinitionNodeModel.isSourceType = function () {
return true;
};
var tuples = [{
columnModel: new Backbone.Model(),
analysisDefinitionNodeModel: analysisDefinitionNodeModel,
layerDefinitionModel: layerDefinitionModel
}];
this.optionsCollection = new Backbone.Collection([
new WidgetOptionModel({
type: 'category',
layer_index: 0,
tuples: tuples
}),
new WidgetOptionModel({
type: 'histogram',
layer_index: 0,
tuples: tuples
}),
new WidgetOptionModel({
type: 'formula',
layer_index: 0,
tuples: tuples
}),
new WidgetOptionModel({
type: 'time-series',
layer_index: 0,
tuples: tuples
})
]);
this.view = new BodyView({
optionsCollection: this.optionsCollection,
widgetsTypes: widgetsTypes
});
this.view = this.view.render();
});
it('should have no leaks', function () {
expect(this.view).toHaveNoLeaks();
});
it('should render fine', function () {
expect(this.view).toBeDefined();
});
it('should create tab pane items when there is at least one option of that type', function () {
expect(this.view.$el.html()).toContain('formula');
expect(this.view.$el.html()).toContain('category');
expect(this.view.$el.html()).toContain('histogram');
expect(this.view.$el.html()).toContain('time-series');
});
});
@@ -0,0 +1,64 @@
var _ = require('underscore');
var Backbone = require('backbone');
var CategoryOptionModel = require('builder/components/modals/add-widgets/category/category-option-model');
describe('components/modals/add-widgets/category/category-option-model', function () {
var model;
var defaultOptions;
var widgetDefinitionsCollection;
var layerDefinitionModel;
var analysisDefinitionNodeModel;
var defaultTuples;
var columnModel;
var createModelFn = function (options, optionsTuples) {
widgetDefinitionsCollection = new Backbone.Collection({});
widgetDefinitionsCollection.addWidget = function () {
return true;
};
layerDefinitionModel = new Backbone.Model({
id: 'layerDefinitionModel1',
style: new Backbone.Model()
});
columnModel = new Backbone.Model({
name: 'line'
});
analysisDefinitionNodeModel = new Backbone.Model({
id: 'analysesDefinitionNodeModel1'
});
defaultTuples = [{
columnModel: columnModel,
layerDefinitionModel: layerDefinitionModel,
analysisDefinitionNodeModel: analysisDefinitionNodeModel
}];
defaultOptions = {
layer_index: '0',
title: 'default-title',
tuples: _.extend(defaultTuples, optionsTuples)
};
return new CategoryOptionModel(_.extend(defaultOptions, options));
};
beforeEach(function () {
model = createModelFn();
});
it('should have a "category" type by default', function () {
expect(model.get('type')).toEqual('category');
});
it('should return the result of the "addWidget" function from the given widgetDefinitionsCollection', function () {
var addWidgetSpy = spyOn(widgetDefinitionsCollection, 'addWidget').and.returnValue(true);
model.save(widgetDefinitionsCollection);
expect(addWidgetSpy).toHaveBeenCalled();
});
});
@@ -0,0 +1,195 @@
var _ = require('underscore');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
var createTuplesItems = require('builder/components/modals/add-widgets/create-tuples-items');
describe('components/modals/add-widgets/create-tuples-items', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var userModel = new UserModel({}, {
configModel: configModel
});
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
configModel: configModel,
userModel: userModel
});
this.analysisDefinitionNodesCollection.add({
id: 'a0',
type: 'source',
query: 'SELECT * FROM table_name'
});
this.analysisDefinitionNodesCollection.add({
id: 'b0',
type: 'source',
query: 'SELECT * FROM table_name_two'
});
this.analysisDefinitionNodesCollection.add({
id: 'a1',
type: 'buffer',
radio: 300,
source: 'a0'
});
this.analysisDefinitionNodesCollection.add({
id: 'b1',
type: 'buffer',
radio: 300,
source: 'a1'
});
this.analysisDefinitionNodesCollection.add({
id: 'secondary_source', // created when selecting secondary nodes, so technically orphaned if not used later.
type: 'source',
query: 'SELECT * FROM secondary_source'
});
this.layerDefinitionsCollection = new LayerDefinitionsCollection([
{
id: 'l0',
kind: 'tiled'
}, {
id: 'l1',
kind: 'carto',
options: {
table_name: 'foo_table'
}
}, {
id: 'l2',
kind: 'carto',
options: {
table_name: 'bar_table'
}
}
], {
configModel: configModel,
userModel: userModel,
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
mapId: 'm-123',
stateDefinitionModel: {}
});
var a1 = this.analysisDefinitionNodesCollection.get('a1');
a1.querySchemaModel.columnsCollection.reset([
{
name: 'cartodb_id',
type: 'number'
}, {
name: 'county_name',
type: 'string'
}, {
name: 'the_geom',
type: 'geom'
}, {
name: 'the_geom_webmercator',
type: 'geom'
}, {
name: 'pop_count',
type: 'number'
}, {
name: 'created_at',
type: 'date'
}, {
name: 'updated_at',
type: 'date'
}
]);
var b1 = this.analysisDefinitionNodesCollection.get('b1');
b1.querySchemaModel.columnsCollection.reset([
{
name: 'cartodb_id',
type: 'number'
}, {
name: 'pop_count',
type: 'number'
}
]);
var secondary = this.analysisDefinitionNodesCollection.get('secondary_source');
secondary.querySchemaModel.columnsCollection.reset([
{
name: 'only_for_secondary',
type: 'number'
}
]);
a1.queryGeometryModel.isDone = function () {
return true;
};
a1.queryGeometryModel.set('simple_geom', true);
b1.queryGeometryModel.isDone = function () {
return true;
};
b1.queryGeometryModel.set('simple_geom', true);
this.tuplesItems = createTuplesItems(this.analysisDefinitionNodesCollection, this.layerDefinitionsCollection);
});
it('should return an object', function () {
expect(this.tuplesItems).toEqual(jasmine.any(Object));
});
it('should contain an item for each column name+type match', function () {
expect(this.tuplesItems['county_name-string'].length).toEqual(1);
expect(this.tuplesItems['pop_count-number'].length).toEqual(2);
});
it('should not add items for blacklisted columns', function () {
var keys = _.keys(this.tuplesItems).toString();
expect(keys).not.toContain('the_geom');
expect(keys).not.toContain('the_geom_webmercator');
expect(keys).not.toContain('created_at');
expect(keys).not.toContain('updated_at');
});
it('should not contain a tuple when no geometry data', function () {
var b1 = this.analysisDefinitionNodesCollection.get('b1');
b1.queryGeometryModel.isDone = function () {
return false;
};
b1.queryGeometryModel.set('simple_geom', false);
this.tuplesItems = createTuplesItems(this.analysisDefinitionNodesCollection, this.layerDefinitionsCollection);
var tuples = this.tuplesItems['pop_count-number'];
expect(tuples.length).toBe(1);
expect(tuples[0].layerDefinitionModel.id).toEqual('l1');
});
it('should contain a tuple of column+layer definition in each item', function () {
var tuples = this.tuplesItems['pop_count-number'];
expect(tuples[0].columnModel).toBeDefined();
expect(tuples[0].columnModel.get('name')).toEqual('pop_count');
expect(tuples[0].layerDefinitionModel.id).toEqual('l1');
expect(tuples[1].columnModel).toBeDefined();
expect(tuples[1].columnModel.get('name')).toEqual('pop_count');
expect(tuples[1].layerDefinitionModel.id).toEqual('l2');
});
it('should skip nodes that does not have an owning layer', function () {
for (var columnName in this.tuplesItems) {
var tuples = this.tuplesItems[columnName];
tuples.forEach(function (tuple) {
expect(tuple.columnModel).toBeDefined('a tuple for ' + columnName + ' is missing columnModel');
expect(tuple.layerDefinitionModel).toBeDefined('a tuple for ' + columnName + ' is missing layerDefinitionModel');
expect(tuple.analysisDefinitionNodeModel).toBeDefined('a tuple for ' + columnName + ' is missing analysisDefinitionModel');
}, this);
}
});
});
@@ -0,0 +1,64 @@
var _ = require('underscore');
var Backbone = require('backbone');
var FormulaOptionModel = require('builder/components/modals/add-widgets/formula/formula-option-model');
describe('components/modals/add-widgets/formula/formula-option-model', function () {
var model;
var defaultOptions;
var widgetDefinitionsCollection;
var layerDefinitionModel;
var analysisDefinitionNodeModel;
var defaultTuples;
var columnModel;
var createModelFn = function (options, optionsTuples) {
widgetDefinitionsCollection = new Backbone.Collection({});
widgetDefinitionsCollection.addWidget = function () {
return true;
};
layerDefinitionModel = new Backbone.Model({
id: 'layerDefinitionModel1',
style: new Backbone.Model()
});
columnModel = new Backbone.Model({
name: 'line'
});
analysisDefinitionNodeModel = new Backbone.Model({
id: 'analysesDefinitionNodeModel1'
});
defaultTuples = [{
columnModel: columnModel,
layerDefinitionModel: layerDefinitionModel,
analysisDefinitionNodeModel: analysisDefinitionNodeModel
}];
defaultOptions = {
layer_index: '0',
title: 'default-title',
tuples: _.extend(defaultTuples, optionsTuples)
};
return new FormulaOptionModel(_.extend(defaultOptions, options));
};
beforeEach(function () {
model = createModelFn();
});
it('should have a "formula" type by default', function () {
expect(model.get('type')).toEqual('formula');
});
it('should return the result of the "addWidget" function from the given widgetDefinitionsCollection', function () {
var addWidgetSpy = spyOn(widgetDefinitionsCollection, 'addWidget').and.returnValue(true);
model.save(widgetDefinitionsCollection);
expect(addWidgetSpy).toHaveBeenCalled();
});
});
@@ -0,0 +1,64 @@
var _ = require('underscore');
var Backbone = require('backbone');
var HistogramOptionModel = require('builder/components/modals/add-widgets/histogram/histogram-option-model');
describe('components/modals/add-widgets/histogram/histogram-option-model', function () {
var model;
var defaultOptions;
var widgetDefinitionsCollection;
var layerDefinitionModel;
var analysisDefinitionNodeModel;
var defaultTuples;
var columnModel;
var createModelFn = function (options, optionsTuples) {
widgetDefinitionsCollection = new Backbone.Collection({});
widgetDefinitionsCollection.addWidget = function () {
return true;
};
layerDefinitionModel = new Backbone.Model({
id: 'layerDefinitionModel1',
style: new Backbone.Model()
});
columnModel = new Backbone.Model({
name: 'line'
});
analysisDefinitionNodeModel = new Backbone.Model({
id: 'analysesDefinitionNodeModel1'
});
defaultTuples = [{
columnModel: columnModel,
layerDefinitionModel: layerDefinitionModel,
analysisDefinitionNodeModel: analysisDefinitionNodeModel
}];
defaultOptions = {
layer_index: '0',
title: 'default-title',
tuples: _.extend(defaultTuples, optionsTuples)
};
return new HistogramOptionModel(_.extend(defaultOptions, options));
};
beforeEach(function () {
model = createModelFn();
});
it('should have a "histogram" type by default', function () {
expect(model.get('type')).toEqual('histogram');
});
it('should return the result of the "addWidget" function from the given widgetDefinitionsCollection', function () {
var addWidgetSpy = spyOn(widgetDefinitionsCollection, 'addWidget').and.returnValue(true);
model.save(widgetDefinitionsCollection);
expect(addWidgetSpy).toHaveBeenCalled();
});
});
@@ -0,0 +1,143 @@
var $ = require('jquery');
var Backbone = require('backbone');
var LayerSelectorView = require('builder/components/modals/add-widgets/layer-selector-view');
describe('components/modals/add-widgets/layer-selector-view', function () {
var $el;
beforeEach(function () {
$el = $('<div class="js-WidgetList-item"></div>');
this.columnModel1 = new Backbone.Model({
name: 'col',
type: 'string'
});
this.analysisDefinitionModel1 = new Backbone.Model({ id: 'a1' });
this.analysisDefinitionModel2 = new Backbone.Model({ id: 'a2' });
this.analysisDefinitionModel1.isSourceType = this.analysisDefinitionModel2.isSourceType = function () {
return true;
};
this.layerDefinitionModel1 = new Backbone.Model({
table_name: 'foo'
});
this.columnModel2 = new Backbone.Model({
name: 'col',
type: 'string'
});
this.layerDefinitionModel2 = new Backbone.Model({
table_name: 'bar'
});
this.layerDefinitionModel1.getColor = this.layerDefinitionModel2.getColor = function () {
return '#fabada';
};
this.layerDefinitionModel1.getName = this.layerDefinitionModel2.getName = function () {
return this.get('table_name');
};
this.layerDefinitionModel1.getTableName = this.layerDefinitionModel2.getTableName = function () {
return this.get('table_name');
};
this.model = new Backbone.Model({
layer_index: 0,
tuples: [{
columnModel: this.columnModel1,
layerDefinitionModel: this.layerDefinitionModel1,
analysisDefinitionNodeModel: this.analysisDefinitionModel1
}, {
columnModel: this.columnModel2,
layerDefinitionModel: this.layerDefinitionModel2,
analysisDefinitionNodeModel: this.analysisDefinitionModel2
}]
});
this.view = new LayerSelectorView({
model: this.model
});
this.view = this.view.render();
});
afterEach(function () {
$el.remove();
});
it('should have no leaks', function () {
expect(this.view).toHaveNoLeaks();
});
it('should render table name and node id in the selected item', function () {
expect(this.view).toBeDefined();
expect(this.view.$el.html()).toContain('foo');
expect(this.view.$el.html()).toContain('a1');
});
it('should render table name and node id in the custom list items', function () {
this.view._selectView._onButtonClick();
var customList = this.view.$('.js-list').html();
expect(customList).toContain('foo');
expect(customList).toContain('a1');
expect(customList).toContain('bar');
expect(customList).toContain('a2');
});
it('should change the selected item', function () {
expect(this.model.get('layer_index')).toEqual(0);
});
it('should stop event propagation when element is clicked', function () {
var eventMock = {
stopPropagation: jasmine.createSpy()
};
this.view._onClick(eventMock);
expect(eventMock.stopPropagation).toHaveBeenCalled();
});
describe('when a new option is selected', function () {
beforeEach(function () {
this.view._selectView.setValue(1);
this.view._selectView.trigger('change');
});
it('should change the selected item', function () {
expect(this.model.get('layer_index')).toEqual(1);
});
});
describe('._onMouseOver', function () {
it('should set highlighted to true', function () {
expect(this.view._stateModel.get('highlighted')).toBe(false);
this.view._onMouseOver();
expect(this.view._stateModel.get('highlighted')).toBe(true);
});
});
describe('._onMouseOut', function () {
it('should set highlighted to false', function () {
this.view._stateModel.set({ highlighted: true }, { silent: true });
this.view._onMouseOut();
expect(this.view._stateModel.get('highlighted')).toBe(false);
});
});
describe('._toggleHover', function () {
it('should toggle highlighted', function () {
$el.append(this.view.el);
this.view._stateModel.set('highlighted', true, { silent: true });
this.view._toggleHover();
expect($el.hasClass('is-hover')).toBe(true);
this.view._stateModel.set('highlighted', false, { silent: true });
this.view._toggleHover();
expect($el.hasClass('is-hover')).toBe(false);
});
});
});
@@ -0,0 +1,64 @@
var _ = require('underscore');
var Backbone = require('backbone');
var TimeSeriesOptionModel = require('builder/components/modals/add-widgets/time-series/time-series-option-model');
describe('components/modals/add-widgets/time-series/time-series-option-model', function () {
var model;
var defaultOptions;
var widgetDefinitionsCollection;
var layerDefinitionModel;
var analysisDefinitionNodeModel;
var defaultTuples;
var columnModel;
var createModelFn = function (options, optionsTuples) {
widgetDefinitionsCollection = new Backbone.Collection({});
widgetDefinitionsCollection.addWidget = function () {
return true;
};
layerDefinitionModel = new Backbone.Model({
id: 'layerDefinitionModel1',
style: new Backbone.Model()
});
columnModel = new Backbone.Model({
name: 'line'
});
analysisDefinitionNodeModel = new Backbone.Model({
id: 'analysesDefinitionNodeModel1'
});
defaultTuples = [{
columnModel: columnModel,
layerDefinitionModel: layerDefinitionModel,
analysisDefinitionNodeModel: analysisDefinitionNodeModel
}];
defaultOptions = {
layer_index: '0',
title: 'default-title',
tuples: _.extend(defaultTuples, optionsTuples)
};
return new TimeSeriesOptionModel(_.extend(defaultOptions, options));
};
beforeEach(function () {
model = createModelFn();
});
it('should have a "time-series" type by default', function () {
expect(model.get('type')).toEqual('time-series');
});
it('should return the result of the "addWidget" function from the given widgetDefinitionsCollection', function () {
var addWidgetSpy = spyOn(widgetDefinitionsCollection, 'addWidget').and.returnValue(true);
model.save(widgetDefinitionsCollection);
expect(addWidgetSpy).toHaveBeenCalled();
});
});
@@ -0,0 +1,37 @@
var _ = require('underscore');
var Backbone = require('backbone');
var widgetsTypes = require('builder/components/modals/add-widgets/widgets-types');
var TimeSeriesNoneOptionModel = require('builder/components/modals/add-widgets/time-series/time-series-none-option-model');
describe('components/modals/add-widgets/widgets-types', function () {
describe('all items', function () {
it('should have a type prop', function () {
widgetsTypes.forEach(function (d) {
expect(d.type).toMatch(/\w+/);
});
});
it('should have a createTabPaneItem method', function () {
widgetsTypes.forEach(function (d) {
expect(d.createTabPaneItem).toEqual(jasmine.any(Function));
});
});
});
describe('times-series', function () {
beforeEach(function () {
this.d = _.find(widgetsTypes, function (d) {
return d.type === 'time-series';
});
});
describe('.createOptionModels', function () {
it('should always create a none-option even if there are no tuples items', function () {
var widgetDefinitionsCollection = new Backbone.Collection();
var models = this.d.createOptionModels([], widgetDefinitionsCollection);
expect(models.length).toEqual(1);
expect(models[0]).toEqual(jasmine.any(TimeSeriesNoneOptionModel));
});
});
});
});
@@ -0,0 +1,42 @@
var Backbone = require('backbone');
var DatasetMetadataView = require('builder/components/modals/dataset-metadata/dataset-metadata-view');
describe('components/modals/dataset-metadata/dataset-metadata-view', function () {
beforeEach(function () {
this.view = new DatasetMetadataView({
modalModel: new Backbone.Model(),
configModel: new Backbone.Model(),
visDefinitionModel: new Backbone.Model(),
isLocked: false
});
});
it('._getMetadataName sanitizes input', function () {
this.view._visMetadataModel.set('name', "><script>alert('yep');</script>");
var name = this.view._getMetadataName();
expect(name).toEqual('&gt;');
});
it('._getMetadataDescription sanitizes input', function () {
this.view._visMetadataModel.set('description', "<img src='http://emojipedia-us.s3.amazonaws.com/cache/b8/b4/b8b4e86a110557e6b6d666c9cf6d6cc8.png' />");
var name = this.view._getMetadataDescription();
expect(name).toEqual('<img>');
});
it('._getMetadataTags sanitizes input', function () {
this.view._visMetadataModel.set('tags', [
"<img src='http://emojipedia-us.s3.amazonaws.com/cache/b8/b4/b8b4e86a110557e6b6d666c9cf6d6cc8.png' />",
"><script>alert('yep');</script>"
]);
var tags = this.view._getMetadataTags();
expect(tags.length).toBe(2);
expect(tags[0]).toEqual('<img>');
expect(tags[1]).toEqual('&gt;');
});
});
@@ -0,0 +1,43 @@
var Backbone = require('backbone');
var FormView = require('builder/components/modals/dataset-metadata/form/form-view');
describe('components/modals/dataset-metadata/form-view', function () {
beforeEach(function () {
this.view = new FormView({
visDefinitionModel: new Backbone.Model(),
visMetadataModel: new Backbone.Model(),
configModel: new Backbone.Model(),
isLocked: false
});
this.view.model = {};
});
it('._commitView should call _sanitizeName', function () {
spyOn(this.view, '_sanitizeName');
var name = 'something';
var view = {
model: {
changed: {name: name},
get: function () {
return name;
}
},
commit: function () {}
};
this.view._commitView(view);
expect(this.view._sanitizeName).toHaveBeenCalledWith(name);
});
it('._sanitizeName should sanitize input and set metadata model name', function () {
this.view._sanitizeName('<script>');
expect(this.view._visMetadataModel.get('name')).toEqual('');
this.view._sanitizeName('something');
expect(this.view._visMetadataModel.get('name')).toEqual('something');
this.view._sanitizeName(null);
expect(this.view._visMetadataModel.get('name')).toEqual('');
});
});
@@ -0,0 +1,132 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var VisDefinitionModel = require('builder/data/vis-definition-model');
var EditorVisualizationWarningView = require('builder/components/modals/editor-visualization-warning/editor-visualization-warning-view');
describe('builder/components/modals/editor-visualization-warning', function () {
beforeEach(function () {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*/viz\?.*'))
.andReturn({ status: 200 });
var visDefinitionModel = new VisDefinitionModel({
id: '1234567890',
name: 'Foo Map',
type: 'derived'
}, {
configModel: new ConfigModel({
base_url: '/u/marieta'
})
});
var modalModel = new Backbone.Model();
spyOn(EditorVisualizationWarningView.prototype, '_goToDashboard');
this.view = new EditorVisualizationWarningView({
modalModel: modalModel,
visDefinitionModel: visDefinitionModel
});
this.view.render();
});
afterEach(function () {
jasmine.Ajax.uninstall();
});
it('should have no leaks', function () {
expect(this.view).toHaveNoLeaks();
});
it('should render template', function () {
expect(this.view.$el.html()).toContain('Modal-inner');
});
describe('_onOpen', function () {
it('should call _onOpen when .js-open is clicked', function () {
spyOn(this.view, '_renderLoading');
this.view.$('.js-open').click();
expect(this.view._renderLoading).toHaveBeenCalled();
});
it('should destroy modalModel if the map version update succeeds', function () {
spyOn(this.view._modalModel, 'destroy');
this.view._visDefinitionModel.save = function (a, opts) {
opts.success();
};
this.view.$('.js-open').click();
expect(this.view._modalModel.destroy).toHaveBeenCalled();
});
it('should call _renderError if the map version update fails', function () {
spyOn(this.view, '_renderError');
this.view._visDefinitionModel.save = function (a, opts) {
var e = {
readyState: 4,
responseText: '',
status: 405,
statusText: 'Method Not Allowed'
};
opts.error({model: 'lorem ipsum'}, e);
};
this.view.$('.js-open').click();
expect(this.view._renderError).toHaveBeenCalled();
});
});
describe('_onDuplicate', function () {
it('should call _onDuplicate when .js-duplicate is clicked', function () {
spyOn(this.view, '_renderLoading').and.callFake(function () {
return true;
});
this.view.$('.js-duplicate').click();
expect(this.view._renderLoading).toHaveBeenCalled();
});
it('should redirect to the duplicated map builder view if duplication succeeds', function () {
var newVisModel = new VisDefinitionModel({
id: '111',
name: 'table_1',
privacy: 'PUBLIC',
permission: {}
}, {
configModel: this.view._visDefinitionModel._configModel
});
VisDefinitionModel.prototype.save = function (a, opts) {
opts.success(newVisModel);
};
spyOn(this.view, '_redirectToBuilder');
this.view.$('.js-duplicate').click();
expect(this.view._redirectToBuilder).toHaveBeenCalledWith('/u/marieta/builder/111/');
});
it('should call _renderError if the map version update fails', function () {
spyOn(this.view, '_renderError');
VisDefinitionModel.prototype.save = function (a, opts) {
var e = {
readyState: 4,
responseText: '',
status: 405,
statusText: 'Method Not Allowed'
};
opts.error({model: 'lorem ipsum'}, e);
};
this.view.$('.js-duplicate').click();
expect(this.view._renderError).toHaveBeenCalled();
});
});
describe('_onCancel', function () {
it('should call _onCancel when .js-cancel is clicked', function () {
this.view.$('.js-cancel').click();
expect(EditorVisualizationWarningView.prototype._goToDashboard).toHaveBeenCalled();
});
});
});
@@ -0,0 +1,200 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var ExportView = require('builder/components/modals/export-data/modal-export-data-view');
var MetricsTracker = require('builder/components/metrics/metrics-tracker');
var MetricsTypes = require('builder/components/metrics/metrics-types');
describe('components/modals/export-data/modal-export-data-view', function () {
beforeEach(function () {
this.configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.queryGeometryModel = new Backbone.Model({
status: 'fetched',
simple_geom: 'point',
query: 'select * from wadus'
});
this.querySchemaModel = new Backbone.Model({
query: 'select * from wadus'
});
spyOn(ExportView.prototype, 'submit');
MetricsTracker.init({
userId: 'USER_ID',
visId: 'VIS_ID',
configModel: new Backbone.Model()
});
spyOn(MetricsTracker, 'track');
this.modalModel = new Backbone.Model();
this.view = new ExportView({
configModel: this.configModel,
queryGeometryModel: this.queryGeometryModel,
querySchemaModel: this.querySchemaModel,
canHideColumns: false,
modalModel: this.modalModel,
fromView: 'layer'
});
spyOn(this.view, 'fetchCSV').and.callThrough();
spyOn(this.view, '_fetchGET');
this.view.render();
});
describe('render', function () {
it('should render properly for points', function () {
expect(this.view.$('.Modal-listFormItem').length).toBe(6);
expect(this.view.$('.Modal-listFormItem.is-disabled').length).toBe(0);
expect(this.view.$('[data-format=csv]:checked').length).toBe(1);
});
it('should render properly for lines', function () {
this.queryGeometryModel.set('simple_geom', 'line');
this.view.render();
expect(this.view.$('.Modal-listFormItem').length).toBe(6);
expect(this.view.$('.Modal-listFormItem.is-disabled').length).toBe(0);
expect(this.view.$('[data-format=csv]:checked').length).toBe(1);
});
it('should render properly for polygons', function () {
this.queryGeometryModel.set('simple_geom', 'polygon');
this.view.render();
expect(this.view.$('.Modal-listFormItem').length).toBe(6);
expect(this.view.$('.Modal-listFormItem.is-disabled').length).toBe(0);
expect(this.view.$('[data-format=csv]:checked').length).toBe(1);
});
it('should render properly for no geometries', function () {
this.queryGeometryModel.set('simple_geom', null);
this.view.render();
expect(this.view.$('.Modal-listFormItem').length).toBe(6);
expect(this.view.$('.Modal-listFormItem.is-disabled').length).toBe(5);
expect(this.view.$('[data-format=csv]:disabled').length).toBe(0);
expect(this.view.$('[data-format=csv]:checked').length).toBe(1);
});
});
describe('actions', function () {
describe('confirm', function () {
it('should track a metric if optional layerModel is present', function () {
var layerModel = new Backbone.Model({
id: 'wadus',
source: 'd0',
visible: true,
table_name: 'wadus',
visualization_id: 'wadus',
user_id: 'wadus'
});
layerModel.getName = jasmine.createSpy('getName');
// Create a new view with (optional) layerModel
var view = new ExportView({
layerModel: layerModel,
configModel: this.configModel,
queryGeometryModel: this.queryGeometryModel,
querySchemaModel: this.querySchemaModel,
canHideColumns: false,
modalModel: {},
fromView: 'layer'
});
spyOn(view, 'fetchCSV');
view.render();
view.$('.js-confirm').trigger('click');
expect(MetricsTracker.track).toHaveBeenCalledWith(MetricsTypes.DOWNLOADED_LAYER, {
'from_view': 'layer',
'format': 'csv',
'layer_id': 'wadus',
'source': 'd0',
'visible': true,
'table_name': 'wadus'
});
expect(view.fetchCSV).toHaveBeenCalled();
view.clean();
});
it('should NOT track a metric if layerModel is NOT present', function () {
this.view.$('.js-confirm').trigger('click');
expect(MetricsTracker.track).not.toHaveBeenCalled();
expect(this.view.fetchCSV).toHaveBeenCalled();
});
it('should fetch the file', function () {
this.view.$('.js-confirm').trigger('click');
expect(this.view.fetchCSV).toHaveBeenCalled();
});
it('should always drop the_geom_webmercator', function () {
this.view.$('.js-confirm').trigger('click');
expect(this.view.$('.js-skipfields').val()).toContain('the_geom_webmercator');
});
it('should not drop the_geom for CSV files', function () {
this.view.$('.js-confirm').trigger('click');
expect(this.view.$('.js-skipfields').val()).toBe('the_geom_webmercator');
});
it('should drop the_geom for files that are not CSV', function () {
this.view.$('.js-format[data-format="csv"]').attr('checked', null);
this.view.$('.js-format[data-format="gpkg"]').attr('checked', true);
this.view.$('.js-confirm').trigger('click');
expect(this.view.$('.js-skipfields').val()).toContain('the_geom_webmercator,the_geom');
});
describe('if the view can hide columns', function () {
beforeEach(function () {
this.querySchemaModel.getColumnNames = function () { return ['center']; };
this.view = new ExportView({
configModel: this.configModel,
queryGeometryModel: this.queryGeometryModel,
querySchemaModel: this.querySchemaModel,
canHideColumns: true,
modalModel: {},
fromView: 'layer'
});
spyOn(this.view, '_fetchGET');
spyOn(this.view, '_close');
this.view.render();
});
afterEach(function () {
this.view.clean();
});
it('should not drop center columns if it is not geometry', function () {
this.querySchemaModel.getColumnType = function () { return 'wadus'; };
this.view.$('.js-confirm').trigger('click');
expect(this.view.$('.js-skipfields').val()).not.toContain('center');
});
it('should drop center column if it is geometry-typed', function () {
this.querySchemaModel.getColumnType = function () { return 'geometry'; };
this.view.$('.js-confirm').trigger('click');
expect(this.view.$('.js-skipfields').val()).toContain('center');
});
});
});
describe('cancel', function () {
it('should destroy the model', function () {
spyOn(this.modalModel, 'destroy');
this.view.$('.js-cancel').trigger('click');
expect(this.modalModel.destroy).toHaveBeenCalled();
});
});
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
@@ -0,0 +1,41 @@
var Backbone = require('backbone');
var FormView = require('builder/components/modals/map-metadata/form/form-view');
describe('components/modals/map-metadata/form-view', function () {
beforeEach(function () {
this.view = new FormView({
visDefinitionModel: new Backbone.Model(),
visMetadataModel: new Backbone.Model()
});
this.view.model = {};
});
it('._commitView should call _sanitizeName', function () {
spyOn(this.view, '_sanitizeName');
var name = 'something';
var view = {
model: {
changed: {name: name},
get: function () {
return name;
}
},
commit: function () {}
};
this.view._commitView(view);
expect(this.view._sanitizeName).toHaveBeenCalledWith(name);
});
it('._sanitizeName should sanitize input and set metadata model name', function () {
this.view._sanitizeName('<script>');
expect(this.view._visMetadataModel.get('name')).toEqual('');
this.view._sanitizeName('something');
expect(this.view._visMetadataModel.get('name')).toEqual('something');
this.view._sanitizeName(null);
expect(this.view._visMetadataModel.get('name')).toEqual('');
});
});
@@ -0,0 +1,98 @@
var Backbone = require('backbone');
var MetadataView = require('builder/components/modals/map-metadata/map-metadata-view');
var VisDefinitionModel = require('builder/data/vis-definition-model');
var ConfigModel = require('builder/data/config-model');
describe('components/modals/map-metadata/map-metadata-view', function () {
var view;
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var visDefinitionModel = new VisDefinitionModel({
name: 'Foo Map',
privacy: 'LINK',
updated_at: '2016-06-21T15:30:06+00:00',
type: 'derived',
tags: ['foo', 'bar'],
permission: {}
}, {
configModel: configModel
});
view = new MetadataView({
modalModel: new Backbone.Model(),
visDefinitionModel: visDefinitionModel
});
view.render();
view.$el.appendTo(document.body);
});
afterEach(function () {
var parent = view.el.parentNode;
parent && parent.removeChild(view.el);
view.clean();
});
it('should render properly', function () {
expect(view.$('.Metadata-form').length).toBe(1);
expect(view.$('.js-name-field input').length).toBe(1);
expect(view.$('.js-name-field input').val()).toBe('Foo Map');
expect(view.$('.js-description-field textarea').length).toBe(1);
expect(view.$('.js-tags-field .Form-tagsList').length).toBe(1);
expect(view.$('.tagit-label').length).toBe(2);
expect(view.$('.tagit-label').eq(0).text()).toBe('foo');
expect(view.$('.tagit-label').eq(1).text()).toBe('bar');
});
it('should render error', function () {
view._stateModel.set({status: 'error'});
expect(view.$('.u-errorTextColor').length).toBe(1);
expect(view.$('input[type=password]').length).toBe(0);
expect(view.$('.Tag').length).toBe(0);
expect(view.$('.Modal-actions-button').length).toBe(1);
});
it('should render loading', function () {
view._stateModel.set({status: 'loading'});
expect(view.$('.Modal-actions-button').length).toBe(0);
expect(view.$('input[type=password]').length).toBe(0);
expect(view.$('.js-loader').length).toBe(1);
});
it('._getMetadataName sanitizes input', function () {
view._visMetadataModel.set('name', "><script>alert('yep');</script>");
var name = view._getMetadataName();
expect(name).toEqual('&gt;');
});
it('._getMetadataDescription sanitizes input', function () {
view._visMetadataModel.set('description', "<img src='http://emojipedia-us.s3.amazonaws.com/cache/b8/b4/b8b4e86a110557e6b6d666c9cf6d6cc8.png' />");
var name = view._getMetadataDescription();
expect(name).toEqual('<img>');
});
it('._getMetadataTags sanitizes input', function () {
view._visMetadataModel.set('tags', [
"<img src='http://emojipedia-us.s3.amazonaws.com/cache/b8/b4/b8b4e86a110557e6b6d666c9cf6d6cc8.png' />",
"><script>alert('yep');</script>"
]);
var tags = view._getMetadataTags();
expect(tags.length).toBe(2);
expect(tags[0]).toEqual('<img>');
expect(tags[1]).toEqual('&gt;');
});
it('should not have any leaks', function () {
expect(view).toHaveNoLeaks();
});
});
@@ -0,0 +1,48 @@
var VisDefinitionModel = require('builder/data/vis-definition-model');
var ConfigModel = require('builder/data/config-model');
var saveMetadataMap = require('builder/components/modals/map-metadata/save-metadata-map');
describe('components/modals/map-metadata/save-metadata-map', function () {
var visDefinitionModel;
var successSaveSpy;
var errorSaveSpy;
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
visDefinitionModel = new VisDefinitionModel({
name: 'Foo Map',
privacy: 'LINK',
updated_at: '2016-06-21T15:30:06+00:00',
type: 'derived',
tags: ['foo', 'bar'],
permission: {}
}, {
configModel: configModel
});
successSaveSpy = jasmine.createSpy('successSave');
errorSaveSpy = jasmine.createSpy('erroSave');
spyOn(visDefinitionModel, 'save');
});
it('should rename document title and the suffix should contain | CARTO', function () {
saveMetadataMap({
onSuccess: successSaveSpy,
onError: errorSaveSpy,
visDefinitionModel: visDefinitionModel,
name: 'FOO',
description: 'Wadus',
tags: ['foo', 'bar']
});
expect(visDefinitionModel.save).toHaveBeenCalled();
visDefinitionModel.save.calls.argsFor(0)[1].success();
expect(successSaveSpy).toHaveBeenCalled();
expect(document.title).not.toMatch(/CartoDB/);
expect(document.title).toBe('FOO | 快点');
});
});
@@ -0,0 +1,59 @@
var $ = require('jquery');
var ModalConfirmationView = require('builder/components/modals/confirmation/modal-confirmation-view');
var templateExample = require('builder/components/table/head/modals-templates/rename-table-column.tpl');
var simulateENTERKeyPress = function () {
var e = $.Event('keydown');
e.keyCode = e.which = $.ui.keyCode.ENTER;
$(document).trigger(e);
};
describe('components/modals/modal-confirmation-view', function () {
beforeEach(function () {
this.view = new ModalConfirmationView({
modalModel: {},
template: templateExample,
loadingTitle: 'loading-title',
renderOpts: {
columnName: 'pepito',
newName: 'pepita'
},
runAction: function () {}
});
this.view.render();
});
it('should have no leaks', function () {
expect(this.view).toHaveNoLeaks();
});
describe('binds', function () {
it('should render loading view when ok button is clicked', function () {
spyOn(this.view, '_renderLoadingView');
this.view.$('.js-confirm').click();
expect(this.view._renderLoadingView).toHaveBeenCalled();
});
it('should runAction when ENTER is pressed', function () {
spyOn(this.view, '_runAction');
simulateENTERKeyPress();
expect(this.view._runAction).toHaveBeenCalled();
});
it('should disable ENTER binding when is disabled', function () {
spyOn(this.view, '_runAction');
this.view._disableBinds();
simulateENTERKeyPress();
expect(this.view._runAction).not.toHaveBeenCalled();
});
});
it('should have .js-confirm and .js-cancel present in the template', function () {
expect(this.view.$('.js-confirm').length).toBe(1);
expect(this.view.$('.js-cancel').length).toBe(1);
});
afterEach(function () {
this.view.clean();
});
});
@@ -0,0 +1,58 @@
var ModalViewModel = require('builder/components/modals/modal-view-model');
describe('components/modals/modal-view-model', function () {
beforeEach(function () {
this.createContentViewResult = {};
this.createContentView = jasmine.createSpy('createContentView').and.returnValue(this.createContentViewResult);
this.model = new ModalViewModel({
createContentView: this.createContentView
});
});
it('should allow show/hide modal', function () {
expect(this.model.get('show')).toBe(true);
expect(this.model.isHidden()).toBe(false);
this.model.hide();
expect(this.model.get('show')).toBe(false);
expect(this.model.isHidden()).toBe(true);
this.model.show();
expect(this.model.get('show')).toBe(true);
expect(this.model.isHidden()).toBe(false);
});
describe('.createContentView', function () {
beforeEach(function () {
this.view = this.model.createContentView();
});
it('should create content view given viewModel', function () {
expect(this.createContentView).toHaveBeenCalled();
expect(this.createContentView).toHaveBeenCalledWith(this.model);
});
it('should return created view', function () {
expect(this.view).toBe(this.createContentViewResult);
});
});
describe('.destroy', function () {
beforeEach(function () {
this.destroySpy = jasmine.createSpy('onDestroy');
this.model.on('destroy', this.destroySpy);
this.model.destroy('arg1', 'arg2', 'arg3');
});
it('should destroy the model', function () {
expect(this.destroySpy).toHaveBeenCalled();
});
it('should pass through any arguments to listener', function () {
var args = this.destroySpy.calls.argsFor(0);
expect(args[0]).toEqual('arg1');
expect(args[1]).toEqual('arg2');
expect(args[2]).toEqual('arg3');
});
});
});
@@ -0,0 +1,131 @@
var _ = require('underscore');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var ModalViewModel = require('builder/components/modals/modal-view-model');
var ModalView = require('builder/components/modals/modal-view');
describe('components/modals/modal-view', function () {
var view, model, contentView, contentViewModel, contentViewEventSpy;
var createViewFn = function (options) {
contentViewEventSpy = jasmine.createSpy('test');
model = new ModalViewModel({
createContentView: function () { return contentView; }
});
var TestView = CoreView.extend({
initialize: function () {
this.model.on('test', contentViewEventSpy, this);
}
});
contentViewModel = new Backbone.Model();
contentView = new TestView({model: contentViewModel});
spyOn(contentView, 'render').and.callThrough();
var defaultOptions = {
model: model
};
view = new ModalView(_.extend(defaultOptions, options));
return view;
};
it('should have no leaks', function () {
view = createViewFn();
view.render();
expect(view).toHaveNoLeaks();
});
it('should render dialog classes', function () {
view = createViewFn();
view.render();
expect(view.$el.html()).toContain('Dialog');
expect(view.$el.html()).toContain('Dialog-contentWrapper');
});
it('should not render close button if escapeOptionsDisabled is present', function () {
view = createViewFn({
escapeOptionsDisabled: true
});
view.render();
expect(view.$el.html()).not.toContain('js-close');
});
it('should not render close button if breadcrumbsEnabled is present', function () {
view = createViewFn({
breadcrumbsEnabled: true
});
view.render();
expect(view.$el.html()).toContain('<div class="Dialog-headerWrapper">');
expect(view.$el.html()).not.toContain('<button class="CDB-Shape js-close Dialog-closeBtn">');
});
describe('when close is clicked', function () {
beforeEach(function () {
view = createViewFn();
view.render();
contentViewModel.trigger('test', 'asd');
expect(contentViewEventSpy).toHaveBeenCalled();
contentViewEventSpy.calls.reset();
jasmine.clock().install();
spyOn(view, 'hide').and.callThrough();
spyOn(view, 'clean').and.callThrough();
spyOn(model, 'destroy').and.callThrough();
view.$('.js-close').click();
});
afterEach(function () {
jasmine.clock().uninstall();
});
it('should destroy the model', function () {
expect(model.destroy).toHaveBeenCalled();
});
it('should hide modal', function () {
expect(view.hide).toHaveBeenCalled();
});
it('should not react to model bindings anymore', function () {
contentViewModel.trigger('test', 'asd');
expect(contentViewEventSpy).not.toHaveBeenCalled();
});
it('should not clean the view right away', function () {
expect(view.clean).not.toHaveBeenCalled();
});
describe('when the close animation is done', function () {
beforeEach(function () {
jasmine.clock().tick(1000);
});
it('should have cleaned the view', function () {
expect(view.clean).toHaveBeenCalled();
});
it('should not react to model bindings anymore', function () {
contentViewModel.trigger('test', 'asd');
expect(contentViewEventSpy).not.toHaveBeenCalled();
});
it('should have no leaks', function () {
expect(view).toHaveNoLeaks();
});
});
});
});
@@ -0,0 +1,117 @@
var CoreView = require('backbone/core-view');
var ModalsServiceModel = require('builder/components/modals/modals-service-model');
var Router = require('builder/routes/router');
describe('components/modals/modals-service-model', function () {
beforeEach(function () {
this.modals = new ModalsServiceModel();
this.willCreateModalSpy = jasmine.createSpy('willCreateModal');
this.didCreateModalSpy = jasmine.createSpy('didCreateModal');
this.modals.on('willCreateModal', this.willCreateModalSpy);
this.modals.on('didCreateModal', this.didCreateModalSpy);
});
describe('.create', function () {
var contentView, contentView2;
beforeEach(function () {
spyOn(Router, 'navigate');
spyOn(document.body, 'appendChild');
contentView = new CoreView();
spyOn(contentView, 'render').and.callThrough();
this.modalView = this.modals.create(function () {
return contentView;
});
});
it('should return a modal view', function () {
expect(this.modalView).toBeDefined();
});
it('should trigger a willCreateModal event', function () {
expect(this.willCreateModalSpy).toHaveBeenCalled();
});
it('should trigger a didCreateModal event', function () {
expect(this.didCreateModalSpy).toHaveBeenCalled();
});
it('should render the content view', function () {
expect(contentView.render).toHaveBeenCalled();
});
it('should append the modal to the body', function () {
expect(document.body.appendChild).toHaveBeenCalledWith(this.modalView.el);
});
it('should add the special body class', function () {
expect(document.body.className).toContain('is-inDialog');
});
describe('subsequent calls', function () {
beforeEach(function () {
contentView2 = new CoreView();
spyOn(contentView2, 'render').and.callThrough();
this.modalView2 = this.modals.create(function () {
return contentView2;
});
});
it('should reuse modal view', function () {
expect(this.modalView2).toBe(this.modalView);
});
it('should append the new modal to the body', function () {
expect(document.body.appendChild).toHaveBeenCalledWith(this.modalView2.el);
});
it('should keep the special body class', function () {
expect(document.body.className).toContain('is-inDialog');
});
describe('when destroyed', function () {
beforeEach(function () {
jasmine.clock().install();
this.destroyOnceSpy = jasmine.createSpy('destroyedModal');
this.modals.onDestroyOnce(this.destroyOnceSpy);
spyOn(this.modalView2, 'clean').and.callThrough();
this.modalView2.destroy();
});
afterEach(function () {
jasmine.clock().uninstall();
});
it('should not clean the view right away but wait until after animation', function () {
expect(this.modalView2.clean).not.toHaveBeenCalled();
});
it('should have closing animation', function () {
expect(this.modalView2.el.className).toContain('is-closing');
expect(this.modalView2.el.className).not.toContain('is-opening');
});
it('should remove the special body class', function () {
expect(document.body.className).not.toContain('is-inDialog');
});
describe('when close animation is done', function () {
beforeEach(function () {
jasmine.clock().tick(250);
});
it('should have cleaned the view', function () {
expect(this.modalView2.clean).toHaveBeenCalled();
});
it('should have triggered listener', function () {
expect(this.destroyOnceSpy).toHaveBeenCalled();
});
});
});
});
});
});
@@ -0,0 +1,70 @@
var _ = require('underscore');
var Backbone = require('backbone');
var PrivacyWarningView = require('builder/components/modals/privacy-warning/privacy-warning-view');
describe('components/modals/privacy-warning/privacy-warning-view', function () {
var view;
var createViewFn = function (viewOptions) {
var modals = new Backbone.Model();
var view = new PrivacyWarningView(
_.extend({
privacyType: 'PUBLIC',
modalModel: modals
}, viewOptions)
);
return view;
};
afterEach(function () {
if (view) view.clean();
});
it('should render properly', function () {
var visualizationView = createViewFn();
visualizationView.render();
expect(visualizationView.$('h2').text()).toContain('components.modals.privacy-warning.title.visualization.PUBLIC');
expect(visualizationView.$('p').text()).toContain('components.modals.privacy-warning.description');
expect(visualizationView.$('.js-cancel').text()).toContain('components.modals.privacy-warning.cancel');
expect(visualizationView.$('.js-confirm').text()).toContain('components.modals.privacy-warning.confirm');
var datasetView = createViewFn({
type: 'dataset'
});
datasetView.render();
expect(datasetView.$('h2').text()).toContain('components.modals.privacy-warning.title.dataset');
visualizationView.clean();
datasetView.clean();
});
it('should invoke confirm callback when clicking confirm', function () {
var confirmCallbackSpy = jasmine.createSpy();
view = createViewFn({
onConfirm: confirmCallbackSpy
});
view.render();
view.$('.js-confirm').click();
expect(confirmCallbackSpy).toHaveBeenCalled();
});
it('should invoke cancel callback when dismissing the modal', function () {
var dismissCallbackSpy = jasmine.createSpy();
view = createViewFn({
onDismiss: dismissCallbackSpy
});
view.render();
view.$('.js-cancel').click();
expect(dismissCallbackSpy).toHaveBeenCalled();
});
});
@@ -0,0 +1,66 @@
var PrivacyCollection = require('builder/components/modals/publish/privacy-collection');
describe('components/modals/publish/privacy-collection', function () {
var collection;
beforeEach(function () {
collection = new PrivacyCollection();
});
describe('when reset with some options', function () {
beforeEach(function () {
collection.reset([{
privacy: 'PUBLIC',
title: 'Public',
desc: 'Lorem ipsum',
cssClass: 'is-green',
selected: true
}, {
privacy: 'LINK',
title: 'Link',
desc: 'Yabadababa',
cssClass: 'is-orange'
}, {
privacy: 'PASSWORD',
title: 'Password',
desc: 'Wadus'
}, {
privacy: 'PRIVATE',
title: 'Private',
desc: 'Funínculo',
cssClass: 'is-red'
}]);
});
it('should create a model for given data', function () {
expect(collection.length).toEqual(4);
expect(collection.at(0).get('title')).toEqual('Public');
expect(collection.at(3).get('title')).toEqual('Private');
});
it('should return the selected option', function () {
collection.at(2).set({selected: true});
expect(collection.selectedOption().get('title')).toEqual('Password');
collection.at(3).set({selected: true});
expect(collection.selectedOption().get('title')).toEqual('Private');
});
it('should return the password option', function () {
expect(collection.passwordOption().get('title')).toEqual('Password');
});
it('should search the option passed', function () {
expect(collection.searchByPrivacy('LINK').get('title')).toEqual('Link');
});
it('should only allow one selection at a time', function () {
expect(collection.pluck('selected')).toEqual([true, false, false, false]);
collection.last().set('selected', true);
expect(collection.pluck('selected')).toEqual([false, false, false, true]);
collection.first().set('selected', true);
expect(collection.pluck('selected')).toEqual([true, false, false, false]);
});
});
});
@@ -0,0 +1,153 @@
var Backbone = require('backbone');
var PublishButtonView = require('builder/components/modals/publish/publish-button-view');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('fixtures/builder/user-model.fixture.js');
var VisDefinitionModel = require('builder/data/vis-definition-model');
describe('components/modals/publish/publish-button-view', function () {
var view, visDefinitionModel, mapcapsCollection;
var createViewFn = function (options) {
var customOptions = options || {};
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
visDefinitionModel = new VisDefinitionModel(
{
name: 'Foo Map',
privacy: 'PUBLIC',
updated_at: '2016-06-21T15:30:06+00:00',
type: 'derived',
permission: {}
},
{ configModel: configModel });
mapcapsCollection = new Backbone.Collection();
var view = new PublishButtonView({
visDefinitionModel: visDefinitionModel,
mapcapsCollection: mapcapsCollection,
configModel: configModel,
userModel: UserModel(customOptions.userModel)
});
return view;
};
beforeEach(function () {
view = createViewFn({
userModel: {
actions: {
private_maps: true,
private_tables: true
}
}
});
view.render();
});
afterEach(function () {
view.remove();
});
it('should render properly', function () {
var publishButton = view.$('.js-button');
expect(publishButton.text()).toContain('components.modals.publish.publish-btn');
expect(view.$el.text()).toContain('components.modals.publish.share.unpublished');
});
describe('_onUpdate', function () {
it('should create map capture if privacy warning is not shown', function () {
spyOn(view, '_shouldShowPrivacyWarning').and.returnValue(false);
spyOn(view, '_createMapCap');
view._onUpdate();
expect(view._createMapCap).toHaveBeenCalled();
});
it('should create map capture if user accepts the privacy warning', function () {
spyOn(view, '_shouldShowPrivacyWarning').and.returnValue(true);
spyOn(view, '_createMapCap');
spyOn(view, '_checkPrivacyChange').and.callFake(
function (_, success) { success(); }
);
view._onUpdate();
expect(view._checkPrivacyChange).toHaveBeenCalled();
expect(view._createMapCap).toHaveBeenCalled();
});
it('should not create map capture if user dismisses the privacy warning', function () {
spyOn(view, '_shouldShowPrivacyWarning').and.returnValue(true);
spyOn(view, '_createMapCap');
spyOn(view, '_checkPrivacyChange').and.callFake(
function (_newPrivacyStatus, _confirm, dismiss) {
if (dismiss) dismiss();
}
);
view._onUpdate();
expect(view._checkPrivacyChange).toHaveBeenCalled();
expect(view._createMapCap).not.toHaveBeenCalled();
});
});
describe('_checkPrivacyChange', function () {
it('should open PrivacyWarningView', function (done) {
view._checkPrivacyChange('PUBLIC');
var dialogView = document.querySelector('.Dialog');
expect(dialogView.innerText).toContain('components.modals.privacy-warning.title.visualization.PUBLIC');
view.clean();
// Wait for the modal to close
setTimeout(function () { done(); }, 250);
});
});
describe('_shouldShowPrivacyWarning', function () {
it('should return false if user cannot select premium options', function () {
view = createViewFn({
userModel: {
actions: {
private_maps: false,
private_tables: false
}
}
});
visDefinitionModel.set('privacy', 'LINK');
expect(view._shouldShowPrivacyWarning()).toBe(false);
});
it('should return true if map is not published and privacy is set to *any* public state', function () {
visDefinitionModel.set('privacy', 'LINK');
expect(view._shouldShowPrivacyWarning()).toBe(true);
});
it('should return false if map is published', function () {
mapcapsCollection.add({
created_at: '2016-06-21T15:30:06+00:00'
});
expect(view._shouldShowPrivacyWarning()).toBe(false);
mapcapsCollection.reset([]);
});
it('should return false if map is published and privacy is set to private', function () {
visDefinitionModel.set('privacy', 'PRIVATE');
mapcapsCollection.add({ created_at: '2016-06-21T15:30:06+00:00' });
expect(view._shouldShowPrivacyWarning()).toBe(false);
mapcapsCollection.reset([]);
});
});
});
@@ -0,0 +1,122 @@
var Backbone = require('backbone');
var PublishView = require('builder/components/modals/publish/publish-view');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
var PrivacyCollection = require('builder/components/modals/publish/privacy-collection');
var VisDefinitionModel = require('builder/data/vis-definition-model');
describe('components/modals/publish/publish-view', function () {
var userModel;
beforeEach(function () {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*grantables.*'))
.andReturn({ status: 200 });
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
userModel = new UserModel({
id: 1,
username: 'pepe',
avatar_url: 'https://example.com/avatar.jpg',
actions: {
private_tables: true
}
}, {
configModel: configModel
});
userModel._organizationModel = new Backbone.Model({
id: 1
});
var collection = new PrivacyCollection([{
privacy: 'PUBLIC',
title: 'Public',
desc: 'Lorem ipsum',
cssClass: 'is-green',
selected: true
}, {
privacy: 'LINK',
title: 'Link',
desc: 'Yabadababa',
cssClass: 'is-orange'
}, {
privacy: 'PASSWORD',
title: 'Password',
desc: 'Wadus'
}, {
privacy: 'PRIVATE',
title: 'Private',
desc: 'Funínculo',
cssClass: 'is-red'
}]);
var mapcapsCollection = new Backbone.Collection([{
created_at: '2016-06-21T15:30:06+00:00'
}]);
var visDefinitionModel = new VisDefinitionModel({
name: 'Foo Map',
privacy: 'PUBLIC',
updated_at: '2016-06-21T15:30:06+00:00',
type: 'derived',
permission: {}
}, {
configModel: configModel
});
this.view = new PublishView({
mapcapsCollection: mapcapsCollection,
modalModel: new Backbone.Model(),
visDefinitionModel: visDefinitionModel,
privacyCollection: collection,
userModel: userModel,
configModel: configModel,
isOwner: true,
ownerName: 'matata'
});
this.view.render();
});
afterEach(function () {
this.view.remove();
jasmine.Ajax.uninstall();
});
it('should render properly', function () {
expect(this.view.$el.html()).toContain('Foo Map');
expect(this.view.$('.js-toggle').length).toBe(1);
expect(this.view.$('.js-toggle').text()).toContain('PUBLIC');
expect(this.view.$('.js-share-users').length).toBe(1);
expect(this.view.$('.js-panes').length).toBe(1);
expect(this.view.$('.js-menu').length).toBe(1);
expect(this.view.$('.Publish-modalLink').length).toBe(2);
});
it('should render properly in share in single mode', function () {
this.view.mode = 'share';
this.view.render();
expect(this.view.$el.html()).toContain('Foo Map');
expect(this.view.$('.js-menu').length).toBe(0);
expect(this.view.$('.Publish-modalLink').length).toBe(0);
});
it('should render properly for users without organization', function () {
userModel._organizationModel = false;
this.view.render();
expect(this.view.$el.html()).toContain('Foo Map');
expect(this.view.$('.js-menu').length).toBe(0);
expect(this.view.$('.js-share-users').length).toBe(0); // share badget
expect(this.view.$('.Publish-modalLink').length).toBe(0); // it runs in single mode without tabs and share view
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
@@ -0,0 +1,81 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var PrivacyView = require('builder/components/modals/publish/publish/publish-view');
var VisDefinitionModel = require('builder/data/vis-definition-model');
var UserModel = require('builder/data/user-model');
describe('components/modals/publish/publish/publish-view', function () {
var view;
var mapcapsCollection;
var visDefinitionModel;
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
visDefinitionModel = new VisDefinitionModel({
name: 'Foo Map',
privacy: 'PRIVATE',
updated_at: '2016-06-21T15:30:06+00:00',
type: 'derived',
permission: {}
}, {
configModel: configModel
});
var userModel = new UserModel({
username: 'pepe',
actions: {
private_tables: true
}
}, {
configModel: configModel
});
mapcapsCollection = new Backbone.Collection([]);
view = new PrivacyView({
mapcapsCollection: mapcapsCollection,
visDefinitionModel: visDefinitionModel,
userModel: userModel
});
view.render();
});
afterEach(function () {
view.remove();
});
it('should render properly', function () {
expect(view.$('.Card').length).toBe(2);
expect(view.$('input').length).toBe(0);
});
it('should render properly when published and private', function () {
mapcapsCollection.reset([{
created_at: '2016-06-21T15:30:06+00:00'
}]);
expect(view.$('.Card').length).toBe(2);
expect(view.$('.Card.is-disabled').length).toBe(2);
expect(view.$('input').length).toBe(0); // it's private
});
it('should render properly when published and public', function () {
mapcapsCollection.reset([{
created_at: '2016-06-21T15:30:06+00:00'
}]);
visDefinitionModel.set({privacy: 'PUBLIC'});
visDefinitionModel.trigger('sync');
expect(view.$('.Card').length).toBe(2);
expect(view.$('.Card.is-disabled').length).toBe(0);
expect(view.$('input').length).toBe(2);
});
it('should not have any leaks', function () {
expect(view).toHaveNoLeaks();
});
});
@@ -0,0 +1,89 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
var VisDefinitionModel = require('builder/data/vis-definition-model');
var ShareWithView = require('builder/components/modals/publish/share-with-view');
describe('components/modals/publish/share-with-view', function () {
var view;
var visDefinitionModel;
var userModel;
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
userModel = new UserModel({
id: 1,
username: 'pepe',
avatar_url: 'https://example.com/avatar.jpg',
actions: {
private_tables: true
}
}, {
configModel: configModel
});
visDefinitionModel = new VisDefinitionModel({
name: 'Foo Map',
privacy: 'PUBLIC',
updated_at: '2016-06-21T15:30:06+00:00',
type: 'derived',
permission: {}
}, {
configModel: configModel
});
view = new ShareWithView({
visDefinitionModel: visDefinitionModel,
userModel: userModel
});
view.render();
});
describe('render', function () {
it('should render properly', function () {
// empty because people is 0 and the user has no org
expect(view.$('.Share-user').length).toBe(0);
expect(view.$el.html()).not.toContain('components.modals.publish.share.add-people');
});
it('should render properly when inside org', function () {
userModel._organizationModel = new Backbone.Model({
id: 1
});
spyOn(view, '_getPeople').and.returnValue(2);
view.render();
expect(view.$('.Share-user').length).toBe(1);
expect(view.$el.text()).toContain('+ 2');
expect(view.$el.html()).not.toContain('components.modals.publish.share.add-people');
});
it('should render properly when inside org and has an action', function () {
userModel._organizationModel = new Backbone.Model({
id: 1
});
spyOn(view, '_getPeople').and.returnValue(0);
view = new ShareWithView({
visDefinitionModel: visDefinitionModel,
userModel: userModel,
clickPrivacyAction: jasmine.createSpy('actionCallback')
});
view.render();
expect(view.$('.Share-user').length).toBe(0);
expect(view.$el.html()).toContain('components.modals.publish.share.add-people');
});
});
it('should not have any leaks', function () {
expect(view).toHaveNoLeaks();
});
});
@@ -0,0 +1,68 @@
var Backbone = require('backbone');
var ShareView = require('builder/components/modals/publish/share/share-view');
var ConfigModel = require('builder/data/config-model');
var VisDefinitionModel = require('builder/data/vis-definition-model');
var OrganizationModel = require('builder/data/organization-model');
describe('components/modals/publish/share/share-view', function () {
var onBack;
beforeEach(function () {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest(new RegExp('^http(s)?://.*/grantables.*'))
.andReturn({ status: 200 });
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var visDefinitionModel = new VisDefinitionModel({
name: 'Foo Map',
privacy: 'PUBLIC',
updated_at: '2016-06-21T15:30:06+00:00',
type: 'derived',
permission: {}
}, {
configModel: configModel
});
var org = new OrganizationModel({
id: 'org1',
owner: {
id: 'hello',
username: 'dev',
email: 'hello@hello'
}
}, {
configModel: configModel
});
spyOn(ShareView.prototype, '_onCancel');
spyOn(ShareView.prototype, '_onSave');
onBack = jasmine.createSpy('onBack');
this.view = new ShareView({
modalModel: new Backbone.Model(),
onBack: onBack,
currentUserId: 'u1',
visDefinitionModel: visDefinitionModel,
organization: org,
configModel: configModel
});
this.view.render();
});
afterEach(function () {
this.view.remove();
jasmine.Ajax.uninstall();
});
it('should render properly', function () {
expect(this.view.$('.js-filters').length).toBe(1);
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
@@ -0,0 +1,331 @@
var Backbone = require('backbone');
var _ = require('underscore');
var RemoveDatasetView = require('builder/components/modals/remove-dataset/remove-dataset-view');
var UserModel = require('builder/data/user-model');
var VisDefinitionModel = require('builder/data/vis-definition-model');
var TableModel = require('builder/data/table-model');
var ConfigModel = require('builder/data/config-model');
describe('modals/remove-dataset-view', function () {
var modalModel = new Backbone.Model();
var avatarBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC';
var configData = {
base_url: '/u/manolo',
maps_api_template: 'http://{user}.localhost.lan:8181'
};
var userData = {
id: '8c253a9a-94d8-4042-883c-3aa81285e5e6',
username: 'cdb',
organization: {
display_name: 'Globex Corporation',
name: 'globex',
avatar_url: 'https://s3.amazonaws.com/com.cartodb.users-assets.staging/development/cdb/assets/20161028125910fake-company-logo.jpg',
user_count: 25
}
};
var tableDataWithNoDependentMaps = {
name: 'NYC Graffiti sights',
accessible_dependent_derived_maps: []
};
var dependentMap = {
id: '5725afbe-9f51-11e6-af45-080027eb929e',
name: 'Untitled Map',
permission: {
owner: {
username: 'cdb'
}
},
updated_at: '2016-10-31T10:03:58+00:00'
};
var visDataWithNoACL = {
permission: {
acl: []
}
};
var visDataWithOrganizationACL = {
permission: {
acl: [{
type: 'org',
entity: {
id: 'c210c579-0780-4ccd-83a8-7bf9fee11b34',
name: 'globex',
avatar_url: avatarBase64
}
}]
}
};
var visDataWithGroupACL = {
permission: {
acl: [{
type: 'group',
entity: {
users: [{
username: 'Meowth',
avatar_url: avatarBase64
}]
}
}]
}
};
var visModel;
var configModel;
var tableModel;
var userModel;
var view;
function createDependantACL (username) {
return {
type: 'user',
entity: {
username: username || 'pepe' + _.random(99999).toString(),
avatar_url: avatarBase64
}
};
}
function createView (options) {
var opts = options || {};
var numberOfDependentMaps = opts.numberOfDependentMaps || 0;
var organizationAffected = opts.organizationAffected || false;
var numberOfDependentEntities = opts.numberOfDependentEntities || 0;
var repeatAffectedEntity = opts.repeatAffectedEntity || false;
var affectedGroup = opts.affectedGroup || false;
var tableData;
var visData;
configModel = new ConfigModel(configData);
userModel = new UserModel(userData, { configModel: configModel });
if (numberOfDependentMaps > 0) {
tableData = _.clone(tableDataWithNoDependentMaps);
_.times(numberOfDependentMaps, function () {
tableData.accessible_dependent_derived_maps.push(dependentMap);
});
} else {
tableModel = new TableModel(tableDataWithNoDependentMaps, { parse: true, configModel: configModel });
}
if (organizationAffected) {
visModel = new VisDefinitionModel(visDataWithOrganizationACL, {
configModel: configModel
});
} else if (affectedGroup) {
visModel = new VisDefinitionModel(visDataWithGroupACL, {
configModel: configModel
});
} else if (numberOfDependentEntities > 0) {
visData = {
permission: {
acl: []
}
};
_.times(numberOfDependentEntities, function () {
visData.permission.acl.push(createDependantACL());
});
visData.permission.acl.push(createDependantACL(userData.username)); // To test if we filter ourselves
if (repeatAffectedEntity) {
visData.permission.acl.push(_.clone(visData.permission.acl[0])); // Repeat one user to test if we filter only uniques
}
visModel = new VisDefinitionModel(visData, {
configModel: configModel
});
} else {
visModel = new VisDefinitionModel(visDataWithNoACL, {
configModel: configModel
});
}
return new RemoveDatasetView({
modalModel: modalModel,
userModel: userModel,
visModel: visModel,
tableModel: tableModel,
configModel: configModel
});
}
describe('render', function () {
it('Nothing affected, visualization and entities sections not shown', function () {
// Arrange
view = createView();
// Act
view.render();
// Assert
expect(view.$('h2')[0].textContent.trim()).toEqual('dataset.delete.title');
expect(view.$('.MapsList-item').length).toBe(0);
expect(view.$('.ShareUser').length).toBe(0);
expect(view.$('button.js-cancel > span')[0].textContent.trim()).toEqual('dataset.delete.cancel');
expect(view.$('button.js-confirm > span')[0].textContent.trim()).toEqual('dataset.delete.confirm');
});
it('1 visualization affected. Visualization gets properly rendered.', function () {
// Arrange
view = createView({ numberOfDependentMaps: 1 });
// Act
view.render();
// Assert
expect(view.$('.js-affectedVis > p')[0].textContent.trim()).toEqual('dataset.delete.affected-vis-count');
expect(view.$('.js-affectedVis > ul > li').length).toBe(1);
expect(view.$('.MapCard')[0].dataset['visId']).toEqual(dependentMap.id);
expect(view.$('.MapCard')[0].dataset['visOwnerName']).toEqual(dependentMap.permission.owner.username);
expect(view.$('.MapCard > a')[0].href).toBeTruthy();
expect(view.$('.MapCard > a > img')[0].src).toBeTruthy();
expect(view.$('.MapCard-content h3 > a')[0].href).toBeTruthy();
expect(view.$('.MapCard-content h3 > a')[0].title).toEqual(dependentMap.name);
expect(view.$('.MapCard-contentBodyTimeDiff')[0].textContent.trim()).toBeTruthy();
});
it('More vis affected than AFFECTED_VIS_COUNT. Text changed to extended one.', function () {
// Arrange
var maxAffectedVisCount = 3; // AFFECTED_VIS_COUNT
view = createView({ numberOfDependentMaps: 4 });
// Act
view.render();
// Assert
expect(view.$('.js-affectedVis > p')[0].textContent.trim()).toEqual('dataset.delete.affected-vis-count-extended');
expect(view.$('.js-affectedVis > ul > li').length).toBe(maxAffectedVisCount);
});
it('Organization affected. Proper text and avatar rendered.', function () {
// Arrange
var userNode;
view = createView({ organizationAffected: true });
// Act
view.render();
// Assert
expect(view.$('.js-affectedEntities > p')[0].textContent.trim()).toEqual('dataset.delete.whole-organization-affected');
expect(view.$('.js-affectedEntities li').length).toBe(1);
userNode = view.$('.js-affectedEntities li .Share-user')[0];
expect(userNode.title).toEqual(userData.organization.display_name);
expect(userNode.dataset['username']).toEqual(userData.organization.display_name);
expect(userNode.style.backgroundImage.indexOf(userData.organization.avatar_url) > -1).toBe(true);
});
it('1 user affected. Proper text and avatar rendered.', function () {
// Arrange
var userNode;
var affectedEntity;
view = createView({ numberOfDependentEntities: 1 });
affectedEntity = view._visModel._permissionModel.get('acl')[0].entity;
// Act
view.render();
// Assert
expect(view.$('.js-affectedEntities > p')[0].textContent.trim()).toEqual('dataset.delete.affected-entities-count');
expect(view.$('.js-affectedEntities li').length).toBe(1);
userNode = view.$('.js-affectedEntities li .Share-user')[0];
expect(userNode.title).toEqual(affectedEntity.username);
expect(userNode.dataset['username']).toEqual(affectedEntity.username);
expect(userNode.style.backgroundImage.indexOf(affectedEntity.avatar_url) > -1).toBe(true);
});
it('More user affected than AFFECTED_ENTITIES_SAMPLE_COUNT. Text changed to extended.', function () {
// Arrange
var affectedEntitiesSampleCount = 20; // AFFECTED_ENTITIES_SAMPLE_COUNT
view = createView({ numberOfDependentEntities: affectedEntitiesSampleCount + 1 });
// Act
view.render();
// Assert
expect(view.$('.js-affectedEntities > p')[0].textContent.trim()).toEqual('dataset.delete.affected-entities-count-extended');
expect(view.$('.js-affectedEntities li').length).toBe(affectedEntitiesSampleCount);
});
it('Affected entity repeated. Repeated entity gets filtered.', function () {
// Arrange
view = createView({
numberOfDependentEntities: 2,
repeatAffectedEntity: true
});
// Act
view.render();
// Assert
expect(view.$('.js-affectedEntities li').length).toBe(2);
});
it('Affected group. Users of that group get rendered.', function () {
// Arrange
view = createView({
affectedGroup: true
});
// Act
view.render();
// Assert
expect(view.$('.js-affectedEntities li').length).toBe(visDataWithGroupACL.permission.acl[0].entity.users.length);
});
});
describe('_runAction', function () {
it('Renders loading view and destory has been set to the right success function.', function () {
// Arrange
var successFunction;
view = createView();
spyOn(view, '_renderLoadingView');
spyOn(view, '_onSuccessDestroyDataset');
spyOn(visModel, 'destroy').and.callFake(function (args) {
successFunction = args.success;
});
// Act
view._runAction();
successFunction();
// Assert
expect(view._renderLoadingView).toHaveBeenCalled();
expect(view._onSuccessDestroyDataset).toHaveBeenCalled();
});
it('Destroy has been set to the right error function.', function () {
// Arrange
var errorFunction;
view = createView();
spyOn(view, '_onErrorDestroyDataset');
spyOn(visModel, 'destroy').and.callFake(function (args) {
errorFunction = args.error;
});
// Act
view._runAction();
errorFunction(null, {});
// Assert
expect(view._onErrorDestroyDataset).toHaveBeenCalled();
});
});
it('_renderLoadingView inserts the right HTML', function () {
// Arrange
view = createView();
// Act
view._renderLoadingView();
// Assert
expect(view.$('.js-loader').length).toBeGreaterThan(0);
});
it('_onErrorDestroyDataset inserts the right HTML', function () {
// Arrange
view = createView();
// Act
view._onErrorDestroyDataset(null, {});
// Assert
expect(view.$('i.CDB-IconFont-cockroach').length).toBeGreaterThan(0);
});
});
@@ -0,0 +1,152 @@
var _ = require('underscore');
var Backbone = require('backbone');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var AnalysisDefinitionsCollection = require('builder/data/analysis-definitions-collection');
var ConfigModel = require('builder/data/config-model');
var DeleteLayerConfirmationView = require('builder/components/modals/remove-layer/delete-layer-confirmation-view');
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
var ModalsServiceModel = require('builder/components/modals/modals-service-model');
var UserActions = require('builder/data/user-actions');
var UserModel = require('builder/data/user-model');
var VisDefinitionModel = require('builder/data/vis-definition-model');
var WidgetDefinitionsCollection = require('builder/data/widget-definitions-collection');
describe('modals/delete-layer-confirmation-view', function () {
var view;
var modalModel;
var userData;
var configData;
var configModel;
var userModel;
var analysisDefinitionNodesCollection;
var layerModel;
var layerDefinitionsCollection;
var visDefinitionModel;
var widgetDefinitionsCollection;
var analysisDefinitionsCollection;
var userActions;
var modals;
modalModel = new Backbone.Model();
userData = {
id: '8c253a9a-94d8-4042-883c-3aa81285e5e6',
username: 'cdb'
};
configData = {
base_url: '/u/manolo',
maps_api_template: 'http://{user}.localhost.lan:8181'
};
configModel = new ConfigModel(configData);
userModel = new UserModel(userData, { configModel: configModel });
analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection([], {
configModel: configModel,
userModel: userModel
});
layerModel = new LayerDefinitionModel({
id: 'map-123',
kind: 'carto',
options: {
type: 'CartoDB',
color: '#FABADA',
table_name: 'foo_bar',
name: 'foo',
letter: 'a',
source: 'map-123',
table_name_alias: 'foo_foo_bar',
query: 'SELECT * FROM foo',
tile_style: 'asdasd',
visible: true
}
}, {
configModel: configModel,
parse: true
});
layerDefinitionsCollection = new LayerDefinitionsCollection([layerModel], {
configModel: configModel,
userModel: userModel,
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
mapId: 'map-123',
stateDefinitionModel: {}
});
visDefinitionModel = new VisDefinitionModel({
name: 'foo',
privacy: 'LINK',
updated_at: '2016-06-21T15:30:06+00:00',
type: 'derived',
tags: ['foo', 'bar'],
permission: {}
}, {
configModel: configModel
});
widgetDefinitionsCollection = new WidgetDefinitionsCollection([layerModel], {
configModel: configModel,
layerDefinitionsCollection: layerDefinitionsCollection,
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
mapId: 'map-123'
});
analysisDefinitionsCollection = new AnalysisDefinitionsCollection([layerModel], {
configModel: configModel,
vizId: 'viz-123',
layerDefinitionsCollection: layerDefinitionsCollection,
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection
});
userActions = new UserActions({
userModel: userModel,
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
analysisDefinitionsCollection: analysisDefinitionsCollection,
layerDefinitionsCollection: layerDefinitionsCollection,
widgetDefinitionsCollection: widgetDefinitionsCollection
});
modals = new ModalsServiceModel();
function createViewFn (options) {
var defaultOptions = {
userActions: userActions,
modals: modals,
layerModel: layerModel,
modalModel: modalModel,
visDefinitionModel: visDefinitionModel,
widgetDefinitionsCollection: widgetDefinitionsCollection
};
return new DeleteLayerConfirmationView(_.extend(defaultOptions, options));
}
describe('render', function () {
it('should render properly', function () {
view = createViewFn();
view.render();
expect(view.$('h2')[0].textContent.trim()).toEqual('editor.layers.delete.title');
expect(view.$('p')[0].textContent.trim()).toEqual('editor.layers.delete.desc');
expect(view.$('li').length).toEqual(3);
expect(view.$('li')[0].textContent.trim()).toEqual('editor.layers.delete.link-to-export');
expect(view.$('button.js-cancel > span')[0].textContent.trim()).toEqual('editor.layers.delete.cancel');
expect(view.$('button.js-confirm > span')[0].textContent.trim()).toEqual('editor.layers.delete.confirm');
});
it('should show the name of the layer to be deleted', function () {
spyOn(layerModel, 'getName').and.callThrough();
view = createViewFn();
view.render();
expect(layerModel.getName).toHaveBeenCalled();
});
});
});