@reactual/handsontable
Version:
Spreadsheet-like data grid editor
634 lines (523 loc) • 15.2 kB
JavaScript
describe('Core_loadData', () => {
var id = 'testContainer';
beforeEach(function() {
this.$container = $(`<div id="${id}"></div>`).appendTo('body');
});
afterEach(function() {
if (this.$container) {
destroy();
this.$container.remove();
}
});
var arrayOfArrays = function() {
return [
['', 'Kia', 'Nissan', 'Toyota', 'Honda'],
['2008', 10, 11, 12, 13],
['2009', 20, 11, 14, 13],
['2010', 30, 15, 12, 13]
];
};
var arrayOfObjects = function() {
return [
{id: 1, name: 'Ted', lastName: 'Right'},
{id: 2, name: 'Frank', lastName: 'Honest'},
{id: 3, name: 'Joan', lastName: 'Well'},
{id: 4, name: 'Sid', lastName: 'Strong'},
{id: 5, name: 'Jane', lastName: 'Neat'},
{id: 6, name: 'Chuck', lastName: 'Jackson'},
{id: 7, name: 'Meg', lastName: 'Jansen'},
{id: 8, name: 'Rob', lastName: 'Norris'},
{id: 9, name: 'Sean', lastName: 'O\'Hara'},
{id: 10, name: 'Eve', lastName: 'Branson'}
];
};
var arrayOfNestedObjects = function() {
return [
{
id: 1,
name: {
first: 'Ted',
last: 'Right'
},
'full.street': 'Street I',
},
{
id: 2,
name: {
first: 'Frank',
last: 'Honest'
},
'full.street': 'Street II',
},
{
id: 3,
name: {
first: 'Joan',
last: 'Well'
},
'full.street': 'Street III',
}
];
};
var htmlData = [
['<b>H&M</b>']
];
it('should allow array of arrays', () => {
handsontable();
loadData(arrayOfArrays());
expect(getDataAtCell(0, 2)).toEqual('Nissan');
});
it('should allow array of objects', () => {
handsontable({
columns: [
{data: 'id'},
{data: 'lastName'},
{data: 'name'}
]
});
loadData(arrayOfObjects());
expect(getDataAtCell(0, 2)).toEqual('Ted');
});
it('should allow array of objects when columns as a function', () => {
handsontable({
columns(column) {
var colMeta = {};
if (column === 0) {
colMeta.data = 'id';
} else if (column === 1) {
colMeta.data = 'lastName';
} else if (column === 2) {
colMeta.data = 'name';
} else {
colMeta = null;
}
return colMeta;
}
});
loadData(arrayOfObjects());
expect(getDataAtCell(0, 2)).toEqual('Ted');
});
it('should allow array of nested objects', () => {
handsontable({
data: arrayOfNestedObjects(),
colHeaders: true,
columns: [
{data: 'id'},
{data: 'name.last'},
{data: 'name.first'},
{data: 'full.street'},
]
});
expect(getDataAtCell(0, 2)).toEqual('Ted');
expect(getDataAtCell(1, 3)).toEqual('Street II');
expect(getDataAtRowProp(2, 'full.street')).toEqual('Street III');
});
it('should allow array of nested objects when columns as a function', () => {
handsontable({
data: arrayOfNestedObjects(),
colHeaders: true,
columns(column) {
var colMeta = {};
if (column === 0) {
colMeta.data = 'id';
} else if (column === 1) {
colMeta.data = 'name.last';
} else if (column === 2) {
colMeta.data = 'name.first';
} else if (column === 3) {
colMeta.data = 'full.street';
} else {
colMeta = null;
}
return colMeta;
}
});
expect(getDataAtCell(0, 2)).toEqual('Ted');
expect(getDataAtCell(1, 3)).toEqual('Street II');
expect(getDataAtRowProp(2, 'full.street')).toEqual('Street III');
});
it('should figure out default column names for array of nested objects', () => {
handsontable({
data: arrayOfNestedObjects(),
colHeaders: true
});
expect(getDataAtCell(0, 2)).toEqual('Right');
});
it('should trigger onChange callback when loaded array of arrays', () => {
var called = false;
handsontable({
afterChange(changes, source) {
if (source === 'loadData') {
called = true;
}
}
});
loadData(arrayOfArrays());
expect(called).toEqual(true);
});
it('should trigger onChange callback when loaded array of objects', () => {
var called = false;
handsontable({
afterChange(changes, source) {
if (source === 'loadData') {
called = true;
}
}
});
loadData(arrayOfObjects());
expect(called).toEqual(true);
});
it('should trigger onChange callback when loaded array of nested objects', () => {
var called = false;
handsontable({
afterChange(changes, source) {
if (source === 'loadData') {
called = true;
}
}
});
loadData(arrayOfNestedObjects());
expect(called).toEqual(true);
});
it('should create new rows for array of arrays (and respect minRows)', () => {
handsontable({
minRows: 20, // minRows should be respected
data: arrayOfArrays()
});
expect(countRows()).toEqual(20); // TODO why this must be checked after render?
});
it('should create new rows for array of nested objects (and respect minRows)', () => {
handsontable({
minRows: 20, // minRows should be respected
data: arrayOfNestedObjects()
});
expect(countRows()).toEqual(20); // TODO why this must be checked after render?
});
it('HTML special chars should be escaped by default', () => {
handsontable();
loadData(htmlData);
expect(getCell(0, 0).innerHTML).toEqual('<b>H&M</b>');
});
it('should create as many rows as needed by array of objects', () => {
handsontable({
minRows: 6,
data: arrayOfObjects()
});
expect(getCell(9, 1).innerHTML).toEqual('Eve');
});
// https://github.com/handsontable/handsontable/pull/233
it('should not invoke the cells callback multiple times with the same row/col (without overlays)', () => {
var cellsSpy = jasmine.createSpy('cellsSpy');
handsontable({
data: arrayOfNestedObjects(),
colWidths: [90, 90, 90, 90],
rowHeights: [23, 23, 23, 23],
cells: cellsSpy
});
//
expect(cellsSpy.calls.count()).toEqual(43);
});
it('should not invoke the cells callback multiple times with the same row/col (with overlays)', () => {
var cellsSpy = jasmine.createSpy('cellsSpy');
handsontable({
data: arrayOfNestedObjects(),
colHeaders: true,
rowHeaders: true,
colWidths: [90, 90, 90, 90],
rowHeights: [90, 90, 90, 90],
cells: cellsSpy
});
expect(cellsSpy.calls.count()).toEqual(56);
});
it('should remove grid rows if new data source has less of them', () => {
var data1 = [
['a'],
['b'],
['c'],
['d'],
['e'],
['f'],
['g'],
['h']
];
var data2 = [
['a'],
['b'],
['c'],
['d'],
['e']
];
handsontable({
data: data1,
rowHeaders: true,
colHeaders: true
});
selectCell(7, 0);
loadData(data2);
expect(countRows()).toBe(data2.length);
expect(getSelected()).toEqual([4, 0, 4, 0]);
});
it('should remove grid rows if new data source has less of them (with minSpareRows)', () => {
var data1 = [
['a'],
['b'],
['c'],
['d'],
['e'],
['f'],
['g'],
['h']
];
var data2 = [
['a'],
['b'],
['c'],
['d'],
['e']
];
handsontable({
data: data1,
minSpareCols: 1,
minSpareRows: 1,
rowHeaders: true,
colHeaders: true
});
selectCell(8, 0);
loadData(data2);
expect(countRows()).toBe(6); // +1 because of minSpareRows
expect(getSelected()).toEqual([5, 0, 5, 0]);
});
it('loading empty data should remove all rows', () => {
var data1 = [
['a'],
['b'],
['c'],
['d'],
['e'],
['f'],
['g'],
['h']
];
var data2 = [];
handsontable({
data: data1,
rowHeaders: true,
colHeaders: true
});
selectCell(7, 0);
loadData(data2);
expect(countRows()).toBe(0);
expect(getSelected()).toBe(void 0);
});
it('should only have as many columns as in settings', () => {
var data1 = arrayOfArrays();
handsontable({
data: data1,
columns: [
{ data: 1 },
{ data: 3 }
]
});
expect(countCols()).toBe(2);
});
it('should only have as many columns as in settings when columns is a function', () => {
var data1 = arrayOfArrays();
handsontable({
data: data1,
columns(column) {
var colMeta = {
data: column
};
if ([1, 3].indexOf(column) < 0) {
colMeta = null;
}
return colMeta;
}
});
expect(countCols()).toBe(2);
});
it('should throw error when trying to load a string (constructor)', () => {
var errors = 0;
try {
handsontable({
data: 'string'
});
} catch (e) {
errors++;
}
expect(errors).toBe(1);
});
it('should throw error when trying to load a string (loadData)', () => {
var errors = 0;
try {
handsontable();
loadData('string');
} catch (e) {
errors++;
}
expect(errors).toBe(1);
});
it('should load Backbone Collection as data source', () => {
// code borrowed from demo/backbone.js
var CarModel = Backbone.Model.extend({});
var CarCollection = Backbone.Collection.extend({
model: CarModel,
// Backbone.Collection doesn't support `splice`, yet! Easy to add.
splice: hackedSplice
});
var cars = new CarCollection();
cars.add([
{make: 'Dodge', model: 'Ram', year: 2012, weight: 6811},
{make: 'Toyota', model: 'Camry', year: 2012, weight: 3190},
{make: 'Smart', model: 'Fortwo', year: 2012, weight: 1808}
]);
handsontable({
data: cars,
columns: [
attr('make'),
attr('model'),
attr('year')
]
});
// use the "good" Collection methods to emulate Array.splice
function hackedSplice(index, howMany /* model1, ... modelN */) {
var args = _.toArray(arguments).slice(2).concat({at: index}),
removed = this.models.slice(index, index + howMany);
this.remove(removed).add.apply(this, args);
return removed;
}
// normally, you'd get these from the server with .fetch()
function attr(attr) {
// this lets us remember `attr` for when when it is get/set
return {
data(car, value) {
if (_.isUndefined(value)) {
return car.get(attr);
}
car.set(attr, value);
}
};
}
expect(countRows()).toBe(3);
});
it('should load Backbone Collection as data source when columns is a function', () => {
// code borrowed from demo/backbone.js
var CarModel = Backbone.Model.extend({});
var CarCollection = Backbone.Collection.extend({
model: CarModel,
// Backbone.Collection doesn't support `splice`, yet! Easy to add.
splice: hackedSplice
});
var cars = new CarCollection();
cars.add([
{make: 'Dodge', model: 'Ram', year: 2012, weight: 6811},
{make: 'Toyota', model: 'Camry', year: 2012, weight: 3190},
{make: 'Smart', model: 'Fortwo', year: 2012, weight: 1808}
]);
handsontable({
data: cars,
columns(column) {
var colMeta = null;
if (column === 0) {
colMeta = attr('make');
} else if (column === 1) {
colMeta = attr('model');
} else if (column === 2) {
colMeta = attr('year');
}
return colMeta;
}
});
// use the "good" Collection methods to emulate Array.splice
function hackedSplice(index, howMany /* model1, ... modelN */) {
var args = _.toArray(arguments).slice(2).concat({at: index}),
removed = this.models.slice(index, index + howMany);
this.remove(removed).add.apply(this, args);
return removed;
}
// normally, you'd get these from the server with .fetch()
function attr(attr) {
// this lets us remember `attr` for when when it is get/set
return {
data(car, value) {
if (_.isUndefined(value)) {
return car.get(attr);
}
car.set(attr, value);
}
};
}
expect(countRows()).toBe(3);
});
it('should clear cell properties after loadData', () => {
handsontable();
loadData(arrayOfArrays());
getCellMeta(0, 0).foo = 'bar';
expect(getCellMeta(0, 0).foo).toEqual('bar');
loadData(arrayOfArrays());
expect(getCellMeta(0, 0).foo).toBeUndefined();
});
it('should clear cell properties after loadData, but before rendering new data', function() {
handsontable();
loadData(arrayOfArrays());
getCellMeta(0, 0).valid = false;
render();
expect(this.$container.find('tbody tr:eq(0) td:eq(0)').hasClass('htInvalid')).toEqual(true);
loadData(arrayOfArrays());
expect(this.$container.find('tbody tr:eq(0) td:eq(0)').hasClass('htInvalid')).toEqual(false);
});
// https://github.com/handsontable/handsontable/issues/1700
// can't edit anything after starting editing cell with no nested object
it('should correct behave with cell with no nested object data source corresponding to column mapping', () => {
var objectData = [
{id: 1, user: {name: {first: 'Ted', last: 'Right'}}},
{id: 2, user: {name: {}}},
{id: 3}
];
handsontable({
data: objectData,
columns: [
{data: 'id'},
{data: 'user.name.first'},
{data: 'user.name.last'}
]
});
mouseDoubleClick(getCell(1, 1));
document.activeElement.value = 'Harry';
deselectCell();
expect(objectData[1].user.name.first).toEqual('Harry');
mouseDoubleClick(getCell(2, 1));
document.activeElement.value = 'Barry';
deselectCell();
expect(objectData[2].user.name.first).toEqual('Barry');
});
it('should correct behave with cell with no nested object data source corresponding to column mapping when columns is a function', () => {
var objectData = [
{id: 1, user: {name: {first: 'Ted', last: 'Right'}}},
{id: 2, user: {name: {}}},
{id: 3}
];
handsontable({
data: objectData,
columns(column) {
var colMeta = null;
if (column === 0) {
colMeta = {data: 'id'};
} else if (column === 1) {
colMeta = {data: 'user.name.first'};
} else if (column === 2) {
colMeta = {data: 'user.name.last'};
}
return colMeta;
}
});
mouseDoubleClick(getCell(1, 1));
document.activeElement.value = 'Harry';
deselectCell();
expect(objectData[1].user.name.first).toEqual('Harry');
mouseDoubleClick(getCell(2, 1));
document.activeElement.value = 'Barry';
deselectCell();
expect(objectData[2].user.name.first).toEqual('Barry');
});
});