Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor and reorganize view trigger and entity events unit tests #3535

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
229 changes: 133 additions & 96 deletions test/unit/view.entity-events.spec.js
Original file line number Diff line number Diff line change
@@ -1,237 +1,274 @@
import _ from 'underscore';
import Backbone from 'backbone';
import View from '../../src/view';

describe('view entity events', function() {
'use strict';

let model;
let collection;
let fooStub;
let barStub;
let modelEventsStub
let collectionEventsStub;

beforeEach(function() {
this.model = new Backbone.Model();
this.collection = new Backbone.Collection();
model = new Backbone.Model();
collection = new Backbone.Collection();

this.fooStub = this.sinon.stub();
this.barStub = this.sinon.stub();
fooStub = this.sinon.stub();
barStub = this.sinon.stub();

this.modelEventsStub = this.sinon.stub().returns({'foo': this.fooStub});
this.collectionEventsStub = this.sinon.stub().returns({'bar': this.barStub});
modelEventsStub = this.sinon.stub().returns({'foo': fooStub});
collectionEventsStub = this.sinon.stub().returns({'bar': barStub});
});

describe('when a view has string-based model and collection event configuration', function() {
let fooOneStub;
let fooTwoStub;
let barOneStub;
let barTwoStub;
let TestView;

beforeEach(function() {
this.fooOneStub = this.sinon.stub();
this.fooTwoStub = this.sinon.stub();
this.barOneStub = this.sinon.stub();
this.barTwoStub = this.sinon.stub();
fooOneStub = this.sinon.stub();
fooTwoStub = this.sinon.stub();
barOneStub = this.sinon.stub();
barTwoStub = this.sinon.stub();

this.View = Marionette.View.extend({
TestView = View.extend({
modelEvents: {'foo': 'fooOne fooTwo'},
collectionEvents: {'bar': 'barOne barTwo'},
fooOne: this.fooOneStub,
fooTwo: this.fooTwoStub,
barOne: this.barOneStub,
barTwo: this.barTwoStub
fooOne: fooOneStub,
fooTwo: fooTwoStub,
barOne: barOneStub,
barTwo: barTwoStub
});

this.view = new this.View({
model: this.model,
collection: this.collection
new TestView({
model: model,
collection: collection
});
});

it('should wire up model events', function() {
this.model.trigger('foo');
expect(this.fooOneStub).to.have.been.calledOnce;
expect(this.fooTwoStub).to.have.been.calledOnce;
model.trigger('foo');
expect(fooOneStub).to.have.been.calledOnce;
expect(fooTwoStub).to.have.been.calledOnce;
});

it('should wire up collection events', function() {
this.collection.trigger('bar');
expect(this.barOneStub).to.have.been.calledOnce;
expect(this.barTwoStub).to.have.been.calledOnce;
collection.trigger('bar');
expect(barOneStub).to.have.been.calledOnce;
expect(barTwoStub).to.have.been.calledOnce;
});
});

describe('when a view has function-based model and collection event configuration', function() {
let TestView;

beforeEach(function() {
this.View = Marionette.View.extend({
modelEvents: {'foo': this.fooStub},
collectionEvents: {'bar': this.barStub}
TestView = View.extend({
modelEvents: {'foo': fooStub},
collectionEvents: {'bar': barStub}
});

this.view = new this.View({
model: this.model,
collection: this.collection
new TestView({
model: model,
collection: collection
});
});

it('should wire up model events', function() {
this.model.trigger('foo');
expect(this.fooStub).to.have.been.calledOnce;
model.trigger('foo');
expect(fooStub).to.have.been.calledOnce;
});

it('should wire up collection events', function() {
this.collection.trigger('bar');
expect(this.barStub).to.have.been.calledOnce;
collection.trigger('bar');
expect(barStub).to.have.been.calledOnce;
});
});

describe('when a view has model event config with a specified handler method that doesnt exist', function() {
beforeEach(function() {
var suite = this;
let MyView;
let getBadViewInstance;

this.View = Marionette.View.extend({
beforeEach(function() {
MyView = View.extend({
modelEvents: {foo: 'doesNotExist'},
model: this.model
model: model
});

this.getBadViewInstance = function() {
return new suite.View();
getBadViewInstance = function() {
return new MyView();
};
});

it('should error when method doesnt exist', function() {
var errorMessage = 'Method "doesNotExist" was configured as an event handler, but does not exist.';
expect(this.getBadViewInstance).to.throw(errorMessage);
const errorMessage = 'Method "doesNotExist" was configured as an event handler, but does not exist.';
expect(getBadViewInstance).to.throw(errorMessage);
});
});

describe('when configuring entity events with a function', function() {
let TestView;
let view;

beforeEach(function() {
this.View = Marionette.View.extend({
modelEvents: this.modelEventsStub,
collectionEvents: this.collectionEventsStub
TestView = View.extend({
modelEvents: modelEventsStub,
collectionEvents: collectionEventsStub
});

this.view = new this.View({
model: this.model,
collection: this.collection
view = new TestView({
model: model,
collection: collection
});
});

it('should trigger the model event', function() {
this.view.model.trigger('foo');
expect(this.fooStub).to.have.been.calledOnce;
view.model.trigger('foo');
expect(fooStub).to.have.been.calledOnce;
});

it('should trigger the collection event', function() {
this.view.collection.trigger('bar');
expect(this.barStub).to.have.been.calledOnce;
view.collection.trigger('bar');
expect(barStub).to.have.been.calledOnce;
});
});

describe('when undelegating entity events on a view', function() {
let TestView;
let view;

beforeEach(function() {
this.View = Marionette.View.extend({
TestView = View.extend({
modelEvents: {'foo': 'foo'},
collectionEvents: {'bar': 'bar'},
foo: this.fooStub,
bar: this.barStub
foo: fooStub,
bar: barStub
});

this.view = new this.View({
model: this.model,
collection: this.collection
view = new TestView({
model: model,
collection: collection
});

this.sinon.spy(this.view, 'undelegateEntityEvents');
this.view.undelegateEntityEvents();
this.sinon.spy(view, 'undelegateEntityEvents');
view.undelegateEntityEvents();

this.model.trigger('foo');
this.collection.trigger('bar');
model.trigger('foo');
collection.trigger('bar');
});

it('should undelegate the model events', function() {
expect(this.fooStub).not.to.have.been.calledOnce;
expect(fooStub).not.to.have.been.calledOnce;
});

it('should undelegate the collection events', function() {
expect(this.barStub).not.to.have.been.calledOnce;
expect(barStub).not.to.have.been.calledOnce;
});

it('should return the view', function() {
expect(this.view.undelegateEntityEvents).to.have.returned(this.view);
expect(view.undelegateEntityEvents).to.have.returned(view);
});
});

describe('when undelegating events on a view, delegating them again, and then triggering a model event', function() {
let TestView;
let view;

beforeEach(function() {
this.View = Marionette.View.extend({
TestView = View.extend({
modelEvents: {'foo': 'foo'},
collectionEvents: {'bar': 'bar'},
foo: this.fooStub,
bar: this.barStub
foo: fooStub,
bar: barStub
});

this.view = new this.View({
model: this.model,
collection: this.collection
view = new TestView({
model: model,
collection: collection
});

this.view.undelegateEntityEvents();
this.sinon.spy(this.view, 'delegateEntityEvents');
this.view.delegateEntityEvents();
view.undelegateEntityEvents();
this.sinon.spy(view, 'delegateEntityEvents');
view.delegateEntityEvents();
});

it('should fire the model event once', function() {
this.model.trigger('foo');
expect(this.fooStub).to.have.been.calledOnce;
model.trigger('foo');
expect(fooStub).to.have.been.calledOnce;
});

it('should fire the collection event once', function() {
this.collection.trigger('bar');
expect(this.barStub).to.have.been.calledOnce;
collection.trigger('bar');
expect(barStub).to.have.been.calledOnce;
});

it('should return the view from delegateEntityEvents', function() {
expect(this.view.delegateEntityEvents).to.have.returned(this.view);
expect(view.delegateEntityEvents).to.have.returned(view);
});
});

describe('when View bound to modelEvent replaces region with new view', function() {
let Layout;
let TestView;
let layoutView;
let itemViewOne;
let itemViewTwo;

beforeEach(function() {
this.Layout = Marionette.View.extend({
Layout = View.extend({
template: _.template('<div id="child"></div>'),
regions: {child: '#child'},
modelEvents: {'baz': 'foo'},
foo: this.fooStub
foo: fooStub
});

this.View = Marionette.View.extend({
TestView = View.extend({
template: _.template('bar'),
modelEvents: {'baz': 'bar'},
bar: this.barStub
bar: barStub
});

this.layoutView = new this.Layout({model: this.model});
this.itemViewOne = new this.View({model: this.model});
this.itemViewTwo = new this.View({model: this.model});
layoutView = new Layout({model: model});
itemViewOne = new TestView({model: model});
itemViewTwo = new TestView({model: model});

this.layoutView.render();
this.layoutView.getRegion('child').show(this.itemViewOne);
this.layoutView.getRegion('child').show(this.itemViewTwo);
layoutView.render();
layoutView.getRegion('child').show(itemViewOne);
layoutView.getRegion('child').show(itemViewTwo);

this.model.trigger('baz');
model.trigger('baz');
});

it('should leave the layoutView\'s modelEvent binded', function() {
expect(this.fooStub).to.have.been.calledOnce;
expect(fooStub).to.have.been.calledOnce;
});

it('should unbind the previous child view\'s modelEvents', function() {
expect(this.barStub).to.have.been.calledOnce;
expect(barStub).to.have.been.calledOnce;
});
});

// Fixes https://github.com/marionettejs/backbone.marionette/issues/3527
describe('when entity events are added in initialize', function() {
let view;

it('should not undelegate them', function() {
const View = Marionette.View.extend({
let TestView = View.extend({
template: false,
initialize() {
this.listenTo(this.model, 'foo', this.onFoo);
this.listenTo(model, 'foo', this.onFoo);
},
onFoo: this.sinon.stub()
});

const model = new Backbone.Model();
model = new Backbone.Model();

const view = new View({ model });
view = new TestView({ model });

model.trigger('foo');

Expand Down
Loading