2 QUnit
.module( 'mediawiki.rcfilters - FiltersViewModel', QUnit
.newMwEnvironment( {
4 'group1filter1-label': 'Group 1: Filter 1',
5 'group1filter1-desc': 'Description of Filter 1 in Group 1',
6 'group1filter2-label': 'Group 1: Filter 2',
7 'group1filter2-desc': 'Description of Filter 2 in Group 1',
8 'group2filter1-label': 'Group 2: Filter 1',
9 'group2filter1-desc': 'Description of Filter 1 in Group 2',
10 'group2filter2-label': 'xGroup 2: Filter 2',
11 'group2filter2-desc': 'Description of Filter 2 in Group 2'
15 QUnit
.test( 'Setting up filters', function ( assert
) {
19 type
: 'send_unselected_if_any',
22 name
: 'group1filter1',
23 label
: 'Group 1: Filter 1',
24 description
: 'Description of Filter 1 in Group 1'
27 name
: 'group1filter2',
28 label
: 'Group 1: Filter 2',
29 description
: 'Description of Filter 2 in Group 1'
35 type
: 'send_unselected_if_any',
38 name
: 'group2filter1',
39 label
: 'Group 2: Filter 1',
40 description
: 'Description of Filter 1 in Group 2'
43 name
: 'group2filter2',
44 label
: 'Group 2: Filter 2',
45 description
: 'Description of Filter 2 in Group 2'
51 type
: 'string_options',
54 name
: 'group3filter1',
55 label
: 'Group 3: Filter 1',
56 description
: 'Description of Filter 1 in Group 3'
59 name
: 'group3filter2',
60 label
: 'Group 3: Filter 2',
61 description
: 'Description of Filter 2 in Group 3'
65 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
67 model
.initializeFilters( definition
);
70 model
.getItemByName( 'group1filter1' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
71 model
.getItemByName( 'group1filter2' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
72 model
.getItemByName( 'group2filter1' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
73 model
.getItemByName( 'group2filter2' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
74 model
.getItemByName( 'group3filter1' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
75 model
.getItemByName( 'group3filter2' ) instanceof mw
.rcfilters
.dm
.FilterItem
,
76 'Filters instantiated and stored correctly'
80 model
.getSelectedState(),
89 'Initial state of filters'
92 model
.toggleFiltersSelected( {
98 model
.getSelectedState(),
101 group1filter2
: false,
102 group2filter1
: false,
107 'Updating filter states correctly'
111 QUnit
.test( 'Finding matching filters', function ( assert
) {
115 title
: 'Group 1 title',
116 type
: 'send_unselected_if_any',
119 name
: 'group1filter1',
120 label
: 'group1filter1-label',
121 description
: 'group1filter1-desc'
124 name
: 'group1filter2',
125 label
: 'group1filter2-label',
126 description
: 'group1filter2-desc'
131 title
: 'Group 2 title',
132 type
: 'send_unselected_if_any',
135 name
: 'group2filter1',
136 label
: 'group2filter1-label',
137 description
: 'group2filter1-desc'
140 name
: 'group2filter2',
141 label
: 'group2filter2-label',
142 description
: 'group2filter2-desc'
150 group1
: [ 'group1filter1', 'group1filter2' ],
151 group2
: [ 'group2filter1' ]
153 reason
: 'Finds filters starting with the query string'
156 query
: 'filter 2 in group',
158 group1
: [ 'group1filter2' ],
159 group2
: [ 'group2filter2' ]
161 reason
: 'Finds filters containing the query string in their description'
166 group1
: [ 'group1filter1', 'group1filter2' ],
167 group2
: [ 'group2filter1', 'group2filter2' ]
169 reason
: 'Finds filters containing the query string in their group title'
172 model
= new mw
.rcfilters
.dm
.FiltersViewModel(),
173 extractNames = function ( matches
) {
175 Object
.keys( matches
).forEach( function ( groupName
) {
176 result
[ groupName
] = matches
[ groupName
].map( function ( item
) {
177 return item
.getName();
183 model
.initializeFilters( definition
);
185 testCases
.forEach( function ( testCase
) {
186 matches
= model
.findMatches( testCase
.query
);
188 extractNames( matches
),
189 testCase
.expectedMatches
,
194 matches
= model
.findMatches( 'foo' );
196 $.isEmptyObject( matches
),
197 'findMatches returns an empty object when no results found'
201 QUnit
.test( 'getParametersFromFilters', function ( assert
) {
205 type
: 'send_unselected_if_any',
209 label
: 'Group 1: Filter 1',
210 description
: 'Description of Filter 1 in Group 1'
214 label
: 'Group 1: Filter 2',
215 description
: 'Description of Filter 2 in Group 1'
219 label
: 'Group 1: Filter 3',
220 description
: 'Description of Filter 3 in Group 1'
226 type
: 'send_unselected_if_any',
230 label
: 'Group 2: Filter 1',
231 description
: 'Description of Filter 1 in Group 2'
235 label
: 'Group 2: Filter 2',
236 description
: 'Description of Filter 2 in Group 2'
240 label
: 'Group 2: Filter 3',
241 description
: 'Description of Filter 3 in Group 2'
247 type
: 'string_options',
252 label
: 'Group 3: Filter 1',
253 description
: 'Description of Filter 1 in Group 3'
257 label
: 'Group 3: Filter 2',
258 description
: 'Description of Filter 2 in Group 3'
262 label
: 'Group 3: Filter 3',
263 description
: 'Description of Filter 3 in Group 3'
267 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
269 model
.initializeFilters( definition
);
271 // Starting with all filters unselected
273 model
.getParametersFromFilters(),
283 'Unselected filters return all parameters falsey or \'\'.'
287 model
.toggleFiltersSelected( {
295 // Only one filter in one group
297 model
.getParametersFromFilters(),
299 // Group 1 (one selected, the others are true)
303 // Group 2 (nothing is selected, all false)
309 'One filters in one "send_unselected_if_any" group returns the other parameters truthy.'
313 model
.toggleFiltersSelected( {
321 // Two selected filters in one group
323 model
.getParametersFromFilters(),
325 // Group 1 (two selected, the others are true)
329 // Group 2 (nothing is selected, all false)
335 'One filters in one "send_unselected_if_any" group returns the other parameters truthy.'
339 model
.toggleFiltersSelected( {
347 // All filters of the group are selected == this is the same as not selecting any
349 model
.getParametersFromFilters(),
351 // Group 1 (all selected, all false)
355 // Group 2 (nothing is selected, all false)
361 'All filters selected in one "send_unselected_if_any" group returns all parameters falsy.'
364 // Select 1 filter from string_options
365 model
.toggleFiltersSelected( {
370 // All filters of the group are selected == this is the same as not selecting any
372 model
.getParametersFromFilters(),
374 // Group 1 (all selected, all)
378 // Group 2 (nothing is selected, all false)
384 'One filter selected in "string_option" group returns that filter in the value.'
387 // Select 2 filters from string_options
388 model
.toggleFiltersSelected( {
393 // All filters of the group are selected == this is the same as not selecting any
395 model
.getParametersFromFilters(),
397 // Group 1 (all selected, all)
401 // Group 2 (nothing is selected, all false)
405 group3
: 'filter7,filter8'
407 'Two filters selected in "string_option" group returns those filters in the value.'
410 // Select 3 filters from string_options
411 model
.toggleFiltersSelected( {
416 // All filters of the group are selected == this is the same as not selecting any
418 model
.getParametersFromFilters(),
420 // Group 1 (all selected, all)
424 // Group 2 (nothing is selected, all false)
430 'All filters selected in "string_option" group returns \'all\'.'
435 QUnit
.test( 'getFiltersFromParameters', function ( assert
) {
439 type
: 'send_unselected_if_any',
443 label
: 'Show filter 1',
444 description
: 'Description of Filter 1 in Group 1',
449 label
: 'Show filter 2',
450 description
: 'Description of Filter 2 in Group 1'
454 label
: 'Show filter 3',
455 description
: 'Description of Filter 3 in Group 1',
462 type
: 'send_unselected_if_any',
466 label
: 'Show filter 4',
467 description
: 'Description of Filter 1 in Group 2'
471 label
: 'Show filter 5',
472 description
: 'Description of Filter 2 in Group 2',
477 label
: 'Show filter 6',
478 description
: 'Description of Filter 3 in Group 2'
485 type
: 'string_options',
490 label
: 'Group 3: Filter 1',
491 description
: 'Description of Filter 1 in Group 3'
495 label
: 'Group 3: Filter 2',
496 description
: 'Description of Filter 2 in Group 3',
501 label
: 'Group 3: Filter 3',
502 description
: 'Description of Filter 3 in Group 3'
506 defaultFilterRepresentation
= {
507 // Group 1 and 2, "send_unselected_if_any", the values of the filters are "flipped" from the values of the parameters
514 // Group 3, "string_options", default values correspond to parameters and filters
519 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
521 model
.initializeFilters( definition
);
523 // Empty query = only default values
525 model
.getFiltersFromParameters( {} ),
526 defaultFilterRepresentation
,
527 'Empty parameter query results in filters in initial default state'
531 model
.getFiltersFromParameters( {
534 $.extend( {}, defaultFilterRepresentation
, {
535 hidefilter1
: false, // The text is "show filter 1"
536 hidefilter2
: false, // The text is "show filter 2"
537 hidefilter3
: false // The text is "show filter 3"
539 'One truthy parameter in a group whose other parameters are true by default makes the rest of the filters in the group false (unchecked)'
543 model
.getFiltersFromParameters( {
548 $.extend( {}, defaultFilterRepresentation
, {
549 hidefilter1
: false, // The text is "show filter 1"
550 hidefilter2
: false, // The text is "show filter 2"
551 hidefilter3
: false // The text is "show filter 3"
553 'All paremeters in the same \'send_unselected_if_any\' group false is equivalent to none are truthy (checked) in the interface'
556 // The ones above don't update the model, so we have a clean state.
557 // getFiltersFromParameters is stateless; any change is unaffected by the current state
558 // This test is demonstrating wrong usage of the method;
559 // We should be aware that getFiltersFromParameters is stateless,
560 // so each call gives us a filter state that only reflects the query given.
561 // This means that the two calls to toggleFiltersSelected() below collide.
562 // The result of the first is overridden by the result of the second,
563 // since both get a full state object from getFiltersFromParameters that **only** relates
564 // to the input it receives.
565 model
.toggleFiltersSelected(
566 model
.getFiltersFromParameters( {
571 model
.toggleFiltersSelected(
572 model
.getFiltersFromParameters( {
577 // The result here is ignoring the first toggleFiltersSelected call
578 // We should receive default values + hidefilter6 as false
580 model
.getSelectedState(),
581 $.extend( {}, defaultFilterRepresentation
, {
585 'getFiltersFromParameters does not care about previous or existing state.'
589 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
590 model
.initializeFilters( definition
);
592 model
.toggleFiltersSelected(
593 model
.getFiltersFromParameters( {
597 model
.toggleFiltersSelected(
598 model
.getFiltersFromParameters( {
603 // Simulates minor edits being hidden in preferences, then unhidden via URL
606 model
.getSelectedState(),
607 defaultFilterRepresentation
,
608 'After checking and then unchecking a \'send_unselected_if_any\' filter (without touching other filters in that group), results are default'
611 model
.toggleFiltersSelected(
612 model
.getFiltersFromParameters( {
617 model
.getSelectedState(),
618 $.extend( {}, defaultFilterRepresentation
, {
623 'A \'string_options\' parameter containing 1 value, results in the corresponding filter as checked'
626 model
.toggleFiltersSelected(
627 model
.getFiltersFromParameters( {
628 group3
: 'filter7,filter8'
632 model
.getSelectedState(),
633 $.extend( {}, defaultFilterRepresentation
, {
638 'A \'string_options\' parameter containing 2 values, results in both corresponding filters as checked'
641 model
.toggleFiltersSelected(
642 model
.getFiltersFromParameters( {
643 group3
: 'filter7,filter8,filter9'
647 model
.getSelectedState(),
648 $.extend( {}, defaultFilterRepresentation
, {
653 'A \'string_options\' parameter containing all values, results in all filters of the group as unchecked.'
656 model
.toggleFiltersSelected(
657 model
.getFiltersFromParameters( {
658 group3
: 'filter7,all,filter9'
662 model
.getSelectedState(),
663 $.extend( {}, defaultFilterRepresentation
, {
668 'A \'string_options\' parameter containing the value \'all\', results in all filters of the group as unchecked.'
671 model
.toggleFiltersSelected(
672 model
.getFiltersFromParameters( {
673 group3
: 'filter7,foo,filter9'
677 model
.getSelectedState(),
678 $.extend( {}, defaultFilterRepresentation
, {
683 'A \'string_options\' parameter containing an invalid value, results in the invalid value ignored and the valid corresponding filters checked.'
687 QUnit
.test( 'sanitizeStringOptionGroup', function ( assert
) {
691 type
: 'string_options',
695 label
: 'Show filter 1',
696 description
: 'Description of Filter 1 in Group 1'
700 label
: 'Show filter 2',
701 description
: 'Description of Filter 2 in Group 1'
705 label
: 'Show filter 3',
706 description
: 'Description of Filter 3 in Group 1'
710 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
712 model
.initializeFilters( definition
);
715 model
.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'filter1', 'filter2' ] ),
716 [ 'filter1', 'filter2' ],
717 'Remove duplicate values'
721 model
.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'foo', 'filter2' ] ),
722 [ 'filter1', 'filter2' ],
723 'Remove invalid values'
727 model
.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'all', 'filter2' ] ),
729 'If any value is "all", the only value is "all".'
733 QUnit
.test( 'setFiltersToDefaults', function ( assert
) {
737 type
: 'send_unselected_if_any',
741 label
: 'Show filter 1',
742 description
: 'Description of Filter 1 in Group 1',
747 label
: 'Show filter 2',
748 description
: 'Description of Filter 2 in Group 1'
752 label
: 'Show filter 3',
753 description
: 'Description of Filter 3 in Group 1',
760 type
: 'send_unselected_if_any',
764 label
: 'Show filter 4',
765 description
: 'Description of Filter 1 in Group 2'
769 label
: 'Show filter 5',
770 description
: 'Description of Filter 2 in Group 2',
775 label
: 'Show filter 6',
776 description
: 'Description of Filter 3 in Group 2'
780 defaultFilterRepresentation
= {
781 // Group 1 and 2, "send_unselected_if_any", the values of the filters are "flipped" from the values of the parameters
789 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
791 model
.initializeFilters( definition
);
794 model
.getSelectedState(),
803 'Initial state: default filters are not selected (controller selects defaults explicitly).'
806 model
.toggleFiltersSelected( {
811 model
.setFiltersToDefaults();
814 model
.getSelectedState(),
815 defaultFilterRepresentation
,
816 'Changing values of filters and then returning to defaults still results in default filters being selected.'
820 QUnit
.test( 'Filter interaction: subsets', function ( assert
) {
824 type
: 'string_options',
828 label
: 'Show filter 1',
829 description
: 'Description of Filter 1 in Group 1',
843 label
: 'Show filter 2',
844 description
: 'Description of Filter 2 in Group 1',
854 label
: 'Show filter 3',
855 description
: 'Description of Filter 3 in Group 1'
860 filter1
: { selected
: false, conflicted
: false, included
: false },
861 filter2
: { selected
: false, conflicted
: false, included
: false },
862 filter3
: { selected
: false, conflicted
: false, included
: false }
864 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
866 model
.initializeFilters( definition
);
867 // Select a filter that has subset with another filter
868 model
.toggleFiltersSelected( {
872 model
.reassessFilterInteractions( model
.getItemByName( 'filter1' ) );
874 model
.getFullState(),
875 $.extend( true, {}, baseFullState
, {
876 filter1
: { selected
: true },
877 filter2
: { included
: true },
878 filter3
: { included
: true }
880 'Filters with subsets are represented in the model.'
883 // Select another filter that has a subset with the same previous filter
884 model
.toggleFiltersSelected( {
887 model
.reassessFilterInteractions( model
.getItemByName( 'filter2' ) );
889 model
.getFullState(),
890 $.extend( true, {}, baseFullState
, {
891 filter1
: { selected
: true },
892 filter2
: { selected
: true, included
: true },
893 filter3
: { included
: true }
895 'Filters that have multiple subsets are represented.'
898 // Remove one filter (but leave the other) that affects filter2
899 model
.toggleFiltersSelected( {
902 model
.reassessFilterInteractions( model
.getItemByName( 'filter1' ) );
904 model
.getFullState(),
905 $.extend( true, {}, baseFullState
, {
906 filter2
: { selected
: true, included
: false },
907 filter3
: { included
: true }
909 'Removing a filter only un-includes its subset if there is no other filter affecting.'
912 model
.toggleFiltersSelected( {
915 model
.reassessFilterInteractions( model
.getItemByName( 'filter2' ) );
917 model
.getFullState(),
919 'Removing all supersets also un-includes the subsets.'
923 QUnit
.test( 'Filter interaction: full coverage', function ( assert
) {
927 type
: 'string_options',
930 { name
: 'filter1', label
: '1', description
: '1' },
931 { name
: 'filter2', label
: '2', description
: '2' },
932 { name
: 'filter3', label
: '3', description
: '3' }
937 type
: 'send_unselected_if_any',
940 { name
: 'filter4', label
: '4', description
: '4' },
941 { name
: 'filter5', label
: '5', description
: '5' },
942 { name
: 'filter6', label
: '6', description
: '6' }
945 model
= new mw
.rcfilters
.dm
.FiltersViewModel(),
946 isCapsuleItemMuted = function ( filterName
) {
947 var itemModel
= model
.getItemByName( filterName
),
948 groupModel
= itemModel
.getGroupModel();
950 // This is the logic inside the capsule widget
952 // The capsule item widget only appears if the item is selected
953 itemModel
.isSelected() &&
954 // Muted state is only valid if group is full coverage and all items are selected
955 groupModel
.isFullCoverage() && groupModel
.areAllSelected()
958 getCurrentItemsMutedState = function () {
960 filter1
: isCapsuleItemMuted( 'filter1' ),
961 filter2
: isCapsuleItemMuted( 'filter2' ),
962 filter3
: isCapsuleItemMuted( 'filter3' ),
963 filter4
: isCapsuleItemMuted( 'filter4' ),
964 filter5
: isCapsuleItemMuted( 'filter5' ),
965 filter6
: isCapsuleItemMuted( 'filter6' )
977 model
.initializeFilters( definition
);
979 // Starting state, no selection, all items are non-muted
981 getCurrentItemsMutedState(),
983 'No selection - all items are non-muted'
986 // Select most (but not all) items in each group
987 model
.toggleFiltersSelected( {
994 // Both groups have multiple (but not all) items selected, all items are non-muted
996 getCurrentItemsMutedState(),
998 'Not all items in the group selected - all items are non-muted'
1001 // Select all items in 'fullCoverage' group (group2)
1002 model
.toggleFiltersSelected( {
1006 // Group2 (full coverage) has all items selected, all its items are muted
1008 getCurrentItemsMutedState(),
1009 $.extend( {}, baseMuteState
, {
1014 'All items in \'full coverage\' group are selected - all items in the group are muted'
1017 // Select all items in non 'fullCoverage' group (group1)
1018 model
.toggleFiltersSelected( {
1022 // Group1 (full coverage) has all items selected, no items in it are muted (non full coverage)
1024 getCurrentItemsMutedState(),
1025 $.extend( {}, baseMuteState
, {
1030 'All items in a non \'full coverage\' group are selected - none of the items in the group are muted'
1033 // Uncheck an item from each group
1034 model
.toggleFiltersSelected( {
1039 getCurrentItemsMutedState(),
1041 'Not all items in the group are checked - all items are non-muted regardless of group coverage'
1045 QUnit
.test( 'Filter interaction: conflicts', function ( assert
) {
1046 var definition
= [ {
1049 type
: 'string_options',
1055 conflicts
: [ { group
: 'group2' } ]
1061 conflicts
: [ { group
: 'group2', filter
: 'filter6' } ]
1072 type
: 'send_unselected_if_any',
1073 conflicts
: [ { group
: 'group1', filter
: 'filter1' } ],
1089 conflicts
: [ { group
: 'group1', filter
: 'filter2' } ]
1094 filter1
: { selected
: false, conflicted
: false, included
: false },
1095 filter2
: { selected
: false, conflicted
: false, included
: false },
1096 filter3
: { selected
: false, conflicted
: false, included
: false },
1097 filter4
: { selected
: false, conflicted
: false, included
: false },
1098 filter5
: { selected
: false, conflicted
: false, included
: false },
1099 filter6
: { selected
: false, conflicted
: false, included
: false }
1101 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
1103 model
.initializeFilters( definition
);
1106 model
.getFullState(),
1108 'Initial state: no conflicts because no selections.'
1111 // Select a filter that has a conflict with an entire group
1112 model
.toggleFiltersSelected( {
1113 filter1
: true // conflicts: entire of group 2 ( filter4, filter5, filter6)
1116 model
.reassessFilterInteractions( model
.getItemByName( 'filter1' ) );
1119 model
.getFullState(),
1120 $.extend( true, {}, baseFullState
, {
1121 filter1
: { selected
: true },
1122 filter4
: { conflicted
: true },
1123 filter5
: { conflicted
: true },
1124 filter6
: { conflicted
: true }
1126 'Selecting a filter that conflicts with a group sets all the conflicted group items as "conflicted".'
1129 // Select one of the conflicts (both filters are now conflicted and selected)
1130 model
.toggleFiltersSelected( {
1131 filter4
: true // conflicts: filter 1
1133 model
.reassessFilterInteractions( model
.getItemByName( 'filter4' ) );
1136 model
.getFullState(),
1137 $.extend( true, {}, baseFullState
, {
1138 filter1
: { selected
: true, conflicted
: true },
1139 filter4
: { selected
: true, conflicted
: true },
1140 filter5
: { conflicted
: true },
1141 filter6
: { conflicted
: true }
1143 'Selecting a conflicting filter inside a group, sets both sides to conflicted and selected.'
1147 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
1148 model
.initializeFilters( definition
);
1150 // Select a filter that has a conflict with a specific filter
1151 model
.toggleFiltersSelected( {
1152 filter2
: true // conflicts: filter6
1155 model
.reassessFilterInteractions( model
.getItemByName( 'filter2' ) );
1158 model
.getFullState(),
1159 $.extend( true, {}, baseFullState
, {
1160 filter2
: { selected
: true },
1161 filter6
: { conflicted
: true }
1163 'Selecting a filter that conflicts with another filter sets the other as "conflicted".'
1166 // Select the conflicting filter
1167 model
.toggleFiltersSelected( {
1168 filter6
: true // conflicts: filter2
1171 model
.reassessFilterInteractions( model
.getItemByName( 'filter6' ) );
1174 model
.getFullState(),
1175 $.extend( true, {}, baseFullState
, {
1176 filter2
: { selected
: true, conflicted
: true },
1177 filter6
: { selected
: true, conflicted
: true },
1178 // This is added to the conflicts because filter6 is part of group2,
1179 // who is in conflict with filter1; note that filter2 also conflicts
1180 // with filter6 which means that filter1 conflicts with filter6 (because it's in group2)
1181 // and also because its **own sibling** (filter2) is **also** in conflict with the
1182 // selected items in group2 (filter6)
1183 filter1
: { conflicted
: true }
1185 'Selecting a conflicting filter with an individual filter, sets both sides to conflicted and selected.'
1188 // Now choose a non-conflicting filter from the group
1189 model
.toggleFiltersSelected( {
1193 model
.reassessFilterInteractions( model
.getItemByName( 'filter5' ) );
1196 model
.getFullState(),
1197 $.extend( true, {}, baseFullState
, {
1198 filter2
: { selected
: true },
1199 filter6
: { selected
: true },
1200 filter5
: { selected
: true }
1201 // Filter6 and filter1 are no longer in conflict because
1202 // filter5, while it is in conflict with filter1, it is
1203 // not in conflict with filter2 - and since filter2 is
1204 // selected, it removes the conflict bidirectionally
1206 'Selecting a non-conflicting filter within the group of a conflicting filter removes the conflicts.'
1209 // Followup on the previous test, unselect filter2 so filter1
1210 // is now the only one selected in its own group, and since
1211 // it is in conflict with the entire of group2, it means
1212 // filter1 is once again conflicted
1213 model
.toggleFiltersSelected( {
1217 model
.reassessFilterInteractions( model
.getItemByName( 'filter2' ) );
1220 model
.getFullState(),
1221 $.extend( true, {}, baseFullState
, {
1222 filter1
: { conflicted
: true },
1223 filter6
: { selected
: true },
1224 filter5
: { selected
: true }
1226 'Unselecting an item that did not conflict returns the conflict state.'
1229 // Followup #2: Now actually select filter1, and make everything conflicted
1230 model
.toggleFiltersSelected( {
1234 model
.reassessFilterInteractions( model
.getItemByName( 'filter1' ) );
1237 model
.getFullState(),
1238 $.extend( true, {}, baseFullState
, {
1239 filter1
: { selected
: true, conflicted
: true },
1240 filter6
: { selected
: true, conflicted
: true },
1241 filter5
: { selected
: true, conflicted
: true },
1242 filter4
: { conflicted
: true } // Not selected but conflicted because it's in group2
1244 'Selecting an item that conflicts with a whole group makes all selections in that group conflicted.'
1249 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
1250 model
.initializeFilters( definition
);
1252 // Select a filter that has a conflict with a specific filter
1253 model
.toggleFiltersSelected( {
1254 filter2
: true // conflicts: filter6
1257 model
.reassessFilterInteractions( model
.getItemByName( 'filter2' ) );
1260 model
.getFullState(),
1261 $.extend( true, {}, baseFullState
, {
1262 filter2
: { selected
: true },
1263 filter6
: { conflicted
: true }
1265 'Simple case: Selecting a filter that conflicts with another filter sets the other as "conflicted".'
1268 model
.toggleFiltersSelected( {
1269 filter3
: true // conflicts: filter6
1272 model
.reassessFilterInteractions( model
.getItemByName( 'filter3' ) );
1275 model
.getFullState(),
1276 $.extend( true, {}, baseFullState
, {
1277 filter2
: { selected
: true },
1278 filter3
: { selected
: true }
1280 'Simple case: Selecting a filter that is not in conflict removes the conflict.'
1285 QUnit
.test( 'Filter highlights', function ( assert
) {
1286 var definition
= [ {
1289 type
: 'string_options',
1291 { name
: 'filter1', cssClass
: 'class1', label
: '1', description
: '1' },
1292 { name
: 'filter2', cssClass
: 'class2', label
: '2', description
: '2' },
1293 { name
: 'filter3', cssClass
: 'class3', label
: '3', description
: '3' },
1294 { name
: 'filter4', cssClass
: 'class4', label
: '4', description
: '4' },
1295 { name
: 'filter5', cssClass
: 'class5', label
: '5', description
: '5' },
1296 { name
: 'filter6', label
: '6', description
: '6' }
1299 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
1301 model
.initializeFilters( definition
);
1304 !model
.isHighlightEnabled(),
1305 'Initially, highlight is disabled.'
1308 model
.toggleHighlight( true );
1310 model
.isHighlightEnabled(),
1311 'Highlight is enabled on toggle.'
1314 model
.setHighlightColor( 'filter1', 'color1' );
1315 model
.setHighlightColor( 'filter2', 'color2' );
1318 model
.getHighlightedItems().map( function ( item
) {
1319 return item
.getName();
1325 'Highlighted items are highlighted.'
1329 model
.getItemByName( 'filter1' ).getHighlightColor(),
1331 'Item highlight color is set.'
1334 model
.setHighlightColor( 'filter1', 'color1changed' );
1336 model
.getItemByName( 'filter1' ).getHighlightColor(),
1338 'Item highlight color is changed on setHighlightColor.'
1341 model
.clearHighlightColor( 'filter1' );
1343 model
.getHighlightedItems().map( function ( item
) {
1344 return item
.getName();
1349 'Clear highlight from an item results in the item no longer being highlighted.'
1353 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
1354 model
.initializeFilters( definition
);
1356 model
.setHighlightColor( 'filter1', 'color1' );
1357 model
.setHighlightColor( 'filter2', 'color2' );
1358 model
.setHighlightColor( 'filter3', 'color3' );
1361 model
.getHighlightedItems().map( function ( item
) {
1362 return item
.getName();
1369 'Even if highlights are not enabled, the items remember their highlight state'
1370 // NOTE: When actually displaying the highlights, the UI checks whether
1371 // highlighting is generally active and then goes over the highlighted
1372 // items. The item models, however, and the view model in general, still
1373 // retains the knowledge about which filters have different colors, so we
1374 // can seamlessly return to the colors the user previously chose if they
1375 // reapply highlights.
1379 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
1380 model
.initializeFilters( definition
);
1382 model
.setHighlightColor( 'filter1', 'color1' );
1383 model
.setHighlightColor( 'filter6', 'color6' );
1386 model
.getHighlightedItems().map( function ( item
) {
1387 return item
.getName();
1392 'Items without a specified class identifier are not highlighted.'
1395 }( mediaWiki
, jQuery
) );