define([
|
'intern!tdd',
|
'intern/chai!assert',
|
'dgrid/OnDemandGrid',
|
'dgrid/Editor',
|
'dgrid/Tree',
|
'dgrid/util/misc',
|
'dojo/_base/declare',
|
'dojo/_base/lang',
|
'dojo/aspect',
|
'dojo/Deferred',
|
'dojo/dom-class',
|
'dojo/dom-construct',
|
'dojo/dom-style',
|
'dojo/on',
|
'dojo/query',
|
'dojo/string',
|
'dgrid/test/data/createHierarchicalStore',
|
'../addCss!'
|
], function (test, assert, OnDemandGrid, Editor, Tree, miscUtil, declare, lang, aspect, Deferred,
|
domClass, domConstruct, domStyle, on, query, string, createHierarchicalStore) {
|
|
var grid,
|
store,
|
testDelay = 15;
|
|
function makeId(num) {
|
return string.pad(num, 3);
|
}
|
|
function createGrid(options, setStoreAfterStartup) {
|
var data = [],
|
treeColumnOptions,
|
i,
|
k,
|
GridConstructor,
|
parentRowCount = options.parentRowCount || 5,
|
childRowCount = options.childRowCount || 500;
|
|
for (i = 0; i < parentRowCount; i++) {
|
var parentId = makeId(i);
|
data.push({
|
id: parentId,
|
value: 'Root ' + i
|
});
|
for (k = 0; k < childRowCount; k++) {
|
data.push({
|
id: makeId(i) + ':' + makeId(k),
|
parent: parentId,
|
value: 'Child ' + k,
|
hasChildren: false
|
});
|
}
|
}
|
|
store = createHierarchicalStore({
|
data: data
|
});
|
|
treeColumnOptions = lang.mixin({
|
renderExpando: true,
|
label: 'id',
|
field: 'id'
|
}, options && options.treeColumnOptions);
|
|
if (options && options.useEditor) {
|
GridConstructor = declare([OnDemandGrid, Editor, Tree]);
|
if (!treeColumnOptions.editor) {
|
treeColumnOptions.editor = 'text';
|
}
|
}
|
else {
|
GridConstructor = declare([OnDemandGrid, Tree]);
|
}
|
|
grid = new GridConstructor(lang.mixin({
|
sort: 'id',
|
collection: setStoreAfterStartup ? null : store,
|
columns: [
|
treeColumnOptions,
|
{ label: 'value', field: 'value' }
|
],
|
enableTreeTransitions: false
|
}, options && options.gridOptions));
|
document.body.appendChild(grid.domNode);
|
grid.startup();
|
|
if (setStoreAfterStartup) {
|
grid.set('collection', store);
|
}
|
}
|
|
function destroyGrid() {
|
grid.destroy();
|
grid = null;
|
}
|
|
function testRowExists(dataItemId, exists) {
|
// Tests existence of a row for a given item ID;
|
// if `exists` is false, tests for nonexistence instead
|
exists = exists !== false;
|
assert[exists ? 'isNotNull' : 'isNull'](document.getElementById(grid.id + '-row-' + dataItemId),
|
'A row for ' + dataItemId + ' should ' + (exists ? '' : 'not ') + 'exist in the grid.');
|
}
|
|
function wait(delay) {
|
// Returns a promise resolving after the given number of ms (or testDelay by default)
|
var dfd = new Deferred();
|
setTimeout(function () {
|
dfd.resolve();
|
}, delay || testDelay);
|
return dfd.promise;
|
}
|
|
function scrollToEnd() {
|
var dfd = new Deferred(),
|
handle;
|
|
handle = on.once(grid.bodyNode, 'scroll', miscUtil.debounce(function () {
|
dfd.resolve();
|
}));
|
|
grid.scrollTo({ y: grid.bodyNode.scrollHeight });
|
|
return dfd.promise;
|
}
|
|
function scrollToBegin() {
|
var dfd = new Deferred(),
|
handle;
|
|
handle = on.once(grid.bodyNode, 'scroll', miscUtil.debounce(function () {
|
dfd.resolve();
|
}));
|
|
grid.scrollTo({ y: 0 });
|
|
return dfd.promise;
|
}
|
|
test.suite('Tree', function () {
|
|
function makeBeforeEach(setStoreAfterStartup) {
|
return function () {
|
createGrid({}, setStoreAfterStartup);
|
|
// Firefox in particular seems to skip transitions sometimes
|
// if we don't wait a bit after creating and placing the grid
|
return wait();
|
};
|
}
|
|
test.suite('configure store last', function () {
|
|
test.beforeEach(makeBeforeEach(true));
|
|
test.afterEach(destroyGrid);
|
|
test.test('expand first row', function () {
|
return grid.expand('000')
|
.then(function () {
|
testRowExists('000:000');
|
testRowExists('000:499', false);
|
});
|
});
|
|
test.test('expand last row', function () {
|
return grid.expand('004').then(function () {
|
testRowExists('004:000');
|
testRowExists('004:499', false);
|
});
|
});
|
});
|
|
test.suite('large family expansion', function () {
|
|
test.beforeEach(makeBeforeEach());
|
|
test.afterEach(destroyGrid);
|
|
test.test('expand first row', function () {
|
return grid.expand('000')
|
.then(function () {
|
testRowExists('000:000');
|
testRowExists('000:499', false);
|
});
|
});
|
|
test.test('expand first row + scroll to bottom', function () {
|
return grid.expand('000')
|
.then(scrollToEnd)
|
.then(function () {
|
testRowExists('000:000', false);
|
testRowExists('000:499');
|
});
|
});
|
|
test.test('expand last row', function () {
|
return grid.expand('004').then(function () {
|
testRowExists('004:000');
|
testRowExists('004:499', false);
|
});
|
});
|
|
test.test('expand last row + scroll to bottom', function () {
|
return grid.expand('004')
|
.then(function () {
|
testRowExists('004:000');
|
}).then(scrollToEnd)
|
.then(function () {
|
testRowExists('004:000', false);
|
testRowExists('004:499');
|
});
|
});
|
|
test.test('expand first and last rows + scroll to bottom', function () {
|
return grid.expand('000')
|
.then(function () {
|
testRowExists('000');
|
testRowExists('000:000');
|
}).then(scrollToEnd)
|
.then(function () {
|
return grid.expand('004');
|
})
|
.then(scrollToEnd)
|
.then(function () {
|
testRowExists('000', false);
|
testRowExists('000:000', false);
|
testRowExists('004:000', false);
|
testRowExists('004:499');
|
});
|
});
|
|
test.test('expand hidden', function () {
|
var dfd = this.async(1000);
|
|
grid.domNode.style.display = 'none';
|
grid.expand('000');
|
grid.domNode.style.display = 'block';
|
|
// Since the grid is not displayed the expansion will occur without a transitionend event
|
// However, DOM updates from the expand will not complete within the current stack frame
|
setTimeout(dfd.callback(function () {
|
var connected = grid.row('000').element.connected;
|
assert.isTrue(connected && connected.offsetHeight > 0,
|
'Node should be expanded with non-zero height');
|
}), 0);
|
});
|
|
// Test goal: ensure the expando icon is displayed consistent with the results of the store's
|
// "mayHaveChildren" method.
|
// Notes:
|
// * The store created in "createGrid" has a "mayHaveChildren" that returns true for nodes with no parentId
|
// * The expando icon (.dgrid-expando-icon) is always rendered, it is not visible if it lacks the class
|
// ".ui-icon"
|
test.test('mayHaveChildren', function () {
|
var rowObject;
|
var expandoNode;
|
var i;
|
var j;
|
var rowId;
|
|
for (i = 0; i < 5; i++) {
|
rowObject = grid.row(i);
|
expandoNode = query('.dgrid-expando-icon.ui-icon', rowObject.element)[0];
|
assert.isDefined(expandoNode, 'Parent node should have an expando icon; node id = ' + i);
|
|
grid.expand(makeId(i), true, true);
|
|
for (j = 0; j < 2; j++) {
|
rowId = makeId(i) + ':' + makeId(j);
|
rowObject = grid.row(rowId);
|
expandoNode = query('.dgrid-expando-icon.ui-icon', rowObject.element)[0];
|
assert.isUndefined(expandoNode,
|
'Child node should not have an expando icon; node id = ' + rowId);
|
}
|
|
grid.expand(makeId(i), false, true);
|
}
|
});
|
|
// Test goal: ensure that rows are correctly expanded/collapsed on grid render in accordance with the
|
// grid's "shouldExpand" method
|
test.test('shouldExpand', function () {
|
var shouldExpand;
|
var i;
|
|
grid.shouldExpand = function (rowObject) {
|
var shouldExpand = false;
|
|
if (rowObject.data.parent === undefined) {
|
shouldExpand = (Number(rowObject.id) % 2) === 0;
|
}
|
|
return shouldExpand;
|
};
|
return grid.refresh().then(function () {
|
for (i = 0; i < 5; i++) {
|
shouldExpand = i % 2 === 0;
|
if (shouldExpand) {
|
assert.isTrue(grid._expanded[makeId(i)], 'Row ' + i + ' should be expanded');
|
}
|
else {
|
assert.isUndefined(grid._expanded[makeId(i)], 'Row ' + i + ' should not be expanded');
|
}
|
}
|
});
|
});
|
|
// Test goal: ensure that a custom "renderExpando" column method produces the expected DOM structure
|
test.test('renderExpando', function () {
|
var columns;
|
var rowObject;
|
var expandoNode;
|
var i;
|
var rowId;
|
|
columns = grid.get('columns');
|
columns[0].renderExpando = function () {
|
|
// * Adds the "test-expando" class
|
// * Floats the expando at the opposite end of the cell
|
var node = grid._defaultRenderExpando.apply(this, arguments);
|
domClass.add(node, 'test-expando');
|
domStyle.set(node, 'float', 'right');
|
return node;
|
};
|
grid.set('columns', columns);
|
|
for (i = 0; i < 5; i++) {
|
rowId = makeId(i);
|
rowObject = grid.row(rowId);
|
expandoNode = query('.dgrid-expando-icon.ui-icon', rowObject.element)[0];
|
assert.isDefined(expandoNode, 'Row ' + rowId + ' should have an expando icon');
|
assert.include(expandoNode.className, 'test-expando',
|
'Row ' + rowId + '\'s expando icon should have the class "test-expando"');
|
}
|
});
|
|
// Test goal: ensure the expando node is still rendered when the column has a custom "renderCell" method
|
test.test('renderCell', function () {
|
var rowObject;
|
var expandoNode;
|
var i;
|
|
grid.destroy();
|
|
createGrid({
|
treeColumnOptions: {
|
renderCell: function (object, value) {
|
var div = domConstruct.create('div', { className: 'testRenderCell' });
|
div.appendChild(document.createTextNode(value));
|
return div;
|
}
|
}
|
});
|
|
for (i = 0; i < 5; i++) {
|
rowObject = grid.row(i);
|
expandoNode = query('.dgrid-expando-icon.ui-icon', rowObject.element)[0];
|
assert.isDefined(expandoNode, 'Row ' + i + ' should have an expando node');
|
}
|
|
grid.destroy();
|
|
createGrid({
|
treeColumnOptions: {
|
renderCell: function (rowObject, cellValue, cellNode) {
|
domClass.add(cellNode, 'testRenderCell');
|
cellNode.appendChild(document.createTextNode(cellValue));
|
}
|
}
|
});
|
|
for (i = 0; i < 5; i++) {
|
rowObject = grid.row(i);
|
expandoNode = query('.dgrid-expando-icon.ui-icon', rowObject.element)[0];
|
assert.isDefined(expandoNode, 'Row ' + i + ' should have an expando node');
|
}
|
});
|
|
// Test goal: ensure the expando node is still rendered with the editor plugin
|
// Note: ordering is important: tree(editor()), not editor(tree())
|
test.test('renderCell with editor', function () {
|
var rowObject;
|
var expandoNode;
|
var inputNode;
|
var i;
|
|
grid.destroy();
|
|
createGrid({
|
useEditor: true
|
});
|
|
for (i = 0; i < 5; i++) {
|
rowObject = grid.row(i);
|
expandoNode = query('.dgrid-expando-icon.ui-icon', rowObject.element)[0];
|
assert.isDefined(expandoNode, 'Row ' + i + ' should have an expando node');
|
inputNode = query('.dgrid-input', rowObject.element)[0];
|
assert.isDefined(inputNode, 'Row ' + i + ' should have an input node');
|
}
|
});
|
|
// Test goal: ensure renderCell does not get re-wrapped over itself if the same column definition object
|
// is received during a column reset (e.g. via ColumnReorder). See #1157
|
test.test('renderCell after same structure is recomputed', function () {
|
function countRowExpandos() {
|
return grid.row('000').element.querySelectorAll('.dgrid-expando-icon').length;
|
}
|
|
assert.strictEqual(countRowExpandos(), 1, 'Each parent row should have one expando icon');
|
grid.set('columns', grid.get('columns'));
|
assert.strictEqual(countRowExpandos(), 1, 'Each parent row should still have only one expando icon');
|
});
|
});
|
|
test.suite('large family expansion without sort', function () {
|
test.beforeEach(function () {
|
createGrid({ gridOptions: { sort: null } });
|
return wait();
|
});
|
|
test.afterEach(destroyGrid);
|
|
test.test('expand first row', function () {
|
return grid.expand('000')
|
.then(function () {
|
testRowExists('000:000');
|
var row = grid.row('000:000').element;
|
assert.strictEqual(row.previousSibling.className, 'dgrid-preload',
|
'Item 000:000 should be the first item even with no sort order specified');
|
});
|
});
|
});
|
|
test.suite('Tree + Trackable', function () {
|
test.beforeEach(createGrid);
|
test.afterEach(destroyGrid);
|
|
test.test('child modification', function () {
|
return grid.expand('000').then(function () {
|
testRowExists('000:000');
|
assert.doesNotThrow(function () {
|
grid.collection.putSync({
|
id: '000:000',
|
value: 'Modified',
|
parent: '0'
|
});
|
}, null, 'Modification of child should not throw error');
|
});
|
});
|
|
test.test('child modification after parent when expanded', function () {
|
return grid.expand('000').then(function () {
|
grid.collection.putSync({
|
id: '000',
|
value: 'Modified'
|
});
|
grid.collection.putSync({
|
id: '000:000',
|
value: 'Modified',
|
parent: '000',
|
hasChildren: false
|
});
|
grid.collection.putSync({
|
id: '000',
|
value: 'Modified'
|
});
|
assert.strictEqual(grid.row('000').element, grid.domNode.querySelector('.dgrid-row'),
|
'Expected first row to be the first row in the dom');
|
});
|
});
|
|
test.test('collapse items removed from store', function () {
|
return grid.expand('000').then(function () {
|
assert.isTrue(domClass.contains(grid.row('000').element, 'dgrid-row-expanded'),
|
'Should have expanded class');
|
var item = grid.collection.getSync('000');
|
grid.collection.remove('000');
|
grid.collection.addSync(item);
|
assert.isFalse(domClass.contains(grid.row('000').element, 'dgrid-row-expanded'),
|
'Should not preserve expanded status after removing from store');
|
});
|
});
|
});
|
|
test.suite('Tree + Trackable + shouldTrackCollection: false', function () {
|
var handles = [];
|
|
test.beforeEach(function () {
|
createGrid({
|
gridOptions: {
|
shouldTrackCollection: false
|
}
|
});
|
});
|
|
test.afterEach(function () {
|
for (var i = handles.length; i--;) {
|
handles[i].remove();
|
}
|
handles = [];
|
destroyGrid();
|
});
|
|
test.test('child add', function () {
|
return grid.expand('000').then(function () {
|
testRowExists('000:000');
|
grid.collection.add({
|
id: '000:0.5',
|
value: 'New',
|
parent: '000'
|
});
|
testRowExists('000:0.5', false);
|
});
|
});
|
|
test.test('child put', function () {
|
return grid.expand('000').then(function () {
|
var calls = 0;
|
|
handles.push(aspect.before(grid, 'removeRow', function () {
|
calls++;
|
}));
|
|
handles.push(aspect.before(grid, 'insertRow', function () {
|
calls++;
|
}));
|
|
testRowExists('000:000');
|
grid.collection.put({
|
id: '000:000',
|
value: 'Modified',
|
parent: '000'
|
});
|
assert.strictEqual(calls, 0, 'insertRow and removeRow should never be called');
|
});
|
});
|
|
test.test('child remove', function () {
|
return grid.expand('000').then(function () {
|
testRowExists('000:000');
|
grid.collection.remove('000:000');
|
testRowExists('000:000');
|
});
|
});
|
});
|
|
test.suite('prune large family on scroll', function () {
|
var handles = [];
|
|
test.beforeEach(function () {
|
createGrid({
|
parentRowCount: 500,
|
childRowCount: 500
|
});
|
});
|
|
test.afterEach(function () {
|
for (var i = handles.length; i--;) {
|
handles[i].remove();
|
}
|
handles = [];
|
destroyGrid();
|
});
|
|
test.test('prune expanded top row', function () {
|
return grid.expand('000').then(function () {
|
testRowExists('000:000');
|
return scrollToEnd();
|
}).then(function () {
|
testRowExists('000:000', false);
|
});
|
});
|
|
test.test('prune expanded bottom row', function () {
|
return scrollToEnd().then(function () {
|
return grid.expand('499');
|
}).then(function () {
|
testRowExists('499:000');
|
return scrollToBegin();
|
}).then(function () {
|
testRowExists('499:000', false);
|
});
|
});
|
|
test.test('prune expanded bottom row at bottom', function () {
|
return scrollToEnd().then(function () {
|
return grid.expand('499');
|
}).then(scrollToEnd).then(function () {
|
testRowExists('499:499');
|
return scrollToBegin();
|
}).then(function () {
|
testRowExists('499:499', false);
|
});
|
});
|
|
test.test('expanded top row, scroll to bottom, verify release range', function () {
|
var releaseCount = 0;
|
var expectedCount;
|
return grid.expand('000').then(function (initialRows) {
|
testRowExists('000:000');
|
expectedCount = initialRows[0].parentNode.querySelectorAll('.dgrid-row').length;
|
handles.push(aspect.after(grid.preload.query.collection, 'releaseRange',
|
function (startIndex, endIndex) {
|
releaseCount = endIndex - startIndex;
|
}, true));
|
return scrollToEnd();
|
}).then(function () {
|
testRowExists('000:000', false);
|
assert.strictEqual(releaseCount, expectedCount, 'All first row children should have been released.');
|
});
|
});
|
|
test.test('expanded bottom row, scroll to top, verify release range', function () {
|
var releaseCount = 0;
|
var expectedCount;
|
|
return scrollToEnd().then(function () {
|
return grid.expand('499');
|
}).then(function (initialRows) {
|
testRowExists('499:000');
|
expectedCount = initialRows[0].parentNode.querySelectorAll('.dgrid-row').length;
|
handles.push(aspect.after(grid.preload.query.collection, 'releaseRange',
|
function (startIndex, endIndex) {
|
releaseCount = endIndex - startIndex;
|
}, true));
|
return scrollToBegin();
|
}).then(function () {
|
testRowExists('499:000', false);
|
assert.strictEqual(releaseCount, expectedCount, 'All first row children should have been released.');
|
});
|
});
|
});
|
});
|
});
|