1 $(document).ready(function () {
5 instance = openerp.testing.instanceFor('search');
8 test('Adding a facet to the query creates a facet and a value', function () {
9 var query = new instance.web.search.SearchQuery;
14 values: [{label: 'Value', value: 3}]
17 var facet = query.at(0);
18 equal(facet.get('category'), 'Foo');
19 equal(facet.get('field'), field);
20 deepEqual(facet.get('values'), [{label: 'Value', value: 3}]);
22 test('Adding two facets', function () {
23 var query = new instance.web.search.SearchQuery;
25 { category: 'Foo', field: {}, values: [{label: 'Value', value: 3}] },
26 { category: 'Bar', field: {}, values: [{label: 'Value 2', value: 4}] }
29 equal(query.length, 2);
30 equal(query.at(0).values.length, 1);
31 equal(query.at(1).values.length, 1);
33 test('If a facet already exists, add values to it', function () {
34 var query = new instance.web.search.SearchQuery;
36 query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
37 query.add({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
39 equal(query.length, 1, "adding an existing facet should merge new values into old facet");
40 var facet = query.at(0);
41 deepEqual(facet.get('values'), [
42 {label: 'V1', value: 0},
43 {label: 'V2', value: 1}
46 test('Facet being implicitly changed should trigger change, not add', function () {
47 var query = new instance.web.search.SearchQuery;
48 var field = {}, added = false, changed = false;
49 query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
50 query.on('add', function () { added = true; })
51 .on('change', function () { changed = true });
52 query.add({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
54 ok(!added, "query.add adding values to a facet should not trigger an add");
55 ok(changed, "query.add adding values to a facet should not trigger a change");
57 test('Toggling a facet, value which does not exist should add it', function () {
58 var query = new instance.web.search.SearchQuery;
60 query.toggle({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
62 equal(query.length, 1, "Should have created a single facet");
63 var facet = query.at(0);
64 equal(facet.values.length, 1, "Facet should have a single value");
65 deepEqual(facet.get('values'), [{label: 'V1', value: 0}],
66 "Facet's value should match input");
68 test('Toggling a facet which exists with a value which does not should add the value to the facet', function () {
70 var query = new instance.web.search.SearchQuery;
71 query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
72 query.toggle({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
74 equal(query.length, 1, "Should have edited the existing facet");
75 var facet = query.at(0);
76 equal(facet.values.length, 2, "Should have added the value to the existing facet");
77 deepEqual(facet.get('values'), [
78 {label: 'V1', value: 0},
79 {label: 'V2', value: 1}
82 test('Toggling a facet which exists with a value which does as well should remove the value from the facet', function () {
84 var query = new instance.web.search.SearchQuery;
85 query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
86 query.add({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
88 query.toggle({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
90 equal(query.length, 1, 'Should have the same single facet');
91 var facet = query.at(0);
92 equal(facet.values.length, 1, "Should only have one value left in the facet");
93 deepEqual(facet.get('values'), [
94 {label: 'V1', value: 0}
97 test('Toggling off the last value of a facet should remove the facet', function () {
99 var query = new instance.web.search.SearchQuery;
100 query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
102 query.toggle({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
104 equal(query.length, 0, 'Should have removed the facet');
106 test('Intermediate emptiness should not remove the facet', function () {
108 var query = new instance.web.search.SearchQuery;
109 query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
111 query.toggle({category: 'A', field: field, values: [
112 {label: 'V1', value: 0},
113 {label: 'V2', value: 1}
116 equal(query.length, 1, 'Should not have removed the facet');
117 var facet = query.at(0);
118 equal(facet.values.length, 1, "Should have one value");
119 deepEqual(facet.get('values'), [
120 {label: 'V2', value: 1}
124 test('Reseting with multiple facets should still work to load defaults', function () {
125 var query = new instance.web.search.SearchQuery;
128 {category: 'A', field: field, values: [{label: 'V1', value: 0}]},
129 {category: 'A', field: field, values: [{label: 'V2', value: 1}]}]);
131 equal(query.length, 1, 'Should have created a single facet');
132 equal(query.at(0).values.length, 2, 'the facet should have merged two values');
133 deepEqual(query.at(0).get('values'), [
134 {label: 'V1', value: 0},
135 {label: 'V2', value: 1}
141 instance = openerp.testing.instanceFor('search');
143 openerp.testing.loadTemplate(instance);
145 openerp.testing.mockifyRPC(instance);
150 * Builds a basic search view with a single "dummy" field. The dummy
151 * extends `instance.web.search.Field`, it does not add any (class)
152 * attributes beyond what is provided through ``dummy_widget_attributes``.
154 * The view is returned un-started, it is the caller's role to start it
155 * (or use DOM-insertion methods to start it indirectly).
157 * @param [dummy_widget_attributes={}]
158 * @param [defaults={}]
159 * @return {instance.web.SearchView}
161 function makeSearchView(dummy_widget_attributes, defaults) {
162 instance.web.search.fields.add(
163 'dummy', 'instance.dummy.DummyWidget');
165 instance.dummy.DummyWidget = instance.web.search.Field.extend(
166 dummy_widget_attributes || {});
167 if (!('/web/searchview/load' in instance.session.responses)) {
168 instance.session.responses['/web/searchview/load'] = function () {
169 return {result: {fields_view: {
172 dummy: {type: 'char', string: "Dummy"}
189 instance.session.responses['/web/searchview/get_filters'] = function () {
192 instance.session.responses['/web/searchview/fields_get'] = function () {
193 return {result: {fields: {
194 dummy: {type: 'char', string: 'Dummy'}
198 var dataset = {model: 'dummy.model', get_context: function () { return {}; }};
199 var view = new instance.web.SearchView(null, dataset, false, defaults);
200 view.on_invalid.add(function () {
201 ok(false, JSON.stringify([].slice(arguments)));
205 asyncTest('calling', 2, function () {
206 var defaults_called = false;
208 var view = makeSearchView({
209 facet_for_defaults: function (defaults) {
210 defaults_called = true;
214 values: [{label: 'dummy', value: defaults.dummy}]
218 view.appendTo($('#qunit-fixture'))
220 .fail(function (error) { ok(false, error.message); })
222 ok(defaults_called, "should have called defaults");
225 [{category: 'Dummy', values: [{label: 'dummy', value: 42}]}],
226 "should have generated a facet with the default value");
229 asyncTest('FilterGroup', 3, function () {
230 var view = {inputs: [], query: {on: function () {}}};
231 var filter_a = new instance.web.search.Filter(
232 {attrs: {name: 'a'}}, view);
233 var filter_b = new instance.web.search.Filter(
234 {attrs: {name: 'b'}}, view);
235 var group = new instance.web.search.FilterGroup(
236 [filter_a, filter_b], view);
237 group.facet_for_defaults({a: true, b: true})
239 .fail(function (error) { ok(false, error && error.message); })
240 .done(function (facet) {
242 if (!(model instanceof instance.web.search.Facet)) {
243 model = new instance.web.search.Facet(facet);
245 var values = model.values;
246 equal(values.length, 2, 'facet should have two values');
247 strictEqual(values.at(0).get('value'), filter_a);
248 strictEqual(values.at(1).get('value'), filter_b);
251 asyncTest('Field', 4, function () {
252 var view = {inputs: []};
253 var f = new instance.web.search.Field(
254 {attrs: {string: 'Dummy', name: 'dummy'}}, {}, view);
255 f.facet_for_defaults({dummy: 42})
257 .fail(function (error) { ok(false, error && error.message); })
258 .done(function (facet) {
260 if (!(model instanceof instance.web.search.Facet)) {
261 model = new instance.web.search.Facet(facet);
264 model.get('category'),
266 "facet category should be field label");
268 model.get('field'), f,
269 "facet field should be field which created default");
270 equal(model.values.length, 1, "facet should have a single value");
272 model.values.toJSON(),
273 [{label: '42', value: 42}],
274 "facet value should match provided default");
277 asyncTest('Selection: valid value', 4, function () {
278 var view = {inputs: []};
279 var f = new instance.web.search.SelectionField(
280 {attrs: {name: 'dummy', string: 'Dummy'}},
281 {selection: [[1, "Foo"], [2, "Bar"], [3, "Baz"], [4, "Qux"]]},
283 f.facet_for_defaults({dummy: 3})
285 .fail(function (error) { ok(false, error && error.message); })
286 .done(function (facet) {
288 if (!(model instanceof instance.web.search.Facet)) {
289 model = new instance.web.search.Facet(facet);
292 model.get('category'),
294 "facet category should be field label");
296 model.get('field'), f,
297 "facet field should be field which created default");
298 equal(model.values.length, 1, "facet should have a single value");
300 model.values.toJSON(),
301 [{label: 'Baz', value: 3}],
302 "facet value should match provided default's selection");
305 asyncTest('Selection: invalid value', 1, function () {
306 var view = {inputs: []};
307 var f = new instance.web.search.SelectionField(
308 {attrs: {name: 'dummy', string: 'Dummy'}},
309 {selection: [[1, "Foo"], [2, "Bar"], [3, "Baz"], [4, "Qux"]]},
311 f.facet_for_defaults({dummy: 42})
313 .fail(function (error) { ok(false, error && error.message); })
314 .done(function (facet) {
315 ok(!facet, "an invalid value should result in a not-facet");
318 asyncTest("M2O default: value", 7, function () {
319 var view = {inputs: []}, id = 4;
320 var f = new instance.web.search.ManyToOneField(
321 {attrs: {name: 'dummy', string: 'Dummy'}},
322 {relation: 'dummy.model.name'},
324 instance.session.responses['/web/dataset/call_kw'] = function (req) {
325 equal(req.params.method, 'name_get',
326 "m2o should resolve default id");
327 equal(req.params.model, f.attrs.relation,
328 "query model should match m2o relation");
329 equal(req.params.args[0], id);
330 return {result: [[id, "DumDumDum"]]};
332 f.facet_for_defaults({dummy: id})
334 .fail(function (error) { ok(false, error && error.message); })
335 .done(function (facet) {
337 if (!(model instanceof instance.web.search.Facet)) {
338 model = new instance.web.search.Facet(facet);
341 model.get('category'),
343 "facet category should be field label");
345 model.get('field'), f,
346 "facet field should be field which created default");
347 equal(model.values.length, 1, "facet should have a single value");
349 model.values.toJSON(),
350 [{label: 'DumDumDum', value: id}],
351 "facet value should match provided default's selection");
354 asyncTest("M2O default: value", 1, function () {
355 var view = {inputs: []}, id = 4;
356 var f = new instance.web.search.ManyToOneField(
357 {attrs: {name: 'dummy', string: 'Dummy'}},
358 {relation: 'dummy.model.name'},
360 instance.session.responses['/web/dataset/call_kw'] = function (req) {
363 f.facet_for_defaults({dummy: id})
365 .fail(function (error) { ok(false, error && error.message); })
366 .done(function (facet) {
367 ok(!facet, "an invalid m2o default should yield a non-facet");
371 module('completions', {
373 instance = openerp.testing.instanceFor('search');
375 openerp.testing.loadTemplate(instance);
377 openerp.testing.mockifyRPC(instance);
380 asyncTest('calling', 4, function () {
381 var view = makeSearchView({
382 complete: function () {
388 values: [{label: 'dummy', value: 42}]
393 view.appendTo($('#qunit-fixture'))
395 view.complete_global_search({term: "dum"}, function (completions) {
397 equal(completions.length, 1, "should have a single completion");
398 var completion = completions[0];
399 equal(completion.label, "Dummy",
400 "should have provided label");
401 equal(completion.facet.category, "Dummy",
402 "should have provided category");
403 deepEqual(completion.facet.values,
404 [{label: 'dummy', value: 42}],
405 "should have provided values");
409 asyncTest('facet selection', 2, function () {
414 get_domain: openerp.testing.noop,
415 get_context: openerp.testing.noop,
416 get_groupby: openerp.testing.noop
419 values: [{label: 'dummy', value: 42}]
423 var view = makeSearchView({});
424 view.appendTo($('#qunit-fixture'))
426 .fail(function (error) { ok(false, error.message); })
428 view.select_completion(
429 {preventDefault: function () {}},
431 equal(view.query.length, 1, "should have one facet in the query");
433 view.query.at(0).toJSON(),
434 {category: 'Dummy', values: [{label: 'dummy', value: 42}]},
435 "should have the right facet in the query");
438 asyncTest('facet selection: new value existing facet', 3, function () {
440 get_domain: openerp.testing.noop,
441 get_context: openerp.testing.noop,
442 get_groupby: openerp.testing.noop
449 values: [{label: 'dummy', value: 42}]
453 var view = makeSearchView({});
454 view.appendTo($('#qunit-fixture'))
456 .fail(function (error) { ok(false, error.message); })
458 view.query.add({field: field, category: 'Dummy',
459 values: [{label: 'previous', value: 41}]});
460 equal(view.query.length, 1, 'should have newly added facet');
461 view.select_completion(
462 {preventDefault: function () {}},
464 equal(view.query.length, 1, "should still have only one facet");
465 var facet = view.query.at(0);
468 [{label: 'previous', value: 41}, {label: 'dummy', value: 42}],
469 "should have added selected value to old one");
472 asyncTest('Field', 1, function () {
473 var view = {inputs: []};
474 var f = new instance.web.search.Field({attrs: {}}, {}, view);
477 .fail(function (error) { ok(false, error.message); })
478 .done(function (completions) {
479 ok(_(completions).isEmpty(), "field should not provide any completion");
482 asyncTest('CharField', 6, function () {
483 var view = {inputs: []};
484 var f = new instance.web.search.CharField(
485 {attrs: {string: "Dummy"}}, {}, view);
488 .fail(function (error) { ok(false, error.message); })
489 .done(function (completions) {
490 equal(completions.length, 1, "should provide a single completion");
491 var c = completions[0];
492 equal(c.label, "Search <em>Dummy</em> for: <strong>foo<</strong>",
493 "should propose a fuzzy matching/searching, with the" +
495 ok(c.facet, "completion should contain a facet proposition");
496 var facet = new instance.web.search.Facet(c.facet);
497 equal(facet.get('category'), f.attrs.string,
498 "completion facet should bear the field's name");
499 strictEqual(facet.get('field'), f,
500 "completion facet should yield the field");
501 deepEqual(facet.values.toJSON(), [{label: 'foo<', value: 'foo<'}],
502 "facet should have single value using completion item");
505 asyncTest('Selection: match found', 14, function () {
506 var view = {inputs: []};
507 var f = new instance.web.search.SelectionField(
508 {attrs: {string: "Dummy"}},
509 {selection: [[1, "Foo"], [2, "Bar"], [3, "Baz"], [4, "Bazador"]]},
513 .fail(function (error) { ok(false, error.message); })
514 .done(function (completions) {
515 equal(completions.length, 4,
516 "should provide two completions and a section title");
517 deepEqual(completions[0], {label: "Dummy"});
519 var c1 = completions[1];
520 equal(c1.label, "Bar");
521 equal(c1.facet.category, f.attrs.string);
522 strictEqual(c1.facet.field, f);
523 deepEqual(c1.facet.values, [{label: "Bar", value: 2}]);
525 var c2 = completions[2];
526 equal(c2.label, "Baz");
527 equal(c2.facet.category, f.attrs.string);
528 strictEqual(c2.facet.field, f);
529 deepEqual(c2.facet.values, [{label: "Baz", value: 3}]);
531 var c3 = completions[3];
532 equal(c3.label, "Bazador");
533 equal(c3.facet.category, f.attrs.string);
534 strictEqual(c3.facet.field, f);
535 deepEqual(c3.facet.values, [{label: "Bazador", value: 4}]);
538 asyncTest('Selection: no match', 1, function () {
539 var view = {inputs: []};
540 var f = new instance.web.search.SelectionField(
541 {attrs: {string: "Dummy"}},
542 {selection: [[1, "Foo"], [2, "Bar"], [3, "Baz"], [4, "Bazador"]]},
546 .fail(function (error) { ok(false, error.message); })
547 .done(function (completions) {
548 ok(!completions, "if no value matches the needle, no completion shall be provided");
551 asyncTest('Date', 6, function () {
552 instance.web._t.database.parameters = {
553 date_format: '%Y-%m-%d',
554 time_format: '%H:%M:%S'
556 var view = {inputs: []};
557 var f = new instance.web.search.DateField(
558 {attrs: {string: "Dummy"}}, {type: 'datetime'}, view);
559 f.complete('2012-05-21T21:21:21')
561 .fail(function (error) { ok(false, error.message); })
562 .done(function (completions) {
563 equal(completions.length, 1, "should provide a single completion");
564 var c = completions[0];
565 equal(c.label, "Search <em>Dummy</em> at: <strong>2012-05-21 21:21:21</strong>");
566 var facet = new instance.web.search.Facet(c.facet);
567 equal(facet.get('category'), f.attrs.string);
568 equal(facet.get('field'), f);
569 var value = facet.values.at(0);
570 equal(value.get('label'), "2012-05-21 21:21:21");
571 equal(value.get('value').getTime(),
572 new Date(2012, 4, 21, 21, 21, 21).getTime());
575 asyncTest("M2O", 15, function () {
576 instance.session.responses['/web/dataset/call_kw'] = function (req) {
577 equal(req.params.method, "name_search");
578 equal(req.params.model, "dummy.model");
579 deepEqual(req.params.args, []);
580 deepEqual(req.params.kwargs.name, 'bob');
581 return {result: [[42, "choice 1"], [43, "choice @"]]}
584 var view = {inputs: []};
585 var f = new instance.web.search.ManyToOneField(
586 {attrs: {string: 'Dummy'}}, {relation: 'dummy.model'}, view);
589 .fail(function (error) { ok(false, error.message); })
591 equal(c.length, 3, "should return results + title");
593 equal(title.label, f.attrs.string, "title should match field name");
594 ok(!title.facet, "title should not have a facet");
596 var f1 = new instance.web.search.Facet(c[1].facet);
597 equal(c[1].label, "choice 1");
598 equal(f1.get('category'), f.attrs.string);
599 equal(f1.get('field'), f);
600 deepEqual(f1.values.toJSON(), [{label: 'choice 1', value: 42}]);
602 var f2 = new instance.web.search.Facet(c[2].facet);
603 equal(c[2].label, "choice @");
604 equal(f2.get('category'), f.attrs.string);
605 equal(f2.get('field'), f);
606 deepEqual(f2.values.toJSON(), [{label: 'choice @', value: 43}]);
609 asyncTest("M2O no match", 5, function () {
610 instance.session.responses['/web/dataset/call_kw'] = function (req) {
611 equal(req.params.method, "name_search");
612 equal(req.params.model, "dummy.model");
613 deepEqual(req.params.args, []);
614 deepEqual(req.params.kwargs.name, 'bob');
617 var view = {inputs: []};
618 var f = new instance.web.search.ManyToOneField(
619 {attrs: {string: 'Dummy'}}, {relation: 'dummy.model'}, view);
622 .fail(function (error) { ok(false, error.message); })
624 ok(!c, "no match should yield no completion");
628 module('search-serialization', {
630 instance = openerp.testing.instanceFor('search');
632 openerp.testing.loadTemplate(instance);
634 openerp.testing.mockifyRPC(instance);
637 asyncTest('No facet, no call', 6, function () {
638 var got_domain = false, got_context = false, got_groupby = false;
639 var $fix = $('#qunit-fixture');
640 var view = makeSearchView({
641 get_domain: function () {
645 get_context: function () {
649 get_groupby: function () {
655 view.on_search.add(function (d, c, g) {
656 ds = d, cs = c, gs = g;
660 .fail(function (error) { ok(false, error.message); })
663 ok(!got_domain, "no facet, should not have fetched domain");
664 ok(_(ds).isEmpty(), "domains list should be empty");
666 ok(!got_context, "no facet, should not have fetched context");
667 ok(_(cs).isEmpty(), "contexts list should be empty");
669 ok(!got_groupby, "no facet, should not have fetched groupby");
670 ok(_(gs).isEmpty(), "groupby list should be empty");
673 asyncTest('London, calling', 8, function () {
674 var got_domain = false, got_context = false, got_groupby = false;
675 var $fix = $('#qunit-fixture');
676 var view = makeSearchView({
677 get_domain: function (facet) {
678 equal(facet.get('category'), "Dummy");
679 deepEqual(facet.values.toJSON(), [{label: "42", value: 42}]);
683 get_context: function () {
687 get_groupby: function () {
693 view.on_search.add(function (d, c, g) {
694 ds = d, cs = c, gs = g;
698 .fail(function (error) { ok(false, error.message); })
701 ok(got_domain, "should have fetched domain");
702 ok(_(ds).isEmpty(), "domains list should be empty");
704 ok(got_context, "should have fetched context");
705 ok(_(cs).isEmpty(), "contexts list should be empty");
707 ok(got_groupby, "should have fetched groupby");
708 ok(_(gs).isEmpty(), "groupby list should be empty");
711 asyncTest('Generate domains', 1, function () {
712 var $fix = $('#qunit-fixture');
713 var view = makeSearchView({
714 get_domain: function (facet) {
715 return facet.values.map(function (value) {
716 return ['win', '4', value.get('value')];
721 view.on_search.add(function (d) { ds = d; });
724 .fail(function (error) { ok(false, error.message); })
727 deepEqual(ds, [[['win', '4', 42]]],
728 "search should yield an array of contexts");
732 test('Field single value, default domain & context', function () {
733 var f = new instance.web.search.Field({}, {name: 'foo'}, {inputs: []});
734 var facet = new instance.web.search.Facet({
736 values: [{value: 42}]
739 deepEqual(f.get_domain(facet), [['foo', '=', 42]],
740 "default field domain is a strict equality of name to facet's value");
741 equal(f.get_context(facet), null,
742 "default field context is null");
744 test('Field multiple values, default domain & context', function () {
745 var f = new instance.web.search.Field({}, {name: 'foo'}, {inputs: []});
746 var facet = new instance.web.search.Facet({
748 values: [{value: 42}, {value: 68}, {value: 999}]
751 var actual_domain = f.get_domain(facet);
752 equal(actual_domain.__ref, "compound_domain",
753 "multiple value should yield compound domain");
754 deepEqual(actual_domain.__domains, [
761 "domain should OR a default domain for each value");
762 equal(f.get_context(facet), null,
763 "default field context is null");
765 test('Field single value, custom domain & context', function () {
766 var f = new instance.web.search.Field({attrs:{
767 context: "{'bob': self}",
768 filter_domain: "[['edmund', 'is', self]]"
769 }}, {name: 'foo'}, {inputs: []});
770 var facet = new instance.web.search.Facet({
772 values: [{value: "great"}]
775 var actual_domain = f.get_domain(facet);
776 equal(actual_domain.__ref, "compound_domain",
777 "@filter_domain should yield compound domain");
778 deepEqual(actual_domain.__domains, [
779 "[['edmund', 'is', self]]"
780 ], 'should hold unevaluated custom domain');
781 deepEqual(actual_domain.get_eval_context(), {
783 }, "evaluation context should hold facet value as self");
785 var actual_context = f.get_context(facet);
786 equal(actual_context.__ref, "compound_context",
787 "@context should yield compound context");
788 deepEqual(actual_context.__contexts, [
790 ], 'should hold unevaluated custom context');
791 deepEqual(actual_context.get_eval_context(), {
793 }, "evaluation context should hold facet value as self");
795 test("M2O default", function () {
796 var f = new instance.web.search.ManyToOneField(
797 {}, {name: 'foo'}, {inputs: []});
798 var facet = new instance.web.search.Facet({
800 values: [{label: "Foo", value: 42}]
803 deepEqual(f.get_domain(facet), [['foo', '=', 42]],
804 "m2o should use identity if default domain");
805 deepEqual(f.get_context(facet), {default_foo: 42},
806 "m2o should use value as context default");
808 test("M2O default multiple values", function () {
809 var f = new instance.web.search.ManyToOneField(
810 {}, {name: 'foo'}, {inputs: []});
811 var facet = new instance.web.search.Facet({
814 {label: "Foo", value: 42},
815 {label: "Bar", value: 36}
819 deepEqual(f.get_domain(facet).__domains,
820 [['|'], [['foo', '=', 42]], [['foo', '=', 36]]],
821 "m2o should or multiple values");
822 equal(f.get_context(facet), null,
823 "m2o should not have default context in case of multiple values");
825 test("M2O custom operator", function () {
826 var f = new instance.web.search.ManyToOneField(
827 {attrs: {operator: 'boos'}}, {name: 'foo'}, {inputs: []});
828 var facet = new instance.web.search.Facet({
830 values: [{label: "Foo", value: 42}]
833 deepEqual(f.get_domain(facet), [['foo', 'boos', 'Foo']],
834 "m2o should use label with custom operators");
835 deepEqual(f.get_context(facet), {default_foo: 42},
836 "m2o should use value as context default");
838 test("M2O custom domain & context", function () {
839 var f = new instance.web.search.ManyToOneField({attrs: {
840 context: "{'whee': self}",
841 filter_domain: "[['filter', 'is', self]]"
842 }}, {name: 'foo'}, {inputs: []});
843 var facet = new instance.web.search.Facet({
845 values: [{label: "Foo", value: 42}]
848 var domain = f.get_domain(facet);
849 deepEqual(domain.__domains, [
850 "[['filter', 'is', self]]"
852 deepEqual(domain.get_eval_context(), {
854 }, "custom domain's self should be label");
855 var context = f.get_context(facet);
856 deepEqual(context.__contexts, [
859 deepEqual(context.get_eval_context(), {
861 }, "custom context's self should be label");
864 asyncTest('FilterGroup', 6, function () {
865 var view = {inputs: [], query: {on: function () {}}};
866 var filter_a = new instance.web.search.Filter(
867 {attrs: {name: 'a', context: 'c1', domain: 'd1'}}, view);
868 var filter_b = new instance.web.search.Filter(
869 {attrs: {name: 'b', context: 'c2', domain: 'd2'}}, view);
870 var filter_c = new instance.web.search.Filter(
871 {attrs: {name: 'c', context: 'c3', domain: 'd3'}}, view);
872 var group = new instance.web.search.FilterGroup(
873 [filter_a, filter_b, filter_c], view);
874 group.facet_for_defaults({a: true, c: true})
876 .fail(function (error) { ok(false, error && error.message); })
877 .done(function (facet) {
879 if (!(model instanceof instance.web.search.Facet)) {
880 model = new instance.web.search.Facet(facet);
883 var domain = group.get_domain(model);
884 equal(domain.__ref, 'compound_domain',
885 "domain should be compound");
886 deepEqual(domain.__domains, [
888 ], "domain should OR filter domains");
889 ok(!domain.get_eval_context(), "domain should have no evaluation context");
890 var context = group.get_context(model);
891 equal(context.__ref, 'compound_context',
892 "context should be compound");
893 deepEqual(context.__contexts, [
895 ], "context should merge all filter contexts");
896 ok(!context.get_eval_context(), "context should have no evaluation context");
902 instance = openerp.testing.instanceFor('search');
904 openerp.testing.loadTemplate(instance);
906 openerp.testing.mockifyRPC(instance);
909 asyncTest('clear button', function () {
910 var $fix = $('#qunit-fixture');
911 var view = makeSearchView({
912 facet_for_defaults: function (defaults) {
916 values: [{label: 'dummy', value: defaults.dummy}]
922 .fail(function (error) { ok(false, error.message); })
924 equal(view.query.length, 1, "view should have default facet");
925 $fix.find('.oe_searchview_clear').click();
926 equal(view.query.length, 0, "cleared view should not have any facet");
932 instance = openerp.testing.instanceFor('search');
934 openerp.testing.loadTemplate(instance);
936 openerp.testing.mockifyRPC(instance);
939 asyncTest('is-drawn', 2, function () {
940 var view = makeSearchView();
941 var $fix = $('#qunit-fixture');
944 .fail(function (error) { ok(false, error.message); })
946 ok($fix.find('.oe_searchview_filters').length,
947 "filters drawer control has been drawn");
948 ok($fix.find('.oe_searchview_advanced').length,
949 "filters advanced search has been drawn");
955 instance = openerp.testing.instanceFor('search');
957 openerp.testing.loadTemplate(instance);
959 openerp.testing.mockifyRPC(instance, {
960 '/web/searchview/load': function () {
961 // view with a single group of filters
962 return {result: {fields_view: {
970 attrs: { string: "Foo1", domain: [ ['foo', '=', '1'] ] },
977 domain: [ ['foo', '=', '2'] ] },
981 attrs: { string: "Foo3", domain: [ ['foo', '=', '3'] ] },
990 asyncTest('drawn', 3, function () {
991 var view = makeSearchView();
992 var $fix = $('#qunit-fixture');
995 .fail(function (error) { ok(false, error.message); })
997 var $fs = $fix.find('.oe_searchview_filters ul');
998 // 3 filters, 1 filtergroup, 1 custom filters widget,
999 // 1 advanced and 1 Filters widget
1000 equal(view.inputs.length, 7,
1001 'view should have 7 inputs total');
1002 equal($fs.children().length, 3,
1003 "drawer should have a filter group with 3 filters");
1004 equal(_.str.strip($fs.children().eq(0).text()), "Foo1",
1005 "Text content of first filter option should match filter string");
1008 asyncTest('click adding from empty query', 4, function () {
1009 var view = makeSearchView();
1010 var $fix = $('#qunit-fixture');
1013 .fail(function (error) { ok(false, error.message); })
1015 var $fs = $fix.find('.oe_searchview_filters ul');
1016 $fs.children(':eq(2)').trigger('click');
1017 equal(view.query.length, 1, "click should have added a facet");
1018 var facet = view.query.at(0);
1019 equal(facet.values.length, 1, "facet should have a single value");
1020 var value = facet.values.at(0);
1021 ok(value.get('value') instanceof instance.web.search.Filter,
1022 "value should be a filter");
1023 equal(value.get('label'), "Foo3",
1024 "value should be third filter");
1027 asyncTest('click adding from existing query', 4, function () {
1028 var view = makeSearchView({}, {foo2: true});
1029 var $fix = $('#qunit-fixture');
1032 .fail(function (error) { ok(false, error.message); })
1034 var $fs = $fix.find('.oe_searchview_filters ul');
1035 $fs.children(':eq(2)').trigger('click');
1036 equal(view.query.length, 1, "click should not have changed facet count");
1037 var facet = view.query.at(0);
1038 equal(facet.values.length, 2, "facet should have a second value");
1039 var v1 = facet.values.at(0);
1040 equal(v1.get('label'), "Foo2",
1041 "first value should be default");
1042 var v2 = facet.values.at(1);
1043 equal(v2.get('label'), "Foo3",
1044 "second value should be clicked filter");
1047 asyncTest('click removing from query', 4, function () {
1049 var view = makeSearchView({}, {foo2: true});
1050 view.on_search.add(function () {
1053 var $fix = $('#qunit-fixture');
1056 .fail(function (error) { ok(false, error.message); })
1058 var $fs = $fix.find('.oe_searchview_filters ul');
1060 equal(view.query.length, 1, "query should have default facet");
1061 strictEqual(calls, 0);
1062 $fs.children(':eq(1)').trigger('click');
1063 equal(view.query.length, 0, "click should have removed facet");
1064 strictEqual(calls, 1, "one search should have been triggered");
1068 module('saved_filters', {
1069 setup: function () {
1070 instance = openerp.testing.instanceFor('search');
1072 openerp.testing.loadTemplate(instance);
1074 openerp.testing.mockifyRPC(instance);
1077 asyncTest('checkboxing', 6, function () {
1078 var view = makeSearchView();
1079 instance.session.responses['/web/searchview/get_filters'] = function () {
1081 name: "filter name",
1085 var $fix = $('#qunit-fixture');
1089 .fail(function (error) { ok(false, error.message); })
1091 var $row = $fix.find('.oe_searchview_custom li:first').click();
1093 ok($row.hasClass('oe_selected'), "should check/select the filter's row");
1094 ok($row.hasClass("oe_searchview_custom_private"),
1095 "should have private filter note/class");
1096 equal(view.query.length, 1, "should have only one facet");
1097 var values = view.query.at(0).values;
1098 equal(values.length, 1,
1099 "should have only one value in the facet");
1100 equal(values.at(0).get('label'), 'filter name',
1101 "displayed label should be the name of the filter");
1102 equal(values.at(0).get('value'), null,
1103 "should have no value set");
1106 asyncTest('removal', 1, function () {
1107 var view = makeSearchView();
1108 instance.session.responses['/web/searchview/get_filters'] = function () {
1110 name: "filter name",
1114 var $fix = $('#qunit-fixture');
1118 .fail(function (error) { ok(false, error.message); })
1120 var $row = $fix.find('.oe_searchview_custom li:first').click();
1122 view.query.remove(view.query.at(0));
1123 ok(!$row.hasClass('oe_selected'),
1124 "should not be checked anymore");
1128 module('advanced', {
1129 setup: function () {
1130 instance = openerp.testing.instanceFor('search');
1132 openerp.testing.loadTemplate(instance);
1134 openerp.testing.mockifyRPC(instance);
1137 asyncTest('single-advanced', 6, function () {
1138 var view = makeSearchView();
1139 var $fix = $('#qunit-fixture');
1143 .fail(function (error) { ok(false, error.message); })
1145 var $advanced = $fix.find('.oe_searchview_advanced');
1146 // open advanced search (not actually useful)
1147 $advanced.find('> h4').click();
1148 // select proposition (only one)
1149 var $prop = $advanced.find('> form li:first');
1150 // field select should have two possible values, dummy and id
1151 equal($prop.find('.searchview_extended_prop_field option').length,
1152 2, "advanced search should provide choice between two fields");
1153 // field should be dummy
1154 equal($prop.find('.searchview_extended_prop_field').val(),
1156 "only field should be dummy");
1157 // operator should be "contains"/'ilike'
1158 equal($prop.find('.searchview_extended_prop_op').val(),
1159 'ilike', "default char operator should be ilike");
1161 $prop.find('.searchview_extended_prop_value input')
1162 .val("stupid value");
1163 // validate advanced search
1164 $advanced.find('button.oe_apply').click();
1167 equal(view.query.length, 1, "search query should have a single facet");
1168 var facet = view.query.at(0);
1169 ok(!facet.get('field').get_context(facet),
1170 "advanced search facets should yield no context");
1171 deepEqual(facet.get('field').get_domain(facet),
1172 [['dummy', 'ilike', "stupid value"]],
1173 "advanced search facet should return proposed domain");
1176 asyncTest('multiple-advanced', 3, function () {
1177 var view = makeSearchView();
1178 var $fix = $('#qunit-fixture');
1182 .fail(function (error) { ok(false, error.message); })
1184 var $advanced = $fix.find('.oe_searchview_advanced');
1185 // open advanced search (not actually useful)
1186 $advanced.find('> h4').click();
1187 // open second condition
1188 $advanced.find('button.oe_add_condition').click();
1189 // select first proposition
1190 var $prop1 = $advanced.find('> form li:first');
1191 $prop1.find('.searchview_extended_prop_field').val('dummy').change();
1192 $prop1.find('.searchview_extended_prop_op').val('ilike');
1193 $prop1.find('.searchview_extended_prop_value input')
1194 .val("stupid value");
1196 // select first proposition
1197 var $prop2 = $advanced.find('> form li:last');
1198 // need to trigger event manually or op not changed
1199 $prop2.find('.searchview_extended_prop_field').val('id').change();
1200 $prop2.find('.searchview_extended_prop_op').val('=');
1201 $prop2.find('.searchview_extended_prop_value input')
1203 // validate advanced search
1204 $advanced.find('button.oe_apply').click();
1207 equal(view.query.length, 1, "search query should have a single facet");
1208 var facet = view.query.at(0);
1209 ok(!facet.get('field').get_context(facet),
1210 "advanced search facets should yield no context");
1211 deepEqual(facet.get('field').get_domain(facet),
1212 ['|', ['dummy', 'ilike', "stupid value"],
1214 "advanced search facet should return proposed domain");