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,287 @@
const ApiKeyModel = require('dashboard/data/api-key-model');
const modelApiURL = 'wadus.com/api/v3/api_keys';
const apiData = {
name: 'Awesome map key',
type: 'regular',
token: 'NmYvZM80laiQQMv5rUGbJA',
grants: [
{ type: 'apis', apis: [ 'maps', 'sql' ] },
{ type: 'database',
tables: [{ 'schema': 'public', 'name': 'untitled_table_14', 'permissions': ['insert', 'update', 'read'] }],
schemas: [] }
]
};
const parsedData = {
name: 'Awesome map key',
type: 'regular',
token: 'NmYvZM80laiQQMv5rUGbJA',
id: 'Awesome map key',
apis: { maps: true, sql: true },
tables: {
untitled_table_14: {
schema: 'public',
name: 'untitled_table_14',
permissions: {
insert: true,
update: true,
read: true
}
}
},
datasets: {
create: false,
listing: false
}
};
describe('dashboard/data/api-key-model', function () {
var model;
beforeEach(function () {
model = new ApiKeyModel({}, {
userModel: {
getSchema: () => 'public'
}
});
spyOn(model, 'url').and.returnValue(modelApiURL);
});
it('should have these attributes by default', function () {
expect(model.attributes).toEqual({
name: '',
token: '',
apis: {
maps: false,
sql: false
},
datasets: {
create: false,
listing: false
},
tables: []
});
});
it('throws an error when userModel is missing', function () {
model = function () {
return new ApiKeyModel(null, {});
};
expect(model).toThrowError('userModel is required');
});
describe('.regenerate', function () {
it('should sync collection', function () {
spyOn(model, 'sync');
model.regenerate();
var spyCallArguments = model.sync.calls.mostRecent().args;
expect(model.sync).toHaveBeenCalled();
expect(spyCallArguments[0]).toBeNull();
expect(spyCallArguments[1]).toBe(model);
expect(spyCallArguments[2].url).toBe('wadus.com/api/v3/api_keys/token/regenerate');
expect(spyCallArguments[2].type).toBe('POST');
});
it('should set data on sync success', function () {
spyOn(model, 'sync').and.callFake(function (method, model, options) {
options.success && options.success({ name: 'wadus' });
});
model.regenerate();
expect(model.get('name')).toBe('wadus');
});
});
describe('.parse', function () {
it('should parse data properly', function () {
const options = {
userModel: {
getSchema: () => 'public'
}
};
const parsed = model.parse(apiData, options);
expect(parsed).toEqual(parsedData);
});
});
describe('.toJSON', function () {
it('should return data properly', function () {
model.set(parsedData);
const data = model.toJSON();
const expectedJSONData = Object.assign({}, apiData, { id: parsedData.id });
expect(data).toEqual(expectedJSONData);
});
it('should return data properly with create and listing set to true', function () {
const parsedDataCreateListing = Object.assign(parsedData, { datasets: { create: true, listing: true } });
model.set(parsedDataCreateListing);
const data = model.toJSON();
const apiDataCreateListing = Object.assign({},
apiData, {
grants: [
{ type: 'apis', apis: [ 'maps', 'sql' ] },
{ type: 'database',
tables: [{ 'schema': 'public', 'name': 'untitled_table_14', 'permissions': ['insert', 'update', 'read'] }],
schemas: [{ name: 'public', permissions: ['create'] }],
table_metadata: [] }
]
});
const expectedJSONData = Object.assign({}, apiDataCreateListing, { id: parsedData.id });
expect(data).toEqual(expectedJSONData);
});
});
describe('.isPublic', function () {
it('should return true if type is default', function () {
model.set('type', 'regular');
expect(model.isPublic()).toBe(false);
model.set('type', 'default');
expect(model.isPublic()).toBe(true);
});
});
describe('.getApiGrants', function () {
it('should return granted API permissions', function () {
model.set('apis', { maps: true, sql: false });
expect(model.getApiGrants()).toEqual(['maps']);
});
});
describe('.getTablesGrants', function () {
it('should return tables with granted permissions', function () {
const tables = {
untitled_table_14: {
schema: 'public',
name: 'untitled_table_14',
permissions: {
insert: true,
update: true,
read: false
}
}
};
model.set('tables', tables);
expect(model.getTablesGrants()).toEqual([{
name: 'untitled_table_14',
schema: 'public',
permissions: ['insert', 'update']
}]);
});
});
describe('._parseApiGrants', function () {
it('should return parsed API grants properly', function () {
const apiGrants = model._parseApiGrants([{ type: 'apis', apis: [ 'maps' ] }]);
expect(apiGrants).toEqual({ maps: true, sql: false });
});
});
describe('._parseTableGrants', function () {
it('should return parsed table grants properly', function () {
const tableGrants = [{
type: 'database',
tables: [{ 'schema': 'public', 'name': 'untitled_table_14', 'permissions': ['insert', 'read'] }]
}];
const parsedTableGrants = model._parseTableGrants(tableGrants);
expect(parsedTableGrants).toEqual({
untitled_table_14: {
schema: 'public',
name: 'untitled_table_14',
permissions: {
insert: true,
read: true
}
}
});
});
});
describe('._parseDatabaseGrants', function () {
it('should be true after parsing database grants', function () {
const tableGrants = [{
type: 'database',
schema: 'public',
name: 'untitled_table_14',
permissions: {
insert: true,
read: true
},
table_metadata: []
}];
const parsedDatabaseGrants = model._parseDatabaseGrants(tableGrants);
expect(parsedDatabaseGrants).toBe(true);
});
it('should be false after parsing database grants', function () {
const tableGrants = [{
type: 'database',
schema: 'public',
name: 'untitled_table_14',
permissions: {
insert: true,
read: true
}
}];
const parsedDatabaseGrants = model._parseDatabaseGrants(tableGrants);
expect(parsedDatabaseGrants).toBe(false);
});
});
describe('._parseDatabaseSchemas', function () {
it('should be true after parsing database schemas', function () {
const tableGrants = [{
type: 'database',
schemas: [{ name: 'public', permissions: ['create'] }]
}];
const schemaName = 'public';
const parsedDatabaseSchemas = model._parseDatabaseSchemas(tableGrants, schemaName);
expect(parsedDatabaseSchemas).toBe(true);
});
it('should be false after parsing database schemas', function () {
const tableGrants = [{
type: 'database',
schemas: []
}];
const parsedDatabaseSchemas = model._parseDatabaseSchemas(tableGrants);
expect(parsedDatabaseSchemas).toBe(false);
});
});
describe('._arrayToObj', function () {
it('should transform an array to an object', function () {
expect(model._arrayToObj(['sql', 'maps'])).toEqual({sql: true, maps: true});
});
});
describe('._hasPermissionsSelected', function () {
it('should return true if any table has permissions', function () {
spyOn(model, 'getTablesGrants').and.returnValues(
[{ schema: 'public', name: 'table_with_permissions', permissions: ['insert', 'update', 'read'] }],
[{ schema: 'public', name: 'table_without_permissions', permissions: [] }]
);
expect(model.hasPermissionsSelected()).toBe(true);
expect(model.hasPermissionsSelected()).toBe(false);
});
});
});
@@ -0,0 +1,81 @@
var Backbone = require('backbone');
var ApiKeyModel = require('dashboard/data/api-key-model');
var ApiKeysCollection = require('dashboard/data/api-keys-collection');
const apiKeysCollectionTypes = require('dashboard/data/api-keys-collection-types');
var modelData = {
name: 'fake_name',
token: 'fake_token'
};
describe('dashboard/data/api-key-collection', function () {
var collection;
beforeEach(function () {
collection = new ApiKeysCollection({}, {
userModel: new Backbone.Model({
base_url: 'wadus.com'
})
});
});
it('throws an error when userModel is missing', function () {
collection = function () {
return new ApiKeysCollection(null, {});
};
expect(collection).toThrowError('userModel is required');
});
it('should set the the default REGULAR type correctly when no type param is passed as option', function () {
collection = new ApiKeysCollection(null, {
userModel: new Backbone.Model({
base_url: 'wadus.com'
})
});
expect(collection._type).toEqual([apiKeysCollectionTypes.REGULAR]);
});
it('should set the type correctly when type param is passed as option', function () {
collection = new ApiKeysCollection(null, {
userModel: new Backbone.Model({
base_url: 'wadus.com'
}),
type: [apiKeysCollectionTypes.MASTER, apiKeysCollectionTypes.DEFAULT]
});
expect(collection._type).toEqual([apiKeysCollectionTypes.MASTER, apiKeysCollectionTypes.DEFAULT]);
});
it('should set status to fetched when synced', function () {
collection.trigger('sync');
expect(collection.status).toBe('fetched');
});
it('should set status to errored when sync has any error', function () {
collection.trigger('error');
expect(collection.status).toBe('errored');
});
describe('._url', function () {
it('should return URL properly', function () {
expect(collection.url()).toBe('wadus.com/api/v3/api_keys?per_page=5&page=1&type%5B%5D=regular');
});
});
it('should add UserModel to items', function () {
const collectionItem = collection.add(modelData);
expect(collectionItem._userModel).toBeDefined();
expect(collectionItem instanceof ApiKeyModel).toBe(true);
});
it('should add id property to item', function () {
const parsedResponse = collection.parse({
result: [modelData]
});
expect(parsedResponse[0].id).toBe(modelData.name);
});
});
@@ -0,0 +1,13 @@
var AuthenticatedUser = require('dashboard/data/authenticated-user-model');
describe('dashboard/data/authenticated-user-model', function () {
beforeEach(function () {
this.model = new AuthenticatedUser({});
});
it('should return the normal URL', function () {
spyOn(this.model, 'getHost').and.returnValue('test.carto.com');
expect(this.model.url()).toBe('//test.carto.com/api/v1/get_authenticated_users');
});
});
@@ -0,0 +1,13 @@
var ConfigModel = require('dashboard/data/config-model');
var Backbone = require('backbone');
describe('dashboard/data/config-model', function () {
beforeEach(function () {
this.configModel = new ConfigModel();
});
it('should have a modules collection', function () {
expect(this.configModel.modules).not.toBe(undefined);
expect(this.configModel.modules).toEqual(jasmine.any(Backbone.Collection));
});
});
@@ -0,0 +1,132 @@
const _ = require('underscore');
const BackgroundPollingModel = require('dashboard/data/background-polling/dashboard-background-polling-model');
const ImportCollection = require('dashboard/data/imports-collection');
const MapUrlModel = require('dashboard/data/map-url-model');
const VisualizationModel = require('dashboard/data/visualization-model');
const configModel = require('fixtures/dashboard/config-model.fixture');
const userModelFixture = require('fixtures/dashboard/user-model.fixture');
function attributesForRedirect (attrs) {
return _.extend({
tables_created_count: 2,
service_name: 'other',
table_name: 'foobar',
state: 'complete',
display_name: 'foo.carto'
}, attrs);
}
describe('dashboard/data/background-polling-model', function () {
beforeEach(function () {
this.user = userModelFixture({
username: 'pepe',
base_url: 'http://pepe.carto.com'
});
this.importsCollection = new ImportCollection({}, {
userModel: this.user,
configModel
});
this.importsModel = this.importsCollection.at(0);
this.model = new BackgroundPollingModel({}, {
userModel: this.user,
configModel,
importsCollection: this.importsCollection
});
spyOn(this.model, '_redirectTo');
this.url = new MapUrlModel({
base_url: 'http://pepe.carto.com/viz/abc-123'
});
spyOn(VisualizationModel.prototype, 'viewUrl').and.returnValue(this.url);
this.importsModel.set('step', 'import');
// Add some more imports to the collection
this.importsCollection.add({});
this.importsCollection.add({});
this.importsModel2 = this.importsCollection.at(1);
this.importsModel3 = this.importsCollection.at(2);
// Two previous imports have failed
spyOn(this.importsModel2, 'hasCompleted').and.returnValue(false);
spyOn(this.importsModel2, 'hasFailed').and.returnValue(true);
spyOn(this.importsModel3, 'hasCompleted').and.returnValue(false);
spyOn(this.importsModel3, 'hasFailed').and.returnValue(true);
});
describe('when import has been completed', function () {
beforeEach(function () {
spyOn(this.importsModel, 'hasCompleted').and.returnValue(true);
});
it('should redirect to vis', function () {
this.importsModel.getImportModel().set(attributesForRedirect());
expect(this.model._redirectTo).toHaveBeenCalled();
expect(this.model._redirectTo.calls.argsFor(0)[0]).toEqual('http://pepe.carto.com/viz/abc-123/map');
expect(VisualizationModel.prototype.viewUrl).toHaveBeenCalledWith(this.user);
});
describe('should NOT redirect', function () {
it('if more than one table is created and file is not .carto', function () {
this.importsModel.getImportModel().set(attributesForRedirect({
tables_created_count: 2,
display_name: 'foo.csv'
}));
this.importsModel.set('state', 'complete');
expect(this.model._redirectTo).not.toHaveBeenCalled();
});
it('if service name is twitter search', function () {
this.importsModel.getImportModel().set(attributesForRedirect({
'service_name': 'twitter_search'
}));
this.importsModel.set('state', 'complete');
expect(this.model._redirectTo).not.toHaveBeenCalled();
});
it('if for some reason there is no vis to redirect to', function () {
spyOn(this.importsModel, 'importedVis').and.returnValue(false);
this.importsModel.set('state', 'complete');
expect(this.model._redirectTo).not.toHaveBeenCalled();
});
it('if other imports are still running', function () {
this.importsModel2.hasCompleted.and.returnValue(false);
this.importsModel2.hasFailed.and.returnValue(false);
this.importsModel.getImportModel().set(attributesForRedirect());
this.importsModel.set('state', 'complete');
expect(this.model._redirectTo).not.toHaveBeenCalled();
});
it('if other imports have been completed', function () {
// Has failed
this.importsModel2.hasFailed.and.returnValue(true);
this.importsModel2.hasCompleted.and.returnValue(false);
// Has completed
this.importsModel3.hasFailed.and.returnValue(false);
this.importsModel3.hasCompleted.and.returnValue(true);
this.importsModel.getImportModel().set(attributesForRedirect());
this.importsModel.set('state', 'complete');
expect(this.model._redirectTo).not.toHaveBeenCalled();
});
});
});
describe('when import has NOT been completed', function () {
beforeEach(function () {
spyOn(this.importsModel, 'hasCompleted').and.returnValue(false);
});
it('should not redirect if not complete yet', function () {
this.importsModel.set('state', 'whatever');
expect(this.model._redirectTo).not.toHaveBeenCalled();
});
});
});
@@ -0,0 +1,53 @@
var DashboardDatasetsUrlModel = require('dashboard/data/dashboard-datasets-url-model');
describe('dashboard/data/dashboard-datasets-url-model', function () {
beforeEach(function () {
this.url = new DashboardDatasetsUrlModel({
base_url: 'http://team.carto.com/u/pepe/dashboard/datasets'
});
});
describe('.lockedItems', function () {
beforeEach(function () {
this.newUrl = this.url.lockedItems();
});
it('should return a new URL pointing at locked items', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/dashboard/datasets/locked');
});
});
describe('.sharedItems', function () {
beforeEach(function () {
this.newUrl = this.url.sharedItems();
});
it('should return a new URL pointing at shared items', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/dashboard/datasets/shared');
});
});
describe('.likedItems', function () {
beforeEach(function () {
this.newUrl = this.url.likedItems();
});
it('should return a new URL pointing at liked items', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/dashboard/datasets/liked');
});
});
describe('.dataLibrary', function () {
beforeEach(function () {
this.newUrl = this.url.dataLibrary();
});
it('should return a new URL pointing at data library', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/dashboard/datasets/library');
});
});
});
@@ -0,0 +1,31 @@
var DashboardUrlModel = require('dashboard/data/dashboard-url-model');
describe('dashboard/data/dashboard-url-model', function () {
beforeEach(function () {
this.url = new DashboardUrlModel({
base_url: 'http://team.carto.com/u/pepe/dashboard'
});
});
describe('.datasets', function () {
beforeEach(function () {
this.newUrl = this.url.datasets();
});
it('should return a new URL pointing for datsets', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url').toString()).toEqual('http://team.carto.com/u/pepe/dashboard/datasets');
});
});
describe('.maps', function () {
beforeEach(function () {
this.newUrl = this.url.maps();
});
it('should return a new URL for maps', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url').toString()).toEqual('http://team.carto.com/u/pepe/dashboard/maps');
});
});
});
@@ -0,0 +1,42 @@
var DashboardVisUrlModel = require('dashboard/data/dashboard-vis-url-model');
describe('dashboard/data/dashboard-vis-url-model', function () {
beforeEach(function () {
this.url = new DashboardVisUrlModel({
base_url: 'http://team.carto.com/u/pepe/dashboard/maps'
});
});
describe('.lockedItems', function () {
beforeEach(function () {
this.newUrl = this.url.lockedItems();
});
it('should return a new URL pointing at locked items', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/dashboard/maps/locked');
});
});
describe('.sharedItems', function () {
beforeEach(function () {
this.newUrl = this.url.sharedItems();
});
it('should return a new URL pointing at shared items', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/dashboard/maps/shared');
});
});
describe('.likedItems', function () {
beforeEach(function () {
this.newUrl = this.url.likedItems();
});
it('should return a new URL pointing at liked items', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/dashboard/maps/liked');
});
});
});
@@ -0,0 +1,31 @@
const DatasetUrlModel = require('dashboard/data/dataset-url-model');
describe('dashboard/data/dataset-url-model', function () {
beforeEach(function () {
this.url = new DatasetUrlModel({
base_url: 'http://team.carto.com/u/pepe/tables/awesome_data'
});
});
describe('.edit', function () {
beforeEach(function () {
this.newUrl = this.url.edit();
});
it('should return a new URL pointing at the page where the map can be edited', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/tables/awesome_data');
});
});
describe('.public', function () {
beforeEach(function () {
this.newUrl = this.url.public();
});
it('should return a new URL pointing at the page where the map can be seen publically', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/tables/awesome_data/public');
});
});
});
@@ -0,0 +1,20 @@
const DatasetsCollection = require('dashboard/data/datasets-collection');
const configModel = require('fixtures/dashboard/config-model.fixture');
describe('data-library/data-library-view', function () {
let collection;
beforeEach(function () {
collection = new DatasetsCollection(null, { configModel });
});
describe('.url', function () {
it('creates the url properly', function () {
const host = `rick.wadus.com`;
const options = 'tag_name=&q=&page=1&type=derived&exclude_shared=false&per_page=12';
expect(collection.url()).toEqual(`//${host}/api/v1/viz/?${options}`);
});
});
});
@@ -0,0 +1,64 @@
const Backbone = require('backbone');
const GeocodingModelPoller = require('dashboard/data/background-polling/geocoding-model-poller');
describe('GeocodingModelPoller', function () {
var originalFetch;
beforeEach(function () {
var Model = Backbone.Model.extend({
url: '/hello'
});
this.model = new Model();
this.model.hasFailed = function () { return false; };
this.model.hasCompleted = function () { return false; };
this.model.sync = function (a, b, opts) {
opts.success();
};
originalFetch = this.model.fetch;
spyOn(this.model, 'fetch').and.callThrough();
this.poller = new GeocodingModelPoller(this.model);
jasmine.clock().install();
});
afterEach(function () {
jasmine.clock().uninstall();
});
it('should stop polling when geocoding has failed', function () {
this.model.hasFailed = function () { return true; };
this.poller.start();
jasmine.clock().tick(4000);
expect(this.model.fetch.calls.count()).toBe(1);
});
it('should stop polling when geocoding has completed', function () {
this.model.hasCompleted = function () { return true; };
this.poller.start();
jasmine.clock().tick(4000);
expect(this.model.fetch.calls.count()).toBe(1);
});
it('should trigger a change event if an error occurs', function () {
this.model.fetch = originalFetch;
spyOn(this.model, 'fetch');
var onErrorCallback = jasmine.createSpy('error');
this.model.bind('change', onErrorCallback);
this.poller.start();
jasmine.clock().tick(2001);
this.model.fetch.calls.mostRecent().args[0].error();
expect(onErrorCallback).toHaveBeenCalled();
});
});
@@ -0,0 +1,90 @@
var GeocodingModel = require('dashboard/data/background-polling/geocoding-model');
const configModel = require('fixtures/dashboard/config-model.fixture');
describe('common/background_polling/geocoding_model', function () {
beforeEach(function () {
GeocodingModel.prototype.sync = function (a, b, opts) {
opts.success();
};
this.model = new GeocodingModel({ configModel });
spyOn(this.model, 'bind').and.callThrough();
});
it('should not start polling when option is not enabled', function () {
spyOn(this.model, 'fetch');
this.model.options.startPollingAutomatically = false;
this.model.set('id', 1);
this.model.initialize({ configModel });
expect(this.model.fetch).not.toHaveBeenCalled();
});
it('should save model when it is new', function () {
spyOn(this.model, 'save');
this.model._checkModel();
expect(this.model.save).toHaveBeenCalled();
});
it('should have several change binds', function () {
this.model._initBinds();
expect(this.model.bind.calls.argsFor(0)[0]).toEqual('change:id');
});
describe('.pollCheck', function () {
beforeEach(function () {
jasmine.clock().install();
spyOn(this.model, 'fetch');
});
afterEach(function () {
jasmine.clock().uninstall();
});
it('should start polling', function () {
spyOn(this.model.poller, 'start');
this.model.pollCheck();
expect(this.model.poller.start).toHaveBeenCalled();
});
});
describe('.destroyCheck', function () {
beforeEach(function () {
jasmine.clock().install();
spyOn(this.model, 'fetch');
});
afterEach(function () {
jasmine.clock().uninstall();
});
it('should stop polling', function () {
spyOn(this.model.poller, 'stop');
this.model.pollCheck();
this.model.destroyCheck();
expect(this.model.poller.stop).toHaveBeenCalled();
});
});
it('should know when geocoding has finished', function () {
expect(this.model.hasCompleted()).toBeFalsy();
this.model.set('state', 'finished');
expect(this.model.hasCompleted()).toBeTruthy();
this.model.set('state', 'geocoding');
expect(this.model.hasCompleted()).toBeFalsy();
});
it('should know when geocoding has failed', function () {
expect(this.model.hasFailed()).toBeFalsy();
this.model.set('state', 'failed');
expect(this.model.hasFailed()).toBeTruthy();
this.model.set('state', 'geocoding');
expect(this.model.hasFailed()).toBeFalsy();
this.model.set('state', 'reset');
expect(this.model.hasFailed()).toBeTruthy();
this.model.set('state', 'cancelled');
expect(this.model.hasFailed()).toBeTruthy();
});
});
@@ -0,0 +1,96 @@
const GrantablesCollection = require('dashboard/data/grantables-collection');
const organizationModel = require('fixtures/dashboard/organization-model.fixture');
const configModel = require('fixtures/dashboard/config-model.fixture');
describe('dashboard/data/grantables-collection', function () {
beforeEach(function () {
this.org = organizationModel;
this.grantables = new GrantablesCollection(undefined, {
organization: this.org,
currentUserId: 'u2',
configModel
});
});
it('should have a working URL', function () {
expect(this.grantables.url()).toMatch('/organization/o1/grantables');
});
describe('when is fetched', function () {
beforeEach(function () {
this.grantables.sync = function (a, b, opts) {
opts.success && opts.success({
'grantables': [{
'id': 'u1',
'type': 'user',
'name': 'an user',
'avatar_url': 'images/avatars/avatar_marker_red.png',
'model': {
'id': 'u1',
'username': 'foo'
// …
}
}, {
'id': 'u2' // current user
}, {
'id': 'g1',
'type': 'group',
'name': 'my group',
'avatar_url': 'avatar_marker_red.png',
'model': {
'id': 'g1',
'display_name': 'my group',
'name': 'my_group'
// …
}
}],
'total_entries': 3
});
};
this.grantables.fetch();
});
it('should set grantables model', function () {
expect(this.grantables.length).toBeGreaterThan(0);
});
it('should have a organization set on collection', function () {
expect(this.grantables.organization).toBe(this.org);
});
it('should not add current user', function () {
expect(this.grantables.length).toEqual(2);
expect(this.grantables.total_entries).toEqual(2);
expect(this.grantables.pluck('id')).toEqual(['u1', 'g1']);
});
it('should set organization on each entity model', function () {
expect(this.grantables.first().entity.organization).toBe(this.org);
expect(this.grantables.last().entity.organization).toBe(this.org);
});
});
describe('.totalCount', function () {
it('should not be set initially', function () {
expect(this.grantables.totalCount()).toBeUndefined();
});
describe('when data is fetched', function () {
beforeEach(function () {
this.grantables.sync = function (a, b, opts) {
opts.success && opts.success({
grantables: [
],
total_entries: 77
});
};
this.grantables.fetch();
});
it('should have the total count set', function () {
expect(this.grantables.totalCount()).toEqual(77);
});
});
});
});
@@ -0,0 +1,37 @@
const GroupModel = require('dashboard/data/group-model');
const CONFIG_MODEL = require('fixtures/dashboard/config-model.fixture');
describe('dashboard/data/group-model', function () {
describe('when given no attrs', function () {
beforeEach(function () {
this.group = new GroupModel({}, { configModel: CONFIG_MODEL });
});
it('should create an empty users collection', function () {
expect(this.group.users).toBeDefined();
expect(this.group.users.length).toEqual(0);
});
});
describe('when given some attrs', function () {
beforeEach(function () {
this.group = new GroupModel({
id: 'g1',
display_name: 'My Group',
name: 'my_group',
users: [{
id: 'u1',
username: 'pepe'
}]
}, { configModel: CONFIG_MODEL });
});
it('should create a users collection from given members collection', function () {
expect(this.group.users).toBeDefined();
expect(this.group.users.length).toEqual(1);
expect(this.group.users.first().get('username')).toEqual('pepe');
expect(this.group.users.group).toBe(this.group);
});
});
});
@@ -0,0 +1,220 @@
const $ = require('jquery');
const OrganizationGroupsCollection = require('dashboard/data/organization-groups-collection');
const GroupUsersCollection = require('dashboard/data/group-users-collection');
const configModel = require('fixtures/dashboard/config-model.fixture');
const userFixture = require('fixtures/dashboard/user-model.fixture');
const group = require('fixtures/dashboard/group-model.fixture');
describe('dashboard/data/group-users-collection', function () {
beforeEach(function () {
var user = userFixture();
const organizationGroups = new OrganizationGroupsCollection(undefined, {
organization: user.organization,
configModel
});
this.group = group;
this.group.collection = organizationGroups;
this.groupUsers = new GroupUsersCollection(undefined, {
group: this.group,
configModel
});
});
it('should create an empty users collection', function () {
expect(this.groupUsers.length).toEqual(0);
});
describe('.addInBatch', function () {
beforeEach(function () {
jasmine.Ajax.install();
spyOn($, 'ajax').and.callThrough();
spyOn(this.groupUsers, 'fetch');
this.result = this.groupUsers.addInBatch([1, 2, 3]);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
responseText: '{}'
});
});
afterEach(function () {
jasmine.Ajax.uninstall();
});
it('should return a deferred object', function () {
expect(this.result).toBeDefined();
expect(this.result.done).toBeDefined();
expect(this.result.fail).toBeDefined();
});
it('should make a POST call to add users to group', function () {
expect($.ajax).toHaveBeenCalled();
expect($.ajax.calls.argsFor(0)[0].type).toEqual('POST');
expect($.ajax.calls.argsFor(0)[0].data).toEqual(jasmine.objectContaining({ users: [1, 2, 3] }));
});
it('should send password confirmation if available', function () {
const passwordConfirmation = 'password';
this.result = this.groupUsers.addInBatch([1, 2, 3], passwordConfirmation);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
responseText: '{}'
});
expect($.ajax.calls.argsFor(1)[0].type).toEqual('POST');
expect($.ajax.calls.argsFor(1)[0].data).toEqual(jasmine.objectContaining({
users: [1, 2, 3],
password_confirmation: passwordConfirmation
}));
});
describe('when successfully added users', function () {
it('should fetch collection to update to proper state', function () {
expect(this.groupUsers.fetch).toHaveBeenCalled();
});
it('should not resolve promise just yet', function () {
const isResolved = this.result.state() === 'resolved';
expect(isResolved).toBe(false);
});
describe('when fetch succeeds', function () {
beforeEach(function () {
this.groupUsers.fetch.calls.argsFor(0)[0].success();
});
it('should resolved promise', function () {
const isResolved = this.result.state() === 'resolved';
expect(isResolved).toBe(true);
});
});
describe('when fetch fails', function () {
beforeEach(function () {
this.groupUsers.fetch.calls.argsFor(0)[0].error();
});
it('should resolved promise', function () {
const isResolved = this.result.state() === 'resolved';
expect(isResolved).toBe(true);
});
});
});
});
describe('.removeInBatch', function () {
beforeEach(function () {
jasmine.Ajax.install();
spyOn($, 'ajax').and.callThrough();
spyOn(this.groupUsers, 'fetch');
this.groupUsers.reset([{id: 1}, {id: 2}, {id: 9000}]);
this.result = this.groupUsers.removeInBatch([1, 2, 3]);
});
afterEach(function () {
jasmine.Ajax.uninstall();
});
it('should return a deferred object', function () {
expect(this.result).toBeDefined();
expect(this.result.done).toBeDefined();
expect(this.result.fail).toBeDefined();
});
it('should make a POST call to add users to group', function () {
expect($.ajax).toHaveBeenCalled();
expect($.ajax.calls.argsFor(0)[0].type).toEqual('DELETE');
expect($.ajax.calls.argsFor(0)[0].data).toEqual(jasmine.objectContaining({ users: [1, 2, 3] }));
});
it('should send password confirmation if available', function () {
const passwordConfirmation = 'password';
this.result = this.groupUsers.removeInBatch([1, 2, 3], passwordConfirmation);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
responseText: '{}'
});
expect($.ajax.calls.argsFor(1)[0].type).toEqual('DELETE');
expect($.ajax.calls.argsFor(1)[0].data).toEqual(jasmine.objectContaining({
users: [1, 2, 3],
password_confirmation: passwordConfirmation
}));
});
describe('when successfully removed users', function () {
beforeEach(function () {
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
responseText: '{}'
});
});
it('should fetch collection to update to proper state', function () {
expect(this.groupUsers.fetch).toHaveBeenCalled();
});
it('should not change collection until fetch is successfully returned', function () {
expect(this.groupUsers.pluck('id')).toEqual([1, 2, 9000]);
});
it('should not resolved promise just yet', function () {
const isResolved = this.result.state() === 'resolved';
expect(isResolved).toBe(false);
});
describe('when fetch succeeds', function () {
beforeEach(function () {
this.groupUsers.fetch.calls.argsFor(0)[0].success();
});
it('should resolved promise', function () {
const isResolved = this.result.state() === 'resolved';
expect(isResolved).toBe(true);
});
});
describe('when fetch fails', function () {
beforeEach(function () {
this.groupUsers.fetch.calls.argsFor(0)[0].error();
});
it('should remove users from collection, but might not be accurate state since fetch failed', function () {
expect(this.groupUsers.pluck('id')).toEqual([9000]);
});
it('should resolved promise', function () {
const isResolved = this.result.state() === 'resolved';
expect(isResolved).toBe(true);
});
});
});
});
describe('.totalCount', function () {
it('should not be set initially', function () {
expect(this.groupUsers.totalCount()).toBeUndefined();
});
describe('when data is fetched', function () {
beforeEach(function () {
this.groupUsers.sync = function (a, b, opts) {
opts.success && opts.success({
users: [
],
total_user_entries: 77,
total_entries: 0
});
};
this.groupUsers.fetch();
});
it('should have the total count set', function () {
expect(this.groupUsers.totalCount()).toEqual(77);
});
});
});
});
@@ -0,0 +1,32 @@
const ImportsCollection = require('dashboard/data/imports-collection');
const userFixture = require('fixtures/dashboard/user-model.fixture');
const configModel = require('fixtures/dashboard/config-model.fixture');
describe('dashboard/data/imports-collection', function () {
beforeEach(function () {
this.user = userFixture({
username: 'pepe',
base_url: 'http://pepe.carto.com'
});
this.collection = new ImportsCollection(undefined, {
userModel: this.user,
configModel
});
});
describe('.failedItems', function () {
it('should return the failed items', function () {
expect(this.collection.failedItems()).toEqual([]);
// Add three of which one is failed
this.collection.reset([{
}, {
state: 'failure',
step: 'import'
}, {
}]);
expect(this.collection.failedItems().length).toEqual(1);
});
});
});
@@ -0,0 +1,230 @@
const userFixture = require('fixtures/dashboard/user-model.fixture');
const configModel = require('fixtures/dashboard/config-model.fixture');
const ImportsModel = require('dashboard/data/imports-model');
const UploadModel = require('dashboard/data/upload-model');
describe('common/background_polling/imports_model', function () {
beforeEach(function () {
var user = userFixture({ username: 'paco' });
this.model = new ImportsModel(null, { userModel: user, configModel });
// No jasmine spy objects
this.model._importModel.createImport = function () {};
this.model._importModel.pollCheck = function () {};
});
it('should start in upload step', function () {
expect(this.model.get('step')).toBe('upload');
});
it('should change state when upload or import state change', function () {
this.model._uploadModel.set('state', 'uploading');
expect(this.model.get('state')).toBe('uploading');
expect(this.model.get('step')).toBe('upload');
this.model._uploadModel.set({
state: 'uploaded',
item_queue_id: 'vamos-neno'
});
expect(this.model.get('state')).toBe('uploaded');
expect(this.model.get('id')).toBe('vamos-neno');
expect(this.model.get('step')).toBe('import');
this.model._importModel.set('state', 'zipping');
expect(this.model.get('state')).toBe('zipping');
});
it('should return error info', function () {
expect(this.model.getError().title).toBeDefined();
expect(this.model.getError().what_about).toBeDefined();
expect(this.model.getError().error_code).toBeDefined();
});
it('should return is import has failed', function () {
expect(this.model.hasFailed()).toBeFalsy();
this.model._uploadModel.set('state', 'error');
expect(this.model.hasFailed()).toBeTruthy();
this.model._uploadModel.set('state', 'upgraded');
expect(this.model.hasFailed()).toBeFalsy();
this.model.set('id', 'vamos-neno');
this.model._importModel.set('state', 'failure');
expect(this.model.hasFailed()).toBeTruthy();
this.model._importModel.set('state', 'error');
expect(this.model.hasFailed()).toBeFalsy();
});
it('should return is import has been completed', function () {
expect(this.model.hasCompleted()).toBeFalsy();
this.model.set('id', 'vamos-neno');
this.model._importModel.set('state', 'complete');
expect(this.model.hasCompleted()).toBeTruthy();
expect(this.model.get('state')).toBe('complete');
});
it('should be able to return import info', function () {
this.model._importModel.createImport = function () {};
this.model.set('id', 'vamos-neno');
expect(this.model.get('import')).not.toBe(undefined);
expect(this.model.get('import').item_queue_id).toBe('vamos-neno');
});
it('should be able to return upload info', function () {
this.model._importModel.createImport = function () {};
this.model._uploadModel.set({ type: 'url', value: 'http://fakeurl.es/j.csv' });
expect(this.model.get('upload')).not.toBe(undefined);
expect(this.model.get('upload').type).toBe('url');
expect(this.model.get('upload').value).toBe('http://fakeurl.es/j.csv');
});
it('should start importing if model id is set', function () {
spyOn(this.model._importModel, 'pollCheck');
this.model.set('id', 'vamos-neno');
expect(this.model._importModel.pollCheck).toHaveBeenCalled();
});
it('should stop upload and import when error is set', function () {
spyOn(this.model, 'stopUpload');
spyOn(this.model, 'stopImport');
this.model.setError({ error_code: 1111 });
expect(this.model.stopUpload).toHaveBeenCalled();
expect(this.model.stopImport).toHaveBeenCalled();
expect(this.model.get('state')).toBe('error');
});
describe('checkStatus', function () {
beforeEach(function () {
spyOn(this.model._importModel, 'set');
spyOn(this.model._importModel, 'createImport');
spyOn(this.model._uploadModel, 'upload');
});
it('should stop process when upload is not valid', function () {
this.model._checkStatus();
expect(this.model._importModel.set).not.toHaveBeenCalled();
expect(this.model._importModel.createImport).not.toHaveBeenCalled();
expect(this.model._uploadModel.upload).not.toHaveBeenCalled();
});
it('should not stop process when upload is not valid but there is an id already set', function () {
this.model._uploadModel.set({
type: 'url',
value: ''
});
this.model.set('id', 'idd');
this.model._checkStatus();
expect(this.model._importModel.set).toHaveBeenCalled();
});
it('should start upload when type is file', function () {
this.model._uploadModel.set({
type: 'file',
value: { name: 'fake.csv', size: 1000 }
});
this.model._checkStatus();
expect(this.model._uploadModel.upload).toHaveBeenCalled();
});
it('should create an import when type is not file and there is no id defined', function () {
this.model._uploadModel.set({
type: 'url',
value: 'http://paco.com'
});
this.model._checkStatus();
expect(this.model._importModel.createImport).toHaveBeenCalled();
});
});
describe('upload model', function () {
beforeEach(function () {
var user = userFixture({ username: 'paco' });
this.upload = new UploadModel({}, { userModel: user, configModel });
spyOn(this.upload, 'bind').and.callThrough();
});
it('should have progress, change:value and error/invalid binds', function () {
this.upload._initBinds();
var args_0 = this.upload.bind.calls.argsFor(0);
expect(args_0[0]).toEqual('progress');
var args_1 = this.upload.bind.calls.argsFor(1);
expect(args_1[0]).toEqual('change:value');
var args_2 = this.upload.bind.calls.argsFor(2);
expect(args_2[0]).toEqual('error invalid');
});
it('should set uploading state when progress change', function () {
this.upload.set({ value: 'http://paco.csv', type: 'url' });
this.upload.trigger('progress', 20);
expect(this.upload.get('state')).toBe('uploading');
});
it('should have a fileAttribute set', function () {
expect(this.upload.fileAttribute).toBe('filename');
});
it('should change state when validate fails', function () {
this.upload.set({
type: 'url',
value: 'hello'
}, { validate: true });
expect(this.upload.get('state')).toBe('error');
});
it('should add error code when size validation fails', function () {
this.upload._userModel.set('remaining_byte_quota', 1);
this.upload.set({
type: 'file',
value: { name: 'fake.csv', size: 1000 }
}, { validate: true });
expect(this.upload.get('state')).toBe('error');
expect(this.upload.get('error_code')).toBe(8001);
this.upload.set({
type: 'remote',
remote_visualization_id: 'iddd',
size: 1000
}, { validate: true });
expect(this.upload.get('state')).toBe('error');
expect(this.upload.get('error_code')).toBe(8001);
this.upload.set({
type: 'url',
value: 'heyheyhey.org'
}, { validate: true });
expect(this.upload.get('state')).toBe('error');
expect(this.upload.get('error_code')).toBe('');
});
it('should raise an error when file name is not provided', function () {
this.upload.set({
type: 'file',
value: { }
}, { validate: true });
expect(this.upload.get('state')).toBe('error');
expect(this.upload.get('get_error_text').what_about).toBe('data.upload-model.file-defined');
});
describe('.setFresh', function () {
beforeEach(function () {
spyOn(this.upload, 'set');
this.upload.setFresh({
foo: 'bar',
create_vis: true
});
});
it('should set the given dataset', function () {
expect(this.upload.set).toHaveBeenCalled();
expect(this.upload.set.calls.argsFor(0)[0]).toEqual(jasmine.objectContaining({ foo: 'bar' }));
});
it('should omit the create_vis value since set in constructor', function () {
expect(this.upload.set.calls.argsFor(0)[0]).not.toEqual(jasmine.objectContaining({ create_vis: true }));
});
});
});
describe('import model', function () {
it('should not have any tests because we are using the builder one');
});
});
@@ -0,0 +1,77 @@
var LikeModel = require('dashboard/data/like-model');
var ConfigModel = require('dashboard/data/config-model');
describe('dashboard/data/like-model', function () {
beforeEach(function () {
this.config = new ConfigModel({
user_name: 'wadus'
});
});
describe('by default', function () {
beforeEach(function () {
this.like = new LikeModel(null, {
config: this.config
});
});
it('should be likeable', function () {
expect(this.like.get('likeable')).toBeTruthy();
});
});
describe('.newByVisData', function () {
beforeEach(function () {
this.visId = 123;
});
describe('when url is provided', function () {
beforeEach(function () {
this.like = LikeModel.newByVisData({
url: 'http://patata.domain.com/api/like',
vis_id: this.visId,
config: this.config
});
});
it('should return a new like model with custom url', function () {
expect(this.like.url).toBe('http://patata.domain.com/api/like');
});
});
describe('when liked', function () {
beforeEach(function () {
this.like = LikeModel.newByVisData({
vis_id: this.visId,
liked: true,
config: this.config
});
});
it('should return a new like model with liked set', function () {
expect(this.like.get('liked')).toBeTruthy();
});
it('should return a new like model with id set to vis id', function () {
expect(this.like.get('id')).toEqual(this.visId);
});
});
describe('when not liked', function () {
beforeEach(function () {
this.like = LikeModel.newByVisData({
vis_id: this.visId,
config: this.config
});
});
it('should return a new like model with liked set to false', function () {
expect(this.like.get('liked')).toBeFalsy();
});
it('should return a new like model with no id', function () {
expect(this.like.get('id')).toBeNull();
});
});
});
});
@@ -0,0 +1,31 @@
const MapUrl = require('dashboard/data/map-url-model');
describe('dashboard/data/map-url-model', function () {
beforeEach(function () {
this.url = new MapUrl({
base_url: 'http://team.carto.com/u/pepe/viz/8b44c8ba-6fcf-11e4-8581-080027880ca6'
});
});
describe('.edit', function () {
beforeEach(function () {
this.newUrl = this.url.edit();
});
it('should return a new URL pointing at the page where the map can be edited', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/viz/8b44c8ba-6fcf-11e4-8581-080027880ca6/map');
});
});
describe('.public', function () {
beforeEach(function () {
this.newUrl = this.url.public();
});
it('should return a new URL pointing at the page where the map can be seen publically', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/viz/8b44c8ba-6fcf-11e4-8581-080027880ca6/public_map');
});
});
});
@@ -0,0 +1,104 @@
const OrganizationGroupsCollection = require('dashboard/data/organization-groups-collection');
const userFixture = require('fixtures/dashboard/user-model.fixture');
const configModel = require('fixtures/dashboard/config-model.fixture');
describe('dashboard/data/OrganizationGroupsCollection', function () {
beforeEach(function () {
var user = userFixture();
this.org = user.organization;
this.groups = new OrganizationGroupsCollection([], {
organization: user.organization,
configModel
});
});
it('should set organization', function () {
expect(this.groups.organization).toBe(this.org);
});
describe('.newGroupById', function () {
describe('when the group with the given id is not yet loaded', function () {
beforeEach(function () {
this.group = this.groups.newGroupById('g1');
});
it('should return a group with the given id set', function () {
expect(this.group.id).toEqual('g1');
});
it('should have a valid url under the organization', function () {
// required to be able to fetch the group from the expected correct location
var url = this.group.url();
expect(url).toBeTruthy();
expect(url).toMatch('organization/o1/groups/g1');
});
it('should set collection on group', function () {
expect(this.group.collection).toBe(this.groups);
});
});
describe('when group with given id already exists in collection', function () {
beforeEach(function () {
this.groups.add({
id: 'g2',
display_name: 'foobar'
});
this.group = this.groups.newGroupById('g2');
});
it('should return existing group', function () {
expect(this.group).toBe(this.groups.first());
expect(this.group.collection).toBe(this.groups);
});
});
});
describe('when groups is fetched', function () {
beforeEach(function () {
this.groups.sync = function (a, b, opts) {
opts.success && opts.success({
groups: [{
id: 'g1'
}],
total_entries: 1
});
};
this.groups.fetch();
});
it('should set organization on group objects', function () {
expect(this.groups.length).toEqual(1);
});
it('should have a total_entries prop set', function () {
expect(this.groups.total_entries).toEqual(1);
});
});
describe('.totalCount', function () {
it('should not be set initially', function () {
expect(this.groups.totalCount()).toBeUndefined();
});
describe('when data is fetched', function () {
beforeEach(function () {
this.groups.sync = function (a, b, opts) {
opts.success && opts.success({
groups: [{
id: 'g1'
}],
total_entries: 1
});
};
this.groups.fetch();
});
it('should have the total count set', function () {
expect(this.groups.totalCount()).toEqual(1);
});
});
});
});
@@ -0,0 +1,44 @@
const OrganizationModel = require('dashboard/data/organization-model');
const CONFIG_MODEL = require('fixtures/dashboard/config-model.fixture');
describe('dashboard/data/organization-model', function () {
beforeEach(function () {
this.organization = new OrganizationModel({
id: '68a57d79-d454-4b28-8af8-968e97b1349c',
seats: 5,
quota_in_bytes: 1234567890,
created_at: '2014-06-05T16:34:51+02:00',
updated_at: '2014-06-05T16:34:51+02:00',
name: 'orgtest3',
owner: { id: '4ab67a64-7cbf-4890-88b8-9d3c398249d5', username: 'dev', avatar_url: null },
users: []
}, { configModel: CONFIG_MODEL });
});
it('should have a users collection attribute', function () {
expect(this.organization.users).not.toBeUndefined();
});
it('all collections should have reference to the organization', function () {
var newOrg = new OrganizationModel({
id: '68a57d79-d454-4b28-8af8-968e97b1349c',
seats: 5,
quota_in_bytes: 1234567890,
created_at: '2014-06-05T16:34:51+02:00',
updated_at: '2014-06-05T16:34:51+02:00',
name: 'orgtest3',
owner: { id: '4ab67a64-7cbf-4890-88b8-9d3c398249d5', username: 'dev', avatar_url: null },
users: [
{id: 'b6551618-9544-4f22-b8ba-2242d6b20733', 'username': 't2', 'avatar_url': null},
{id: '5b514d61-fb7a-4b9b-8a0a-3fdb82cf79ca', 'username': 't1', 'avatar_url': null}
],
groups: [
{id: 'g1'},
{id: 'g2'}
]
}, { configModel: CONFIG_MODEL });
expect(newOrg.users.organization).toEqual(newOrg);
expect(newOrg.groups.organization).toEqual(newOrg);
expect(newOrg.grantables.organization).toEqual(newOrg);
});
});
@@ -0,0 +1,45 @@
var Backbone = require('backbone');
var OrganizationUrlModel = require('dashboard/data/organization-url-model');
describe('dashboard/data/organization-url-model', function () {
var BASE_URL = 'http://wadus.com';
var model = new OrganizationUrlModel({ base_url: BASE_URL });
it('throws an error when base_url is missing', function () {
var newModel = function () {
return new OrganizationUrlModel({});
};
expect(newModel).toThrowError('base_url is required');
});
describe('.edit', function () {
it('throws an error if user is missing', function () {
expect(model.edit).toThrowError('User is needed to create the url');
});
it('creates a new url model with edit as base url', function () {
var username = 'rick';
var user = new Backbone.Model({ username: username });
var editUrlModel = model.edit(user);
expect(editUrlModel.get('base_url')).toEqual(BASE_URL + '/' + username + '/edit');
});
});
describe('.create', function () {
it('creates a new url model with new as base url', function () {
var createUrlModel = model.create();
expect(createUrlModel.get('base_url')).toEqual(BASE_URL + '/new');
});
});
describe('.groups', function () {
it('creates a new url model with groups as base url', function () {
var createUrlModel = model.groups();
expect(createUrlModel.get('base_url')).toEqual(BASE_URL + '/groups');
});
});
});
@@ -0,0 +1,121 @@
const Backbone = require('backbone');
const $ = require('jquery');
const OrganizationModel = require('dashboard/data/organization-model');
const OrganizationUsersCollection = require('dashboard/data/organization-users-collection');
const configModel = require('fixtures/dashboard/config-model.fixture');
describe('dashboard/data/organization-users-collection', function () {
beforeEach(function () {
this.org = new OrganizationModel({
id: 'hello-org-id'
}, { configModel });
this.orgUsers = new OrganizationUsersCollection(
null,
{
organization: this.org,
currentUserId: 'current-user-id',
configModel
}
);
this.originalSync = this.orgUsers.sync;
this.orgUsers.sync = function (a, b, opts) {};
});
it('should include sync abort', function () {
expect(this.orgUsers.sync).not.toBeUndefined();
});
it('should parse results properly', function () {
this.orgUsers.sync = function (a, b, opts) {
opts.success && opts.success({
users: [
generateUser()
],
total_user_entries: 1,
total_entries: 1
});
};
this.orgUsers.fetch();
expect(this.orgUsers.length).toBe(1);
expect(this.orgUsers.total_user_entries).toBe(1);
expect(this.orgUsers.total_entries).toBe(1);
});
it('should exclude current user from results', function () {
this.orgUsers.sync = function (a, b, opts) {
opts.success && opts.success({
users: [
generateUser('current-user-id'),
generateUser()
],
total_user_entries: 2,
total_entries: 2
});
};
this.orgUsers.fetch();
expect(this.orgUsers.length).toBe(1);
expect(this.orgUsers.total_user_entries).toBe(1);
expect(this.orgUsers.total_entries).toBe(1);
});
describe('when fetch is given data options', function () {
beforeEach(function () {
spyOn(Backbone, 'sync').and.returnValue($.Deferred());
this.orgUsers.sync = this.originalSync;
this.orgUsers.fetch({
data: {
page: 1,
per_page: 77
}
});
});
it('should set them on the fetch URL', function () {
expect(Backbone.sync).toHaveBeenCalled();
var opts = Backbone.sync.calls.argsFor(0)[2];
expect(opts).toEqual(
jasmine.objectContaining({
data: {
page: 1,
per_page: 77
}
})
);
});
});
describe('.totalCount', function () {
it('should not be set initially', function () {
expect(this.orgUsers.totalCount()).toBeUndefined();
});
describe('when data is fetched', function () {
beforeEach(function () {
this.orgUsers.sync = function (a, b, opts) {
opts.success && opts.success({
users: [
generateUser()
],
total_user_entries: 77,
total_entries: 1
});
};
this.orgUsers.fetch();
});
it('should have the total count set', function () {
expect(this.orgUsers.totalCount()).toEqual(77);
});
});
});
function generateUser (id) {
return {
id: id || 'hello-id',
username: 'user' + id,
avatar_url: 'hi',
base_url: 'base-url' + id
};
}
});
@@ -0,0 +1,519 @@
const UserModel = require('dashboard/data/user-model');
const GroupModel = require('dashboard/data/group-model');
const UserGroupsCollection = require('dashboard/data/user-groups-collection');
const OrganizationModel = require('dashboard/data/organization-model');
const PermissionModel = require('dashboard/data/permission-model');
const configModel = require('fixtures/dashboard/config-model.fixture');
describe('dashboard/data/permission-model', function () {
let model, user1, user2, org, owner;
beforeEach(function () {
this.owner = owner = new UserModel({
id: 'uuid_owner',
username: 'owner'
});
this.permission = model = new PermissionModel({
owner: owner.toJSON(),
entity: {
id: 'test_id',
type: 'vis'
}
}, { configModel });
user1 = new UserModel({
id: 'uuid',
username: 'u1'
});
user2 = new UserModel({
id: 'uuid2',
username: 'u2'
});
this.organization = org = new OrganizationModel({
id: 'org_uuid',
users: [{
id: 'uuid2',
username: 'u2'
}]
}, { configModel });
user2.setOrganization(org);
});
describe('.isOwner', function () {
it('should return true if given model is considered owner of item', function () {
expect(this.permission.isOwner(user1)).toBe(false);
expect(this.permission.isOwner({})).toBe(false);
expect(this.permission.isOwner(this.owner)).toBe(true);
});
it('should return true if ids are not set for owner', function () {
// otherwise there are tons of tests that fails randomly and stalls test suite
this.permission.owner.unset('id');
expect(this.permission.isOwner({})).toBe(true);
});
describe('when has no owner', function () {
beforeEach(function () {
this.permission.owner = undefined;
});
it('should return false when owner is not set', function () {
expect(this.permission.isOwner(this.owner)).toBe(false);
});
});
describe('when owner has no id', function () {
beforeEach(function () {
this.other = this.owner.clone();
this.permission.owner.set('id', undefined);
});
it('should return false when owner has no id', function () {
expect(this.permission.isOwner(this.other)).toBe(false);
});
});
});
describe('.revokeWriteAccess', function () {
beforeEach(function () {
spyOn(this.permission, 'grantReadAccess');
this.permission.revokeWriteAccess(user1);
});
it('should downgrade to read access', function () {
expect(this.permission.grantReadAccess).toHaveBeenCalled();
expect(this.permission.grantReadAccess).toHaveBeenCalledWith(user1);
});
});
describe('.revokeAccess', function () {
describe('when model has own ACL item', function () {
beforeEach(function () {
this.permission.acl.add(this.permission._newAclItem(user1, PermissionModel.READ_ONLY));
this.permission.revokeAccess(user1);
});
it('should return the ACL item', function () {
expect(this.permission.acl.length).toEqual(0);
});
});
});
// Test all can/has/grant-access methods at once, since they all depend on similar setups
describe('access', function () {
beforeEach(function () {
// Setup various group scenarios for later use
this.groupX = new GroupModel({ id: 'gx' }, { configModel });
this.permission.acl.add(this.permission._newAclItem(this.groupX, PermissionModel.READ_ONLY));
this.groupA = new GroupModel({ id: 'ga' }, { configModel });
this.permission.acl.add(this.permission._newAclItem(this.groupA, PermissionModel.READ_ONLY));
this.groupB = new GroupModel({ id: 'gb' }, { configModel });
this.permission.acl.add(this.permission._newAclItem(this.groupB, PermissionModel.READ_WRITE));
});
describe('when given a model w/o any access', function () {
it('should not have any access', function () {
expect(this.permission.hasAccess(user2)).toBe(false);
expect(this.permission.hasReadAccess(user2)).toBe(false);
expect(this.permission.hasWriteAccess(user2)).toBe(false);
});
it('should be able to set any access', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(true);
expect(this.permission.canChangeWriteAccess(user2)).toBe(true);
});
});
describe('when given an owner', function () {
it('should have all access', function () {
expect(this.permission.hasAccess(this.owner)).toBe(true);
expect(this.permission.hasReadAccess(this.owner)).toBe(true);
expect(this.permission.hasWriteAccess(this.owner)).toBe(true);
});
it('should be able to set any access', function () {
expect(this.permission.canChangeReadAccess(this.owner)).toBe(true);
expect(this.permission.canChangeWriteAccess(this.owner)).toBe(true);
});
});
describe('when given an user', function () {
describe('when user is given read access', function () {
beforeEach(function () {
this.permission.grantReadAccess(user2);
});
it('should have read access', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(false);
});
it('should be able to set any access', function () {
expect(this.permission.canChangeReadAccess(this.owner)).toBe(true);
expect(this.permission.canChangeWriteAccess(this.owner)).toBe(true);
});
});
describe('when user is given write access', function () {
beforeEach(function () {
this.permission.grantWriteAccess(user2);
});
it('should have both read+write access', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(true);
});
it('should be able to set any access', function () {
expect(this.permission.canChangeReadAccess(this.owner)).toBe(true);
expect(this.permission.canChangeWriteAccess(this.owner)).toBe(true);
});
});
describe('when user is part of organization with read access', function () {
beforeEach(function () {
this.permission.grantReadAccess(this.organization);
});
it('should have read access', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(false);
});
it('should only be able to set write access', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(false);
expect(this.permission.canChangeWriteAccess(user2)).toBe(true);
});
describe('when organization has write access', function () {
beforeEach(function () {
this.permission.grantWriteAccess(this.organization);
});
it('should have both read+write access', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(true);
});
it('should not be able to change any access', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(false);
expect(this.permission.canChangeWriteAccess(user2)).toBe(false);
});
});
});
});
describe('when given a group', function () {
describe('when given group has own access', function () {
it('should have the expected access', function () {
expect(this.permission.hasAccess(this.groupX)).toBe(true);
expect(this.permission.hasReadAccess(this.groupX)).toBe(true);
expect(this.permission.hasWriteAccess(this.groupX)).toBe(false);
expect(this.permission.hasAccess(this.groupA)).toBe(true);
expect(this.permission.hasReadAccess(this.groupA)).toBe(true);
expect(this.permission.hasWriteAccess(this.groupA)).toBe(false);
expect(this.permission.hasAccess(this.groupB)).toBe(true);
expect(this.permission.hasReadAccess(this.groupB)).toBe(true);
expect(this.permission.hasWriteAccess(this.groupB)).toBe(true);
});
describe('when group is part of organization that has more privileged access', function () {
beforeEach(function () {
this.permission.grantWriteAccess(this.organization);
this.organization.groups.add(this.groupA);
});
it('should have organization access', function () {
expect(this.permission.hasAccess(this.groupA)).toBe(true);
expect(this.permission.hasReadAccess(this.groupA)).toBe(true);
expect(this.permission.hasWriteAccess(this.groupA)).toBe(true);
});
it('should not be able to change any access', function () {
expect(this.permission.canChangeReadAccess(this.groupA)).toBe(false);
expect(this.permission.canChangeWriteAccess(this.groupA)).toBe(false);
});
});
});
describe('when group w/o own access but is part of organization with read access', function () {
beforeEach(function () {
this.permission.grantReadAccess(this.organization);
this.organization.groups.add(this.groupX);
});
it('should have the organization read access', function () {
expect(this.permission.hasAccess(this.groupX)).toBe(true);
expect(this.permission.hasReadAccess(this.groupX)).toBe(true);
expect(this.permission.hasWriteAccess(this.groupX)).toBe(false);
});
it('should only be able to change write access', function () {
expect(this.permission.canChangeReadAccess(this.groupX)).toBe(false);
expect(this.permission.canChangeWriteAccess(this.groupX)).toBe(true);
});
describe('when organization has write access', function () {
beforeEach(function () {
this.permission.grantWriteAccess(this.organization);
this.organization.groups.add(this.groupX);
});
it('should have the organization read+write access', function () {
expect(this.permission.hasAccess(this.groupX)).toBe(true);
expect(this.permission.hasReadAccess(this.groupX)).toBe(true);
expect(this.permission.hasWriteAccess(this.groupX)).toBe(true);
});
it('should not be able to change any access', function () {
expect(this.permission.canChangeReadAccess(this.groupX)).toBe(false);
expect(this.permission.canChangeWriteAccess(this.groupX)).toBe(false);
});
});
});
});
describe('when given a user w/o own access but is member of group with read access', function () {
beforeEach(function () {
const userGroupsCollection = new UserGroupsCollection([], { configModel });
userGroupsCollection.add(this.groupA);
user1.setGroups(userGroupsCollection);
});
it('should have the group access', function () {
expect(this.permission.hasAccess(user1)).toBe(true);
expect(this.permission.hasReadAccess(user1)).toBe(true);
expect(this.permission.hasWriteAccess(user1)).toBe(false);
});
it('should only be able to change write access', function () {
expect(this.permission.canChangeReadAccess(user1)).toBe(false);
expect(this.permission.canChangeWriteAccess(user1)).toBe(true);
});
describe('when user is member of multiple groups', function () {
beforeEach(function () {
user1.groups.add(this.groupB);
});
it('should have the most privileged access of the grops', function () {
expect(this.permission.hasAccess(user1)).toBe(true);
expect(this.permission.hasReadAccess(user1)).toBe(true);
expect(this.permission.hasWriteAccess(user1)).toBe(true);
});
it('should not be able to change any access (since has write inherited)', function () {
expect(this.permission.canChangeReadAccess(user1)).toBe(false);
expect(this.permission.canChangeWriteAccess(user1)).toBe(false);
});
});
describe('when user is also part of organization with read+write access', function () {
beforeEach(function () {
this.permission.grantWriteAccess(this.organization);
user1.organization = this.organization;
});
it('should return the organization access since it has precedence over groups', function () {
expect(this.permission.hasAccess(user1)).toBe(true);
expect(this.permission.hasReadAccess(user1)).toBe(true);
expect(this.permission.hasWriteAccess(user1)).toBe(true);
});
it('should not be able to change any access (since has write inherited)', function () {
expect(this.permission.canChangeReadAccess(user1)).toBe(false);
expect(this.permission.canChangeWriteAccess(user1)).toBe(false);
});
});
describe('when user is part of organization with read+write access', function () {
beforeEach(function () {
// treat user1 as if retrieved through org users collection
user1.organization = null;
this.organization.users.add(user1);
this.permission.grantWriteAccess(this.organization);
});
it('should return the organization access since it has precedence over groups', function () {
expect(this.permission.hasAccess(user1)).toBe(true);
expect(this.permission.hasReadAccess(user1)).toBe(true);
expect(this.permission.hasWriteAccess(user1)).toBe(true);
});
it('should not be able to change any access (since has write inherited)', function () {
expect(this.permission.canChangeReadAccess(user1)).toBe(false);
expect(this.permission.canChangeWriteAccess(user1)).toBe(false);
});
});
describe('when user has organization read-only access', function () {
beforeEach(function () {
user1.organization = this.organization;
this.permission.grantReadAccess(this.organization);
});
it('should return the organization access', function () {
expect(this.permission.hasAccess(user1)).toBe(true);
expect(this.permission.hasReadAccess(user1)).toBe(true);
expect(this.permission.hasWriteAccess(user1)).toBe(false);
});
it('should be able to change write access only', function () {
expect(this.permission.canChangeReadAccess(user1)).toBe(false);
expect(this.permission.canChangeWriteAccess(user1)).toBe(true);
});
describe('when user has better access than organization', function () {
beforeEach(function () {
this.permission.grantWriteAccess(user1);
});
it('should have read+write access', function () {
expect(this.permission.hasAccess(user1)).toBe(true);
expect(this.permission.hasReadAccess(user1)).toBe(true);
expect(this.permission.hasWriteAccess(user1)).toBe(true);
});
it('should be able to change access', function () {
expect(this.permission.canChangeReadAccess(user1)).toBe(true);
expect(this.permission.canChangeWriteAccess(user1)).toBe(true);
});
});
describe('when user is also owner', function () {
beforeEach(function () {
this.permission.owner = user1;
});
it('should have read+write access', function () {
expect(this.permission.hasAccess(user1)).toBe(true);
expect(this.permission.hasReadAccess(user1)).toBe(true);
expect(this.permission.hasWriteAccess(user1)).toBe(true);
});
it('should be able to change access', function () {
expect(this.permission.canChangeReadAccess(user1)).toBe(true);
expect(this.permission.canChangeWriteAccess(user1)).toBe(true);
});
});
});
});
});
it('should parse owner and acl', function () {
model = new PermissionModel({
owner: {
username: 'rambo'
},
acl: [
{
type: 'user',
entity: {
id: 'u1',
username: 'u1'
},
access: 'r'
},
{
type: 'user',
entity: {
id: 'u2',
username: 'u2'
},
access: 'rw'
}
]
}, { configModel });
expect(model.owner.get('username')).toEqual('rambo');
expect(model.acl.length).toEqual(2);
expect(model.acl.at(0).get('entity').get('username')).toEqual('u1');
});
it('toJSON', function () {
model.grantReadAccess(user1);
model.grantWriteAccess(user2);
expect(model.toJSON()).toEqual({
entity: {
id: 'test_id',
type: 'vis'
},
acl: [
{ type: 'user', entity: { id: 'uuid', username: 'u1', avatar_url: 'http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png' }, access: 'r' },
{ type: 'user', entity: { id: 'uuid2', username: 'u2', avatar_url: 'http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png' }, access: 'rw' }
]
});
});
it('updates owner', function () {
model.set('owner', { username: 'changed' });
expect(model.owner.get('username')).toEqual('changed');
model.set('acl', [
{ type: 'user', entity: { id: 'uuid', username: 'u1', avatar_url: 'http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png' }, access: 'r' },
{ type: 'user', entity: { id: 'uuid2', username: 'u2', avatar_url: 'http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png' }, access: 'rw' }
]);
expect(model.acl.length).toEqual(2);
});
it("shouldn't trigger an acl reset change when acl is re-generated", function () {
var count = 0;
model.acl.bind('reset', function () {
++count;
});
model.set('acl', [
{ type: 'user', entity: { id: 'uuid', username: 'u1', avatar_url: 'http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png' }, access: 'r' },
{ type: 'user', entity: { id: 'uuid2', username: 'u2', avatar_url: 'http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png' }, access: 'rw' }
]);
expect(count).toEqual(0);
});
describe('.clone', function () {
beforeEach(function () {
model.set('id', 'abc-123', { silent: true });
this.permission = model.clone();
});
it('should return a new Permission object', function () {
expect(this.permission).not.toBe(model);
expect(this.permission instanceof PermissionModel).toBeTruthy();
});
it('should contain the same attributes as the original permission', function () {
expect(this.permission.owner).not.toBeUndefined();
expect(this.permission.acl).not.toBeUndefined();
});
it('should not have an id set', function () {
expect(this.permission.get('id')).toBeUndefined();
expect(this.permission.get('id')).not.toEqual(model.get('id'));
});
});
describe('.overwriteAcl', function () {
beforeEach(function () {
this.otherPermission = model.clone();
this.otherPermission.grantReadAccess(user1);
this.otherPermission.grantWriteAccess(user2);
model.overwriteAcl(this.otherPermission);
});
it('should set the ACL list from other permission object', function () {
expect(model.acl.models).toEqual(this.otherPermission.acl.models);
});
});
});
@@ -0,0 +1,178 @@
const Backbone = require('backbone');
const Poller = require('dashboard/data/background-polling/poller');
describe('Poller', function () {
var model, originalSync, originalFetch;
beforeEach(function () {
var Model = Backbone.Model.extend({
url: '/hello'
});
model = new Model();
originalSync = model.sync;
model.sync = function (a, b, opts) {
opts.success();
};
jasmine.clock().install();
originalFetch = model.fetch;
spyOn(model, 'fetch').and.callThrough();
});
afterEach(function () {
jasmine.clock().uninstall();
});
describe('.start', function () {
it('should start fetching periodically after the specified number of seconds', function () {
var poller = new Poller(model, { interval: 100 });
poller.start();
expect(model.fetch).not.toHaveBeenCalled();
jasmine.clock().tick(100);
expect(model.fetch.calls.count()).toEqual(1);
jasmine.clock().tick(100);
expect(model.fetch.calls.count()).toEqual(2);
});
it('should auto start fetching if autoStart option is true', function () {
var poller = new Poller(model, { // eslint-disable-line no-unused-vars
interval: 100,
autoStart: true
});
expect(model.fetch).not.toHaveBeenCalled();
jasmine.clock().tick(100);
expect(model.fetch.calls.count()).toEqual(1);
jasmine.clock().tick(100);
expect(model.fetch.calls.count()).toEqual(2);
});
it('should not trigger new requests if previous request haven\'t succeeded', function () {
model.sync = originalSync;
var poller = new Poller(model, { interval: 100 });
poller.start();
jasmine.clock().tick(100);
expect(model.fetch.calls.count()).toEqual(1);
// First request has been received
model.fetch.calls.mostRecent().args[0].success();
// 2000 more millisecs are gone
jasmine.clock().tick(2000);
// Only one more request has been triggered
expect(model.fetch.calls.count()).toEqual(2);
});
it('should not try to poll if already polling', function () {
var poller = new Poller(model, { interval: 100 });
poller.start();
poller.start();
expect(model.fetch).not.toHaveBeenCalled();
jasmine.clock().tick(100);
expect(model.fetch.calls.count()).toEqual(1);
});
it('should stop polling when the condition is satisfied', function () {
// This satisfies the condition
model.set({ 'something': 'wadus' });
var poller = new Poller(model, {
interval: 100,
stopWhen: function (model) {
return model.get('something') === 'wadus';
}
});
poller.start();
expect(model.fetch).not.toHaveBeenCalled();
jasmine.clock().tick(100);
// Fetch has been called
expect(model.fetch.calls.count()).toEqual(1);
jasmine.clock().tick(100);
// Fetch was only called the first time (condition was met afterwards)
expect(model.fetch.calls.count()).toEqual(1);
});
it('should use the given function to determine the interval', function () {
model.sync = function (a, b, opts) {
opts.success();
};
var poller = new Poller(model, { // eslint-disable-line no-unused-vars
interval: function (n) {
if (n === 0) {
return 100;
} else {
return 1;
}
},
autoStart: true
});
expect(model.fetch).not.toHaveBeenCalled();
jasmine.clock().tick(100);
expect(model.fetch.calls.count()).toEqual(1);
jasmine.clock().tick(1);
expect(model.fetch.calls.count()).toEqual(2);
});
it('should invoke the error callback if the request fails', function () {
model.fetch = originalFetch;
spyOn(model, 'fetch');
var errorCallback = jasmine.createSpy('error');
var poller = new Poller(model, {
interval: 100,
error: errorCallback
});
poller.start();
jasmine.clock().tick(100);
model.fetch.calls.mostRecent().args[0].error();
expect(errorCallback).toHaveBeenCalledWith(model);
});
});
describe('.stop', function () {
it('should stop polling', function () {
var poller = new Poller(model, { interval: 100 });
poller.start();
poller.stop();
expect(model.fetch).not.toHaveBeenCalled();
jasmine.clock().tick(1000);
expect(model.fetch).not.toHaveBeenCalled();
});
});
});
@@ -0,0 +1,22 @@
const TableDataCollection = require('dashboard/data/table/table-data-collection');
describe('dashboard/data/table/table-data-collection', function () {
let cols;
beforeEach(function () {
cols = new TableDataCollection();
cols.reset([
{'id': 1, 'col1': 1, 'col2': 2, 'col3': 3},
{'id': 2, 'col1': 4, 'col2': 5, 'col3': 6}
]);
});
it('should return the value for cell', function () {
expect(cols.getCell(0, 'col1')).toEqual(1);
});
it('should return null for non existing cell', function () {
expect(cols.getCell(10, 'col1')).toEqual(null);
});
});
@@ -0,0 +1,80 @@
const _ = require('underscore');
const TableSynchronizationModel = require('dashboard/data/table-synchronization-model.js');
const CartoTableMetadata = require('fixtures/dashboard/carto-table-metadata.fixture');
const configModel = require('fixtures/dashboard/config-model.fixture');
describe('dashboard/data/table-synchronization-model', function () {
let table, sync;
beforeEach(function () {
table = CartoTableMetadata({ name: 'test' }, configModel);
sync = new TableSynchronizationModel({ configModel });
});
describe('JSON stringification', function () {
it("should return guessing parameters, interval and URL when it doesn't come from a service", function () {
sync.set({
content_guessing: true,
fake_guessing: false
});
const d = sync.toJSON();
expect(_.size(d)).toBe(5);
expect(d.type_guessing).not.toBeUndefined();
expect(d.url).not.toBeUndefined();
expect(d.interval).not.toBeUndefined();
expect(d.fake_guessing).toBeUndefined();
});
it('should return the right params when it comes from a remote source', function () {
sync.set({
type: 'remote',
remote_visualization_id: 123,
value: 1234
});
const d = sync.toJSON();
expect(d.value).not.toBeUndefined();
expect(d.create_vis).toBeFalsy();
expect(d.remote_visualization_id).not.toBeUndefined();
});
it('should add service attributes when it comes from a service', function () {
sync.set({
service_name: 'follower'
});
const d = sync.toJSON();
expect(_.size(d)).toBe(7);
expect(d.service_name).not.toBeUndefined();
expect(d.service_item_id).not.toBeUndefined();
});
});
describe('linkToTable', function () {
it('should assign id from table it exists and not fetch', function () {
const s = { id: 'test', duration: 10 };
table.set('synchronization', s);
spyOn(sync, 'fetch');
sync.linkToTable(table);
expect(sync.get('id')).toEqual(s.id);
expect(sync.get('duration')).toEqual(s.duration);
expect(sync.fetch).not.toHaveBeenCalled();
table.set('synchronization', { id: 'test2' });
expect(sync.get('id')).toEqual('test2');
});
it('should remove sync', function () {
spyOn(sync, 'destroy');
sync.linkToTable(table);
table.destroy();
expect(sync.destroy).toHaveBeenCalled();
});
});
it('should unset id on destroy', function () {
sync.set('id', 'test');
expect(sync.isSync()).toEqual(true);
sync.destroy();
expect(sync.isNew()).toEqual(true);
expect(sync.isSync()).toEqual(false);
});
});
@@ -0,0 +1,26 @@
const ColumnModel = require('dashboard/data/table/column-model');
const CartoTableMetadata = require('dashboard/views/public-dataset/carto-table-metadata');
const configModel = require('fixtures/dashboard/config-model.fixture');
describe('dashboard/data/table/column-model', function () {
let column;
beforeEach(function () {
const table = new CartoTableMetadata({
id: 'testTable',
name: 'testTable'
}, { configModel });
column = new ColumnModel({
table,
name: 'columnName',
configModel
});
});
it('should have correct url', function () {
expect(column.url()).toEqual(
'/api/v1/tables/testTable/columns/columnName'
);
});
});
@@ -0,0 +1,147 @@
const Backbone = require('backbone');
const SQLViewDataModel = require('dashboard/data/table/sqlviewdata-model');
const configModel = require('fixtures/dashboard/config-model.fixture');
const wkbPoint = '0101000020110F00004E3CE77C32B324418662B3BD88715841';
const wkbPolygon = '0106000020E61000000100000001030000000100000016000000000000C0D4211740000000A07DC34840000000C0285C1740000000E0AEC6484000000000BF98174000000060D5D44840000000A03F48174000000020E4DF48400000000096FC164000000060CBE54840000000C06903174000000000B1EC484000000060A8EC16400000008073F2484000000060BB3B17400000006005FB48400000000065471740000000203E0149400000002085EB1740000000000B16494000000080826D1840000000207915494000000040FF861840000000601110494000000040138F18400000004092FF484000000080814E1940000000A07BEB484000000060C1161A4000000020D2E74840000000E0CE0A1A40000000A06ADA484000000060707D1940000000A08DCB484000000000EA721940000000A0E0BA4840000000603EA91840000000609AC0484000000000F1EC17400000008062B948400000000074DA17400000004081BE4840000000C0D4211740000000A07DC34840';
describe('dashboard/data/table/sqlviewdata-model', function () {
let sqlView;
beforeEach(function () {
sqlView = new SQLViewDataModel(null, { sql: 'select * from a', configModel });
});
it('default order should be empty after set sql', function () {
sqlView.setSQL('select * from table');
expect(sqlView.options.get('order_by')).toEqual('');
expect(sqlView.options.get('sort_order')).toEqual('asc');
expect(sqlView.options.get('filter_column')).toEqual('');
expect(sqlView.options.get('filter_value')).toEqual('');
expect(sqlView.options.get('page')).toEqual(0);
});
it('should guess geometry type', function () {
sqlView.reset({ the_geom: wkbPoint });
expect(sqlView.getGeometryTypes()).toEqual(['ST_Point']);
sqlView.reset({ the_geom: wkbPolygon });
expect(sqlView.getGeometryTypes()).toEqual(['ST_Multipolygon']);
sqlView.reset({ the_geom_webmercator: wkbPoint });
expect(sqlView.getGeometryTypes()).toEqual(['ST_Point']);
sqlView.reset({ the_geom_webmercator: wkbPolygon });
expect(sqlView.getGeometryTypes()).toEqual(['ST_Multipolygon']);
// precedence
sqlView.reset({ the_geom: wkbPolygon, the_geom_webmercator: wkbPoint });
expect(sqlView.getGeometryTypes()).toEqual(['ST_Multipolygon']);
sqlView.reset([
{ the_geom: null },
{ the_geom: null },
null,
{ the_geom: wkbPoint },
{ the_geom: null }
]);
expect(sqlView.getGeometryTypes()).toEqual(['ST_Point']);
});
it('should return true if has georeferenced data', function () {
sqlView.reset([
{the_geom: '{"type":"Point","coordinates":[-5.84198,43.648001]}'}
]);
expect(sqlView.isGeoreferenced()).toEqual(true);
});
it("should return false if hasn't any georeferenced data", function () {
sqlView.reset([
{r: 1}
]);
expect(sqlView.isGeoreferenced()).toEqual(false);
});
it('should raise change:sql always', function () {
var c = 0;
sqlView.options.bind('change:sql', function () {
++c;
});
sqlView.setSQL('select * from test');
sqlView.setSQL('select * from test');
sqlView.setSQL('select * from test2');
expect(c).toEqual(3);
});
it('should not set null sql', function () {
sqlView.setSQL(null);
expect(sqlView.options.get('sql')).toEqual('');
});
it('should replace variables like {x},{y},{z} by a 0', function () {
sqlView.setSQL('select {x}, {y}, {z} as aaa from table');
expect(sqlView.options.get('sql')).toEqual('select 0, 0, 0 as aaa from table');
sqlView.setSQL('select \\{x} as aaa from table');
expect(sqlView.options.get('sql')).toEqual('select {x} as aaa from table');
});
it('should be read only when a filter has been applied', function () {
sqlView.setSQL('select {x}, {y}, {z} as aaa from table');
expect(sqlView.isReadOnly()).toBeTruthy();
sqlView.options.set('sql_source', 'filters');
expect(sqlView.isReadOnly()).toBeFalsy();
});
it('should reset rows when fails', function () {
sqlView.reset([{a: 1, b: 2}]);
expect(sqlView.size()).toEqual(1);
const spy = jasmine.createSpy();
sqlView.bind('reset', spy);
sqlView.trigger('error');
expect(spy).toHaveBeenCalled();
expect(sqlView.size()).toEqual(0);
});
it('fech should use POST when the query is longer than 1024 bytes', function () {
spyOn(sqlView, '_sqlQuery');
var sql = 'select * from table limit ';
while (sql.length < 1024 + 1) {
sql += '0';
}
sqlView.setSQL(sql);
sqlView.fetch();
expect(sqlView._sqlQuery.calls.argsFor(0)[3]).toEqual('POST');
});
it('write queries should not fetch metadata', function () {
sqlView.url = 'test';
spyOn(sqlView, '_sqlQuery');
sqlView.setSQL('update table set a = 1');
sqlView.fetch();
expect(sqlView._sqlQuery).not.toHaveBeenCalled();
sqlView.setSQL('select * from table');
sqlView.fetch();
expect(sqlView._sqlQuery).toHaveBeenCalled();
});
it('write queries should use post', function () {
spyOn(Backbone, 'sync');
sqlView.setSQL('update table set a = 1');
sqlView.sync();
expect(Backbone.sync.calls.argsFor(0)[2].type).toEqual('POST');
});
it("should explicitly add 'as' to aliases", function () {
spyOn(sqlView, '_sqlQuery');
sqlView.setSQL('SELECT the_geom as location FROM testTable');
sqlView.fetch();
// expect(sqlView._sqlQuery).toHaveBeenCalled();
// expect(sqlView._sqlQuery).toHaveBeenCalledWith("select * from (SELECT the_geom as location FROM testTable) __wrapped limit 0", Function, Function, 'GET');
expect(sqlView._sqlQuery.calls.mostRecent().args[0]).toEqual('select * from (SELECT the_geom as location FROM testTable) __wrapped limit 0', Function, Function, 'GET');
// expect(sqlView._sqlQuery).toEqual;
});
});
@@ -0,0 +1,51 @@
var UrlModel = require('dashboard/data/url-model');
describe('dashboard/data/url-model', function () {
beforeEach(function () {
this.baseUrl = 'http://subdomain.carto.com/foobar';
this.url = new UrlModel({ base_url: this.baseUrl });
});
describe('.pathname', function () {
it('should return the full path of this URL', function () {
expect(this.url.pathname()).toEqual('/foobar');
});
});
describe('.urlToPath', function () {
describe('when given no arguments', function () {
beforeEach(function () {
this.newUrl = this.url.urlToPath();
});
it('should return a new url object', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl instanceof UrlModel).toBeTruthy();
});
it('should return a new url object that have the same base url', function () {
expect(this.newUrl.get('base_url')).toContain(this.baseUrl);
});
});
describe('when given any argument', function () {
beforeEach(function () {
this.newUrl = this.url.urlToPath('sub', 'path');
});
it('should return a new url object that have same base url', function () {
expect(this.newUrl.get('base_url')).toContain(this.baseUrl);
});
it('should return a new url object that have the given params as path', function () {
expect(this.newUrl.get('base_url')).toContain(this.baseUrl + '/sub/path');
});
});
});
describe('.toString', function () {
it('should return the string representation of the URL', function () {
expect(this.url.toString()).toEqual(this.baseUrl);
});
});
});
@@ -0,0 +1,36 @@
const UserGroupsCollection = require('dashboard/data/user-groups-collection.js');
const org = require('fixtures/dashboard/organization-model.fixture');
const configModel = require('fixtures/dashboard/config-model.fixture');
describe('dashboard/data/user-groups-collection', function () {
beforeEach(function () {
this.org = org;
this.userGroupsCollection = new UserGroupsCollection(undefined, {
organization: this.org,
configModel
});
});
it('should have an organization', function () {
expect(this.userGroupsCollection.organization).toBe(this.org);
});
it('should add groups correctly even without a configModel', function () {
const newCollection = new UserGroupsCollection({
id: 'g1'
}, {
organization: this.org,
configModel
});
newCollection.add({
id: 'g2'
});
expect(newCollection.first()._configModel).not.toBeUndefined();
expect(newCollection.first().get('id')).toBe('g1');
expect(newCollection.last().get('id')).toBe('g2');
expect(newCollection.last()._configModel).not.toBeUndefined();
});
});
@@ -0,0 +1,256 @@
const _ = require('underscore');
const Backbone = require('backbone');
const UserModel = require('dashboard/data/user-model');
const UserGroupsCollection = require('dashboard/data/user-groups-collection');
const OrganizationModel = require('dashboard/data/organization-model');
const getObjectValue = require('deep-insights/util/get-object-value');
var USER_MODEL_OPTS = {
configModel: require('fixtures/dashboard/config-model.fixture')
};
describe('dashboard/data/user-model', function () {
var user;
var createModelFn = function (options) {
const userPayload = _.extend({
base_url: 'http://team.carto.com/u/pepe',
id: 'uuid',
organization: {
id: 'o1',
admins: []
},
groups: [{
id: 'g1',
display_name: 'my group'
}]
}, options);
user = new UserModel(userPayload, USER_MODEL_OPTS);
const organization = new OrganizationModel(
userPayload.organization,
{
currentUserId: userPayload.id,
configModel: USER_MODEL_OPTS.configModel
}
);
organization.owner = new UserModel(getObjectValue(userPayload, 'organization.owner'));
user.setOrganization(organization);
const groups = new UserGroupsCollection(userPayload.groups, {
organization: _.result(user.collection, 'organization') || user.organization,
configModel: USER_MODEL_OPTS.configModel
});
user.setGroups(groups);
return user;
};
beforeEach(function () {
createModelFn();
});
it('should create an organization', function () {
expect(user.organization.id).toEqual('o1');
});
it('should create a user groups collection', function () {
expect(user.groups).toBeDefined();
expect(user.groups.length).toEqual(1);
expect(user.groups.organization).toBeDefined();
});
it('shouldn\'t set avatar_url is it comes with null value', function () {
var user1 = new UserModel({ avatar_url: null }, USER_MODEL_OPTS);
expect(user1.get('avatar_url')).toBe('http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png');
});
it('isAuthUsernamePasswordEnabled', function () {
user.organization.set('auth_username_password_enabled', true);
expect(user.isAuthUsernamePasswordEnabled()).toEqual(true);
user.organization.set('auth_username_password_enabled', false);
expect(user.isAuthUsernamePasswordEnabled()).toEqual(false);
});
it('isInsideOrg', function () {
user.organization.users.reset([]);
user.organization.id = false;
expect(user.isInsideOrg()).toEqual(false);
user.organization.users.add(new UserModel(undefined, USER_MODEL_OPTS));
expect(user.isInsideOrg()).toEqual(false);
user.organization.id = 'hello-org-id';
expect(user.isInsideOrg()).toEqual(true);
});
it('isOrgOwner', function () {
user.organization.owner = user;
expect(user.isOrgOwner()).toEqual(true);
user.organization.owner = new UserModel({
id: 'test',
organization: {}
}, USER_MODEL_OPTS);
expect(user.isOrgOwner()).toEqual(false);
});
it('isOrgAdmin', function () {
user.organization.set('admins', [{id: user.id}]);
expect(user.isOrgAdmin()).toEqual(true);
user.organization.set('admins', [{id: 'not_me'}]);
expect(user.isOrgAdmin()).toEqual(false);
user.organization = null;
expect(user.isOrgAdmin()).toEqual(false);
});
it('should answer if user can create new datasets', function () {
user.set('remaining_byte_quota', 0);
expect(user.canCreateDatasets()).toEqual(false);
user.set('remaining_byte_quota', 10);
expect(user.canCreateDatasets()).toEqual(true);
user.set('remaining_byte_quota', undefined);
user.unset('remaining_byte_quota');
expect(user.canCreateDatasets()).toEqual(false);
});
it('hasFeatureFlagEnabled', function () {
var flagOK = 'test_flag';
var feature_flags = [];
feature_flags.push(flagOK);
user.set('feature_flags', feature_flags);
expect(user.featureEnabled(flagOK)).toEqual(true);
expect(user.featureEnabled('flagWrong')).toEqual(false);
});
describe('.equals', function () {
describe('given same user', function () {
it('should return true', function () {
expect(user.equals(user)).toBeTruthy();
});
});
describe('given not same user', function () {
it('should return false', function () {
expect(user.equals(new UserModel(undefined, USER_MODEL_OPTS))).toBeFalsy();
expect(user.equals(new Backbone.Model())).toBeFalsy();
});
});
});
describe('.viewUrl', function () {
it('should return a user URL', function () {
expect(user.viewUrl()).toEqual(jasmine.any(Object));
});
it('should have been set with base url', function () {
expect(user.viewUrl().get('base_url')).toEqual('http://team.carto.com/u/pepe');
});
it('should have been created with if user is org admin or not', function () {
expect(user.viewUrl().get('is_org_admin')).toBeFalsy();
spyOn(user, 'isOrgAdmin').and.returnValue(true);
expect(user.viewUrl().get('is_org_admin')).toBeTruthy();
});
});
describe('.upgradeContactEmail', function () {
describe('when is a normal user', function () {
beforeEach(function () {
spyOn(user, 'isInsideOrg').and.returnValue(false);
});
it('should return the general support email', function () {
expect(user.upgradeContactEmail()).toEqual('support@carto.com');
});
});
describe('when us a organization user', function () {
beforeEach(function () {
spyOn(user, 'isInsideOrg').and.returnValue(true);
});
describe('when user is also admin of organization', function () {
beforeEach(function () {
spyOn(user, 'isOrgOwner').and.returnValue(true);
});
it('should return enterprise support email', function () {
expect(user.upgradeContactEmail()).toEqual('enterprise-support@carto.com');
});
});
describe('when user is a normal organization member', function () {
beforeEach(function () {
user.organization.owner = new UserModel({
email: 'owner@org.com'
}, USER_MODEL_OPTS);
});
it('should return the organiation owner email', function () {
expect(user.upgradeContactEmail()).toEqual('owner@org.com');
});
});
});
});
describe('.nameOrUsername', function () {
it('should return the name or username as fallback if name is not available', function () {
user.set('username', 'kalle');
expect(user.nameOrUsername()).toEqual('kalle');
user.set('name', 'Kalle');
expect(user.nameOrUsername()).toEqual('Kalle');
user.set('last_name', 'Anka');
expect(user.nameOrUsername()).toEqual('Kalle Anka');
user.set('name', '');
expect(user.nameOrUsername()).toEqual('Anka');
});
});
describe('.needsPasswordConfirmation', function () {
it('should return false if user has needs_password_confirmation property set to false', function () {
createModelFn({
needs_password_confirmation: false
});
expect(user.needsPasswordConfirmation()).toBe(false);
});
it('should return true if user has needs_password_confirmation property set to true', function () {
createModelFn({
needs_password_confirmation: true
});
expect(user.needsPasswordConfirmation()).toBe(true);
});
});
});
@@ -0,0 +1,143 @@
var Backbone = require('backbone');
var UserTablesModel = require('dashboard/data/user-tables-model');
describe('dashboard/data/user-tables-model', function () {
var model;
beforeEach(function () {
spyOn(UserTablesModel.prototype, 'fetch');
var userModel = new Backbone.Model({ base_url: 'http://wadus.com' });
model = new UserTablesModel(null, { userModel: userModel });
});
it('throws an error when base_url is missing', function () {
model = function () {
return new UserTablesModel(null, {});
};
expect(model).toThrowError('userModel is required');
});
describe('._initBinds', function () {
it('sets stateModel to fetched on sync', function () {
expect(model.stateModel.get('status')).toEqual('fetching');
model.trigger('sync');
expect(model.stateModel.get('status')).toEqual('fetched');
});
it('sets stateModel to errored on error', function () {
expect(model.stateModel.get('status')).toEqual('fetching');
model.trigger('error');
expect(model.stateModel.get('status')).toEqual('errored');
});
describe('when paramsModel change:q', function () {
it('sets stateModel to fetching', function () {
model.paramsModel.set({ q: 'wubalubadubdub' });
expect(model.stateModel.get('status')).toEqual('fetching');
});
it('calls .fetch', function () {
model.paramsModel.set({ q: 'wubalubadubdub' });
expect(model.fetch).toHaveBeenCalled();
});
});
});
describe('.url', function () {
it('sets the default params in the url', function () {
var defaultUrl = 'http://wadus.com/api/v1/viz?tag_name=&q=&page=1&type=&exclude_shared=true&tags=&shared=no&only_liked=false&order=updated_at&types=table&deepInsights=false';
expect(model.url()).toEqual(defaultUrl);
});
});
describe('.generateParams', function () {
it('returns the paramsModel attributes encoded', function () {
var encodedParams = 'tag_name=&q=&page=1&type=&exclude_shared=true&tags=&shared=no&only_liked=false&order=updated_at&types=table&deepInsights=false';
expect(model.generateParams()).toEqual(encodedParams);
});
});
describe('.parse', function () {
it('transforms a tables array into an object', function () {
var response = {
visualizations: [
{ name: 'rick' },
{ name: 'morty' },
{ name: 'jerry' }
]
};
var values = model.parse(response);
expect(values.rick).toBeDefined();
});
it('adds the default permissions to the table', function () {
var response = {
visualizations: [
{ name: 'jerry' }
]
};
var values = model.parse(response);
expect(values.jerry.permissions).toEqual({
select: false,
update: false,
insert: false,
delete: false
});
});
});
describe('.setQuery', function () {
it('sets the query in paramsModel', function () {
expect(model.paramsModel.get('q')).toEqual('');
model.setQuery('morty');
expect(model.paramsModel.get('q')).toEqual('morty');
});
});
describe('.getStateModel', function () {
it('returns the stateModel', function () {
expect(model.getStateModel()).toEqual(model.stateModel);
});
});
describe('.isFetched', function () {
it('returns true if stateModel:status is fetched', function () {
model.stateModel.set({ status: 'fetched' });
expect(model.isFetched()).toBe(true);
});
});
describe('.hasQuery', function () {
it('returns true if paramsModel has q', function () {
expect(model.hasQuery()).toBe(false);
model.paramsModel.set({ q: 'morty' });
expect(model.hasQuery()).toBe(true);
});
});
describe('.isEmpty', function () {
it('returns true if model has no attributes', function () {
expect(model.isEmpty()).toBe(true);
model.set({ rick: 'morty' });
expect(model.isEmpty()).toBe(false);
});
});
});
@@ -0,0 +1,114 @@
var UserUrlModel = require('dashboard/data/user-url-model');
describe('dashboard/data/user-url-model', function () {
beforeEach(function () {
this.is_org_admin = false;
this.createUrl = function () {
this.url = new UserUrlModel({
base_url: 'http://team.carto.com/u/pepe',
is_org_admin: this.is_org_admin
});
};
});
describe('.organization', function () {
describe('when user is organization admin', function () {
beforeEach(function () {
this.is_org_admin = true;
this.createUrl();
this.newUrl = this.url.organization();
});
it('should return a new url point to the organization settings page', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url').toString()).toEqual('http://team.carto.com/u/pepe/organization');
});
});
describe('when user is a normal organization member', function () {
beforeEach(function () {
this.is_org_admin = false;
this.createUrl();
this.newUrl = this.url.organization();
});
it('should return a new url point to the organization settings page', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/account');
});
});
});
describe('.accountProfile', function () {
beforeEach(function () {
this.createUrl();
this.newUrl = this.url.accountProfile();
});
it('should return a new url point to the user\'s account profile page', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/profile');
});
});
describe('.accountSettings', function () {
beforeEach(function () {
this.createUrl();
this.newUrl = this.url.accountSettings();
});
it('should return a new url point to the user\'s account profile page', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toEqual('http://team.carto.com/u/pepe/account');
});
});
describe('.publicProfile', function () {
beforeEach(function () {
this.createUrl();
this.newUrl = this.url.publicProfile();
});
it('should return a new URL pointing to the user\'s public profile home', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toMatch('http://team.carto.com/u/pepe/me');
});
});
describe('.apiKeys', function () {
beforeEach(function () {
this.createUrl();
this.newUrl = this.url.apiKeys();
});
it('should return a new URL pointing to where the user can find his/her API keys', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toMatch('http://team.carto.com/u/pepe/your_apps');
});
});
describe('.logout', function () {
beforeEach(function () {
this.createUrl();
this.newUrl = this.url.logout();
});
it('should return a new URL pointing to the logout endpoint', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toMatch('http://team.carto.com/u/pepe/logout');
});
});
describe('.dashboard', function () {
beforeEach(function () {
this.createUrl();
this.newUrl = this.url.dashboard();
});
it('should return a new URL pointing to the dashboard of the user', function () {
expect(this.newUrl).not.toBe(this.url);
expect(this.newUrl.get('base_url')).toMatch('http://team.carto.com/u/pepe/dashboard');
});
});
});
@@ -0,0 +1,259 @@
const CartoTableMetadata = require('dashboard/views/public-dataset/carto-table-metadata');
const VisualizationsCollection = require('dashboard/data/visualizations-collection');
const VisualizationModel = require('dashboard/data/visualization-model');
const UserModel = require('dashboard/data/user-model');
const ConfigModelFixture = require('fixtures/dashboard/config-model.fixture');
// ==================================================
//
//
//
// THOSE TEST ARE **REALLY** IMPORTANT SO IF SOME
// OF THEM IS BROKEN, PLEASE, TAKE CARE OF IT
//
// Visualization model specs
//
// ==================================================
describe('Visualization model', function () {
var vis, map_id;
beforeEach(function () {
// cdb.admin.CartoDBLayer.updateCartoCss = function () {};
map_id = 96;
// Visualization model
vis = new VisualizationModel({
map_id: map_id,
active_layer_id: null,
name: 'test_table',
description: 'Visualization description',
tags: ['jamon', 'probando', 'test'],
privacy: 'PUBLIC',
updated_at: '2013-03-04T18:09:34+01:00',
type: 'table',
permission: {
owner: {
username: 'rambo',
base_url: 'http://team.carto.com/u/rambo'
},
acl: [
{
type: 'user',
access: 'r',
entity: {
username: 'charly',
base_url: 'http://team.carto.com/u/charly'
}
}
]
},
transition_options: {
action: 'click',
time: 40
},
table: {
name: '"rambo".test_table'
}
}, { configModel: ConfigModelFixture });
});
describe('transition_options', function () {
it('should load transition_options', function () {
expect(vis.transition.get('action')).toEqual('click');
});
it('should serailize transition_options', function () {
vis.transition.set('time', 50);
expect(vis.toJSON().transition_options).toEqual({ action: 'click', time: 50 });
});
});
describe('permission', function () {
it('should load permissions', function () {
expect(vis.permission.owner.get('username')).toEqual('rambo');
expect(vis.permission.acl.length).toEqual(1);
});
});
/* defaults & config */
describe('> defaults & config', function () {
it('should define a number of ITEMS_PER_PAGE', function () {
expect(VisualizationsCollection.prototype._ITEMS_PER_PAGE).toBeDefined();
});
it('should define a number of PREVIEW_ITEMS_PER_PAGE', function () {
expect(VisualizationsCollection.prototype._PREVIEW_ITEMS_PER_PAGE).toBeDefined();
});
it('should setup map bindings by default', function () {
expect(vis.get('bindMap')).toBeTruthy();
expect(vis.map.get('id')).toEqual(map_id);
});
it("shouldn't setup map bindings when bindMap is false", function () {
vis = new VisualizationModel({
map_id: map_id,
bindMap: false
}, { configModel: ConfigModelFixture });
expect(vis.get('bindMap')).toBeFalsy();
expect(vis.map.get('id')).toEqual(undefined);
});
});
/* map_id specs */
describe('> map_id', function () {
it('should set bindMap to false for all visualizations on parse/fetch', function () {
var visualizations = new VisualizationsCollection({ type: 'derived' }, { configModel: ConfigModelFixture });
var v = visualizations.parse({total_entries: 1, visualizations: [{ id: 1 }]});
expect(v[0].bindMap).toBeFalsy();
});
});
describe('.sharedWithEntities', function () {
beforeEach(function () {
this.sharedWithEntities = function () {
return vis.sharedWithEntities();
};
});
describe('given at least shared with one other user', function () {
beforeEach(function () {
// the permission object is set in the top-most beforeEach
this.sharedWithEntities = this.sharedWithEntities();
});
it('should return an array containing the entities the visualization is shared with', function () {
expect(this.sharedWithEntities.length).toEqual(1);
expect(this.sharedWithEntities[0].get('username')).toEqual('charly');
});
});
describe('given that model is not shared with anyone', function () {
beforeEach(function () {
vis.permission.acl.reset([], { silent: true });
});
it('should return an empty array', function () {
expect(this.sharedWithEntities()).toEqual([]);
});
});
});
describe('.tableMetadata', function () {
beforeEach(function () {
vis.set({ table: { foo: 'bar' } }, { silent: true });
this.tableMetadata = vis.tableMetadata();
});
it('should return a Table metadata object', function () {
expect(this.tableMetadata instanceof CartoTableMetadata).toBeTruthy();
});
it('should return a single instance if called several times', function () {
expect(this.tableMetadata).toBe(vis.tableMetadata());
});
it('should created the tableMetadata object with the table attributes', function () {
expect(this.tableMetadata.get('foo')).toEqual('bar');
});
});
describe('.privacyOptions', function () {
describe('given a visualization', function () {
beforeEach(function () {
this.vis = new VisualizationModel({
type: 'derived'
}, { configModel: ConfigModelFixture });
this.privacyOptions = this.vis.privacyOptions();
});
it('should return all privacy options', function () {
expect(this.privacyOptions.length).toEqual(4);
expect(this.privacyOptions).toContain('PUBLIC');
expect(this.privacyOptions).toContain('PRIVATE');
expect(this.privacyOptions).toContain('LINK');
expect(this.privacyOptions).toContain('PASSWORD');
});
});
describe('given a table', function () {
beforeEach(function () {
this.vis = new VisualizationModel({
type: 'table'
}, { configModel: ConfigModelFixture });
this.privacyOptions = this.vis.privacyOptions();
});
it('should return all privacy options but the password', function () {
expect(this.privacyOptions.length).toEqual(3);
expect(this.privacyOptions).toContain('PUBLIC');
expect(this.privacyOptions).toContain('PRIVATE');
expect(this.privacyOptions).toContain('LINK');
expect(this.privacyOptions).not.toContain('PASSWORD');
});
});
});
describe('.viewUrl', function () {
describe('when vis is a dataset', function () {
beforeEach(function () {
vis.set('type', 'table');
});
it('should return a new dataset URL', function () {
expect(vis.viewUrl().toString()).toEqual('http://team.carto.com/u/rambo/tables/rambo.test_table');
});
describe('when given a current user', function () {
it('should return the URL from perspective of owner', function () {
expect(vis.viewUrl(vis.permission.owner).toString()).toEqual('http://team.carto.com/u/rambo/tables/rambo.test_table');
});
it('should return the URL from the perspective of shared user', function () {
var userSharingVis = vis.permission.acl.first().get('entity');
expect(vis.viewUrl(userSharingVis).toString()).toEqual('http://team.carto.com/u/charly/tables/rambo.test_table');
});
it('should return the URL from the perspective of owner if do not have read access', function () {
var otherUser = new UserModel({
id: 123,
base_url: 'http://team.carto.com/u/current-user',
username: 'current-user'
});
expect(vis.viewUrl(otherUser).toString()).toEqual('http://team.carto.com/u/rambo/tables/rambo.test_table');
});
});
});
describe('when vis is a map', function () {
beforeEach(function () {
vis.set('type', 'derived');
vis.set('id', 'abc-123');
});
it('should return a new map URL', function () {
expect(vis.viewUrl().toString()).toEqual('http://team.carto.com/u/rambo/viz/abc-123');
});
describe('when the type is not available', function () {
it("should assume it's a map and thus return a new map URL", function () {
vis.set('type', undefined);
expect(vis.viewUrl().toString()).toEqual('http://team.carto.com/u/rambo/viz/abc-123');
});
});
describe('when given a other current user (e.g. a shared map)', function () {
beforeEach(function () {
this.userSharingVis = vis.permission.acl.first().get('entity');
this.userSharingVis.set('id', '123');
});
it('should return the URL from the perspective of the current user', function () {
expect(vis.viewUrl(this.userSharingVis).toString()).toEqual('http://team.carto.com/u/charly/viz/rambo.abc-123');
});
});
});
});
});
@@ -0,0 +1,31 @@
const VisualizationOrderModel = require('dashboard/data/visualization-order-model');
describe('dashboard/data/visualization-order-model', function () {
let model, visualization, url;
beforeEach(function () {
url = 'wadus.com';
visualization = {
id: 1337,
url: () => url
};
model = new VisualizationOrderModel({
visualization
});
});
describe('.initialize', function () {
it('sets this.visualization and unsets it from the attributes', function () {
expect(model.get('visualization')).toBeUndefined();
expect(model.visualization).toEqual(visualization);
});
});
describe('.url', function () {
it('returns the url properly', function () {
expect(model.url()).toEqual(`${url}/next_id`);
});
});
});
@@ -0,0 +1,187 @@
const Backbone = require('backbone');
const VisualizationsCollection = require('dashboard/data/visualizations-collection');
const configModel = require('fixtures/dashboard/config-model.fixture');
describe('dashboard/data/visualizations-collection', function () {
let collection, visualization, url, checkPageSpy;
beforeEach(function () {
url = 'wadus.com';
visualization = {
id: 1337,
url: () => url
};
checkPageSpy = spyOn(VisualizationsCollection.prototype, '_checkPage');
collection = new VisualizationsCollection({
visualization
}, { configModel });
});
describe('._PREVIEW_TABLES_PER_PAGE', function () {
it('returns the correct value', function () {
expect(collection._PREVIEW_TABLES_PER_PAGE).toEqual(10);
});
});
describe('._TABLES_PER_PAGE', function () {
it('returns the correct value', function () {
expect(collection._TABLES_PER_PAGE).toEqual(20);
});
});
describe('._PREVIEW_ITEMS_PER_PAGE', function () {
it('returns the correct value', function () {
expect(collection._PREVIEW_ITEMS_PER_PAGE).toEqual(3);
});
});
describe('._ITEMS_PER_PAGE', function () {
it('returns the correct value', function () {
expect(collection._ITEMS_PER_PAGE).toEqual(9);
});
});
describe('.initialize', function () {
it('calls _checkPage on reset', function () {
collection.reset();
expect(collection._checkPage).toHaveBeenCalled();
});
});
describe('.model', function () {
it('adds configModel when creating a model', function () {
collection.add({
rick: 'morty'
});
expect(collection.at(0)._configModel).toEqual(configModel);
});
});
describe('.getTotalPages', function () {
it('returns the total pages rounded based on total_entries and options.per_page', function () {
collection.total_entries = 10;
collection.options.set('per_page', 3);
expect(collection.getTotalPages()).toBe(4);
});
});
describe('._checkPage', function () {
it('increases the page in options if is bigger than the total', function () {
checkPageSpy.and.callThrough();
spyOn(collection, 'getTotalPages').and.returnValue(10);
collection.options.set({ page: 9 }, { silent: true });
collection._checkPage();
expect(collection.options.get('page')).toEqual(9);
collection.options.set({ page: 15 }, { silent: true });
collection._checkPage();
expect(collection.options.get('page')).toEqual(11);
});
});
describe('._createUrlOptions', function () {
it('returns options.attributes as url params', function () {
const urlParams = 'tag_name=&q=&page=1&type=derived&exclude_shared=false&per_page=9';
expect(collection._createUrlOptions()).toEqual(urlParams);
});
});
describe('.url', function () {
it('returns the correct url', function () {
const excpectedUrl = '/api/v1/viz?tag_name=&q=&page=1&type=derived&exclude_shared=false&per_page=9';
expect(collection.url()).toEqual(excpectedUrl);
});
});
describe('.remove', function () {
beforeEach(function () {
collection.add({ rick: 'morty' });
});
it('decreases the total_entries by one', function () {
collection.total_entries = 5;
collection.remove(collection.at(0));
expect(collection.total_entries).toEqual(4);
});
it('calls parent remove', function () {
spyOn(Backbone.Collection.prototype, 'remove');
collection.remove(collection.at(0));
expect(Backbone.Collection.prototype.remove).toHaveBeenCalled();
});
});
describe('.parse', function () {
let parsedData;
beforeEach(function () {
const response = {
total_entries: 1,
total_shared: 3,
total_likes: 3,
total_user_entries: 7,
visualizations: [
{ id: 1337, name: 'Mr Meeseeks', bindMap: true }
]
};
parsedData = collection.parse(response);
});
it('sets response.total_entries to collection', function () {
expect(collection.total_entries).toBe(1);
});
it('sets response.total_shared to collection', function () {
expect(collection.total_shared).toBe(3);
});
it('sets response.total_likes to collection', function () {
expect(collection.total_likes).toBe(3);
});
it('sets response.total_user_entries to collection', function () {
expect(collection.total_user_entries).toBe(7);
});
it('changes bindMap to false', function () {
expect(parsedData[0].bindMap).toBe(false);
});
});
describe('.create', function () {
it('calls prototype create', function () {
spyOn(Backbone.Collection.prototype, 'create');
collection.create({ mr: 'meeseeks' });
expect(Backbone.Collection.prototype.create).toHaveBeenCalled();
});
});
describe('.fetch', function () {
it('calls prototype fetch', function () {
spyOn(Backbone.Collection.prototype, 'fetch');
collection.fetch();
expect(Backbone.Collection.prototype.fetch).toHaveBeenCalled();
});
});
});
@@ -0,0 +1,37 @@
var Backbone = require('backbone');
/**
* Model that encapsulates params for fetching data in a cdb.admin.Visualizations collection.
*/
module.exports = Backbone.Model.extend({
defaults: {
content_type: '',
page: 1,
q: '',
tag: '',
category: '',
shared: 'no',
locked: false,
liked: false,
library: false,
order: 'updated_at',
deepInsights: false
},
isSearching: function () {
return this.get('q') || this.get('tag');
},
isDatasets: function () {
return this.get('content_type') === 'datasets';
},
isMaps: function () {
return this.get('content_type') === 'maps';
},
isDeepInsights: function () {
return this.isMaps() && this.get('deepInsights');
}
});
@@ -0,0 +1,50 @@
var VisFetchModel = require('builder/data/visualizations-fetch-model');
describe('common/visualizations-fetch-model', function () {
beforeEach(function () {
this.model = new VisFetchModel({
shared: false,
page: 1
});
});
describe('.isSearching', function () {
it('should return true if set to a search or tag query', function () {
this.model.set({
q: '',
tag: ''
});
expect(this.model.isSearching()).toBeFalsy();
this.model.set({
q: 'foobar',
tag: ''
});
expect(this.model.isSearching()).toBeTruthy();
this.model.set({
q: '',
tag: 'some-tag'
});
expect(this.model.isSearching()).toBeTruthy();
});
});
describe('.isDeepInsights', function () {
it('should return true if content_type is maps and deep-insights are enabled', function () {
this.model.set({
content_type: 'datasets',
deepInsights: true
});
expect(this.model.isDeepInsights()).toBeFalsy();
this.model.set({
content_type: 'maps'
});
expect(this.model.isDeepInsights()).toBeTruthy();
this.model.set({
deepInsights: false
});
expect(this.model.isDeepInsights()).toBeFalsy();
});
});
});