Files
carto.js/test/spec/api/v4/layer/layer.spec.js
2020-06-13 18:34:34 +08:00

678 lines
22 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var carto = require('../../../../../src/api/v4');
describe('api/v4/layer', function () {
var source;
var style;
var originalTimeout;
beforeEach(function () {
source = new carto.source.Dataset('ne_10m_populated_places_simple');
style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
});
afterEach(function () {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
describe('constructor', function () {
it('should build a new Layer params: (source, style)', function () {
var layer = new carto.layer.Layer(source, style);
expect(layer.getSource()).toEqual(source);
expect(layer.getStyle()).toEqual(style);
});
it('should assign a unique layer ID string', function () {
var layer1 = new carto.layer.Layer(source, style);
var layer2 = new carto.layer.Layer(source, style);
var id1 = layer1.getId();
var id2 = layer2.getId();
expect(id1).toMatch(/L\d+/);
expect(id2).toMatch(/L\d+/);
expect(id1).not.toEqual(id2);
});
it('should be able to create a hidden layer', function () {
var layer = new carto.layer.Layer(source, style, {
visible: false
});
expect(layer.isHidden()).toBe(true);
});
it('should build a new Layer params: (source, style, options)', function () {
var layer = new carto.layer.Layer(source, style, {
featureClickColumns: ['a', 'b'],
featureOverColumns: ['c', 'd']
});
expect(layer.getSource()).toEqual(source);
expect(layer.getStyle()).toEqual(style);
expect(layer.getFeatureClickColumns()).toEqual(['a', 'b']);
expect(layer.getFeatureOverColumns()).toEqual(['c', 'd']);
});
it('should throw an error if source is not valid', function () {
expect(function () {
new carto.layer.Layer({}, style); // eslint-disable-line
}).toThrowError('The given object is not a valid source. See "carto.source.Base".');
});
it('should throw an error if style is not valid', function () {
expect(function () {
new carto.layer.Layer(source, {}); // eslint-disable-line
}).toThrowError('The given object is not a valid style. See "carto.style.Base".');
});
it('should allow custom layer id as an option', function () {
var layer = new carto.layer.Layer(source, style, { id: 'fake_id' });
expect(layer.getId()).toEqual('fake_id');
});
describe('columns validation', function () {
var aggregation = new carto.layer.Aggregation({
threshold: 1,
resolution: 4,
columns: {
population: {
aggregateFunction: 'sum',
aggregatedColumn: 'pop_max'
}
}
});
it('should validate that featureClick columns are contained in aggregation columns', function () {
expect(function () {
new carto.layer.Layer(source, style, { // eslint-disable-line
featureClickColumns: ['a', 'b'],
aggregation: aggregation
});
}).toThrowError('Columns [a, b] set on `featureClick` do not match the columns set in aggregation options.');
});
it('should validate that featureOver columns are contained in aggregation columns', function () {
expect(function () {
new carto.layer.Layer(source, style, { // eslint-disable-line
featureOverColumns: ['a', 'b'],
aggregation: aggregation
});
}).toThrowError('Columns [a, b] set on `featureOver` do not match the columns set in aggregation options.');
});
});
});
describe('.setStyle', function () {
var layer;
var newStyle;
beforeEach(function () {
layer = new carto.layer.Layer(source, style);
newStyle = new carto.style.CartoCSS('#layer { marker-fill: green; }');
});
it('should throw an error when the parameter is not a valid style', function () {
expect(function () {
layer.setStyle('bad-style');
}).toThrowError('The given object is not a valid style. See "carto.style.Base".');
});
describe('when the layer has no engine', function () {
it('should set the layer style', function () {
layer.setStyle(newStyle);
expect(layer.getStyle()).toEqual(newStyle);
});
it('should fire a styleChanged event', function (done) {
layer.on('styleChanged', function (l) {
expect(l).toBe(layer);
expect(l.getStyle()).toEqual(newStyle);
done();
});
layer.setStyle(newStyle);
});
it('should not fire a styleChanged event when setting the same style twice', function () {
var styleChangedSpy = jasmine.createSpy('styleChangedSpy');
layer.on('styleChanged', styleChangedSpy);
layer.setStyle(style);
expect(styleChangedSpy).not.toHaveBeenCalled();
});
});
describe('when the layer has an engine', function () {
var client;
beforeEach(function () {
client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
});
describe('and the style has no engine', function () {
it('should set the engine into the style and update the internal style.', function (done) {
client.addLayer(layer)
.then(function () {
return layer.setStyle(newStyle);
})
.then(function () {
expect(layer.getStyle().$getEngine()).toBe(layer._engine);
done();
});
});
});
describe('and the style has an engine', function () {
it('should update the internal style when the engines are equal', function (done) {
newStyle.$setEngine(client._engine);
client.addLayer(layer)
.then(function () {
return layer.setStyle(newStyle);
})
.then(function () {
expect(layer.getStyle().$getEngine()).toBe(layer._engine);
done();
});
});
it('should throw an error when the engines are not equal', function (done) {
newStyle.$setEngine('fakeEngine');
client.addLayer(layer)
.then(function () {
expect(function () {
return layer.setStyle(newStyle);
}).toThrowError('A layer can\'t have a style which belongs to a different client.');
done();
});
});
});
it('should fire a cartoError when the style is invalid', function (done) {
var styleChangedSpy = jasmine.createSpy('styleChangedSpy');
layer.on('error', styleChangedSpy);
client.addLayer(layer)
.then(function () {
var malformedStyle = new carto.style.CartoCSS('#layer { invalid-rule: foo}');
return layer.setStyle(malformedStyle);
})
.catch(function () {
expect(styleChangedSpy).toHaveBeenCalled();
done();
});
});
it('should fire a styleChanged event when the layer belongs to a client', function (done) {
var styleChangedSpy = jasmine.createSpy('styleChangedSpy');
layer.on('styleChanged', styleChangedSpy);
client.addLayer(layer)
.then(function () {
return layer.setStyle(newStyle);
})
.then(function () {
expect(styleChangedSpy).toHaveBeenCalled();
done();
});
});
it('should set the internal model style', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
client.on(carto.events.ERROR, alert);
client.addLayer(layer)
.then(function () {
client.on(carto.events.SUCCESS, function () {
var expected = '#layer { marker-fill: green; }';
var actual = layer.$getInternalModel().get('cartocss');
expect(expected).toEqual(actual);
done();
});
layer.setStyle(newStyle);
});
});
});
});
describe('.$setEngine', function () {
it('probando', function () {
var layer = new carto.layer.Layer(source, style);
var engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.resolve()) };
var error = {
message: 'an error'
};
var capturedError;
layer.$setEngine(engineMock);
layer.on(carto.events.ERROR, function (error) {
capturedError = error;
});
layer._internalModel.set('error', error);
expect(capturedError).toBeDefined();
expect(capturedError.name).toEqual('CartoError');
expect(capturedError.message).toEqual('an error');
});
});
describe('.setSource', function () {
var layer;
var newSource;
beforeEach(function () {
layer = new carto.layer.Layer(source, style);
newSource = new carto.source.SQL('SELECT * FROM ne_10m_populated_places_simple LIMIT 10');
});
it('should throw an error when the source is not a valid parameter', function () {
expect(function () {
layer.setSource('bad-parameter');
}).toThrowError('The given object is not a valid source. See "carto.source.Base".');
});
describe('when the layer hasn\'t been set an engine', function () {
it('should normally add the source', function () {
layer.setSource(newSource);
expect(layer.getSource()).toEqual(newSource);
});
it('should fire a sourceChanged event', function (done) {
layer.on('sourceChanged', function (l) {
expect(l).toBe(layer);
expect(l.getSource()).toEqual(newSource);
done();
});
layer.setSource(newSource);
});
});
describe('when the layer has been set an engine', function () {
var engineMock;
beforeEach(function () {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.resolve()) };
layer.$setEngine(engineMock);
});
describe('and the source has no engine', function () {
it('should normally add the source', function (done) {
layer.setSource(newSource)
.then(function () {
var actualSource = layer.$getInternalModel().get('source');
var expectedSource = newSource.$getInternalModel();
expect(actualSource).toBeDefined();
expect(actualSource).toEqual(expectedSource);
done();
});
});
it('should fire a sourceChanged event', function (done) {
var sourceChangedSpy = jasmine.createSpy('sourceChangedSpy');
layer.on('sourceChanged', sourceChangedSpy);
layer.setSource(newSource)
.then(function () {
expect(sourceChangedSpy).toHaveBeenCalled();
done();
});
});
});
describe('and the source has an engine', function () {
it('should add the source if the engines are the same', function () {
newSource.$setEngine(engineMock);
layer.setSource(newSource);
var actualSource = layer.$getInternalModel().get('source');
var expectedSource = newSource.$getInternalModel();
expect(actualSource).toEqual(expectedSource);
});
it('should throw an error if the engines are different', function () {
// This engine is different from the layer's one.
var engineMock1 = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload') };
newSource.$setEngine(engineMock1);
expect(function () {
layer.setSource(newSource);
}).toThrowError('A layer can\'t have a source which belongs to a different client.');
});
});
});
it('should not fire a sourceChanged event when setting the same source twice', function () {
var sourceChangedSpy = jasmine.createSpy('sourceChangedSpy');
layer.on('sourceChanged', sourceChangedSpy);
layer.setSource(source);
expect(sourceChangedSpy).not.toHaveBeenCalled();
});
it('should fire a cartoError when the source is invalid', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
var sourceChangedSoy = jasmine.createSpy('sourceChangedSoy');
layer.on('error', sourceChangedSoy);
client.addLayer(layer)
.then(function () {
var invalidDataset = new carto.source.Dataset('invalid_dataset');
return layer.setSource(invalidDataset);
})
.catch(function () {
expect(sourceChangedSoy).toHaveBeenCalled();
done();
});
});
});
describe('.setFeatureClickColumns', function () {
var layer;
var newColums;
beforeEach(function () {
layer = new carto.layer.Layer(source, style);
newColums = ['a', 'b'];
});
it('should throw an error when the columns are not a valid parameter', function () {
expect(function () {
layer.setFeatureClickColumns([1, 2, 3]);
}).toThrowError('The given object is not a valid array of string columns.');
});
describe('when the layer hasn\'t been set an engine', function () {
it('should normally add the columns', function () {
layer.setFeatureClickColumns(newColums);
expect(layer.getFeatureClickColumns()).toEqual(newColums);
});
});
describe('when the layer has been set an engine', function () {
var engineMock;
it('should normally add the columns', function (done) {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.resolve()) };
layer.$setEngine(engineMock);
layer.setFeatureClickColumns(newColums)
.then(function () {
var actualColumns = layer.getFeatureClickColumns();
var expectedColumns = newColums;
expect(actualColumns).toBeDefined();
expect(actualColumns).toEqual(expectedColumns);
done();
});
});
it('should throw an error when the columns are not valid', function (done) {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.reject(new Error())) };
layer.$setEngine(engineMock);
layer.setFeatureClickColumns(['wrong'])
.catch(function (error) {
expect(error instanceof Error).toBe(true);
done();
});
});
it('should fire an error event when the columns are not valid', function (done) {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.reject(new Error())) };
layer.$setEngine(engineMock);
var columnChangedError = jasmine.createSpy('columnChangedError');
layer.on('error', columnChangedError);
layer.setFeatureClickColumns(['wrong'])
.catch(function () {
expect(columnChangedError).toHaveBeenCalled();
done();
});
});
});
});
describe('.setFeatureOverColumns', function () {
var layer;
var newColums;
beforeEach(function () {
layer = new carto.layer.Layer(source, style);
newColums = ['a', 'b'];
});
it('should throw an error when the columns are not a valid parameter', function () {
expect(function () {
layer.setFeatureOverColumns([1, 2, 3]);
}).toThrowError('The given object is not a valid array of string columns.');
});
describe('when the layer hasn\'t been set an engine', function () {
it('should normally add the columns', function () {
layer.setFeatureOverColumns(newColums);
expect(layer.getFeatureOverColumns()).toEqual(newColums);
});
});
describe('when the layer has been set an engine', function () {
var engineMock;
it('should normally add the columns', function (done) {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.resolve()) };
layer.$setEngine(engineMock);
layer.setFeatureOverColumns(newColums)
.then(function () {
var actualColumns = layer.getFeatureOverColumns();
var expectedColumns = newColums;
expect(actualColumns).toBeDefined();
expect(actualColumns).toEqual(expectedColumns);
done();
});
});
it('should throw an error when the columns are not valid', function (done) {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.reject(new Error())) };
layer.$setEngine(engineMock);
layer.setFeatureOverColumns(['wrong'])
.catch(function (error) {
expect(error instanceof Error).toBe(true);
done();
});
});
it('should fire an error event when the columns are not valid', function (done) {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.reject(new Error())) };
layer.$setEngine(engineMock);
var columnChangedError = jasmine.createSpy('columnChangedError');
layer.on('error', columnChangedError);
layer.setFeatureOverColumns(['wrong'])
.catch(function () {
expect(columnChangedError).toHaveBeenCalled();
done();
});
});
});
});
describe('.show', function () {
it('should set the layer visibility to true', function () {
var layer = new carto.layer.Layer(source, style);
expect(layer.isVisible()).toEqual(true);
layer.hide();
expect(layer.isVisible()).toEqual(false);
expect(layer.isHidden()).toEqual(true);
layer.show();
expect(layer.isVisible()).toEqual(true);
expect(layer.isHidden()).toEqual(false);
});
it('should trigger a visibilityChanged event', function (done) {
var layer = new carto.layer.Layer(source, style);
expect(layer.isVisible()).toEqual(true);
layer.hide();
layer.on('visibilityChanged', function () {
expect(layer.isVisible()).toEqual(true);
expect(layer.isHidden()).toEqual(false);
done();
});
layer.show();
});
});
describe('.hide', function () {
it('should set the layer visibility to false', function () {
var layer = new carto.layer.Layer(source, style);
expect(layer.isVisible()).toEqual(true);
layer.hide();
expect(layer.isVisible()).toEqual(false);
expect(layer.isHidden()).toEqual(true);
});
it('should trigger a visibilityChanged event', function (done) {
var layer = new carto.layer.Layer(source, style);
expect(layer.isVisible()).toEqual(true);
layer.on('visibilityChanged', function () {
expect(layer.isVisible()).toEqual(false);
expect(layer.isHidden()).toEqual(true);
done();
});
layer.hide();
});
});
describe('.toggle', function () {
it('should toggle the layer visibility', function () {
var layer = new carto.layer.Layer(source, style);
expect(layer.isVisible()).toEqual(true);
layer.toggle();
expect(layer.isVisible()).toEqual(false);
expect(layer.isHidden()).toEqual(true);
layer.toggle();
expect(layer.isVisible()).toEqual(true);
expect(layer.isHidden()).toEqual(false);
});
it('should trigger a visibilityChanged event', function (done) {
var layer = new carto.layer.Layer(source, style);
expect(layer.isVisible()).toEqual(true);
layer.on('visibilityChanged', function () {
expect(layer.isVisible()).toEqual(false);
expect(layer.isHidden()).toEqual(true);
done();
});
layer.toggle();
});
});
describe('.setOrder', function () {
it('should call moveLayer with the passed index', function () {
var clientMock = { moveLayer: jasmine.createSpy('moveLayer') };
var layer = new carto.layer.Layer(source, style);
layer.$setClient(clientMock);
layer.setOrder(1);
expect(clientMock.moveLayer).toHaveBeenCalledWith(layer, 1);
});
});
describe('.bringToBack', function () {
it('should call moveLayer with the passed index', function () {
var clientMock = { moveLayer: jasmine.createSpy('moveLayer') };
var layer = new carto.layer.Layer(source, style);
layer.$setClient(clientMock);
layer.bringToBack();
expect(clientMock.moveLayer).toHaveBeenCalledWith(layer, 0);
});
});
describe('.bringToFront', function () {
it('should call moveLayer with the passed index', function () {
var numberOfLayers = 3;
var clientMock = { moveLayer: jasmine.createSpy('moveLayer'), _layers: { size: function () { return numberOfLayers; } } };
var layer = new carto.layer.Layer(source, style);
layer.$setClient(clientMock);
layer.bringToFront();
expect(clientMock.moveLayer).toHaveBeenCalledWith(layer, numberOfLayers - 1);
});
});
describe('.isInteractive', function () {
it('returns true if layer has featureClickColumns', function () {
const layer = new carto.layer.Layer(source, style, {
featureClickColumns: ['cartodb_id']
});
expect(layer.isInteractive()).toBe(true);
});
it('returns true if layer has featureOverColumns', function () {
const layer = new carto.layer.Layer(source, style, {
featureOverColumns: ['cartodb_id']
});
expect(layer.isInteractive()).toBe(true);
});
it('returns false if layer doesn\'t have getFeatureClickColumns or getFeatureHoverColumns', function () {
const layer = new carto.layer.Layer(source, style);
expect(layer.isInteractive()).toBe(false);
});
});
xit('should update "internalmodel.cartocss" when the style is updated', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
var layer = new carto.layer.Layer(source, style);
var newStyle = '#layer { marker-fill: #FABADA; }';
client.addLayer(layer)
.then(function () {
return style.setContent(newStyle);
})
.then(function () {
expect(layer.$getInternalModel().get('cartocss')).toEqual(newStyle);
done();
});
});
});