2 QUnit
.module( 'mediawiki.rcfilters - FiltersViewModel' );
4 QUnit
.test( 'Setting up filters', function ( assert
) {
8 type
: 'send_unselected_if_any',
11 name
: 'group1filter1',
12 label
: 'Group 1: Filter 1',
13 description
: 'Description of Filter 1 in Group 1'
16 name
: 'group1filter2',
17 label
: 'Group 1: Filter 2',
18 description
: 'Description of Filter 2 in Group 1'
24 type
: 'send_unselected_if_any',
27 name
: 'group2filter1',
28 label
: 'Group 2: Filter 1',
29 description
: 'Description of Filter 1 in Group 2'
32 name
: 'group2filter2',
33 label
: 'Group 2: Filter 2',
34 description
: 'Description of Filter 2 in Group 2'
40 type
: 'string_options',
43 name
: 'group3filter1',
44 label
: 'Group 3: Filter 1',
45 description
: 'Description of Filter 1 in Group 3'
48 name
: 'group3filter2',
49 label
: 'Group 3: Filter 2',
50 description
: 'Description of Filter 2 in Group 3'
55 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
57 model
.initializeFilters( definition
);
60 model
.getItemByName( 'group1filter1' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
61 model
.getItemByName( 'group1filter2' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
62 model
.getItemByName( 'group2filter1' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
63 model
.getItemByName( 'group2filter2' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
64 model
.getItemByName( 'group3filter1' ) instanceof mw
.rcfilters
.dm
.FilterItem
&&
65 model
.getItemByName( 'group3filter2' ) instanceof mw
.rcfilters
.dm
.FilterItem
,
66 'Filters instantiated and stored correctly'
70 model
.getSelectedState(),
79 'Initial state of filters'
82 model
.updateFilters( {
88 model
.getSelectedState(),
97 'Updating filter states correctly'
101 QUnit
.test( 'Finding matching filters', function ( assert
) {
106 type
: 'send_unselected_if_any',
109 name
: 'group1filter1',
110 label
: 'Group 1: Filter 1',
111 description
: 'Description of Filter 1 in Group 1'
114 name
: 'group1filter2',
115 label
: 'Group 1: Filter 2',
116 description
: 'Description of Filter 2 in Group 1'
122 type
: 'send_unselected_if_any',
125 name
: 'group2filter1',
126 label
: 'Group 2: Filter 1',
127 description
: 'Description of Filter 1 in Group 2'
130 name
: 'group2filter2',
131 label
: 'Group 2: Filter 2',
132 description
: 'Description of Filter 2 in Group 2'
137 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
139 model
.initializeFilters( definition
);
141 matches
= model
.findMatches( 'group 1' );
143 matches
.group1
.length
,
145 'findMatches finds correct group with correct number of results'
149 matches
.group1
.map( function ( item
) { return item
.getName(); } ),
150 [ 'group1filter1', 'group1filter2' ],
151 'findMatches finds the correct items within a single group'
154 matches
= model
.findMatches( 'filter 1' );
156 matches
.group1
.length
=== 1 && matches
.group2
.length
=== 1,
157 'findMatches finds correct number of results in multiple groups'
162 matches
.group1
.map( function ( item
) { return item
.getName(); } ),
163 matches
.group2
.map( function ( item
) { return item
.getName(); } )
169 'findMatches finds the correct items within multiple groups'
172 matches
= model
.findMatches( 'foo' );
174 $.isEmptyObject( matches
),
175 'findMatches returns an empty object when no results found'
179 QUnit
.test( 'getParametersFromFilters', function ( assert
) {
183 type
: 'send_unselected_if_any',
187 label
: 'Group 1: Filter 1',
188 description
: 'Description of Filter 1 in Group 1'
192 label
: 'Group 1: Filter 2',
193 description
: 'Description of Filter 2 in Group 1'
197 label
: 'Group 1: Filter 3',
198 description
: 'Description of Filter 3 in Group 1'
204 type
: 'send_unselected_if_any',
208 label
: 'Group 2: Filter 1',
209 description
: 'Description of Filter 1 in Group 2'
213 label
: 'Group 2: Filter 2',
214 description
: 'Description of Filter 2 in Group 2'
218 label
: 'Group 2: Filter 3',
219 description
: 'Description of Filter 3 in Group 2'
225 type
: 'string_options',
230 label
: 'Group 3: Filter 1',
231 description
: 'Description of Filter 1 in Group 3'
235 label
: 'Group 3: Filter 2',
236 description
: 'Description of Filter 2 in Group 3'
240 label
: 'Group 3: Filter 3',
241 description
: 'Description of Filter 3 in Group 3'
246 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
248 model
.initializeFilters( definition
);
250 // Starting with all filters unselected
252 model
.getParametersFromFilters(),
262 'Unselected filters return all parameters falsey or \'all\'.'
266 model
.updateFilters( {
274 // Only one filter in one group
276 model
.getParametersFromFilters(),
278 // Group 1 (one selected, the others are true)
282 // Group 2 (nothing is selected, all false)
288 'One filters in one "send_unselected_if_any" group returns the other parameters truthy.'
292 model
.updateFilters( {
300 // Two selected filters in one group
302 model
.getParametersFromFilters(),
304 // Group 1 (two selected, the others are true)
308 // Group 2 (nothing is selected, all false)
314 'One filters in one "send_unselected_if_any" group returns the other parameters truthy.'
318 model
.updateFilters( {
326 // All filters of the group are selected == this is the same as not selecting any
328 model
.getParametersFromFilters(),
330 // Group 1 (all selected, all false)
334 // Group 2 (nothing is selected, all false)
340 'All filters selected in one "send_unselected_if_any" group returns all parameters falsy.'
343 // Select 1 filter from string_options
344 model
.updateFilters( {
349 // All filters of the group are selected == this is the same as not selecting any
351 model
.getParametersFromFilters(),
353 // Group 1 (all selected, all)
357 // Group 2 (nothing is selected, all false)
363 'One filter selected in "string_option" group returns that filter in the value.'
366 // Select 2 filters from string_options
367 model
.updateFilters( {
372 // All filters of the group are selected == this is the same as not selecting any
374 model
.getParametersFromFilters(),
376 // Group 1 (all selected, all)
380 // Group 2 (nothing is selected, all false)
384 group3
: 'filter7,filter8'
386 'Two filters selected in "string_option" group returns those filters in the value.'
389 // Select 3 filters from string_options
390 model
.updateFilters( {
395 // All filters of the group are selected == this is the same as not selecting any
397 model
.getParametersFromFilters(),
399 // Group 1 (all selected, all)
403 // Group 2 (nothing is selected, all false)
409 'All filters selected in "string_option" group returns \'all\'.'
414 QUnit
.test( 'getFiltersFromParameters', function ( assert
) {
418 type
: 'send_unselected_if_any',
422 label
: 'Show filter 1',
423 description
: 'Description of Filter 1 in Group 1'
427 label
: 'Show filter 2',
428 description
: 'Description of Filter 2 in Group 1'
432 label
: 'Show filter 3',
433 description
: 'Description of Filter 3 in Group 1'
439 type
: 'send_unselected_if_any',
443 label
: 'Show filter 4',
444 description
: 'Description of Filter 1 in Group 2'
448 label
: 'Show filter 5',
449 description
: 'Description of Filter 2 in Group 2'
453 label
: 'Show filter 6',
454 description
: 'Description of Filter 3 in Group 2'
460 type
: 'string_options',
465 label
: 'Group 3: Filter 1',
466 description
: 'Description of Filter 1 in Group 3'
470 label
: 'Group 3: Filter 2',
471 description
: 'Description of Filter 2 in Group 3'
475 label
: 'Group 3: Filter 3',
476 description
: 'Description of Filter 3 in Group 3'
481 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
483 model
.initializeFilters( definition
);
485 // Empty query = empty filter definition
487 model
.getFiltersFromParameters( {} ),
489 hidefilter1
: false, // The text is "show filter 1"
490 hidefilter2
: false, // The text is "show filter 2"
491 hidefilter3
: false, // The text is "show filter 3"
492 hidefilter4
: false, // The text is "show filter 4"
493 hidefilter5
: false, // The text is "show filter 5"
494 hidefilter6
: false, // The text is "show filter 6"
499 'Empty parameter query results in filters in initial state'
503 model
.getFiltersFromParameters( {
507 hidefilter1
: false, // The text is "show filter 1"
508 hidefilter2
: true, // The text is "show filter 2"
509 hidefilter3
: true, // The text is "show filter 3"
510 hidefilter4
: false, // The text is "show filter 4"
511 hidefilter5
: false, // The text is "show filter 5"
512 hidefilter6
: false, // The text is "show filter 6"
517 'One falsey parameter in a group makes the rest of the filters in the group truthy (checked) in the interface'
521 model
.getFiltersFromParameters( {
526 hidefilter1
: false, // The text is "show filter 1"
527 hidefilter2
: false, // The text is "show filter 2"
528 hidefilter3
: true, // The text is "show filter 3"
529 hidefilter4
: false, // The text is "show filter 4"
530 hidefilter5
: false, // The text is "show filter 5"
531 hidefilter6
: false, // The text is "show filter 6"
536 'Two falsey parameters in a \'send_unselected_if_any\' group makes the rest of the filters in the group truthy (checked) in the interface'
540 model
.getFiltersFromParameters( {
546 // TODO: This will have to be represented as a different state, though.
547 hidefilter1
: false, // The text is "show filter 1"
548 hidefilter2
: false, // The text is "show filter 2"
549 hidefilter3
: false, // The text is "show filter 3"
550 hidefilter4
: false, // The text is "show filter 4"
551 hidefilter5
: false, // The text is "show filter 5"
552 hidefilter6
: false, // The text is "show filter 6"
557 'All paremeters in the same \'send_unselected_if_any\' group false is equivalent to none are truthy (checked) in the interface'
560 // The ones above don't update the model, so we have a clean state.
563 model
.getFiltersFromParameters( {
569 model
.getFiltersFromParameters( {
574 // 1 and 3 are separately unchecked via hide parameters, 2 should still be
576 // This can simulate separate filters in the same group being hidden different
577 // ways (e.g. preferences and URL).
579 model
.getSelectedState(),
581 hidefilter1
: false, // The text is "show filter 1"
582 hidefilter2
: true, // The text is "show filter 2"
583 hidefilter3
: false, // The text is "show filter 3"
584 hidefilter4
: false, // The text is "show filter 4"
585 hidefilter5
: false, // The text is "show filter 5"
586 hidefilter6
: false, // The text is "show filter 6"
591 'After unchecking 2 of 3 \'send_unselected_if_any\' filters via separate updateFilters calls, only the remaining one is still checked.'
595 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
596 model
.initializeFilters( definition
);
599 model
.getFiltersFromParameters( {
604 model
.getFiltersFromParameters( {
609 // Simulates minor edits being hidden in preferences, then unhidden via URL
612 model
.getSelectedState(),
614 hidefilter1
: false, // The text is "show filter 1"
615 hidefilter2
: false, // The text is "show filter 2"
616 hidefilter3
: false, // The text is "show filter 3"
617 hidefilter4
: false, // The text is "show filter 4"
618 hidefilter5
: false, // The text is "show filter 5"
619 hidefilter6
: false, // The text is "show filter 6"
624 'After unchecking then checking a \'send_unselected_if_any\' filter (without touching other filters in that group), all are checked'
628 model
.getFiltersFromParameters( {
633 model
.getSelectedState(),
635 hidefilter1
: false, // The text is "show filter 1"
636 hidefilter2
: false, // The text is "show filter 2"
637 hidefilter3
: false, // The text is "show filter 3"
638 hidefilter4
: false, // The text is "show filter 4"
639 hidefilter5
: false, // The text is "show filter 5"
640 hidefilter6
: false, // The text is "show filter 6"
645 'A \'string_options\' parameter containing 1 value, results in the corresponding filter as checked'
649 model
.getFiltersFromParameters( {
650 group3
: 'filter7,filter8'
654 model
.getSelectedState(),
656 hidefilter1
: false, // The text is "show filter 1"
657 hidefilter2
: false, // The text is "show filter 2"
658 hidefilter3
: false, // The text is "show filter 3"
659 hidefilter4
: false, // The text is "show filter 4"
660 hidefilter5
: false, // The text is "show filter 5"
661 hidefilter6
: false, // The text is "show filter 6"
666 'A \'string_options\' parameter containing 2 values, results in both corresponding filters as checked'
670 model
.getFiltersFromParameters( {
671 group3
: 'filter7,filter8,filter9'
675 model
.getSelectedState(),
677 hidefilter1
: false, // The text is "show filter 1"
678 hidefilter2
: false, // The text is "show filter 2"
679 hidefilter3
: false, // The text is "show filter 3"
680 hidefilter4
: false, // The text is "show filter 4"
681 hidefilter5
: false, // The text is "show filter 5"
682 hidefilter6
: false, // The text is "show filter 6"
687 'A \'string_options\' parameter containing all values, results in all filters of the group as unchecked.'
691 model
.getFiltersFromParameters( {
692 group3
: 'filter7,filter8,filter9'
696 model
.getSelectedState(),
698 hidefilter1
: false, // The text is "show filter 1"
699 hidefilter2
: false, // The text is "show filter 2"
700 hidefilter3
: false, // The text is "show filter 3"
701 hidefilter4
: false, // The text is "show filter 4"
702 hidefilter5
: false, // The text is "show filter 5"
703 hidefilter6
: false, // The text is "show filter 6"
708 'A \'string_options\' parameter containing the value \'all\', results in all filters of the group as unchecked.'
712 model
.getFiltersFromParameters( {
713 group3
: 'filter7,foo,filter9'
717 model
.getSelectedState(),
719 hidefilter1
: false, // The text is "show filter 1"
720 hidefilter2
: false, // The text is "show filter 2"
721 hidefilter3
: false, // The text is "show filter 3"
722 hidefilter4
: false, // The text is "show filter 4"
723 hidefilter5
: false, // The text is "show filter 5"
724 hidefilter6
: false, // The text is "show filter 6"
729 'A \'string_options\' parameter containing an invalid value, results in the invalid value ignored and the valid corresponding filters checked.'
733 QUnit
.test( 'sanitizeStringOptionGroup', function ( assert
) {
737 type
: 'string_options',
741 label
: 'Show filter 1',
742 description
: 'Description of Filter 1 in Group 1'
746 label
: 'Show filter 2',
747 description
: 'Description of Filter 2 in Group 1'
751 label
: 'Show filter 3',
752 description
: 'Description of Filter 3 in Group 1'
757 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
759 model
.initializeFilters( definition
);
762 model
.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'filter1', 'filter2' ] ),
763 [ 'filter1', 'filter2' ],
764 'Remove duplicate values'
768 model
.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'foo', 'filter2' ] ),
769 [ 'filter1', 'filter2' ],
770 'Remove invalid values'
774 model
.sanitizeStringOptionGroup( 'group1', [ 'filter1', 'all', 'filter2' ] ),
776 'If any value is "all", the only value is "all".'
780 QUnit
.test( 'reapplyActiveFilters - "default" exclusion rules', function ( assert
) {
784 type
: 'send_unselected_if_any',
785 exclusionType
: 'default',
789 label
: 'Show filter 1',
790 description
: 'Description of Filter 1 in Group 1'
794 label
: 'Show filter 2',
795 description
: 'Description of Filter 2 in Group 1'
799 label
: 'Show filter 3',
800 description
: 'Description of Filter 3 in Group 1'
806 type
: 'send_unselected_if_any',
810 label
: 'Show filter 4',
811 description
: 'Description of Filter 1 in Group 2'
815 label
: 'Show filter 5',
816 description
: 'Description of Filter 2 in Group 2'
820 label
: 'Show filter 6',
821 description
: 'Description of Filter 3 in Group 2'
826 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
828 model
.initializeFilters( definition
);
831 model
.getFullState(),
834 hidefilter1
: { selected
: false, active
: true },
835 hidefilter2
: { selected
: false, active
: true },
836 hidefilter3
: { selected
: false, active
: true },
838 hidefilter4
: { selected
: false, active
: true },
839 hidefilter5
: { selected
: false, active
: true },
840 hidefilter6
: { selected
: false, active
: true },
842 'Initial state: all filters are active.'
845 // Default behavior for 'exclusion' type with only 1 item selected, means that:
846 // - The items in the same group that are *not* selected are *not* active
847 // - Items in other groups are unaffected (all active)
848 model
.updateFilters( {
857 model
.getFullState(),
859 // Group 1: not affected
860 hidefilter1
: { selected
: false, active
: true },
861 hidefilter2
: { selected
: false, active
: true },
862 hidefilter3
: { selected
: false, active
: true },
864 hidefilter4
: { selected
: false, active
: false },
865 hidefilter5
: { selected
: false, active
: false },
866 hidefilter6
: { selected
: true, active
: true },
868 'Default exclusion behavior with 1 item selected in the group.'
871 // Default behavior for 'exclusion' type with multiple items selected, but not all, means that:
872 // - The items in the same group that are *not* selected are *not* active
873 // - Items in other groups are unaffected (all active)
874 model
.updateFilters( {
875 // Literally updating filters to create a clean state
884 model
.getFullState(),
886 // Group 1: not affected
887 hidefilter1
: { selected
: false, active
: true },
888 hidefilter2
: { selected
: false, active
: true },
889 hidefilter3
: { selected
: false, active
: true },
891 hidefilter4
: { selected
: false, active
: false },
892 hidefilter5
: { selected
: true, active
: true },
893 hidefilter6
: { selected
: true, active
: true },
895 'Default exclusion behavior with multiple items (but not all) selected in the group.'
898 // Default behavior for 'exclusion' type with all items in the group selected, means that:
899 // - All items in the group are NOT active
900 // - Items in other groups are unaffected (all active)
901 model
.updateFilters( {
902 // Literally updating filters to create a clean state
911 model
.getFullState(),
913 // Group 1: not affected
914 hidefilter1
: { selected
: false, active
: true },
915 hidefilter2
: { selected
: false, active
: true },
916 hidefilter3
: { selected
: false, active
: true },
918 hidefilter4
: { selected
: true, active
: false },
919 hidefilter5
: { selected
: true, active
: false },
920 hidefilter6
: { selected
: true, active
: false },
922 'Default exclusion behavior with all items in the group.'
926 QUnit
.test( 'reapplyActiveFilters - "explicit" exclusion rules', function ( assert
) {
930 type
: 'send_unselected_if_any',
931 exclusionType
: 'explicit',
935 excludes
: [ 'filter2', 'filter3' ],
936 label
: 'Show filter 1',
937 description
: 'Description of Filter 1 in Group 1'
941 excludes
: [ 'filter3' ],
942 label
: 'Show filter 2',
943 description
: 'Description of Filter 2 in Group 1'
947 label
: 'Show filter 3',
948 excludes
: [ 'filter1' ],
949 description
: 'Description of Filter 3 in Group 1'
953 label
: 'Show filter 4',
954 description
: 'Description of Filter 4 in Group 1'
959 model
= new mw
.rcfilters
.dm
.FiltersViewModel();
961 model
.initializeFilters( definition
);
964 model
.getFullState(),
966 filter1
: { selected
: false, active
: true },
967 filter2
: { selected
: false, active
: true },
968 filter3
: { selected
: false, active
: true },
969 filter4
: { selected
: false, active
: true }
971 'Initial state: all filters are active.'
974 // "Explicit" behavior for 'exclusion' with one item checked:
975 // - Items in the 'excluded' list of the selected filter are inactive
976 model
.updateFilters( {
977 // Literally updating filters to create a clean state
978 filter1
: true, // Excludes 'hidefilter2', 'hidefilter3'
979 filter2
: false, // Excludes 'hidefilter3'
980 filter3
: false, // Excludes 'hidefilter1'
981 filter4
: false // No exclusion list
984 model
.getFullState(),
986 filter1
: { selected
: true, active
: true },
987 filter2
: { selected
: false, active
: false },
988 filter3
: { selected
: false, active
: false },
989 filter4
: { selected
: false, active
: true }
991 '"Explicit" exclusion behavior with one item selected that has an exclusion list.'
994 // "Explicit" behavior for 'exclusion' with two item checked:
995 // - Items in the 'excluded' list of each of the selected filter are inactive
996 model
.updateFilters( {
997 // Literally updating filters to create a clean state
998 filter1
: true, // Excludes 'hidefilter2', 'hidefilter3'
999 filter2
: false, // Excludes 'hidefilter3'
1000 filter3
: true, // Excludes 'hidefilter1'
1001 filter4
: false // No exclusion list
1004 model
.getFullState(),
1006 filter1
: { selected
: true, active
: false },
1007 filter2
: { selected
: false, active
: false },
1008 filter3
: { selected
: true, active
: false },
1009 filter4
: { selected
: false, active
: true }
1011 '"Explicit" exclusion behavior with two selected items that both have an exclusion list.'
1014 // "Explicit behavior" with two filters that exclude the same item
1016 // Two filters selected, both exclude 'hidefilter3'
1017 model
.updateFilters( {
1018 // Literally updating filters to create a clean state
1019 filter1
: true, // Excludes 'hidefilter2', 'hidefilter3'
1020 filter2
: true, // Excludes 'hidefilter3'
1021 filter3
: false, // Excludes 'hidefilter1'
1022 filter4
: false // No exclusion list
1025 model
.getFullState(),
1027 filter1
: { selected
: true, active
: true },
1028 filter2
: { selected
: true, active
: false }, // Excluded by filter1
1029 filter3
: { selected
: false, active
: false }, // Excluded by both filter1 and filter2
1030 filter4
: { selected
: false, active
: true }
1032 '"Explicit" exclusion behavior with two selected items that both exclude another item.'
1035 // Unselect filter2: filter3 should still be excluded, because filter1 excludes it and is selected
1036 model
.updateFilters( {
1037 filter2
: false, // Excludes 'hidefilter3'
1040 model
.getFullState(),
1042 filter1
: { selected
: true, active
: true },
1043 filter2
: { selected
: false, active
: false }, // Excluded by filter1
1044 filter3
: { selected
: false, active
: false }, // Still excluded by filter1
1045 filter4
: { selected
: false, active
: true }
1047 '"Explicit" exclusion behavior unselecting one item that excludes another item, that is being excluded by a third active item.'
1050 // Unselect filter1: filter3 should now be active, since both filters that exclude it are unselected
1051 model
.updateFilters( {
1052 filter1
: false, // Excludes 'hidefilter3' and 'hidefilter2'
1055 model
.getFullState(),
1057 filter1
: { selected
: false, active
: true },
1058 filter2
: { selected
: false, active
: true }, // No longer excluded by filter1
1059 filter3
: { selected
: false, active
: true }, // No longer excluded by either filter1 nor filter2
1060 filter4
: { selected
: false, active
: true }
1062 '"Explicit" exclusion behavior unselecting both items that excluded the same third item.'
1066 }( mediaWiki
, jQuery
) );