backbone.cycle
Version:
Navigating and selecting Backbone Models and Collections.
1,087 lines (778 loc) • 175 kB
JavaScript
/*global describe, it */
(function () {
"use strict";
describe( 'Options for SelectableCollection: autoSelect', function () {
var Model, m1, m2, m3, models, Collection, collection, events;
function bindOptions ( options ) {
return Collection.extend( {
initialize: function ( models ) {
Backbone.Cycle.SelectableCollection.applyTo( this, models, options );
}
} );
}
function getSelected ( modelArray, label ) {
label || ( label = "selected" );
return _.filter( modelArray, function ( model ) { return model[label]; } );
}
beforeEach( function () {
Model = Backbone.Model.extend( {
initialize: function ( attributes, options ) {
Backbone.Cycle.SelectableModel.applyTo( this, options );
}
} );
Collection = Backbone.Collection.extend( {
initialize: function ( models, options ) {
Backbone.Cycle.SelectableCollection.applyTo( this, models, options );
}
} );
m1 = new Model();
m2 = new Model();
m3 = new Model();
models = [m1, m2, m3];
} );
describe( 'By default, when creating a collection', function () {
it( 'has its autoSelect option set to "none"', function () {
// Internally, "none" is represented by the absence of an entry in _cycleOpts.autoSelect
collection = new Collection( models );
expect( collection._cycleOpts.autoSelect ).to.deep.equal( {} );
} );
} );
describe( 'Invalid option values', function () {
it( 'the collection throws an error when an invalid option value is passed', function () {
expect( function () { new Collection( models, { autoSelect: "foo" } ); } ).to.throw( 'autoSelect option: Invalid value "foo"' );
} );
it( 'the collection throws an error when an invalid option value is passed as part of a hash', function () {
expect( function () { new Collection( models, { autoSelect: { starred: "foo" } } ); } ).to.throw( 'autoSelect option: Invalid value "foo" inside hash' );
} );
it( 'the collection ignores the option when explicitly set to undefined, and uses the default value "none"', function () {
// Internally, "none" is represented by the absence of an entry in _cycleOpts.autoSelect
collection = new Collection( models, { autoSelect: undefined } );
expect( collection._cycleOpts.autoSelect ).to.deep.equal( {} );
} );
} );
describe( 'autoSelect is set to "none"', function () {
beforeEach( function () {
Collection = bindOptions( { autoSelect: "none" } );
} );
describe( 'all models are deselected initially', function () {
it( 'when they are passed to the constructor, they remain unselected', function () {
collection = new Collection( models );
expect( collection.selected ).to.be.undefined;
expect( getSelected( models ) ).to.be.empty;
} );
it( 'when they are batch-added as the initial models, the remain unselected', function () {
collection = new Collection();
collection.add( models );
expect( collection.selected ).to.be.undefined;
expect( getSelected( models ) ).to.be.empty;
} );
it( 'when they are passed in on reset, they remain unselected', function () {
collection = new Collection();
collection.add( models );
expect( collection.selected ).to.be.undefined;
expect( getSelected( models ) ).to.be.empty;
} );
} );
describe( 'when one of the models is already selected initially', function () {
beforeEach( function () {
m2.select();
} );
it( 'when they are passed to the constructor, the selection remains unchanged', function () {
collection = new Collection( models );
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'when they are batch-added as the initial models, the selection remains unchanged', function () {
collection = new Collection();
collection.add( models );
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'when they are batch-added as the initial models, with options.silent enabled, the selection remains unchanged', function () {
collection = new Collection();
collection.add( models, { silent: true } );
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'when they are passed in on reset, the selection remains unchanged', function () {
collection = new Collection();
collection.reset( models );
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'when they are passed in on reset, with options.silent enabled, the selection remains unchanged', function () {
collection = new Collection();
collection.reset( models, { silent: true } );
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
} );
} );
describe( 'autoSelect is set to "first"', function () {
beforeEach( function () {
Collection = bindOptions( { autoSelect: "first" } );
} );
describe( 'all models are deselected initially', function () {
beforeEach( function () {
events = getEventSpies( models );
} );
describe( 'when they are passed to the constructor', function () {
var selectOneEventCounter;
beforeEach( function () {
var SelfObservingCollection = Collection.extend( {
initialize: function ( models ) {
Collection.prototype.initialize.call( this, models );
this.listenTo( this, "select:one", function () {
selectOneEventCounter++;
} )
}
} );
selectOneEventCounter = 0;
sinon.spy( m1, "trigger" );
collection = new SelfObservingCollection( models );
} );
it( 'the first model is selected', function () {
expect( collection.selected ).to.deep.equal( m1 );
expect( getSelected( models ) ).to.deep.equal( [m1] );
} );
it( 'no select:one event is triggered in the collection', function () {
// In Backbone.Select, adding models to a collection during instantiation is treated like a
// reset. The collection doesn't fire selection-related events. The autoSelect mechanism should
// behave the same way and not fire a collection event during instantiation.
expect( selectOneEventCounter ).to.equal( 0 );
} );
it( 'a selected event is triggered on the first model', function () {
expect( events.get( m1, "selected" ) ).to.have.been.calledOnce;
expect( events.get( m1, "selected" ) ).to.have.been.calledWith( m1, { label: "selected" } );
} );
} );
describe( 'when they are batch-added as the initial models', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( models.concat( collection ) );
collection.add( models );
} );
it( 'the first model is selected', function () {
expect( collection.selected ).to.deep.equal( m1 );
expect( getSelected( models ) ).to.deep.equal( [m1] );
} );
it( 'a selected event is triggered on the first model', function () {
expect( events.get( m1, "selected" ) ).to.have.been.calledWith( m1, { label: "selected" } );
} );
it( 'a select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).to.have.been.calledWith( m1, collection, { label: "selected" } );
} );
it( 'no deselected event is triggered on any model', function () {
// Meaning that the only selection taking place is that of the first model, without being
// preceded by a string of selections and deselections if other models are added before.
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
expect( events.get( m2, "deselected" ) ).not.to.have.been.called;
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
// See above.
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when they are batch-added as the initial models, with options.silent enabled', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( models.concat( collection ) );
collection.add( models, { silent: true } );
} );
it( 'the first model is selected', function () {
expect( collection.selected ).to.deep.equal( m1 );
expect( getSelected( models ) ).to.deep.equal( [m1] );
} );
it( 'no selected event is triggered on the first model', function () {
expect( events.get( m1, "selected" ) ).not.to.have.been.called;
} );
it( 'no select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).not.to.have.been.called;
} );
it( 'no deselected event is triggered on any model', function () {
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
expect( events.get( m2, "deselected" ) ).not.to.have.been.called;
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when they are passed in on reset', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( [m1, collection] );
collection.reset( models );
} );
it( 'the first model is selected', function () {
expect( collection.selected ).to.deep.equal( m1 );
expect( getSelected( models ) ).to.deep.equal( [m1] );
} );
it( 'a selected event is triggered on the first model', function () {
expect( events.get( m1, "selected" ) ).to.have.been.calledWith( m1, { label: "selected" } );
} );
it( 'no select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when they are passed in on reset, with options.silent enabled', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( [m1, collection] );
collection.reset( models, { silent: true } );
} );
it( 'the first model is selected', function () {
expect( collection.selected ).to.deep.equal( m1 );
expect( getSelected( models ) ).to.deep.equal( [m1] );
} );
it( 'no selected event is triggered on the first model', function () {
expect( events.get( m1, "selected" ) ).not.to.have.been.called;
} );
it( 'no select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).not.to.have.been.called;
} );
} );
} );
describe( 'one of the models - but not the first one - is already selected initially', function () {
beforeEach( function () {
m2.select();
} );
describe( 'when they are passed to the constructor', function () {
beforeEach( function () {
events = getEventSpies( [m1] );
collection = new Collection( models );
} );
it( 'the selection remains unchanged', function () {
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'no deselected event is triggered on the first model', function () {
// ... meaning that the first model hadn't been selected first, and then had the selection
// reversed. It should remain deselected the whole time.
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
} );
// A deselect:one event is not triggered on the collection, in any case. See above for the reasons
// (corresponding test with all models deselected).
} );
describe( 'when they are batch-added as the initial models', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( [m1, collection] );
collection.add( models );
} );
it( 'the selection remains unchanged', function () {
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'no deselected event is triggered on the first model', function () {
// ... meaning that the first model hadn't been selected first, and then had the selection
// reversed. It should remain deselected the whole time.
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
// See above.
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when they are batch-added as the initial models, with options.silent enabled', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( [m1, collection] );
collection.add( models, { silent: true } );
} );
it( 'the selection remains unchanged', function () {
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'no deselected event is triggered on the first model', function () {
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when they are passed in on reset', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( [m1, collection] );
collection.reset( models );
} );
it( 'the selection remains unchanged', function () {
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'no deselected event is triggered on the first model', function () {
// ... meaning that the first model hadn't been selected first, and then had the selection
// reversed. It should remain deselected the whole time.
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when they are passed in on reset, with options.silent enabled', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( [m1, collection] );
collection.reset( models, { silent: true } );
} );
it( 'the selection remains unchanged', function () {
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'no deselected event is triggered on the first model', function () {
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
} );
describe( 'the collection is already populated, without a selection', function () {
var newModels, allModels, mA, mB;
beforeEach( function () {
mA = new Model();
mB = new Model();
newModels = [mA, mB];
allModels = models.concat( newModels );
} );
describe( 'when new models are batch-added to the end, and none of them is selected', function () {
beforeEach( function () {
collection = new Collection();
collection.add( models );
collection.deselect();
events = getEventSpies( allModels.concat( collection ) );
collection.add( newModels );
} );
it( 'the first model is selected', function () {
expect( collection.selected ).to.deep.equal( m1 );
expect( getSelected( allModels ) ).to.deep.equal( [m1] );
} );
it( 'a selected event is triggered on the first model', function () {
expect( events.get( m1, "selected" ) ).to.have.been.calledWith( m1, { label: "selected" } );
} );
it( 'a select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).to.have.been.calledWith( m1, collection, { label: "selected" } );
} );
it( 'no deselected event is triggered on any model', function () {
// Meaning that the only selection taking place is that of the first model, without being
// preceded by a string of selections and deselections on other models beforehand.
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
expect( events.get( m2, "deselected" ) ).not.to.have.been.called;
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
expect( events.get( mA, "deselected" ) ).not.to.have.been.called;
expect( events.get( mB, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
// See above.
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when new models are batch-added to the end, with options.silent enabled, and none of the models is selected', function () {
beforeEach( function () {
collection = new Collection();
collection.add( models );
collection.deselect();
events = getEventSpies( allModels.concat( collection ) );
collection.add( newModels, { silent: true } );
} );
it( 'the first model is selected', function () {
expect( collection.selected ).to.deep.equal( m1 );
expect( getSelected( allModels ) ).to.deep.equal( [m1] );
} );
it( 'no selected event is triggered on the first model', function () {
expect( events.get( m1, "selected" ) ).not.to.have.been.called;
} );
it( 'no select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).not.to.have.been.called;
} );
it( 'no deselected event is triggered on any model', function () {
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
expect( events.get( m2, "deselected" ) ).not.to.have.been.called;
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
expect( events.get( mA, "deselected" ) ).not.to.have.been.called;
expect( events.get( mB, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when new models are batch-added to the end, and one of them is selected', function () {
beforeEach( function () {
collection = new Collection();
collection.add( models );
collection.deselect();
mA.select();
events = getEventSpies( [m1, collection] );
collection.add( newModels );
} );
it( 'the selection remains unchanged', function () {
expect( collection.selected ).to.deep.equal( mA );
expect( getSelected( allModels ) ).to.deep.equal( [mA] );
} );
it( 'no deselected event is triggered on the first model', function () {
// ... meaning that the first model hadn't been selected first, and then had the selection
// reversed. It should remain deselected the whole time.
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
// See above.
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when new models are batch-added at the front, and none of them is selected', function () {
beforeEach( function () {
collection = new Collection();
collection.add( models );
collection.deselect();
events = getEventSpies( allModels.concat( collection ) );
collection.add( newModels, { at: 0 } );
} );
it( 'the first model is selected', function () {
expect( collection.selected ).to.deep.equal( mA );
expect( getSelected( allModels ) ).to.deep.equal( [mA] );
} );
it( 'a selected event is triggered on the first model', function () {
expect( events.get( mA, "selected" ) ).to.have.been.calledWith( mA, { label: "selected" } );
} );
it( 'a select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).to.have.been.calledWith( mA, collection, { label: "selected" } );
} );
it( 'no deselected event is triggered on any model', function () {
// Meaning that the only selection taking place is that of the first model, without being
// preceded by a string of selections and deselections on other models beforehand.
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
expect( events.get( m2, "deselected" ) ).not.to.have.been.called;
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
expect( events.get( mA, "deselected" ) ).not.to.have.been.called;
expect( events.get( mB, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
// See above.
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when new models are batch-added at the front, with options.silent enabled, and none of the models is selected', function () {
beforeEach( function () {
collection = new Collection();
collection.add( models );
collection.deselect();
events = getEventSpies( allModels.concat( collection ) );
collection.add( newModels, { at: 0, silent: true } );
} );
it( 'the first model is selected', function () {
expect( collection.selected ).to.deep.equal( mA );
expect( getSelected( allModels ) ).to.deep.equal( [mA] );
} );
it( 'no selected event is triggered on the first model', function () {
expect( events.get( mA, "selected" ) ).not.to.have.been.called;
} );
it( 'no select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).not.to.have.been.called;
} );
it( 'no deselected event is triggered on any model', function () {
// Meaning that the only selection taking place is that of the first model, without being
// preceded by a string of selections and deselections on other models beforehand.
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
expect( events.get( m2, "deselected" ) ).not.to.have.been.called;
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
expect( events.get( mA, "deselected" ) ).not.to.have.been.called;
expect( events.get( mB, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
// See above.
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when new models are batch-added at the front, and one of them is selected', function () {
beforeEach( function () {
collection = new Collection();
collection.add( models );
collection.deselect();
mB.select();
events = getEventSpies( [m1, mA, collection] );
collection.add( newModels, { at: 0 } );
} );
it( 'the selection remains unchanged', function () {
expect( collection.selected ).to.deep.equal( mB );
expect( getSelected( allModels ) ).to.deep.equal( [mB] );
} );
it( 'no deselected event is triggered on the first model', function () {
// ... meaning that the first model hadn't been selected first, and then had the selection
// reversed. It should remain deselected the whole time.
// Looking at the former first model, m1, as well as the new first model, mA.
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
expect( events.get( mA, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
// See above.
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
} );
} );
describe( 'autoSelect is set to "last"', function () {
beforeEach( function () {
Collection = bindOptions( { autoSelect: "last" } );
} );
describe( 'all models are deselected initially', function () {
describe( 'when they are passed to the constructor', function () {
beforeEach( function () {
events = getEventSpies( [m3] );
collection = new Collection( models );
} );
it( 'the last model is selected', function () {
expect( collection.selected ).to.deep.equal( m3 );
expect( getSelected( models ) ).to.deep.equal( [m3] );
} );
// NB A select:one event is not triggered on the collection. The case is tested in 'autoSelect is
// set to "first"'. See there for rationale.
it( 'a selected event is triggered on the last model', function () {
expect( events.get( m3, "selected" ) ).to.have.been.calledWith( m3, { label: "selected" } );
} );
} );
describe( 'when they are batch-added as the initial models', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( models.concat( collection ) );
collection.add( models );
} );
it( 'the last model is selected', function () {
expect( collection.selected ).to.deep.equal( m3 );
expect( getSelected( models ) ).to.deep.equal( [m3] );
} );
it( 'a selected event is triggered on the last model', function () {
expect( events.get( m3, "selected" ) ).to.have.been.calledWith( m3, { label: "selected" } );
} );
it( 'a select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).to.have.been.calledWith( m3, collection, { label: "selected" } );
} );
it( 'no deselected event is triggered on any model', function () {
// Meaning that the only selection taking place is that of the last model, without being
// preceded by a string of selections and deselections while models are added successively.
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
expect( events.get( m2, "deselected" ) ).not.to.have.been.called;
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
// See above.
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when they are batch-added as the initial models, with options.silent enabled', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( models.concat( collection ) );
collection.add( models, { silent: true } );
} );
it( 'the last model is selected', function () {
expect( collection.selected ).to.deep.equal( m3 );
expect( getSelected( models ) ).to.deep.equal( [m3] );
} );
it( 'no selected event is triggered on the last model', function () {
expect( events.get( m3, "selected" ) ).not.to.have.been.called;
} );
it( 'no select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).not.to.have.been.called;
} );
it( 'no deselected event is triggered on any model', function () {
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
expect( events.get( m2, "deselected" ) ).not.to.have.been.called;
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when they are passed in on reset', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( [m3, collection] );
collection.reset( models );
} );
it( 'the last model is selected', function () {
expect( collection.selected ).to.deep.equal( m3 );
expect( getSelected( models ) ).to.deep.equal( [m3] );
} );
it( 'a selected event is triggered on the last model', function () {
expect( events.get( m3, "selected" ) ).to.have.been.calledWith( m3, { label: "selected" } );
} );
it( 'no select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when they are passed in on reset, with options.silent enabled', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( [m3, collection] );
collection.reset( models, { silent: true } );
} );
it( 'the last model is selected', function () {
expect( collection.selected ).to.deep.equal( m3 );
expect( getSelected( models ) ).to.deep.equal( [m3] );
} );
it( 'no selected event is triggered on the last model', function () {
expect( events.get( m3, "selected" ) ).not.to.have.been.called;
} );
it( 'no select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).not.to.have.been.called;
} );
} );
} );
describe( 'one of the models - but not the last one - is already selected initially', function () {
beforeEach( function () {
m2.select();
} );
describe( 'when they are passed to the constructor', function () {
beforeEach( function () {
events = getEventSpies( [m3] );
collection = new Collection( models );
} );
it( 'the selection remains unchanged', function () {
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'no deselected event is triggered on the last model', function () {
// ... meaning that the last model hadn't been selected first, and then had the selection
// reversed. It should remain deselected the whole time.
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
} );
// A deselect:one event is not triggered on the collection, in any case. See above for the reasons
// (corresponding test with all models deselected).
} );
describe( 'when they are batch-added as the initial models', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( [m3, collection] );
collection.add( models );
} );
it( 'the selection remains unchanged', function () {
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'no deselected event is triggered on the last model', function () {
// ... meaning that the last model hadn't been selected first, and then had the selection
// reversed. It should remain deselected the whole time.
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
// See above.
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when they are batch-added as the initial models, with options.silent enabled', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( [m3, collection] );
collection.add( models, { silent: true } );
} );
it( 'the selection remains unchanged', function () {
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'no deselected event is triggered on the last model', function () {
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when they are passed in on reset', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( [m3, collection] );
collection.reset( models );
} );
it( 'the selection remains unchanged', function () {
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'no deselected event is triggered on the last model', function () {
// ... meaning that the last model hadn't been selected first, and then had the selection
// reversed. It should remain deselected the whole time.
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when they are passed in on reset, with options.silent enabled', function () {
beforeEach( function () {
collection = new Collection();
events = getEventSpies( [m3, collection] );
collection.reset( models, { silent: true } );
} );
it( 'the selection remains unchanged', function () {
expect( collection.selected ).to.deep.equal( m2 );
expect( getSelected( models ) ).to.deep.equal( [m2] );
} );
it( 'no deselected event is triggered on the last model', function () {
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
} );
describe( 'the collection is already populated, without a selection', function () {
var newModels, allModels, mA, mB;
beforeEach( function () {
mA = new Model();
mB = new Model();
newModels = [mA, mB];
allModels = models.concat( newModels );
} );
describe( 'when new models are batch-added to the end, and none of them is selected', function () {
beforeEach( function () {
collection = new Collection();
collection.add( models );
collection.deselect();
events = getEventSpies( allModels.concat( collection ) );
collection.add( newModels );
} );
it( 'the last model is selected', function () {
expect( collection.selected ).to.deep.equal( mB );
expect( getSelected( allModels ) ).to.deep.equal( [mB] );
} );
it( 'a selected event is triggered on the last model', function () {
expect( events.get( mB, "selected" ) ).to.have.been.calledWith( mB, { label: "selected" } );
} );
it( 'no selected event is triggered on the first model of the new batch', function () {
expect( events.get( mA, "selected" ) ).not.to.have.been.called;
} );
it( 'a select:one event is triggered on the collection', function () {
expect( events.get( collection, "select:one" ) ).to.have.been.calledWith( mB, collection, { label: "selected" } );
} );
it( 'no deselected event is triggered on any model', function () {
// Meaning that the only selection taking place is that of the last model, without being
// preceded by a string of selections and deselections on other models beforehand.
expect( events.get( m1, "deselected" ) ).not.to.have.been.called;
expect( events.get( m2, "deselected" ) ).not.to.have.been.called;
expect( events.get( m3, "deselected" ) ).not.to.have.been.called;
expect( events.get( mA, "deselected" ) ).not.to.have.been.called;
expect( events.get( mB, "deselected" ) ).not.to.have.been.called;
} );
it( 'no deselect:one event is triggered on the collection', function () {
// See above.
expect( events.get( collection, "deselect:one" ) ).not.to.have.been.called;
} );
} );
describe( 'when new models are batch-added to the end, with options.silent enabled, and none of the models is selected', function () {
beforeEach( function () {
collection = new Collection();
collection.add( models );
collection.deselect();
events = getEventSpies( allModels.concat( collection ) );
collection.add( newModels, { silent: true } );
} );
it( 'the last model is selected', function () {
expect( collection.selected ).to.deep.equal( mB );
expect( getSelected( allModels ) ).to.deep.equal( [mB] );
} );
it( 'no selected event is triggered on the last model', function () {
expect( events.get( mB, "selected" ) ).not.to.have.been.called;
} );
it( 'no selected event is triggered on the first model of the new batch', function () {
expect( events.get( mA, "selected" ) ).not.to.have.been.called;
} );