UNPKG

extjs-gpl

Version:

GPL licensed version of Sencha Ext JS

1,345 lines (1,087 loc) 65.9 kB
describe("Ext.data.proxy.Direct", function() { var proxy, provider, spies, Writer, writer, Model, readSpy, createSpy, updateSpy, destroySpy, directSpy, namedSpy, orderedSpy; function makeApi(cfg) { cfg = Ext.apply({ "namespace": "spec", type: "remoting", url: "fake", enableBuffer: false }, cfg); provider = Ext.direct.Manager.addProvider(cfg); } function makeProxy(cfg, ctor) { var writerCfg = cfg && cfg.writer; writer = new Writer(writerCfg || {}); cfg = Ext.apply({ writer: writer }, cfg); proxy = new (ctor || Ext.data.proxy.Direct)(cfg); } function makeSpy(name) { var directCfg = spec.DirectSpecs[name].directCfg, spy = spyOn(spec.DirectSpecs, name); spy.directCfg = directCfg; return spy; } function returnData(spy, data) { spy.andCallFake(function(params, callback, scope) { Ext.callback(callback, scope, [ null, { status: true, result: data } ]); }); } function makeOperation(op, action) { op = Ext.apply({}, op); var records = op.records, rec, i, len; if (records) { for (i = 0, len = records.length; i < len; i++) { records[i] = new Model(records[i]); } } return op; } function readSome(proxyObject, operation) { proxyObject = proxyObject || proxy; operation = makeOperation(operation); proxyObject.read(new Ext.data.operation.Read(operation)); } function createSome(proxyObject, operation) { proxyObject = proxyObject || proxy; operation = makeOperation(operation); proxyObject.create(new Ext.data.operation.Create(operation || {})); } function updateSome(proxyObject, operation) { proxyObject = proxyObject || proxy; operation = makeOperation(operation); proxyObject.update(new Ext.data.operation.Update(operation || {})); } function destroySome(proxyObject, operation) { proxyObject = proxyObject || proxy; operation = makeOperation(operation); proxyObject.erase(new Ext.data.operation.Destroy(operation || {})); } function expectArgs(spy, expected) { var args = spy.mostRecentCall.args; expect(args[0]).toEqual(expected); expect(typeof args[1]).toBe('function'); } beforeEach(function() { Model = Ext.define(null, { extend: 'Ext.data.Model', fields: [ { name: 'id', type: 'integer' }, { name: 'name', type: 'string' } ], idProperty: 'id' }); Writer = Ext.define(null, { extend: 'Ext.data.writer.Json', write: function(request) { var op = request.getOperation(), data = op.data; if (data) { request.setJsonData(data); } else { return this.callParent(arguments); } return request; } }); MockAjaxManager.addMethods(); }); afterEach(function() { if (proxy) { Ext.destroy(proxy); } if (writer) { Ext.destroy(writer); } if (provider) { Ext.direct.Manager.removeProvider(provider); } provider = proxy = Writer = writer = Model = null; readSpy = createSpy = updateSpy = destroySpy = directSpy = null; namedSpy = orderedSpy = spies = window.spec = null; MockAjaxManager.removeMethods(); }); describe("API declaration", function() { beforeEach(function() { makeApi({ actions: { 'DirectSpecs': [{ len: 0, name: 'read' }, { len: 0, name: 'create' }, { len: 0, name: 'update' }, { len: 0, name: 'destroy' }, { len: 0, name: 'directFn' }] } }); readSpy = makeSpy('read'); createSpy = makeSpy('create'); updateSpy = makeSpy('update'); destroySpy = makeSpy('destroy'); directSpy = makeSpy('directFn'); spies = { read: readSpy, create: createSpy, update: updateSpy, destroy: destroySpy, directFn: directSpy }; }); describe("directFn", function() { beforeEach(function() { makeProxy({ directFn: directSpy }); }); it("should be used to read", function() { readSome(); expect(directSpy).toHaveBeenCalled(); }); it("should be used to create", function() { createSome(); expect(directSpy).toHaveBeenCalled(); }); it("should be used to update", function() { updateSome(); expect(directSpy).toHaveBeenCalled(); }); it("should be used to destroy", function() { destroySome(); expect(directSpy).toHaveBeenCalled(); }); }); describe("api blob", function() { beforeEach(function() { makeProxy({ api: { read: readSpy, create: createSpy, update: updateSpy, destroy: destroySpy } }); }); it("should be used to read", function() { readSome(); expect(readSpy).toHaveBeenCalled(); }); it("should be used to create", function() { createSome(); expect(createSpy).toHaveBeenCalled(); }); it("should be used to update", function() { updateSome(); expect(updateSpy).toHaveBeenCalled(); }); it("should be used to destroy", function() { destroySome(); expect(destroySpy).toHaveBeenCalled(); }); }); describe("both directFn and api blob", function() { function makeSuite(name, wantCalled, wantNotCalled, opFn) { return describe(name, function() { beforeEach(opFn); it("should call " + wantCalled, function() { var spy = spies[wantCalled]; expect(spy).toHaveBeenCalled(); }); it("should not call " + wantNotCalled, function() { var spy = spies[wantNotCalled]; expect(spy).not.toHaveBeenCalled(); }); }); } beforeEach(function() { // This configuration is taken from customer use case, // see https://sencha.jira.com/browse/EXTJS-14843 makeProxy({ api: { create: createSpy, update: updateSpy, destroy: destroySpy }, directFn: directSpy }); }); makeSuite('read', 'directFn', 'read', function() { readSome() }); makeSuite('create', 'create', 'directFn', function() { createSome() }); makeSuite('update', 'update', 'directFn', function() { updateSome() }); makeSuite('destroy', 'destroy', 'directFn', function() { destroySome() }); }); describe("string name resolving", function() { describe("directFn", function() { beforeEach(function() { makeProxy({ directFn: 'spec.DirectSpecs.directFn' }); }); it("should resolve directFn", function() { readSome(); expect(directSpy.callCount).toBe(1); }); it("should be able to resolve a new directFn after loading", function() { readSome(); // To resolve the first time proxy.setDirectFn('spec.DirectSpecs.read'); readSome(); expect(readSpy.callCount).toBe(1); }); }); describe("api blob", function() { describe("no prefix", function() { describe("initial", function() { beforeEach(function() { makeProxy({ api: { read: 'spec.DirectSpecs.read', create: 'spec.DirectSpecs.create', update: 'spec.DirectSpecs.update', destroy: 'spec.DirectSpecs.destroy' } }); }); it("should resolve read fn", function() { readSome(); expect(readSpy.callCount).toBe(1); }); it("should resolve create fn", function() { createSome(); expect(createSpy.callCount).toBe(1); }); it("should resolve update fn", function() { updateSome(); expect(updateSpy.callCount).toBe(1); }); it("should resolve destroy fn", function() { destroySome(); expect(destroySpy.callCount).toBe(1); }); }); describe("re-initializing", function() { beforeEach(function() { makeProxy({ api: { read: 'spec.DirectSpecs.directFn', create: 'spec.DirectSpecs.directFn', update: 'spec.DirectSpecs.directFn', destroy: 'spec.DirectSpecs.directFn' } }); it("should resolve to directFn upfront", function() { readSome(); expect(directSpy.callCount).toBe(1); }); describe("after re-init", function() { beforeEach(function() { proxy.setApi({ read: 'spec.DirectSpecs.read', create: 'spec.DirectSpecs.create', update: 'spec.DirectSpecs.update', destroy: 'spec.DirectSpecs.destroy' }); }); it("should resolve read fn", function() { readSome(); expect(readSpy.callCount).toBe(1); }); it("should resolve create fn", function() { createSome(); expect(createSpy.callCount).toBe(1); }); it("should resolve update fn", function() { updateSome(); expect(updateSpy.callCount).toBe(1); }); it("should resolve destroy fn", function() { destroySome(); expect(destroySpy.callCount).toBe(1); }); }); }); }); }); describe("with prefix", function() { describe("initial", function() { beforeEach(function() { makeProxy({ api: { prefix: 'spec.DirectSpecs', read: 'read', create: 'create', update: 'update', destroy: 'destroy' } }); }); it("should resolve read fn", function() { readSome(); expect(readSpy.callCount).toBe(1); }); it("should resolve create fn", function() { createSome(); expect(createSpy.callCount).toBe(1); }); it("should resolve update fn", function() { updateSome(); expect(updateSpy.callCount).toBe(1); }); it("should resolve destroy fn", function() { destroySome(); expect(destroySpy.callCount).toBe(1); }); }); describe("re-initializing", function() { beforeEach(function() { makeProxy({ api: { prefix: 'spec.DirectSpecs.', read: 'directFn', create: 'directFn', update: 'directFn', destroy: 'directFn' } }); it("should resolve to directFn upfront", function() { readSome(); expect(directSpy.callCount).toBe(1); }); describe("after re-init", function() { beforeEach(function() { proxy.setApi({ read: 'read', create: 'create', update: 'update', destroy: 'destroy' }); }); it("should resolve read fn", function() { readSome(); expect(readSpy.callCount).toBe(1); }); it("should resolve create fn", function() { createSome(); expect(createSpy.callCount).toBe(1); }); it("should resolve update fn", function() { updateSome(); expect(updateSpy.callCount).toBe(1); }); it("should resolve destroy fn", function() { destroySome(); expect(destroySpy.callCount).toBe(1); }); }); describe("after re-init with no prefix", function() { beforeEach(function() { proxy.setApi({ prefix: null, read: 'spec.DirectSpecs.read', create: 'spec.DirectSpecs.create', update: 'spec.DirectSpecs.update', destroy: 'spec.DirectSpecs.destroy' }); }); it("should resolve read fn", function() { readSome(); expect(readSpy.callCount).toBe(1); }); it("should resolve create fn", function() { createSome(); expect(createSpy.callCount).toBe(1); }); it("should resolve update fn", function() { updateSome(); expect(updateSpy.callCount).toBe(1); }); it("should resolve destroy fn", function() { destroySome(); expect(destroySpy.callCount).toBe(1); }); }); }); }); describe("merged through inheritance", function() { var Foo, Bar; beforeEach(function() { Foo = Ext.define(null, { extend: 'Ext.data.proxy.Direct', api: { read: 'read', create: 'create', update: 'update', destroy: 'destroy' } }); Bar = Ext.define(null, { extend: Foo, api: { prefix: 'spec.DirectSpecs' } }); makeProxy(null, Bar); }); afterEach(function() { Foo = Bar = null; }); it("should resolve read fn", function() { readSome(); expect(readSpy.callCount).toBe(1); }); it("should resolve create fn", function() { createSome(); expect(createSpy.callCount).toBe(1); }); it("should resolve update fn", function() { updateSome(); expect(updateSpy.callCount).toBe(1); }); it("should resolve destroy fn", function() { destroySome(); expect(destroySpy.callCount).toBe(1); }); }); }); }); describe("both directFn and api blob", function() { beforeEach(function() { makeProxy({ api: { read: 'spec.DirectSpecs.read', create: 'spec.DirectSpecs.create', update: 'spec.DirectSpecs.update', destroy: 'spec.DirectSpecs.destroy' }, directFn: 'spec.DirectSpecs.directFn' }); proxy.resolveMethods(); }); it("should resolve directFn", function() { expect(proxy.directFn).toBe(directSpy); }); it("should resolve api.read", function() { expect(proxy.api.read).toBe(readSpy); }); it("should resolve api.create", function() { expect(proxy.api.create).toBe(createSpy); }); it("should resolve api.update", function() { expect(proxy.api.update).toBe(updateSpy); }); it("should resolve api.destroy", function() { expect(proxy.api.destroy).toBe(destroySpy); }); }); }); }); describe("invalid API", function() { // https://sencha.jira.com/browse/EXTJS-16255 it("should not cause stack overflow when both params and len is declared", function() { makeApi({ actions: { "Proxy.Query": [ { "name": "GetUserlog", "strict": false, "params": [ "username", "start", "limit", "sort" ], "len": 4 } ] } }); makeProxy({ directFn: 'Proxy.Query.GetUserlog' }); // Console error is expected spyOn(Ext, 'log'); expect(function() { readSome(); }).toThrow('Incorrect parameters for Direct proxy "read" operation'); }); }); describe("store handling with canonical API", function() { var store, loadSpy; beforeEach(function() { makeApi({ actions: { DirectSpecs: [ { name: 'readNamed', params: [], strict: false }, { name: 'readOrdered', len: 1 }, { name: 'create', len: 1 }, { name: 'update', len: 1 }, { name: 'destroy', len: 1 } ] } }); namedSpy = makeSpy('readNamed'); orderedSpy = makeSpy('readOrdered'); createSpy = makeSpy('create'); updateSpy = makeSpy('update'); destroySpy = makeSpy('destroy'); makeProxy({ api: { read: orderedSpy, create: createSpy, update: updateSpy, destroy: destroySpy } }); loadSpy = jasmine.createSpy('store load'); store = new Ext.data.Store({ model: Model, proxy: proxy, autoLoad: false, asynchronousLoad: false, listeners: { load: loadSpy } }); }); afterEach(function() { store = Ext.destroy(store); }); describe("read", function() { var result = [ { id: 1, name: 'foo' }, { id: 2, name: 'bar' }, { id: 3, name: 'qux' } ]; beforeEach(function() { returnData(namedSpy, result); returnData(orderedSpy, result); }); describe("with named", function() { beforeEach(function() { proxy.api.read = namedSpy; store.load(); waitForSpy(loadSpy); }); it("should load the records in the store", function() { expect(store.getCount()).toBe(3); expect(store.getAt(0).get('name')).toBe('foo'); expect(store.getAt(1).get('name')).toBe('bar'); expect(store.getAt(2).get('name')).toBe('qux'); }); it("should pass correct params to the fn", function() { expectArgs(proxy.api.read, { page: 1, start: 0, limit: 25 }); }); }); describe("with ordered", function() { beforeEach(function() { proxy.api.read = orderedSpy; }); describe("no paramOrder", function() { beforeEach(function() { store.load(); waitForSpy(loadSpy); }); it("should load the records in the store", function() { expect(store.getCount()).toBe(3); expect(store.getAt(0).get('name')).toBe('foo'); expect(store.getAt(1).get('name')).toBe('bar'); expect(store.getAt(2).get('name')).toBe('qux'); }); it("should pass correct params to the fn", function() { expectArgs(proxy.api.read, { page: 1, start: 0, limit: 25 }); }); }); describe("with paramOrder", function() { beforeEach(function() { proxy.setParamOrder('start,limit,page'); store.load(); waitForSpy(loadSpy); }); it("should load the records in the store", function() { expect(store.getCount()).toBe(3); expect(store.getAt(0).get('name')).toBe('foo'); expect(store.getAt(1).get('name')).toBe('bar'); expect(store.getAt(2).get('name')).toBe('qux'); }); it("should pass correct params to the fn", function() { expectArgs(proxy.api.read, [0,25,1]); }); }); }); }); describe("create", function() { describe("single record, allowSingle == true", function() { beforeEach(function() { store.add( { name: 'xyzzy' } ); }); describe("no paramOrder", function() { beforeEach(function() { store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(createSpy, { id: store.getAt(0).id, name: 'xyzzy' }); }); }); describe("with paramOrder", function() { describe("matching number of arguments", function() { beforeEach(function() { proxy.setParamOrder('name'); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(createSpy, 'xyzzy'); }); }); describe("paramOrder.length > method.len", function() { beforeEach(function() { proxy.setParamOrder('id,name'); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(createSpy, [ store.getAt(0).id, 'xyzzy' ]); }); }); }); }); describe("single record, allowSingle == false", function() { beforeEach(function() { proxy.getWriter().setAllowSingle(false); store.add( { name: 'xyzzy' } ); }); describe("no paramOrder", function() { beforeEach(function() { store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(createSpy, [ { id: store.getAt(0).id, name: 'xyzzy' } ]); }); }); describe("with paramOrder", function() { describe("matching number of arguments", function() { beforeEach(function() { proxy.setParamOrder('name'); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(createSpy, ['xyzzy']); }); }); describe("paramOrder.length > method.len", function() { beforeEach(function() { proxy.setParamOrder('id,name'); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(createSpy, [ [ store.getAt(0).id, 'xyzzy' ] ]); }); }); }); }); describe("multiple records", function() { beforeEach(function() { store.add( { name: 'xyzzy' }, { name: 'zyxxy' } ); }); describe("no paramOrder", function() { beforeEach(function() { store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(createSpy, [ { id: store.getAt(0).id, name: 'xyzzy' }, { id: store.getAt(1).id, name: 'zyxxy' } ]); }); }); describe("with paramOrder", function() { describe("matching number of arguments", function() { beforeEach(function() { proxy.setParamOrder('name'); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(createSpy, ['xyzzy', 'zyxxy']); }); }); describe("paramOrder.length > method.len", function() { beforeEach(function() { proxy.setParamOrder('id,name'); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(createSpy, [ [store.getAt(0).id, 'xyzzy'], [store.getAt(1).id, 'zyxxy'] ]); }); }); }); }); }); describe("update", function() { beforeEach(function() { store.loadRawData([ { id: 41, name: 'blergo' }, { id: 42, name: 'frobbe' } ]); store.getAt(1).set({ name: 'throbbe' }); }); describe("single record, allowSingle == true", function() { describe("no paramOrder", function() { beforeEach(function() { store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(updateSpy, { id: 42, name: 'throbbe' }); }); }); describe("with paramOrder", function() { describe("matching number of arguments", function() { beforeEach(function() { proxy.setParamOrder('name'); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(updateSpy, 'throbbe'); }); }); describe("paramOrder.length > method.len", function() { beforeEach(function() { proxy.setParamOrder('id,name'); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(updateSpy, [42, 'throbbe']); }); }); }); }); describe("single record, allowSingle == false", function() { beforeEach(function() { proxy.getWriter().setAllowSingle(false); }); describe("no paramOrder", function() { beforeEach(function() { store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(updateSpy, [ { id: 42, name: 'throbbe' } ]); }); }); describe("with paramOrder", function() { describe("matching number of arguments", function() { beforeEach(function() { proxy.setParamOrder('name'); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(updateSpy, ['throbbe']); }); }); describe("paramOrder.length > method.len", function() { beforeEach(function() { proxy.setParamOrder('id,name'); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(updateSpy, [ [42, 'throbbe'] ]); }); }); }); }); describe("multiple records", function() { beforeEach(function() { store.getAt(0).set({ name: 'zingbong' }); }); describe("no paramOrder", function() { beforeEach(function() { store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(updateSpy, [ { id: 41, name: 'zingbong' }, { id: 42, name: 'throbbe' } ]); }); }); describe("with paramOrder", function() { describe("matching number of arguments", function() { beforeEach(function() { proxy.setParamOrder('name'); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(updateSpy, ['zingbong', 'throbbe']); }); }); describe("paramOrder.length > method.len", function() { beforeEach(function() { proxy.setParamOrder('id,name'); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(updateSpy, [ [41, 'zingbong'], [42, 'throbbe'] ]); }); }); }); }); }); describe("destroy", function() { beforeEach(function() { store.loadRawData([ { id: 7, name: 'yin' }, { id: 8, name: 'yang' } ]); }); describe("single record, allowSingle == true", function() { describe("no paramOrder", function() { beforeEach(function() { store.removeAt(0); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(destroySpy, { id: 7 }); }); }); describe("with paramOrder", function() { beforeEach(function() { proxy.setParamOrder('id'); store.removeAt(0); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(destroySpy, 7); }); }); }); describe("single record, allowSingle == false", function() { beforeEach(function() { proxy.getWriter().setAllowSingle(false); }); describe("no paramOrder", function() { beforeEach(function() { store.removeAt(1); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(destroySpy, [{ id: 8 }]); }); }); describe("with paramOrder", function() { beforeEach(function() { proxy.setParamOrder('id'); store.removeAt(1); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(destroySpy, [8]); }); }); }); describe("multiple records", function() { describe("no paramOrder", function() { beforeEach(function() { store.removeAll(); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(destroySpy, [ { id: 7 }, { id: 8 } ]); }); }); describe("with paramOrder", function() { beforeEach(function() { proxy.setParamOrder('id'); store.removeAll(); store.sync(); }); it("should pass correct params to the fn", function() { expectArgs(destroySpy, [7, 8]); }); }); }); }); }); describe("params", function() { var ordered2Spy; beforeEach(function() { makeApi({ actions: { DirectSpecs: [{ name: 'named', params: ['blerg'] }, { name: 'ordered', len: 1 }, { name: 'ordered2', len: 2 }] } }); namedSpy = makeSpy('named'); orderedSpy = makeSpy('ordered'); ordered2Spy = makeSpy('ordered2'); }); afterEach(function() { ordered2Spy = null; }); describe("with named fn", function() { beforeEach(function() { makeProxy({ directFn: namedSpy }); }); it("should pass params to read method", function() { readSome(proxy, { params: { blerg: -1 } }); expectArgs(namedSpy, { blerg: -1 }); }); }); describe("with ordered fn", function() { describe("paramOrder", function() { beforeEach(function() { makeProxy({ directFn: ordered2Spy }); proxy.setParamOrder('foo,bar'); }); describe("read", function() { it("should use paramOrder", function() { readSome(proxy, { params: { foo: 1, bar: 101 } }); var args = ordered2Spy.mostRecentCall.args; expect(args[0]).toBe(1); expect(args[1]).toBe(101); }); }); }); describe("paramsAsHash == true", function() { beforeEach(function() { makeProxy({ directFn: orderedSpy }); proxy.setParamsAsHash(true); }); it("should pass an object to read method", function() { readSome(proxy, { params: { foo: 'bar', blerg: 'throbbe' } }); expect(orderedSpy.mostRecentCall.args[0]).toEqual({ foo: 'bar', blerg: 'throbbe' }); }); }); describe("paramsAsHash == false", function() { beforeEach(function() { makeProxy({ directFn: orderedSpy }); proxy.setParamsAsHash(false); }); it("should pass object as argument to read method", function() { readSome(proxy, { params: { throbbe: 'knurl', bonzo: 'gurgle' } }); expect(orderedSpy.mostRecentCall.args[0]).toEqual({ throbbe: 'knurl', bonzo: 'gurgle' }); }); }); }); }); describe("extraParams", function() { var ordered2Spy; beforeEach(function() { makeApi({ actions: { 'DirectSpecs': [{ name: 'named', params: ['blerg'] }, { name: 'ordered', len: 1 }, { name: 'ordered2', len: 2 }] } }); namedSpy = makeSpy('named'); orderedSpy = makeSpy('ordered'); ordered2Spy = makeSpy('ordered2'); }); afterEach(function() { ordered2Spy = null; }); describe("with named fn", function() { beforeEach(function() { makeProxy({ directFn: namedSpy, extraParams: { foo: true, bar: false } }); }); it("should pass extraParams with read", function() { readSome(proxy, { params: { blerg: 42 } }); expect(namedSpy.mostRecentCall.args[0]).toEqual({ foo: true, bar: false, blerg: 42 }); }); it("should not pass extraParams with create", function() { createSome(); expect(namedSpy.mostRecentCall.args[0]).toBe(undefined); }); it("should not pass extraParams with update", function() { updateSome(); expect(namedSpy.mostRecentCall.args[0]).toBe(undefined); }); it("should not pass extraParams with destroy", function() { destroySome(); expect(namedSpy.mostRecentCall.args[0]).toBe(undefined); }); }); describe("with ordered fn", function() { beforeEach(function() { makeProxy({ directFn: orderedSpy, extraParams: { foo: true, bar: false } }); }); describe("read", function() {