1 openerp.testing.section('query', {
2 dependencies: ['web.search']
4 test('Adding a facet to the query creates a facet and a value', function (instance) {
5 var query = new instance.web.search.SearchQuery;
10 values: [{label: 'Value', value: 3}]
13 var facet = query.at(0);
14 equal(facet.get('category'), 'Foo');
15 equal(facet.get('field'), field);
16 deepEqual(facet.get('values'), [{label: 'Value', value: 3}]);
18 test('Adding two facets', function (instance) {
19 var query = new instance.web.search.SearchQuery;
21 { category: 'Foo', field: {}, values: [{label: 'Value', value: 3}] },
22 { category: 'Bar', field: {}, values: [{label: 'Value 2', value: 4}] }
25 equal(query.length, 2);
26 equal(query.at(0).values.length, 1);
27 equal(query.at(1).values.length, 1);
29 test('If a facet already exists, add values to it', function (instance) {
30 var query = new instance.web.search.SearchQuery;
32 query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
33 query.add({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
35 equal(query.length, 1, "adding an existing facet should merge new values into old facet");
36 var facet = query.at(0);
37 deepEqual(facet.get('values'), [
38 {label: 'V1', value: 0},
39 {label: 'V2', value: 1}
42 test('Facet being implicitly changed should trigger change, not add', function (instance) {
43 var query = new instance.web.search.SearchQuery;
44 var field = {}, added = false, changed = false;
45 query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
46 query.on('add', function () { added = true; })
47 .on('change', function () { changed = true });
48 query.add({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
50 ok(!added, "query.add adding values to a facet should not trigger an add");
51 ok(changed, "query.add adding values to a facet should not trigger a change");
53 test('Toggling a facet, value which does not exist should add it', function (instance) {
54 var query = new instance.web.search.SearchQuery;
56 query.toggle({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
58 equal(query.length, 1, "Should have created a single facet");
59 var facet = query.at(0);
60 equal(facet.values.length, 1, "Facet should have a single value");
61 deepEqual(facet.get('values'), [{label: 'V1', value: 0}],
62 "Facet's value should match input");
64 test('Toggling a facet which exists with a value which does not should add the value to the facet', function (instance) {
66 var query = new instance.web.search.SearchQuery;
67 query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
68 query.toggle({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
70 equal(query.length, 1, "Should have edited the existing facet");
71 var facet = query.at(0);
72 equal(facet.values.length, 2, "Should have added the value to the existing facet");
73 deepEqual(facet.get('values'), [
74 {label: 'V1', value: 0},
75 {label: 'V2', value: 1}
78 test('Toggling a facet which exists with a value which does as well should remove the value from the facet', function (instance) {
80 var query = new instance.web.search.SearchQuery;
81 query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
82 query.add({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
84 query.toggle({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
86 equal(query.length, 1, 'Should have the same single facet');
87 var facet = query.at(0);
88 equal(facet.values.length, 1, "Should only have one value left in the facet");
89 deepEqual(facet.get('values'), [
90 {label: 'V1', value: 0}
93 test('Toggling off the last value of a facet should remove the facet', function (instance) {
95 var query = new instance.web.search.SearchQuery;
96 query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
98 query.toggle({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
100 equal(query.length, 0, 'Should have removed the facet');
102 test('Intermediate emptiness should not remove the facet', function (instance) {
104 var query = new instance.web.search.SearchQuery;
105 query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
107 query.toggle({category: 'A', field: field, values: [
108 {label: 'V1', value: 0},
109 {label: 'V2', value: 1}
112 equal(query.length, 1, 'Should not have removed the facet');
113 var facet = query.at(0);
114 equal(facet.values.length, 1, "Should have one value");
115 deepEqual(facet.get('values'), [
116 {label: 'V2', value: 1}
120 test('Reseting with multiple facets should still work to load defaults', function (instance) {
121 var query = new instance.web.search.SearchQuery;
124 {category: 'A', field: field, values: [{label: 'V1', value: 0}]},
125 {category: 'A', field: field, values: [{label: 'V2', value: 1}]}]);
127 equal(query.length, 1, 'Should have created a single facet');
128 equal(query.at(0).values.length, 2, 'the facet should have merged two values');
129 deepEqual(query.at(0).get('values'), [
130 {label: 'V1', value: 0},
131 {label: 'V2', value: 1}
137 * Builds a basic search view with a single "dummy" field. The dummy
138 * extends `instance.web.search.Field`, it does not add any (class)
139 * attributes beyond what is provided through ``dummy_widget_attributes``.
141 * The view is returned un-started, it is the caller's role to start it
142 * (or use DOM-insertion methods to start it indirectly).
145 * @param [dummy_widget_attributes={}]
146 * @param [defaults={}]
147 * @return {instance.web.SearchView}
149 var makeSearchView = function (instance, dummy_widget_attributes, defaults) {
150 instance.web.search.fields.add(
151 'dummy', 'instance.dummy.DummyWidget');
153 instance.dummy.DummyWidget = instance.web.search.Field.extend(
154 dummy_widget_attributes || {});
155 if (!('dummy.model:fields_view_get' in instance.session.responses)) {
156 instance.session.responses['dummy.model:fields_view_get'] = function () {
160 dummy: {type: 'char', string: "Dummy"}
162 arch: '<search><field name="dummy" widget="dummy"/></search>'
166 instance.session.responses['ir.filters:get_filters'] = function () {
169 instance.session.responses['dummy.model:fields_get'] = function () {
171 dummy: {type: 'char', string: 'Dummy'}
175 var dataset = new instance.web.DataSet(null, 'dummy.model');
176 var view = new instance.web.SearchView(null, dataset, false, defaults);
178 view.on('invalid_search', self, function () {
179 ok(false, JSON.stringify([].slice(arguments)));
183 openerp.testing.section('defaults', {
184 dependencies: ['web.search'],
188 test('calling', {asserts: 2}, function (instance, $s) {
189 var defaults_called = false;
191 var view = makeSearchView(instance, {
192 facet_for_defaults: function (defaults) {
193 defaults_called = true;
197 values: [{label: 'dummy', value: defaults.dummy}]
201 return view.appendTo($s)
203 ok(defaults_called, "should have called defaults");
206 [{category: 'Dummy', values: [{label: 'dummy', value: 42}]}],
207 "should have generated a facet with the default value");
210 test('FilterGroup', {asserts: 3}, function (instance) {
211 var view = {inputs: [], query: {on: function () {}}};
212 var filter_a = new instance.web.search.Filter(
213 {attrs: {name: 'a'}}, view);
214 var filter_b = new instance.web.search.Filter(
215 {attrs: {name: 'b'}}, view);
216 var group = new instance.web.search.FilterGroup(
217 [filter_a, filter_b], view);
218 return group.facet_for_defaults({a: true, b: true})
219 .done(function (facet) {
221 if (!(model instanceof instance.web.search.Facet)) {
222 model = new instance.web.search.Facet(facet);
224 var values = model.values;
225 equal(values.length, 2, 'facet should have two values');
226 strictEqual(values.at(0).get('value'), filter_a);
227 strictEqual(values.at(1).get('value'), filter_b);
230 test('Field', {asserts: 4}, function (instance) {
231 var view = {inputs: []};
232 var f = new instance.web.search.Field(
233 {attrs: {string: 'Dummy', name: 'dummy'}}, {}, view);
234 return f.facet_for_defaults({dummy: 42})
235 .done(function (facet) {
237 if (!(model instanceof instance.web.search.Facet)) {
238 model = new instance.web.search.Facet(facet);
241 model.get('category'),
243 "facet category should be field label");
245 model.get('field'), f,
246 "facet field should be field which created default");
247 equal(model.values.length, 1, "facet should have a single value");
249 model.values.toJSON(),
250 [{label: '42', value: 42}],
251 "facet value should match provided default");
254 test('Selection: valid value', {asserts: 4}, function (instance) {
255 var view = {inputs: []};
256 var f = new instance.web.search.SelectionField(
257 {attrs: {name: 'dummy', string: 'Dummy'}},
258 {selection: [[1, "Foo"], [2, "Bar"], [3, "Baz"], [4, "Qux"]]},
260 return f.facet_for_defaults({dummy: 3})
261 .done(function (facet) {
263 if (!(model instanceof instance.web.search.Facet)) {
264 model = new instance.web.search.Facet(facet);
267 model.get('category'),
269 "facet category should be field label");
271 model.get('field'), f,
272 "facet field should be field which created default");
273 equal(model.values.length, 1, "facet should have a single value");
275 model.values.toJSON(),
276 [{label: 'Baz', value: 3}],
277 "facet value should match provided default's selection");
280 test('Selection: invalid value', {asserts: 1}, function (instance) {
281 var view = {inputs: []};
282 var f = new instance.web.search.SelectionField(
283 {attrs: {name: 'dummy', string: 'Dummy'}},
284 {selection: [[1, "Foo"], [2, "Bar"], [3, "Baz"], [4, "Qux"]]},
286 return f.facet_for_defaults({dummy: 42})
287 .done(function (facet) {
288 ok(!facet, "an invalid value should result in a not-facet");
291 test("M2O default: value", {asserts: 5}, function (instance, $s, mock) {
292 var view = {inputs: []}, id = 4;
293 var f = new instance.web.search.ManyToOneField(
294 {attrs: {name: 'dummy', string: 'Dummy'}},
295 {relation: 'dummy.model.name'},
297 mock('dummy.model.name:name_get', function (args) {
299 return [[id, "DumDumDum"]];
301 return f.facet_for_defaults({dummy: id})
302 .done(function (facet) {
304 if (!(model instanceof instance.web.search.Facet)) {
305 model = new instance.web.search.Facet(facet);
308 model.get('category'),
310 "facet category should be field label");
312 model.get('field'), f,
313 "facet field should be field which created default");
314 equal(model.values.length, 1, "facet should have a single value");
316 model.values.toJSON(),
317 [{label: 'DumDumDum', value: id}],
318 "facet value should match provided default's selection");
321 test("M2O default: value", {asserts: 1}, function (instance, $s, mock) {
322 var view = {inputs: []}, id = 4;
323 var f = new instance.web.search.ManyToOneField(
324 {attrs: {name: 'dummy', string: 'Dummy'}},
325 {relation: 'dummy.model.name'},
327 mock('dummy.model.name:name_get', function () { return [] });
328 return f.facet_for_defaults({dummy: id})
329 .done(function (facet) {
330 ok(!facet, "an invalid m2o default should yield a non-facet");
334 openerp.testing.section('completions', {
335 dependencies: ['web.search'],
339 test('calling', {asserts: 4}, function (instance, $s) {
340 var view = makeSearchView(instance, {
341 complete: function () {
347 values: [{label: 'dummy', value: 42}]
352 var done = $.Deferred();
355 view.complete_global_search({term: "dum"}, function (completions) {
357 equal(completions.length, 1, "should have a single completion");
358 var completion = completions[0];
359 equal(completion.label, "Dummy",
360 "should have provided label");
361 equal(completion.facet.category, "Dummy",
362 "should have provided category");
363 deepEqual(completion.facet.values,
364 [{label: 'dummy', value: 42}],
365 "should have provided values");
367 }).fail(function () { done.reject.apply(done, arguments); });
370 test('facet selection', {asserts: 2}, function (instance, $s) {
375 get_domain: openerp.testing.noop,
376 get_context: openerp.testing.noop,
377 get_groupby: openerp.testing.noop
380 values: [{label: 'dummy', value: 42}]
384 var view = makeSearchView(instance);
385 return view.appendTo($s)
387 view.select_completion(
388 {preventDefault: function () {}},
390 equal(view.query.length, 1, "should have one facet in the query");
392 view.query.at(0).toJSON(),
393 {category: 'Dummy', values: [{label: 'dummy', value: 42}]},
394 "should have the right facet in the query");
397 test('facet selection: new value existing facet', {asserts: 3}, function (instance, $s) {
399 get_domain: openerp.testing.noop,
400 get_context: openerp.testing.noop,
401 get_groupby: openerp.testing.noop
408 values: [{label: 'dummy', value: 42}]
412 var view = makeSearchView(instance);
413 return view.appendTo($s)
415 view.query.add({field: field, category: 'Dummy',
416 values: [{label: 'previous', value: 41}]});
417 equal(view.query.length, 1, 'should have newly added facet');
418 view.select_completion(
419 {preventDefault: function () {}},
421 equal(view.query.length, 1, "should still have only one facet");
422 var facet = view.query.at(0);
425 [{label: 'previous', value: 41}, {label: 'dummy', value: 42}],
426 "should have added selected value to old one");
429 test('Field', {asserts: 1}, function (instance) {
430 var view = {inputs: []};
431 var f = new instance.web.search.Field({attrs: {}}, {}, view);
432 return f.complete('foo')
433 .done(function (completions) {
434 ok(_(completions).isEmpty(), "field should not provide any completion");
437 test('CharField', {asserts: 6}, function (instance) {
438 var view = {inputs: []};
439 var f = new instance.web.search.CharField(
440 {attrs: {string: "Dummy"}}, {}, view);
441 return f.complete('foo<')
442 .done(function (completions) {
443 equal(completions.length, 1, "should provide a single completion");
444 var c = completions[0];
445 equal(c.label, "Search <em>Dummy</em> for: <strong>foo<</strong>",
446 "should propose a fuzzy matching/searching, with the" +
448 ok(c.facet, "completion should contain a facet proposition");
449 var facet = new instance.web.search.Facet(c.facet);
450 equal(facet.get('category'), f.attrs.string,
451 "completion facet should bear the field's name");
452 strictEqual(facet.get('field'), f,
453 "completion facet should yield the field");
454 deepEqual(facet.values.toJSON(), [{label: 'foo<', value: 'foo<'}],
455 "facet should have single value using completion item");
458 test('Selection: match found', {asserts: 14}, function (instance) {
459 var view = {inputs: []};
460 var f = new instance.web.search.SelectionField(
461 {attrs: {string: "Dummy"}},
462 {selection: [[1, "Foo"], [2, "Bar"], [3, "Baz"], [4, "Bazador"]]},
464 return f.complete("ba")
465 .done(function (completions) {
466 equal(completions.length, 4,
467 "should provide two completions and a section title");
468 deepEqual(completions[0], {label: "Dummy"});
470 var c1 = completions[1];
471 equal(c1.label, "Bar");
472 equal(c1.facet.category, f.attrs.string);
473 strictEqual(c1.facet.field, f);
474 deepEqual(c1.facet.values, [{label: "Bar", value: 2}]);
476 var c2 = completions[2];
477 equal(c2.label, "Baz");
478 equal(c2.facet.category, f.attrs.string);
479 strictEqual(c2.facet.field, f);
480 deepEqual(c2.facet.values, [{label: "Baz", value: 3}]);
482 var c3 = completions[3];
483 equal(c3.label, "Bazador");
484 equal(c3.facet.category, f.attrs.string);
485 strictEqual(c3.facet.field, f);
486 deepEqual(c3.facet.values, [{label: "Bazador", value: 4}]);
489 test('Selection: no match', {asserts: 1}, function (instance) {
490 var view = {inputs: []};
491 var f = new instance.web.search.SelectionField(
492 {attrs: {string: "Dummy"}},
493 {selection: [[1, "Foo"], [2, "Bar"], [3, "Baz"], [4, "Bazador"]]},
495 return f.complete("qux")
496 .done(function (completions) {
497 ok(!completions, "if no value matches the needle, no completion shall be provided");
500 test('Date', {asserts: 6}, function (instance) {
501 instance.web._t.database.parameters = {
502 date_format: '%Y-%m-%d',
503 time_format: '%H:%M:%S'
505 var view = {inputs: []};
506 var f = new instance.web.search.DateField(
507 {attrs: {string: "Dummy"}}, {type: 'datetime'}, view);
508 return f.complete('2012-05-21T21:21:21')
509 .done(function (completions) {
510 equal(completions.length, 1, "should provide a single completion");
511 var c = completions[0];
512 equal(c.label, "Search <em>Dummy</em> at: <strong>2012-05-21 21:21:21</strong>");
513 var facet = new instance.web.search.Facet(c.facet);
514 equal(facet.get('category'), f.attrs.string);
515 equal(facet.get('field'), f);
516 var value = facet.values.at(0);
517 equal(value.get('label'), "2012-05-21 21:21:21");
518 equal(value.get('value').getTime(),
519 new Date(2012, 4, 21, 21, 21, 21).getTime());
522 test("M2O", {asserts: 13}, function (instance, $s, mock) {
523 mock('dummy.model:name_search', function (args, kwargs) {
525 strictEqual(kwargs.name, 'bob');
526 return [[42, "choice 1"], [43, "choice @"]];
529 var view = {inputs: []};
530 var f = new instance.web.search.ManyToOneField(
531 {attrs: {string: 'Dummy'}}, {relation: 'dummy.model'}, view);
532 return f.complete("bob")
534 equal(c.length, 3, "should return results + title");
536 equal(title.label, f.attrs.string, "title should match field name");
537 ok(!title.facet, "title should not have a facet");
539 var f1 = new instance.web.search.Facet(c[1].facet);
540 equal(c[1].label, "choice 1");
541 equal(f1.get('category'), f.attrs.string);
542 equal(f1.get('field'), f);
543 deepEqual(f1.values.toJSON(), [{label: 'choice 1', value: 42}]);
545 var f2 = new instance.web.search.Facet(c[2].facet);
546 equal(c[2].label, "choice @");
547 equal(f2.get('category'), f.attrs.string);
548 equal(f2.get('field'), f);
549 deepEqual(f2.values.toJSON(), [{label: 'choice @', value: 43}]);
552 test("M2O no match", {asserts: 3}, function (instance, $s, mock) {
553 mock('dummy.model:name_search', function (args, kwargs) {
555 strictEqual(kwargs.name, 'bob');
558 var view = {inputs: []};
559 var f = new instance.web.search.ManyToOneField(
560 {attrs: {string: 'Dummy'}}, {relation: 'dummy.model'}, view);
561 return f.complete("bob")
563 ok(!c, "no match should yield no completion");
567 openerp.testing.section('search-serialization', {
568 dependencies: ['web.search'],
572 test('No facet, no call', {asserts: 6}, function (instance, $s) {
573 var got_domain = false, got_context = false, got_groupby = false;
574 var view = makeSearchView(instance, {
575 get_domain: function () {
579 get_context: function () {
583 get_groupby: function () {
589 view.on('search_data', this, function (d, c, g) {
590 ds = d; cs = c; gs = g;
592 return view.appendTo($s)
595 ok(!got_domain, "no facet, should not have fetched domain");
596 ok(_(ds).isEmpty(), "domains list should be empty");
598 ok(!got_context, "no facet, should not have fetched context");
599 ok(_(cs).isEmpty(), "contexts list should be empty");
601 ok(!got_groupby, "no facet, should not have fetched groupby");
602 ok(_(gs).isEmpty(), "groupby list should be empty");
605 test('London, calling', {asserts: 8}, function (instance, $fix) {
606 var got_domain = false, got_context = false, got_groupby = false;
607 var view = makeSearchView(instance, {
608 get_domain: function (facet) {
609 equal(facet.get('category'), "Dummy");
610 deepEqual(facet.values.toJSON(), [{label: "42", value: 42}]);
614 get_context: function () {
618 get_groupby: function () {
624 view.on('search_data', this, function (d, c, g) {
625 ds = d; cs = c; gs = g;
627 return view.appendTo($fix)
630 ok(got_domain, "should have fetched domain");
631 ok(_(ds).isEmpty(), "domains list should be empty");
633 ok(got_context, "should have fetched context");
634 ok(_(cs).isEmpty(), "contexts list should be empty");
636 ok(got_groupby, "should have fetched groupby");
637 ok(_(gs).isEmpty(), "groupby list should be empty");
640 test('Generate domains', {asserts: 1}, function (instance, $fix) {
641 var view = makeSearchView(instance, {
642 get_domain: function (facet) {
643 return facet.values.map(function (value) {
644 return ['win', '4', value.get('value')];
649 view.on('search_data', this, function (d) { ds = d; });
650 return view.appendTo($fix)
653 deepEqual(ds, [[['win', '4', 42]]],
654 "search should yield an array of contexts");
658 test('Field single value, default domain & context', {
660 }, function (instance) {
661 var f = new instance.web.search.Field({}, {name: 'foo'}, {inputs: []});
662 var facet = new instance.web.search.Facet({
664 values: [{value: 42}]
667 deepEqual(f.get_domain(facet), [['foo', '=', 42]],
668 "default field domain is a strict equality of name to facet's value");
669 equal(f.get_context(facet), null,
670 "default field context is null");
672 test('Field multiple values, default domain & context', {
674 }, function (instance) {
675 var f = new instance.web.search.Field({}, {name: 'foo'}, {inputs: []});
676 var facet = new instance.web.search.Facet({
678 values: [{value: 42}, {value: 68}, {value: 999}]
681 var actual_domain = f.get_domain(facet);
682 equal(actual_domain.__ref, "compound_domain",
683 "multiple value should yield compound domain");
684 deepEqual(actual_domain.__domains, [
691 "domain should OR a default domain for each value");
692 equal(f.get_context(facet), null,
693 "default field context is null");
695 test('Field single value, custom domain & context', {
697 }, function (instance) {
698 var f = new instance.web.search.Field({attrs:{
699 context: "{'bob': self}",
700 filter_domain: "[['edmund', 'is', self]]"
701 }}, {name: 'foo'}, {inputs: []});
702 var facet = new instance.web.search.Facet({
704 values: [{value: "great"}]
707 var actual_domain = f.get_domain(facet);
708 equal(actual_domain.__ref, "compound_domain",
709 "@filter_domain should yield compound domain");
710 deepEqual(actual_domain.__domains, [
711 "[['edmund', 'is', self]]"
712 ], 'should hold unevaluated custom domain');
713 deepEqual(actual_domain.get_eval_context(), {
715 }, "evaluation context should hold facet value as self");
717 var actual_context = f.get_context(facet);
718 equal(actual_context.__ref, "compound_context",
719 "@context should yield compound context");
720 deepEqual(actual_context.__contexts, [
722 ], 'should hold unevaluated custom context');
723 deepEqual(actual_context.get_eval_context(), {
725 }, "evaluation context should hold facet value as self");
727 test("M2O default", {
729 }, function (instance) {
730 var f = new instance.web.search.ManyToOneField(
731 {}, {name: 'foo'}, {inputs: []});
732 var facet = new instance.web.search.Facet({
734 values: [{label: "Foo", value: 42}]
737 deepEqual(f.get_domain(facet), [['foo', '=', 42]],
738 "m2o should use identity if default domain");
739 deepEqual(f.get_context(facet), {default_foo: 42},
740 "m2o should use value as context default");
742 test("M2O default multiple values", {
744 }, function (instance) {
745 var f = new instance.web.search.ManyToOneField(
746 {}, {name: 'foo'}, {inputs: []});
747 var facet = new instance.web.search.Facet({
750 {label: "Foo", value: 42},
751 {label: "Bar", value: 36}
755 deepEqual(f.get_domain(facet).__domains,
756 [['|'], [['foo', '=', 42]], [['foo', '=', 36]]],
757 "m2o should or multiple values");
758 equal(f.get_context(facet), null,
759 "m2o should not have default context in case of multiple values");
761 test("M2O custom operator", {
763 }, function (instance) {
764 var f = new instance.web.search.ManyToOneField(
765 {attrs: {operator: 'boos'}}, {name: 'foo'}, {inputs: []});
766 var facet = new instance.web.search.Facet({
768 values: [{label: "Foo", value: 42}]
771 deepEqual(f.get_domain(facet), [['foo', 'boos', 'Foo']],
772 "m2o should use label with custom operators");
773 deepEqual(f.get_context(facet), {default_foo: 42},
774 "m2o should use value as context default");
776 test("M2O custom domain & context", {
778 }, function (instance) {
779 var f = new instance.web.search.ManyToOneField({attrs: {
780 context: "{'whee': self}",
781 filter_domain: "[['filter', 'is', self]]"
782 }}, {name: 'foo'}, {inputs: []});
783 var facet = new instance.web.search.Facet({
785 values: [{label: "Foo", value: 42}]
788 var domain = f.get_domain(facet);
789 deepEqual(domain.__domains, [
790 "[['filter', 'is', self]]"
792 deepEqual(domain.get_eval_context(), {
794 }, "custom domain's self should be label");
795 var context = f.get_context(facet);
796 deepEqual(context.__contexts, [
799 deepEqual(context.get_eval_context(), {
801 }, "custom context's self should be label");
804 test('FilterGroup', {asserts: 6}, function (instance) {
805 var view = {inputs: [], query: {on: function () {}}};
806 var filter_a = new instance.web.search.Filter(
807 {attrs: {name: 'a', context: 'c1', domain: 'd1'}}, view);
808 var filter_b = new instance.web.search.Filter(
809 {attrs: {name: 'b', context: 'c2', domain: 'd2'}}, view);
810 var filter_c = new instance.web.search.Filter(
811 {attrs: {name: 'c', context: 'c3', domain: 'd3'}}, view);
812 var group = new instance.web.search.FilterGroup(
813 [filter_a, filter_b, filter_c], view);
814 return group.facet_for_defaults({a: true, c: true})
815 .done(function (facet) {
817 if (!(model instanceof instance.web.search.Facet)) {
818 model = new instance.web.search.Facet(facet);
821 var domain = group.get_domain(model);
822 equal(domain.__ref, 'compound_domain',
823 "domain should be compound");
824 deepEqual(domain.__domains, [
826 ], "domain should OR filter domains");
827 ok(!domain.get_eval_context(), "domain should have no evaluation context");
828 var context = group.get_context(model);
829 equal(context.__ref, 'compound_context',
830 "context should be compound");
831 deepEqual(context.__contexts, [
833 ], "context should merge all filter contexts");
834 ok(!context.get_eval_context(), "context should have no evaluation context");
837 test('Empty filter domains', {asserts: 4}, function (instance) {
838 var view = {inputs: [], query: {on: function () {}}};
839 var filter_a = new instance.web.search.Filter(
840 {attrs: {name: 'a', context: '{}', domain: '[]'}}, view);
841 var filter_b = new instance.web.search.Filter(
842 {attrs: {name: 'b', context: '{}', domain: '[]'}}, view);
843 var filter_c = new instance.web.search.Filter(
844 {attrs: {name: 'c', context: '{b: 42}', domain: '[["a", "=", 3]]'}}, view);
845 var group = new instance.web.search.FilterGroup(
846 [filter_a, filter_b, filter_c], view);
847 var t1 = group.facet_for_defaults({a: true, c: true})
848 .done(function (facet) {
850 if (!(model instanceof instance.web.search.Facet)) {
851 model = new instance.web.search.Facet(facet);
854 var domain = group.get_domain(model);
855 deepEqual(domain, '[["a", "=", 3]]', "domain should ignore empties");
856 var context = group.get_context(model);
857 deepEqual(context, '{b: 42}', "context should ignore empties");
859 var t2 = group.facet_for_defaults({a: true, b: true})
860 .done(function (facet) {
862 if (!(model instanceof instance.web.search.Facet)) {
863 model = new instance.web.search.Facet(facet);
866 var domain = group.get_domain(model);
867 equal(domain, null, "domain should ignore empties");
868 var context = group.get_context(model);
869 equal(context, null, "context should ignore empties");
871 return $.when(t1, t2);
874 openerp.testing.section('removal', {
875 dependencies: ['web.search'],
879 test('clear button', {asserts: 2}, function (instance, $fix) {
880 var view = makeSearchView(instance, {
881 facet_for_defaults: function (defaults) {
885 values: [{label: 'dummy', value: defaults.dummy}]
889 return view.appendTo($fix)
891 equal(view.query.length, 1, "view should have default facet");
892 $fix.find('.oe_searchview_clear').click();
893 equal(view.query.length, 0, "cleared view should not have any facet");
897 openerp.testing.section('drawer', {
898 dependencies: ['web.search'],
902 test('is-drawn', {asserts: 2}, function (instance, $fix) {
903 var view = makeSearchView(instance);
904 return view.appendTo($fix)
906 ok($fix.find('.oe_searchview_filters').length,
907 "filters drawer control has been drawn");
908 ok($fix.find('.oe_searchview_advanced').length,
909 "filters advanced search has been drawn");
913 openerp.testing.section('filters', {
914 dependencies: ['web.search'],
917 setup: function (instance, $s, mock) {
918 mock('dummy.model:fields_view_get', function () {
919 // view with a single group of filters
924 '<filter string="Foo1" domain="[ [\'foo\', \'=\', \'1\'] ]"/>' +
925 '<filter name="foo2" string="Foo2" domain="[ [\'foo\', \'=\', \'2\'] ]"/>' +
926 '<filter string="Foo3" domain="[ [\'foo\', \'=\', \'3\'] ]"/>' +
932 test('drawn', {asserts: 3}, function (instance, $fix) {
933 var view = makeSearchView(instance);
934 return view.appendTo($fix)
936 var $fs = $fix.find('.oe_searchview_filters ul');
937 // 3 filters, 1 filtergroup, 1 custom filters widget,
938 // 1 advanced and 1 Filters widget
939 equal(view.inputs.length, 7,
940 'view should have 7 inputs total');
941 equal($fs.children().length, 3,
942 "drawer should have a filter group with 3 filters");
943 equal(_.str.strip($fs.children().eq(0).text()), "Foo1",
944 "Text content of first filter option should match filter string");
947 test('click adding from empty query', {asserts: 4}, function (instance, $fix) {
948 var view = makeSearchView(instance);
949 return view.appendTo($fix)
951 var $fs = $fix.find('.oe_searchview_filters ul');
952 $fs.children(':eq(2)').trigger('click');
953 equal(view.query.length, 1, "click should have added a facet");
954 var facet = view.query.at(0);
955 equal(facet.values.length, 1, "facet should have a single value");
956 var value = facet.values.at(0);
957 ok(value.get('value') instanceof instance.web.search.Filter,
958 "value should be a filter");
959 equal(value.get('label'), "Foo3",
960 "value should be third filter");
963 test('click adding from existing query', {asserts: 4}, function (instance, $fix) {
964 var view = makeSearchView(instance, {}, {foo2: true});
965 return view.appendTo($fix)
967 var $fs = $fix.find('.oe_searchview_filters ul');
968 $fs.children(':eq(2)').trigger('click');
969 equal(view.query.length, 1, "click should not have changed facet count");
970 var facet = view.query.at(0);
971 equal(facet.values.length, 2, "facet should have a second value");
972 var v1 = facet.values.at(0);
973 equal(v1.get('label'), "Foo2",
974 "first value should be default");
975 var v2 = facet.values.at(1);
976 equal(v2.get('label'), "Foo3",
977 "second value should be clicked filter");
980 test('click removing from query', {asserts: 4}, function (instance, $fix) {
982 var view = makeSearchView(instance, {}, {foo2: true});
983 view.on('search_data', null, function () {
986 return view.appendTo($fix)
988 var $fs = $fix.find('.oe_searchview_filters ul');
990 equal(view.query.length, 1, "query should have default facet");
991 strictEqual(calls, 0);
992 $fs.children(':eq(1)').trigger('click');
993 equal(view.query.length, 0, "click should have removed facet");
994 strictEqual(calls, 1, "one search should have been triggered");
998 openerp.testing.section('saved_filters', {
999 dependencies: ['web.search'],
1002 }, function (test) {
1003 test('checkboxing', {asserts: 6}, function (instance, $fix, mock) {
1004 var view = makeSearchView(instance);
1005 mock('ir.filters:get_filters', function () {
1006 return [{ name: "filter name", user_id: 42 }];
1009 return view.appendTo($fix)
1011 var $row = $fix.find('.oe_searchview_custom li:first').click();
1013 ok($row.hasClass('oe_selected'), "should check/select the filter's row");
1014 ok($row.hasClass("oe_searchview_custom_private"),
1015 "should have private filter note/class");
1016 equal(view.query.length, 1, "should have only one facet");
1017 var values = view.query.at(0).values;
1018 equal(values.length, 1,
1019 "should have only one value in the facet");
1020 equal(values.at(0).get('label'), 'filter name',
1021 "displayed label should be the name of the filter");
1022 equal(values.at(0).get('value'), null,
1023 "should have no value set");
1026 test('removal', {asserts: 1}, function (instance, $fix, mock) {
1027 var view = makeSearchView(instance);
1028 mock('ir.filters:get_filters', function () {
1029 return [{ name: "filter name", user_id: 42 }];
1032 return view.appendTo($fix)
1034 var $row = $fix.find('.oe_searchview_custom li:first').click();
1036 view.query.remove(view.query.at(0));
1037 ok(!$row.hasClass('oe_selected'),
1038 "should not be checked anymore");
1041 test('toggling', {asserts: 2}, function (instance, $fix, mock) {
1042 var view = makeSearchView(instance);
1043 mock('ir.filters:get_filters', function () {
1044 return [{name: 'filter name', user_id: 42, id: 1}];
1047 return view.appendTo($fix)
1049 var $row = $fix.find('.oe_searchview_custom li:first').click();
1050 equal(view.query.length, 1, "should have one facet");
1052 equal(view.query.length, 0, "should have removed facet");
1055 test('replacement', {asserts: 4}, function (instance, $fix, mock) {
1056 var view = makeSearchView(instance);
1057 mock('ir.filters:get_filters', function () {
1059 {name: 'f', user_id: 42, id: 1, context: {'private': 1}},
1060 {name: 'f', user_id: false, id: 2, context: {'private': 0}}
1063 return view.appendTo($fix)
1065 $fix.find('.oe_searchview_custom li:eq(0)').click();
1066 equal(view.query.length, 1, "should have one facet");
1068 view.query.at(0).get('field').get_context(),
1070 "should have selected first filter");
1071 $fix.find('.oe_searchview_custom li:eq(1)').click();
1072 equal(view.query.length, 1, "should have one facet");
1074 view.query.at(0).get('field').get_context(),
1076 "should have selected second filter");
1080 openerp.testing.section('advanced', {
1081 dependencies: ['web.search'],
1084 }, function (test) {
1085 test('single-advanced', {asserts: 6}, function (instance, $fix) {
1086 var view = makeSearchView(instance);
1088 return view.appendTo($fix)
1090 var $advanced = $fix.find('.oe_searchview_advanced');
1091 // open advanced search (not actually useful)
1092 $advanced.find('> h4').click();
1093 // select proposition (only one)
1094 var $prop = $advanced.find('> form li:first');
1095 // field select should have two possible values, dummy and id
1096 equal($prop.find('.searchview_extended_prop_field option').length,
1097 2, "advanced search should provide choice between two fields");
1098 // field should be dummy
1099 equal($prop.find('.searchview_extended_prop_field').val(),
1101 "only field should be dummy");
1102 // operator should be "contains"/'ilike'
1103 equal($prop.find('.searchview_extended_prop_op').val(),
1104 'ilike', "default char operator should be ilike");
1106 $prop.find('.searchview_extended_prop_value input')
1107 .val("stupid value");
1108 // validate advanced search
1109 $advanced.find('button.oe_apply').click();
1112 equal(view.query.length, 1, "search query should have a single facet");
1113 var facet = view.query.at(0);
1114 ok(!facet.get('field').get_context(facet),
1115 "advanced search facets should yield no context");
1116 deepEqual(facet.get('field').get_domain(facet),
1117 [['dummy', 'ilike', "stupid value"]],
1118 "advanced search facet should return proposed domain");
1121 test('multiple-advanced', {asserts: 3}, function (instance, $fix) {
1122 var view = makeSearchView(instance);
1124 return view.appendTo($fix)
1126 var $advanced = $fix.find('.oe_searchview_advanced');
1127 // open advanced search (not actually useful)
1128 $advanced.find('> h4').click();
1129 // open second condition
1130 $advanced.find('button.oe_add_condition').click();
1131 // select first proposition
1132 var $prop1 = $advanced.find('> form li:first');
1133 $prop1.find('.searchview_extended_prop_field').val('dummy').change();
1134 $prop1.find('.searchview_extended_prop_op').val('ilike');
1135 $prop1.find('.searchview_extended_prop_value input')
1136 .val("stupid value");
1138 // select first proposition
1139 var $prop2 = $advanced.find('> form li:last');
1140 // need to trigger event manually or op not changed
1141 $prop2.find('.searchview_extended_prop_field').val('id').change();
1142 $prop2.find('.searchview_extended_prop_op').val('=');
1143 $prop2.find('.searchview_extended_prop_value input')
1145 // validate advanced search
1146 $advanced.find('button.oe_apply').click();
1149 equal(view.query.length, 1, "search query should have a single facet");
1150 var facet = view.query.at(0);
1151 ok(!facet.get('field').get_context(facet),
1152 "advanced search facets should yield no context");
1153 deepEqual(facet.get('field').get_domain(facet),
1154 ['|', ['dummy', 'ilike', "stupid value"],
1156 "advanced search facet should return proposed domain");