extjs-gpl
Version:
GPL licensed version of Sencha Ext JS
1,157 lines (1,002 loc) • 120 kB
JavaScript
describe("Ext.data.schema.ManyToOne", function() {
var schema, Post, Thread, threadRole, postRole,
threadCalled = false,
postCalled = false;
function definePost(refCfg) {
Post = Ext.define('spec.Post', {
extend: 'Ext.data.Model',
fields: ['id', 'content', {
name: 'threadId',
reference: Ext.apply({
type: 'Thread'
}, refCfg)
}],
constructor: function() {
postCalled = true;
this.callParent(arguments);
}
});
threadRole = Post.associations.thread;
postRole = Thread.associations.posts;
}
function complete(data, status) {
Ext.Ajax.mockComplete({
status: status || 200,
responseText: Ext.JSON.encode(data)
});
}
beforeEach(function() {
threadCalled = postCalled = false;
MockAjaxManager.addMethods();
schema = Ext.data.Model.schema;
schema.setNamespace('spec');
Thread = Ext.define('spec.Thread', {
extend: 'Ext.data.Model',
fields: ['id', 'title'],
constructor: function() {
threadCalled = true;
this.callParent(arguments);
}
});
});
afterEach(function() {
MockAjaxManager.removeMethods();
Ext.undefine('spec.Post');
Ext.undefine('spec.Thread');
schema.clear(true);
Post = postRole = Thread = threadRole = schema = null;
threadCalled = postCalled = false;
});
describe("Model.associations", function() {
it("should have an association role on each model", function() {
definePost();
expect(Post.associations.thread).toBeDefined();
expect(Thread.associations.posts).toBeDefined();
});
it("should have a reference back to the association for each role", function() {
definePost();
expect(Post.associations.thread.association).toBe(Thread.associations.posts.association);
expect(Thread.associations.posts.association.isManyToOne).toBe(true);
});
});
describe("association default config", function() {
var assoc;
beforeEach(function() {
definePost();
assoc = threadRole.association;
});
it("should have a schema set", function() {
expect(assoc.schema).toBe(schema);
});
it("should have the reference field set", function() {
expect(assoc.field).toBe(Post.getField('threadId'));
});
it("should have the left part be set to the key holder", function() {
expect(assoc.left).toBe(postRole);
});
it("should set definedBy to the key holder", function() {
expect(assoc.definedBy).toBe(Post);
});
it("should have the right part be set to the non key holder", function() {
expect(assoc.right).toBe(threadRole);
});
it("should have the owner as null", function() {
expect(assoc.owner).toBeNull();
});
it("should set the assoc name to {PluralKeyHolder}By{SingluarOther}", function() {
expect(assoc.name).toBe('ThreadPosts');
});
});
describe("left", function() {
beforeEach(function() {
definePost();
});
it("should set the role to be plural lowercase & the type to be the entity name", function() {
expect(postRole.role).toBe('posts');
expect(postRole.type).toBe('Post');
});
it("should set the inverse role to the right", function() {
expect(postRole.inverse).toBe(threadRole);
});
it("should set the entity", function() {
expect(postRole.cls).toBe(Post);
});
});
describe("right", function() {
beforeEach(function() {
definePost();
});
it("should set the role to be singular lowercase & the type to be the entity name", function() {
expect(threadRole.role).toBe('thread');
expect(threadRole.type).toBe('Thread');
});
it("should set the inverse role to the left", function() {
expect(threadRole.inverse).toBe(postRole);
});
it("should set the entity", function() {
expect(threadRole.cls).toBe(Thread);
});
});
describe("configuring", function() {
it("should set an association name", function() {
definePost({
association: 'CustomName'
});
expect(postRole.association.name).toBe('CustomName');
});
it("should set the owner based on the child param", function() {
definePost({
child: true
});
expect(postRole.association.owner).toBe(postRole);
expect(postRole.owner).toBe(true);
expect(threadRole.owner).toBe(false);
});
it("should set the owner based on the parent param", function() {
definePost({
parent: true
});
expect(postRole.association.owner).toBe(threadRole);
expect(threadRole.owner).toBe(true);
expect(postRole.owner).toBe(false);
});
it("should be able to set a custom role", function() {
definePost({
role: 'foo'
});
threadRole = Post.associations.foo;
expect(threadRole.association.name).toBe('ThreadFooPosts');
expect(threadRole.role).toBe('foo');
});
describe("inverse", function() {
it("should set with a string", function() {
definePost({
inverse: 'foo'
});
postRole = Thread.associations.foo;
expect(postRole.association.name).toBe('ThreadFoo');
expect(postRole.role).toBe('foo');
});
it("should set with an object", function() {
definePost({
inverse: {
role: 'foo'
}
});
postRole = Thread.associations.foo;
expect(postRole.association.name).toBe('ThreadFoo');
expect(postRole.role).toBe('foo');
});
});
});
describe("model decoration", function() {
it("should generate a getter on the key holder", function() {
definePost();
expect(typeof Post.prototype.getThread).toBe('function');
});
it("should generate a setter on the key holder", function() {
definePost();
expect(typeof Post.prototype.setThread).toBe('function');
});
it("should define a getter on the inverse", function() {
definePost();
expect(typeof Thread.prototype.posts).toBe('function');
});
it("should allow a custom getter name on the key holder", function() {
definePost({
inverse: {
getterName: 'getFoo'
}
});
expect(typeof Thread.prototype.getFoo).toBe('function');
});
it("should allow a custom setter name on the key holder", function() {
definePost({
setterName: 'setFoo'
});
expect(typeof Post.prototype.setFoo).toBe('function');
});
it("should allow a custom getter name on the inverse", function() {
definePost({
getterName: 'ghosts'
});
expect(typeof Post.prototype.ghosts).toBe('function');
});
it("should decorate the model based on the role", function() {
var OtherPost = Ext.define('spec.OtherPost', {
extend: 'Ext.data.Model',
fields: ['id', 'name', {
name: 'threadAId',
reference: {
type: 'Thread',
role: 'ThreadA'
}
}, {
name: 'threadBId',
reference: {
type: 'Thread',
role: 'ThreadB'
}
}]
});
expect(typeof OtherPost.prototype.getThreadA).toBe('function');
expect(typeof OtherPost.prototype.getThreadB).toBe('function');
Ext.undefine('spec.OtherPost');
});
});
describe("subclassing", function() {
// Post
describe("the left", function() {
var SubPost;
beforeEach(function() {
definePost();
SubPost = Ext.define('spec.SubPost', {
extend: 'spec.Post'
});
});
afterEach(function() {
Ext.undefine('spec.SubPost');
SubPost = null;
});
it("should still have the original association", function() {
var inverse = Post.associations.thread.inverse;
expect(inverse.role).toBe('posts');
expect(inverse.cls).toBe(Post);
});
it("should inherit the association from the parent and modify the relevant classes", function() {
var inverse = SubPost.associations.thread.inverse;
expect(inverse.role).toBe('subPosts');
expect(inverse.cls).toBe(SubPost);
});
});
// Thread
describe("the right", function() {
var SubThread;
beforeEach(function() {
definePost();
SubThread = Ext.define('spec.SubThread', {
extend: 'spec.Thread'
});
});
it("should not have any associations", function() {
expect(SubThread.associations).toEqual({});
});
});
});
describe("nested loading", function() {
it("should infer the key when using remoteFilter: false", function() {
definePost({
inverse: {
storeConfig: {
remoteFilter: false
}
}
});
var thread = Thread.load(1);
complete({
id: 1,
posts: [{
id: 101
}, {
id: 102
}]
});
var posts = thread.posts();
expect(posts.getAt(0).get('threadId')).toBe(1);
expect(posts.getAt(0).dirty).toBe(false);
expect(posts.getAt(1).get('threadId')).toBe(1);
expect(posts.getAt(1).dirty).toBe(false);
expect(posts.getRemoteFilter()).toBe(false);
});
it("should delete the many from the data collection", function() {
definePost();
var thread = Thread.load(1);
complete({
id: 1,
posts: [{
id: 101
}, {
id: 102
}]
});
expect(thread.get('posts')).toBeUndefined();
expect(thread.posts().getCount()).toBe(2);
});
it("should delete the one from the data collection", function() {
definePost();
var post = Post.load(101);
complete({
id: 101,
thread: {
id: 1
}
});
expect(post.get('thread')).toBeUndefined();
expect(post.getThread().getId()).toBe(1);
});
it("should not pollute the reader when reading nested data of the same type", function() {
function getData() {
return {
records: [{
id: 1,
parentId: null,
children: [{
id: 101,
parentId: 1
}, {
id: 102,
parentId: 1
}]
}]
};
}
Ext.define('spec.Node', {
extend: 'Ext.data.Model',
fields: [{
name: 'parentId',
reference: {
type: 'Node',
inverse: 'children'
}
}],
proxy: {
type: 'ajax',
reader: {
type: 'json',
rootProperty: 'records'
}
}
});
var store = new Ext.data.Store({
// Always want immediate load
asynchronousLoad: false,
model: 'Node'
});
store.load();
complete(getData());
expect(store.first().children().getCount()).toBe(2);
store.load();
complete(getData());
expect(store.first().children().getCount()).toBe(2);
store.destroy();
Ext.undefine('spec.Node');
});
describe("complete", function() {
it("should mark the store complete if there are records", function() {
definePost();
var store = new Ext.data.Store({
model: Thread
});
store.loadRawData([{
id: 1,
posts: [{
id: 101,
content: 'Foo'
}, {
id: 102,
content: 'Bar'
}]
}]);
expect(store.first().posts().complete).toBe(true);
});
it("should mark the store complete if a root exists and there are no records", function() {
definePost();
var store = new Ext.data.Store({
model: Thread
});
store.loadRawData([{
id: 1,
posts: []
}]);
expect(store.first().posts().complete).toBe(true);
});
it("should not mark the store complete if a root doesn't exist", function() {
definePost();
var store = new Ext.data.Store({
model: Thread
});
store.loadRawData([{
id: 1
}]);
expect(store.first().posts().complete).toBe(false);
});
});
describe("key inference", function() {
describe("without session", function() {
beforeEach(function() {
definePost();
});
it("should infer the key from the parent", function() {
var thread = Thread.load(1);
complete({
id: 1,
posts: [{
id: 101
}, {
id: 102
}]
});
var posts = thread.posts();
expect(posts.getCount()).toBe(2);
expect(posts.getAt(0).getId()).toBe(101);
expect(posts.getAt(0).get('threadId')).toBe(1);
expect(posts.getAt(0).dirty).toBe(false);
expect(posts.getAt(1).getId()).toBe(102);
expect(posts.getAt(1).get('threadId')).toBe(1);
expect(posts.getAt(1).dirty).toBe(false);
});
it("should infer the key when loading the store, not nested", function() {
var thread = Thread.load(1);
complete({
id: 1
});
var posts = thread.posts();
posts.load();
complete([{
id: 101
}, {
id: 102
}]);
expect(posts.getCount()).toBe(2);
expect(posts.getAt(0).getId()).toBe(101);
expect(posts.getAt(0).get('threadId')).toBe(1);
expect(posts.getAt(0).dirty).toBe(false);
expect(posts.getAt(1).getId()).toBe(102);
expect(posts.getAt(1).get('threadId')).toBe(1);
expect(posts.getAt(1).dirty).toBe(false);
});
});
describe("with session", function() {
var session;
beforeEach(function() {
definePost();
session = new Ext.data.Session();
});
afterEach(function() {
session.destroy();
session = null;
});
it("should favour an existing reference", function() {
var post = session.createRecord('Post', {
id: 101,
threadId: 3
});
var thread = Thread.load(1, null, session);
complete({
id: 1,
posts: [{
id: 101
}, {
id: 102
}]
});
var posts = thread.posts();
expect(posts.getCount()).toBe(1);
expect(posts.getAt(0).getId()).toBe(102);
expect(posts.getAt(0).get('threadId')).toBe(1);
expect(posts.getAt(0).dirty).toBe(false);
expect(posts.indexOf(post)).toBe(-1);
});
it("should infer the key from the parent if not specified", function() {
var thread = Thread.load(1, null, session);
complete({
id: 1,
posts: [{
id: 101
}, {
id: 102
}]
});
var posts = thread.posts();
expect(posts.getCount()).toBe(2);
expect(posts.getAt(0).getId()).toBe(101);
expect(posts.getAt(0).get('threadId')).toBe(1);
expect(posts.getAt(0).dirty).toBe(false);
expect(posts.getAt(1).getId()).toBe(102);
expect(posts.getAt(1).get('threadId')).toBe(1);
expect(posts.getAt(1).dirty).toBe(false);
});
it("should infer the key when loading the store, not nested", function() {
var thread = Thread.load(1, null, session);
complete({
id: 1
});
var posts = thread.posts();
posts.load();
complete([{
id: 101
}, {
id: 102
}]);
expect(posts.getCount()).toBe(2);
expect(posts.getAt(0).getId()).toBe(101);
expect(posts.getAt(0).get('threadId')).toBe(1);
expect(posts.getAt(0).dirty).toBe(false);
expect(posts.getAt(1).getId()).toBe(102);
expect(posts.getAt(1).get('threadId')).toBe(1);
expect(posts.getAt(1).dirty).toBe(false);
});
it("should not infer the key from the parent if a key is specified", function() {
var thread = Thread.load(1, null, session);
complete({
id: 1,
posts: [{
id: 101,
threadId: 100
}, {
id: 102
}]
});
var posts = thread.posts();
expect(posts.getCount()).toBe(1);
expect(posts.getAt(0).getId()).toBe(102);
expect(posts.getAt(0).get('threadId')).toBe(1);
expect(posts.getAt(0).dirty).toBe(false);
var rec = session.peekRecord('Post', 101);
expect(posts.indexOf(rec)).toBe(-1);
});
it("should allow inference for phantom records", function() {
var thread = session.createRecord('Thread', {}),
post = session.createRecord('Post', {
threadId: thread.getId()
});
expect(thread.posts().getCount()).toBe(1);
expect(thread.posts().getAt(0)).toBe(post);
});
});
});
});
describe("id change of the one", function() {
function createSuite(withSession) {
describe(withSession ? "with session" : "without session", function() {
var session, thread;
beforeEach(function() {
definePost();
if (withSession) {
session = new Ext.data.Session();
}
thread = new Thread(null, session);
});
afterEach(function() {
if (withSession) {
session.destroy();
}
session = thread = null;
});
it("should not cause an exception if the store is not created", function() {
expect(function() {
thread.setId(100);
}).not.toThrow();
});
it("should update the store filter", function() {
var posts = thread.posts(),
filter = posts.getFilters().getAt(0);
expect(filter.getValue()).toBe(thread.id);
posts.add({
id: 1
});
thread.setId(100);
expect(filter.getValue()).toBe(100);
});
it("should update the foreign key", function() {
var records = thread.posts().add([{id: 1}, {id: 2}, {id: 3}]);
expect(records[0].get('threadId')).toBe(thread.id);
expect(records[1].get('threadId')).toBe(thread.id);
expect(records[2].get('threadId')).toBe(thread.id);
thread.setId(100);
expect(records[0].get('threadId')).toBe(100);
expect(records[1].get('threadId')).toBe(100);
expect(records[2].get('threadId')).toBe(100);
});
});
}
createSuite(false);
createSuite(true);
});
describe("getters/setters", function() {
function createSuite(withSession) {
describe(withSession ? "with session" : "without session", function() {
var spy, session, post, thread;
beforeEach(function() {
spy = jasmine.createSpy();
if (withSession) {
session = new Ext.data.Session();
}
});
afterEach(function() {
if (withSession) {
session.destroy();
}
session = post = thread = null;
});
describe("the one", function() {
beforeEach(function() {
definePost();
});
describe("getter", function() {
beforeEach(function() {
post = new Post({
id: 4
}, session);
});
describe("without an instance", function() {
describe("with no foreign key value", function() {
it("should return null", function() {
expect(post.getThread()).toBeNull();
});
it("should not make any request", function() {
spy = spyOn(Thread.getProxy(), 'read');
post.getThread();
expect(spy).not.toHaveBeenCalled();
});
describe("callbacks", function() {
it("should call the callbacks before the function returns", function() {
post.getThread(spy);
expect(spy).toHaveBeenCalled();
spy.reset();
post.getThread({
success: spy
});
expect(spy).toHaveBeenCalled();
spy.reset();
post.getThread({
callback: spy
});
expect(spy).toHaveBeenCalled();
});
it("should accept a function as the callback and default the scope to the model", function() {
post.getThread(spy);
var call = spy.mostRecentCall;
expect(call.args[0]).toBe(thread);
expect(call.args[1]).toBeNull();
expect(call.args[2]).toBe(true);
expect(call.object).toBe(post);
});
it("should accept a function with a scope", function() {
var o = {};
post.getThread(spy, o);
expect(spy.mostRecentCall.object).toBe(o);
});
it("should accept an options object with success and default the scope to the model", function() {
post.getThread({
success: spy
});
var call = spy.mostRecentCall;
expect(call.args[0]).toBe(thread);
expect(call.args[1]).toBeNull();
expect(call.object).toBe(post);
});
it("should accept an options object with success and a scope", function() {
var o = {},
call;
post.getThread({
scope: o,
success: spy
});
call = spy.mostRecentCall;
expect(call.object).toBe(o);
});
it("should accept an options object with callback and default the scope to the model", function() {
post.getThread({
callback: spy
});
var call = spy.mostRecentCall;
expect(call.args[0]).toBe(thread);
expect(call.args[1]).toBeNull();
expect(call.args[2]).toBe(true);
expect(call.object).toBe(post);
});
it("should accept an options object with callback and a scope", function() {
var o = {},
call;
post.getThread({
scope: o,
callback: spy
});
call = spy.mostRecentCall;
expect(call.object).toBe(o);
});
});
});
describe("with a foreign key value", function() {
beforeEach(function() {
post.set('threadId', 17);
});
if (withSession) {
it("should create an instance in the session", function() {
expect(post.getThread()).toBe(session.getRecord('Thread', 17, false));
});
it("should use an existing record instance", function() {
thread = session.getRecord('Thread', 17, false);
expect(post.getThread()).toBe(thread);
});
it("should not load an existing instance", function() {
thread = session.getRecord('Thread', {
id: 17
}, false);
post.getThread();
expect(thread.isLoading()).toBe(false);
});
}
it("should return an instance with the matching id", function() {
expect(post.getThread().getId()).toBe(17);
});
it("should be in a loading state", function() {
expect(post.getThread().isLoading()).toBe(true);
});
it("should trigger a load for the record", function() {
spy = spyOn(Thread.getProxy(), 'read');
post.getThread();
expect(spy.mostRecentCall.args[0].getId()).toBe(17);
});
describe("calling while during a load", function() {
it("should return the same record", function() {
var rec = post.getThread();
expect(post.getThread()).toBe(rec);
});
it("should not trigger a second load", function() {
post.getThread();
spy = spyOn(Thread.getProxy(), 'read');
post.getThread();
expect(spy).not.toHaveBeenCalled();
});
it("should not trigger any callback until load completes", function() {
post.getThread();
post.getThread({
success: spy,
callback: spy
});
expect(spy).not.toHaveBeenCalled();
});
it("should trigger the callbacks once loaded", function() {
post.getThread();
post.getThread({
success: spy,
callback: spy
});
complete({});
expect(spy.callCount).toBe(2);
});
});
describe("callbacks", function() {
it("should not trigger any callbacks until the load completes", function() {
post.getThread(spy);
post.getThread({
success: spy
});
post.getThread({
failure: spy
});
post.getThread({
callback: spy
});
expect(spy).not.toHaveBeenCalled();
});
describe("when successful", function() {
it("should accept a function as the callback and default the scope to the model", function() {
thread = post.getThread(spy);
complete({});
var call = spy.mostRecentCall;
expect(call.args[0]).toBe(thread);
expect(call.args[1].isOperation).toBe(true);
expect(call.args[2]).toBe(true);
expect(call.object).toBe(post);
});
it("should accept a function with a scope", function() {
var o = {};
post.getThread(spy, o);
complete({});
expect(spy.mostRecentCall.object).toBe(o);
});
it("should accept an options object with success and default the scope to the model", function() {
thread = post.getThread({
success: spy
});
complete({});
var call = spy.mostRecentCall;
expect(call.args[0]).toBe(thread);
expect(call.args[1].isOperation).toBe(true);
expect(call.object).toBe(post);
});
it("should accept an options object with success and a scope", function() {
var o = {},
call;
post.getThread({
scope: o,
success: spy
});
complete({});
call = spy.mostRecentCall;
expect(call.object).toBe(o);
});
it("should accept an options object with callback and default the scope to the model", function() {
thread = post.getThread({
callback: spy
});
complete({});
var call = spy.mostRecentCall;
expect(call.args[0]).toBe(thread);
expect(call.args[1].isOperation).toBe(true);
expect(call.args[2]).toBe(true);
expect(call.object).toBe(post);
});
it("should accept an options object with callback and a scope", function() {
var o = {},
call;
post.getThread({
scope: o,
callback: spy
});
complete({});
call = spy.mostRecentCall;
expect(call.object).toBe(o);
});
});
describe("when failed", function() {
it("should accept a function as the callback and default the scope to the model", function() {
thread = post.getThread(spy);
complete(null, 500);
var call = spy.mostRecentCall;
expect(call.args[0]).toBe(thread);
expect(call.args[1].isOperation).toBe(true);
expect(call.args[2]).toBe(false);
expect(call.object).toBe(post);
});
it("should accept a function with a scope", function() {
var o = {};
post.getThread(spy, o);
complete(null, 500);
expect(spy.mostRecentCall.object).toBe(o);
});
it("should accept an options object with failure and default the scope to the model", function() {
thread = post.getThread({
failure: spy
});
complete(null, 500);
var call = spy.mostRecentCall;
expect(call.args[0]).toBe(thread);
expect(call.args[1].isOperation).toBe(true);
expect(call.object).toBe(post);
});
it("should accept an options object with failure and a scope", function() {
var o = {},
call;
post.getThread({
scope: o,
failure: spy
});
complete(null, 500);
call = spy.mostRecentCall;
expect(call.object).toBe(o);
});
it("should accept an options object with callback and default the scope to the model", function() {
thread = post.getThread({
callback: spy
});
complete(null, 500);
var call = spy.mostRecentCall;
expect(call.args[0]).toBe(thread);
expect(call.args[1].isOperation).toBe(true);
expect(call.args[2]).toBe(false);
expect(call.object).toBe(post);
});
it("should accept an options object with callback and a scope", function() {
var o = {},
call;
post.getThread({
scope: o,
callback: spy
});
complete(null, 500);
call = spy.mostRecentCall;
expect(call.object).toBe(o);
});
});
});
});
});
describe("with an already loaded instance", function() {
beforeEach(function() {
thread = new Thread({
id: 2
}, session);
post.setThread(thread);
});
it("should return the same instance", function() {
expect(post.getThread()).toBe(thread);
});
it("should not attempt to load", function() {
spy = spyOn(Thread.getProxy(), 'read');
post.getThread();
expect(spy).not.toHaveBeenCalled();
});
it("should attempt to reload if called with options.reload", function() {
spy = spyOn(Thread.getProxy(), 'read').andReturn();
post.getThread({
reload: true
});
expect(spy).toHaveBeenCalled();
});
it("should reload the same record when called with reload", function() {
var result = post.getThread({
reload: true
});
expect(result).toBe(thread);
});
describe("callbacks", function() {
it("should call the callbacks before the function returns", function() {
post.getThread(spy);
expect(spy).toHaveBeenCalled();
spy.reset();
post.getThread({
success: spy
});
expect(spy).toHaveBeenCalled();
spy.reset();
post.getThread({
callback: spy
});
expect(spy).toHaveBeenCalled();
});
it("should accept a function as the callback and default the scope to the model", function() {
post.getThread(spy);
var call = spy.mostRecentCall;
expect(call.args[0]).toBe(thread);
expect(call.args[1]).toBeNull();
expect(call.args[2]).toBe(true);
expect(call.object).toBe(post);
});
it("should accept a function with a scope", function() {
var o = {};
post.getThread(spy, o);
expect(spy.mostRecentCall.object).toBe(o);
});
it("should accept an options object with success and default the scope to the model", function() {
post.getThread({
success: spy
});
var call = spy.mostRecentCall;
expect(call.args[0]).toBe(thread);
expect(call.args[1]).toBeNull();
expect(call.object).toBe(post);
});
it("should accept an options object with success and a scope", function() {
var o = {},
call;
post.getThread({
scope: o,
success: spy
});
call = spy.mostRecentCall;
expect(call.object).toBe(o);
});
it("should accept an options object with callback and default the scope to the model", function() {
post.getThread({
callback: spy
});
var call = spy.mostRecentCall;
expect(call.args[0]).toBe(thread);
expect(call.args[1]).toBeNull();
expect(call.args[2]).toBe(true);
expect(call.object).toBe(post);
});
it("should accept an options object with callback and a scope", function() {
var o = {},
call;
post.getThread({
scope: o,
callback: spy
});
call = spy.mostRecentCall;
expect(call.object).toBe(o);
});
});
});
});
describe("setter", function() {
beforeEach(function() {
post = new Post({
id: 7
}, session);
});
describe("insta