Merge "RCFilters: refactor highlight state"
[lhc/web/wiklou.git] / tests / qunit / suites / resources / mediawiki.rcfilters / dm.SavedQueriesModel.test.js
1 /* eslint-disable camelcase */
2 ( function ( mw ) {
3 var filterDefinition = [ {
4 name: 'group1',
5 type: 'send_unselected_if_any',
6 filters: [
7 // Note: The fact filter2 is default means that in the
8 // filter representation, filter1 and filter3 are 'true'
9 { name: 'filter1', cssClass: 'filter1class' },
10 { name: 'filter2', cssClass: 'filter2class', default: true },
11 { name: 'filter3', cssClass: 'filter3class' }
12 ]
13 }, {
14 name: 'group2',
15 type: 'string_options',
16 separator: ',',
17 filters: [
18 { name: 'filter4', cssClass: 'filter4class' },
19 { name: 'filter5' }, // NOTE: Not supporting highlights!
20 { name: 'filter6', cssClass: 'filter6class' }
21 ]
22 }, {
23 name: 'group3',
24 type: 'boolean',
25 isSticky: true,
26 filters: [
27 { name: 'group3option1', cssClass: 'filter1class' },
28 { name: 'group3option2', cssClass: 'filter1class' },
29 { name: 'group3option3', cssClass: 'filter1class' }
30 ]
31 }, {
32 // Copy of the way the controller defines invert
33 // to check whether the conversion works
34 name: 'invertGroup',
35 type: 'boolean',
36 hidden: true,
37 filters: [ {
38 name: 'invert',
39 default: '0'
40 } ]
41 } ],
42 queriesFilterRepresentation = {
43 queries: {
44 1234: {
45 label: 'Item converted',
46 data: {
47 filters: {
48 // - This value is true, but the original filter-representation
49 // of the saved queries ran against defaults. Since filter1 was
50 // set as default in the definition, the value would actually
51 // not appear in the representation itself.
52 // It is considered 'true', though, and should appear in the
53 // converted result in its parameter representation.
54 // >> group1__filter1: true,
55 // - The reverse is true for filter3. Filter3 is set as default
56 // but we don't want it in this representation of the saved query.
57 // Since the filter representation ran against default values,
58 // it will appear as 'false' value in this representation explicitly
59 // and the resulting parameter representation should have that
60 // as the result as well
61 group1__filter3: false,
62 group2__filter4: true,
63 group3__group3option1: true
64 },
65 highlights: {
66 highlight: true,
67 group1__filter1: 'c5',
68 group3__group3option1: 'c1'
69 },
70 invert: true
71 }
72 }
73 }
74 },
75 queriesParamRepresentation = {
76 version: '2',
77 queries: {
78 1234: {
79 label: 'Item converted',
80 data: {
81 params: {
82 // filter1 is 'true' so filter2 and filter3 are both '1'
83 // in param representation
84 filter2: '1', filter3: '1',
85 // Group type string_options
86 group2: 'filter4'
87 // Note - Group3 is sticky, so it won't show in output
88 },
89 highlights: {
90 group1__filter1_color: 'c5',
91 group3__group3option1_color: 'c1'
92 }
93 }
94 }
95 }
96 },
97 removeHighlights = function ( data ) {
98 var copy = $.extend( true, {}, data );
99 copy.queries[ 1234 ].data.highlights = {};
100 return copy;
101 };
102
103 QUnit.module( 'mediawiki.rcfilters - SavedQueriesModel' );
104
105 QUnit.test( 'Initializing queries', function ( assert ) {
106 var filtersModel = new mw.rcfilters.dm.FiltersViewModel(),
107 queriesModel = new mw.rcfilters.dm.SavedQueriesModel( filtersModel ),
108 exampleQueryStructure = {
109 version: '2',
110 default: '1234',
111 queries: {
112 1234: {
113 label: 'Query 1234',
114 data: {
115 params: {
116 filter2: '1'
117 },
118 highlights: {
119 group1__filter3_color: 'c2'
120 }
121 }
122 }
123 }
124 },
125 cases = [
126 {
127 input: {},
128 finalState: { version: '2', queries: {} },
129 msg: 'Empty initial query structure results in base saved queries structure.'
130 },
131 {
132 input: $.extend( true, {}, exampleQueryStructure ),
133 finalState: $.extend( true, {}, exampleQueryStructure ),
134 msg: 'Initialization of given query structure does not corrupt the structure.'
135 },
136 {
137 // Converting from old structure
138 input: $.extend( true, {}, queriesFilterRepresentation ),
139 finalState: $.extend( true, {}, queriesParamRepresentation ),
140 msg: 'Conversion from filter representation to parameters retains data.'
141 },
142 {
143 // Converting from old structure
144 input: $.extend( true, {}, queriesFilterRepresentation, { queries: { 1234: { data: {
145 filters: {
146 // Entire group true: normalize params
147 filter1: true,
148 filter2: true,
149 filter3: true
150 },
151 highlights: {
152 filter3: null // Get rid of empty highlight
153 }
154 } } } } ),
155 finalState: $.extend( true, {}, queriesParamRepresentation ),
156 msg: 'Conversion from filter representation to parameters normalizes params and highlights.'
157 },
158 {
159 // Converting from old structure with default
160 input: $.extend( true, { default: '1234' }, queriesFilterRepresentation ),
161 finalState: $.extend( true, { default: '1234' }, queriesParamRepresentation ),
162 msg: 'Conversion from filter representation to parameters, with default set up, retains data.'
163 },
164 {
165 // Converting from old structure and cleaning up highlights
166 input: $.extend( true, queriesFilterRepresentation, { queries: { 1234: { data: { highlights: { highlight: false } } } } } ),
167 finalState: removeHighlights( queriesParamRepresentation ),
168 msg: 'Conversion from filter representation to parameters and highlight cleanup'
169 },
170 {
171 // New structure
172 input: $.extend( true, {}, queriesParamRepresentation ),
173 finalState: $.extend( true, {}, queriesParamRepresentation ),
174 msg: 'Parameter representation retains its queries structure'
175 },
176 {
177 // Do not touch invalid color parameters from the initialization routine
178 // (Normalization, or "fixing" the query should only happen when we add new query or actively convert queries)
179 input: $.extend( true, { queries: { 1234: { data: { highlights: { group2__filter5_color: 'c2' } } } } }, exampleQueryStructure ),
180 finalState: $.extend( true, { queries: { 1234: { data: { highlights: { group2__filter5_color: 'c2' } } } } }, exampleQueryStructure ),
181 msg: 'Structure that contains invalid highlights remains the same in initialization'
182 },
183 {
184 // Trim colors when highlight=false is stored
185 input: $.extend( true, { queries: { 1234: { data: { params: { highlight: '0' } } } } }, queriesParamRepresentation ),
186 finalState: removeHighlights( queriesParamRepresentation ),
187 msg: 'Colors are removed when highlight=false'
188 },
189 {
190 // Remove highlight when it is true but no colors are specified
191 input: $.extend( true, { queries: { 1234: { data: { params: { highlight: '1' } } } } }, removeHighlights( queriesParamRepresentation ) ),
192 finalState: removeHighlights( queriesParamRepresentation ),
193 msg: 'remove highlight when it is true but there is no colors'
194 }
195 ];
196
197 filtersModel.initializeFilters( filterDefinition );
198
199 cases.forEach( function ( testCase ) {
200 queriesModel.initialize( testCase.input );
201 assert.deepEqual(
202 queriesModel.getState(),
203 testCase.finalState,
204 testCase.msg
205 );
206 } );
207 } );
208
209 QUnit.test( 'Adding new queries', function ( assert ) {
210 var filtersModel = new mw.rcfilters.dm.FiltersViewModel(),
211 queriesModel = new mw.rcfilters.dm.SavedQueriesModel( filtersModel ),
212 cases = [
213 {
214 methodParams: [
215 'label1', // Label
216 { // Data
217 filter1: '1',
218 filter2: '2',
219 group1__filter1_color: 'c2',
220 group1__filter3_color: 'c5'
221 },
222 true, // isDefault
223 '1234' // ID
224 ],
225 result: {
226 itemState: {
227 label: 'label1',
228 data: {
229 params: {
230 filter1: '1',
231 filter2: '2'
232 },
233 highlights: {
234 group1__filter1_color: 'c2',
235 group1__filter3_color: 'c5'
236 }
237 }
238 },
239 isDefault: true,
240 id: '1234'
241 },
242 msg: 'Given valid data is preserved.'
243 },
244 {
245 methodParams: [
246 'label2',
247 {
248 filter1: '1',
249 invert: '1',
250 filter15: '1', // Invalid filter - removed
251 filter2: '0', // Falsey value - removed
252 group1__filter1_color: 'c3',
253 foobar: 'w00t' // Unrecognized parameter - removed
254 }
255 ],
256 result: {
257 itemState: {
258 label: 'label2',
259 data: {
260 params: {
261 filter1: '1',
262 invert: '1'
263 },
264 highlights: {
265 group1__filter1_color: 'c3'
266 }
267 }
268 },
269 isDefault: false
270 },
271 msg: 'Given data with invalid filters and highlights is normalized'
272 }
273 ];
274
275 filtersModel.initializeFilters( filterDefinition );
276
277 // Start with an empty saved queries model
278 queriesModel.initialize( {} );
279
280 cases.forEach( function ( testCase ) {
281 var itemID = queriesModel.addNewQuery.apply( queriesModel, testCase.methodParams ),
282 item = queriesModel.getItemByID( itemID );
283
284 assert.deepEqual(
285 item.getState(),
286 testCase.result.itemState,
287 testCase.msg + ' (itemState)'
288 );
289
290 assert.equal(
291 item.isDefault(),
292 testCase.result.isDefault,
293 testCase.msg + ' (isDefault)'
294 );
295
296 if ( testCase.result.id !== undefined ) {
297 assert.equal(
298 item.getID(),
299 testCase.result.id,
300 testCase.msg + ' (item ID)'
301 );
302 }
303 } );
304 } );
305
306 QUnit.test( 'Manipulating queries', function ( assert ) {
307 var id1, id2, item1, matchingItem,
308 queriesStructure = {},
309 filtersModel = new mw.rcfilters.dm.FiltersViewModel(),
310 queriesModel = new mw.rcfilters.dm.SavedQueriesModel( filtersModel );
311
312 filtersModel.initializeFilters( filterDefinition );
313
314 // Start with an empty saved queries model
315 queriesModel.initialize( {} );
316
317 // Add items
318 id1 = queriesModel.addNewQuery(
319 'New query 1',
320 {
321 group2: 'filter5',
322 group1__filter1_color: 'c5',
323 group3__group3option1_color: 'c1'
324 }
325 );
326 id2 = queriesModel.addNewQuery(
327 'New query 2',
328 {
329 filter1: '1',
330 filter2: '1',
331 invert: '1'
332 }
333 );
334 item1 = queriesModel.getItemByID( id1 );
335
336 assert.equal(
337 item1.getID(),
338 id1,
339 'Item created and its data retained successfully'
340 );
341
342 // NOTE: All other methods that the item itself returns are
343 // tested in the dm.SavedQueryItemModel.test.js file
344
345 // Build the query structure we expect per item
346 queriesStructure[ id1 ] = {
347 label: 'New query 1',
348 data: {
349 params: {
350 group2: 'filter5'
351 },
352 highlights: {
353 group1__filter1_color: 'c5',
354 group3__group3option1_color: 'c1'
355 }
356 }
357 };
358 queriesStructure[ id2 ] = {
359 label: 'New query 2',
360 data: {
361 params: {
362 filter1: '1',
363 filter2: '1',
364 invert: '1'
365 },
366 highlights: {}
367 }
368 };
369
370 assert.deepEqual(
371 queriesModel.getState(),
372 {
373 version: '2',
374 queries: queriesStructure
375 },
376 'Full query represents current state of items'
377 );
378
379 // Add default
380 queriesModel.setDefault( id2 );
381
382 assert.deepEqual(
383 queriesModel.getState(),
384 {
385 version: '2',
386 default: id2,
387 queries: queriesStructure
388 },
389 'Setting default is reflected in queries state'
390 );
391
392 // Remove default
393 queriesModel.setDefault( null );
394
395 assert.deepEqual(
396 queriesModel.getState(),
397 {
398 version: '2',
399 queries: queriesStructure
400 },
401 'Removing default is reflected in queries state'
402 );
403
404 // Find matching query
405 matchingItem = queriesModel.findMatchingQuery(
406 {
407 group2: 'filter5',
408 group1__filter1_color: 'c5',
409 group3__group3option1_color: 'c1'
410 }
411 );
412 assert.deepEqual(
413 matchingItem.getID(),
414 id1,
415 'Finding matching item by identical state'
416 );
417
418 // Find matching query with 0-values (base state)
419 matchingItem = queriesModel.findMatchingQuery(
420 {
421 group2: 'filter5',
422 filter1: '0',
423 filter2: '0',
424 invert: '0',
425 group1__filter1_color: 'c5',
426 group3__group3option1_color: 'c1'
427 }
428 );
429 assert.deepEqual(
430 matchingItem.getID(),
431 id1,
432 'Finding matching item by "dirty" state with 0-base values'
433 );
434 } );
435 }( mediaWiki ) );