1 /* eslint-disable camelcase */
3 QUnit
.module( 'mediawiki.rcfilters - FiltersViewModel', QUnit
.newMwEnvironment( {
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'
16 QUnit
.test( 'Setting up filters', function ( assert
) {
20 type
: 'send_unselected_if_any',
24 label
: 'Group 1: Filter 1',
25 description
: 'Description of Filter 1 in Group 1'
29 label
: 'Group 1: Filter 2',
30 description
: 'Description of Filter 2 in Group 1'
36 type
: 'send_unselected_if_any',
40 label
: 'Group 2: Filter 1',
41 description
: 'Description of Filter 1 in Group 2'
45 label
: 'Group 2: Filter 2',
46 description
: 'Description of Filter 2 in Group 2'
52 type
: 'string_options',
56 label
: 'Group 3: Filter 1',
57 description
: 'Description of Filter 1 in Group 3'
61 label
: 'Group 3: Filter 2',
62 description
: 'Description of Filter 2 in Group 3'
66 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
68 model
.initializeFilters( definition
);
71 model
.getItemByName( 'group1__filter1' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
72 model
.getItemByName( 'group1__filter2' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
73 model
.getItemByName( 'group2__filter1' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
74 model
.getItemByName( 'group2__filter2' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
75 model
.getItemByName( 'group3__filter1' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
76 model
.getItemByName( 'group3__filter2' ) instanceof mw
.rcfilters
.dm
.FilterItem
,
77 'Filters instantiated and stored correctly'
81 model
.getSelectedState(),
83 group1__filter1
: false,
84 group1__filter2
: false,
85 group2__filter1
: false,
86 group2__filter2
: false,
87 group3__filter1
: false,
88 group3__filter2
: false
90 'Initial state of filters'
93 model
.toggleFiltersSelected( {
94 group1__filter1
: true,
95 group2__filter2
: true,
99 model
.getSelectedState(),
101 group1__filter1
: true,
102 group1__filter2
: false,
103 group2__filter1
: false,
104 group2__filter2
: true,
105 group3__filter1
: true,
106 group3__filter2
: false
108 'Updating filter states correctly'
112 QUnit
.test( 'Default filters', function ( assert
) {
116 type
: 'send_unselected_if_any',
120 label
: 'Show filter 1',
121 description
: 'Description of Filter 1 in Group 1',
126 label
: 'Show filter 2',
127 description
: 'Description of Filter 2 in Group 1'
131 label
: 'Show filter 3',
132 description
: 'Description of Filter 3 in Group 1',
139 type
: 'send_unselected_if_any',
143 label
: 'Show filter 4',
144 description
: 'Description of Filter 1 in Group 2'
148 label
: 'Show filter 5',
149 description
: 'Description of Filter 2 in Group 2',
154 label
: 'Show filter 6',
155 description
: 'Description of Filter 3 in Group 2'
162 type
: 'string_options',
168 label
: 'Group 3: Filter 1',
169 description
: 'Description of Filter 1 in Group 3'
173 label
: 'Group 3: Filter 2',
174 description
: 'Description of Filter 2 in Group 3'
178 label
: 'Group 3: Filter 3',
179 description
: 'Description of Filter 3 in Group 3'
183 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
185 model
.initializeFilters( definition
);
187 // Empty query = only default values
189 model
.getDefaultParams(),
191 group1__hidefilter1_color
: null,
192 group1__hidefilter2_color
: null,
193 group1__hidefilter3_color
: null,
194 group2__hidefilter4_color
: null,
195 group2__hidefilter5_color
: null,
196 group2__hidefilter6_color
: null,
197 group3__filter7_color
: null,
198 group3__filter8_color
: null,
199 group3__filter9_color
: null,
209 'Default parameters are stored properly per filter and group'
213 QUnit
.test( 'Finding matching filters', function ( assert
) {
217 title
: 'Group 1 title',
218 type
: 'send_unselected_if_any',
222 label
: 'group1filter1-label',
223 description
: 'group1filter1-desc'
227 label
: 'group1filter2-label',
228 description
: 'group1filter2-desc'
233 title
: 'Group 2 title',
234 type
: 'send_unselected_if_any',
238 label
: 'group2filter1-label',
239 description
: 'group2filter1-desc'
243 label
: 'group2filter2-label',
244 description
: 'group2filter2-desc'
252 group1
: [ 'group1__filter1', 'group1__filter2' ],
253 group2
: [ 'group2__filter1' ]
255 reason
: 'Finds filters starting with the query string'
258 query
: 'filter 2 in group',
260 group1
: [ 'group1__filter2' ],
261 group2
: [ 'group2__filter2' ]
263 reason
: 'Finds filters containing the query string in their description'
268 group1
: [ 'group1__filter1', 'group1__filter2' ],
269 group2
: [ 'group2__filter1', 'group2__filter2' ]
271 reason
: 'Finds filters containing the query string in their group title'
274 model
= new mw
.rcfilters
.dm
.FiltersViewModel(),
275 extractNames = function ( matches
) {
277 Object
.keys( matches
).forEach( function ( groupName
) {
278 result
[ groupName
] = matches
[ groupName
].map( function ( item
) {
279 return item
.getName();
285 model
.initializeFilters( definition
);
287 testCases
.forEach( function ( testCase
) {
288 matches
= model
.findMatches( testCase
.query
);
290 extractNames( matches
),
291 testCase
.expectedMatches
,
296 matches
= model
.findMatches( 'foo' );
298 $.isEmptyObject( matches
),
299 'findMatches returns an empty object when no results found'
303 QUnit
.test( 'getParametersFromFilters', function ( assert
) {
307 type
: 'send_unselected_if_any',
311 label
: 'Group 1: Filter 1',
312 description
: 'Description of Filter 1 in Group 1'
316 label
: 'Group 1: Filter 2',
317 description
: 'Description of Filter 2 in Group 1'
321 label
: 'Group 1: Filter 3',
322 description
: 'Description of Filter 3 in Group 1'
328 type
: 'send_unselected_if_any',
332 label
: 'Group 2: Filter 1',
333 description
: 'Description of Filter 1 in Group 2'
337 label
: 'Group 2: Filter 2',
338 description
: 'Description of Filter 2 in Group 2'
342 label
: 'Group 2: Filter 3',
343 description
: 'Description of Filter 3 in Group 2'
349 type
: 'string_options',
354 label
: 'Group 3: Filter 1',
355 description
: 'Description of Filter 1 in Group 3'
359 label
: 'Group 3: Filter 2',
360 description
: 'Description of Filter 2 in Group 3'
364 label
: 'Group 3: Filter 3',
365 description
: 'Description of Filter 3 in Group 3'
369 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
371 model
.initializeFilters( definition
);
373 // Starting with all filters unselected
375 model
.getParametersFromFilters(),
385 'Unselected filters return all parameters falsey or \'\'.'
389 model
.toggleFiltersSelected( {
390 group1__hidefilter1
: true,
391 group1__hidefilter2
: false,
392 group1__hidefilter3
: false,
393 group2__hidefilter4
: false,
394 group2__hidefilter5
: false,
395 group2__hidefilter6
: false
397 // Only one filter in one group
399 model
.getParametersFromFilters(),
401 // Group 1 (one selected, the others are true)
405 // Group 2 (nothing is selected, all false)
411 'One filter in one "send_unselected_if_any" group returns the other parameters truthy.'
415 model
.toggleFiltersSelected( {
416 group1__hidefilter1
: true,
417 group1__hidefilter2
: true,
418 group1__hidefilter3
: false,
419 group2__hidefilter4
: false,
420 group2__hidefilter5
: false,
421 group2__hidefilter6
: false
423 // Two selected filters in one group
425 model
.getParametersFromFilters(),
427 // Group 1 (two selected, the others are true)
431 // Group 2 (nothing is selected, all false)
437 'Two filters in one "send_unselected_if_any" group returns the other parameters truthy.'
441 model
.toggleFiltersSelected( {
442 group1__hidefilter1
: true,
443 group1__hidefilter2
: true,
444 group1__hidefilter3
: true,
445 group2__hidefilter4
: false,
446 group2__hidefilter5
: false,
447 group2__hidefilter6
: false
449 // All filters of the group are selected == this is the same as not selecting any
451 model
.getParametersFromFilters(),
453 // Group 1 (all selected, all false)
457 // Group 2 (nothing is selected, all false)
463 'All filters selected in one "send_unselected_if_any" group returns all parameters falsy.'
466 // Select 1 filter from string_options
467 model
.toggleFiltersSelected( {
468 group3__filter7
: true,
469 group3__filter8
: false,
470 group3__filter9
: false
472 // All filters of the group are selected == this is the same as not selecting any
474 model
.getParametersFromFilters(),
476 // Group 1 (all selected, all)
480 // Group 2 (nothing is selected, all false)
486 'One filter selected in "string_option" group returns that filter in the value.'
489 // Select 2 filters from string_options
490 model
.toggleFiltersSelected( {
491 group3__filter7
: true,
492 group3__filter8
: true,
493 group3__filter9
: false
495 // All filters of the group are selected == this is the same as not selecting any
497 model
.getParametersFromFilters(),
499 // Group 1 (all selected, all)
503 // Group 2 (nothing is selected, all false)
507 group3
: 'filter7,filter8'
509 'Two filters selected in "string_option" group returns those filters in the value.'
512 // Select 3 filters from string_options
513 model
.toggleFiltersSelected( {
514 group3__filter7
: true,
515 group3__filter8
: true,
516 group3__filter9
: true
518 // All filters of the group are selected == this is the same as not selecting any
520 model
.getParametersFromFilters(),
522 // Group 1 (all selected, all)
526 // Group 2 (nothing is selected, all false)
532 'All filters selected in "string_option" group returns \'all\'.'
537 QUnit
.test( 'getParametersFromFilters (custom object)', function ( assert
) {
539 model
= new mw
.rcfilters
.dm
.FiltersViewModel(),
543 type
: 'send_unselected_if_any',
545 { name
: 'hidefilter1', label
: 'Hide filter 1', description
: '' },
546 { name
: 'hidefilter2', label
: 'Hide filter 2', description
: '' },
547 { name
: 'hidefilter3', label
: 'Hide filter 3', description
: '' }
552 type
: 'send_unselected_if_any',
554 { name
: 'hidefilter4', label
: 'Hide filter 4', description
: '' },
555 { name
: 'hidefilter5', label
: 'Hide filter 5', description
: '' },
556 { name
: 'hidefilter6', label
: 'Hide filter 6', description
: '' }
561 type
: 'string_options',
564 { name
: 'filter7', label
: 'Hide filter 7', description
: '' },
565 { name
: 'filter8', label
: 'Hide filter 8', description
: '' },
566 { name
: 'filter9', label
: 'Hide filter 9', description
: '' }
571 // This is mocking the cases above, both
572 // - 'Two filters in one "send_unselected_if_any" group returns the other parameters truthy.'
573 // - 'Two filters selected in "string_option" group returns those filters in the value.'
575 group1__hidefilter1
: true,
576 group1__hidefilter2
: true,
577 group1__hidefilter3
: false,
578 group2__hidefilter4
: false,
579 group2__hidefilter5
: false,
580 group2__hidefilter6
: false,
581 group3__filter7
: true,
582 group3__filter8
: true,
583 group3__filter9
: false
586 // Group 1 (two selected, the others are true)
590 // Group 2 (nothing is selected, all false)
594 group3
: 'filter7,filter8'
596 msg
: 'Given an explicit (complete) filter state object, the result is the same as if the object given represented the model state.'
599 // This is mocking case above
600 // - 'One filter in one "send_unselected_if_any" group returns the other parameters truthy.'
602 group1__hidefilter1
: 1
605 // Group 1 (one selected, the others are true)
609 // Group 2 (nothing is selected, all false)
615 msg
: 'Given an explicit (incomplete) filter state object, the result is the same as if the object give represented the model state.'
628 msg
: 'Given an explicit empty object, the result is all filters set to their falsey unselected value.'
632 model
.initializeFilters( definition
);
633 // Store original state
634 originalState
= model
.getSelectedState();
637 cases
.forEach( function ( test
) {
639 model
.getParametersFromFilters( test
.input
),
645 // After doing the above tests, make sure the actual state
646 // of the filter stayed the same
648 model
.getSelectedState(),
650 'Running the method with external definition to parse does not actually change the state of the model'
654 QUnit
.test( 'getFiltersFromParameters', function ( assert
) {
658 type
: 'send_unselected_if_any',
662 label
: 'Show filter 1',
663 description
: 'Description of Filter 1 in Group 1',
668 label
: 'Show filter 2',
669 description
: 'Description of Filter 2 in Group 1'
673 label
: 'Show filter 3',
674 description
: 'Description of Filter 3 in Group 1',
681 type
: 'send_unselected_if_any',
685 label
: 'Show filter 4',
686 description
: 'Description of Filter 1 in Group 2'
690 label
: 'Show filter 5',
691 description
: 'Description of Filter 2 in Group 2',
696 label
: 'Show filter 6',
697 description
: 'Description of Filter 3 in Group 2'
704 type
: 'string_options',
710 label
: 'Group 3: Filter 1',
711 description
: 'Description of Filter 1 in Group 3'
715 label
: 'Group 3: Filter 2',
716 description
: 'Description of Filter 2 in Group 3'
720 label
: 'Group 3: Filter 3',
721 description
: 'Description of Filter 3 in Group 3'
725 baseFilterRepresentation
= {
726 group1__hidefilter1
: false,
727 group1__hidefilter2
: false,
728 group1__hidefilter3
: false,
729 group2__hidefilter4
: false,
730 group2__hidefilter5
: false,
731 group2__hidefilter6
: false,
732 group3__filter7
: false,
733 group3__filter8
: false,
734 group3__filter9
: false
736 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
738 model
.initializeFilters( definition
);
740 // Empty query = only default values
742 model
.getFiltersFromParameters( {} ),
743 baseFilterRepresentation
,
744 'Empty parameter query results in an object representing all filters set to false'
748 model
.getFiltersFromParameters( {
751 $.extend( {}, baseFilterRepresentation
, {
752 group1__hidefilter1
: true, // The text is "show filter 1"
753 group1__hidefilter2
: false, // The text is "show filter 2"
754 group1__hidefilter3
: true // The text is "show filter 3"
756 'One truthy parameter in a group whose other parameters are true by default makes the rest of the filters in the group false (unchecked)'
760 model
.getFiltersFromParameters( {
765 $.extend( {}, baseFilterRepresentation
, {
766 group1__hidefilter1
: false, // The text is "show filter 1"
767 group1__hidefilter2
: false, // The text is "show filter 2"
768 group1__hidefilter3
: false // The text is "show filter 3"
770 'All paremeters in the same \'send_unselected_if_any\' group false is equivalent to none are truthy (checked) in the interface'
773 // The ones above don't update the model, so we have a clean state.
774 // getFiltersFromParameters is stateless; any change is unaffected by the current state
775 // This test is demonstrating wrong usage of the method;
776 // We should be aware that getFiltersFromParameters is stateless,
777 // so each call gives us a filter state that only reflects the query given.
778 // This means that the two calls to toggleFiltersSelected() below collide.
779 // The result of the first is overridden by the result of the second,
780 // since both get a full state object from getFiltersFromParameters that **only** relates
781 // to the input it receives.
782 model
.toggleFiltersSelected(
783 model
.getFiltersFromParameters( {
788 model
.toggleFiltersSelected(
789 model
.getFiltersFromParameters( {
794 // The result here is ignoring the first toggleFiltersSelected call
796 model
.getSelectedState(),
797 $.extend( {}, baseFilterRepresentation
, {
798 group2__hidefilter4
: true,
799 group2__hidefilter5
: true,
800 group2__hidefilter6
: false
802 'getFiltersFromParameters does not care about previous or existing state.'
806 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
807 model
.initializeFilters( definition
);
809 model
.toggleFiltersSelected(
810 model
.getFiltersFromParameters( {
815 model
.getSelectedState(),
816 $.extend( {}, baseFilterRepresentation
, {
817 group3__filter7
: true,
818 group3__filter8
: false,
819 group3__filter9
: false
821 'A \'string_options\' parameter containing 1 value, results in the corresponding filter as checked'
824 model
.toggleFiltersSelected(
825 model
.getFiltersFromParameters( {
826 group3
: 'filter7,filter8'
830 model
.getSelectedState(),
831 $.extend( {}, baseFilterRepresentation
, {
832 group3__filter7
: true,
833 group3__filter8
: true,
834 group3__filter9
: false
836 'A \'string_options\' parameter containing 2 values, results in both corresponding filters as checked'
839 model
.toggleFiltersSelected(
840 model
.getFiltersFromParameters( {
841 group3
: 'filter7,filter8,filter9'
845 model
.getSelectedState(),
846 $.extend( {}, baseFilterRepresentation
, {
847 group3__filter7
: true,
848 group3__filter8
: true,
849 group3__filter9
: true
851 'A \'string_options\' parameter containing all values, results in all filters of the group as checked.'
854 model
.toggleFiltersSelected(
855 model
.getFiltersFromParameters( {
856 group3
: 'filter7,all,filter9'
860 model
.getSelectedState(),
861 $.extend( {}, baseFilterRepresentation
, {
862 group3__filter7
: true,
863 group3__filter8
: true,
864 group3__filter9
: true
866 'A \'string_options\' parameter containing the value \'all\', results in all filters of the group as checked.'
869 model
.toggleFiltersSelected(
870 model
.getFiltersFromParameters( {
871 group3
: 'filter7,foo,filter9'
875 model
.getSelectedState(),
876 $.extend( {}, baseFilterRepresentation
, {
877 group3__filter7
: true,
878 group3__filter8
: false,
879 group3__filter9
: true
881 'A \'string_options\' parameter containing an invalid value, results in the invalid value ignored and the valid corresponding filters checked.'
885 QUnit
.test( 'sanitizeStringOptionGroup', function ( assert
) {
889 type
: 'string_options',
893 label
: 'Show filter 1',
894 description
: 'Description of Filter 1 in Group 1'
898 label
: 'Show filter 2',
899 description
: 'Description of Filter 2 in Group 1'
903 label
: 'Show filter 3',
904 description
: 'Description of Filter 3 in Group 1'
908 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
910 model
.initializeFilters( definition
);
913 model
.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'filter1', 'filter2' ] ),
914 [ 'filter1', 'filter2' ],
915 'Remove duplicate values'
919 model
.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'foo', 'filter2' ] ),
920 [ 'filter1', 'filter2' ],
921 'Remove invalid values'
925 model
.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'all', 'filter2' ] ),
927 'If any value is "all", the only value is "all".'
931 QUnit
.test( 'Filter interaction: subsets', function ( assert
) {
935 type
: 'string_options',
939 label
: 'Show filter 1',
940 description
: 'Description of Filter 1 in Group 1',
954 label
: 'Show filter 2',
955 description
: 'Description of Filter 2 in Group 1',
965 label
: 'Show filter 3',
966 description
: 'Description of Filter 3 in Group 1'
971 group1__filter1
: { selected
: false, conflicted
: false, included
: false },
972 group1__filter2
: { selected
: false, conflicted
: false, included
: false },
973 group1__filter3
: { selected
: false, conflicted
: false, included
: false }
975 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
977 model
.initializeFilters( definition
);
978 // Select a filter that has subset with another filter
979 model
.toggleFiltersSelected( {
980 group1__filter1
: true
983 model
.reassessFilterInteractions( model
.getItemByName( 'group1__filter1' ) );
985 model
.getFullState(),
986 $.extend( true, {}, baseFullState
, {
987 group1__filter1
: { selected
: true },
988 group1__filter2
: { included
: true },
989 group1__filter3
: { included
: true }
991 'Filters with subsets are represented in the model.'
994 // Select another filter that has a subset with the same previous filter
995 model
.toggleFiltersSelected( {
996 group1__filter2
: true
998 model
.reassessFilterInteractions( model
.getItemByName( 'filter2' ) );
1000 model
.getFullState(),
1001 $.extend( true, {}, baseFullState
, {
1002 group1__filter1
: { selected
: true },
1003 group1__filter2
: { selected
: true, included
: true },
1004 group1__filter3
: { included
: true }
1006 'Filters that have multiple subsets are represented.'
1009 // Remove one filter (but leave the other) that affects filter3
1010 model
.toggleFiltersSelected( {
1011 group1__filter1
: false
1013 model
.reassessFilterInteractions( model
.getItemByName( 'group1__filter1' ) );
1015 model
.getFullState(),
1016 $.extend( true, {}, baseFullState
, {
1017 group1__filter2
: { selected
: true, included
: false },
1018 group1__filter3
: { included
: true }
1020 'Removing a filter only un-includes its subset if there is no other filter affecting.'
1023 model
.toggleFiltersSelected( {
1024 group1__filter2
: false
1026 model
.reassessFilterInteractions( model
.getItemByName( 'group1__filter2' ) );
1028 model
.getFullState(),
1030 'Removing all supersets also un-includes the subsets.'
1034 QUnit
.test( 'Filter interaction: full coverage', function ( assert
) {
1035 var definition
= [ {
1038 type
: 'string_options',
1039 fullCoverage
: false,
1041 { name
: 'filter1', label
: '1', description
: '1' },
1042 { name
: 'filter2', label
: '2', description
: '2' },
1043 { name
: 'filter3', label
: '3', description
: '3' }
1048 type
: 'send_unselected_if_any',
1051 { name
: 'filter4', label
: '4', description
: '4' },
1052 { name
: 'filter5', label
: '5', description
: '5' },
1053 { name
: 'filter6', label
: '6', description
: '6' }
1056 model
= new mw
.rcfilters
.dm
.FiltersViewModel(),
1057 isCapsuleItemMuted = function ( filterName
) {
1058 var itemModel
= model
.getItemByName( filterName
),
1059 groupModel
= itemModel
.getGroupModel();
1061 // This is the logic inside the capsule widget
1063 // The capsule item widget only appears if the item is selected
1064 itemModel
.isSelected() &&
1065 // Muted state is only valid if group is full coverage and all items are selected
1066 groupModel
.isFullCoverage() && groupModel
.areAllSelected()
1069 getCurrentItemsMutedState = function () {
1071 group1__filter1
: isCapsuleItemMuted( 'group1__filter1' ),
1072 group1__filter2
: isCapsuleItemMuted( 'group1__filter2' ),
1073 group1__filter3
: isCapsuleItemMuted( 'group1__filter3' ),
1074 group2__filter4
: isCapsuleItemMuted( 'group2__filter4' ),
1075 group2__filter5
: isCapsuleItemMuted( 'group2__filter5' ),
1076 group2__filter6
: isCapsuleItemMuted( 'group2__filter6' )
1080 group1__filter1
: false,
1081 group1__filter2
: false,
1082 group1__filter3
: false,
1083 group2__filter4
: false,
1084 group2__filter5
: false,
1085 group2__filter6
: false
1088 model
.initializeFilters( definition
);
1090 // Starting state, no selection, all items are non-muted
1092 getCurrentItemsMutedState(),
1094 'No selection - all items are non-muted'
1097 // Select most (but not all) items in each group
1098 model
.toggleFiltersSelected( {
1099 group1__filter1
: true,
1100 group1__filter2
: true,
1101 group2__filter4
: true,
1102 group2__filter5
: true
1105 // Both groups have multiple (but not all) items selected, all items are non-muted
1107 getCurrentItemsMutedState(),
1109 'Not all items in the group selected - all items are non-muted'
1112 // Select all items in 'fullCoverage' group (group2)
1113 model
.toggleFiltersSelected( {
1114 group2__filter6
: true
1117 // Group2 (full coverage) has all items selected, all its items are muted
1119 getCurrentItemsMutedState(),
1120 $.extend( {}, baseMuteState
, {
1121 group2__filter4
: true,
1122 group2__filter5
: true,
1123 group2__filter6
: true
1125 'All items in \'full coverage\' group are selected - all items in the group are muted'
1128 // Select all items in non 'fullCoverage' group (group1)
1129 model
.toggleFiltersSelected( {
1130 group1__filter3
: true
1133 // Group1 (full coverage) has all items selected, no items in it are muted (non full coverage)
1135 getCurrentItemsMutedState(),
1136 $.extend( {}, baseMuteState
, {
1137 group2__filter4
: true,
1138 group2__filter5
: true,
1139 group2__filter6
: true
1141 'All items in a non \'full coverage\' group are selected - none of the items in the group are muted'
1144 // Uncheck an item from each group
1145 model
.toggleFiltersSelected( {
1146 group1__filter3
: false,
1147 group2__filter5
: false
1150 getCurrentItemsMutedState(),
1152 'Not all items in the group are checked - all items are non-muted regardless of group coverage'
1156 QUnit
.test( 'Filter interaction: conflicts', function ( assert
) {
1157 var definition
= [ {
1160 type
: 'string_options',
1166 conflicts
: [ { group
: 'group2' } ]
1172 conflicts
: [ { group
: 'group2', filter
: 'filter6' } ]
1183 type
: 'send_unselected_if_any',
1184 conflicts
: [ { group
: 'group1', filter
: 'filter1' } ],
1200 conflicts
: [ { group
: 'group1', filter
: 'filter2' } ]
1205 group1__filter1
: { selected
: false, conflicted
: false, included
: false },
1206 group1__filter2
: { selected
: false, conflicted
: false, included
: false },
1207 group1__filter3
: { selected
: false, conflicted
: false, included
: false },
1208 group2__filter4
: { selected
: false, conflicted
: false, included
: false },
1209 group2__filter5
: { selected
: false, conflicted
: false, included
: false },
1210 group2__filter6
: { selected
: false, conflicted
: false, included
: false }
1212 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
1214 model
.initializeFilters( definition
);
1217 model
.getFullState(),
1219 'Initial state: no conflicts because no selections.'
1222 // Select a filter that has a conflict with an entire group
1223 model
.toggleFiltersSelected( {
1224 group1__filter1
: true // conflicts: entire of group 2 ( filter4, filter5, filter6)
1227 model
.reassessFilterInteractions( model
.getItemByName( 'group1__filter1' ) );
1230 model
.getFullState(),
1231 $.extend( true, {}, baseFullState
, {
1232 group1__filter1
: { selected
: true },
1233 group2__filter4
: { conflicted
: true },
1234 group2__filter5
: { conflicted
: true },
1235 group2__filter6
: { conflicted
: true }
1237 'Selecting a filter that conflicts with a group sets all the conflicted group items as "conflicted".'
1240 // Select one of the conflicts (both filters are now conflicted and selected)
1241 model
.toggleFiltersSelected( {
1242 group2__filter4
: true // conflicts: filter 1
1244 model
.reassessFilterInteractions( model
.getItemByName( 'group2__filter4' ) );
1247 model
.getFullState(),
1248 $.extend( true, {}, baseFullState
, {
1249 group1__filter1
: { selected
: true, conflicted
: true },
1250 group2__filter4
: { selected
: true, conflicted
: true },
1251 group2__filter5
: { conflicted
: true },
1252 group2__filter6
: { conflicted
: true }
1254 'Selecting a conflicting filter inside a group, sets both sides to conflicted and selected.'
1258 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
1259 model
.initializeFilters( definition
);
1261 // Select a filter that has a conflict with a specific filter
1262 model
.toggleFiltersSelected( {
1263 group1__filter2
: true // conflicts: filter6
1265 model
.reassessFilterInteractions( model
.getItemByName( 'group1__filter2' ) );
1268 model
.getFullState(),
1269 $.extend( true, {}, baseFullState
, {
1270 group1__filter2
: { selected
: true },
1271 group2__filter6
: { conflicted
: true }
1273 'Selecting a filter that conflicts with another filter sets the other as "conflicted".'
1276 // Select the conflicting filter
1277 model
.toggleFiltersSelected( {
1278 group2__filter6
: true // conflicts: filter2
1281 model
.reassessFilterInteractions( model
.getItemByName( 'group2__filter6' ) );
1284 model
.getFullState(),
1285 $.extend( true, {}, baseFullState
, {
1286 group1__filter2
: { selected
: true, conflicted
: true },
1287 group2__filter6
: { selected
: true, conflicted
: true },
1288 // This is added to the conflicts because filter6 is part of group2,
1289 // who is in conflict with filter1; note that filter2 also conflicts
1290 // with filter6 which means that filter1 conflicts with filter6 (because it's in group2)
1291 // and also because its **own sibling** (filter2) is **also** in conflict with the
1292 // selected items in group2 (filter6)
1293 group1__filter1
: { conflicted
: true }
1295 'Selecting a conflicting filter with an individual filter, sets both sides to conflicted and selected.'
1298 // Now choose a non-conflicting filter from the group
1299 model
.toggleFiltersSelected( {
1300 group2__filter5
: true
1303 model
.reassessFilterInteractions( model
.getItemByName( 'group2__filter5' ) );
1306 model
.getFullState(),
1307 $.extend( true, {}, baseFullState
, {
1308 group1__filter2
: { selected
: true },
1309 group2__filter6
: { selected
: true },
1310 group2__filter5
: { selected
: true }
1311 // Filter6 and filter1 are no longer in conflict because
1312 // filter5, while it is in conflict with filter1, it is
1313 // not in conflict with filter2 - and since filter2 is
1314 // selected, it removes the conflict bidirectionally
1316 'Selecting a non-conflicting filter within the group of a conflicting filter removes the conflicts.'
1319 // Followup on the previous test, unselect filter2 so filter1
1320 // is now the only one selected in its own group, and since
1321 // it is in conflict with the entire of group2, it means
1322 // filter1 is once again conflicted
1323 model
.toggleFiltersSelected( {
1324 group1__filter2
: false
1327 model
.reassessFilterInteractions( model
.getItemByName( 'group1__filter2' ) );
1330 model
.getFullState(),
1331 $.extend( true, {}, baseFullState
, {
1332 group1__filter1
: { conflicted
: true },
1333 group2__filter6
: { selected
: true },
1334 group2__filter5
: { selected
: true }
1336 'Unselecting an item that did not conflict returns the conflict state.'
1339 // Followup #2: Now actually select filter1, and make everything conflicted
1340 model
.toggleFiltersSelected( {
1341 group1__filter1
: true
1344 model
.reassessFilterInteractions( model
.getItemByName( 'group1__filter1' ) );
1347 model
.getFullState(),
1348 $.extend( true, {}, baseFullState
, {
1349 group1__filter1
: { selected
: true, conflicted
: true },
1350 group2__filter6
: { selected
: true, conflicted
: true },
1351 group2__filter5
: { selected
: true, conflicted
: true },
1352 group2__filter4
: { conflicted
: true } // Not selected but conflicted because it's in group2
1354 'Selecting an item that conflicts with a whole group makes all selections in that group conflicted.'
1359 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
1360 model
.initializeFilters( definition
);
1362 // Select a filter that has a conflict with a specific filter
1363 model
.toggleFiltersSelected( {
1364 group1__filter2
: true // conflicts: filter6
1367 model
.reassessFilterInteractions( model
.getItemByName( 'group1__filter2' ) );
1370 model
.getFullState(),
1371 $.extend( true, {}, baseFullState
, {
1372 group1__filter2
: { selected
: true },
1373 group2__filter6
: { conflicted
: true }
1375 'Simple case: Selecting a filter that conflicts with another filter sets the other as "conflicted".'
1378 model
.toggleFiltersSelected( {
1379 group1__filter3
: true // conflicts: filter6
1382 model
.reassessFilterInteractions( model
.getItemByName( 'group1__filter3' ) );
1385 model
.getFullState(),
1386 $.extend( true, {}, baseFullState
, {
1387 group1__filter2
: { selected
: true },
1388 group1__filter3
: { selected
: true }
1390 'Simple case: Selecting a filter that is not in conflict removes the conflict.'
1395 QUnit
.test( 'Filter highlights', function ( assert
) {
1396 var definition
= [ {
1399 type
: 'string_options',
1401 { name
: 'filter1', cssClass
: 'class1', label
: '1', description
: '1' },
1402 { name
: 'filter2', cssClass
: 'class2', label
: '2', description
: '2' },
1403 { name
: 'filter3', cssClass
: 'class3', label
: '3', description
: '3' },
1404 { name
: 'filter4', cssClass
: 'class4', label
: '4', description
: '4' },
1405 { name
: 'filter5', cssClass
: 'class5', label
: '5', description
: '5' },
1406 { name
: 'filter6', label
: '6', description
: '6' }
1409 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
1411 model
.initializeFilters( definition
);
1414 !model
.isHighlightEnabled(),
1415 'Initially, highlight is disabled.'
1418 model
.toggleHighlight( true );
1420 model
.isHighlightEnabled(),
1421 'Highlight is enabled on toggle.'
1424 model
.setHighlightColor( 'group1__filter1', 'color1' );
1425 model
.setHighlightColor( 'group1__filter2', 'color2' );
1428 model
.getHighlightedItems().map( function ( item
) {
1429 return item
.getName();
1435 'Highlighted items are highlighted.'
1439 model
.getItemByName( 'group1__filter1' ).getHighlightColor(),
1441 'Item highlight color is set.'
1444 model
.setHighlightColor( 'group1__filter1', 'color1changed' );
1446 model
.getItemByName( 'group1__filter1' ).getHighlightColor(),
1448 'Item highlight color is changed on setHighlightColor.'
1451 model
.clearHighlightColor( 'group1__filter1' );
1453 model
.getHighlightedItems().map( function ( item
) {
1454 return item
.getName();
1459 'Clear highlight from an item results in the item no longer being highlighted.'
1463 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
1464 model
.initializeFilters( definition
);
1466 model
.setHighlightColor( 'group1__filter1', 'color1' );
1467 model
.setHighlightColor( 'group1__filter2', 'color2' );
1468 model
.setHighlightColor( 'group1__filter3', 'color3' );
1471 model
.getHighlightedItems().map( function ( item
) {
1472 return item
.getName();
1479 'Even if highlights are not enabled, the items remember their highlight state'
1480 // NOTE: When actually displaying the highlights, the UI checks whether
1481 // highlighting is generally active and then goes over the highlighted
1482 // items. The item models, however, and the view model in general, still
1483 // retains the knowledge about which filters have different colors, so we
1484 // can seamlessly return to the colors the user previously chose if they
1485 // reapply highlights.
1489 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
1490 model
.initializeFilters( definition
);
1492 model
.setHighlightColor( 'group1__filter1', 'color1' );
1493 model
.setHighlightColor( 'group1__filter6', 'color6' );
1496 model
.getHighlightedItems().map( function ( item
) {
1497 return item
.getName();
1502 'Items without a specified class identifier are not highlighted.'
1505 }( mediaWiki
, jQuery
) );