UNPKG

backbone.cycle

Version:

Navigating and selecting Backbone Models and Collections.

1,087 lines (778 loc) 175 kB
/*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; } );