2 var loremIpsum
= 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.';
4 QUnit
.module( 'jquery.makeCollapsible', QUnit
.newMwEnvironment() );
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
);
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>'
18 $content
= $collapsible
.find( '.mw-collapsible-content' ),
19 $toggle
= $collapsible
.find( '.mw-collapsible-toggle' );
21 // In one full collapse-expand cycle, each event will be fired once
24 $collapsible
.on( 'beforeCollapse.mw-collapsible', function () {
25 assert
.assertTrue( $content
.css( 'display' ) !== 'none', 'first beforeCollapseExpand: content is visible' );
27 $collapsible
.on( 'afterCollapse.mw-collapsible', function () {
28 assert
.assertTrue( $content
.css( 'display' ) === 'none', 'first afterCollapseExpand: content is hidden' );
31 $collapsible
.on( 'beforeExpand.mw-collapsible', function () {
32 assert
.assertTrue( $content
.css( 'display' ) === 'none', 'second beforeCollapseExpand: content is hidden' );
34 $collapsible
.on( 'afterExpand.mw-collapsible', function () {
35 assert
.assertTrue( $content
.css( 'display' ) !== 'none', 'second afterCollapseExpand: content is visible' );
38 // ...expanding happens here
39 $toggle
.trigger( 'click' );
42 // ...collapsing happens here
43 $toggle
.trigger( 'click' );
46 QUnit
.test( 'basic operation (<div>)', function ( assert
) {
47 var $collapsible
= prepareCollapsible(
48 '<div class="mw-collapsible">' + loremIpsum
+ '</div>'
50 $content
= $collapsible
.find( '.mw-collapsible-content' ),
51 $toggle
= $collapsible
.find( '.mw-collapsible-toggle' );
53 assert
.strictEqual( $content
.length
, 1, 'content is present' );
54 assert
.strictEqual( $content
.find( $toggle
).length
, 0, 'toggle is not a descendant of content' );
56 assert
.assertTrue( $content
.css( 'display' ) !== 'none', 'content is visible' );
58 $collapsible
.on( 'afterCollapse.mw-collapsible', function () {
59 assert
.assertTrue( $content
.css( 'display' ) === 'none', 'after collapsing: content is hidden' );
61 $collapsible
.on( 'afterExpand.mw-collapsible', function () {
62 assert
.assertTrue( $content
.css( 'display' ) !== 'none', 'after expanding: content is visible' );
65 $toggle
.trigger( 'click' );
68 $toggle
.trigger( 'click' );
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>' +
79 $headerRow
= $collapsible
.find( 'tr' ).first(),
80 $contentRow
= $collapsible
.find( 'tr' ).last(),
81 $toggle
= $headerRow
.find( 'td' ).last().find( '.mw-collapsible-toggle' );
83 assert
.strictEqual( $toggle
.length
, 1, 'toggle is added to last cell of first row' );
85 assert
.assertTrue( $headerRow
.css( 'display' ) !== 'none', 'headerRow is visible' );
86 assert
.assertTrue( $contentRow
.css( 'display' ) !== 'none', 'contentRow is visible' );
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' );
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' );
97 $toggle
.trigger( 'click' );
100 $toggle
.trigger( 'click' );
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' );
109 assert
.strictEqual( $toggle
.length
, 1, 'toggle is added to the end of the caption' );
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' );
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' );
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' );
126 $toggle
.trigger( 'click' );
129 $toggle
.trigger( 'click' );
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>' +
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>' +
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
+ '>'
161 $toggleItem
= $collapsible
.find( 'li.mw-collapsible-toggle-li:first-child' ),
162 $contentItem
= $collapsible
.find( 'li' ).last(),
163 $toggle
= $toggleItem
.find( '.mw-collapsible-toggle' );
165 assert
.strictEqual( $toggle
.length
, 1, 'toggle is present, added inside new zeroth list item' );
167 assert
.assertTrue( $toggleItem
.css( 'display' ) !== 'none', 'toggleItem is visible' );
168 assert
.assertTrue( $contentItem
.css( 'display' ) !== 'none', 'contentItem is visible' );
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' );
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' );
179 $toggle
.trigger( 'click' );
182 $toggle
.trigger( 'click' );
185 QUnit
.test( 'basic operation (<ul>)', function ( assert
) {
186 listTest( 'ul', this, assert
);
189 QUnit
.test( 'basic operation (<ol>)', function ( assert
) {
190 listTest( 'ol', this, assert
);
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 }
198 $content
= $collapsible
.find( '.mw-collapsible-content' );
200 assert
.assertTrue( $content
.css( 'display' ) !== 'none', 'content is visible' );
202 $collapsible
.find( '.mw-collapsible-toggle' ).trigger( 'click' );
204 assert
.assertTrue( $content
.css( 'display' ) === 'none', 'after collapsing: content is hidden' );
207 QUnit
.test( 'mw-made-collapsible data added', function ( assert
) {
208 var $collapsible
= prepareCollapsible(
209 '<div>' + loremIpsum
+ '</div>'
212 assert
.strictEqual( $collapsible
.data( 'mw-made-collapsible' ), true, 'mw-made-collapsible data present' );
215 QUnit
.test( 'mw-collapsible added when missing', function ( assert
) {
216 var $collapsible
= prepareCollapsible(
217 '<div>' + loremIpsum
+ '</div>'
220 // eslint-disable-next-line no-jquery/no-class-state
221 assert
.assertTrue( $collapsible
.hasClass( 'mw-collapsible' ), 'mw-collapsible class present' );
224 QUnit
.test( 'mw-collapsed added when missing', function ( assert
) {
225 var $collapsible
= prepareCollapsible(
226 '<div>' + loremIpsum
+ '</div>',
230 // eslint-disable-next-line no-jquery/no-class-state
231 assert
.assertTrue( $collapsible
.hasClass( 'mw-collapsed' ), 'mw-collapsed class present' );
234 QUnit
.test( 'initial collapse (mw-collapsed class)', function ( assert
) {
235 var $collapsible
= prepareCollapsible(
236 '<div class="mw-collapsible mw-collapsed">' + loremIpsum
+ '</div>'
238 $content
= $collapsible
.find( '.mw-collapsible-content' );
240 // Synchronous - mw-collapsed should cause instantHide: true to be used on initial collapsing
241 assert
.assertTrue( $content
.css( 'display' ) === 'none', 'content is hidden' );
243 $collapsible
.on( 'afterExpand.mw-collapsible', function () {
244 assert
.assertTrue( $content
.css( 'display' ) !== 'none', 'after expanding: content is visible' );
247 $collapsible
.find( '.mw-collapsible-toggle' ).trigger( 'click' );
250 QUnit
.test( 'initial collapse (options.collapsed)', function ( assert
) {
251 var $collapsible
= prepareCollapsible(
252 '<div class="mw-collapsible">' + loremIpsum
+ '</div>',
255 $content
= $collapsible
.find( '.mw-collapsible-content' );
257 // Synchronous - collapsed: true should cause instantHide: true to be used on initial collapsing
258 assert
.assertTrue( $content
.css( 'display' ) === 'none', 'content is hidden' );
260 $collapsible
.on( 'afterExpand.mw-collapsible', function () {
261 assert
.assertTrue( $content
.css( 'display' ) !== 'none', 'after expanding: content is visible' );
264 $collapsible
.find( '.mw-collapsible-toggle' ).trigger( 'click' );
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>' +
273 '<div class="mw-collapsible-content">' + loremIpsum
+ '</div>' +
275 // Can't do asynchronous because we're testing that the event *doesn't* happen
276 { instantHide
: true }
278 $content
= $collapsible
.find( '.mw-collapsible-content' );
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)' );
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' );
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>' +
293 '<div class="mw-collapsible-content">' + loremIpsum
+ '</div>' +
295 { instantHide
: true }
297 $content
= $collapsible
.find( '.mw-collapsible-content' );
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' );
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!">' +
309 $toggleText
= $collapsible
.find( '.mw-collapsible-text' );
311 assert
.strictEqual( $toggleText
.text(), 'Collapse me!', 'data-collapsetext is respected' );
313 $collapsible
.on( 'afterCollapse.mw-collapsible', function () {
314 assert
.strictEqual( $toggleText
.text(), 'Expand me!', 'data-expandtext is respected' );
317 $collapsible
.find( '.mw-collapsible-toggle' ).trigger( 'click' );
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!' }
325 $toggleText
= $collapsible
.find( '.mw-collapsible-text' );
327 assert
.strictEqual( $toggleText
.text(), 'Collapse me!', 'options.collapseText is respected' );
329 $collapsible
.on( 'afterCollapse.mw-collapsible', function () {
330 assert
.strictEqual( $toggleText
.text(), 'Expand me!', 'options.expandText is respected' );
333 $collapsible
.find( '.mw-collapsible-toggle' ).trigger( 'click' );
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>' +
342 '<div class="mw-collapsible-content">' + loremIpsum
+ '</div>' +
344 { collapseText
: 'Hide', expandText
: 'Show' }
346 $toggleText
= $collapsible
.find( '.mw-collapsible-text' );
348 assert
.strictEqual( $toggleText
.text(), 'Toggle', 'predefined text remains' );
350 $collapsible
.on( 'afterCollapse.mw-collapsible', function () {
351 assert
.strictEqual( $toggleText
.text(), 'Show', 'predefined text is toggled' );
353 $collapsible
.on( 'afterExpand.mw-collapsible', function () {
354 assert
.strictEqual( $toggleText
.text(), 'Hide', 'predefined text is toggled back' );
357 $collapsible
.find( '.mw-collapsible-toggle' ).trigger( 'click' );
360 $collapsible
.find( '.mw-collapsible-toggle' ).trigger( 'click' );
363 QUnit
.test( 'cloned collapsibles can be made collapsible again', function ( assert
) {
364 var $collapsible
= prepareCollapsible(
365 '<div class="mw-collapsible">' + loremIpsum
+ '</div>'
367 $clone
= $collapsible
.clone() // clone without data and events
368 .appendTo( '#qunit-fixture' ).makeCollapsible(),
369 $content
= $clone
.find( '.mw-collapsible-content' );
371 assert
.assertTrue( $content
.css( 'display' ) !== 'none', 'content is visible' );
373 $clone
.on( 'afterCollapse.mw-collapsible', function () {
374 assert
.assertTrue( $content
.css( 'display' ) === 'none', 'after collapsing: content is hidden' );
377 $clone
.find( '.mw-collapsible-toggle a' ).trigger( 'click' );
380 QUnit
.test( 'T168689 - nested collapsible divs should keep independent state', function ( assert
) {
381 var $collapsible1
= prepareCollapsible(
382 '<div class="mw-collapsible">' + loremIpsum
+ '</div>'
384 $collapsible2
= prepareCollapsible(
385 '<div class="mw-collapsible">' + loremIpsum
+ '</div>'
389 .append( $collapsible2
)
390 .appendTo( '#qunit-fixture' ).makeCollapsible();
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' );