Initial commit

This commit is contained in:
zhongjin
2020-06-13 18:34:34 +08:00
commit 52aaa9f15d
655 changed files with 96796 additions and 0 deletions
+121
View File
@@ -0,0 +1,121 @@
var $ = require('jquery');
var _ = require('underscore');
var AttributionView = require('../../../../src/geo/ui/attribution/attribution-view');
var Map = require('../../../../src/geo/map');
describe('geo/ui/attribution', function () {
beforeEach(function () {
// Disable defer
spyOn(_, 'delay').and.callFake(function (func) { return function () { func.apply(this, arguments); }; });
this.keyEsc = function () {
var e = $.Event('keydown');
e.keyCode = 27; // ESC
$(document).trigger(e);
};
this.map = new Map(null, {
layersFactory: {}
});
this.map.setMapViewSize({ x: 650, y: 1000 });
spyOn(this.map, 'bind').and.callThrough();
spyOn(AttributionView.prototype, 'render').and.callThrough();
this.view = new AttributionView({
map: this.map
});
this.view.render();
this.$button = this.view.$('.js-button');
this.$text = this.view.$('.js-text');
this.viewHasClass = function (className) {
return this.view.$el.hasClass(className);
};
});
describe('render', function () {
it('should render properly', function () {
expect(this.$button.length).toBe(1);
expect(this.$text.length).toBe(1);
});
it('should render expanded if map container is greater than 650px', function () {
this.map.setMapViewSize({ x: 750, y: 1000 });
this.view.render();
expect(this.viewHasClass('CDB-Attribution')).toBeTruthy();
expect(this.viewHasClass('is-active')).toBeTruthy();
});
it('should render collapsed if map container is smaller than 650px', function () {
this.map.setMapViewSize({ x: 450, y: 1000 });
this.view.render();
expect(this.viewHasClass('CDB-Attribution')).toBeTruthy();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should add GMaps properly when provider is not Leaflet', function () {
expect(this.viewHasClass('CDB-Attribution--gmaps')).toBeFalsy();
this.map.set('provider', 'gmaps');
this.view.render();
expect(this.viewHasClass('CDB-Attribution--gmaps')).toBeTruthy();
});
});
describe('attributions change', function () {
it('should render when it is visible', function () {
this.$button.click(); // Making it visible
this.map.trigger('change:attribution');
expect(AttributionView.prototype.render).toHaveBeenCalled();
});
it('should render when it is not visible', function () {
this.map.trigger('change:attribution');
expect(AttributionView.prototype.render).toHaveBeenCalled();
});
});
describe('when the attributions are displayed', function () {
beforeEach(function () {
this.$button.click();
});
it('should have is-active class', function () {
expect(this.viewHasClass('is-active')).toBeTruthy();
});
it('should hide attributions text when js-button is clicked', function () {
this.$button.click();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should collapse the attributions when ESC is pressed', function () {
this.keyEsc();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
});
describe('when attributions are hidden', function () {
beforeEach(function () {
this.$button.click();
this.keyEsc();
});
it('shouldn\'t have is-active class', function () {
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should show attributions text when js-button is clicked', function () {
this.$button.click();
expect(this.viewHasClass('is-active')).toBeTruthy();
});
it('should not respond to ESC', function () {
this.keyEsc();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should not respond to clicks on the document', function () {
$(document).trigger('click');
expect(this.viewHasClass('is-active')).toBeFalsy();
});
});
});
+173
View File
@@ -0,0 +1,173 @@
var InfowindowModel = require('../../../../src/geo/ui/infowindow-model');
var InfowindowTemplate = require('../../../../src/geo/map/infowindow-template');
describe('geo/ui/infowindow-model', function () {
it('should set a default template if template is empty', function () {
var infowindowModel = new InfowindowModel({});
expect(infowindowModel.get('template')).toEqual(infowindowModel.DEFAULT_TEMPLATE);
infowindowModel = new InfowindowModel({ template: '' });
expect(infowindowModel.get('template')).toEqual(infowindowModel.DEFAULT_TEMPLATE);
infowindowModel = new InfowindowModel({ template: ' ' });
expect(infowindowModel.get('template')).toEqual(infowindowModel.DEFAULT_TEMPLATE);
});
it('should NOT set a default template if template is present', function () {
var infowindowModel = new InfowindowModel({ template: 'template' });
expect(infowindowModel.get('template')).toEqual('template');
});
describe('.setContent', function () {
it('should NOT include empty fields', function () {
var infowindowModel = new InfowindowModel({ fields: [{ name: 'NAME', title: true }, { name: 'SOMETHING', title: true }] });
infowindowModel.updateContent({ NAME: 'CartoDB' }, { showEmptyFields: false });
expect(infowindowModel.get('content').fields).toEqual([{ name: 'NAME', title: 'NAME', value: 'CartoDB', index: 0 }]);
});
it('should include empty fields', function () {
var infowindowModel = new InfowindowModel({ fields: [{ name: 'NAME', title: true }, { name: 'SOMETHING', title: true }] });
infowindowModel.updateContent({ NAME: 'CartoDB' }, { showEmptyFields: true });
expect(infowindowModel.get('content').fields).toEqual([{ name: 'NAME', title: 'NAME', value: 'CartoDB', index: 0 }, { name: 'SOMETHING', title: 'SOMETHING', value: null, index: 1 }]);
});
});
describe('.contentForFields', function () {
it('should return the title and value of each field', function () {
var attributes = { field1: 'value1' };
var fields = [{ name: 'field1', title: true }];
var content = InfowindowModel.contentForFields(attributes, fields, {});
expect(content.fields.length).toEqual(1);
expect(content.fields[0].title).toEqual('field1');
expect(content.fields[0].value).toEqual('value1');
});
it('should not return the title if not specified', function () {
var attributes = { field1: 'value1' };
var fields = [{ name: 'field1' }]; // Field doesn't have a title attribute
var content = InfowindowModel.contentForFields(attributes, fields, {});
expect(content.fields.length).toEqual(1);
expect(content.fields[0].title).toEqual(null);
});
it('should return the index of each field', function () {
var attributes = { field1: 'value1', field2: 'value2' };
var fields = [{ name: 'field1' }, { name: 'field2' }];
var content = InfowindowModel.contentForFields(attributes, fields, {});
expect(content.fields.length).toEqual(2);
expect(content.fields[0].index).toEqual(0);
expect(content.fields[1].index).toEqual(1);
});
it('should return empty fields', function () {
var attributes = { field1: 'value1' };
var fields = [{ name: 'field1' }, { name: 'field2' }];
var options = { showEmptyFields: true };
var content = InfowindowModel.contentForFields(attributes, fields, options);
expect(content.fields.length).toEqual(2);
expect(content.fields[0]).toEqual({
name: 'field1',
title: null,
value: 'value1',
index: 0
});
expect(content.fields[1]).toEqual({
name: 'field2',
title: null,
value: null,
index: 1
});
});
it('should not return empty fields', function () {
var attributes = { field1: 'value1' };
var fields = [{ name: 'field1' }, { name: 'field2' }];
var options = { showEmptyFields: false };
var content = InfowindowModel.contentForFields(attributes, fields, options);
expect(content.fields.length).toEqual(1);
expect(content.fields[0]).toEqual({
name: 'field1',
title: null,
value: 'value1',
index: 0
});
});
it('should not return fields with a null value', function () {
var attributes = { field1: 'wadus', field2: null };
var fields = [{ name: 'field1' }, { name: 'field2' }];
var content = InfowindowModel.contentForFields(attributes, fields, {});
expect(content.fields.length).toEqual(1);
expect(content.fields[0]).toEqual({
name: 'field1',
title: null,
value: 'wadus',
index: 0
});
});
it('should return the attributes as data', function () {
var attributes = { field1: 'value1' };
var fields = [{ name: 'field1' }];
var content = InfowindowModel.contentForFields(attributes, fields, {});
expect(content.data).toEqual(attributes);
});
it('should return an empty field when no data is available', function () {
var attributes = {};
var fields = [{ name: 'field1' }, { name: 'field2' }];
var content = InfowindowModel.contentForFields(attributes, fields, {});
expect(content.fields.length).toEqual(1);
expect(content.fields[0]).toEqual({
title: null,
value: 'No data available',
index: 0,
type: 'empty'
});
});
});
describe('.setInfowindowTemplate', function () {
it('should only pick and set specific attributes from the given template', function () {
var infowindowModel = new InfowindowModel({ template: 'template' });
infowindowModel.setInfowindowTemplate(new InfowindowTemplate({
template: 'new_template',
something: 'something'
}));
expect(infowindowModel.get('template')).toEqual('new_template');
expect(infowindowModel.get('something')).toBeUndefined();
});
it('should reject attributes from the template that have a falsy value', function () {
var infowindowModel = new InfowindowModel({ template: 'template' });
infowindowModel.setInfowindowTemplate(new InfowindowTemplate({
template: ''
}));
// Attributes have NOT changed
expect(infowindowModel.get('template')).toEqual('template');
infowindowModel.setInfowindowTemplate(new InfowindowTemplate({
template: 'new_template'
}));
// Atributtes have changed
expect(infowindowModel.get('template')).toEqual('new_template');
});
});
});
+603
View File
@@ -0,0 +1,603 @@
var $ = require('jquery');
var Backbone = require('backbone');
var Map = require('../../../../src/geo/map');
var MapView = require('../../../../src/geo/map-view');
var InfowindowModel = require('../../../../src/geo/ui/infowindow-model');
var Infowindow = require('../../../../src/geo/ui/infowindow-view');
describe('geo/ui/infowindow-view', function () {
var model, view, mapView, map;
beforeEach(function () {
var container = $('<div>').css('height', '200px');
map = new Map(null, { layersFactory: {} });
mapView = new MapView({
el: container,
mapModel: map,
engine: new Backbone.Model(),
layerViewFactory: jasmine.createSpyObj('layerViewFactory', ['createLayerView']),
layerGroupModel: new Backbone.Model()
});
model = new InfowindowModel({
fields: [
{ name: 'test1', position: 1, title: true },
{ name: 'test2', position: 2, title: true }
]
});
view = new Infowindow({
model: model,
mapView: mapView
});
});
it('should add render when template changes', function () {
spyOn(view, 'render');
model.set('template', 'jaja');
expect(view.render).toHaveBeenCalled();
});
it('should change width of the infowindow when width attribute changes', function () {
spyOn(view, 'render');
view.model.set({
'template': '<div class="js-infowindow"></div>',
'width': 100
});
expect(view.$('.js-infowindow').css('width')).toBe('100px');
});
it('shouldn\'t change width of the infowindow when width attribute is undefined', function () {
spyOn(view, 'render');
view.model.set({
'template': '<div class="js-infowindow"></div>'
});
var previousWidth = view.$('.js-infowindow').css('width');
// Unset the width from the model
view.model.unset('width');
// Width hasn't changed
expect(view.$('.js-infowindow').css('width')).toBe(previousWidth);
});
it('should change maxHeight of the infowindow when maxHeight attribute changes', function () {
spyOn(view, 'render');
view.model.set({
template: '<div class="js-infowindow"><div class="js-content"></div></div>',
maxHeight: 100
});
expect(view.$('.js-content').css('max-height')).toBe('100px');
});
it('should render without alternative_name set', function () {
var template = '<div class="js-infowindow">' +
'<a href="#close" class="cartodb-popup-close-button close">x</a>' +
'<div class="cartodb-popup-content-wrapper">' +
'<div class="js-content">' +
'<ul id="mylist"></ul>' +
'</div>' +
'</div>' +
'<div class="hook"></div>' +
'</div>';
model.unset('alternative_names');
model.set({
content: {
fields: [{
title: 'test',
value: true,
position: 0,
index: 0
}]
},
template: template
});
expect(view.render().$el.html().length).not.toBe(0);
});
it('should render with alternative_name set', function () {
model.set({
content: {
fields: [
{ name: 'jamon1', title: 'jamon1_', value: 'jamon1' }
]
}
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('jamon1');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon1_');
});
it('should render without title', function () {
model.set({
content: {
fields: [
{ name: 'jamon1', title: null, value: 'jamon1' }
]
}
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('jamon1');
expect(item1.find('.CDB-infowindow-subtitle').length).toBe(0);
});
it('should convert value to string when it is a number', function () {
model.set({
content: {
fields: [{
title: 'jamon1',
value: 0,
index: 0
}, {
title: 'jamon2',
value: 1,
index: 1
}]
}
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('0');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon1');
var item2 = view.$el.find('.CDB-infowindow-listItem:nth-child(2)');
expect(item2.find('.CDB-infowindow-title').text()).toEqual('1');
expect(item2.find('.CDB-infowindow-subtitle').text()).toEqual('jamon2');
});
it('should convert value to \'\' when it is undefined', function () {
model.set({
content: {
fields: [{
title: 'jamon', value: undefined
}]
}
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon');
});
it('should convert value to \'\' when it is null', function () {
model.set('content', {
fields: [{
title: 'jamon',
value: null
}]
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon');
});
it('shouldn\'t convert the value if it is empty', function () {
model.set('content', {
fields: [{
title: 'jamon',
value: ''
}]
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon');
});
it('should leave a string as it is', function () {
model.set('content', {
fields: [{
title: 'jamon',
value: 'jamon is testing'
}]
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('jamon is testing');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon');
});
it('should convert value to string when it is a boolean', function () {
model.set('content', {
fields: [
{ title: 'jamon1', value: false },
{ title: 'jamon2', value: true }
]
}, { silent: true });
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.find('.CDB-infowindow-title').text()).toEqual('false');
expect(item1.find('.CDB-infowindow-subtitle').text()).toEqual('jamon1');
var item2 = view.$el.find('.CDB-infowindow-listItem:nth-child(2)');
expect(item2.find('.CDB-infowindow-title').text()).toEqual('true');
expect(item2.find('.CDB-infowindow-subtitle').text()).toEqual('jamon2');
});
it('shouldn\'t convert the value if it is an object or an array', function () {
var template = '<div class="js-infowindow">' +
'<a href="#close" class="cartodb-popup-close-button close">x</a>' +
'<div class="cartodb-popup-content-wrapper">' +
'<div class="js-content">' +
'<ul class="CDB-infowindow-listItem">{{#jamon1}}<li>{{jamon2}}, {{istrue}}, "{{isempty}}", {{isnum}}</li>{{/jamon1}}</ul>' +
'<ul class="CDB-infowindow-listItem">{{#jamon3}}<li>{{.}}</li>{{/jamon3}}</ul>' +
'</div>' +
'</div>' +
'<div class="hook"></div>' +
'</div>';
model.set({
content: {
fields: [
{ name: 'jamon1', title: 'jamon1', value: [{ jamon2: 'jamon2', istrue: true, isempty: '', isnum: 9 }] },
{ name: 'jamon3', title: 'jamon3', value: ['jamon4', 'jamon5'] }
]
},
template: template,
template_type: 'mustache'
});
view.render();
var item1 = view.$el.find('.CDB-infowindow-listItem:nth-child(1)');
expect(item1.text()).toContain('jamon2, true, "", 9');
var item2 = view.$el.find('.CDB-infowindow-listItem:nth-child(2)');
expect(item2.text()).toContain('jamon4jamon5');
});
it('should close the infowindow when user clicks close button', function () {
model.set({
template: '<div><button class="js-close">X</button></div>'
});
view = new Infowindow({
model: model,
mapView: mapView
});
view.render();
// Infowindow is visible
model.set('visibility', true, { silent: true });
expect(model.get('visibility')).toBe(true);
view.$('.js-close').click();
// Infowindow has been closed
expect(model.get('visibility')).toBe(false);
});
describe('custom template', function () {
var model, view;
beforeEach(function () {
var container = $('<div>').css('height', '200px');
map = new Map(null, { layersFactory: {} });
mapView = new MapView({
el: container,
mapModel: map,
engine: new Backbone.Model(),
layerViewFactory: jasmine.createSpyObj('layerViewFactory', ['createLayerView']),
layerGroupModel: new Backbone.Model()
});
model = new InfowindowModel({
template: '<div>{{ test1 }}</div>',
fields: [
{ title: 'test1', position: 1, value: 'x' },
{ title: 'test2', position: 2, value: 'b' }
]
});
view = new Infowindow({
model: model,
mapView: mapView
});
});
it('should compile the template when changes', function () {
view.model.set('template', '<div>{{test1}}</div>');
expect(view.template({ test1: 'new' })).toEqual('<div>new</div>');
});
it('should render properly when there is only a field without title', function () {
view.model.set({
template: ['{{#content.fields}}',
'<li class="CDB-infowindow-listItem">',
'{{#title}}<h5 class="CDB-infowindow-subtitle">{{title}}</h5>{{/title}}',
'{{#value}}<h4 class="CDB-infowindow-title">{{{ value }}}</h4>{{/value}}',
'{{^value}}<h4 class="CDB-infowindow-title">null</h4>{{/value}}',
'</li>',
'{{/content.fields}}'].join(' '),
content: {
fields: [
{ name: 'test1', title: null, position: 0, value: 'jamon' }
]
}
});
view.render();
expect(view.$el.html()).toContain('<li class="CDB-infowindow-listItem"> <h4 class="CDB-infowindow-title">jamon</h4> </li>');
});
it('should render with alternative_name set', function () {
view.model.set({
template: ['{{#content.fields}}',
'<li class="CDB-infowindow-listItem">',
'{{#title}}<h5 class="CDB-infowindow-subtitle">{{title}}</h5>{{/title}}',
'{{#value}}<h4 class="CDB-infowindow-title">{{{ value }}}</h4>{{/value}}',
'{{^value}}<h4 class="CDB-infowindow-title">null</h4>{{/value}}',
'</li>',
'{{/content.fields}}'].join(' '),
content: {
fields: [
{ name: 'test1', title: 'test1_', value: 'test1' }
]
}
});
view.render();
expect(view.$el.html()).toContain('<h5 class="CDB-infowindow-subtitle">test1_</h5> <h4 class="CDB-infowindow-title">test1</h4>');
});
it('shouldn\'t sanitize the fields', function () {
spyOn(view, '_sanitizeField');
view.render();
expect(view._sanitizeField).not.toHaveBeenCalled();
});
it('should sanitize the template output by default', function () {
view.model.set('template', 'no <iframe src="" onload="document.body.appendChild(document.createElement(\'script\')).src=\'http://localhost/xss.js\'"/> no');
view.render();
expect(view.$el.html()).toEqual('no ');
});
it('should allow to override sanitization', function () {
view.model.set({
template: 'no <iframe src="" onload="document.body.appendChild(document.createElement(\'script\')).src=\'http://localhost/xss.js\'"/> no',
sanitizeTemplate: false
});
view.render();
expect(view.$el.html()).toEqual('no <iframe src="" onload="document.body.appendChild(document.createElement(\'script\')).src=\'http://localhost/xss.js\'"></iframe> no');
view.model.set('sanitizeTemplate', null);
view.render();
expect(view.$el.html()).toEqual('no <iframe src="" onload="document.body.appendChild(document.createElement(\'script\')).src=\'http://localhost/xss.js\'"></iframe> no');
var customSanitizeSpy = jasmine.createSpy('sanitizeTemplateSpy').and.returnValue('<p>custom sanitizied result</p>');
view.model.set('sanitizeTemplate', customSanitizeSpy);
expect(customSanitizeSpy).toHaveBeenCalledWith('no <iframe src="" onload="document.body.appendChild(document.createElement(\'script\')).src=\'http://localhost/xss.js\'"/> no');
view.render();
expect(view.$el.html()).toEqual('<p>custom sanitizied result</p>');
view.model.set('sanitizeTemplate', undefined);
view.render();
expect(view.$el.html()).toEqual('no ');
});
});
describe('image template', function () {
var model, view, container, fields, fieldsWithoutURL, fieldsWithInvalidURL, url;
beforeEach(function () {
container = $('<div>').css('height', '200px');
url = 'http://fake.url/image.jpg';
fields = [
{ title: 'test1', position: 1, value: 'http://fake.url/image.jpg' },
{ title: 'test2', position: 2, value: 'b' }
];
fieldsWithInvalidURL = [
{ title: 'test1', position: 1, value: 'invalid_url' },
{ title: 'test2', position: 2, value: 'b' }
];
fieldsWithoutURL = [
{ title: 'test1', position: 1, value: 'x' },
{ title: 'test2', position: 2, value: 'b' }
];
map = new Map(null, { layersFactory: {} });
mapView = new MapView({
el: container,
mapModel: map,
engine: new Backbone.Model(),
layerViewFactory: jasmine.createSpyObj('layerViewFactory', ['createLayerView']),
layerGroupModel: new Backbone.Model()
});
model = new InfowindowModel({
content: {
fields: fields
}
});
view = new Infowindow({
model: model,
mapView: mapView
});
});
it('should get the cover url', function () {
expect(view._getCoverURL()).toEqual(url);
});
it('should validate the cover url', function () {
var url = view._getCoverURL();
expect(view._isValidURL(url)).toEqual(true);
});
it('should accept google chart URLS in the cover', function () {
var url = 'http://chart.googleapis.com/chart?chxl=0:|1990%2F92|2001%2F03|2011%2F13&chxr=1,0,75&chxs=0,676767,11.5,0.5,lt,676767&chxt=x,y&chs=279x210&cht=lc&chco=FF0000&chds=0,69&chd=t:9.5,12.8,15.1,14.6,12.9,10.2,9.5,8.2,7.4,6.2,6,6.5,7.1,7.5,7.9,8,8.2,7.9,7.5,6.9,6.3,5.6&chg=-1,0,0,4&chls=1&chma=0,0,0,25&chm=B,EFEFEF,0,0,0&chtt=Malnourishment+in+&chts=676767,14';
expect(view._isValidURL(url)).toEqual(true);
});
it('shouldn\'t modify the height of the cover when there are several fields', function () {
model.set('content', { fields: fieldsWithoutURL });
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover" style="height:123px"><div class="CDB-hook"></div></div>');
expect(view._containsCover()).toEqual(true);
expect(view.$('.js-cover').height()).toEqual(123);
expect(view.$('.CDB-hook img').length).toEqual(0);
});
it('should add the image cover class in the custom template', function () {
spyOn(view, '_loadCoverFromTemplate').and.callThrough();
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover" style="height: 123px"><img src="http://fake.url" style="height: 100px"></div><div class="CDB-hook"></div></div>');
expect(view._loadCoverFromTemplate).toHaveBeenCalled();
expect(view._containsCover()).toEqual(true);
expect(view.$('.CDB-infowindow-media-item').length).toEqual(1);
});
it('should setup the hook correctly', function () {
spyOn(view, '_loadCoverFromUrl').and.callThrough();
model.set('content', { fields: fields });
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover"></div><div class="CDB-hook"></div></div>');
expect(view._loadCoverFromUrl).toHaveBeenCalled();
view._onLoadImageSuccess();
expect(view.$('.CDB-hook img').length).toEqual(1);
expect(view.$('.CDB-hook img').attr('src')).toEqual('http://fake.url/image.jpg');
});
it('should detect if the infowindow has a cover', function () {
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover"></div></div>');
expect(view._containsCover()).toEqual(true);
});
it('should render the loader for infowindows with cover', function () {
model.set('content', { fields: fields });
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover"></div></div>');
expect(view.$('.js-cover .js-loader').length).toEqual(1);
});
it('should append the image', function () {
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover"></div></div>');
expect(view.$('img').length).toEqual(1);
});
it('shouldn\'t append an image if it\'s not valid', function () {
model.set('content', { fields: fieldsWithoutURL });
model.set('template', '<div class="js-infowindow header" data-cover="true"><div class="js-cover"></div></div>');
expect(view.$('img').length).toEqual(0);
});
it('shouldn\'t append the image if the theme doesn\'t have cover', function () {
model.set('content', { fields: fields });
model.set('template', '<div class="js-cover"></div>');
expect(view.$('img').length).toEqual(0);
});
it('should append a non-valid error message if the image is not valid', function () {
model.set('content', { fields: fieldsWithInvalidURL });
model.set('template', '<div class="js-infowindow header" data-cover="true"><div class="js-cover"></div></div>');
expect(view.$el.find('.CDB-infowindow-fail').length).toEqual(1);
});
it('should add header class', function () {
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-header"></div></div>');
expect(view.$el.find('.has-header').length).toEqual(1);
});
it('should add has-header-image class', function () {
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover"></div></div>');
expect(view.$el.find('.has-header-image').length).toEqual(1);
});
it('should add has-fields class', function () {
model.set('content', { fields: fields });
model.set('template', '<div class="js-infowindow"><div class="js-content"></div></div>');
expect(view.$el.find('.has-fields').length).toEqual(1);
});
it('should load image hook if fields is 2 or less and image height is equal or less than content height', function () {
model.set('template', '<div class="js-infowindow" data-cover="true"><div class="js-cover" style="height: 123px"><img src="http://fake.url" class="CDB-infowindow-media-item"></div><div class="CDB-hook"></div></div>');
spyOn(view, '_loadImageHook').and.callThrough();
view.$el.height(100);
view.$('.CDB-infowindow-media-item').height(200);
view._onLoadImageSuccess();
expect(view._loadImageHook).toHaveBeenCalled();
view.$('.CDB-infowindow-media-item').height(90);
view._loadImageHook.calls.reset();
view._onLoadImageSuccess();
expect(view._loadImageHook).not.toHaveBeenCalled();
view.$('.CDB-infowindow-media-item').height(200);
view._loadImageHook.calls.reset();
fields.push({ title: 'test3', position: 3, value: 'c' });
model.set('fields', fields);
view._onLoadImageSuccess();
expect(view._loadImageHook).not.toHaveBeenCalled();
});
it('should stop the loader and show an error if url is invalid', function () {
spyOn(view, '_stopCoverLoader').and.callThrough();
spyOn(view, '_showInfowindowImageError').and.callThrough();
model.set({
fields: fieldsWithInvalidURL,
template: '<div class="js-infowindow" data-cover="true"><div class="js-cover" style="height: 123px"><img src="invalid.url" style="height: 100px"></div><div class="CDB-hook"></div></div>'
});
view.render();
expect(view._stopCoverLoader).toHaveBeenCalled();
expect(view._showInfowindowImageError).toHaveBeenCalled();
});
it('should stop the loader and show an error if url is invalid', function () {
spyOn(view, '_stopCoverLoader').and.callThrough();
spyOn(view, '_showInfowindowImageError').and.callThrough();
model.set({
fields: fieldsWithInvalidURL,
template: '<div class="js-infowindow" data-cover="true"><div class="js-cover" style="height: 123px"><img src="invalid.url" style="height: 100px"></div><div class="CDB-hook"></div></div>'
});
view.render();
expect(view._stopCoverLoader).toHaveBeenCalled();
expect(view._showInfowindowImageError).toHaveBeenCalled();
});
it('should load the cover from template if it already contains the template', function () {
spyOn(view, '_loadCoverFromTemplate').and.callThrough();
model.set({
fields: fieldsWithInvalidURL,
template: '<div class="js-infowindow" data-cover="true"><div class="js-cover" style="height: 123px"><img src="http://fake.url" style="height: 100px"></div><div class="CDB-hook"></div></div>'
});
view.render();
expect(view._loadCoverFromTemplate).toHaveBeenCalled();
});
it('should load the cover from url if it doesn\'t contain the template', function () {
spyOn(view, '_loadCoverFromUrl').and.callThrough();
model.set({
fields: fieldsWithInvalidURL,
template: '<div class="js-infowindow" data-cover="true"><div class="CDB-hook"></div></div>'
});
view.render();
expect(view._loadCoverFromUrl).toHaveBeenCalled();
});
});
});
@@ -0,0 +1,132 @@
var LegendViewBase = require('../../../../../../src/geo/ui/legends/base/legend-view-base.js');
var LegendModelBase = require('../../../../../../src/geo/map/legends/legend-model-base.js');
var createEngine = require('../../../../fixtures/engine.fixture.js');
var MyLegendModel = LegendModelBase.extend({
TYPE: 'something',
isAvailable: function () {
return true;
}
});
var MyLegendView = LegendViewBase.extend({
_getCompiledTemplate: function () {
return '<h1>It Works!</h1>';
}
});
describe('geo/ui/legends/legend-view-base.js', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.model = new MyLegendModel({
title: 'My Beautiful Legend',
preHTMLSnippet: '<p>before</p>',
postHTMLSnippet: '<p>after</p>',
visible: true
}, {
engine: engineMock
});
this.myLegend = new MyLegendView({
model: this.model,
placeholderTemplate: function () {
return '<p>Placeholder</p>';
}
});
});
describe('if model is loading', function () {
beforeEach(function () {
this.model.set('state', 'loading');
this.myLegend.render();
});
it('should mark legend as loading', function () {
expect(this.myLegend.$el.hasClass('is-loading')).toBeTruthy();
});
it('should only render a placeholder', function () {
expect(this.myLegend.$el.html()).toEqual('<p>Placeholder</p>');
});
});
describe('if model has errors', function () {
beforeEach(function () {
this.model.set('state', 'error');
this.myLegend.render();
});
it('should NOT mark legend as loading', function () {
expect(this.myLegend.$el.hasClass('is-loading')).toBeFalsy();
});
it('should render a placeholder', function () {
expect(this.myLegend.$el.html()).toMatch('Placeholder');
});
it('should render an error', function () {
expect(this.myLegend.$el.html()).toMatch('Something went wrong');
});
});
describe('if model has been loaded and has no data', function () {
beforeEach(function () {
this.model.set('state', 'success');
spyOn(this.model, 'isAvailable').and.returnValue(false);
this.myLegend.render();
});
it('should NOT mark legend as loading', function () {
expect(this.myLegend.$el.hasClass('is-loading')).toBeFalsy();
});
it('should render a placeholder', function () {
expect(this.myLegend.$el.html()).toMatch('Placeholder');
});
it('should render a warning', function () {
expect(this.myLegend.$el.html()).toMatch('No data available');
});
});
describe('if model has been loaded and has data', function () {
beforeEach(function () {
this.model.set('state', 'success');
this.myLegend.render();
});
it('should NOT mark legend as loading', function () {
expect(this.myLegend.$el.hasClass('is-loading')).toBeFalsy();
});
it('should NOT render a placeholder', function () {
expect(this.myLegend.$el.html()).not.toMatch('Placeholder');
});
it('should render properly', function () {
var html = this.myLegend.$el.html().split('\n');
expect(html[0]).toMatch('My Beautiful Legend');
expect(html[1]).toEqual('<p>before</p>');
expect(html[2]).toEqual('<h1>It Works!</h1>');
expect(html[3]).toEqual('<p>after</p>');
});
it('should sanitize preHTMLSnippet, compiled template and postHTMLSnippet', function () {
this.model.set({
preHTMLSnippet: '<p>before<script>alert("before");</script></p>',
postHTMLSnippet: '<p>after<script>alert("after");</script></p>'
});
spyOn(this.myLegend, '_getCompiledTemplate').and.returnValue('<h1>It Works!<script>alert("It Works!");</script></h1>');
this.myLegend.render();
var html = this.myLegend.$el.html().split('\n');
expect(html[1]).toEqual('<p>before</p>');
expect(html[2]).toEqual('<h1>It Works!</h1>');
expect(html[3]).toEqual('<p>after</p>');
});
});
});
@@ -0,0 +1,85 @@
var LegendViewBubble = require('../../../../../../src/geo/ui/legends/bubble/legend-view.js');
var BubbleLegendModel = require('../../../../../../src/geo/map/legends/bubble-legend-model.js');
var createEngine = require('../../../../fixtures/engine.fixture.js');
describe('geo/ui/legends/bubbles/legend-view.js', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.model = new BubbleLegendModel({
title: 'Bubble',
type: 'bubble',
avg: 30,
fillColor: '#fabada',
postHTMLSnippet: '',
preHTMLSnippet: '',
prefix: '',
suffix: '',
sizes: [2, 4, 8],
values: [5, 10, 20, 40],
visible: true
}, {
engine: engineMock
});
spyOn(this.model, 'isAvailable').and.returnValue(true);
this.legendView = new LegendViewBubble({
model: this.model,
placeholderTemplate: function () {
return '<p>Placeholder</p>';
}
});
});
describe('render', function () {
beforeEach(function () {
this.model.set('state', 'success');
this.legendView.render();
});
it('should render bubbles', function () {
expect(this.legendView.$('.js-bubbleItem').length).toBe(4);
expect(this.legendView._calculateBubbleSizes()).toEqual([100, 50, 25]);
});
it('should place avg properly', function () {
// values: [5, 10, 20, 40];
// range: [0, 25, 50, 100];
this.model.set('avg', 5); // at the beginning of range [0, 25]
expect(this.legendView._calculateAverageSize()).toBe(0);
this.model.set('avg', 7.5); // in the middle of range [0, 25]
expect(this.legendView._calculateAverageSize()).toBe(12.5);
this.model.set('avg', 10); // at the end of range [0, 25]
expect(this.legendView._calculateAverageSize()).toBe(25);
this.model.set('avg', 15); // in the middle of range [25, 50]
expect(this.legendView._calculateAverageSize()).toBe(37.5);
this.model.set('avg', 19); // almost at the top of [25, 50]
expect(this.legendView._calculateAverageSize()).toBe(25 + (50 - 25) * (9 / 10));
this.model.set('avg', 25); // in a quarter of range [50, 100]
expect(this.legendView._calculateAverageSize()).toBe(62.5);
this.model.set('avg', 30); // in the middle of range [50, 100]
expect(this.legendView._calculateAverageSize()).toBe(75); // (50 + (100 - 50) * 0.5)
this.model.set('avg', 35); // in the three quarter of range [50, 100]
expect(this.legendView._calculateAverageSize()).toBe(87.5); // (50 + (100 - 50) * (15 / 20)
this.model.set('avg', 40);
expect(this.legendView._calculateAverageSize()).toBe(100);
});
it('should render custom labels properly', function () {
this.model.set('topLabel', 'foo');
expect(this.legendView.$('.Bubble-numbersItem').length).toBe(1);
expect(this.legendView.$('.Bubble-numbersItem').eq(0).text()).toBe('foo');
expect(this.legendView.$('.Bubble-average').length).toBe(0);
});
});
});
@@ -0,0 +1,67 @@
var CustomLegendViewChoropleth = require('../../../../../../src/geo/ui/legends/custom-choropleth/legend-view.js');
var ChoroplethLegendModel = require('../../../../../../src/geo/map/legends/choropleth-legend-model.js');
var createEngine = require('../../../../fixtures/engine.fixture.js');
describe('geo/ui/legends/custom-choropleth/legend-view.js', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.model = new ChoroplethLegendModel({
title: 'Foo',
type: ' choropleth',
postHTMLSnippet: '',
preHTMLSnippet: '',
prefix: '',
suffix: '',
leftLabel: 'low',
rightLabel: 'high',
colors: [
{
label: '1',
value: '#ffc6c4'
}, {
label: '',
value: '#ee919b'
}, {
label: '',
value: '#cc607d'
}, {
label: '',
value: '#9e3963'
}, {
label: '127',
value: '#672044'
}
],
visible: true
}, {
engine: engineMock
});
spyOn(this.model, 'isAvailable').and.returnValue(true);
this.legendView = new CustomLegendViewChoropleth({
model: this.model,
placeholderTemplate: function () {
return '<p>Placeholder</p>';
}
});
});
describe('render', function () {
beforeEach(function () {
this.model.set('state', 'success');
this.legendView.render();
});
it('should render choropleth', function () {
expect(this.legendView.$('.Legend-choropleth').length).toBe(1);
expect(this.legendView.$('h3').text()).toContain('Foo');
expect(this.legendView.$('h3 ~ div p').length).toBe(2);
expect(this.legendView.$('h3 ~ div p').length).toBe(2);
expect(this.legendView.$('h3 ~ div p').eq(0).text()).toContain('low');
expect(this.legendView.$('h3 ~ div p').eq(1).text()).toContain('high');
});
});
});
@@ -0,0 +1,78 @@
var LegendViewChoropleth = require('../../../../../../src/geo/ui/legends/choropleth/legend-view.js');
var ChoroplethLegendModel = require('../../../../../../src/geo/map/legends/choropleth-legend-model.js');
var createEngine = require('../../../../fixtures/engine.fixture.js');
describe('geo/ui/legends/choropleth/legend-view.js', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.model = new ChoroplethLegendModel({
title: 'Foo',
type: ' choropleth',
avg: 64,
postHTMLSnippet: '',
preHTMLSnippet: '',
prefix: '',
suffix: '',
colors: [
{
label: '1',
value: '#ffc6c4'
}, {
label: '',
value: '#ee919b'
}, {
label: '',
value: '#cc607d'
}, {
label: '',
value: '#9e3963'
}, {
label: '127',
value: '#672044'
}
],
min: 1,
max: 127,
visible: true
}, {
engine: engineMock
});
spyOn(this.model, 'isAvailable').and.returnValue(true);
this.legendView = new LegendViewChoropleth({
model: this.model,
placeholderTemplate: function () {
return '<p>Placeholder</p>';
}
});
});
describe('render', function () {
beforeEach(function () {
this.model.set('state', 'success');
this.legendView.render();
});
it('should render choropleth', function () {
expect(this.legendView.$('.Legend-choropleth').length).toBe(1);
expect(this.legendView.$('.Legend-choroplethAverage').length).toBe(1);
expect(this.legendView.$('.Legend-choroplethAverage').text()).toContain('64');
expect(this.legendView.$('h3').text()).toContain('Foo');
expect(this.legendView.$('h3 ~ div p').length).toBe(2);
expect(this.legendView.$('h3 ~ div p').length).toBe(2);
expect(this.legendView.$('h3 ~ div p').eq(0).text()).toContain('1');
expect(this.legendView.$('h3 ~ div p').eq(1).text()).toContain('127');
});
it('should hide AVG if custom labels', function () {
this.model.set('leftLabel', 'Bar');
expect(this.legendView.$('.Legend-choroplethAverage').length).toBe(0);
expect(this.legendView.$('h3 ~ div p').eq(0).text()).toContain('Bar');
expect(this.legendView.$('h3 ~ div p').eq(1).text()).toContain('127');
});
});
});
@@ -0,0 +1,131 @@
var $ = require('jquery');
var ImgLoaderView = require('../../../../../../src/geo/ui/legends/base/img-loader-view.js');
describe('geo/ui/legends/custom/img-loader-view', function () {
var svgResponse = {
status: 200,
contentType: 'image/svg+xml',
responseText: '<svg xmlns:a="genius.com" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"><path></path></svg>'
};
beforeEach(function () {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*/logo.svg.*'))
.andReturn(svgResponse);
this.svgUrl = 'http://image.io/logo.svg';
this.color = '#cebada';
var $el = $('<div class="js-image-container" data-icon="' + this.svgUrl + '" data-color="' + this.color + '">');
this.imageClass = 'Legend-fillImageAsset';
this.view = new ImgLoaderView({
el: $el,
imageClass: this.imageClass
});
});
afterEach(function () {
jasmine.Ajax.uninstall();
});
describe('._loadImage', function () {
it('should render resource if format is not SVG', function () {
this.imgUrl = 'http://www.imageserver.com/image.png';
var $pngEl = $('<div class="js-image-container" data-icon="' + this.imgUrl + '" data-color="' + this.color + '">');
this.imageClass = 'Legend-fillImageAsset';
var view = new ImgLoaderView({
el: $pngEl,
imageClass: this.imageClass
});
view._loadImage();
var $img = view.$('img');
var classes = $img.attr('class');
var src = $img.attr('src');
var crossOrigin = $img.attr('crossOrigin');
$img.empty(); // To avoid DOM requests to the image file
expect(classes.indexOf('js-image') > -1).toBe(true);
expect(classes.indexOf(this.imageClass) > -1).toBe(true);
expect(src).toBe(this.imgUrl + '?req=markup');
expect(crossOrigin).toBe('anonymous');
});
it('should render SVG tag if resource is SVG file', function () {
this.view._loadImage();
var $svg = this.view.$('svg');
var classes = $svg.attr('class');
var svgFill = $svg.css('fill').toLowerCase();
var pathFill = $svg.find('path')[0].style['fill'].toLowerCase();
expect($svg.length).toBe(1);
expect(classes.indexOf('js-image') > -1).toBe(true);
expect(classes.indexOf(this.imageClass) > -1).toBe(true);
expect(svgFill === 'rgb(206, 186, 218)' || svgFill === this.color).toBe(true);
expect(pathFill).toBe('inherit');
});
});
describe('._requestImage', function () {
var obj = {
callback: function () {}
};
it('successful caches the response and calls the callback', function () {
var expectedUrl = this.svgUrl + '?req=ajax';
spyOn(obj, 'callback');
this.view._lastImage.url = null;
this.view._lastImage.content = null;
jasmine.Ajax.requests.reset();
this.view._requestImage(this.svgUrl, obj.callback);
expect(jasmine.Ajax.requests.mostRecent().url).toBe(expectedUrl);
expect(obj.callback).toHaveBeenCalled();
expect(this.view._lastImage.url).toBe(expectedUrl);
expect(this.view._lastImage.content).toBeDefined();
});
it('_requestImage with cached response, does not make an Ajax call and it calls the callback', function () {
var expectedUrl = this.svgUrl + '?req=ajax';
spyOn(obj, 'callback');
this.view._requestImage(this.svgUrl, obj.callback);
jasmine.Ajax.requests.reset();
this.view._requestImage(this.svgUrl, obj.callback);
expect(jasmine.Ajax.requests.mostRecent()).toBeUndefined();
expect(obj.callback).toHaveBeenCalled();
expect(this.view._lastImage.url).toBe(expectedUrl);
expect(this.view._lastImage.content).toBeDefined();
});
it('_requestImage with error response, throws an error', function () {
this.view._lastImage.url = null;
this.view._lastImage.content = null;
jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*/logo.svg.*'))
.andReturn({ status: 500 });
var foo = function () {
this.view._requestImage(this.svgUrl, obj.callback);
};
expect(foo).toThrow();
});
});
describe('._isSVG', function () {
it('behaves properly', function () {
expect(this.view._isSVG('http://image.io/image.svg')).toBe(true);
expect(this.view._isSVG('http://image.io/image.svg?req=ajax')).toBe(true);
expect(this.view._isSVG('http://image.io/image.png')).toBe(false);
expect(this.view._isSVG('http://image.io/image.png?req=markup')).toBe(false);
expect(this.view._isSVG('')).toBe(false);
});
});
});
@@ -0,0 +1,65 @@
var LegendViewCustom = require('../../../../../../src/geo/ui/legends/custom/legend-view.js');
var CustomLegendModel = require('../../../../../../src/geo/map/legends/custom-legend-model.js');
var createEngine = require('../../../../fixtures/engine.fixture.js');
describe('geo/ui/legends/custom/legend-view.js', function () {
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.model = new CustomLegendModel({
title: 'Foo',
type: 'custom',
html: '',
postHTMLSnippet: '',
preHTMLSnippet: '',
items: [
{
title: 'foo',
color: '#ffc6c4'
}, {
title: 'bar',
color: '#ff6600'
}, {
title: 'bar',
color: '#ff6600',
icon: 'https://s3.amazonaws.com/com.cartodb.users-assets.production/maki-icons/circle-stroked-18.svg'
}
],
visible: true
}, {
engine: engineMock
});
spyOn(this.model, 'isAvailable').and.returnValue(true);
this.legendView = new LegendViewCustom({
model: this.model,
placeholderTemplate: function () {
return '<p>Placeholder</p>';
}
});
});
describe('render', function () {
beforeEach(function () {
this.model.set('state', 'success');
this.legendView.render();
});
it('should render custom', function () {
expect(this.legendView.$('li').length).toBe(3);
expect(this.legendView.$('.Legend-categoryCircle').length).toBe(2);
expect(this.legendView.$('.Legend-categoryIcon').length).toBe(1);
});
it('should render custom html', function () {
this.model.set('html', '<ul><li>Foo</li></ul>');
this.legendView.render();
expect(this.legendView.$('li').length).toBe(1);
expect(this.legendView.$('.Legend-categoryCircle').length).toBe(0);
expect(this.legendView.$('li').eq(0).text()).toContain('Foo');
});
});
});
@@ -0,0 +1,109 @@
var $ = require('jquery');
var Backbone = require('backbone');
var LayerLegendsView = require('../../../../../src/geo/ui/legends/layer-legends-view');
var CartoDBLayer = require('../../../../../src/geo/map/cartodb-layer');
describe('geo/ui/legends/layer-legends-view', function () {
beforeEach(function () {
var engineMock = new Backbone.Model();
engineMock.reload = jasmine.createSpy('reload');
this.settingsModel = new Backbone.Model({
showLegends: true,
showLayerSelector: true,
layerSelectorEnabled: true
});
this.cartoDBLayer = new CartoDBLayer({
layer_name: 'CartoDB Layer #1',
legends: [
{ type: 'bubble', title: 'Bubble Legend' },
{ type: 'category', title: 'Category Legend' }
]
}, { engine: engineMock });
this.tryContainerVisibility = jasmine.createSpy('tryContainerVisibility');
this.layerLegendsView = new LayerLegendsView({
model: this.cartoDBLayer,
settingsModel: this.settingsModel,
tryContainerVisibility: this.tryContainerVisibility
});
this.layerLegendsView.render();
$('body').append(this.layerLegendsView.$el);
});
afterEach(function () {
this.layerLegendsView.$el.remove();
});
it('should render all legends', function () {
// Legends of all types have been rendered
expect(this.layerLegendsView.$('.CDB-Legend-item').length).toEqual(6);
});
it('should trigger a "render" event when the view is rendered', function () {
var callback = jasmine.createSpy('callback');
this.layerLegendsView.on('render', callback);
this.layerLegendsView.render();
expect(callback).toHaveBeenCalled();
});
it('should update the name of the layer when it changes', function () {
expect(this.layerLegendsView.$el.html()).toMatch('CartoDB Layer #1');
this.cartoDBLayer.set('layer_name', 'A NEW layer name');
expect(this.layerLegendsView.$el.html()).not.toMatch('CartoDB Layer #1');
expect(this.layerLegendsView.$el.html()).toMatch('A NEW layer name');
});
it('should hide/show the layer if check next to title is clicked', function () {
expect(this.cartoDBLayer.isVisible()).toBeTruthy();
this.layerLegendsView.$('.js-toggle-layer').trigger('click');
expect(this.cartoDBLayer.isVisible()).toBeFalsy();
this.layerLegendsView.$('.js-toggle-layer').trigger('click');
expect(this.cartoDBLayer.isVisible()).toBeTruthy();
});
it('should uncheck/check the check if layer is hidden/shown', function () {
expect(this.layerLegendsView.$('.js-toggle-layer').is(':checked')).toBe(true);
this.cartoDBLayer.hide();
expect(this.layerLegendsView.$('.js-toggle-layer').is(':checked')).toBe(false);
this.cartoDBLayer.show();
expect(this.layerLegendsView.$('.js-toggle-layer').is(':checked')).toBe(true);
});
it('should hide/show legend views when layer is hidden/shown', function () {
this.cartoDBLayer.hide();
expect(this.layerLegendsView.$('.js-legends').length).toBe(0);
this.cartoDBLayer.show();
expect(this.layerLegendsView.$('.js-legends').length).toBe(1);
});
it('should be hidden if no legends are rendered', function () {
spyOn(this.cartoDBLayer.legends, 'hasAnyLegend').and.returnValue(false);
this.settingsModel.set('showLayerSelector', false);
this.layerLegendsView.render();
expect(this.layerLegendsView.$el.is(':empty')).toBe(true);
});
it('should not render layer selector checkbox if not embed', function () {
this.settingsModel.set('layerSelectorEnabled', false);
this.layerLegendsView.render();
expect(this.layerLegendsView.$('.js-toggle-layer').length).toBe(0);
});
});
@@ -0,0 +1,103 @@
var Backbone = require('backbone');
var LegendsView = require('../../../../../src/geo/ui/legends/legends-view');
var TileLayer = require('../../../../../src/geo/map/tile-layer');
var CartoDBLayer = require('../../../../../src/geo/map/cartodb-layer');
var TorqueLayer = require('../../../../../src/geo/map/torque-layer');
var LayersCollection = require('../../../../../src/geo/map/layers');
var MockFactory = require('../../../../helpers/mockFactory');
describe('geo/ui/legends/legends-view', function () {
beforeEach(function () {
var engineMock = new Backbone.Model();
engineMock.reload = jasmine.createSpy('reload');
this.tileLayer = new TileLayer(null, { engine: {} });
this.cartoDBLayer1 = new CartoDBLayer({
source: MockFactory.createAnalysisModel({ id: 'a1' }),
layer_name: 'CartoDB Layer #1',
legends: []
}, { engine: engineMock });
this.cartoDBLayer2 = new CartoDBLayer({
source: MockFactory.createAnalysisModel({ id: 'a2' }),
layer_name: 'CartoDB Layer #2',
legends: []
}, { engine: engineMock });
this.torqueLayer = new TorqueLayer({ layer_name: 'Torque Layer #3', legends: [] }, { engine: engineMock });
this.layersCollection = new LayersCollection([]);
this.layersCollection.reset([this.tileLayer, this.cartoDBLayer1, this.cartoDBLayer2, this.torqueLayer]);
this.settingsModel = new Backbone.Model({
showLegends: true,
showLayerSelector: true,
layerSelectorEnabled: true
});
this.legendsView = new LegendsView({
layersCollection: this.layersCollection,
settingsModel: this.settingsModel
});
this.legendsView.render();
});
var getLayerLegendTitles = function (legendsView) {
var titleElements = legendsView.$('.CDB-LayerLegends > .CDB-Text').get();
return titleElements.map(function (element) { return element.textContent.trim(); });
};
it('should render legends for CartoDB and torque layers', function () {
expect(this.legendsView.$('.CDB-LayerLegends').length).toEqual(3);
expect(getLayerLegendTitles(this.legendsView)).toEqual(['Torque Layer #3', 'CartoDB Layer #2', 'CartoDB Layer #1']);
});
it('should re-render when a layer with legends is removed from the collection of layers', function () {
this.cartoDBLayer1.remove();
expect(this.legendsView.$('.CDB-LayerLegends').length).toEqual(2);
expect(getLayerLegendTitles(this.legendsView)).toEqual(['Torque Layer #3', 'CartoDB Layer #2']);
});
it('should re-render when new layers with legends are added to the collection of layers', function () {
this.cartoDBLayer1.remove();
this.layersCollection.add(this.cartoDBLayer1, { at: 1 });
expect(this.legendsView.$('.CDB-LayerLegends').length).toEqual(3);
expect(getLayerLegendTitles(this.legendsView)).toEqual(['Torque Layer #3', 'CartoDB Layer #2', 'CartoDB Layer #1']);
});
it('should re-render when a layer with legends is moved', function () {
expect(getLayerLegendTitles(this.legendsView)).toEqual(['Torque Layer #3', 'CartoDB Layer #2', 'CartoDB Layer #1']);
this.layersCollection.moveCartoDBLayer(2, 1);
expect(getLayerLegendTitles(this.legendsView)).toEqual(['Torque Layer #3', 'CartoDB Layer #1', 'CartoDB Layer #2']);
});
it('should show legends if showLegends is true', function () {
this.settingsModel.set('showLegends', true);
expect(this.legendsView.$('.Legends').length).toBe(3);
});
it('should hide legends if showLegends is false', function () {
this.settingsModel.set('showLegends', false);
expect(this.legendsView.$('.Legends').length).toBe(0);
});
it('should show layer selector if showLayerSelector is true', function () {
this.settingsModel.set('showLayerSelector', true);
expect(this.legendsView.$('input').length).toBe(3);
});
it('should hide layer selector if showLayerSelector is false', function () {
this.settingsModel.set('showLayerSelector', false);
expect(this.legendsView.$('input').length).toBe(0);
});
it('should hide element if showLayerSelector changes to false and no legends are visible', function () {
spyOn(this.cartoDBLayer1.legends, 'hasAnyLegend').and.returnValue(false);
spyOn(this.cartoDBLayer2.legends, 'hasAnyLegend').and.returnValue(false);
expect(this.legendsView.el.style.display).toEqual('block');
this.settingsModel.set('showLayerSelector', false);
expect(this.legendsView.el.style.display).toEqual('none');
});
});
+95
View File
@@ -0,0 +1,95 @@
var $ = require('jquery');
var _ = require('underscore');
var LimitsView = require('../../../../src/geo/ui/limits/limits-view');
var Map = require('../../../../src/geo/map');
describe('geo/ui/limits', function () {
beforeEach(function () {
// Disable defer
spyOn(_, 'delay').and.callFake(function (func) {
return function () {
func.apply(this, arguments);
};
});
this.keyEsc = function () {
var event = $.Event('keydown');
event.keyCode = 27; // ESC
$(document).trigger(event);
};
this.map = new Map(null, {
layersFactory: {}
});
spyOn(this.map, 'bind').and.callThrough();
spyOn(LimitsView.prototype, 'render').and.callThrough();
this.view = new LimitsView({
map: this.map
});
this.view.render();
this.$button = this.view.$('.js-button');
this.$text = this.view.$('.js-text');
this.viewHasClass = function (className) {
return this.view.$el.hasClass(className);
};
});
describe('render', function () {
it('should render properly', function () {
expect(this.$button.length).toBe(1);
expect(this.$text.length).toBe(1);
});
});
describe('when the limits overlay is displayed', function () {
beforeEach(function () {
this.$button.click();
});
it('should have .is-active class', function () {
expect(this.viewHasClass('is-active')).toBeTruthy();
});
it('should hide the overlay when .js-button is clicked', function () {
this.$button.click();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should hide the overlay when ESC is pressed', function () {
this.keyEsc();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should hide the overlay when user clicks on the document', function () {
$(document).trigger('click');
expect(this.viewHasClass('is-active')).toBeFalsy();
});
});
describe('when the limits overlay is hidden', function () {
beforeEach(function () {
this.$button.click();
this.keyEsc();
});
it('should not have .is-active class', function () {
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should show the overlay when js-button is clicked', function () {
this.$button.click();
expect(this.viewHasClass('is-active')).toBeTruthy();
});
it('should ignore the ESC key', function () {
this.keyEsc();
expect(this.viewHasClass('is-active')).toBeFalsy();
});
it('should ignore clicks in the document', function () {
$(document).trigger('click');
expect(this.viewHasClass('is-active')).toBeFalsy();
});
});
});
+217
View File
@@ -0,0 +1,217 @@
var Backbone = require('backbone');
var OverlaysView = require('../../../../src/geo/ui/overlays-view.js');
var Engine = require('../../../../src/engine');
var createEngine = require('../../fixtures/engine.fixture.js');
describe('src/geo/ui/overlays-view.js', function () {
var mapModelMock = new Backbone.Model();
var engineMock;
beforeEach(function () {
engineMock = createEngine();
this.overlaysCollection = new Backbone.Collection([
{
type: 'zoom',
order: 2,
options: {
display: true
}
},
{
type: 'search',
order: 1,
options: {
display: false
}
},
{
type: 'loader',
order: 5,
options: {
display: true
}
}
]);
this.view = new OverlaysView({
overlaysCollection: this.overlaysCollection,
engine: engineMock,
visView: new Backbone.View({
model: new Backbone.Model({ showLimitErrors: false })
}),
mapModel: mapModelMock,
mapView: new Backbone.View()
});
this.view.render();
});
it('should render existing overlays', function () {
expect(this.view.$('.CDB-OverlayContainer').length).toEqual(1);
// Some overlays are grouped together in a container
expect(this.view.$('.CDB-OverlayContainer > .CDB-Zoom').length).toEqual(1);
expect(this.view.$('.CDB-OverlayContainer > .CDB-Search').length).toEqual(1);
// Some others are global
expect(this.view.$('> .CDB-Loader').length).toEqual(1);
});
it('should only show overlays with a truthy display option', function () {
expect(this.view.$('.CDB-OverlayContainer > .CDB-Zoom').length).toEqual(1);
expect(this.view.$('.CDB-OverlayContainer > .CDB-Search').length).toEqual(1);
expect(this.view.$('> .CDB-Loader').length).toEqual(1);
});
it('should re-render overlays when a new overlay is added', function () {
this.overlaysCollection.add({ type: 'fullscreen' });
expect(this.view.$('.CDB-OverlayContainer > .CDB-Zoom').length).toEqual(1);
expect(this.view.$('.CDB-OverlayContainer > .CDB-Search').length).toEqual(1);
expect(this.view.$('.CDB-OverlayContainer > .CDB-Fullscreen').length).toEqual(1);
expect(this.view.$('> .CDB-Loader').length).toEqual(1);
});
it('should re-render overlays when an overlay is removed', function () {
this.overlaysCollection.remove(this.overlaysCollection.at(0));
expect(this.view.$('.CDB-OverlayContainer > .CDB-Zoom').length).toEqual(0);
expect(this.view.$('.CDB-OverlayContainer > .CDB-Search').length).toEqual(1);
expect(this.view.$('> .CDB-Loader').length).toEqual(1);
});
it('should handle unknown overlay types properly', function () {
this.overlaysCollection.add({ type: 'unknown' });
expect(this.view.$('.CDB-OverlayContainer > .CDB-Zoom').length).toEqual(1);
expect(this.view.$('.CDB-OverlayContainer > .CDB-Search').length).toEqual(1);
expect(this.view.$('> .CDB-Loader').length).toEqual(1);
});
it('should toggle the loader overlay', function () {
var loaderOverlay = this.view.$('> .CDB-Loader');
expect(loaderOverlay.hasClass('is-visible')).toBeFalsy();
engineMock._eventEmmitter.trigger(Engine.Events.RELOAD_STARTED);
expect(loaderOverlay.hasClass('is-visible')).toBeTruthy();
engineMock._eventEmmitter.trigger(Engine.Events.RELOAD_SUCCESS);
expect(loaderOverlay.hasClass('is-visible')).toBeFalsy();
});
describe('._initBinds', function () {
it('should call ._addLimitsOverlay on mapModel error:limit', function () {
spyOn(this.view, '_addLimitsOverlay');
this.view._initBinds();
this.view._mapModel.trigger('error:limit');
expect(this.view._addLimitsOverlay).toHaveBeenCalled();
});
it('should call ._addTilesOverlay on mapModel error:limit', function () {
spyOn(this.view, '_addTilesOverlay');
this.view._initBinds();
this.view._mapModel.trigger('error:tile');
expect(this.view._addTilesOverlay).toHaveBeenCalled();
});
});
describe('._areLimitsErrorsEnabled', function () {
it('should return visModel showLimitErrors', function () {
expect(this.view._areLimitsErrorsEnabled()).toBe(false);
this.view._visView.model.set('showLimitErrors', true);
expect(this.view._areLimitsErrorsEnabled()).toBe(true);
});
});
describe('._hasLimitsOverlay', function () {
it('should return true if overlaysCollection has a limits overlay', function () {
expect(this.view._hasLimitsOverlay()).toBe(false);
this.view._overlaysCollection.add({ type: 'limits' });
expect(this.view._hasLimitsOverlay()).toBe(true);
});
});
describe('._addLimitsOverlay', function () {
describe('when visModel showLimitErrors is true', function () {
beforeEach(function () {
this.view._visView.model.set('showLimitErrors', true);
});
it('should call ._removeTilesOverlay', function () {
spyOn(this.view, '_removeTilesOverlay');
this.view._mapModel.trigger('error:limit');
expect(this.view._removeTilesOverlay).toHaveBeenCalled();
});
it('should add the overlay to the collection', function () {
spyOn(this.view._overlaysCollection, 'add');
this.view._mapModel.trigger('error:limit');
expect(this.view._overlaysCollection.add).toHaveBeenCalled();
});
it('should add only one overlay of type limit', function () {
spyOn(this.view._overlaysCollection, 'add').and.callThrough();
this.view._mapModel.trigger('error:limit');
this.view._mapModel.trigger('error:limit');
expect(this.view._overlaysCollection.add.calls.count()).toEqual(1);
});
});
describe('when visModel showLimitErrors is false', function () {
it('should not add the overlay to the collection', function () {
spyOn(this.view._overlaysCollection, 'add');
this.view._mapModel.trigger('error:limit');
expect(this.view._overlaysCollection.add).not.toHaveBeenCalled();
});
});
});
describe('._addTilesOverlay', function () {
it('should add the overlay to the collection', function () {
spyOn(this.view._overlaysCollection, 'add');
this.view._mapModel.trigger('error:tile');
expect(this.view._overlaysCollection.add).toHaveBeenCalled();
});
it('should add only one overlay of type tile', function () {
spyOn(this.view._overlaysCollection, 'add').and.callThrough();
this.view._mapModel.trigger('error:tile');
this.view._mapModel.trigger('error:tile');
expect(this.view._overlaysCollection.add.calls.count()).toEqual(1);
});
it('should not add the overlay if there is a limits overlay', function () {
this.view._overlaysCollection.add({ type: 'limits' });
spyOn(this.view._overlaysCollection, 'add');
this.view._mapModel.trigger('error:tile');
expect(this.view._overlaysCollection.add).not.toHaveBeenCalled();
});
});
describe('._removeLimitsOverlay', function () {
it('should remove the limits overlay from the collection', function () {
spyOn(this.view._overlaysCollection, 'remove').and.callThrough();
expect(this.view._overlaysCollection.length).toEqual(3);
this.view._overlaysCollection.add({ type: 'limits' });
expect(this.view._overlaysCollection.length).toEqual(4);
this.view._removeLimitsOverlay();
expect(this.view._overlaysCollection.remove).toHaveBeenCalled();
expect(this.view._overlaysCollection.length).toEqual(3);
});
});
describe('._removeTilesOverlay', function () {
it('should remove the tiles overlay from the collection', function () {
spyOn(this.view._overlaysCollection, 'remove').and.callThrough();
expect(this.view._overlaysCollection.length).toEqual(3);
this.view._overlaysCollection.add({ type: 'tiles' });
expect(this.view._overlaysCollection.length).toEqual(4);
this.view._removeTilesOverlay();
expect(this.view._overlaysCollection.remove).toHaveBeenCalled();
expect(this.view._overlaysCollection.length).toEqual(3);
});
});
});
+281
View File
@@ -0,0 +1,281 @@
var $ = require('jquery');
var Backbone = require('backbone');
var Search = require('../../../../src/geo/ui/search/search');
var mapboxGeocoder = require('../../../../src/geo/geocoder/mapbox-geocoder');
var tomtomGeocoder = require('../../../../src/geo/geocoder/tomtom-geocoder');
var Map = require('../../../../src/geo/map');
var LeafletMapView = require('../../../../src/geo/leaflet/leaflet-map-view');
var fakeEvent = {
preventDefault: jasmine.createSpy()
};
describe('geo/ui/search', function () {
beforeEach(function () {
this.$el = $('<div>')
.attr('id', 'map')
.height(500)
.width(500);
$('body').append(this.$el);
this.map = new Map(null, { layersFactory: {} });
this.mapView = new LeafletMapView({
el: this.$el,
mapModel: this.map,
engine: new Backbone.Model(),
layerViewFactory: jasmine.createSpyObj('layerViewFactory', ['createLayerView']),
layerGroupModel: new Backbone.Model()
});
this.mapView.render();
this.view = new Search({
model: this.map,
mapView: this.mapView,
token: 'a_valid_api_key'
});
this.view.render();
});
it('should use tomtom geocoder by default', function () {
expect(this.view.geocoder).toBe(tomtomGeocoder);
});
it('should allow changing geocoder easily', function () {
var search = new Search({
model: this.map,
mapView: this.mapView,
geocoderService: 'mapbox', // <<<
token: 'a_valid_mapbox_api_key'
});
expect(search.geocoder).toBe(mapboxGeocoder);
});
it('should render properly', function () {
expect(this.view.$('.js-form').length).toBe(1);
expect(this.view.$('.js-textInput').length).toBe(1);
expect(this.view.$('input[type="submit"]').length).toBe(0);
expect(this.view.$('span.loader').length).toBe(0);
});
describe('onSubmit', function () {
beforeEach(function () {
this.result = {
lat: 43.0,
lon: -3.0,
boundingbox: {
south: 6.0,
north: 4.0,
west: 6.0,
east: 4.0
},
type: undefined
};
// spyOn(this.view.geocoder, 'geocode').and.callThrough();
spyOn(this.view.geocoder, 'geocode').and.returnValue(Promise.resolve([{
center: [40.41889, -3.69194],
type: 'localadmin'
}]));
this.view.$('.js-textInput').val('Madrid, Spain');
});
it('should search with geocoder when form is submit', function () {
this.view.$('.js-form').submit();
expect(this.view.geocoder.geocode).toHaveBeenCalled();
});
it('should change map center when geocoder returns any result', function (done) {
var onCenterChangedSpy = jasmine.createSpy('onCenterChangedSpy');
this.map.bind('change:center', onCenterChangedSpy, this.view);
this.view._onSubmit(fakeEvent).then(function () {
expect(onCenterChangedSpy).toHaveBeenCalled();
this.map.unbind('change:center', onCenterChangedSpy, this.view);
done();
}.bind(this));
});
describe('result zoom', function () {
function testZoom (context, featureType, expectedZoom, done) {
context.view.geocoder.geocode.and.returnValue(Promise.resolve([{
center: [43.0, -3],
type: featureType
}]));
context.view._onSubmit(fakeEvent).then(function () {
expect(context.map.get('zoom')).toBe(expectedZoom);
done();
});
}
it('should zoom to 18 when search result is building type', function (done) {
testZoom(this, 'building', 18, done);
});
it('should zoom to 15 when search result is postal-area type', function (done) {
testZoom(this, 'postal-area', 15, done);
});
it('should zoom to 18 when search result is venue type', function (done) {
testZoom(this, 'venue', 18, done);
});
it('should zoom to 8 when search result is region type', function (done) {
testZoom(this, 'region', 8, done);
});
it('should zoom to 5 when search result is country type', function (done) {
testZoom(this, 'country', 5, done);
});
it('should zoom to 8 when search result is county type', function (done) {
testZoom(this, 'county', 8, done);
});
it('should zoom to 18 when search result is address type', function (done) {
testZoom(this, 'address', 18, done);
});
it('should zoom to 12 when search result is locality type', function (done) {
testZoom(this, 'locality', 12, done);
});
it('should zoom to 11 when search result is localadmin type', function (done) {
testZoom(this, 'localadmin', 11, done);
});
it('should zoom to 15 when search result is neighbourhood type', function (done) {
testZoom(this, 'neighbourhood', 15, done);
});
it('should zoom to 12 when search result is unknown type', function (done) {
testZoom(this, 'whatever', 12, done);
});
});
describe('searchPin', function () {
beforeEach(function () {
this.view.options.searchPin = true;
});
it('should add a pin and an infowindow when search is completed', function (done) {
this.view._onSubmit(fakeEvent).then(function () {
expect(this.view._searchPin).toBeDefined();
expect(this.view._searchInfowindow).toBeDefined();
done();
}.bind(this));
});
it('should place pin in the feature center', function (done) {
this.view._onSubmit(fakeEvent).then(function () {
expect(this.view._searchPin.get('latlng')).toEqual([40.41889, -3.69194]);
done();
}.bind(this));
});
it('should display address in the search infowindow', function (done) {
this.view._onSubmit(fakeEvent).then(function () {
expect(this.view._searchInfowindow.$('.CDB-infowindow-title').text()).toBe('Madrid, Spain');
done();
}.bind(this));
});
it('should destroy/hide search pin when map is clicked', function (done) {
var view = this.view;
view._onSubmit(fakeEvent).then(function () {
expect(view._searchPin).toBeDefined();
expect(view._searchInfowindow).toBeDefined();
this.mapView.trigger('click');
setTimeout(function () {
expect(view._searchPin).toBeUndefined();
expect(view._searchInfowindow).toBeUndefined();
done();
}, 1000);
}.bind(this));
});
});
});
describe('_initializeGeocoder', function () {
it('should set geocoder from options if available', function () {
this.view = new Search({
model: this.map,
mapView: this.mapView,
geocoderService: 'mapbox',
token: 'token_from_options'
});
expect(this.view.geocoderService).toBe('mapbox');
expect(this.view.token).toBe('token_from_options');
});
it('should set geocoder from injected configuration if available', function () {
spyOn(Search.prototype, '_getGeocodingInfoFromConfig').and.returnValue({
provider: 'tomtom_fake',
token: 'fakeAPIKey'
});
this.view = new Search({
model: this.map,
mapView: this.mapView
});
expect(this.view.geocoderService).toBe('tomtom_fake');
expect(this.view.token).toBe('fakeAPIKey');
});
it('should set default geocoder if no configurations are available', function () {
Object.defineProperty(window, 'tomtomApiKey', {
value: 'fake_tomtom_key',
writable: true
});
this.view = new Search({
model: this.map,
mapView: this.mapView
});
expect(this.view.geocoderService).toBe('tomtom');
expect(this.view.token).toBe('fake_tomtom_key');
});
});
describe('_getGeocodingInfoFromConfig', function () {
it('should return an empty object if there is no geocoderConfiguration', function () {
expect(this.view._getGeocodingInfoFromConfig()).toEqual({});
});
it('should return provider and token from geocoderConfiguration', function () {
var geocoderConfiguration = {
provider: 'tomtom',
tomtom: {
search_bar_api_key: 'fakeAPIKey'
}
};
Object.defineProperty(window, 'geocoderConfiguration', {
value: geocoderConfiguration,
writable: true
});
expect(this.view._getGeocodingInfoFromConfig()).toEqual({
provider: 'tomtom', token: 'fakeAPIKey'
});
});
it('should return undefined provider and token if no provider found in geocoderConfiguration', function () {
var geocoderConfiguration = {};
Object.defineProperty(window, 'geocoderConfiguration', {
value: geocoderConfiguration,
writable: true
});
expect(this.view._getGeocodingInfoFromConfig()).toEqual({
provider: undefined, token: undefined
});
});
});
afterEach(function () {
this.$el.remove();
});
});
+173
View File
@@ -0,0 +1,173 @@
var $ = require('jquery');
var Backbone = require('backbone');
var Map = require('../../../../src/geo/map');
var LeafletMapView = require('../../../../src/geo/leaflet/leaflet-map-view');
var TooltipModel = require('../../../../src/geo/ui/tooltip-model');
var TooltipView = require('../../../../src/geo/ui/tooltip-view');
describe('geo/ui/tooltip-view', function () {
beforeEach(function () {
this.container = $("<div id='map'>").css('height', '1000px');
$('body').append(this.container);
var map = new Map(null, { layersFactory: {} });
this.mapView = new LeafletMapView({
el: $('#map'),
mapModel: map,
engine: new Backbone.Model(),
layerViewFactory: jasmine.createSpyObj('layerViewFactory', ['createLayerView']),
layerGroupModel: new Backbone.Model()
});
this.mapView.render();
this.layerView = new Backbone.View();
this.tooltipModel = new TooltipModel({
template: '{{#fields}}{{{ value }}},{{/fields}}'
});
this.tooltipView = new TooltipView({
model: this.tooltipModel,
layerView: this.layerView,
mapView: this.mapView
});
});
afterEach(function () {
$('#map').remove();
});
it('should render fields in specified order', function () {
this.tooltipModel.set('fields', [{
name: 'test2'
}, {
name: 'test1'
}, {
name: 'huracan'
}]);
this.tooltipView.model.updateContent({
test1: 'test1',
test2: 'test2',
huracan: 'huracan'
});
expect(this.tooltipView.$el.html()).toEqual('test2,test1,huracan,');
});
it('should use alternate_names ', function () {
this.tooltipModel.set('template', '{{#fields}}{{{ title }}},{{/fields}}');
this.tooltipModel.set('fields', [{
name: 'test2',
title: true
}, {
name: 'test1',
title: true
}, {
name: 'huracan',
title: true
}]);
this.tooltipModel.set('alternative_names', {
'test1': 'testnamed'
});
this.tooltipView.model.updateContent({
test1: 'test1',
test2: 'test2',
huracan: 'huracan'
});
expect(this.tooltipView.$el.html()).toEqual('test2,testnamed,huracan,');
});
it('should position the element correctly', function () {
this.tooltipView.$el.css('width', '200px');
this.tooltipView.$el.css('height', '20px');
this.tooltipModel.set('placement', 'bottom|right');
this.tooltipModel.setPosition({ x: 10, y: 10 });
expect(this.tooltipView.$el.css('top')).toBe('10px');
expect(this.tooltipView.$el.css('left')).toBe('10px');
this.tooltipModel.set('placement', 'top|left');
this.tooltipModel.setPosition({ x: 210, y: 40 });
expect(this.tooltipView.$el.css('top')).toBe('20px');
expect(this.tooltipView.$el.css('left')).toBe('10px');
this.tooltipModel.set('placement', 'middle|center');
this.tooltipModel.setPosition({ x: 150, y: 30 });
expect(this.tooltipView.$el.css('top')).toBe('20px');
expect(this.tooltipView.$el.css('left')).toBe('50px');
// With offsets
this.tooltipModel.set('placement', 'middle|center');
this.tooltipModel.set('offset', [-10, -10]);
this.tooltipModel.setPosition({ x: 0, y: 0 });
this.tooltipModel.setPosition({ x: 150, y: 30 });
expect(this.tooltipView.$el.css('top')).toBe('10px');
expect(this.tooltipView.$el.css('left')).toBe('40px');
});
describe('overflow positioning', function () {
beforeEach(function () {
$('#map').css('height', '100px');
$('#map').css('width', '100px');
this.tooltipView.$el.css('height', '80px');
this.tooltipView.$el.css('width', '80px');
this.mapView.invalidateSize();
});
it('should position the element on top when bottom overflow occurs', function () {
this.tooltipModel.set('placement', 'bottom|right');
this.tooltipModel.setPosition({ x: 0, y: 90 });
expect(this.tooltipView.$el.css('top')).toBe('10px');
});
it('should position the element on the bottom when top overflow occurs', function () {
this.tooltipModel.set('placement', 'top|right');
this.tooltipModel.setPosition({ x: 0, y: 10 });
expect(this.tooltipView.$el.css('top')).toBe('10px');
});
it('should position the element on top/bottom when overflow vertically centered and overflow occurs', function () {
this.tooltipModel.set('placement', 'middle|right');
this.tooltipModel.setPosition({ x: 0, y: 90 });
expect(this.tooltipView.$el.css('top')).toBe('10px');
this.tooltipModel.set('placement', 'middle|right');
this.tooltipModel.setPosition({ x: 0, y: 10 });
expect(this.tooltipView.$el.css('top')).toBe('10px');
});
it('should position the element on the left when right overflow occurs', function () {
this.tooltipModel.set('placement', 'top|right');
this.tooltipModel.setPosition({ x: 90, y: 10 });
expect(this.tooltipView.$el.css('left')).toBe('10px');
});
it('should position the element on the right when left overflow occurs', function () {
this.tooltipModel.set('placement', 'top|left');
this.tooltipModel.setPosition({ x: 10, y: 10 });
expect(this.tooltipView.$el.css('left')).toBe('10px');
});
it('should position the element on the left/right when horizontally centered and overflow occurs', function () {
this.tooltipModel.set('placement', 'top|center');
this.tooltipModel.setPosition({ x: 10, y: 10 });
expect(this.tooltipView.$el.css('left')).toBe('10px');
this.tooltipModel.set('placement', 'top|center');
this.tooltipModel.setPosition({ x: 90, y: 10 });
expect(this.tooltipView.$el.css('left')).toBe('10px');
});
});
});
+84
View File
@@ -0,0 +1,84 @@
var Map = require('../../../../src/geo/map');
var Zoom = require('../../../../src/geo/ui/zoom/zoom-view');
var Template = require('../../../../src/core/template');
describe('geo/ui/zoom', function () {
beforeEach(function () {
this.map = new Map(null, { layersFactory: {} });
spyOn(this.map, 'bind').and.callThrough();
spyOn(this.map, 'setZoom').and.callThrough();
this.view = new Zoom({
model: this.map,
template: Template.compile(' ' +
'<div class="CDB-Overlay">' +
'<button class="CDB-Zoom-action CDB-Zoom-action--out js-zoomOut"></button>' +
'<button class="CDB-Zoom-action CDB-Zoom-action--in js-zoomIn"></button>' +
'</div>' +
'<div class="CDB-Zoom-info">-</div>')
});
this.view.render();
});
it('should have bindings when map zoom has any change', function () {
expect(this.map.bind.calls.argsFor(0)[0]).toEqual('change:zoom change:minZoom change:maxZoom');
expect(this.map.bind.calls.argsFor(0)[1]).toEqual(this.view._checkZoom);
});
describe('zoom in', function () {
beforeEach(function () {
this.$zoomIn = this.view.$('.js-zoomIn');
});
it('should zoom in when button is clicked', function () {
this.$zoomIn.click();
expect(this.map.setZoom).toHaveBeenCalled();
expect(this.$zoomIn.hasClass('is-disabled')).toBeFalsy();
});
it('should not zoom in if max zoom is reached', function () {
this.map.set({
zoom: 3,
maxZoom: 3
});
this.$zoomIn.click();
expect(this.map.setZoom).not.toHaveBeenCalled();
});
it('should add a is-disabled class when it is not possible to zoom in', function () {
this.map.set({
zoom: 3,
maxZoom: 3
});
expect(this.$zoomIn.hasClass('is-disabled')).toBeTruthy();
});
});
describe('zoom out', function () {
beforeEach(function () {
this.$zoomOut = this.view.$('.js-zoomOut');
});
it('should zoom out when button is clicked', function () {
this.$zoomOut.click();
expect(this.map.setZoom).toHaveBeenCalled();
expect(this.$zoomOut.hasClass('is-disabled')).toBeFalsy();
});
it('should not zoom in if min zoom is reached', function () {
this.map.set({
zoom: 3,
minZoom: 3
});
this.$zoomOut.click();
expect(this.map.setZoom).not.toHaveBeenCalled();
});
it('should add a is-disabled class when it is not possible to zoom out', function () {
this.map.set({
zoom: 3,
minZoom: 3
});
expect(this.$zoomOut.hasClass('is-disabled')).toBeTruthy();
});
});
});