Skip to content
This repository has been archived by the owner on May 12, 2021. It is now read-only.

Adds unit tests for machine column and machine tokens #330

Merged
merged 2 commits into from
May 20, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
94 changes: 61 additions & 33 deletions app/widgets/machine-view-panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,13 @@ YUI.add('machine-view-panel', function(Y) {
* @method _bindEvents
*/
_bindEvents: function() {
this.addEvent(
this.get('db').machines.after(['add', 'remove', '*:change'],
this._onMachinesChange, this)
var db = this.get('db');
this.addEvent(db.machines.after(
['add', 'remove', '*:change'], this._onMachinesChange, this)
);

this.addEvent(
this.get('db').units.after(['add', 'remove', '*:change'],
this._onUnitsChange, this)
);
this.addEvent(db.units.after(
['add', 'remove', '*:change'], this._onUnitsChange, this));

this.on('*:unit-token-drag-start', this._showDraggingUI, this);
this.on('*:unit-token-drag-end', this._hideDraggingUI, this);
Expand Down Expand Up @@ -372,14 +370,15 @@ YUI.add('machine-view-panel', function(Y) {
* @param {Node} list the list node to append the machine to.
*/
_renderMachineToken: function(machineOrId) {
var machine;
var db = this.get('db'),
machine;
if (typeof machineOrId === 'string') {
machine = this.get('db').machines.getById(machineOrId);
machine = db.machines.getById(machineOrId);
} else {
machine = machineOrId;
}
var node = Y.Node.create('<li></li>'),
units = this.get('db').units.filterByMachine(machine.id, true);
units = db.units.filterByMachine(machine.id, true);
this._updateMachineWithUnitData(machine, units);
var token = new views.MachineToken({
container: node,
Expand Down Expand Up @@ -424,28 +423,45 @@ YUI.add('machine-view-panel', function(Y) {
},

/**
* A helper function that handles intelligently updating the lists in
* the machine view columns. When an update comes in, we don't want to
* re-render the entire view, just the items in the list that have
* changed. The logic below handles that and uses the render and
* cleanup callbacks to allow for column-specific logic.
*
* @method _smartUpdateList
* @param {Array} models The models that are bound to the list
* @param {NodeList} list The DOM list
* @param {Function} render A callback to handle rendering one item in
* the list
* @param {Function} cleanup Any other changes that need to occur after
* an update in the list, i.e., clearing the
* container column
A helper function that handles intelligently updating the lists in
the machine view columns. When an update comes in, we don't want to
re-render the entire view, just the items in the list that have
changed. The logic below handles that and uses the render and
cleanup callbacks to allow for column-specific logic.

@method _smartUpdateList
@param {Array} models The models that are bound to the list
@param {NodeList} list The DOM list
@param {Function} renderFn A callback to handle rendering one item
in the list
@param {Function} cleanupFn Any other changes that need to occur
after an update in the list, i.e., clearing the
container column
*/
_smartUpdateList: function(models, list, render, cleanup) {
var exists, newElement;
if (!models || !list) {
return;
}
var elements = list.all('li');
var newElements = this._addNewTokens(models, elements, render);
newElements.forEach(function(newToken) {
list.append(newToken);
});
this._removeOldTokens(models, elements, cleanup);
},

/**
Creates new tokens for any models not yet found in the DOM.

@method _addNewTokens
@param {Array} models The list of machine models
@param {Y.NodeList} elements The list of tokens in the DOM
@param {function} renderFn The function to create the rendered token.
*/
_addNewTokens: function(models, elements, renderFn) {
var list = [],
exists,
newElement;
models.forEach(function(model) {
exists = elements.some(function(element) {
// If the model already exists in the dom, mark it as such.
Expand All @@ -454,33 +470,45 @@ YUI.add('machine-view-panel', function(Y) {
return true;
}
});

if (!exists) {
// If the model does not exist in the dom, render the token.
newElement = render(model);
list.append(newElement);
newElement = renderFn(model);
list.push(newElement);
newElement.setData('exists', true);
}
}, this);
return list;
},

/**
Removes tokens for items that no longer have a corresponding model.

@method _removeOldTokens
@param {Array} models The list of machine models
@param {Y.NodeList} elements The list of tokens in the DOM
@param {function} cleanupFn Option cleanup function
*/
_removeOldTokens: function(models, elements, cleanupFn) {
elements.each(function(element) {
if (!element.getData('exists')) {
// If the element exists in the dom, but not in the model
// list then it must have been removed from the DB, so remove it
// from the dom.
var token = element.one('.token');
if (token && token.hasClass('active')) {
if (token && token.hasClass('active') && cleanupFn) {
// If the selected model was removed then stop showing
// its containers.
if (typeof cleanup === 'function') {
cleanup();
if (cleanupFn) {
cleanupFn();
}
}
element.remove();
} else if (models.length === 0) {
element.remove();
if (typeof cleanup === 'function') {
cleanup();
if (cleanupFn) {
cleanupFn();
}
element.remove();
} else {
// Clean up the 'exists' flag for the next loop through
// the nodes.
Expand Down
166 changes: 166 additions & 0 deletions test/test_machine_view_panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,88 @@ describe('machine view panel view', function() {
'container').one('.label').get('text'), label);
});

describe('_smartUpdateList', function() {
// _smartUpdateList use two private methods, _addNewTokens and
// _removeOldTokens that do all the work--we can just test those.
it('can determine if a model is new and needs token', function() {
var models = [{id: 1}];
var newTokens = view._addNewTokens(models, [], function(model) {
return Y.Node.create('<li/>');
});
assert.equal(
newTokens.length, 1,
'Did not received expected number of tokens');
assert.equal(
newTokens[0].getData('exists'), true,
'New element not marked with "exists"');
});

it('can determine if a model already has a token', function() {
var models = [{id: 1}],
element = Y.Node.create('<li data-id="1"></li>');
var newTokens = view._addNewTokens(models, [element], function(model) {
return Y.Node.create('<li/>');
});
assert.equal(
newTokens.length, 0,
'Received tokens when no new tokens should have been created');
assert.equal(
element.getData('exists'), true,
'Element not marked with "exists"');
});

it('can determine if a token has no model and remove it', function() {
container.append(Y.Node.create('<li data-id="2"></li>'));
var models = [{id: 1}],
elements = container.all('li');
assert.equal(elements.size(), 1);
view._removeOldTokens(models, elements);
assert.equal(container.all('li').size(), 0, 'Element not removed.');
});

it('can determine if there are no models and clear nodes', function() {
container.append(Y.Node.create('<li></li>'));
var models = [],
elements = container.all('li');
// Make sure the element has "exists" so we test the models condition.
elements.each(function(element) {
element.setData('exists', true);
});
assert.equal(elements.size(), 1);
view._removeOldTokens(models, elements);
assert.equal(container.all('li').size(), 0, 'Element not removed.');
});

it('calls a cleanup function if an "active" token is removed', function() {
container.append(Y.Node.create('<li><span class="token active"/></li>'));
var models = [{id: 1}],
elements = container.all('li'),
cleanup = false;
assert.equal(elements.size(), 1);
var cleanupFn = function() {
cleanup = true;
};
view._removeOldTokens(models, elements, cleanupFn);
assert.equal(cleanup, true, 'Cleanup function not called.');
});

it('calls a cleanup function if there are no models', function() {
container.append(Y.Node.create('<li></li>'));
var models = [],
elements = container.all('li'),
cleanup = false;
// Make sure the element has "exists" so we test the models condition.
elements.each(function(element) {
element.setData('exists', true);
});
var cleanupFn = function() {
cleanup = true;
};
view._removeOldTokens(models, elements, cleanupFn);
assert.equal(cleanup, true, 'Cleanup function not called.');
});
});

describe('token drag and drop', function() {
beforeEach(function() {
view.set('env', {
Expand Down Expand Up @@ -321,6 +403,61 @@ describe('machine view panel view', function() {


describe('machine column', function() {
it('can create machine tokens from a machine', function() {
var updateStub = utils.makeStubMethod(
view, '_updateMachineWithUnitData');
this._cleanups.push(updateStub.reset);
var db = view.get('db'),
filterStub = utils.makeStubMethod(db.units, 'filterByMachine', []),
getByIdStub = utils.makeStubMethod(db.machines, 'getById', machine);
this._cleanups.push(filterStub.reset);
this._cleanups.push(getByIdStub.reset);
var rendered = false,
target;
var viewStub = utils.makeStubMethod(views, 'MachineToken', {
render: function() { rendered = true; },
addTarget: function(t) { target = t; }
});
this._cleanups.push(viewStub.reset);
view._renderMachineToken(machine);

assert.equal(updateStub.calledOnce(), true);
assert.equal(viewStub.calledOnce(), true);
// GetById should not be called, as we have provided a machine.
assert.equal(getByIdStub.callCount(), 0);
// Verify token is rendered and has the view added as a target.
assert.equal(rendered, true);
assert.equal(target, view);
});

it('can create machine tokens from the ID as a string', function() {
var updateStub = utils.makeStubMethod(
view, '_updateMachineWithUnitData');
this._cleanups.push(updateStub.reset);
var db = view.get('db'),
filterStub = utils.makeStubMethod(db.units, 'filterByMachine', []),
getByIdStub = utils.makeStubMethod(db.machines, 'getById', machine);
this._cleanups.push(filterStub.reset);
this._cleanups.push(getByIdStub.reset);
var rendered = false,
target;
var viewStub = utils.makeStubMethod(views, 'MachineToken', {
render: function() { rendered = true; },
addTarget: function(t) { target = t; }
});
this._cleanups.push(viewStub.reset);
view._renderMachineToken('1');

assert.equal(updateStub.calledOnce(), true);
assert.equal(viewStub.calledOnce(), true);
// GetById should be called, so we can look up the machine.
assert.equal(getByIdStub.calledOnce(), 1);
assert.equal(getByIdStub.lastArguments()[0], '1');
// Verify token is rendered and has the view added as a target.
assert.equal(rendered, true);
assert.equal(target, view);
});

it('should render a list of machines', function() {
view.render();
var list = container.all('.machines .content li');
Expand All @@ -340,6 +477,31 @@ describe('machine view panel view', function() {
assert.equal(view._machinesHeader.get(
'container').one('.label').get('text'), label);
});

it('can update a machine via an object', function() {
machine = {id: 1};
container = view.get('container');
var oneStub = utils.makeStubMethod(container, 'one', null);
this._cleanups.push(oneStub.reset);
view._updateMachine(machine);
assert.equal(
'.machines .content .machine-token[data-id="1"]',
oneStub.lastArguments()[0] ,
'Selector created with wrong id');
});

it('can update a machine via a string', function() {
machine = '1';
container = view.get('container');
var oneStub = utils.makeStubMethod(container, 'one', null);
this._cleanups.push(oneStub.reset);
view._updateMachine(machine);
assert.equal(
'.machines .content .machine-token[data-id="1"]',
oneStub.lastArguments()[0] ,
'Selector created with wrong id');
});

/// XXX Jeff May 15 2014 - drop handlers no longer update UI. Fix once
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you get with jeff on these. I wonder if this is fixed and if not, I don't think we've got a card on the board to fix it. It looks like we might have missed a card of work in prep for the 2wk cycle.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure; I think he's handling these skipped tests in his branch in progress, but I'll talk with him later today.

// handlers update the UI.
it.skip('updates and re-renders a specific machine', function() {
Expand All @@ -355,6 +517,7 @@ describe('machine view panel view', function() {
assert.equal(node.all('.service-icons .unit').size(),
machine.units.length, 'icons not updated along with units');
});

/// XXX Jeff May 15 2014 - drop handlers no longer update UI. Fix once
// handlers update the UI.
it.skip('should add new tokens when machines are added', function() {
Expand Down Expand Up @@ -397,6 +560,7 @@ describe('machine view panel view', function() {
assert.equal(deletedItem, null,
'found the deleted machine still in the list');
});

/// XXX Jeff May 15 2014 - drop handlers no longer update UI. Fix once
// handlers update the UI.
it.skip('should re-render token when machine is updated', function() {
Expand Down Expand Up @@ -485,6 +649,7 @@ describe('machine view panel view', function() {
render: function() { rendered = true; },
addTarget: function(t) { target = t; }
});
this._cleanups.push(viewStub.reset);
var containerParent = utils.makeContainer(this, 'machine-view-panel'),
container = {};
view._createContainerToken(containerParent, container);
Expand All @@ -510,6 +675,7 @@ describe('machine view panel view', function() {
render: function() { rendered = true; },
addTarget: function(t) { target = t; }
});
this._cleanups.push(viewStub.reset);
var containerParent = utils.makeContainer(this, 'machine-view-panel'),
units = [{}],
container = {};
Expand Down