);
assert.deepEqual(
- model.getState(),
+ model.getSelectedState(),
{
group1filter1: false,
group1filter2: false,
group3filter1: true
} );
assert.deepEqual(
- model.getState(),
+ model.getSelectedState(),
{
group1filter1: true,
group1filter2: false,
var matches,
definition = {
group1: {
- title: 'Group 1',
+ title: 'Group 1 title',
type: 'send_unselected_if_any',
filters: [
{
]
},
group2: {
- title: 'Group 2',
+ title: 'Group 2 title',
type: 'send_unselected_if_any',
filters: [
{
},
{
name: 'group2filter2',
- label: 'Group 2: Filter 2',
+ label: 'xGroup 2: Filter 2',
description: 'Description of Filter 2 in Group 2'
}
]
}
},
- model = new mw.rcfilters.dm.FiltersViewModel();
+ testCases = [
+ {
+ query: 'group',
+ expectedMatches: {
+ group1: [ 'group1filter1', 'group1filter2' ],
+ group2: [ 'group2filter1' ]
+ },
+ reason: 'Finds filters starting with the query string'
+ },
+ {
+ query: 'filter 2 in group',
+ expectedMatches: {
+ group1: [ 'group1filter2' ],
+ group2: [ 'group2filter2' ]
+ },
+ reason: 'Finds filters containing the query string in their description'
+ },
+ {
+ query: 'title',
+ expectedMatches: {
+ group1: [ 'group1filter1', 'group1filter2' ],
+ group2: [ 'group2filter1', 'group2filter2' ]
+ },
+ reason: 'Finds filters containing the query string in their group title'
+ }
+ ],
+ model = new mw.rcfilters.dm.FiltersViewModel(),
+ extractNames = function ( matches ) {
+ var result = {};
+ Object.keys( matches ).forEach( function ( groupName ) {
+ result[ groupName ] = matches[ groupName ].map( function ( item ) {
+ return item.getName();
+ } );
+ } );
+ return result;
+ };
model.initializeFilters( definition );
- matches = model.findMatches( 'group 1' );
- assert.equal(
- matches.group1.length,
- 2,
- 'findMatches finds correct group with correct number of results'
- );
-
- assert.deepEqual(
- matches.group1.map( function ( item ) { return item.getName(); } ),
- [ 'group1filter1', 'group1filter2' ],
- 'findMatches finds the correct items within a single group'
- );
-
- matches = model.findMatches( 'filter 1' );
- assert.ok(
- matches.group1.length === 1 && matches.group2.length === 1,
- 'findMatches finds correct number of results in multiple groups'
- );
-
- assert.deepEqual(
- [
- matches.group1.map( function ( item ) { return item.getName(); } ),
- matches.group2.map( function ( item ) { return item.getName(); } )
- ],
- [
- [ 'group1filter1' ],
- [ 'group2filter1' ]
- ],
- 'findMatches finds the correct items within multiple groups'
- );
+ testCases.forEach( function ( testCase ) {
+ matches = model.findMatches( testCase.query );
+ assert.deepEqual(
+ extractNames( matches ),
+ testCase.expectedMatches,
+ testCase.reason
+ );
+ } );
matches = model.findMatches( 'foo' );
assert.ok(
hidefilter4: 0,
hidefilter5: 0,
hidefilter6: 0,
- group3: 'all',
+ group3: 'all'
},
'Unselected filters return all parameters falsey or \'all\'.'
);
{
name: 'hidefilter1',
label: 'Show filter 1',
- description: 'Description of Filter 1 in Group 1'
+ description: 'Description of Filter 1 in Group 1',
+ default: true
},
{
name: 'hidefilter2',
{
name: 'hidefilter3',
label: 'Show filter 3',
- description: 'Description of Filter 3 in Group 1'
+ description: 'Description of Filter 3 in Group 1',
+ default: true
}
]
},
{
name: 'hidefilter5',
label: 'Show filter 5',
- description: 'Description of Filter 2 in Group 2'
+ description: 'Description of Filter 2 in Group 2',
+ default: true
},
{
name: 'hidefilter6',
{
name: 'filter8',
label: 'Group 3: Filter 2',
- description: 'Description of Filter 2 in Group 3'
+ description: 'Description of Filter 2 in Group 3',
+ default: true
},
{
name: 'filter9',
]
}
},
+ defaultFilterRepresentation = {
+ // Group 1 and 2, "send_unselected_if_any", the values of the filters are "flipped" from the values of the parameters
+ hidefilter1: false,
+ hidefilter2: true,
+ hidefilter3: false,
+ hidefilter4: true,
+ hidefilter5: false,
+ hidefilter6: true,
+ // Group 3, "string_options", default values correspond to parameters and filters
+ filter7: false,
+ filter8: true,
+ filter9: false
+ },
model = new mw.rcfilters.dm.FiltersViewModel();
model.initializeFilters( definition );
- // Empty query = empty filter definition
+ // Empty query = only default values
assert.deepEqual(
model.getFiltersFromParameters( {} ),
- {
- hidefilter1: false, // The text is "show filter 1"
- hidefilter2: false, // The text is "show filter 2"
- hidefilter3: false, // The text is "show filter 3"
- hidefilter4: false, // The text is "show filter 4"
- hidefilter5: false, // The text is "show filter 5"
- hidefilter6: false, // The text is "show filter 6"
- filter7: false,
- filter8: false,
- filter9: false
- },
- 'Empty parameter query results in filters in initial state'
- );
-
- assert.deepEqual(
- model.getFiltersFromParameters( {
- hidefilter1: '1'
- } ),
- {
- hidefilter1: false, // The text is "show filter 1"
- hidefilter2: true, // The text is "show filter 2"
- hidefilter3: true, // The text is "show filter 3"
- hidefilter4: false, // The text is "show filter 4"
- hidefilter5: false, // The text is "show filter 5"
- hidefilter6: false, // The text is "show filter 6"
- filter7: false,
- filter8: false,
- filter9: false
- },
- 'One falsey parameter in a group makes the rest of the filters in the group truthy (checked) in the interface'
+ defaultFilterRepresentation,
+ 'Empty parameter query results in filters in initial default state'
);
assert.deepEqual(
model.getFiltersFromParameters( {
- hidefilter1: '1',
hidefilter2: '1'
} ),
- {
+ $.extend( {}, defaultFilterRepresentation, {
hidefilter1: false, // The text is "show filter 1"
hidefilter2: false, // The text is "show filter 2"
- hidefilter3: true, // The text is "show filter 3"
- hidefilter4: false, // The text is "show filter 4"
- hidefilter5: false, // The text is "show filter 5"
- hidefilter6: false, // The text is "show filter 6"
- filter7: false,
- filter8: false,
- filter9: false
- },
- 'Two falsey parameters in a \'send_unselected_if_any\' group makes the rest of the filters in the group truthy (checked) in the interface'
+ hidefilter3: false // The text is "show filter 3"
+ } ),
+ 'One truthy parameter in a group whose other parameters are true by default makes the rest of the filters in the group false (unchecked)'
);
assert.deepEqual(
hidefilter2: '1',
hidefilter3: '1'
} ),
- {
- // TODO: This will have to be represented as a different state, though.
+ $.extend( {}, defaultFilterRepresentation, {
hidefilter1: false, // The text is "show filter 1"
hidefilter2: false, // The text is "show filter 2"
- hidefilter3: false, // The text is "show filter 3"
- hidefilter4: false, // The text is "show filter 4"
- hidefilter5: false, // The text is "show filter 5"
- hidefilter6: false, // The text is "show filter 6"
- filter7: false,
- filter8: false,
- filter9: false
- },
+ hidefilter3: false // The text is "show filter 3"
+ } ),
'All paremeters in the same \'send_unselected_if_any\' group false is equivalent to none are truthy (checked) in the interface'
);
// The ones above don't update the model, so we have a clean state.
-
+ // getFiltersFromParameters is stateless; any change is unaffected by the current state
+ // This test is demonstrating wrong usage of the method;
+ // We should be aware that getFiltersFromParameters is stateless,
+ // so each call gives us a filter state that only reflects the query given.
+ // This means that the two calls to updateFilters() below collide.
+ // The result of the first is overridden by the result of the second,
+ // since both get a full state object from getFiltersFromParameters that **only** relates
+ // to the input it receives.
model.updateFilters(
model.getFiltersFromParameters( {
hidefilter1: '1'
model.updateFilters(
model.getFiltersFromParameters( {
- hidefilter3: '1'
+ hidefilter6: '1'
} )
);
- // 1 and 3 are separately unchecked via hide parameters, 2 should still be
- // checked.
- // This can simulate separate filters in the same group being hidden different
- // ways (e.g. preferences and URL).
+ // The result here is ignoring the first updateFilters call
+ // We should receive default values + hidefilter6 as false
assert.deepEqual(
- model.getState(),
- {
- hidefilter1: false, // The text is "show filter 1"
- hidefilter2: true, // The text is "show filter 2"
- hidefilter3: false, // The text is "show filter 3"
- hidefilter4: false, // The text is "show filter 4"
- hidefilter5: false, // The text is "show filter 5"
- hidefilter6: false, // The text is "show filter 6"
- filter7: false,
- filter8: false,
- filter9: false
- },
- 'After unchecking 2 of 3 \'send_unselected_if_any\' filters via separate updateFilters calls, only the remaining one is still checked.'
+ model.getSelectedState(),
+ $.extend( {}, defaultFilterRepresentation, {
+ hidefilter5: false,
+ hidefilter6: false
+ } ),
+ 'getFiltersFromParameters does not care about previous or existing state.'
);
// Reset
model.updateFilters(
model.getFiltersFromParameters( {
- hidefilter1: '1'
+ hidefilter1: '0'
} )
);
model.updateFilters(
model.getFiltersFromParameters( {
- hidefilter1: '0'
+ hidefilter1: '1'
} )
);
// Simulates minor edits being hidden in preferences, then unhidden via URL
// override.
assert.deepEqual(
- model.getState(),
- {
- hidefilter1: false, // The text is "show filter 1"
- hidefilter2: false, // The text is "show filter 2"
- hidefilter3: false, // The text is "show filter 3"
- hidefilter4: false, // The text is "show filter 4"
- hidefilter5: false, // The text is "show filter 5"
- hidefilter6: false, // The text is "show filter 6"
- filter7: false,
- filter8: false,
- filter9: false
- },
- 'After unchecking then checking a \'send_unselected_if_any\' filter (without touching other filters in that group), all are checked'
+ model.getSelectedState(),
+ defaultFilterRepresentation,
+ 'After checking and then unchecking a \'send_unselected_if_any\' filter (without touching other filters in that group), results are default'
);
model.updateFilters(
} )
);
assert.deepEqual(
- model.getState(),
- {
- hidefilter1: false, // The text is "show filter 1"
- hidefilter2: false, // The text is "show filter 2"
- hidefilter3: false, // The text is "show filter 3"
- hidefilter4: false, // The text is "show filter 4"
- hidefilter5: false, // The text is "show filter 5"
- hidefilter6: false, // The text is "show filter 6"
+ model.getSelectedState(),
+ $.extend( {}, defaultFilterRepresentation, {
filter7: true,
filter8: false,
filter9: false
- },
+ } ),
'A \'string_options\' parameter containing 1 value, results in the corresponding filter as checked'
);
} )
);
assert.deepEqual(
- model.getState(),
- {
- hidefilter1: false, // The text is "show filter 1"
- hidefilter2: false, // The text is "show filter 2"
- hidefilter3: false, // The text is "show filter 3"
- hidefilter4: false, // The text is "show filter 4"
- hidefilter5: false, // The text is "show filter 5"
- hidefilter6: false, // The text is "show filter 6"
+ model.getSelectedState(),
+ $.extend( {}, defaultFilterRepresentation, {
filter7: true,
filter8: true,
filter9: false
- },
+ } ),
'A \'string_options\' parameter containing 2 values, results in both corresponding filters as checked'
);
} )
);
assert.deepEqual(
- model.getState(),
- {
- hidefilter1: false, // The text is "show filter 1"
- hidefilter2: false, // The text is "show filter 2"
- hidefilter3: false, // The text is "show filter 3"
- hidefilter4: false, // The text is "show filter 4"
- hidefilter5: false, // The text is "show filter 5"
- hidefilter6: false, // The text is "show filter 6"
+ model.getSelectedState(),
+ $.extend( {}, defaultFilterRepresentation, {
filter7: false,
filter8: false,
filter9: false
- },
+ } ),
'A \'string_options\' parameter containing all values, results in all filters of the group as unchecked.'
);
model.updateFilters(
model.getFiltersFromParameters( {
- group3: 'filter7,filter8,filter9'
+ group3: 'filter7,all,filter9'
} )
);
assert.deepEqual(
- model.getState(),
- {
- hidefilter1: false, // The text is "show filter 1"
- hidefilter2: false, // The text is "show filter 2"
- hidefilter3: false, // The text is "show filter 3"
- hidefilter4: false, // The text is "show filter 4"
- hidefilter5: false, // The text is "show filter 5"
- hidefilter6: false, // The text is "show filter 6"
+ model.getSelectedState(),
+ $.extend( {}, defaultFilterRepresentation, {
filter7: false,
filter8: false,
filter9: false
- },
+ } ),
'A \'string_options\' parameter containing the value \'all\', results in all filters of the group as unchecked.'
);
} )
);
assert.deepEqual(
- model.getState(),
- {
- hidefilter1: false, // The text is "show filter 1"
- hidefilter2: false, // The text is "show filter 2"
- hidefilter3: false, // The text is "show filter 3"
- hidefilter4: false, // The text is "show filter 4"
- hidefilter5: false, // The text is "show filter 5"
- hidefilter6: false, // The text is "show filter 6"
+ model.getSelectedState(),
+ $.extend( {}, defaultFilterRepresentation, {
filter7: true,
filter8: false,
filter9: true
- },
+ } ),
'A \'string_options\' parameter containing an invalid value, results in the invalid value ignored and the valid corresponding filters checked.'
);
} );
'If any value is "all", the only value is "all".'
);
} );
+
+ QUnit.test( 'setFiltersToDefaults', function ( assert ) {
+ var definition = {
+ group1: {
+ title: 'Group 1',
+ type: 'send_unselected_if_any',
+ filters: [
+ {
+ name: 'hidefilter1',
+ label: 'Show filter 1',
+ description: 'Description of Filter 1 in Group 1',
+ default: true
+ },
+ {
+ name: 'hidefilter2',
+ label: 'Show filter 2',
+ description: 'Description of Filter 2 in Group 1'
+ },
+ {
+ name: 'hidefilter3',
+ label: 'Show filter 3',
+ description: 'Description of Filter 3 in Group 1',
+ default: true
+ }
+ ]
+ },
+ group2: {
+ title: 'Group 2',
+ type: 'send_unselected_if_any',
+ filters: [
+ {
+ name: 'hidefilter4',
+ label: 'Show filter 4',
+ description: 'Description of Filter 1 in Group 2'
+ },
+ {
+ name: 'hidefilter5',
+ label: 'Show filter 5',
+ description: 'Description of Filter 2 in Group 2',
+ default: true
+ },
+ {
+ name: 'hidefilter6',
+ label: 'Show filter 6',
+ description: 'Description of Filter 3 in Group 2'
+ }
+ ]
+ }
+ },
+ defaultFilterRepresentation = {
+ // Group 1 and 2, "send_unselected_if_any", the values of the filters are "flipped" from the values of the parameters
+ hidefilter1: false,
+ hidefilter2: true,
+ hidefilter3: false,
+ hidefilter4: true,
+ hidefilter5: false,
+ hidefilter6: true
+ },
+ model = new mw.rcfilters.dm.FiltersViewModel();
+
+ model.initializeFilters( definition );
+
+ assert.deepEqual(
+ model.getSelectedState(),
+ {
+ hidefilter1: false,
+ hidefilter2: false,
+ hidefilter3: false,
+ hidefilter4: false,
+ hidefilter5: false,
+ hidefilter6: false
+ },
+ 'Initial state: default filters are not selected (controller selects defaults explicitly).'
+ );
+
+ model.updateFilters( {
+ hidefilter1: false,
+ hidefilter3: false
+ } );
+
+ model.setFiltersToDefaults();
+
+ assert.deepEqual(
+ model.getSelectedState(),
+ defaultFilterRepresentation,
+ 'Changing values of filters and then returning to defaults still results in default filters being selected.'
+ );
+ } );
+
+ QUnit.test( 'Filter interaction: subsets', function ( assert ) {
+ var definition = {
+ group1: {
+ title: 'Group 1',
+ type: 'string_options',
+ filters: [
+ {
+ name: 'filter1',
+ label: 'Show filter 1',
+ description: 'Description of Filter 1 in Group 1',
+ subset: [ 'filter2', 'filter5' ]
+ },
+ {
+ name: 'filter2',
+ label: 'Show filter 2',
+ description: 'Description of Filter 2 in Group 1'
+ },
+ {
+ name: 'filter3',
+ label: 'Show filter 3',
+ description: 'Description of Filter 3 in Group 1'
+ }
+ ]
+ },
+ group2: {
+ title: 'Group 2',
+ type: 'send_unselected_if_any',
+ filters: [
+ {
+ name: 'filter4',
+ label: 'Show filter 4',
+ description: 'Description of Filter 1 in Group 2',
+ subset: [ 'filter3', 'filter5' ]
+ },
+ {
+ name: 'filter5',
+ label: 'Show filter 5',
+ description: 'Description of Filter 2 in Group 2'
+ },
+ {
+ name: 'filter6',
+ label: 'Show filter 6',
+ description: 'Description of Filter 3 in Group 2'
+ }
+ ]
+ }
+ },
+ baseFullState = {
+ filter1: { selected: false, conflicted: false, included: false },
+ filter2: { selected: false, conflicted: false, included: false },
+ filter3: { selected: false, conflicted: false, included: false },
+ filter4: { selected: false, conflicted: false, included: false },
+ filter5: { selected: false, conflicted: false, included: false },
+ filter6: { selected: false, conflicted: false, included: false }
+ },
+ model = new mw.rcfilters.dm.FiltersViewModel();
+
+ model.initializeFilters( definition );
+ // Select a filter that has subset with another filter
+ model.updateFilters( {
+ filter1: true
+ } );
+
+ model.reassessFilterInteractions( model.getItemByName( 'filter1' ) );
+ assert.deepEqual(
+ model.getFullState(),
+ $.extend( true, {}, baseFullState, {
+ filter1: { selected: true },
+ filter2: { included: true },
+ filter5: { included: true }
+ } ),
+ 'Filters with subsets are represented in the model.'
+ );
+
+ // Select another filter that has a subset with the same previous filter
+ model.updateFilters( {
+ filter4: true
+ } );
+ model.reassessFilterInteractions( model.getItemByName( 'filter4' ) );
+ assert.deepEqual(
+ model.getFullState(),
+ $.extend( true, {}, baseFullState, {
+ filter1: { selected: true },
+ filter2: { included: true },
+ filter3: { included: true },
+ filter4: { selected: true },
+ filter5: { included: true }
+ } ),
+ 'Filters that have multiple subsets are represented.'
+ );
+
+ // Remove one filter (but leave the other) that affects filter2
+ model.updateFilters( {
+ filter1: false
+ } );
+ model.reassessFilterInteractions( model.getItemByName( 'filter1' ) );
+ assert.deepEqual(
+ model.getFullState(),
+ $.extend( true, {}, baseFullState, {
+ filter2: { included: false },
+ filter3: { included: true },
+ filter4: { selected: true },
+ filter5: { included: true }
+ } ),
+ 'Removing a filter only un-includes its subset if there is no other filter affecting.'
+ );
+
+ model.updateFilters( {
+ filter4: false
+ } );
+ model.reassessFilterInteractions( model.getItemByName( 'filter4' ) );
+ assert.deepEqual(
+ model.getFullState(),
+ baseFullState,
+ 'Removing all supersets also un-includes the subsets.'
+ );
+ } );
+
+ QUnit.test( 'Filter interaction: full coverage', function ( assert ) {
+ var definition = {
+ group1: {
+ title: 'Group 1',
+ type: 'string_options',
+ fullCoverage: false,
+ filters: [
+ { name: 'filter1' },
+ { name: 'filter2' },
+ { name: 'filter3' }
+ ]
+ },
+ group2: {
+ title: 'Group 2',
+ type: 'send_unselected_if_any',
+ fullCoverage: true,
+ filters: [
+ { name: 'filter4' },
+ { name: 'filter5' },
+ { name: 'filter6' }
+ ]
+ }
+ },
+ model = new mw.rcfilters.dm.FiltersViewModel(),
+ isCapsuleItemMuted = function ( filterName ) {
+ var itemModel = model.getItemByName( filterName ),
+ groupModel = itemModel.getGroupModel();
+
+ // This is the logic inside the capsule widget
+ return (
+ // The capsule item widget only appears if the item is selected
+ itemModel.isSelected() &&
+ // Muted state is only valid if group is full coverage and all items are selected
+ groupModel.isFullCoverage() && groupModel.areAllSelected()
+ );
+ },
+ getCurrentItemsMutedState = function () {
+ return {
+ filter1: isCapsuleItemMuted( 'filter1' ),
+ filter2: isCapsuleItemMuted( 'filter2' ),
+ filter3: isCapsuleItemMuted( 'filter3' ),
+ filter4: isCapsuleItemMuted( 'filter4' ),
+ filter5: isCapsuleItemMuted( 'filter5' ),
+ filter6: isCapsuleItemMuted( 'filter6' )
+ };
+ },
+ baseMuteState = {
+ filter1: false,
+ filter2: false,
+ filter3: false,
+ filter4: false,
+ filter5: false,
+ filter6: false
+ };
+
+ model.initializeFilters( definition );
+
+ // Starting state, no selection, all items are non-muted
+ assert.deepEqual(
+ getCurrentItemsMutedState(),
+ baseMuteState,
+ 'No selection - all items are non-muted'
+ );
+
+ // Select most (but not all) items in each group
+ model.updateFilters( {
+ filter1: true,
+ filter2: true,
+ filter4: true,
+ filter5: true
+ } );
+
+ // Both groups have multiple (but not all) items selected, all items are non-muted
+ assert.deepEqual(
+ getCurrentItemsMutedState(),
+ baseMuteState,
+ 'Not all items in the group selected - all items are non-muted'
+ );
+
+ // Select all items in 'fullCoverage' group (group2)
+ model.updateFilters( {
+ filter6: true
+ } );
+
+ // Group2 (full coverage) has all items selected, all its items are muted
+ assert.deepEqual(
+ getCurrentItemsMutedState(),
+ $.extend( {}, baseMuteState, {
+ filter4: true,
+ filter5: true,
+ filter6: true
+ } ),
+ 'All items in \'full coverage\' group are selected - all items in the group are muted'
+ );
+
+ // Select all items in non 'fullCoverage' group (group1)
+ model.updateFilters( {
+ filter3: true
+ } );
+
+ // Group1 (full coverage) has all items selected, no items in it are muted (non full coverage)
+ assert.deepEqual(
+ getCurrentItemsMutedState(),
+ $.extend( {}, baseMuteState, {
+ filter4: true,
+ filter5: true,
+ filter6: true
+ } ),
+ 'All items in a non \'full coverage\' group are selected - none of the items in the group are muted'
+ );
+
+ // Uncheck an item from each group
+ model.updateFilters( {
+ filter3: false,
+ filter5: false
+ } );
+ assert.deepEqual(
+ getCurrentItemsMutedState(),
+ baseMuteState,
+ 'Not all items in the group are checked - all items are non-muted regardless of group coverage'
+ );
+ } );
+
+ QUnit.test( 'Filter interaction: conflicts', function ( assert ) {
+ var definition = {
+ group1: {
+ title: 'Group 1',
+ type: 'string_options',
+ filters: [
+ {
+ name: 'filter1',
+ conflicts: [ 'filter2', 'filter4' ]
+ },
+ {
+ name: 'filter2',
+ conflicts: [ 'filter6' ]
+ },
+ {
+ name: 'filter3'
+ }
+ ]
+ },
+ group2: {
+ title: 'Group 2',
+ type: 'send_unselected_if_any',
+ filters: [
+ {
+ name: 'filter4'
+ },
+ {
+ name: 'filter5',
+ conflicts: [ 'filter3' ]
+ },
+ {
+ name: 'filter6'
+ }
+ ]
+ }
+ },
+ baseFullState = {
+ filter1: { selected: false, conflicted: false, included: false },
+ filter2: { selected: false, conflicted: false, included: false },
+ filter3: { selected: false, conflicted: false, included: false },
+ filter4: { selected: false, conflicted: false, included: false },
+ filter5: { selected: false, conflicted: false, included: false },
+ filter6: { selected: false, conflicted: false, included: false }
+ },
+ model = new mw.rcfilters.dm.FiltersViewModel();
+
+ model.initializeFilters( definition );
+
+ assert.deepEqual(
+ model.getFullState(),
+ baseFullState,
+ 'Initial state: no conflicts because no selections.'
+ );
+
+ // Select a filter that has a conflict with another
+ model.updateFilters( {
+ filter1: true // conflicts: filter2, filter4
+ } );
+
+ model.reassessFilterInteractions( model.getItemByName( 'filter1' ) );
+
+ assert.deepEqual(
+ model.getFullState(),
+ $.extend( true, {}, baseFullState, {
+ filter1: { selected: true },
+ filter2: { conflicted: true },
+ filter4: { conflicted: true }
+ } ),
+ 'Selecting a filter set its conflicts list as "conflicted".'
+ );
+
+ // Select one of the conflicts (both filters are now conflicted and selected)
+ model.updateFilters( {
+ filter4: true // conflicts: filter 1
+ } );
+ model.reassessFilterInteractions( model.getItemByName( 'filter4' ) );
+
+ assert.deepEqual(
+ model.getFullState(),
+ $.extend( true, {}, baseFullState, {
+ filter1: { selected: true, conflicted: true },
+ filter2: { conflicted: true },
+ filter4: { selected: true, conflicted: true }
+ } ),
+ 'Selecting a conflicting filter sets both sides to conflicted and selected.'
+ );
+
+ // Select another filter from filter4 group, meaning:
+ // now filter1 no longer conflicts with filter4
+ model.updateFilters( {
+ filter6: true // conflicts: filter2
+ } );
+ model.reassessFilterInteractions( model.getItemByName( 'filter6' ) );
+
+ assert.deepEqual(
+ model.getFullState(),
+ $.extend( true, {}, baseFullState, {
+ filter1: { selected: true, conflicted: false }, // No longer conflicts (filter4 is not the only in the group)
+ filter2: { conflicted: true }, // While not selected, still in conflict with filter1, which is selected
+ filter4: { selected: true, conflicted: false }, // No longer conflicts with filter1
+ filter6: { selected: true, conflicted: false }
+ } ),
+ 'Selecting a non-conflicting filter from a conflicting group removes the conflict'
+ );
+ } );
+
+ QUnit.test( 'Filter highlights', function ( assert ) {
+ var definition = {
+ group1: {
+ title: 'Group 1',
+ type: 'string_options',
+ filters: [
+ { name: 'filter1', class: 'class1' },
+ { name: 'filter2', class: 'class2' },
+ { name: 'filter3', class: 'class3' },
+ { name: 'filter4', class: 'class4' },
+ { name: 'filter5', class: 'class5' },
+ { name: 'filter6' }
+ ]
+ }
+ },
+ model = new mw.rcfilters.dm.FiltersViewModel();
+
+ model.initializeFilters( definition );
+
+ assert.ok(
+ !model.isHighlightEnabled(),
+ 'Initially, highlight is disabled.'
+ );
+
+ model.toggleHighlight( true );
+ assert.ok(
+ model.isHighlightEnabled(),
+ 'Highlight is enabled on toggle.'
+ );
+
+ model.setHighlightColor( 'filter1', 'color1' );
+ model.setHighlightColor( 'filter2', 'color2' );
+
+ assert.deepEqual(
+ model.getHighlightedItems().map( function ( item ) {
+ return item.getName();
+ } ),
+ [
+ 'filter1',
+ 'filter2'
+ ],
+ 'Highlighted items are highlighted.'
+ );
+
+ assert.equal(
+ model.getItemByName( 'filter1' ).getHighlightColor(),
+ 'color1',
+ 'Item highlight color is set.'
+ );
+
+ model.setHighlightColor( 'filter1', 'color1changed' );
+ assert.equal(
+ model.getItemByName( 'filter1' ).getHighlightColor(),
+ 'color1changed',
+ 'Item highlight color is changed on setHighlightColor.'
+ );
+
+ model.clearHighlightColor( 'filter1' );
+ assert.deepEqual(
+ model.getHighlightedItems().map( function ( item ) {
+ return item.getName();
+ } ),
+ [
+ 'filter2'
+ ],
+ 'Clear highlight from an item results in the item no longer being highlighted.'
+ );
+
+ // Reset
+ model = new mw.rcfilters.dm.FiltersViewModel();
+ model.initializeFilters( definition );
+
+ model.setHighlightColor( 'filter1', 'color1' );
+ model.setHighlightColor( 'filter2', 'color2' );
+ model.setHighlightColor( 'filter3', 'color3' );
+
+ assert.deepEqual(
+ model.getHighlightedItems().map( function ( item ) {
+ return item.getName();
+ } ),
+ [
+ 'filter1',
+ 'filter2',
+ 'filter3'
+ ],
+ 'Even if highlights are not enabled, the items remember their highlight state'
+ // NOTE: When actually displaying the highlights, the UI checks whether
+ // highlighting is generally active and then goes over the highlighted
+ // items. The item models, however, and the view model in general, still
+ // retains the knowledge about which filters have different colors, so we
+ // can seamlessly return to the colors the user previously chose if they
+ // reapply highlights.
+ );
+
+ // Reset
+ model = new mw.rcfilters.dm.FiltersViewModel();
+ model.initializeFilters( definition );
+
+ model.setHighlightColor( 'filter1', 'color1' );
+ model.setHighlightColor( 'filter6', 'color6' );
+
+ assert.deepEqual(
+ model.getHighlightedItems().map( function ( item ) {
+ return item.getName();
+ } ),
+ [
+ 'filter1'
+ ],
+ 'Items without a specified class identifier are not highlighted.'
+ );
+ } );
}( mediaWiki, jQuery ) );