backbone.select
Version:
Selecting Backbone models; handling selections in Backbone collections.
750 lines (591 loc) • 38.8 kB
JavaScript
describe( "Models shared between multiple collections: adding and removing models with reset()", function () {
var Model, SingleSelectCollection, MultiSelectCollection;
beforeAll( function () {
limitJasmineRecursiveScreenOutput();
} );
afterAll( function () {
restoreJasmineRecursiveScreenOutput();
} );
beforeEach( function () {
Model = Backbone.Model.extend( {
initialize: function () {
Backbone.Select.Me.applyTo( this );
}
} );
SingleSelectCollection = Backbone.Collection.extend( {
initialize: function ( models ) {
Backbone.Select.One.applyTo( this, models );
}
} );
MultiSelectCollection = Backbone.Collection.extend( {
initialize: function ( models ) {
Backbone.Select.Many.applyTo( this, models );
}
} );
} );
describe( "when a selected model is added by resetting the collection", function () {
var model1, model2, model3,
singleCollectionA, singleCollectionB, multiCollectionA;
beforeEach( function () {
model1 = new Model();
model2 = new Model();
model3 = new Model();
singleCollectionA = new SingleSelectCollection( [model1] );
singleCollectionB = new SingleSelectCollection( [model3] );
multiCollectionA = new MultiSelectCollection( [model1, model2, model3] );
model1.select();
model2.select();
model3.select();
spyOn( model1, "trigger" ).and.callThrough();
spyOn( model2, "trigger" ).and.callThrough();
spyOn( singleCollectionA, "trigger" ).and.callThrough();
spyOn( singleCollectionB, "trigger" ).and.callThrough();
spyOn( multiCollectionA, "trigger" ).and.callThrough();
} );
afterEach( function () {
singleCollectionA.close();
singleCollectionB.close();
multiCollectionA.close();
} );
it( "should be selected in a single-select collection it is added to", function () {
singleCollectionA.reset( [model1] );
expect( singleCollectionA.selected ).toBe( model1 );
} );
it( "should be selected in a single-select collection if multiple models with selected status are added, and it is the last of them", function () {
singleCollectionA.reset( [model1, model2] );
expect( model2.selected ).toBe( true );
expect( singleCollectionA.selected ).toBe( model2 );
} );
it( "should be deselected in a single-select collection if multiple models with selected status are added, and it is not the last of them", function () {
singleCollectionA.reset( [model1, model2] );
expect( model1.selected ).toBe( false );
} );
it( "should be added to the selected models in a multi-select collection (being the first of multiple selected models)", function () {
multiCollectionA.reset( [model1, model3] );
expect( multiCollectionA.selected[model1.cid] ).not.toBeUndefined();
} );
it( "should be added to the selected models in a multi-select collection (being the last of multiple selected models)", function () {
multiCollectionA.reset( [model1, model3] );
expect( multiCollectionA.selected[model3.cid] ).not.toBeUndefined();
} );
it( "should deselect other selected models in a multi-select collection if they are shared with the single-select collection the new model is added to", function () {
singleCollectionA.reset( [model1, model2] );
expect( multiCollectionA.selected[model1.cid] ).toBeUndefined();
} );
it( "should leave other selected models untouched in a multi-select collection, provided they are not shared with the single-select collection the new model is added to", function () {
singleCollectionA.reset( [model1, model2] );
expect( multiCollectionA.selected[model3.cid] ).not.toBeUndefined();
} );
it( "should become deselected if it is not part of any other collections, and is being re-added to the original collection (single-select)", function () {
// By resetting the collection, the selected model is first removed, then re-added. If the model is not part of
// any other collection, it loses its 'selected' status while being removed.
//
// This is expected behaviour, and it serves a purpose. A reset, with models which are not shared by any other
// collection, should provide a clean slate.
multiCollectionA.reset();
singleCollectionA.reset( [model1] );
expect( model1.selected ).toBe( false );
expect( singleCollectionA.selected ).toBeUndefined();
} );
it( "should become deselected if it is not part of any other collections, and is being re-added to the original collection (multi-select)", function () {
// The model loses its 'selected' status while being removed. See comment above.
singleCollectionA.reset();
multiCollectionA.reset( [model1, model2] );
expect( model1.selected ).toBe( false );
expect( model2.selected ).toBe( false );
expect( multiCollectionA.selected ).toEqual( {} );
} );
it( 'should not trigger a selected event on the model (added to a single-select collection)', function () {
singleCollectionA.reset( [model1] );
expect( model1.trigger ).not.toHaveBeenCalledWithInitial( "selected" );
} );
it( 'should not trigger a selected event on the model (added to a multi-select collection)', function () {
multiCollectionA.reset( [model1] );
expect( model1.trigger ).not.toHaveBeenCalledWithInitial( "selected" );
} );
it( 'should not trigger a reselected event on the model (added to a single-select collection)', function () {
// For one, reset() is meant to suppress individual notifications. Also,
// the reselect event implies some sort of active 'select' action, which
// is not present here. See comments in the 'add' tests for more on the
// rationale.
singleCollectionA.reset( [model1] );
expect( model1.trigger ).not.toHaveBeenCalledWithInitial( "reselected" );
} );
it( 'should not trigger a reselected event on the model (added to a multi-select collection)', function () {
multiCollectionA.reset( [model1] );
expect( model1.trigger ).not.toHaveBeenCalledWithInitial( "reselected" );
} );
it( 'should not trigger a select:one event when added to a single-select collection', function () {
// reset() is meant to suppress individual notifications. Just like the add event, selection events are silenced.
// Whatever needs to be done, should be dealt with in the reset event handler.
singleCollectionA.reset( [model1] );
expect( singleCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "select:one" );
} );
it( 'should not trigger a reselect:one event when added to a single-select collection', function () {
singleCollectionA.reset( [model1] );
expect( singleCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "reselect:one" );
} );
it( 'should not trigger a select:some or select:all event when added to a multi-select collection', function () {
// For the rationale, see above.
multiCollectionA.reset( [model1] );
expect( multiCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "select:some" );
expect( multiCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "select:all" );
} );
it( 'should not trigger a reselect:any event when added to a multi-select collection', function () {
// For the rationale, see above.
multiCollectionA.reset( [model1] );
expect( multiCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "reselect:any" );
} );
it( 'should trigger a deselected event on the model when added to a singe-select collection together with other selected models, and it is not the last of them', function () {
singleCollectionA.reset( [model1, model2] );
expect( model1.trigger ).toHaveBeenCalledWith( "deselected", model1, { label: "selected" } );
} );
it( 'should not trigger a deselect:one event when added to a singe-select collection, even if multiple models with selected status are added, and all but the last one are deselected', function () {
singleCollectionA.reset( [model1, model2] );
expect( singleCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "deselect:one" );
} );
it( 'should not trigger a reselect:one event when added to a singe-select collection, even if multiple models with selected status are added, and all but the last one are deselected', function () {
singleCollectionA.reset( [model1, model2] );
expect( singleCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "reselect:one" );
} );
it( 'should trigger a select:some or select:none event when the reset is inducing a deselection in another multi-select collection', function () {
singleCollectionA.reset( [model1, model2] );
expect( multiCollectionA.trigger ).toHaveBeenCalledWith( "select:some", { selected: [], deselected: [model1] }, multiCollectionA, { label: "selected" } );
} );
it( 'should not trigger a reselect:one event on another single-select collection holding the model', function () {
singleCollectionA.reset( [model3] );
expect( singleCollectionB.trigger ).not.toHaveBeenCalledWithInitial( "reselect:one" );
} );
it( 'should not trigger a reselect:any event on another multi-select collection holding the model', function () {
singleCollectionA.reset( [model1] );
expect( multiCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "reselect:any" );
} );
it( 'should not trigger a reselect:any event on another multi-select collection, even when the reset is inducing a change (a deselection) there', function () {
singleCollectionA.reset( [model1, model2] );
expect( multiCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "reselect:any" );
} );
it( 'should not leak the internal backboneSubcall flag into the options of the reset event (single-select collection)', function () {
// To make this test resilient against naming changes of internal flags, we test the absence of **any**
// unexpected options. We simply expect the unmodified default reset options object, which contains
// options.previousModels only.
var resetListener = jasmine.createSpy( "resetListener" );
singleCollectionA.on( "reset", resetListener );
singleCollectionA.reset( [model1, model2] );
expect( resetListener ).toHaveBeenCalledOnce();
expect( resetListener ).toHaveBeenCalledWith( singleCollectionA, { previousModels: [model1] } );
} );
it( 'should not leak the internal backboneSubcall flag into the options of the reset event (multi-select collection)', function () {
// To make this test resilient against naming changes of internal flags, we test the absence of **any**
// unexpected options. See above.
var resetListener = jasmine.createSpy( "resetListener" );
multiCollectionA.on( "reset", resetListener );
multiCollectionA.reset( [model1, model2] );
expect( resetListener ).toHaveBeenCalledOnce();
expect( resetListener ).toHaveBeenCalledWith( multiCollectionA, { previousModels: [model1, model2, model3] } );
} );
it( 'should not leak the internal backboneSubcall flag into the options of the add event when reset is called with an explicit `silent: false` option (single-select collection)', function () {
// To make this test resilient against naming changes of internal flags, we test the absence of **any**
// unexpected options. We simply expect the unmodified default reset options object, which contains
// options.previousModels only.
// ATTN This Backbone behaviour may change at some point. If, in the future, silent: false no longer
// triggers an add event, the test can be removed.
var addListener = jasmine.createSpy( "addListener" );
singleCollectionA.on( "add", addListener );
singleCollectionA.reset( [model1, model2], { silent: false } );
expect( addListener ).toHaveBeenCalledTwice();
expect( addListener ).toHaveBeenCalledWith( model1, singleCollectionA, jasmine.any( Object ) );
expect( addListener ).toHaveBeenCalledWith( model2, singleCollectionA, jasmine.any( Object ) );
var allCallsAllArgs = addListener.calls.allArgs(),
allArgsByPosition = _.zip.apply( null, allCallsAllArgs ),
allOptionsObjects = allArgsByPosition[2] || [],
optionKeys = _.reduce( allOptionsObjects, function ( collected, optionsObject ) {
return _.union( collected, _.keys( optionsObject ) );
}, [] );
expect( optionKeys ).not.toContain( jasmine.stringMatching( /^@bbs:/ ) );
} );
it( 'should not leak the internal backboneSubcall flag into the options of the add event when reset is called with an explicit `silent: false` option (multi-select collection)', function () {
// Because of the flurry of semi-internal stuff showing up in the add event options here, a the full
// expected options object would be a brittle construct. We have to make sure that no internal flags are
// present, so we check for anything that matches the @bbs: prefix.
// ATTN This Backbone behaviour may change at some point. If, in the future, silent: false no longer
// triggers an add event, the test can be removed.
var addListener = jasmine.createSpy( "addListener" );
multiCollectionA.on( "add", addListener );
multiCollectionA.reset( [model1, model2], { silent: false } );
expect( addListener ).toHaveBeenCalledTwice();
expect( addListener ).toHaveBeenCalledWith( model1, multiCollectionA, jasmine.any( Object ) );
expect( addListener ).toHaveBeenCalledWith( model2, multiCollectionA, jasmine.any( Object ) );
var allCallsAllArgs = addListener.calls.allArgs(),
allArgsByPosition = _.zip.apply( null, allCallsAllArgs ),
allOptionsObjects = allArgsByPosition[2] || [],
optionKeys = _.reduce( allOptionsObjects, function ( collected, optionsObject ) {
return _.union( collected, _.keys( optionsObject ) );
}, [] );
expect( optionKeys ).not.toContain( jasmine.stringMatching( /^@bbs:/ ) );
} );
it( "should remain selected itself", function () {
singleCollectionA.reset( [model1] );
expect( model1.selected ).toBe( true );
} );
} );
describe( "when a selected model is added by resetting the collection, with options.silent enabled", function () {
var model1, model2, model3,
singleCollectionA, singleCollectionB, multiCollectionA;
beforeEach( function () {
model1 = new Model();
model2 = new Model();
model3 = new Model();
singleCollectionA = new SingleSelectCollection( [model1] );
singleCollectionB = new SingleSelectCollection( [model3] );
multiCollectionA = new MultiSelectCollection( [model1, model2, model3] );
model1.select();
model2.select();
model3.select();
spyOn( model1, "trigger" ).and.callThrough();
spyOn( model2, "trigger" ).and.callThrough();
spyOn( singleCollectionA, "trigger" ).and.callThrough();
spyOn( singleCollectionB, "trigger" ).and.callThrough();
spyOn( multiCollectionA, "trigger" ).and.callThrough();
} );
afterEach( function () {
singleCollectionA.close();
singleCollectionB.close();
multiCollectionA.close();
} );
it( "should be selected in a single-select collection it is added to", function () {
singleCollectionA.reset( [model1], { silent: true } );
expect( singleCollectionA.selected ).toBe( model1 );
} );
it( "should be selected in a single-select collection if multiple models with selected status are added, and it is the last of them", function () {
singleCollectionA.reset( [model1, model2], { silent: true } );
expect( model2.selected ).toBe( true );
expect( singleCollectionA.selected ).toBe( model2 );
} );
it( "should be deselected in a single-select collection if multiple models with selected status are added, and it is not the last of them", function () {
singleCollectionA.reset( [model1, model2], { silent: true } );
expect( model1.selected ).toBe( false );
} );
it( "should be added to the selected models in a multi-select collection (being the first of multiple selected models)", function () {
multiCollectionA.reset( [model1, model3], { silent: true } );
expect( multiCollectionA.selected[model1.cid] ).not.toBeUndefined();
} );
it( "should be added to the selected models in a multi-select collection (being the last of multiple selected models)", function () {
multiCollectionA.reset( [model1, model3], { silent: true } );
expect( multiCollectionA.selected[model3.cid] ).not.toBeUndefined();
} );
it( "should deselect other selected models in a multi-select collection if they are shared with the single-select collection the new model is added to", function () {
singleCollectionA.reset( [model1, model2], { silent: true } );
expect( multiCollectionA.selected[model1.cid] ).toBeUndefined();
} );
it( "should leave other selected models untouched in a multi-select collection, provided they are not shared with the single-select collection the new model is added to", function () {
singleCollectionA.reset( [model1, model2], { silent: true } );
expect( multiCollectionA.selected[model3.cid] ).not.toBeUndefined();
} );
it( "should become deselected if it is not part of any other collections, and is being re-added to the original collection (single-select)", function () {
// By resetting the collection, the selected model is first removed, then re-added. If the model is not part of
// any other collection, it loses its 'selected' status while being removed.
//
// This is expected behaviour, and it serves a purpose. A reset, with models which are not shared by any other
// collection, should provide a clean slate.
multiCollectionA.reset( null, { silent: true } );
singleCollectionA.reset( [model1], { silent: true } );
expect( model1.selected ).toBe( false );
expect( singleCollectionA.selected ).toBeUndefined();
} );
it( "should become deselected if it is not part of any other collections, and is being re-added to the original collection (multi-select)", function () {
// The model loses its 'selected' status while being removed. See comment above.
singleCollectionA.reset( null, { silent: true } );
multiCollectionA.reset( [model1, model2], { silent: true } );
expect( model1.selected ).toBe( false );
expect( model2.selected ).toBe( false );
expect( multiCollectionA.selected ).toEqual( {} );
} );
it( 'should not trigger an event on the model (added to a single-select collection)', function () {
singleCollectionA.reset( [model1], { silent: true } );
expect( model1.trigger ).not.toHaveBeenCalled_ignoringInternalEvents();
} );
it( 'should not trigger an event on the model (added to a multi-select collection)', function () {
multiCollectionA.reset( [model1], { silent: true } );
expect( model1.trigger ).not.toHaveBeenCalled_ignoringInternalEvents();
} );
it( 'should not trigger a collection event when added to a single-select collection', function () {
// We ignore the @bbs:reset:silent event here, which is always fired during an otherwise silent reset.
singleCollectionA.reset( [model1], { silent: true } );
expect( singleCollectionA.trigger ).not.toHaveBeenCalled_ignoringInternalEventsAnd( "@bbs:reset:silent" );
} );
it( 'should not trigger a collection event when added to a multi-select collection', function () {
// We ignore the @bbs:reset:silent event here, which is always fired during an otherwise silent reset.
multiCollectionA.reset( [model1], { silent: true } );
expect( multiCollectionA.trigger ).not.toHaveBeenCalled_ignoringInternalEventsAnd( "@bbs:reset:silent" );
} );
it( 'should not trigger an event on another single-select collection holding the model', function () {
singleCollectionA.reset( [model3], { silent: true } );
expect( singleCollectionB.trigger ).not.toHaveBeenCalled_ignoringInternalEvents();
} );
it( 'should not trigger an event on another multi-select collection holding the model', function () {
singleCollectionA.reset( [model1], { silent: true } );
expect( multiCollectionA.trigger ).not.toHaveBeenCalled_ignoringInternalEvents();
} );
it( 'should not trigger an event on the model when added to a singe-select collection together with other selected models, and it is not the last of them', function () {
singleCollectionA.reset( [model1, model2], { silent: true } );
expect( model1.trigger ).not.toHaveBeenCalled_ignoringInternalEvents();
} );
it( 'should not trigger a collection event when added to a singe-select collection, even if multiple models with selected status are added, and all but the last one are deselected', function () {
// We ignore the @bbs:reset:silent event here, which is always fired during an otherwise silent reset.
singleCollectionA.reset( [model1, model2], { silent: true } );
expect( singleCollectionA.trigger ).not.toHaveBeenCalled_ignoringInternalEventsAnd( "@bbs:reset:silent" );
} );
it( 'should not trigger a multi-select collection event when the reset is inducing a deselection in another multi-select collection', function () {
singleCollectionA.reset( [model1, model2], { silent: true } );
expect( multiCollectionA.trigger ).not.toHaveBeenCalled_ignoringInternalEvents();
} );
it( "should remain selected itself", function () {
singleCollectionA.reset( [model1], { silent: true } );
expect( model1.selected ).toBe( true );
} );
} );
describe( "when a selected model is removed by resetting the collection", function () {
var model1, model2,
singleCollectionA, multiCollectionA;
beforeEach( function () {
model1 = new Model();
model2 = new Model();
singleCollectionA = new SingleSelectCollection( [model1, model2] );
multiCollectionA = new MultiSelectCollection( [model1, model2] );
model1.select();
spyOn( model1, "trigger" ).and.callThrough();
spyOn( singleCollectionA, "trigger" ).and.callThrough();
spyOn( multiCollectionA, "trigger" ).and.callThrough();
} );
afterEach( function () {
singleCollectionA.close();
multiCollectionA.close();
} );
it( "should no longer be selected in a single-select collection", function () {
singleCollectionA.reset();
expect( singleCollectionA.selected ).toBeUndefined();
} );
it( "should no longer be selected in a multi-select collection", function () {
multiCollectionA.reset();
expect( multiCollectionA.selected[model1.cid] ).toBeUndefined();
} );
it( "should no longer be selected if removed from all collections", function () {
singleCollectionA.reset();
multiCollectionA.reset();
expect( model1.selected ).toBe( false );
} );
it( "should remain selected in those collections it has not been removed from (removed from single-select)", function () {
singleCollectionA.reset();
expect( multiCollectionA.selected[model1.cid] ).not.toBeUndefined();
} );
it( "should remain selected in those collections it has not been removed from (removed from multi-select)", function () {
multiCollectionA.reset();
expect( singleCollectionA.selected ).toBe( model1 );
} );
it( "should remain selected itself if not removed from all collections", function () {
singleCollectionA.reset();
expect( model1.selected ).toBe( true );
} );
it( 'should not trigger a deselected event on the model when it is still part of another collection (removed from single-select)', function () {
singleCollectionA.reset();
expect( model1.trigger ).not.toHaveBeenCalledWithInitial( "deselected" );
} );
it( 'should not trigger a deselected event on the model when it is still part of another collection (removed from multi-select)', function () {
multiCollectionA.reset();
expect( model1.trigger ).not.toHaveBeenCalledWithInitial( "deselected" );
} );
it( 'should trigger a deselected event on the model when removed from all collections (single-select collection last)', function () {
multiCollectionA.reset();
singleCollectionA.reset();
expect( model1.trigger ).toHaveBeenCalledWith( "deselected", model1, { label: "selected" } );
} );
it( 'should trigger a deselected event on the model when removed from all collections (multi-select collection last)', function () {
singleCollectionA.reset();
multiCollectionA.reset();
expect( model1.trigger ).toHaveBeenCalledWith( "deselected", model1, { label: "selected" } );
} );
it( 'should not trigger a deselect:one event when removed from a single-select collection', function () {
// reset() is meant to suppress individual notifications. Just like the add event, selection events are silenced.
// Whatever needs to be done, should be dealt with in the reset event handler.
singleCollectionA.reset();
expect( singleCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "deselect:one" );
} );
it( 'should not trigger a select:none event when removed from a multi-select collection', function () {
// For the rationale, see above.
multiCollectionA.reset();
expect( multiCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "select:none" );
} );
it( 'should not trigger a deselect:one event when removed from all collections, single-select collection last', function () {
// See above.
multiCollectionA.reset();
singleCollectionA.reset();
expect( singleCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "deselect:one" );
} );
it( 'should not trigger a select:none event when removed from all collections, multi-select collection last', function () {
// See above.
singleCollectionA.reset();
multiCollectionA.reset();
expect( multiCollectionA.trigger ).not.toHaveBeenCalledWithInitial( "select:none" );
} );
} );
describe( "when a selected model is removed by resetting the collection, with options.silent enabled", function () {
var model1, model2,
singleCollectionA, multiCollectionA;
beforeEach( function () {
model1 = new Model();
model2 = new Model();
singleCollectionA = new SingleSelectCollection( [model1, model2] );
multiCollectionA = new MultiSelectCollection( [model1, model2] );
model1.select();
spyOn( model1, "trigger" ).and.callThrough();
spyOn( singleCollectionA, "trigger" ).and.callThrough();
spyOn( multiCollectionA, "trigger" ).and.callThrough();
} );
afterEach( function () {
singleCollectionA.close();
multiCollectionA.close();
} );
it( "should no longer be selected in a single-select collection", function () {
singleCollectionA.reset( null, { silent: true } );
expect( singleCollectionA.selected ).toBeUndefined();
} );
it( "should no longer be selected in a multi-select collection", function () {
multiCollectionA.reset( null, { silent: true } );
expect( multiCollectionA.selected[model1.cid] ).toBeUndefined();
} );
it( "should no longer be selected if removed from all collections", function () {
singleCollectionA.reset( null, { silent: true } );
multiCollectionA.reset( null, { silent: true } );
expect( model1.selected ).toBe( false );
} );
it( "should remain selected in those collections it has not been removed from (removed from single-select)", function () {
singleCollectionA.reset( null, { silent: true } );
expect( multiCollectionA.selected[model1.cid] ).not.toBeUndefined();
} );
it( "should remain selected in those collections it has not been removed from (removed from multi-select)", function () {
multiCollectionA.reset( null, { silent: true } );
expect( singleCollectionA.selected ).toBe( model1 );
} );
it( "should remain selected itself if not removed from all collections", function () {
singleCollectionA.reset( null, { silent: true } );
expect( model1.selected ).toBe( true );
} );
it( 'should not trigger an event on the model while being removed from all collections (single-select collection last)', function () {
multiCollectionA.reset( null, { silent: true } );
singleCollectionA.reset( null, { silent: true } );
expect( model1.trigger ).not.toHaveBeenCalled_ignoringInternalEvents();
} );
it( 'should not trigger an event on the model while being removed from all collections (multi-select collection last)', function () {
singleCollectionA.reset( null, { silent: true } );
multiCollectionA.reset( null, { silent: true } );
expect( model1.trigger ).not.toHaveBeenCalled_ignoringInternalEvents();
} );
it( 'should not trigger a single-select collection event while being removed from all collections, single-select collection last', function () {
// We ignore the @bbs:reset:silent event here, which is always fired during an otherwise silent reset.
multiCollectionA.reset( null, { silent: true } );
singleCollectionA.reset( null, { silent: true } );
expect( singleCollectionA.trigger ).not.toHaveBeenCalled_ignoringInternalEventsAnd( "@bbs:reset:silent" );
} );
it( 'should not trigger a multi-select collection event while being removed from all collections, multi-select collection last', function () {
// We ignore the @bbs:reset:silent event here, which is always fired during an otherwise silent reset.
singleCollectionA.reset( null, { silent: true } );
multiCollectionA.reset( null, { silent: true } );
expect( multiCollectionA.trigger ).not.toHaveBeenCalled_ignoringInternalEventsAnd( "@bbs:reset:silent" );
} );
} );
describe( 'The event @bbs:reset:silent', function () {
var newPlainModel, newSelectedModel, existingModel, collection;
beforeEach( function () {
newPlainModel = new Backbone.Model();
newSelectedModel = new Model();
existingModel = new Model();
existingModel.select();
newSelectedModel.select();
} );
afterEach( function () {
collection.close();
} );
describe( 'in a Select.One collection', function () {
beforeEach( function () {
collection = new SingleSelectCollection( [existingModel] );
spyOn( collection, "trigger" ).and.callThrough();
} );
it( 'is fired on reset() with options.silent enabled', function () {
collection.reset( null, { silent: true } );
expect( collection.trigger ).toHaveBeenCalledOnceForEvents( "@bbs:reset:silent" );
} );
it( 'passes along the collection as first argument', function () {
collection.reset( null, { silent: true } );
expect( collection.trigger ).toHaveBeenCalledWithInitial( "@bbs:reset:silent", collection );
} );
it( 'passes along an options object as second argument, containing the previous models in options.previousModels', function () {
collection.reset( null, { silent: true } );
expect( collection.trigger ).toHaveBeenCalledWithInitial( "@bbs:reset:silent", collection, { silent: true, previousModels: [existingModel] } );
} );
it( 'is fired after reset() has done its job', function () {
var modelsInCollection;
collection.on( "@bbs:reset:silent", function ( cbCollection ) {
modelsInCollection = cbCollection.models.slice();
} );
collection.reset( [newPlainModel], { silent: true } );
expect( modelsInCollection ).toEqual( [newPlainModel] );
} );
it( 'is fired after new models have been processed by Backbone.Select, ie after selections have been updated and the Select.Me mixin has been applied to plain models', function () {
var selectedInCollection, pickyType;
collection.on( "@bbs:reset:silent", function ( cbCollection ) {
selectedInCollection = cbCollection.selected;
pickyType = cbCollection.first() && cbCollection.first()._pickyType;
} );
collection.reset( [newSelectedModel], { silent: true } );
expect( selectedInCollection ).toBe( newSelectedModel );
collection.reset( [newPlainModel], { silent: true } );
expect( pickyType ).toEqual( "Backbone.Select.Me" );
} );
it( 'is not fired on reset() if options.silent is not enabled', function () {
collection.reset();
expect( collection.trigger ).not.toHaveBeenCalledForEvents( "@bbs:reset:silent" );
} );
} );
describe( 'in a Select.Many collection', function () {
beforeEach( function () {
collection = new MultiSelectCollection( [existingModel] );
spyOn( collection, "trigger" ).and.callThrough();
} );
it( 'is fired on reset() with options.silent enabled', function () {
collection.reset( null, { silent: true } );
expect( collection.trigger ).toHaveBeenCalledOnceForEvents( "@bbs:reset:silent" );
} );
it( 'passes along the collection as first argument', function () {
collection.reset( null, { silent: true } );
expect( collection.trigger ).toHaveBeenCalledWithInitial( "@bbs:reset:silent", collection );
} );
it( 'passes along an options object as second argument, containing the previous models in options.previousModels', function () {
collection.reset( null, { silent: true } );
expect( collection.trigger ).toHaveBeenCalledWithInitial( "@bbs:reset:silent", collection, { silent: true, previousModels: [existingModel] } );
} );
it( 'is fired after reset() has done its job', function () {
var modelsInCollection;
collection.on( "@bbs:reset:silent", function ( cbCollection ) {
modelsInCollection = cbCollection.models.slice();
} );
collection.reset( [newPlainModel], { silent: true } );
expect( modelsInCollection ).toEqual( [newPlainModel] );
} );
it( 'is fired after new models have been processed by Backbone.Select, ie after selections have been updated and the Select.Me mixin has been applied to plain models', function () {
var selectedInCollection, pickyType;
collection.on( "@bbs:reset:silent", function ( cbCollection ) {
selectedInCollection = _.clone( cbCollection.selected );
pickyType = cbCollection.first() && cbCollection.first()._pickyType;
} );
collection.reset( [newSelectedModel], { silent: true } );
expect( _.values( selectedInCollection ) ).toEqual( [newSelectedModel] );
collection.reset( [newPlainModel], { silent: true } );
expect( pickyType ).toEqual( "Backbone.Select.Me" );
} );
it( 'is not fired on reset() if options.silent is not enabled', function () {
collection.reset();
expect( collection.trigger ).not.toHaveBeenCalledForEvents( "@bbs:reset:silent" );
} );
} );
} );
} );