Merge "RCFilters: Change the input icon on interaction"
[lhc/web/wiklou.git] / tests / qunit / suites / resources / mediawiki.rcfilters / dm.FiltersViewModel.test.js
1 /* eslint-disable camelcase */
2 ( function ( mw, $ ) {
3 QUnit.module( 'mediawiki.rcfilters - FiltersViewModel', QUnit.newMwEnvironment( {
4 messages: {
5 'group1filter1-label': 'Group 1: Filter 1',
6 'group1filter1-desc': 'Description of Filter 1 in Group 1',
7 'group1filter2-label': 'Group 1: Filter 2',
8 'group1filter2-desc': 'Description of Filter 2 in Group 1',
9 'group2filter1-label': 'Group 2: Filter 1',
10 'group2filter1-desc': 'Description of Filter 1 in Group 2',
11 'group2filter2-label': 'xGroup 2: Filter 2',
12 'group2filter2-desc': 'Description of Filter 2 in Group 2'
13 },
14 config: {
15 wgStructuredChangeFiltersEnableExperimentalViews: true
16 }
17 } ) );
18
19 QUnit.test( 'Setting up filters', function ( assert ) {
20 var definition = [ {
21 name: 'group1',
22 title: 'Group 1',
23 type: 'send_unselected_if_any',
24 filters: [
25 {
26 name: 'filter1',
27 label: 'Group 1: Filter 1',
28 description: 'Description of Filter 1 in Group 1'
29 },
30 {
31 name: 'filter2',
32 label: 'Group 1: Filter 2',
33 description: 'Description of Filter 2 in Group 1'
34 }
35 ]
36 }, {
37 name: 'group2',
38 title: 'Group 2',
39 type: 'send_unselected_if_any',
40 filters: [
41 {
42 name: 'filter1',
43 label: 'Group 2: Filter 1',
44 description: 'Description of Filter 1 in Group 2'
45 },
46 {
47 name: 'filter2',
48 label: 'Group 2: Filter 2',
49 description: 'Description of Filter 2 in Group 2'
50 }
51 ]
52 }, {
53 name: 'group3',
54 title: 'Group 3',
55 type: 'string_options',
56 filters: [
57 {
58 name: 'filter1',
59 label: 'Group 3: Filter 1',
60 description: 'Description of Filter 1 in Group 3'
61 },
62 {
63 name: 'filter2',
64 label: 'Group 3: Filter 2',
65 description: 'Description of Filter 2 in Group 3'
66 }
67 ]
68 } ],
69 views = {
70 namespaces: {
71 label: 'Namespaces',
72 trigger: ':',
73 groups: [ {
74 name: 'namespace',
75 label: 'Namespaces',
76 separator: ';',
77 items: [
78 { name: 0, label: 'Main' },
79 { name: 1, label: 'Talk' },
80 { name: 2, label: 'User' },
81 { name: 3, label: 'User talk' }
82 ]
83 } ]
84 }
85 },
86 model = new mw.rcfilters.dm.FiltersViewModel();
87
88 model.initializeFilters( definition, views );
89
90 assert.ok(
91 model.getItemByName( 'group1__filter1' ) instanceof mw.rcfilters.dm.FilterItem &&
92 model.getItemByName( 'group1__filter2' ) instanceof mw.rcfilters.dm.FilterItem &&
93 model.getItemByName( 'group2__filter1' ) instanceof mw.rcfilters.dm.FilterItem &&
94 model.getItemByName( 'group2__filter2' ) instanceof mw.rcfilters.dm.FilterItem &&
95 model.getItemByName( 'group3__filter1' ) instanceof mw.rcfilters.dm.FilterItem &&
96 model.getItemByName( 'group3__filter2' ) instanceof mw.rcfilters.dm.FilterItem,
97 model.getItemByName( 'namespace__0' ) instanceof mw.rcfilters.dm.FilterItem,
98 model.getItemByName( 'namespace__1' ) instanceof mw.rcfilters.dm.FilterItem,
99 model.getItemByName( 'namespace__2' ) instanceof mw.rcfilters.dm.FilterItem,
100 model.getItemByName( 'namespace__3' ) instanceof mw.rcfilters.dm.FilterItem,
101 'Filters instantiated and stored correctly'
102 );
103
104 assert.deepEqual(
105 model.getSelectedState(),
106 {
107 group1__filter1: false,
108 group1__filter2: false,
109 group2__filter1: false,
110 group2__filter2: false,
111 group3__filter1: false,
112 group3__filter2: false,
113 namespace__0: false,
114 namespace__1: false,
115 namespace__2: false,
116 namespace__3: false
117 },
118 'Initial state of filters'
119 );
120
121 model.toggleFiltersSelected( {
122 group1__filter1: true,
123 group2__filter2: true,
124 group3__filter1: true
125 } );
126 assert.deepEqual(
127 model.getSelectedState(),
128 {
129 group1__filter1: true,
130 group1__filter2: false,
131 group2__filter1: false,
132 group2__filter2: true,
133 group3__filter1: true,
134 group3__filter2: false,
135 namespace__0: false,
136 namespace__1: false,
137 namespace__2: false,
138 namespace__3: false
139 },
140 'Updating filter states correctly'
141 );
142 } );
143
144 QUnit.test( 'Default filters', function ( assert ) {
145 var definition = [ {
146 name: 'group1',
147 title: 'Group 1',
148 type: 'send_unselected_if_any',
149 filters: [
150 {
151 name: 'hidefilter1',
152 label: 'Show filter 1',
153 description: 'Description of Filter 1 in Group 1',
154 default: true
155 },
156 {
157 name: 'hidefilter2',
158 label: 'Show filter 2',
159 description: 'Description of Filter 2 in Group 1'
160 },
161 {
162 name: 'hidefilter3',
163 label: 'Show filter 3',
164 description: 'Description of Filter 3 in Group 1',
165 default: true
166 }
167 ]
168 }, {
169 name: 'group2',
170 title: 'Group 2',
171 type: 'send_unselected_if_any',
172 filters: [
173 {
174 name: 'hidefilter4',
175 label: 'Show filter 4',
176 description: 'Description of Filter 1 in Group 2'
177 },
178 {
179 name: 'hidefilter5',
180 label: 'Show filter 5',
181 description: 'Description of Filter 2 in Group 2',
182 default: true
183 },
184 {
185 name: 'hidefilter6',
186 label: 'Show filter 6',
187 description: 'Description of Filter 3 in Group 2'
188 }
189 ]
190 }, {
191
192 name: 'group3',
193 title: 'Group 3',
194 type: 'string_options',
195 separator: ',',
196 default: 'filter8',
197 filters: [
198 {
199 name: 'filter7',
200 label: 'Group 3: Filter 1',
201 description: 'Description of Filter 1 in Group 3'
202 },
203 {
204 name: 'filter8',
205 label: 'Group 3: Filter 2',
206 description: 'Description of Filter 2 in Group 3'
207 },
208 {
209 name: 'filter9',
210 label: 'Group 3: Filter 3',
211 description: 'Description of Filter 3 in Group 3'
212 }
213 ]
214 } ],
215 model = new mw.rcfilters.dm.FiltersViewModel();
216
217 model.initializeFilters( definition );
218
219 // Empty query = only default values
220 assert.deepEqual(
221 model.getDefaultParams(),
222 {
223 hidefilter1: '1',
224 hidefilter2: '0',
225 hidefilter3: '1',
226 hidefilter4: '0',
227 hidefilter5: '1',
228 hidefilter6: '0',
229 group3: 'filter8'
230 },
231 'Default parameters are stored properly per filter and group'
232 );
233 } );
234
235 QUnit.test( 'Finding matching filters', function ( assert ) {
236 var matches,
237 definition = [ {
238 name: 'group1',
239 title: 'Group 1 title',
240 type: 'send_unselected_if_any',
241 filters: [
242 {
243 name: 'filter1',
244 label: 'group1filter1-label',
245 description: 'group1filter1-desc'
246 },
247 {
248 name: 'filter2',
249 label: 'group1filter2-label',
250 description: 'group1filter2-desc'
251 }
252 ]
253 }, {
254 name: 'group2',
255 title: 'Group 2 title',
256 type: 'send_unselected_if_any',
257 filters: [
258 {
259 name: 'filter1',
260 label: 'group2filter1-label',
261 description: 'group2filter1-desc'
262 },
263 {
264 name: 'filter2',
265 label: 'group2filter2-label',
266 description: 'group2filter2-desc'
267 }
268 ]
269 } ],
270 views = {
271 namespaces: {
272 label: 'Namespaces',
273 trigger: ':',
274 groups: [ {
275 name: 'namespace',
276 label: 'Namespaces',
277 separator: ';',
278 items: [
279 { name: 0, label: 'Main' },
280 { name: 1, label: 'Talk' },
281 { name: 2, label: 'User' },
282 { name: 3, label: 'User talk' }
283 ]
284 } ]
285 }
286 },
287 testCases = [
288 {
289 query: 'group',
290 expectedMatches: {
291 group1: [ 'group1__filter1', 'group1__filter2' ],
292 group2: [ 'group2__filter1' ]
293 },
294 reason: 'Finds filters starting with the query string'
295 },
296 {
297 query: 'filter 2 in group',
298 expectedMatches: {
299 group1: [ 'group1__filter2' ],
300 group2: [ 'group2__filter2' ]
301 },
302 reason: 'Finds filters containing the query string in their description'
303 },
304 {
305 query: 'title',
306 expectedMatches: {
307 group1: [ 'group1__filter1', 'group1__filter2' ],
308 group2: [ 'group2__filter1', 'group2__filter2' ]
309 },
310 reason: 'Finds filters containing the query string in their group title'
311 },
312 {
313 query: ':Main',
314 expectedMatches: {
315 namespace: [ 'namespace__0' ]
316 },
317 reason: 'Finds namespaces when using : prefix'
318 },
319 {
320 query: ':group',
321 expectedMatches: {},
322 reason: 'Finds no results if using namespaces prefix (:) to search for filter title'
323 }
324 ],
325 model = new mw.rcfilters.dm.FiltersViewModel(),
326 extractNames = function ( matches ) {
327 var result = {};
328 Object.keys( matches ).forEach( function ( groupName ) {
329 result[ groupName ] = matches[ groupName ].map( function ( item ) {
330 return item.getName();
331 } );
332 } );
333 return result;
334 };
335
336 model.initializeFilters( definition, views );
337
338 testCases.forEach( function ( testCase ) {
339 matches = model.findMatches( testCase.query );
340 assert.deepEqual(
341 extractNames( matches ),
342 testCase.expectedMatches,
343 testCase.reason
344 );
345 } );
346
347 matches = model.findMatches( 'foo' );
348 assert.ok(
349 $.isEmptyObject( matches ),
350 'findMatches returns an empty object when no results found'
351 );
352 } );
353
354 QUnit.test( 'getParametersFromFilters', function ( assert ) {
355 var definition = [ {
356 name: 'group1',
357 title: 'Group 1',
358 type: 'send_unselected_if_any',
359 filters: [
360 {
361 name: 'hidefilter1',
362 label: 'Group 1: Filter 1',
363 description: 'Description of Filter 1 in Group 1'
364 },
365 {
366 name: 'hidefilter2',
367 label: 'Group 1: Filter 2',
368 description: 'Description of Filter 2 in Group 1'
369 },
370 {
371 name: 'hidefilter3',
372 label: 'Group 1: Filter 3',
373 description: 'Description of Filter 3 in Group 1'
374 }
375 ]
376 }, {
377 name: 'group2',
378 title: 'Group 2',
379 type: 'send_unselected_if_any',
380 filters: [
381 {
382 name: 'hidefilter4',
383 label: 'Group 2: Filter 1',
384 description: 'Description of Filter 1 in Group 2'
385 },
386 {
387 name: 'hidefilter5',
388 label: 'Group 2: Filter 2',
389 description: 'Description of Filter 2 in Group 2'
390 },
391 {
392 name: 'hidefilter6',
393 label: 'Group 2: Filter 3',
394 description: 'Description of Filter 3 in Group 2'
395 }
396 ]
397 }, {
398 name: 'group3',
399 title: 'Group 3',
400 type: 'string_options',
401 separator: ',',
402 filters: [
403 {
404 name: 'filter7',
405 label: 'Group 3: Filter 1',
406 description: 'Description of Filter 1 in Group 3'
407 },
408 {
409 name: 'filter8',
410 label: 'Group 3: Filter 2',
411 description: 'Description of Filter 2 in Group 3'
412 },
413 {
414 name: 'filter9',
415 label: 'Group 3: Filter 3',
416 description: 'Description of Filter 3 in Group 3'
417 }
418 ]
419 } ],
420 model = new mw.rcfilters.dm.FiltersViewModel();
421
422 model.initializeFilters( definition );
423
424 // Starting with all filters unselected
425 assert.deepEqual(
426 model.getParametersFromFilters(),
427 {
428 hidefilter1: '0',
429 hidefilter2: '0',
430 hidefilter3: '0',
431 hidefilter4: '0',
432 hidefilter5: '0',
433 hidefilter6: '0',
434 group3: ''
435 },
436 'Unselected filters return all parameters falsey or \'\'.'
437 );
438
439 // Select 1 filter
440 model.toggleFiltersSelected( {
441 group1__hidefilter1: true,
442 group1__hidefilter2: false,
443 group1__hidefilter3: false,
444 group2__hidefilter4: false,
445 group2__hidefilter5: false,
446 group2__hidefilter6: false
447 } );
448 // Only one filter in one group
449 assert.deepEqual(
450 model.getParametersFromFilters(),
451 {
452 // Group 1 (one selected, the others are true)
453 hidefilter1: '0',
454 hidefilter2: '1',
455 hidefilter3: '1',
456 // Group 2 (nothing is selected, all false)
457 hidefilter4: '0',
458 hidefilter5: '0',
459 hidefilter6: '0',
460 group3: ''
461 },
462 'One filter in one "send_unselected_if_any" group returns the other parameters truthy.'
463 );
464
465 // Select 2 filters
466 model.toggleFiltersSelected( {
467 group1__hidefilter1: true,
468 group1__hidefilter2: true,
469 group1__hidefilter3: false,
470 group2__hidefilter4: false,
471 group2__hidefilter5: false,
472 group2__hidefilter6: false
473 } );
474 // Two selected filters in one group
475 assert.deepEqual(
476 model.getParametersFromFilters(),
477 {
478 // Group 1 (two selected, the others are true)
479 hidefilter1: '0',
480 hidefilter2: '0',
481 hidefilter3: '1',
482 // Group 2 (nothing is selected, all false)
483 hidefilter4: '0',
484 hidefilter5: '0',
485 hidefilter6: '0',
486 group3: ''
487 },
488 'Two filters in one "send_unselected_if_any" group returns the other parameters truthy.'
489 );
490
491 // Select 3 filters
492 model.toggleFiltersSelected( {
493 group1__hidefilter1: true,
494 group1__hidefilter2: true,
495 group1__hidefilter3: true,
496 group2__hidefilter4: false,
497 group2__hidefilter5: false,
498 group2__hidefilter6: false
499 } );
500 // All filters of the group are selected == this is the same as not selecting any
501 assert.deepEqual(
502 model.getParametersFromFilters(),
503 {
504 // Group 1 (all selected, all false)
505 hidefilter1: '0',
506 hidefilter2: '0',
507 hidefilter3: '0',
508 // Group 2 (nothing is selected, all false)
509 hidefilter4: '0',
510 hidefilter5: '0',
511 hidefilter6: '0',
512 group3: ''
513 },
514 'All filters selected in one "send_unselected_if_any" group returns all parameters falsy.'
515 );
516
517 // Select 1 filter from string_options
518 model.toggleFiltersSelected( {
519 group3__filter7: true,
520 group3__filter8: false,
521 group3__filter9: false
522 } );
523 // All filters of the group are selected == this is the same as not selecting any
524 assert.deepEqual(
525 model.getParametersFromFilters(),
526 {
527 // Group 1 (all selected, all)
528 hidefilter1: '0',
529 hidefilter2: '0',
530 hidefilter3: '0',
531 // Group 2 (nothing is selected, all false)
532 hidefilter4: '0',
533 hidefilter5: '0',
534 hidefilter6: '0',
535 group3: 'filter7'
536 },
537 'One filter selected in "string_option" group returns that filter in the value.'
538 );
539
540 // Select 2 filters from string_options
541 model.toggleFiltersSelected( {
542 group3__filter7: true,
543 group3__filter8: true,
544 group3__filter9: false
545 } );
546 // All filters of the group are selected == this is the same as not selecting any
547 assert.deepEqual(
548 model.getParametersFromFilters(),
549 {
550 // Group 1 (all selected, all)
551 hidefilter1: '0',
552 hidefilter2: '0',
553 hidefilter3: '0',
554 // Group 2 (nothing is selected, all false)
555 hidefilter4: '0',
556 hidefilter5: '0',
557 hidefilter6: '0',
558 group3: 'filter7,filter8'
559 },
560 'Two filters selected in "string_option" group returns those filters in the value.'
561 );
562
563 // Select 3 filters from string_options
564 model.toggleFiltersSelected( {
565 group3__filter7: true,
566 group3__filter8: true,
567 group3__filter9: true
568 } );
569 // All filters of the group are selected == this is the same as not selecting any
570 assert.deepEqual(
571 model.getParametersFromFilters(),
572 {
573 // Group 1 (all selected, all)
574 hidefilter1: '0',
575 hidefilter2: '0',
576 hidefilter3: '0',
577 // Group 2 (nothing is selected, all false)
578 hidefilter4: '0',
579 hidefilter5: '0',
580 hidefilter6: '0',
581 group3: 'all'
582 },
583 'All filters selected in "string_option" group returns \'all\'.'
584 );
585
586 } );
587
588 QUnit.test( 'getParametersFromFilters (custom object)', function ( assert ) {
589 var originalState,
590 model = new mw.rcfilters.dm.FiltersViewModel(),
591 definition = [ {
592 name: 'group1',
593 title: 'Group 1',
594 type: 'send_unselected_if_any',
595 filters: [
596 { name: 'hidefilter1', label: 'Hide filter 1', description: '' },
597 { name: 'hidefilter2', label: 'Hide filter 2', description: '' },
598 { name: 'hidefilter3', label: 'Hide filter 3', description: '' }
599 ]
600 }, {
601 name: 'group2',
602 title: 'Group 2',
603 type: 'send_unselected_if_any',
604 filters: [
605 { name: 'hidefilter4', label: 'Hide filter 4', description: '' },
606 { name: 'hidefilter5', label: 'Hide filter 5', description: '' },
607 { name: 'hidefilter6', label: 'Hide filter 6', description: '' }
608 ]
609 }, {
610 name: 'group3',
611 title: 'Group 3',
612 type: 'string_options',
613 separator: ',',
614 filters: [
615 { name: 'filter7', label: 'Hide filter 7', description: '' },
616 { name: 'filter8', label: 'Hide filter 8', description: '' },
617 { name: 'filter9', label: 'Hide filter 9', description: '' }
618 ]
619 } ],
620 cases = [
621 {
622 // This is mocking the cases above, both
623 // - 'Two filters in one "send_unselected_if_any" group returns the other parameters truthy.'
624 // - 'Two filters selected in "string_option" group returns those filters in the value.'
625 input: {
626 group1__hidefilter1: true,
627 group1__hidefilter2: true,
628 group1__hidefilter3: false,
629 group2__hidefilter4: false,
630 group2__hidefilter5: false,
631 group2__hidefilter6: false,
632 group3__filter7: true,
633 group3__filter8: true,
634 group3__filter9: false
635 },
636 expected: {
637 // Group 1 (two selected, the others are true)
638 hidefilter1: '0',
639 hidefilter2: '0',
640 hidefilter3: '1',
641 // Group 2 (nothing is selected, all false)
642 hidefilter4: '0',
643 hidefilter5: '0',
644 hidefilter6: '0',
645 group3: 'filter7,filter8'
646 },
647 msg: 'Given an explicit (complete) filter state object, the result is the same as if the object given represented the model state.'
648 },
649 {
650 // This is mocking case above
651 // - 'One filter in one "send_unselected_if_any" group returns the other parameters truthy.'
652 input: {
653 group1__hidefilter1: 1
654 },
655 expected: {
656 // Group 1 (one selected, the others are true)
657 hidefilter1: '0',
658 hidefilter2: '1',
659 hidefilter3: '1',
660 // Group 2 (nothing is selected, all false)
661 hidefilter4: '0',
662 hidefilter5: '0',
663 hidefilter6: '0',
664 group3: ''
665 },
666 msg: 'Given an explicit (incomplete) filter state object, the result is the same as if the object give represented the model state.'
667 },
668 {
669 input: {},
670 expected: {
671 hidefilter1: '0',
672 hidefilter2: '0',
673 hidefilter3: '0',
674 hidefilter4: '0',
675 hidefilter5: '0',
676 hidefilter6: '0',
677 group3: ''
678 },
679 msg: 'Given an explicit empty object, the result is all filters set to their falsey unselected value.'
680 }
681 ];
682
683 model.initializeFilters( definition );
684 // Store original state
685 originalState = model.getSelectedState();
686
687 // Test each case
688 cases.forEach( function ( test ) {
689 assert.deepEqual(
690 model.getParametersFromFilters( test.input ),
691 test.expected,
692 test.msg
693 );
694 } );
695
696 // After doing the above tests, make sure the actual state
697 // of the filter stayed the same
698 assert.deepEqual(
699 model.getSelectedState(),
700 originalState,
701 'Running the method with external definition to parse does not actually change the state of the model'
702 );
703 } );
704
705 QUnit.test( 'getFiltersFromParameters', function ( assert ) {
706 var definition = [ {
707 name: 'group1',
708 title: 'Group 1',
709 type: 'send_unselected_if_any',
710 filters: [
711 {
712 name: 'hidefilter1',
713 label: 'Show filter 1',
714 description: 'Description of Filter 1 in Group 1',
715 default: true
716 },
717 {
718 name: 'hidefilter2',
719 label: 'Show filter 2',
720 description: 'Description of Filter 2 in Group 1'
721 },
722 {
723 name: 'hidefilter3',
724 label: 'Show filter 3',
725 description: 'Description of Filter 3 in Group 1',
726 default: true
727 }
728 ]
729 }, {
730 name: 'group2',
731 title: 'Group 2',
732 type: 'send_unselected_if_any',
733 filters: [
734 {
735 name: 'hidefilter4',
736 label: 'Show filter 4',
737 description: 'Description of Filter 1 in Group 2'
738 },
739 {
740 name: 'hidefilter5',
741 label: 'Show filter 5',
742 description: 'Description of Filter 2 in Group 2',
743 default: true
744 },
745 {
746 name: 'hidefilter6',
747 label: 'Show filter 6',
748 description: 'Description of Filter 3 in Group 2'
749 }
750 ]
751 }, {
752
753 name: 'group3',
754 title: 'Group 3',
755 type: 'string_options',
756 separator: ',',
757 default: 'filter8',
758 filters: [
759 {
760 name: 'filter7',
761 label: 'Group 3: Filter 1',
762 description: 'Description of Filter 1 in Group 3'
763 },
764 {
765 name: 'filter8',
766 label: 'Group 3: Filter 2',
767 description: 'Description of Filter 2 in Group 3'
768 },
769 {
770 name: 'filter9',
771 label: 'Group 3: Filter 3',
772 description: 'Description of Filter 3 in Group 3'
773 }
774 ]
775 } ],
776 baseFilterRepresentation = {
777 group1__hidefilter1: false,
778 group1__hidefilter2: false,
779 group1__hidefilter3: false,
780 group2__hidefilter4: false,
781 group2__hidefilter5: false,
782 group2__hidefilter6: false,
783 group3__filter7: false,
784 group3__filter8: false,
785 group3__filter9: false
786 },
787 model = new mw.rcfilters.dm.FiltersViewModel();
788
789 model.initializeFilters( definition );
790
791 // Empty query = only default values
792 assert.deepEqual(
793 model.getFiltersFromParameters( {} ),
794 baseFilterRepresentation,
795 'Empty parameter query results in an object representing all filters set to false'
796 );
797
798 assert.deepEqual(
799 model.getFiltersFromParameters( {
800 hidefilter2: '1'
801 } ),
802 $.extend( {}, baseFilterRepresentation, {
803 group1__hidefilter1: true, // The text is "show filter 1"
804 group1__hidefilter2: false, // The text is "show filter 2"
805 group1__hidefilter3: true // The text is "show filter 3"
806 } ),
807 'One truthy parameter in a group whose other parameters are true by default makes the rest of the filters in the group false (unchecked)'
808 );
809
810 assert.deepEqual(
811 model.getFiltersFromParameters( {
812 hidefilter1: '1',
813 hidefilter2: '1',
814 hidefilter3: '1'
815 } ),
816 $.extend( {}, baseFilterRepresentation, {
817 group1__hidefilter1: false, // The text is "show filter 1"
818 group1__hidefilter2: false, // The text is "show filter 2"
819 group1__hidefilter3: false // The text is "show filter 3"
820 } ),
821 'All paremeters in the same \'send_unselected_if_any\' group false is equivalent to none are truthy (checked) in the interface'
822 );
823
824 // The ones above don't update the model, so we have a clean state.
825 // getFiltersFromParameters is stateless; any change is unaffected by the current state
826 // This test is demonstrating wrong usage of the method;
827 // We should be aware that getFiltersFromParameters is stateless,
828 // so each call gives us a filter state that only reflects the query given.
829 // This means that the two calls to toggleFiltersSelected() below collide.
830 // The result of the first is overridden by the result of the second,
831 // since both get a full state object from getFiltersFromParameters that **only** relates
832 // to the input it receives.
833 model.toggleFiltersSelected(
834 model.getFiltersFromParameters( {
835 hidefilter1: '1'
836 } )
837 );
838
839 model.toggleFiltersSelected(
840 model.getFiltersFromParameters( {
841 hidefilter6: '1'
842 } )
843 );
844
845 // The result here is ignoring the first toggleFiltersSelected call
846 assert.deepEqual(
847 model.getSelectedState(),
848 $.extend( {}, baseFilterRepresentation, {
849 group2__hidefilter4: true,
850 group2__hidefilter5: true,
851 group2__hidefilter6: false
852 } ),
853 'getFiltersFromParameters does not care about previous or existing state.'
854 );
855
856 // Reset
857 model = new mw.rcfilters.dm.FiltersViewModel();
858 model.initializeFilters( definition );
859
860 model.toggleFiltersSelected(
861 model.getFiltersFromParameters( {
862 group3: 'filter7'
863 } )
864 );
865 assert.deepEqual(
866 model.getSelectedState(),
867 $.extend( {}, baseFilterRepresentation, {
868 group3__filter7: true,
869 group3__filter8: false,
870 group3__filter9: false
871 } ),
872 'A \'string_options\' parameter containing 1 value, results in the corresponding filter as checked'
873 );
874
875 model.toggleFiltersSelected(
876 model.getFiltersFromParameters( {
877 group3: 'filter7,filter8'
878 } )
879 );
880 assert.deepEqual(
881 model.getSelectedState(),
882 $.extend( {}, baseFilterRepresentation, {
883 group3__filter7: true,
884 group3__filter8: true,
885 group3__filter9: false
886 } ),
887 'A \'string_options\' parameter containing 2 values, results in both corresponding filters as checked'
888 );
889
890 model.toggleFiltersSelected(
891 model.getFiltersFromParameters( {
892 group3: 'filter7,filter8,filter9'
893 } )
894 );
895 assert.deepEqual(
896 model.getSelectedState(),
897 $.extend( {}, baseFilterRepresentation, {
898 group3__filter7: true,
899 group3__filter8: true,
900 group3__filter9: true
901 } ),
902 'A \'string_options\' parameter containing all values, results in all filters of the group as checked.'
903 );
904
905 model.toggleFiltersSelected(
906 model.getFiltersFromParameters( {
907 group3: 'filter7,all,filter9'
908 } )
909 );
910 assert.deepEqual(
911 model.getSelectedState(),
912 $.extend( {}, baseFilterRepresentation, {
913 group3__filter7: true,
914 group3__filter8: true,
915 group3__filter9: true
916 } ),
917 'A \'string_options\' parameter containing the value \'all\', results in all filters of the group as checked.'
918 );
919
920 model.toggleFiltersSelected(
921 model.getFiltersFromParameters( {
922 group3: 'filter7,foo,filter9'
923 } )
924 );
925 assert.deepEqual(
926 model.getSelectedState(),
927 $.extend( {}, baseFilterRepresentation, {
928 group3__filter7: true,
929 group3__filter8: false,
930 group3__filter9: true
931 } ),
932 'A \'string_options\' parameter containing an invalid value, results in the invalid value ignored and the valid corresponding filters checked.'
933 );
934 } );
935
936 QUnit.test( 'sanitizeStringOptionGroup', function ( assert ) {
937 var definition = [ {
938 name: 'group1',
939 title: 'Group 1',
940 type: 'string_options',
941 filters: [
942 {
943 name: 'filter1',
944 label: 'Show filter 1',
945 description: 'Description of Filter 1 in Group 1'
946 },
947 {
948 name: 'filter2',
949 label: 'Show filter 2',
950 description: 'Description of Filter 2 in Group 1'
951 },
952 {
953 name: 'filter3',
954 label: 'Show filter 3',
955 description: 'Description of Filter 3 in Group 1'
956 }
957 ]
958 } ],
959 model = new mw.rcfilters.dm.FiltersViewModel();
960
961 model.initializeFilters( definition );
962
963 assert.deepEqual(
964 model.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'filter1', 'filter2' ] ),
965 [ 'filter1', 'filter2' ],
966 'Remove duplicate values'
967 );
968
969 assert.deepEqual(
970 model.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'foo', 'filter2' ] ),
971 [ 'filter1', 'filter2' ],
972 'Remove invalid values'
973 );
974
975 assert.deepEqual(
976 model.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'all', 'filter2' ] ),
977 [ 'all' ],
978 'If any value is "all", the only value is "all".'
979 );
980 } );
981
982 QUnit.test( 'Filter interaction: subsets', function ( assert ) {
983 var definition = [ {
984 name: 'group1',
985 title: 'Group 1',
986 type: 'string_options',
987 filters: [
988 {
989 name: 'filter1',
990 label: 'Show filter 1',
991 description: 'Description of Filter 1 in Group 1',
992 subset: [
993 {
994 group: 'group1',
995 filter: 'filter2'
996 },
997 {
998 group: 'group1',
999 filter: 'filter3'
1000 }
1001 ]
1002 },
1003 {
1004 name: 'filter2',
1005 label: 'Show filter 2',
1006 description: 'Description of Filter 2 in Group 1',
1007 subset: [
1008 {
1009 group: 'group1',
1010 filter: 'filter3'
1011 }
1012 ]
1013 },
1014 {
1015 name: 'filter3',
1016 label: 'Show filter 3',
1017 description: 'Description of Filter 3 in Group 1'
1018 }
1019 ]
1020 } ],
1021 baseFullState = {
1022 group1__filter1: { selected: false, conflicted: false, included: false },
1023 group1__filter2: { selected: false, conflicted: false, included: false },
1024 group1__filter3: { selected: false, conflicted: false, included: false }
1025 },
1026 model = new mw.rcfilters.dm.FiltersViewModel();
1027
1028 model.initializeFilters( definition );
1029 // Select a filter that has subset with another filter
1030 model.toggleFiltersSelected( {
1031 group1__filter1: true
1032 } );
1033
1034 model.reassessFilterInteractions( model.getItemByName( 'group1__filter1' ) );
1035 assert.deepEqual(
1036 model.getFullState(),
1037 $.extend( true, {}, baseFullState, {
1038 group1__filter1: { selected: true },
1039 group1__filter2: { included: true },
1040 group1__filter3: { included: true }
1041 } ),
1042 'Filters with subsets are represented in the model.'
1043 );
1044
1045 // Select another filter that has a subset with the same previous filter
1046 model.toggleFiltersSelected( {
1047 group1__filter2: true
1048 } );
1049 model.reassessFilterInteractions( model.getItemByName( 'filter2' ) );
1050 assert.deepEqual(
1051 model.getFullState(),
1052 $.extend( true, {}, baseFullState, {
1053 group1__filter1: { selected: true },
1054 group1__filter2: { selected: true, included: true },
1055 group1__filter3: { included: true }
1056 } ),
1057 'Filters that have multiple subsets are represented.'
1058 );
1059
1060 // Remove one filter (but leave the other) that affects filter3
1061 model.toggleFiltersSelected( {
1062 group1__filter1: false
1063 } );
1064 model.reassessFilterInteractions( model.getItemByName( 'group1__filter1' ) );
1065 assert.deepEqual(
1066 model.getFullState(),
1067 $.extend( true, {}, baseFullState, {
1068 group1__filter2: { selected: true, included: false },
1069 group1__filter3: { included: true }
1070 } ),
1071 'Removing a filter only un-includes its subset if there is no other filter affecting.'
1072 );
1073
1074 model.toggleFiltersSelected( {
1075 group1__filter2: false
1076 } );
1077 model.reassessFilterInteractions( model.getItemByName( 'group1__filter2' ) );
1078 assert.deepEqual(
1079 model.getFullState(),
1080 baseFullState,
1081 'Removing all supersets also un-includes the subsets.'
1082 );
1083 } );
1084
1085 QUnit.test( 'Filter interaction: full coverage', function ( assert ) {
1086 var definition = [ {
1087 name: 'group1',
1088 title: 'Group 1',
1089 type: 'string_options',
1090 fullCoverage: false,
1091 filters: [
1092 { name: 'filter1', label: '1', description: '1' },
1093 { name: 'filter2', label: '2', description: '2' },
1094 { name: 'filter3', label: '3', description: '3' }
1095 ]
1096 }, {
1097 name: 'group2',
1098 title: 'Group 2',
1099 type: 'send_unselected_if_any',
1100 fullCoverage: true,
1101 filters: [
1102 { name: 'filter4', label: '4', description: '4' },
1103 { name: 'filter5', label: '5', description: '5' },
1104 { name: 'filter6', label: '6', description: '6' }
1105 ]
1106 } ],
1107 model = new mw.rcfilters.dm.FiltersViewModel(),
1108 isCapsuleItemMuted = function ( filterName ) {
1109 var itemModel = model.getItemByName( filterName ),
1110 groupModel = itemModel.getGroupModel();
1111
1112 // This is the logic inside the capsule widget
1113 return (
1114 // The capsule item widget only appears if the item is selected
1115 itemModel.isSelected() &&
1116 // Muted state is only valid if group is full coverage and all items are selected
1117 groupModel.isFullCoverage() && groupModel.areAllSelected()
1118 );
1119 },
1120 getCurrentItemsMutedState = function () {
1121 return {
1122 group1__filter1: isCapsuleItemMuted( 'group1__filter1' ),
1123 group1__filter2: isCapsuleItemMuted( 'group1__filter2' ),
1124 group1__filter3: isCapsuleItemMuted( 'group1__filter3' ),
1125 group2__filter4: isCapsuleItemMuted( 'group2__filter4' ),
1126 group2__filter5: isCapsuleItemMuted( 'group2__filter5' ),
1127 group2__filter6: isCapsuleItemMuted( 'group2__filter6' )
1128 };
1129 },
1130 baseMuteState = {
1131 group1__filter1: false,
1132 group1__filter2: false,
1133 group1__filter3: false,
1134 group2__filter4: false,
1135 group2__filter5: false,
1136 group2__filter6: false
1137 };
1138
1139 model.initializeFilters( definition );
1140
1141 // Starting state, no selection, all items are non-muted
1142 assert.deepEqual(
1143 getCurrentItemsMutedState(),
1144 baseMuteState,
1145 'No selection - all items are non-muted'
1146 );
1147
1148 // Select most (but not all) items in each group
1149 model.toggleFiltersSelected( {
1150 group1__filter1: true,
1151 group1__filter2: true,
1152 group2__filter4: true,
1153 group2__filter5: true
1154 } );
1155
1156 // Both groups have multiple (but not all) items selected, all items are non-muted
1157 assert.deepEqual(
1158 getCurrentItemsMutedState(),
1159 baseMuteState,
1160 'Not all items in the group selected - all items are non-muted'
1161 );
1162
1163 // Select all items in 'fullCoverage' group (group2)
1164 model.toggleFiltersSelected( {
1165 group2__filter6: true
1166 } );
1167
1168 // Group2 (full coverage) has all items selected, all its items are muted
1169 assert.deepEqual(
1170 getCurrentItemsMutedState(),
1171 $.extend( {}, baseMuteState, {
1172 group2__filter4: true,
1173 group2__filter5: true,
1174 group2__filter6: true
1175 } ),
1176 'All items in \'full coverage\' group are selected - all items in the group are muted'
1177 );
1178
1179 // Select all items in non 'fullCoverage' group (group1)
1180 model.toggleFiltersSelected( {
1181 group1__filter3: true
1182 } );
1183
1184 // Group1 (full coverage) has all items selected, no items in it are muted (non full coverage)
1185 assert.deepEqual(
1186 getCurrentItemsMutedState(),
1187 $.extend( {}, baseMuteState, {
1188 group2__filter4: true,
1189 group2__filter5: true,
1190 group2__filter6: true
1191 } ),
1192 'All items in a non \'full coverage\' group are selected - none of the items in the group are muted'
1193 );
1194
1195 // Uncheck an item from each group
1196 model.toggleFiltersSelected( {
1197 group1__filter3: false,
1198 group2__filter5: false
1199 } );
1200 assert.deepEqual(
1201 getCurrentItemsMutedState(),
1202 baseMuteState,
1203 'Not all items in the group are checked - all items are non-muted regardless of group coverage'
1204 );
1205 } );
1206
1207 QUnit.test( 'Filter interaction: conflicts', function ( assert ) {
1208 var definition = [ {
1209 name: 'group1',
1210 title: 'Group 1',
1211 type: 'string_options',
1212 filters: [
1213 {
1214 name: 'filter1',
1215 label: '1',
1216 description: '1',
1217 conflicts: [ { group: 'group2' } ]
1218 },
1219 {
1220 name: 'filter2',
1221 label: '2',
1222 description: '2',
1223 conflicts: [ { group: 'group2', filter: 'filter6' } ]
1224 },
1225 {
1226 name: 'filter3',
1227 label: '3',
1228 description: '3'
1229 }
1230 ]
1231 }, {
1232 name: 'group2',
1233 title: 'Group 2',
1234 type: 'send_unselected_if_any',
1235 conflicts: [ { group: 'group1', filter: 'filter1' } ],
1236 filters: [
1237 {
1238 name: 'filter4',
1239 label: '1',
1240 description: '1'
1241 },
1242 {
1243 name: 'filter5',
1244 label: '5',
1245 description: '5'
1246 },
1247 {
1248 name: 'filter6',
1249 label: '6',
1250 description: '6',
1251 conflicts: [ { group: 'group1', filter: 'filter2' } ]
1252 }
1253 ]
1254 } ],
1255 baseFullState = {
1256 group1__filter1: { selected: false, conflicted: false, included: false },
1257 group1__filter2: { selected: false, conflicted: false, included: false },
1258 group1__filter3: { selected: false, conflicted: false, included: false },
1259 group2__filter4: { selected: false, conflicted: false, included: false },
1260 group2__filter5: { selected: false, conflicted: false, included: false },
1261 group2__filter6: { selected: false, conflicted: false, included: false }
1262 },
1263 model = new mw.rcfilters.dm.FiltersViewModel();
1264
1265 model.initializeFilters( definition );
1266
1267 assert.deepEqual(
1268 model.getFullState(),
1269 baseFullState,
1270 'Initial state: no conflicts because no selections.'
1271 );
1272
1273 // Select a filter that has a conflict with an entire group
1274 model.toggleFiltersSelected( {
1275 group1__filter1: true // conflicts: entire of group 2 ( filter4, filter5, filter6)
1276 } );
1277
1278 model.reassessFilterInteractions( model.getItemByName( 'group1__filter1' ) );
1279
1280 assert.deepEqual(
1281 model.getFullState(),
1282 $.extend( true, {}, baseFullState, {
1283 group1__filter1: { selected: true },
1284 group2__filter4: { conflicted: true },
1285 group2__filter5: { conflicted: true },
1286 group2__filter6: { conflicted: true }
1287 } ),
1288 'Selecting a filter that conflicts with a group sets all the conflicted group items as "conflicted".'
1289 );
1290
1291 // Select one of the conflicts (both filters are now conflicted and selected)
1292 model.toggleFiltersSelected( {
1293 group2__filter4: true // conflicts: filter 1
1294 } );
1295 model.reassessFilterInteractions( model.getItemByName( 'group2__filter4' ) );
1296
1297 assert.deepEqual(
1298 model.getFullState(),
1299 $.extend( true, {}, baseFullState, {
1300 group1__filter1: { selected: true, conflicted: true },
1301 group2__filter4: { selected: true, conflicted: true },
1302 group2__filter5: { conflicted: true },
1303 group2__filter6: { conflicted: true }
1304 } ),
1305 'Selecting a conflicting filter inside a group, sets both sides to conflicted and selected.'
1306 );
1307
1308 // Reset
1309 model = new mw.rcfilters.dm.FiltersViewModel();
1310 model.initializeFilters( definition );
1311
1312 // Select a filter that has a conflict with a specific filter
1313 model.toggleFiltersSelected( {
1314 group1__filter2: true // conflicts: filter6
1315 } );
1316 model.reassessFilterInteractions( model.getItemByName( 'group1__filter2' ) );
1317
1318 assert.deepEqual(
1319 model.getFullState(),
1320 $.extend( true, {}, baseFullState, {
1321 group1__filter2: { selected: true },
1322 group2__filter6: { conflicted: true }
1323 } ),
1324 'Selecting a filter that conflicts with another filter sets the other as "conflicted".'
1325 );
1326
1327 // Select the conflicting filter
1328 model.toggleFiltersSelected( {
1329 group2__filter6: true // conflicts: filter2
1330 } );
1331
1332 model.reassessFilterInteractions( model.getItemByName( 'group2__filter6' ) );
1333
1334 assert.deepEqual(
1335 model.getFullState(),
1336 $.extend( true, {}, baseFullState, {
1337 group1__filter2: { selected: true, conflicted: true },
1338 group2__filter6: { selected: true, conflicted: true },
1339 // This is added to the conflicts because filter6 is part of group2,
1340 // who is in conflict with filter1; note that filter2 also conflicts
1341 // with filter6 which means that filter1 conflicts with filter6 (because it's in group2)
1342 // and also because its **own sibling** (filter2) is **also** in conflict with the
1343 // selected items in group2 (filter6)
1344 group1__filter1: { conflicted: true }
1345 } ),
1346 'Selecting a conflicting filter with an individual filter, sets both sides to conflicted and selected.'
1347 );
1348
1349 // Now choose a non-conflicting filter from the group
1350 model.toggleFiltersSelected( {
1351 group2__filter5: true
1352 } );
1353
1354 model.reassessFilterInteractions( model.getItemByName( 'group2__filter5' ) );
1355
1356 assert.deepEqual(
1357 model.getFullState(),
1358 $.extend( true, {}, baseFullState, {
1359 group1__filter2: { selected: true },
1360 group2__filter6: { selected: true },
1361 group2__filter5: { selected: true }
1362 // Filter6 and filter1 are no longer in conflict because
1363 // filter5, while it is in conflict with filter1, it is
1364 // not in conflict with filter2 - and since filter2 is
1365 // selected, it removes the conflict bidirectionally
1366 } ),
1367 'Selecting a non-conflicting filter within the group of a conflicting filter removes the conflicts.'
1368 );
1369
1370 // Followup on the previous test, unselect filter2 so filter1
1371 // is now the only one selected in its own group, and since
1372 // it is in conflict with the entire of group2, it means
1373 // filter1 is once again conflicted
1374 model.toggleFiltersSelected( {
1375 group1__filter2: false
1376 } );
1377
1378 model.reassessFilterInteractions( model.getItemByName( 'group1__filter2' ) );
1379
1380 assert.deepEqual(
1381 model.getFullState(),
1382 $.extend( true, {}, baseFullState, {
1383 group1__filter1: { conflicted: true },
1384 group2__filter6: { selected: true },
1385 group2__filter5: { selected: true }
1386 } ),
1387 'Unselecting an item that did not conflict returns the conflict state.'
1388 );
1389
1390 // Followup #2: Now actually select filter1, and make everything conflicted
1391 model.toggleFiltersSelected( {
1392 group1__filter1: true
1393 } );
1394
1395 model.reassessFilterInteractions( model.getItemByName( 'group1__filter1' ) );
1396
1397 assert.deepEqual(
1398 model.getFullState(),
1399 $.extend( true, {}, baseFullState, {
1400 group1__filter1: { selected: true, conflicted: true },
1401 group2__filter6: { selected: true, conflicted: true },
1402 group2__filter5: { selected: true, conflicted: true },
1403 group2__filter4: { conflicted: true } // Not selected but conflicted because it's in group2
1404 } ),
1405 'Selecting an item that conflicts with a whole group makes all selections in that group conflicted.'
1406 );
1407
1408 /* Simple case */
1409 // Reset
1410 model = new mw.rcfilters.dm.FiltersViewModel();
1411 model.initializeFilters( definition );
1412
1413 // Select a filter that has a conflict with a specific filter
1414 model.toggleFiltersSelected( {
1415 group1__filter2: true // conflicts: filter6
1416 } );
1417
1418 model.reassessFilterInteractions( model.getItemByName( 'group1__filter2' ) );
1419
1420 assert.deepEqual(
1421 model.getFullState(),
1422 $.extend( true, {}, baseFullState, {
1423 group1__filter2: { selected: true },
1424 group2__filter6: { conflicted: true }
1425 } ),
1426 'Simple case: Selecting a filter that conflicts with another filter sets the other as "conflicted".'
1427 );
1428
1429 model.toggleFiltersSelected( {
1430 group1__filter3: true // conflicts: filter6
1431 } );
1432
1433 model.reassessFilterInteractions( model.getItemByName( 'group1__filter3' ) );
1434
1435 assert.deepEqual(
1436 model.getFullState(),
1437 $.extend( true, {}, baseFullState, {
1438 group1__filter2: { selected: true },
1439 group1__filter3: { selected: true }
1440 } ),
1441 'Simple case: Selecting a filter that is not in conflict removes the conflict.'
1442 );
1443
1444 } );
1445
1446 QUnit.test( 'Filter highlights', function ( assert ) {
1447 var definition = [ {
1448 name: 'group1',
1449 title: 'Group 1',
1450 type: 'string_options',
1451 filters: [
1452 { name: 'filter1', cssClass: 'class1', label: '1', description: '1' },
1453 { name: 'filter2', cssClass: 'class2', label: '2', description: '2' },
1454 { name: 'filter3', cssClass: 'class3', label: '3', description: '3' },
1455 { name: 'filter4', cssClass: 'class4', label: '4', description: '4' },
1456 { name: 'filter5', cssClass: 'class5', label: '5', description: '5' },
1457 { name: 'filter6', label: '6', description: '6' }
1458 ]
1459 } ],
1460 model = new mw.rcfilters.dm.FiltersViewModel();
1461
1462 model.initializeFilters( definition );
1463
1464 assert.ok(
1465 !model.isHighlightEnabled(),
1466 'Initially, highlight is disabled.'
1467 );
1468
1469 model.toggleHighlight( true );
1470 assert.ok(
1471 model.isHighlightEnabled(),
1472 'Highlight is enabled on toggle.'
1473 );
1474
1475 model.setHighlightColor( 'group1__filter1', 'color1' );
1476 model.setHighlightColor( 'group1__filter2', 'color2' );
1477
1478 assert.deepEqual(
1479 model.getHighlightedItems().map( function ( item ) {
1480 return item.getName();
1481 } ),
1482 [
1483 'group1__filter1',
1484 'group1__filter2'
1485 ],
1486 'Highlighted items are highlighted.'
1487 );
1488
1489 assert.equal(
1490 model.getItemByName( 'group1__filter1' ).getHighlightColor(),
1491 'color1',
1492 'Item highlight color is set.'
1493 );
1494
1495 model.setHighlightColor( 'group1__filter1', 'color1changed' );
1496 assert.equal(
1497 model.getItemByName( 'group1__filter1' ).getHighlightColor(),
1498 'color1changed',
1499 'Item highlight color is changed on setHighlightColor.'
1500 );
1501
1502 model.clearHighlightColor( 'group1__filter1' );
1503 assert.deepEqual(
1504 model.getHighlightedItems().map( function ( item ) {
1505 return item.getName();
1506 } ),
1507 [
1508 'group1__filter2'
1509 ],
1510 'Clear highlight from an item results in the item no longer being highlighted.'
1511 );
1512
1513 // Reset
1514 model = new mw.rcfilters.dm.FiltersViewModel();
1515 model.initializeFilters( definition );
1516
1517 model.setHighlightColor( 'group1__filter1', 'color1' );
1518 model.setHighlightColor( 'group1__filter2', 'color2' );
1519 model.setHighlightColor( 'group1__filter3', 'color3' );
1520
1521 assert.deepEqual(
1522 model.getHighlightedItems().map( function ( item ) {
1523 return item.getName();
1524 } ),
1525 [
1526 'group1__filter1',
1527 'group1__filter2',
1528 'group1__filter3'
1529 ],
1530 'Even if highlights are not enabled, the items remember their highlight state'
1531 // NOTE: When actually displaying the highlights, the UI checks whether
1532 // highlighting is generally active and then goes over the highlighted
1533 // items. The item models, however, and the view model in general, still
1534 // retains the knowledge about which filters have different colors, so we
1535 // can seamlessly return to the colors the user previously chose if they
1536 // reapply highlights.
1537 );
1538
1539 // Reset
1540 model = new mw.rcfilters.dm.FiltersViewModel();
1541 model.initializeFilters( definition );
1542
1543 model.setHighlightColor( 'group1__filter1', 'color1' );
1544 model.setHighlightColor( 'group1__filter6', 'color6' );
1545
1546 assert.deepEqual(
1547 model.getHighlightedItems().map( function ( item ) {
1548 return item.getName();
1549 } ),
1550 [
1551 'group1__filter1'
1552 ],
1553 'Items without a specified class identifier are not highlighted.'
1554 );
1555 } );
1556 }( mediaWiki, jQuery ) );