Merge "Expand RC filters search to include desc and group title"
[lhc/web/wiklou.git] / tests / qunit / suites / resources / mediawiki.rcfilters / dm.FiltersViewModel.test.js
1 ( function ( mw, $ ) {
2 QUnit.module( 'mediawiki.rcfilters - FiltersViewModel' );
3
4 QUnit.test( 'Setting up filters', function ( assert ) {
5 var definition = {
6 group1: {
7 title: 'Group 1',
8 type: 'send_unselected_if_any',
9 filters: [
10 {
11 name: 'group1filter1',
12 label: 'Group 1: Filter 1',
13 description: 'Description of Filter 1 in Group 1'
14 },
15 {
16 name: 'group1filter2',
17 label: 'Group 1: Filter 2',
18 description: 'Description of Filter 2 in Group 1'
19 }
20 ]
21 },
22 group2: {
23 title: 'Group 2',
24 type: 'send_unselected_if_any',
25 filters: [
26 {
27 name: 'group2filter1',
28 label: 'Group 2: Filter 1',
29 description: 'Description of Filter 1 in Group 2'
30 },
31 {
32 name: 'group2filter2',
33 label: 'Group 2: Filter 2',
34 description: 'Description of Filter 2 in Group 2'
35 }
36 ]
37 },
38 group3: {
39 title: 'Group 3',
40 type: 'string_options',
41 filters: [
42 {
43 name: 'group3filter1',
44 label: 'Group 3: Filter 1',
45 description: 'Description of Filter 1 in Group 3'
46 },
47 {
48 name: 'group3filter2',
49 label: 'Group 3: Filter 2',
50 description: 'Description of Filter 2 in Group 3'
51 }
52 ]
53 }
54 },
55 model = new mw.rcfilters.dm.FiltersViewModel();
56
57 model.initializeFilters( definition );
58
59 assert.ok(
60 model.getItemByName( 'group1filter1' ) instanceof mw.rcfilters.dm.FilterItem &&
61 model.getItemByName( 'group1filter2' ) instanceof mw.rcfilters.dm.FilterItem &&
62 model.getItemByName( 'group2filter1' ) instanceof mw.rcfilters.dm.FilterItem &&
63 model.getItemByName( 'group2filter2' ) instanceof mw.rcfilters.dm.FilterItem &&
64 model.getItemByName( 'group3filter1' ) instanceof mw.rcfilters.dm.FilterItem &&
65 model.getItemByName( 'group3filter2' ) instanceof mw.rcfilters.dm.FilterItem,
66 'Filters instantiated and stored correctly'
67 );
68
69 assert.deepEqual(
70 model.getSelectedState(),
71 {
72 group1filter1: false,
73 group1filter2: false,
74 group2filter1: false,
75 group2filter2: false,
76 group3filter1: false,
77 group3filter2: false
78 },
79 'Initial state of filters'
80 );
81
82 model.updateFilters( {
83 group1filter1: true,
84 group2filter2: true,
85 group3filter1: true
86 } );
87 assert.deepEqual(
88 model.getSelectedState(),
89 {
90 group1filter1: true,
91 group1filter2: false,
92 group2filter1: false,
93 group2filter2: true,
94 group3filter1: true,
95 group3filter2: false
96 },
97 'Updating filter states correctly'
98 );
99 } );
100
101 QUnit.test( 'Finding matching filters', function ( assert ) {
102 var matches,
103 definition = {
104 group1: {
105 title: 'Group 1 title',
106 type: 'send_unselected_if_any',
107 filters: [
108 {
109 name: 'group1filter1',
110 label: 'Group 1: Filter 1',
111 description: 'Description of Filter 1 in Group 1'
112 },
113 {
114 name: 'group1filter2',
115 label: 'Group 1: Filter 2',
116 description: 'Description of Filter 2 in Group 1'
117 }
118 ]
119 },
120 group2: {
121 title: 'Group 2 title',
122 type: 'send_unselected_if_any',
123 filters: [
124 {
125 name: 'group2filter1',
126 label: 'Group 2: Filter 1',
127 description: 'Description of Filter 1 in Group 2'
128 },
129 {
130 name: 'group2filter2',
131 label: 'xGroup 2: Filter 2',
132 description: 'Description of Filter 2 in Group 2'
133 }
134 ]
135 }
136 },
137 testCases = [
138 {
139 query: 'group',
140 expectedMatches: {
141 group1: [ 'group1filter1', 'group1filter2' ],
142 group2: [ 'group2filter1' ]
143 },
144 reason: 'Finds filters starting with the query string'
145 },
146 {
147 query: 'filter 2 in group',
148 expectedMatches: {
149 group1: [ 'group1filter2' ],
150 group2: [ 'group2filter2' ]
151 },
152 reason: 'Finds filters containing the query string in their description'
153 },
154 {
155 query: 'title',
156 expectedMatches: {
157 group1: [ 'group1filter1', 'group1filter2' ],
158 group2: [ 'group2filter1', 'group2filter2' ]
159 },
160 reason: 'Finds filters containing the query string in their group title'
161 }
162 ],
163 model = new mw.rcfilters.dm.FiltersViewModel(),
164 extractNames = function ( matches ) {
165 var result = {};
166 Object.keys( matches ).forEach( function ( groupName ) {
167 result[ groupName ] = matches[ groupName ].map( function ( item ) {
168 return item.getName();
169 } );
170 } );
171 return result;
172 };
173
174 model.initializeFilters( definition );
175
176 testCases.forEach( function ( testCase ) {
177 matches = model.findMatches( testCase.query );
178 assert.deepEqual(
179 extractNames( matches ),
180 testCase.expectedMatches,
181 testCase.reason
182 );
183 } );
184
185 matches = model.findMatches( 'foo' );
186 assert.ok(
187 $.isEmptyObject( matches ),
188 'findMatches returns an empty object when no results found'
189 );
190 } );
191
192 QUnit.test( 'getParametersFromFilters', function ( assert ) {
193 var definition = {
194 group1: {
195 title: 'Group 1',
196 type: 'send_unselected_if_any',
197 filters: [
198 {
199 name: 'hidefilter1',
200 label: 'Group 1: Filter 1',
201 description: 'Description of Filter 1 in Group 1'
202 },
203 {
204 name: 'hidefilter2',
205 label: 'Group 1: Filter 2',
206 description: 'Description of Filter 2 in Group 1'
207 },
208 {
209 name: 'hidefilter3',
210 label: 'Group 1: Filter 3',
211 description: 'Description of Filter 3 in Group 1'
212 }
213 ]
214 },
215 group2: {
216 title: 'Group 2',
217 type: 'send_unselected_if_any',
218 filters: [
219 {
220 name: 'hidefilter4',
221 label: 'Group 2: Filter 1',
222 description: 'Description of Filter 1 in Group 2'
223 },
224 {
225 name: 'hidefilter5',
226 label: 'Group 2: Filter 2',
227 description: 'Description of Filter 2 in Group 2'
228 },
229 {
230 name: 'hidefilter6',
231 label: 'Group 2: Filter 3',
232 description: 'Description of Filter 3 in Group 2'
233 }
234 ]
235 },
236 group3: {
237 title: 'Group 3',
238 type: 'string_options',
239 separator: ',',
240 filters: [
241 {
242 name: 'filter7',
243 label: 'Group 3: Filter 1',
244 description: 'Description of Filter 1 in Group 3'
245 },
246 {
247 name: 'filter8',
248 label: 'Group 3: Filter 2',
249 description: 'Description of Filter 2 in Group 3'
250 },
251 {
252 name: 'filter9',
253 label: 'Group 3: Filter 3',
254 description: 'Description of Filter 3 in Group 3'
255 }
256 ]
257 }
258 },
259 model = new mw.rcfilters.dm.FiltersViewModel();
260
261 model.initializeFilters( definition );
262
263 // Starting with all filters unselected
264 assert.deepEqual(
265 model.getParametersFromFilters(),
266 {
267 hidefilter1: 0,
268 hidefilter2: 0,
269 hidefilter3: 0,
270 hidefilter4: 0,
271 hidefilter5: 0,
272 hidefilter6: 0,
273 group3: 'all',
274 },
275 'Unselected filters return all parameters falsey or \'all\'.'
276 );
277
278 // Select 1 filter
279 model.updateFilters( {
280 hidefilter1: true,
281 hidefilter2: false,
282 hidefilter3: false,
283 hidefilter4: false,
284 hidefilter5: false,
285 hidefilter6: false
286 } );
287 // Only one filter in one group
288 assert.deepEqual(
289 model.getParametersFromFilters(),
290 {
291 // Group 1 (one selected, the others are true)
292 hidefilter1: 0,
293 hidefilter2: 1,
294 hidefilter3: 1,
295 // Group 2 (nothing is selected, all false)
296 hidefilter4: 0,
297 hidefilter5: 0,
298 hidefilter6: 0,
299 group3: 'all'
300 },
301 'One filters in one "send_unselected_if_any" group returns the other parameters truthy.'
302 );
303
304 // Select 2 filters
305 model.updateFilters( {
306 hidefilter1: true,
307 hidefilter2: true,
308 hidefilter3: false,
309 hidefilter4: false,
310 hidefilter5: false,
311 hidefilter6: false
312 } );
313 // Two selected filters in one group
314 assert.deepEqual(
315 model.getParametersFromFilters(),
316 {
317 // Group 1 (two selected, the others are true)
318 hidefilter1: 0,
319 hidefilter2: 0,
320 hidefilter3: 1,
321 // Group 2 (nothing is selected, all false)
322 hidefilter4: 0,
323 hidefilter5: 0,
324 hidefilter6: 0,
325 group3: 'all'
326 },
327 'One filters in one "send_unselected_if_any" group returns the other parameters truthy.'
328 );
329
330 // Select 3 filters
331 model.updateFilters( {
332 hidefilter1: true,
333 hidefilter2: true,
334 hidefilter3: true,
335 hidefilter4: false,
336 hidefilter5: false,
337 hidefilter6: false
338 } );
339 // All filters of the group are selected == this is the same as not selecting any
340 assert.deepEqual(
341 model.getParametersFromFilters(),
342 {
343 // Group 1 (all selected, all false)
344 hidefilter1: 0,
345 hidefilter2: 0,
346 hidefilter3: 0,
347 // Group 2 (nothing is selected, all false)
348 hidefilter4: 0,
349 hidefilter5: 0,
350 hidefilter6: 0,
351 group3: 'all'
352 },
353 'All filters selected in one "send_unselected_if_any" group returns all parameters falsy.'
354 );
355
356 // Select 1 filter from string_options
357 model.updateFilters( {
358 filter7: true,
359 filter8: false,
360 filter9: false
361 } );
362 // All filters of the group are selected == this is the same as not selecting any
363 assert.deepEqual(
364 model.getParametersFromFilters(),
365 {
366 // Group 1 (all selected, all)
367 hidefilter1: 0,
368 hidefilter2: 0,
369 hidefilter3: 0,
370 // Group 2 (nothing is selected, all false)
371 hidefilter4: 0,
372 hidefilter5: 0,
373 hidefilter6: 0,
374 group3: 'filter7'
375 },
376 'One filter selected in "string_option" group returns that filter in the value.'
377 );
378
379 // Select 2 filters from string_options
380 model.updateFilters( {
381 filter7: true,
382 filter8: true,
383 filter9: false
384 } );
385 // All filters of the group are selected == this is the same as not selecting any
386 assert.deepEqual(
387 model.getParametersFromFilters(),
388 {
389 // Group 1 (all selected, all)
390 hidefilter1: 0,
391 hidefilter2: 0,
392 hidefilter3: 0,
393 // Group 2 (nothing is selected, all false)
394 hidefilter4: 0,
395 hidefilter5: 0,
396 hidefilter6: 0,
397 group3: 'filter7,filter8'
398 },
399 'Two filters selected in "string_option" group returns those filters in the value.'
400 );
401
402 // Select 3 filters from string_options
403 model.updateFilters( {
404 filter7: true,
405 filter8: true,
406 filter9: true
407 } );
408 // All filters of the group are selected == this is the same as not selecting any
409 assert.deepEqual(
410 model.getParametersFromFilters(),
411 {
412 // Group 1 (all selected, all)
413 hidefilter1: 0,
414 hidefilter2: 0,
415 hidefilter3: 0,
416 // Group 2 (nothing is selected, all false)
417 hidefilter4: 0,
418 hidefilter5: 0,
419 hidefilter6: 0,
420 group3: 'all'
421 },
422 'All filters selected in "string_option" group returns \'all\'.'
423 );
424
425 } );
426
427 QUnit.test( 'getFiltersFromParameters', function ( assert ) {
428 var definition = {
429 group1: {
430 title: 'Group 1',
431 type: 'send_unselected_if_any',
432 filters: [
433 {
434 name: 'hidefilter1',
435 label: 'Show filter 1',
436 description: 'Description of Filter 1 in Group 1',
437 default: true
438 },
439 {
440 name: 'hidefilter2',
441 label: 'Show filter 2',
442 description: 'Description of Filter 2 in Group 1'
443 },
444 {
445 name: 'hidefilter3',
446 label: 'Show filter 3',
447 description: 'Description of Filter 3 in Group 1',
448 default: true
449 }
450 ]
451 },
452 group2: {
453 title: 'Group 2',
454 type: 'send_unselected_if_any',
455 filters: [
456 {
457 name: 'hidefilter4',
458 label: 'Show filter 4',
459 description: 'Description of Filter 1 in Group 2'
460 },
461 {
462 name: 'hidefilter5',
463 label: 'Show filter 5',
464 description: 'Description of Filter 2 in Group 2',
465 default: true
466 },
467 {
468 name: 'hidefilter6',
469 label: 'Show filter 6',
470 description: 'Description of Filter 3 in Group 2'
471 }
472 ]
473 },
474 group3: {
475 title: 'Group 3',
476 type: 'string_options',
477 separator: ',',
478 filters: [
479 {
480 name: 'filter7',
481 label: 'Group 3: Filter 1',
482 description: 'Description of Filter 1 in Group 3'
483 },
484 {
485 name: 'filter8',
486 label: 'Group 3: Filter 2',
487 description: 'Description of Filter 2 in Group 3',
488 default: true
489 },
490 {
491 name: 'filter9',
492 label: 'Group 3: Filter 3',
493 description: 'Description of Filter 3 in Group 3'
494 }
495 ]
496 }
497 },
498 defaultFilterRepresentation = {
499 // Group 1 and 2, "send_unselected_if_any", the values of the filters are "flipped" from the values of the parameters
500 hidefilter1: false,
501 hidefilter2: true,
502 hidefilter3: false,
503 hidefilter4: true,
504 hidefilter5: false,
505 hidefilter6: true,
506 // Group 3, "string_options", default values correspond to parameters and filters
507 filter7: false,
508 filter8: true,
509 filter9: false
510 },
511 model = new mw.rcfilters.dm.FiltersViewModel();
512
513 model.initializeFilters( definition );
514
515 // Empty query = only default values
516 assert.deepEqual(
517 model.getFiltersFromParameters( {} ),
518 defaultFilterRepresentation,
519 'Empty parameter query results in filters in initial default state'
520 );
521
522 assert.deepEqual(
523 model.getFiltersFromParameters( {
524 hidefilter2: '1'
525 } ),
526 $.extend( {}, defaultFilterRepresentation, {
527 hidefilter1: false, // The text is "show filter 1"
528 hidefilter2: false, // The text is "show filter 2"
529 hidefilter3: false // The text is "show filter 3"
530 } ),
531 'One truthy parameter in a group whose other parameters are true by default makes the rest of the filters in the group false (unchecked)'
532 );
533
534 assert.deepEqual(
535 model.getFiltersFromParameters( {
536 hidefilter1: '1',
537 hidefilter2: '1',
538 hidefilter3: '1'
539 } ),
540 $.extend( {}, defaultFilterRepresentation, {
541 hidefilter1: false, // The text is "show filter 1"
542 hidefilter2: false, // The text is "show filter 2"
543 hidefilter3: false // The text is "show filter 3"
544 } ),
545 'All paremeters in the same \'send_unselected_if_any\' group false is equivalent to none are truthy (checked) in the interface'
546 );
547
548 // The ones above don't update the model, so we have a clean state.
549 // getFiltersFromParameters is stateless; any change is unaffected by the current state
550 // This test is demonstrating wrong usage of the method;
551 // We should be aware that getFiltersFromParameters is stateless,
552 // so each call gives us a filter state that only reflects the query given.
553 // This means that the two calls to updateFilters() below collide.
554 // The result of the first is overridden by the result of the second,
555 // since both get a full state object from getFiltersFromParameters that **only** relates
556 // to the input it receives.
557 model.updateFilters(
558 model.getFiltersFromParameters( {
559 hidefilter1: '1'
560 } )
561 );
562
563 model.updateFilters(
564 model.getFiltersFromParameters( {
565 hidefilter6: '1'
566 } )
567 );
568
569 // The result here is ignoring the first updateFilters call
570 // We should receive default values + hidefilter6 as false
571 assert.deepEqual(
572 model.getSelectedState(),
573 $.extend( {}, defaultFilterRepresentation, {
574 hidefilter5: false,
575 hidefilter6: false
576 } ),
577 'getFiltersFromParameters does not care about previous or existing state.'
578 );
579
580 // Reset
581 model = new mw.rcfilters.dm.FiltersViewModel();
582 model.initializeFilters( definition );
583
584 model.updateFilters(
585 model.getFiltersFromParameters( {
586 hidefilter1: '0'
587 } )
588 );
589 model.updateFilters(
590 model.getFiltersFromParameters( {
591 hidefilter1: '1'
592 } )
593 );
594
595 // Simulates minor edits being hidden in preferences, then unhidden via URL
596 // override.
597 assert.deepEqual(
598 model.getSelectedState(),
599 defaultFilterRepresentation,
600 'After checking and then unchecking a \'send_unselected_if_any\' filter (without touching other filters in that group), results are default'
601 );
602
603 model.updateFilters(
604 model.getFiltersFromParameters( {
605 group3: 'filter7'
606 } )
607 );
608 assert.deepEqual(
609 model.getSelectedState(),
610 $.extend( {}, defaultFilterRepresentation, {
611 filter7: true,
612 filter8: false,
613 filter9: false
614 } ),
615 'A \'string_options\' parameter containing 1 value, results in the corresponding filter as checked'
616 );
617
618 model.updateFilters(
619 model.getFiltersFromParameters( {
620 group3: 'filter7,filter8'
621 } )
622 );
623 assert.deepEqual(
624 model.getSelectedState(),
625 $.extend( {}, defaultFilterRepresentation, {
626 filter7: true,
627 filter8: true,
628 filter9: false
629 } ),
630 'A \'string_options\' parameter containing 2 values, results in both corresponding filters as checked'
631 );
632
633 model.updateFilters(
634 model.getFiltersFromParameters( {
635 group3: 'filter7,filter8,filter9'
636 } )
637 );
638 assert.deepEqual(
639 model.getSelectedState(),
640 $.extend( {}, defaultFilterRepresentation, {
641 filter7: false,
642 filter8: false,
643 filter9: false
644 } ),
645 'A \'string_options\' parameter containing all values, results in all filters of the group as unchecked.'
646 );
647
648 model.updateFilters(
649 model.getFiltersFromParameters( {
650 group3: 'filter7,all,filter9'
651 } )
652 );
653 assert.deepEqual(
654 model.getSelectedState(),
655 $.extend( {}, defaultFilterRepresentation, {
656 filter7: false,
657 filter8: false,
658 filter9: false
659 } ),
660 'A \'string_options\' parameter containing the value \'all\', results in all filters of the group as unchecked.'
661 );
662
663 model.updateFilters(
664 model.getFiltersFromParameters( {
665 group3: 'filter7,foo,filter9'
666 } )
667 );
668 assert.deepEqual(
669 model.getSelectedState(),
670 $.extend( {}, defaultFilterRepresentation, {
671 filter7: true,
672 filter8: false,
673 filter9: true
674 } ),
675 'A \'string_options\' parameter containing an invalid value, results in the invalid value ignored and the valid corresponding filters checked.'
676 );
677 } );
678
679 QUnit.test( 'sanitizeStringOptionGroup', function ( assert ) {
680 var definition = {
681 group1: {
682 title: 'Group 1',
683 type: 'string_options',
684 filters: [
685 {
686 name: 'filter1',
687 label: 'Show filter 1',
688 description: 'Description of Filter 1 in Group 1'
689 },
690 {
691 name: 'filter2',
692 label: 'Show filter 2',
693 description: 'Description of Filter 2 in Group 1'
694 },
695 {
696 name: 'filter3',
697 label: 'Show filter 3',
698 description: 'Description of Filter 3 in Group 1'
699 }
700 ]
701 }
702 },
703 model = new mw.rcfilters.dm.FiltersViewModel();
704
705 model.initializeFilters( definition );
706
707 assert.deepEqual(
708 model.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'filter1', 'filter2' ] ),
709 [ 'filter1', 'filter2' ],
710 'Remove duplicate values'
711 );
712
713 assert.deepEqual(
714 model.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'foo', 'filter2' ] ),
715 [ 'filter1', 'filter2' ],
716 'Remove invalid values'
717 );
718
719 assert.deepEqual(
720 model.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'all', 'filter2' ] ),
721 [ 'all' ],
722 'If any value is "all", the only value is "all".'
723 );
724 } );
725
726 QUnit.test( 'setFiltersToDefaults', function ( assert ) {
727 var definition = {
728 group1: {
729 title: 'Group 1',
730 type: 'send_unselected_if_any',
731 exclusionType: 'default',
732 filters: [
733 {
734 name: 'hidefilter1',
735 label: 'Show filter 1',
736 description: 'Description of Filter 1 in Group 1',
737 default: true
738 },
739 {
740 name: 'hidefilter2',
741 label: 'Show filter 2',
742 description: 'Description of Filter 2 in Group 1'
743 },
744 {
745 name: 'hidefilter3',
746 label: 'Show filter 3',
747 description: 'Description of Filter 3 in Group 1',
748 default: true
749 }
750 ]
751 },
752 group2: {
753 title: 'Group 2',
754 type: 'send_unselected_if_any',
755 filters: [
756 {
757 name: 'hidefilter4',
758 label: 'Show filter 4',
759 description: 'Description of Filter 1 in Group 2'
760 },
761 {
762 name: 'hidefilter5',
763 label: 'Show filter 5',
764 description: 'Description of Filter 2 in Group 2',
765 default: true
766 },
767 {
768 name: 'hidefilter6',
769 label: 'Show filter 6',
770 description: 'Description of Filter 3 in Group 2'
771 }
772 ]
773 }
774 },
775 model = new mw.rcfilters.dm.FiltersViewModel();
776
777 model.initializeFilters( definition );
778
779 assert.deepEqual(
780 model.getFullState(),
781 {
782 // Group 1
783 hidefilter1: { selected: true, active: true },
784 hidefilter2: { selected: false, active: true },
785 hidefilter3: { selected: true, active: true },
786 // Group 2
787 hidefilter4: { selected: false, active: true },
788 hidefilter5: { selected: true, active: true },
789 hidefilter6: { selected: false, active: true },
790 },
791 'Initial state: all filters are active, and select states are default.'
792 );
793
794 // Default behavior for 'exclusion' type with only 1 item selected, means that:
795 // - The items in the same group that are *not* selected are *not* active
796 // - Items in other groups are unaffected (all active)
797 model.updateFilters( {
798 hidefilter1: false,
799 hidefilter2: false,
800 hidefilter3: false,
801 hidefilter4: false,
802 hidefilter5: false,
803 hidefilter6: true
804 } );
805 assert.deepEqual(
806 model.getFullState(),
807 {
808 // Group 1: not affected
809 hidefilter1: { selected: false, active: true },
810 hidefilter2: { selected: false, active: true },
811 hidefilter3: { selected: false, active: true },
812 // Group 2: affected
813 hidefilter4: { selected: false, active: false },
814 hidefilter5: { selected: false, active: false },
815 hidefilter6: { selected: true, active: true },
816 },
817 'Default exclusion behavior with 1 item selected in the group.'
818 );
819
820 // Default behavior for 'exclusion' type with multiple items selected, but not all, means that:
821 // - The items in the same group that are *not* selected are *not* active
822 // - Items in other groups are unaffected (all active)
823 model.updateFilters( {
824 // Literally updating filters to create a clean state
825 hidefilter1: false,
826 hidefilter2: false,
827 hidefilter3: false,
828 hidefilter4: false,
829 hidefilter5: true,
830 hidefilter6: true
831 } );
832 assert.deepEqual(
833 model.getFullState(),
834 {
835 // Group 1: not affected
836 hidefilter1: { selected: false, active: true },
837 hidefilter2: { selected: false, active: true },
838 hidefilter3: { selected: false, active: true },
839 // Group 2: affected
840 hidefilter4: { selected: false, active: false },
841 hidefilter5: { selected: true, active: true },
842 hidefilter6: { selected: true, active: true },
843 },
844 'Default exclusion behavior with multiple items (but not all) selected in the group.'
845 );
846
847 // Default behavior for 'exclusion' type with all items in the group selected, means that:
848 // - All items in the group are NOT active
849 // - Items in other groups are unaffected (all active)
850 model.updateFilters( {
851 // Literally updating filters to create a clean state
852 hidefilter1: false,
853 hidefilter2: false,
854 hidefilter3: false,
855 hidefilter4: true,
856 hidefilter5: true,
857 hidefilter6: true
858 } );
859 assert.deepEqual(
860 model.getFullState(),
861 {
862 // Group 1: not affected
863 hidefilter1: { selected: false, active: true },
864 hidefilter2: { selected: false, active: true },
865 hidefilter3: { selected: false, active: true },
866 // Group 2: affected
867 hidefilter4: { selected: true, active: false },
868 hidefilter5: { selected: true, active: false },
869 hidefilter6: { selected: true, active: false },
870 },
871 'Default exclusion behavior with all items in the group.'
872 );
873 } );
874
875 QUnit.test( 'reapplyActiveFilters - "explicit" exclusion rules', function ( assert ) {
876 var definition = {
877 group1: {
878 title: 'Group 1',
879 type: 'send_unselected_if_any',
880 exclusionType: 'explicit',
881 filters: [
882 {
883 name: 'filter1',
884 excludes: [ 'filter2', 'filter3' ],
885 label: 'Show filter 1',
886 description: 'Description of Filter 1 in Group 1'
887 },
888 {
889 name: 'filter2',
890 excludes: [ 'filter3' ],
891 label: 'Show filter 2',
892 description: 'Description of Filter 2 in Group 1'
893 },
894 {
895 name: 'filter3',
896 label: 'Show filter 3',
897 excludes: [ 'filter1' ],
898 description: 'Description of Filter 3 in Group 1'
899 },
900 {
901 name: 'filter4',
902 label: 'Show filter 4',
903 description: 'Description of Filter 4 in Group 1'
904 }
905 ]
906 }
907 },
908 defaultFilterRepresentation = {
909 // Group 1 and 2, "send_unselected_if_any", the values of the filters are "flipped" from the values of the parameters
910 hidefilter1: false,
911 hidefilter2: true,
912 hidefilter3: false,
913 hidefilter4: true,
914 hidefilter5: false,
915 hidefilter6: true,
916 // Group 3, "string_options", default values correspond to parameters and filters
917 filter7: false,
918 filter8: true,
919 filter9: false
920 },
921 model = new mw.rcfilters.dm.FiltersViewModel();
922
923 model.initializeFilters( definition );
924
925 assert.deepEqual(
926 model.getFullState(),
927 {
928 filter1: { selected: false, active: true },
929 filter2: { selected: false, active: true },
930 filter3: { selected: false, active: true },
931 filter4: { selected: false, active: true }
932 },
933 'Initial state: all filters are active.'
934 );
935
936 // "Explicit" behavior for 'exclusion' with one item checked:
937 // - Items in the 'excluded' list of the selected filter are inactive
938 model.updateFilters( {
939 // Literally updating filters to create a clean state
940 filter1: true, // Excludes 'hidefilter2', 'hidefilter3'
941 filter2: false, // Excludes 'hidefilter3'
942 filter3: false, // Excludes 'hidefilter1'
943 filter4: false // No exclusion list
944 } );
945 assert.deepEqual(
946 model.getFullState(),
947 {
948 filter1: { selected: true, active: true },
949 filter2: { selected: false, active: false },
950 filter3: { selected: false, active: false },
951 filter4: { selected: false, active: true }
952 },
953 '"Explicit" exclusion behavior with one item selected that has an exclusion list.'
954 );
955
956 // "Explicit" behavior for 'exclusion' with two item checked:
957 // - Items in the 'excluded' list of each of the selected filter are inactive
958 model.updateFilters( {
959 // Literally updating filters to create a clean state
960 filter1: true, // Excludes 'hidefilter2', 'hidefilter3'
961 filter2: false, // Excludes 'hidefilter3'
962 filter3: true, // Excludes 'hidefilter1'
963 filter4: false // No exclusion list
964 } );
965 assert.deepEqual(
966 model.getFullState(),
967 {
968 filter1: { selected: true, active: false },
969 filter2: { selected: false, active: false },
970 filter3: { selected: true, active: false },
971 filter4: { selected: false, active: true }
972 },
973 '"Explicit" exclusion behavior with two selected items that both have an exclusion list.'
974 );
975
976 // "Explicit behavior" with two filters that exclude the same item
977
978 // Two filters selected, both exclude 'hidefilter3'
979 model.updateFilters( {
980 // Literally updating filters to create a clean state
981 filter1: true, // Excludes 'hidefilter2', 'hidefilter3'
982 filter2: true, // Excludes 'hidefilter3'
983 filter3: false, // Excludes 'hidefilter1'
984 filter4: false // No exclusion list
985 } );
986 assert.deepEqual(
987 model.getFullState(),
988 {
989 filter1: { selected: true, active: true },
990 filter2: { selected: true, active: false }, // Excluded by filter1
991 filter3: { selected: false, active: false }, // Excluded by both filter1 and filter2
992 filter4: { selected: false, active: true }
993 },
994 '"Explicit" exclusion behavior with two selected items that both exclude another item.'
995 );
996
997 // Unselect filter2: filter3 should still be excluded, because filter1 excludes it and is selected
998 model.updateFilters( {
999 filter2: false, // Excludes 'hidefilter3'
1000 } );
1001 assert.deepEqual(
1002 model.getFullState(),
1003 {
1004 filter1: { selected: true, active: true },
1005 filter2: { selected: false, active: false }, // Excluded by filter1
1006 filter3: { selected: false, active: false }, // Still excluded by filter1
1007 filter4: { selected: false, active: true }
1008 },
1009 '"Explicit" exclusion behavior unselecting one item that excludes another item, that is being excluded by a third active item.'
1010 );
1011
1012 // Unselect filter1: filter3 should now be active, since both filters that exclude it are unselected
1013 model.updateFilters( {
1014 filter1: false, // Excludes 'hidefilter3' and 'hidefilter2'
1015 } );
1016 assert.deepEqual(
1017 model.getFullState(),
1018 {
1019 filter1: { selected: false, active: true },
1020 filter2: { selected: false, active: true }, // No longer excluded by filter1
1021 filter3: { selected: false, active: true }, // No longer excluded by either filter1 nor filter2
1022 filter4: { selected: false, active: true }
1023 },
1024 '"Explicit" exclusion behavior unselecting both items that excluded the same third item.'
1025 );
1026
1027 } );
1028 }( mediaWiki, jQuery ) );