Initial commit

This commit is contained in:
zhongjin
2020-06-15 10:58:47 +08:00
commit 4f1dfe7564
8590 changed files with 1516878 additions and 0 deletions
@@ -0,0 +1,140 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var AnalysisDefinitionNodessCollection = require('builder/data/analysis-definition-nodes-collection');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
var AddAnalysisView = require('builder/components/modals/add-analysis/add-analysis-view');
var UserModel = require('builder/data/user-model');
var DataServicesApiCheck = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-info');
var Router = require('builder/routes/router');
describe('components/modals/add-analysis/add-analysis-view', function () {
beforeEach(function () {
this.modalModel = new Backbone.Model();
spyOn(this.modalModel, 'destroy');
spyOn(Router, 'navigate');
var configModel = new ConfigModel({
base_url: '/u/pepe',
user_name: 'foo',
sql_api_template: 'foo',
api_key: 'foo'
});
var userModel = new UserModel({}, {
configModel: configModel
});
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodessCollection(null, {
configModel: configModel,
userModel: userModel
});
this.a0 = this.analysisDefinitionNodesCollection.add({
id: 'a0',
type: 'source',
params: {
query: 'SELECT * from alice'
},
options: {
table_name: 'alice'
}
});
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
configModel: configModel,
userModel: userModel,
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
mapId: '123',
stateDefinitionModel: new Backbone.Model()
});
this.layerA = this.layerDefinitionsCollection.add({
id: 'layerA',
kind: 'carto',
options: {
source: 'a0',
table_name: 'alice'
}
});
DataServicesApiCheck.get(configModel)._state = 'fetched';
this.queryGeometryModel = this.a0.queryGeometryModel;
spyOn(this.queryGeometryModel, 'sync');
spyOn(this.queryGeometryModel, 'fetch').and.callThrough();
this.view = new AddAnalysisView({
configModel: configModel,
userModel: userModel,
modalModel: this.modalModel,
layerDefinitionModel: this.layerA
});
this.view.render();
});
it('should have no leaks', function () {
expect(this.view).toHaveNoLeaks();
});
it('should render the loading view', function () {
expect(this.view.$el.html()).toContain('loading');
});
describe('when geometry output type is fetched', function () {
beforeEach(function () {
this.queryGeometryModel.set('simple_geom', 'polygon');
this.queryGeometryModel.sync.calls.argsFor(0)[2].success({
fields: {},
rows: []
});
});
it('should render the content view', function () {
expect(this.view.$el.html()).not.toContain('loading');
expect(this.view.$el.html()).not.toContain('error');
});
it('should render the tabs', function () {
expect(this.view.$('.js-menu li').size()).toBe(4);
expect(this.view.$('.js-menu li:eq(0) button').html()).toContain('analysis-category.all');
expect(this.view.$('.js-menu li:eq(1) button').html()).toContain('analysis-category.create-clean');
expect(this.view.$('.js-menu li:eq(2) button').html()).toContain('analysis-category.analyze-predict');
expect(this.view.$('.js-menu li:eq(3) button').html()).toContain('analysis-category.data-transformation');
});
describe('when click add when there is no selection', function () {
it('should do nothing', function () {
this.view.$('.js-add').click();
expect(this.modalModel.destroy).not.toHaveBeenCalled();
});
});
describe('when an option is selected', function () {
beforeEach(function () {
expect(this.view.$('.js-add').hasClass('is-disabled')).toBe(true);
var $el = this.view.$('[title*=georeference]').first();
expect($el.length).toEqual(1, 'should only click one item (the custom georeference one)');
$el.click();
});
it('should enable add-button', function () {
expect(this.view.$('.js-add').hasClass('is-disabled')).toBe(false);
});
describe('when click add', function () {
beforeEach(function () {
this.view.$('.js-add').click();
});
it('should destroy the modal and pass the created node model', function () {
expect(this.modalModel.destroy).toHaveBeenCalled();
expect(this.modalModel.destroy.calls.argsFor(0)).toEqual([
jasmine.objectContaining({
id: 'a1',
source: 'a0',
type: jasmine.any(String)
})
]);
});
});
});
});
});
@@ -0,0 +1,87 @@
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var CamshaftReference = require('builder/data/camshaft-reference');
var AnalysisOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/analysis-option-model');
describe('builder/components/modals/add-analysis/analysis-option-models/analysis-option-model', function () {
beforeEach(function () {
this.model = new AnalysisOptionModel({
title: 'a Buffer',
desc: 'An example of how buffer would/should behave',
type_group: 'Specs',
dummy: false
}, {
nodeAttrs: {
type: 'buffer',
radius: 123
}
});
});
describe('.acceptsGeometryTypeAsInput', function () {
it('should return true for valid geometries', function () {
expect(this.model.acceptsGeometryTypeAsInput('point')).toBe(true);
expect(this.model.acceptsGeometryTypeAsInput('polygon')).toBe(true);
expect(this.model.acceptsGeometryTypeAsInput('line')).toBe(true);
var model = new AnalysisOptionModel(null, {nodeAttrs: {type: 'trade-area'}});
expect(model.acceptsGeometryTypeAsInput('point')).toBe(true);
expect(model.acceptsGeometryTypeAsInput('polygon')).toBe(false);
});
it('should skip camshaft geometry validation if our analysis is a dummy one', function () {
this.model.set('dummy', true);
spyOn(CamshaftReference, 'isValidInputGeometryForType');
this.model.acceptsGeometryTypeAsInput('line');
expect(CamshaftReference.isValidInputGeometryForType).not.toHaveBeenCalled();
this.model.set('dummy', false);
});
});
describe('.getValidInputGeometries', function () {
it('should return valid input geometries', function () {
expect(this.model.getValidInputGeometries()).toEqual(jasmine.any(Array));
expect(this.model.getValidInputGeometries()[0]).toEqual(jasmine.any(String));
});
});
describe('.getFormAttrs', function () {
beforeEach(function () {
this.layerDefModel = new LayerDefinitionModel({
id: 'layerA',
type: 'cartoDB',
letter: 'a',
source: 'a0'
}, {
configModel: {}
});
});
describe('when given a layer with own source', function () {
it('should return attrs to create a form model for analysis', function () {
expect(this.model.getFormAttrs(this.layerDefModel)).toEqual({
id: 'a1',
source: 'a0',
type: 'buffer',
radius: 123
});
});
});
describe('when given a layer which source belongs to another layer', function () {
beforeEach(function () {
this.layerDefModel.set('source', 'b0');
});
it('should return attrs to create a form model for analysis', function () {
var attrs = this.model.getFormAttrs(this.layerDefModel);
expect(attrs.id).toEqual('a1', 'id should be adapted for layer, not continue on source node');
expect(attrs).toEqual({
id: 'a1',
source: 'b0',
type: 'buffer',
radius: 123
});
});
});
});
});
@@ -0,0 +1,31 @@
var Backbone = require('backbone');
var AnalysisOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/analysis-option-model');
var SQLFunctionOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/deprecated-sql-function-option-model');
describe('deprecated-sql-function-option-model', function () {
var model;
beforeEach(function () {
spyOn(AnalysisOptionModel.prototype, 'getFormAttrs').and.returnValue({
source: 'y0' // Return always this `source` property. We need to check that it gets deleted.
});
model = new SQLFunctionOptionModel(null, {
nodeAttrs: {
key: 'value'
}
});
});
describe('getFormAttrs', function () {
it('should get form attributes, delete source and set primary_source with the layer definition attributes', function () {
var fakeLayerDefModel = new Backbone.Model({
source: 'z0'
});
var formAttrs = model.getFormAttrs(fakeLayerDefModel);
expect(AnalysisOptionModel.prototype.getFormAttrs).toHaveBeenCalledWith(fakeLayerDefModel);
expect(formAttrs).toEqual({ primary_source: 'z0' });
});
});
});
@@ -0,0 +1,60 @@
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var GeneratedAnalysisOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/generated-analysis-option-model');
describe('builder/components/modals/add-analysis/analysis-option-models/generated-analysis-option-model', function () {
beforeEach(function () {
this.layerA = new LayerDefinitionModel({
id: 'layerA',
type: 'cartoDB',
letter: 'a',
source: 'a0'
}, {
configModel: {}
});
});
describe('when created a type with a single source', function () {
beforeEach(function () {
this.model = new GeneratedAnalysisOptionModel({
title: 'filter-category',
desc: 'An example of a generated option model'
}, {
nodeAttrs: {
type: 'filter-category'
}
});
});
describe('.getFormAttrs', function () {
it('should return attrs to create a form model for analysis', function () {
expect(this.model.getFormAttrs(this.layerA)).toEqual({
id: 'a1',
source: 'a0',
type: 'filter-category'
});
});
});
});
describe('when created a type with multiple input sources', function () {
beforeEach(function () {
this.model = new GeneratedAnalysisOptionModel({
title: 'aggregate-intersection',
desc: 'An example of a generated option model with multiple sources'
}, {
nodeAttrs: {
type: 'aggregate-intersection'
}
});
});
describe('.getFormAttrs', function () {
it('should return attrs but w/o any source set', function () {
expect(this.model.getFormAttrs(this.layerA)).toEqual({
id: 'a1',
type: 'aggregate-intersection'
});
});
});
});
});
@@ -0,0 +1,35 @@
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var MergeAnalysisOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/merge-analysis-option-model');
describe('builder/components/modals/add-analysis/analysis-option-models/merge-analysis-option-model', function () {
beforeEach(function () {
this.layerA = new LayerDefinitionModel({
id: 'layerA',
type: 'cartoDB',
letter: 'a',
source: 'a0'
}, {
configModel: {}
});
this.model = new MergeAnalysisOptionModel({
title: 'merge',
desc: 'An example of a generated option model'
}, {
nodeAttrs: {
type: 'merge'
}
});
});
describe('.getFormAttrs', function () {
it('should return attrs to create a form model for analysis', function () {
expect(this.model.getFormAttrs(this.layerA)).toEqual({
id: 'a1',
type: 'merge',
left_source: 'a0',
primary_source_name: 'left_source'
});
});
});
});
@@ -0,0 +1,101 @@
var AnalysisOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/analysis-option-model');
var AnalysisOptionView = require('builder/components/modals/add-analysis/analysis-option-view');
describe('components/modals/add-analysis/analysis-option-view', function () {
beforeEach(function () {
this.model = new AnalysisOptionModel({
title: 'Buffer',
desc: 'describes the buffer type',
type_group: 'area of influence',
link: 'https://carto.com'
}, {
nodeAttrs: {
type: 'buffer'
}
});
});
describe('when given a simple geometry type', function () {
beforeEach(function () {
this.view = new AnalysisOptionView({
model: this.model,
simpleGeometryTypeInput: 'point'
});
this.view.render();
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
it('should render the info', function () {
expect(this.view.$el.html()).toContain('Buffer');
expect(this.view.$el.html()).toContain('describes the buffer');
});
it('should not select the item if the link is clicked', function () {
expect(this.view.$('.js-more').length).toBe(1);
this.view.$('.js-more').click();
expect(this.model.get('selected')).toBeFalsy();
});
it('should render the animation', function () {
expect(this.view.$('.js-animation').length).toBe(1);
});
it('should launch the animation on hover', function () {
this.view._acceptsInputGeometry = function () { return true; };
this.view.$el.trigger('mouseenter');
expect(this.view.$('.js-animation').hasClass('has-autoplay')).toBeTruthy();
this.view.$el.trigger('mouseleave');
expect(this.view.$('.js-animation').hasClass('has-autoplay')).toBeFalsy();
});
describe('when selected', function () {
beforeEach(function () {
this.model.set('selected', true);
});
it('should highlight the item', function () {
expect(this.view.el.className).toContain('is-selected');
});
});
describe('when geometry type does not match ', function () {
beforeEach(function () {
spyOn(this.model, 'acceptsGeometryTypeAsInput').and.returnValue(false);
this.view.render();
});
it('should disable the view', function () {
expect(this.view.el.className).toContain('is-disabled');
});
it('should show alternative desc', function () {
expect(this.view.$el.html()).toContain('disabled-option-desc');
expect(this.view.$el.html()).not.toContain('describes the buffer');
});
it('should not be able to select it', function () {
this.view.$el.click();
expect(this.view.el.className).not.toContain('selected');
});
});
});
describe('when not given any geometry type', function () {
beforeEach(function () {
this.view = new AnalysisOptionView({
model: this.model
});
this.view.render();
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
});
@@ -0,0 +1,41 @@
var AnalysisOptionsCollection = require('builder/components/modals/add-analysis/analysis-options-collection');
describe('components/modals/add-analysis/analysis-options-collection', function () {
beforeEach(function () {
this.collection = new AnalysisOptionsCollection();
});
describe('when reset with some options', function () {
beforeEach(function () {
this.collection.reset([
{
title: 'A Buffer',
nodeAttrs: {
type: 'buffer'
}
}, {
title: 'A Trade-Area',
nodeAttrs: {
type: 'trade-area'
}
}
]);
});
it('should create a model for given data', function () {
expect(this.collection.length).toEqual(2);
expect(this.collection.first().get('title')).toEqual(jasmine.any(String));
expect(this.collection.first().getFormAttrs).toEqual(jasmine.any(Function));
});
it('should only allow one selection at a time', function () {
expect(this.collection.pluck('selected')).toEqual([false, false]);
this.collection.first().set('selected', true);
expect(this.collection.pluck('selected')).toEqual([true, false]);
this.collection.last().set('selected', true);
expect(this.collection.pluck('selected')).toEqual([false, true]);
});
});
});
@@ -0,0 +1,92 @@
var Backbone = require('backbone');
var _ = require('underscore');
var camshaftReferenceAnalyses = require('camshaft-reference').getVersion('latest').analyses;
var analysisOptions = require('builder/components/modals/add-analysis/analysis-options');
var DataServicesApiCheck = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-info');
describe('builder/components/modals/add-analysis/analysis-options', function () {
var configModel = new Backbone.Model({
user_name: 'foo',
sql_api_template: 'foo',
api_key: 'foo'
});
configModel.dataServiceEnabled = function () { return true; };
var userModel = new Backbone.Model();
userModel.featureEnabled = function () { return true; };
var queryGeometryModel = new Backbone.Model();
queryGeometryModel.isPolygon = function () { return true; };
DataServicesApiCheck.get(configModel)._state = 'fetched';
it('should not add generated option if feature flag is disabled', function () {
userModel.featureEnabled = function () { return false; };
configModel.dataServiceEnabled = function () { return false; };
var defaultAnalysisOptions = analysisOptions({
userModel: userModel,
configModel: configModel,
queryGeometryModel: queryGeometryModel
});
expect(defaultAnalysisOptions['generated']).toBeUndefined('should not have any generated options');
});
describe('each category (including generated)', function () {
var options = analysisOptions({
userModel: userModel,
configModel: configModel,
queryGeometryModel: queryGeometryModel
});
_.each(options, function (item, category) {
it('should have a key', function () {
expect(category).toEqual(jasmine.any(String));
});
describe('category: ' + category, function () {
describe('should have a definition', function () {
var def = options[category];
beforeEach(function () {
expect(def).toEqual(jasmine.any(Object));
});
it('should have a title', function () {
expect(def.title).toEqual(jasmine.any(String));
});
it('should have analyses', function () {
expect(def.analyses).toEqual(jasmine.any(Array));
expect(def.analyses.length).toBeGreaterThan(0);
});
describe('each analysis', function () {
def.analyses.forEach(function (d) {
describe('analysis: ' + d.title, function () {
it('should have a title', function () {
expect(d.title).toEqual(jasmine.any(String));
});
it('should have a description', function () {
expect(d.desc).toEqual(jasmine.any(String));
});
it('should have attrs to create a node from', function () {
expect(d.nodeAttrs).toEqual(jasmine.any(Object));
});
it('should have at least a type', function () {
expect(d.nodeAttrs.type).toEqual(jasmine.any(String));
if (d.dummy !== true) {
expect(camshaftReferenceAnalyses[d.nodeAttrs.type]).toBeDefined();
}
});
});
});
});
});
});
});
});
});
@@ -0,0 +1,141 @@
var Backbone = require('backbone');
var AnalysisViewPane = require('builder/components/modals/add-analysis/analysis-view-pane');
var AnalysisOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/analysis-option-model');
var Router = require('builder/routes/router');
describe('components/modals/add-analysis/analysis-view-pane', function () {
var view, analysisOptionsCollection;
var createViewFn = function (options) {
analysisOptionsCollection = new Backbone.Collection([
new AnalysisOptionModel({
category: 'create_clean',
desc: 'Use street addresses, city names, or other location text to generate point geometries.',
selected: false,
title: 'Georeference',
type: 'georeference-long-lat',
type_group: ''
}, {
nodeAttrs: {
type: 'georeference-long-lat'
}
}), new AnalysisOptionModel({
category: 'analyze_predict',
desc: 'Moran description',
selected: false,
title: 'Moran',
type: 'moran',
type_group: ''
}, {
nodeAttrs: {
type: 'moran'
}
}), new AnalysisOptionModel({
category: 'data_transformation',
desc: 'Centroid description',
selected: false,
title: 'Centroid',
type: 'centroid',
type_group: ''
}, {
nodeAttrs: {
type: 'centroid'
}
})
]);
var view = new AnalysisViewPane({
stackLayoutModel: new Backbone.Model(),
modalModel: new Backbone.Model(),
queryGeometryModel: new Backbone.Model(),
analysisOptions: {
create_clean: {
title: 'Create and clean',
analyses: [{
desc: 'Use street addresses, city names, or other location text to generate point geometries.',
nodeAttrs: {
type: 'georeference-long-lat'
},
title: 'Georeference'
}]
},
analyze_predict: {
title: 'Analyze and predict',
analyses: [{
desc: 'Moran desc',
nodeAttrs: {
type: 'moran'
},
title: 'Moran'
}]
},
data_transformation: {
title: 'Data Transformation',
analyses: [{
desc: 'Centroid desc',
nodeAttrs: {
type: 'centroid'
},
title: 'Centroid'
}]
}
},
analysisOptionsCollection: analysisOptionsCollection,
layerDefinitionModel: new Backbone.Model({
id: 'some-layer-id',
letter: 'a',
source: 'a0'
}),
collection: new Backbone.Collection([
{ selected: true,
id: 'a3',
title: 'Intersection',
category: 'analyze-predict',
type: 'intersection',
genericType: 'intersection',
analysisParams: {}
}
])
});
return view;
};
beforeEach(function () {
spyOn(Router, 'navigate');
view = createViewFn();
view.render();
});
describe('.render', function () {
it('should render properly', function () {
expect(view.$el.html()).toContain('analysis-category.create-clean');
expect(view.$el.html()).toContain('analysis-category.analyze-predict');
expect(view.$el.html()).toContain('analysis-category.data-transformation');
expect(view.$('.js-add').hasClass('is-disabled')).toBeTruthy();
});
it('should not have any leaks', function () {
expect(view).toHaveNoLeaks();
});
});
it('should go to tab item', function () {
analysisOptionsCollection.at(0).set('selected', true);
view.goToTabItem('create_clean');
expect(view.$('.js-add').hasClass('is-disabled')).toBeFalsy();
});
describe('._onAddAnalysis', function () {
it('should call goToAnalysisNode', function () {
spyOn(Router, 'goToAnalysisNode');
analysisOptionsCollection.at(0).set('selected', true);
view._onAddAnalysis();
expect(Router.goToAnalysisNode).toHaveBeenCalledWith('some-layer-id', 'a1');
});
});
});