[TEST] serialization of search domains & contexts by the search view
bzr revid: xmo@openerp.com-20120503125949-2m8euott3xzyivdm
This commit is contained in:
parent
836dfa6aa8
commit
6d12c155a4
|
@ -656,7 +656,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
|
||||||
do_search: function () {
|
do_search: function () {
|
||||||
var domains = [], contexts = [], groupbys = [], errors = [];
|
var domains = [], contexts = [], groupbys = [], errors = [];
|
||||||
|
|
||||||
return this.on_search([], [], []);
|
|
||||||
this.query.each(function (facet) {
|
this.query.each(function (facet) {
|
||||||
var field = facet.get('field');
|
var field = facet.get('field');
|
||||||
try {
|
try {
|
||||||
|
@ -902,12 +901,12 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
|
||||||
/**
|
/**
|
||||||
* Fetches contexts for all enabled filters in the group
|
* Fetches contexts for all enabled filters in the group
|
||||||
*
|
*
|
||||||
* @param {VS.model.SearchFacet} facet
|
* @param {openerp.web.search.Facet} facet
|
||||||
* @return {*} combined contexts of the enabled filters in this group
|
* @return {*} combined contexts of the enabled filters in this group
|
||||||
*/
|
*/
|
||||||
get_context: function (facet) {
|
get_context: function (facet) {
|
||||||
var contexts = _(facet.get('values')).chain()
|
var contexts = _(facet.values).chain()
|
||||||
.map(function (filter) { return filter.attrs.context; })
|
.map(function (f) { return f.get('value').attrs.context; })
|
||||||
.reject(_.isEmpty)
|
.reject(_.isEmpty)
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
|
@ -924,8 +923,8 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
|
||||||
* @return {Array} enabled filters in this group
|
* @return {Array} enabled filters in this group
|
||||||
*/
|
*/
|
||||||
get_groupby: function (facet) {
|
get_groupby: function (facet) {
|
||||||
return _(facet.get('values')).chain()
|
return _(facet.values).chain()
|
||||||
.map(function (filter) { return filter.attrs.context; })
|
.map(function (f) { return f.get('value').attrs.context; })
|
||||||
.reject(_.isEmpty)
|
.reject(_.isEmpty)
|
||||||
.value();
|
.value();
|
||||||
},
|
},
|
||||||
|
@ -936,8 +935,8 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
|
||||||
* @return {*} combined domains of the enabled filters in this group
|
* @return {*} combined domains of the enabled filters in this group
|
||||||
*/
|
*/
|
||||||
get_domain: function (facet) {
|
get_domain: function (facet) {
|
||||||
var domains = _(facet.get('values')).chain()
|
var domains = _(facet.values).chain()
|
||||||
.map(function (filter) { return filter.attrs.domain; })
|
.map(function (f) { return f.get('value').attrs.domain; })
|
||||||
.reject(_.isEmpty)
|
.reject(_.isEmpty)
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
|
@ -1014,14 +1013,14 @@ instance.web.search.Field = instance.web.search.Input.extend( /** @lends instanc
|
||||||
return facet.value();
|
return facet.value();
|
||||||
},
|
},
|
||||||
get_context: function (facet) {
|
get_context: function (facet) {
|
||||||
var val = this.get_value(facet);
|
|
||||||
// A field needs a value to be "active", and a context to send when
|
// A field needs a value to be "active", and a context to send when
|
||||||
// active
|
// active
|
||||||
var has_value = (val !== null && val !== '');
|
|
||||||
var context = this.attrs.context;
|
var context = this.attrs.context;
|
||||||
if (!(has_value && context)) {
|
if (!context) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
var val = this.get_value(facet);
|
||||||
|
var has_value = (val !== null && val !== '');
|
||||||
return new instance.web.CompoundContext(context)
|
return new instance.web.CompoundContext(context)
|
||||||
.set_eval_context({self: val});
|
.set_eval_context({self: val});
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,6 +4,25 @@ $(document).ready(function () {
|
||||||
xhr.send(null);
|
xhr.send(null);
|
||||||
var doc = xhr.responseXML;
|
var doc = xhr.responseXML;
|
||||||
|
|
||||||
|
var noop = function () {};
|
||||||
|
/**
|
||||||
|
* Make connection RPC responses mockable by setting keys on the
|
||||||
|
* Connection#responses object (key is the URL, value is the function to
|
||||||
|
* call with the RPC request payload)
|
||||||
|
*
|
||||||
|
* @param {openerp.web.Connection} connection connection instance to mockify
|
||||||
|
* @param {Object} [responses] url:function mapping to seed the mock connection
|
||||||
|
*/
|
||||||
|
var mockifyRPC = function (connection, responses) {
|
||||||
|
connection.responses = responses || {};
|
||||||
|
connection.rpc_function = function (url, payload) {
|
||||||
|
if (!(url.url in this.responses)) {
|
||||||
|
return $.Deferred().reject({}, 'failed', _.str.sprintf("Url %s not found in mock responses", url.url)).promise();
|
||||||
|
}
|
||||||
|
return $.when(this.responses[url.url](payload));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
var instance;
|
var instance;
|
||||||
module('query', {
|
module('query', {
|
||||||
setup: function () {
|
setup: function () {
|
||||||
|
@ -157,16 +176,7 @@ $(document).ready(function () {
|
||||||
|
|
||||||
instance.web.qweb.add_template(doc);
|
instance.web.qweb.add_template(doc);
|
||||||
|
|
||||||
instance.connection.responses = {};
|
mockifyRPC(instance.connection);
|
||||||
instance.connection.rpc_function = function (url, payload) {
|
|
||||||
if (!(url.url in this.responses)) {
|
|
||||||
return $.Deferred().reject(
|
|
||||||
{}, 'failed',
|
|
||||||
_.str.sprintf("Url %s not found in mock responses",
|
|
||||||
url.url)).promise();
|
|
||||||
}
|
|
||||||
return $.when(this.responses[url.url](payload));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -220,7 +230,11 @@ $(document).ready(function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
var dataset = {model: 'dummy.model', get_context: function () { return {}; }};
|
var dataset = {model: 'dummy.model', get_context: function () { return {}; }};
|
||||||
return new instance.web.SearchView(null, dataset, false, defaults);
|
var view = new instance.web.SearchView(null, dataset, false, defaults);
|
||||||
|
view.on_invalid.add(function () {
|
||||||
|
ok(false, JSON.stringify([].slice(arguments)));
|
||||||
|
});
|
||||||
|
return view;
|
||||||
}
|
}
|
||||||
asyncTest('calling', 2, function () {
|
asyncTest('calling', 2, function () {
|
||||||
var defaults_called = false;
|
var defaults_called = false;
|
||||||
|
@ -401,16 +415,7 @@ $(document).ready(function () {
|
||||||
|
|
||||||
instance.web.qweb.add_template(doc);
|
instance.web.qweb.add_template(doc);
|
||||||
|
|
||||||
instance.connection.responses = {};
|
mockifyRPC(instance.connection);
|
||||||
instance.connection.rpc_function = function (url, payload) {
|
|
||||||
if (!(url.url in this.responses)) {
|
|
||||||
return $.Deferred().reject(
|
|
||||||
{}, 'failed',
|
|
||||||
_.str.sprintf("Url %s not found in mock responses",
|
|
||||||
url.url)).promise();
|
|
||||||
}
|
|
||||||
return $.when(this.responses[url.url](payload));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
asyncTest('calling', 4, function () {
|
asyncTest('calling', 4, function () {
|
||||||
|
@ -449,7 +454,7 @@ $(document).ready(function () {
|
||||||
var completion = {
|
var completion = {
|
||||||
label: "Dummy",
|
label: "Dummy",
|
||||||
facet: {
|
facet: {
|
||||||
field: {},
|
field: {get_domain: noop, get_context: noop, get_groupby: noop},
|
||||||
category: 'Dummy',
|
category: 'Dummy',
|
||||||
values: [{label: 'dummy', value: 42}]
|
values: [{label: 'dummy', value: 42}]
|
||||||
}
|
}
|
||||||
|
@ -471,7 +476,7 @@ $(document).ready(function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
asyncTest('facet selection: new value existing facet', 3, function () {
|
asyncTest('facet selection: new value existing facet', 3, function () {
|
||||||
var field = {};
|
var field = {get_domain: noop, get_context: noop, get_groupby: noop};
|
||||||
var completion = {
|
var completion = {
|
||||||
label: "Dummy",
|
label: "Dummy",
|
||||||
facet: {
|
facet: {
|
||||||
|
@ -656,6 +661,115 @@ $(document).ready(function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module('search-serialization', {
|
||||||
|
setup: function () {
|
||||||
|
instance = window.openerp.init([]);
|
||||||
|
window.openerp.web.corelib(instance);
|
||||||
|
window.openerp.web.coresetup(instance);
|
||||||
|
window.openerp.web.chrome(instance);
|
||||||
|
window.openerp.web.data(instance);
|
||||||
|
window.openerp.web.search(instance);
|
||||||
|
|
||||||
|
instance.web.qweb.add_template(doc);
|
||||||
|
|
||||||
|
mockifyRPC(instance.connection);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
asyncTest('No facet, no call', 6, function () {
|
||||||
|
var got_domain = false, got_context = false, got_groupby = false;
|
||||||
|
var $fix = $('#qunit-fixture');
|
||||||
|
var view = makeSearchView({
|
||||||
|
get_domain: function () {
|
||||||
|
got_domain = true;
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
get_context: function () {
|
||||||
|
got_context = true;
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
get_groupby: function () {
|
||||||
|
got_groupby = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var ds, cs, gs;
|
||||||
|
view.on_search.add(function (d, c, g) {
|
||||||
|
ds = d, cs = c, gs = g;
|
||||||
|
});
|
||||||
|
view.appendTo($fix)
|
||||||
|
.always(start)
|
||||||
|
.fail(function (error) { ok(false, error.message); })
|
||||||
|
.done(function () {
|
||||||
|
view.do_search();
|
||||||
|
ok(!got_domain, "no facet, should not have fetched domain");
|
||||||
|
ok(_(ds).isEmpty(), "domains list should be empty");
|
||||||
|
|
||||||
|
ok(!got_context, "no facet, should not have fetched context");
|
||||||
|
ok(_(cs).isEmpty(), "contexts list should be empty");
|
||||||
|
|
||||||
|
ok(!got_groupby, "no facet, should not have fetched groupby");
|
||||||
|
ok(_(gs).isEmpty(), "groupby list should be empty");
|
||||||
|
})
|
||||||
|
});
|
||||||
|
asyncTest('London, calling', 8, function () {
|
||||||
|
var got_domain = false, got_context = false, got_groupby = false;
|
||||||
|
var $fix = $('#qunit-fixture');
|
||||||
|
var view = makeSearchView({
|
||||||
|
get_domain: function (facet) {
|
||||||
|
equal(facet.get('category'), "dummy");
|
||||||
|
deepEqual(facet.values.toJSON(), [{label: "42", value: 42}]);
|
||||||
|
got_domain = true;
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
get_context: function () {
|
||||||
|
got_context = true;
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
get_groupby: function () {
|
||||||
|
got_groupby = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, {dummy: 42});
|
||||||
|
var ds, cs, gs;
|
||||||
|
view.on_search.add(function (d, c, g) {
|
||||||
|
ds = d, cs = c, gs = g;
|
||||||
|
});
|
||||||
|
view.appendTo($fix)
|
||||||
|
.always(start)
|
||||||
|
.fail(function (error) { ok(false, error.message); })
|
||||||
|
.done(function () {
|
||||||
|
view.do_search();
|
||||||
|
ok(got_domain, "should have fetched domain");
|
||||||
|
ok(_(ds).isEmpty(), "domains list should be empty");
|
||||||
|
|
||||||
|
ok(got_context, "should have fetched context");
|
||||||
|
ok(_(cs).isEmpty(), "contexts list should be empty");
|
||||||
|
|
||||||
|
ok(got_groupby, "should have fetched groupby");
|
||||||
|
ok(_(gs).isEmpty(), "groupby list should be empty");
|
||||||
|
})
|
||||||
|
});
|
||||||
|
asyncTest('Generate domains', 1, function () {
|
||||||
|
var $fix = $('#qunit-fixture');
|
||||||
|
var view = makeSearchView({
|
||||||
|
get_domain: function (facet) {
|
||||||
|
return facet.values.map(function (value) {
|
||||||
|
return ['win', '4', value.get('value')];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, {dummy: 42});
|
||||||
|
var ds;
|
||||||
|
view.on_search.add(function (d) { ds = d; });
|
||||||
|
view.appendTo($fix)
|
||||||
|
.always(start)
|
||||||
|
.fail(function (error) { ok(false, error.message); })
|
||||||
|
.done(function () {
|
||||||
|
view.do_search();
|
||||||
|
deepEqual(ds, [[['win', '4', 42]]],
|
||||||
|
"search should yield an array of contexts");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
module('drawer', {
|
module('drawer', {
|
||||||
setup: function () {
|
setup: function () {
|
||||||
instance = window.openerp.init([]);
|
instance = window.openerp.init([]);
|
||||||
|
@ -667,16 +781,7 @@ $(document).ready(function () {
|
||||||
|
|
||||||
instance.web.qweb.add_template(doc);
|
instance.web.qweb.add_template(doc);
|
||||||
|
|
||||||
instance.connection.responses = {};
|
mockifyRPC(instance.connection);
|
||||||
instance.connection.rpc_function = function (url, payload) {
|
|
||||||
if (!(url.url in this.responses)) {
|
|
||||||
return $.Deferred().reject(
|
|
||||||
{}, 'failed',
|
|
||||||
_.str.sprintf("Url %s not found in mock responses",
|
|
||||||
url.url)).promise();
|
|
||||||
}
|
|
||||||
return $.when(this.responses[url.url](payload));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
asyncTest('is-drawn', 2, function () {
|
asyncTest('is-drawn', 2, function () {
|
||||||
|
@ -704,17 +809,8 @@ $(document).ready(function () {
|
||||||
|
|
||||||
instance.web.qweb.add_template(doc);
|
instance.web.qweb.add_template(doc);
|
||||||
|
|
||||||
instance.connection.responses = {};
|
mockifyRPC(instance.connection, {
|
||||||
instance.connection.rpc_function = function (url, payload) {
|
'/web/searchview/load': function () {
|
||||||
if (!(url.url in this.responses)) {
|
|
||||||
return $.Deferred().reject(
|
|
||||||
{}, 'failed',
|
|
||||||
_.str.sprintf("Url %s not found in mock responses",
|
|
||||||
url.url)).promise();
|
|
||||||
}
|
|
||||||
return $.when(this.responses[url.url](payload));
|
|
||||||
};
|
|
||||||
instance.connection.responses['/web/searchview/load'] = function () {
|
|
||||||
// view with a single group of filters
|
// view with a single group of filters
|
||||||
return {result: {fields_view: {
|
return {result: {fields_view: {
|
||||||
type: 'search',
|
type: 'search',
|
||||||
|
@ -740,7 +836,8 @@ $(document).ready(function () {
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}}};
|
}}};
|
||||||
};
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
asyncTest('drawn', 3, function () {
|
asyncTest('drawn', 3, function () {
|
||||||
|
|
|
@ -249,21 +249,33 @@ Converting from facet objects
|
||||||
Ultimately, the point of the search view is to allow searching. In
|
Ultimately, the point of the search view is to allow searching. In
|
||||||
OpenERP this is done via :ref:`domains <openerpserver:domains>`. On
|
OpenERP this is done via :ref:`domains <openerpserver:domains>`. On
|
||||||
the other hand, the OpenERP Web 7 search view's state is modelled
|
the other hand, the OpenERP Web 7 search view's state is modelled
|
||||||
after a collection of :js:class:`~VS.model.SearchFacet`, and each
|
after a collection of :js:class:`~openerp.web.search.Facet`, and each
|
||||||
field of a search view may have special requirements when it comes to
|
field of a search view may have special requirements when it comes to
|
||||||
the domains it produces [#]_.
|
the domains it produces [#]_.
|
||||||
|
|
||||||
So there needs to be some way of mapping
|
So there needs to be some way of mapping
|
||||||
:js:class:`~VS.model.SearchFacet` objects to OpenERP search data.
|
:js:class:`~openerp.web.search.Facet` objects to OpenERP search data.
|
||||||
|
|
||||||
This is done via an input's
|
This is done via an input's
|
||||||
:js:func:`~openerp.web.search.Input.get_domain` and
|
:js:func:`~openerp.web.search.Input.get_domain` and
|
||||||
:js:func:`~openerp.web.search.Input.get_context`. Each takes a
|
:js:func:`~openerp.web.search.Input.get_context`. Each takes a
|
||||||
:js:class:`~VS.model.SearchFacet` and returns whatever it's supposed
|
:js:class:`~openerp.web.search.Facet` and returns whatever it's
|
||||||
to generate (a domain or a context, respectively). Either can return
|
supposed to generate (a domain or a context, respectively). Either can
|
||||||
``null`` if the current value does not map to a domain or context, and
|
return ``null`` if the current value does not map to a domain or
|
||||||
can throw an :js:class:`~openerp.web.search.Invalid` exception if the
|
context, and can throw an :js:class:`~openerp.web.search.Invalid`
|
||||||
value is not valid at all for the field.
|
exception if the value is not valid at all for the field.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The :js:class:`~openerp.web.search.Facet` object can have any
|
||||||
|
number of values (from 1 upwards)
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
There is a third conversion method,
|
||||||
|
:js:func:`~openerp.web.search.Input.get_groupby`, which returns an
|
||||||
|
``Array`` of groupby domains rather than a single context. At this
|
||||||
|
point, it is only implemented on (and used by) filters.
|
||||||
|
|
||||||
Converting to facet objects
|
Converting to facet objects
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
Loading…
Reference in New Issue