Merge "Type hint against LinkTarget in WatchedItemStore"
[lhc/web/wiklou.git] / tests / qunit / suites / resources / jquery / jquery.makeCollapsible.test.js
1 ( function () {
2 var loremIpsum = 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.';
3
4 QUnit.module( 'jquery.makeCollapsible', QUnit.newMwEnvironment() );
5
6 function prepareCollapsible( html, options ) {
7 return $( $.parseHTML( html ) )
8 .appendTo( '#qunit-fixture' )
9 // options might be undefined here - this is okay
10 .makeCollapsible( options );
11 }
12
13 // This test is first because if it fails, then almost all of the latter tests are meaningless.
14 QUnit.test( 'testing hooks/triggers', function ( assert ) {
15 var $collapsible = prepareCollapsible(
16 '<div class="mw-collapsible">' + loremIpsum + '</div>'
17 ),
18 $content = $collapsible.find( '.mw-collapsible-content' ),
19 $toggle = $collapsible.find( '.mw-collapsible-toggle' );
20
21 // In one full collapse-expand cycle, each event will be fired once
22
23 // On collapse...
24 $collapsible.on( 'beforeCollapse.mw-collapsible', function () {
25 assert.assertTrue( $content.css( 'display' ) !== 'none', 'first beforeCollapseExpand: content is visible' );
26 } );
27 $collapsible.on( 'afterCollapse.mw-collapsible', function () {
28 assert.assertTrue( $content.css( 'display' ) === 'none', 'first afterCollapseExpand: content is hidden' );
29
30 // On expand...
31 $collapsible.on( 'beforeExpand.mw-collapsible', function () {
32 assert.assertTrue( $content.css( 'display' ) === 'none', 'second beforeCollapseExpand: content is hidden' );
33 } );
34 $collapsible.on( 'afterExpand.mw-collapsible', function () {
35 assert.assertTrue( $content.css( 'display' ) !== 'none', 'second afterCollapseExpand: content is visible' );
36 } );
37
38 // ...expanding happens here
39 $toggle.trigger( 'click' );
40 } );
41
42 // ...collapsing happens here
43 $toggle.trigger( 'click' );
44 } );
45
46 QUnit.test( 'basic operation (<div>)', function ( assert ) {
47 var $collapsible = prepareCollapsible(
48 '<div class="mw-collapsible">' + loremIpsum + '</div>'
49 ),
50 $content = $collapsible.find( '.mw-collapsible-content' ),
51 $toggle = $collapsible.find( '.mw-collapsible-toggle' );
52
53 assert.strictEqual( $content.length, 1, 'content is present' );
54 assert.strictEqual( $content.find( $toggle ).length, 0, 'toggle is not a descendant of content' );
55
56 assert.assertTrue( $content.css( 'display' ) !== 'none', 'content is visible' );
57
58 $collapsible.on( 'afterCollapse.mw-collapsible', function () {
59 assert.assertTrue( $content.css( 'display' ) === 'none', 'after collapsing: content is hidden' );
60
61 $collapsible.on( 'afterExpand.mw-collapsible', function () {
62 assert.assertTrue( $content.css( 'display' ) !== 'none', 'after expanding: content is visible' );
63 } );
64
65 $toggle.trigger( 'click' );
66 } );
67
68 $toggle.trigger( 'click' );
69 } );
70
71 QUnit.test( 'basic operation (<table>)', function ( assert ) {
72 var $collapsible = prepareCollapsible(
73 '<table class="mw-collapsible">' +
74 '<tr><td>' + loremIpsum + '</td><td>' + loremIpsum + '</td></tr>' +
75 '<tr><td>' + loremIpsum + '</td><td>' + loremIpsum + '</td></tr>' +
76 '<tr><td>' + loremIpsum + '</td><td>' + loremIpsum + '</td></tr>' +
77 '</table>'
78 ),
79 $headerRow = $collapsible.find( 'tr' ).first(),
80 $contentRow = $collapsible.find( 'tr' ).last(),
81 $toggle = $headerRow.find( 'td' ).last().find( '.mw-collapsible-toggle' );
82
83 assert.strictEqual( $toggle.length, 1, 'toggle is added to last cell of first row' );
84
85 assert.assertTrue( $headerRow.css( 'display' ) !== 'none', 'headerRow is visible' );
86 assert.assertTrue( $contentRow.css( 'display' ) !== 'none', 'contentRow is visible' );
87
88 $collapsible.on( 'afterCollapse.mw-collapsible', function () {
89 assert.assertTrue( $headerRow.css( 'display' ) !== 'none', 'after collapsing: headerRow is still visible' );
90 assert.assertTrue( $contentRow.css( 'display' ) === 'none', 'after collapsing: contentRow is hidden' );
91
92 $collapsible.on( 'afterExpand.mw-collapsible', function () {
93 assert.assertTrue( $headerRow.css( 'display' ) !== 'none', 'after expanding: headerRow is still visible' );
94 assert.assertTrue( $contentRow.css( 'display' ) !== 'none', 'after expanding: contentRow is visible' );
95 } );
96
97 $toggle.trigger( 'click' );
98 } );
99
100 $toggle.trigger( 'click' );
101 } );
102
103 function tableWithCaptionTest( $collapsible, test, assert ) {
104 var $caption = $collapsible.find( 'caption' ),
105 $headerRow = $collapsible.find( 'tr' ).first(),
106 $contentRow = $collapsible.find( 'tr' ).last(),
107 $toggle = $caption.find( '.mw-collapsible-toggle' );
108
109 assert.strictEqual( $toggle.length, 1, 'toggle is added to the end of the caption' );
110
111 assert.assertTrue( $caption.css( 'display' ) !== 'none', 'caption is visible' );
112 assert.assertTrue( $headerRow.css( 'display' ) !== 'none', 'headerRow is visible' );
113 assert.assertTrue( $contentRow.css( 'display' ) !== 'none', 'contentRow is visible' );
114
115 $collapsible.on( 'afterCollapse.mw-collapsible', function () {
116 assert.assertTrue( $caption.css( 'display' ) !== 'none', 'after collapsing: caption is still visible' );
117 assert.assertTrue( $headerRow.css( 'display' ) === 'none', 'after collapsing: headerRow is hidden' );
118 assert.assertTrue( $contentRow.css( 'display' ) === 'none', 'after collapsing: contentRow is hidden' );
119
120 $collapsible.on( 'afterExpand.mw-collapsible', function () {
121 assert.assertTrue( $caption.css( 'display' ) !== 'none', 'after expanding: caption is still visible' );
122 assert.assertTrue( $headerRow.css( 'display' ) !== 'none', 'after expanding: headerRow is visible' );
123 assert.assertTrue( $contentRow.css( 'display' ) !== 'none', 'after expanding: contentRow is visible' );
124 } );
125
126 $toggle.trigger( 'click' );
127 } );
128
129 $toggle.trigger( 'click' );
130 }
131
132 QUnit.test( 'basic operation (<table> with caption)', function ( assert ) {
133 tableWithCaptionTest( prepareCollapsible(
134 '<table class="mw-collapsible">' +
135 '<caption>' + loremIpsum + '</caption>' +
136 '<tr><th>' + loremIpsum + '</th><th>' + loremIpsum + '</th></tr>' +
137 '<tr><td>' + loremIpsum + '</td><td>' + loremIpsum + '</td></tr>' +
138 '<tr><td>' + loremIpsum + '</td><td>' + loremIpsum + '</td></tr>' +
139 '</table>'
140 ), this, assert );
141 } );
142
143 QUnit.test( 'basic operation (<table> with caption and <thead>)', function ( assert ) {
144 tableWithCaptionTest( prepareCollapsible(
145 '<table class="mw-collapsible">' +
146 '<caption>' + loremIpsum + '</caption>' +
147 '<thead><tr><th>' + loremIpsum + '</th><th>' + loremIpsum + '</th></tr></thead>' +
148 '<tr><td>' + loremIpsum + '</td><td>' + loremIpsum + '</td></tr>' +
149 '<tr><td>' + loremIpsum + '</td><td>' + loremIpsum + '</td></tr>' +
150 '</table>'
151 ), this, assert );
152 } );
153
154 function listTest( listType, test, assert ) {
155 var $collapsible = prepareCollapsible(
156 '<' + listType + ' class="mw-collapsible">' +
157 '<li>' + loremIpsum + '</li>' +
158 '<li>' + loremIpsum + '</li>' +
159 '</' + listType + '>'
160 ),
161 $toggleItem = $collapsible.find( 'li.mw-collapsible-toggle-li:first-child' ),
162 $contentItem = $collapsible.find( 'li' ).last(),
163 $toggle = $toggleItem.find( '.mw-collapsible-toggle' );
164
165 assert.strictEqual( $toggle.length, 1, 'toggle is present, added inside new zeroth list item' );
166
167 assert.assertTrue( $toggleItem.css( 'display' ) !== 'none', 'toggleItem is visible' );
168 assert.assertTrue( $contentItem.css( 'display' ) !== 'none', 'contentItem is visible' );
169
170 $collapsible.on( 'afterCollapse.mw-collapsible', function () {
171 assert.assertTrue( $toggleItem.css( 'display' ) !== 'none', 'after collapsing: toggleItem is still visible' );
172 assert.assertTrue( $contentItem.css( 'display' ) === 'none', 'after collapsing: contentItem is hidden' );
173
174 $collapsible.on( 'afterExpand.mw-collapsible', function () {
175 assert.assertTrue( $toggleItem.css( 'display' ) !== 'none', 'after expanding: toggleItem is still visible' );
176 assert.assertTrue( $contentItem.css( 'display' ) !== 'none', 'after expanding: contentItem is visible' );
177 } );
178
179 $toggle.trigger( 'click' );
180 } );
181
182 $toggle.trigger( 'click' );
183 }
184
185 QUnit.test( 'basic operation (<ul>)', function ( assert ) {
186 listTest( 'ul', this, assert );
187 } );
188
189 QUnit.test( 'basic operation (<ol>)', function ( assert ) {
190 listTest( 'ol', this, assert );
191 } );
192
193 QUnit.test( 'basic operation when synchronous (options.instantHide)', function ( assert ) {
194 var $collapsible = prepareCollapsible(
195 '<div class="mw-collapsible">' + loremIpsum + '</div>',
196 { instantHide: true }
197 ),
198 $content = $collapsible.find( '.mw-collapsible-content' );
199
200 assert.assertTrue( $content.css( 'display' ) !== 'none', 'content is visible' );
201
202 $collapsible.find( '.mw-collapsible-toggle' ).trigger( 'click' );
203
204 assert.assertTrue( $content.css( 'display' ) === 'none', 'after collapsing: content is hidden' );
205 } );
206
207 QUnit.test( 'mw-made-collapsible data added', function ( assert ) {
208 var $collapsible = prepareCollapsible(
209 '<div>' + loremIpsum + '</div>'
210 );
211
212 assert.strictEqual( $collapsible.data( 'mw-made-collapsible' ), true, 'mw-made-collapsible data present' );
213 } );
214
215 QUnit.test( 'mw-collapsible added when missing', function ( assert ) {
216 var $collapsible = prepareCollapsible(
217 '<div>' + loremIpsum + '</div>'
218 );
219
220 // eslint-disable-next-line no-jquery/no-class-state
221 assert.assertTrue( $collapsible.hasClass( 'mw-collapsible' ), 'mw-collapsible class present' );
222 } );
223
224 QUnit.test( 'mw-collapsed added when missing', function ( assert ) {
225 var $collapsible = prepareCollapsible(
226 '<div>' + loremIpsum + '</div>',
227 { collapsed: true }
228 );
229
230 // eslint-disable-next-line no-jquery/no-class-state
231 assert.assertTrue( $collapsible.hasClass( 'mw-collapsed' ), 'mw-collapsed class present' );
232 } );
233
234 QUnit.test( 'initial collapse (mw-collapsed class)', function ( assert ) {
235 var $collapsible = prepareCollapsible(
236 '<div class="mw-collapsible mw-collapsed">' + loremIpsum + '</div>'
237 ),
238 $content = $collapsible.find( '.mw-collapsible-content' );
239
240 // Synchronous - mw-collapsed should cause instantHide: true to be used on initial collapsing
241 assert.assertTrue( $content.css( 'display' ) === 'none', 'content is hidden' );
242
243 $collapsible.on( 'afterExpand.mw-collapsible', function () {
244 assert.assertTrue( $content.css( 'display' ) !== 'none', 'after expanding: content is visible' );
245 } );
246
247 $collapsible.find( '.mw-collapsible-toggle' ).trigger( 'click' );
248 } );
249
250 QUnit.test( 'initial collapse (options.collapsed)', function ( assert ) {
251 var $collapsible = prepareCollapsible(
252 '<div class="mw-collapsible">' + loremIpsum + '</div>',
253 { collapsed: true }
254 ),
255 $content = $collapsible.find( '.mw-collapsible-content' );
256
257 // Synchronous - collapsed: true should cause instantHide: true to be used on initial collapsing
258 assert.assertTrue( $content.css( 'display' ) === 'none', 'content is hidden' );
259
260 $collapsible.on( 'afterExpand.mw-collapsible', function () {
261 assert.assertTrue( $content.css( 'display' ) !== 'none', 'after expanding: content is visible' );
262 } );
263
264 $collapsible.find( '.mw-collapsible-toggle' ).trigger( 'click' );
265 } );
266
267 QUnit.test( 'clicks on links inside toggler pass through', function ( assert ) {
268 var $collapsible = prepareCollapsible(
269 '<div class="mw-collapsible">' +
270 '<div class="mw-collapsible-toggle">' +
271 'Toggle <a href="#top">toggle</a> toggle <b>toggle</b>' +
272 '</div>' +
273 '<div class="mw-collapsible-content">' + loremIpsum + '</div>' +
274 '</div>',
275 // Can't do asynchronous because we're testing that the event *doesn't* happen
276 { instantHide: true }
277 ),
278 $content = $collapsible.find( '.mw-collapsible-content' );
279
280 $collapsible.find( '.mw-collapsible-toggle a' ).trigger( 'click' );
281 assert.assertTrue( $content.css( 'display' ) !== 'none', 'click event on link inside toggle passes through (content not toggled)' );
282
283 $collapsible.find( '.mw-collapsible-toggle b' ).trigger( 'click' );
284 assert.assertTrue( $content.css( 'display' ) === 'none', 'click event on non-link inside toggle toggles content' );
285 } );
286
287 QUnit.test( 'click on non-link inside toggler counts as trigger', function ( assert ) {
288 var $collapsible = prepareCollapsible(
289 '<div class="mw-collapsible">' +
290 '<div class="mw-collapsible-toggle">' +
291 'Toggle <a>toggle</a> toggle <b>toggle</b>' +
292 '</div>' +
293 '<div class="mw-collapsible-content">' + loremIpsum + '</div>' +
294 '</div>',
295 { instantHide: true }
296 ),
297 $content = $collapsible.find( '.mw-collapsible-content' );
298
299 $collapsible.find( '.mw-collapsible-toggle a' ).trigger( 'click' );
300 assert.assertTrue( $content.css( 'display' ) === 'none', 'click event on link (with no href) inside toggle toggles content' );
301 } );
302
303 QUnit.test( 'collapse/expand text (data-collapsetext, data-expandtext)', function ( assert ) {
304 var $collapsible = prepareCollapsible(
305 '<div class="mw-collapsible" data-collapsetext="Collapse me!" data-expandtext="Expand me!">' +
306 loremIpsum +
307 '</div>'
308 ),
309 $toggleText = $collapsible.find( '.mw-collapsible-text' );
310
311 assert.strictEqual( $toggleText.text(), 'Collapse me!', 'data-collapsetext is respected' );
312
313 $collapsible.on( 'afterCollapse.mw-collapsible', function () {
314 assert.strictEqual( $toggleText.text(), 'Expand me!', 'data-expandtext is respected' );
315 } );
316
317 $collapsible.find( '.mw-collapsible-toggle' ).trigger( 'click' );
318 } );
319
320 QUnit.test( 'collapse/expand text (options.collapseText, options.expandText)', function ( assert ) {
321 var $collapsible = prepareCollapsible(
322 '<div class="mw-collapsible">' + loremIpsum + '</div>',
323 { collapseText: 'Collapse me!', expandText: 'Expand me!' }
324 ),
325 $toggleText = $collapsible.find( '.mw-collapsible-text' );
326
327 assert.strictEqual( $toggleText.text(), 'Collapse me!', 'options.collapseText is respected' );
328
329 $collapsible.on( 'afterCollapse.mw-collapsible', function () {
330 assert.strictEqual( $toggleText.text(), 'Expand me!', 'options.expandText is respected' );
331 } );
332
333 $collapsible.find( '.mw-collapsible-toggle' ).trigger( 'click' );
334 } );
335
336 QUnit.test( 'predefined toggle button and text (.mw-collapsible-toggle/.mw-collapsible-text)', function ( assert ) {
337 var $collapsible = prepareCollapsible(
338 '<div class="mw-collapsible">' +
339 '<div class="mw-collapsible-toggle">' +
340 '<span>[</span><span class="mw-collapsible-text">Toggle</span><span>]</span>' +
341 '</div>' +
342 '<div class="mw-collapsible-content">' + loremIpsum + '</div>' +
343 '</div>',
344 { collapseText: 'Hide', expandText: 'Show' }
345 ),
346 $toggleText = $collapsible.find( '.mw-collapsible-text' );
347
348 assert.strictEqual( $toggleText.text(), 'Toggle', 'predefined text remains' );
349
350 $collapsible.on( 'afterCollapse.mw-collapsible', function () {
351 assert.strictEqual( $toggleText.text(), 'Show', 'predefined text is toggled' );
352
353 $collapsible.on( 'afterExpand.mw-collapsible', function () {
354 assert.strictEqual( $toggleText.text(), 'Hide', 'predefined text is toggled back' );
355 } );
356
357 $collapsible.find( '.mw-collapsible-toggle' ).trigger( 'click' );
358 } );
359
360 $collapsible.find( '.mw-collapsible-toggle' ).trigger( 'click' );
361 } );
362
363 QUnit.test( 'cloned collapsibles can be made collapsible again', function ( assert ) {
364 var $collapsible = prepareCollapsible(
365 '<div class="mw-collapsible">' + loremIpsum + '</div>'
366 ),
367 $clone = $collapsible.clone() // clone without data and events
368 .appendTo( '#qunit-fixture' ).makeCollapsible(),
369 $content = $clone.find( '.mw-collapsible-content' );
370
371 assert.assertTrue( $content.css( 'display' ) !== 'none', 'content is visible' );
372
373 $clone.on( 'afterCollapse.mw-collapsible', function () {
374 assert.assertTrue( $content.css( 'display' ) === 'none', 'after collapsing: content is hidden' );
375 } );
376
377 $clone.find( '.mw-collapsible-toggle a' ).trigger( 'click' );
378 } );
379
380 QUnit.test( 'T168689 - nested collapsible divs should keep independent state', function ( assert ) {
381 var $collapsible1 = prepareCollapsible(
382 '<div class="mw-collapsible">' + loremIpsum + '</div>'
383 ),
384 $collapsible2 = prepareCollapsible(
385 '<div class="mw-collapsible">' + loremIpsum + '</div>'
386 );
387
388 $collapsible1
389 .append( $collapsible2 )
390 .appendTo( '#qunit-fixture' ).makeCollapsible();
391
392 $collapsible1.on( 'afterCollapse.mw-collapsible', function () {
393 // eslint-disable-next-line no-jquery/no-class-state
394 assert.assertTrue( $collapsible1.hasClass( 'mw-collapsed' ), 'after collapsing: parent is collapsed' );
395 // eslint-disable-next-line no-jquery/no-class-state
396 assert.assertFalse( $collapsible2.hasClass( 'mw-collapsed' ), 'after collapsing: child is not collapsed' );
397 // eslint-disable-next-line no-jquery/no-class-state
398 assert.assertTrue( $collapsible1.find( '> .mw-collapsible-toggle' ).hasClass( 'mw-collapsible-toggle-collapsed' ) );
399 // eslint-disable-next-line no-jquery/no-class-state
400 assert.assertFalse( $collapsible2.find( '> .mw-collapsible-toggle' ).hasClass( 'mw-collapsible-toggle-collapsed' ) );
401 } ).find( '> .mw-collapsible-toggle a' ).trigger( 'click' );
402 } );
403 }() );