Merge "Sync up with Parsoid parserTests."
[lhc/web/wiklou.git] / tests / qunit / suites / resources / mediawiki / mediawiki.loader.test.js
1 ( function ( mw, $ ) {
2 QUnit.module( 'mediawiki (mw.loader)' );
3
4 mw.loader.addSource(
5 'testloader',
6 QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/load.mock.php' )
7 );
8
9 /**
10 * The sync style load test (for @import). This is, in a way, also an open bug for
11 * ResourceLoader ("execute js after styles are loaded"), but browsers don't offer a
12 * way to get a callback from when a stylesheet is loaded (that is, including any
13 * `@import` rules inside). To work around this, we'll have a little time loop to check
14 * if the styles apply.
15 *
16 * Note: This test originally used new Image() and onerror to get a callback
17 * when the url is loaded, but that is fragile since it doesn't monitor the
18 * same request as the css @import, and Safari 4 has issues with
19 * onerror/onload not being fired at all in weird cases like this.
20 */
21 function assertStyleAsync( assert, $element, prop, val, fn ) {
22 var styleTestStart,
23 el = $element.get( 0 ),
24 styleTestTimeout = ( QUnit.config.testTimeout || 5000 ) - 200;
25
26 function isCssImportApplied() {
27 // Trigger reflow, repaint, redraw, whatever (cross-browser)
28 var x = $element.css( 'height' );
29 x = el.innerHTML;
30 el.className = el.className;
31 x = document.documentElement.clientHeight;
32
33 return $element.css( prop ) === val;
34 }
35
36 function styleTestLoop() {
37 var styleTestSince = new Date().getTime() - styleTestStart;
38 // If it is passing or if we timed out, run the real test and stop the loop
39 if ( isCssImportApplied() || styleTestSince > styleTestTimeout ) {
40 assert.equal( $element.css( prop ), val,
41 'style "' + prop + ': ' + val + '" from url is applied (after ' + styleTestSince + 'ms)'
42 );
43
44 if ( fn ) {
45 fn();
46 }
47
48 return;
49 }
50 // Otherwise, keep polling
51 setTimeout( styleTestLoop );
52 }
53
54 // Start the loop
55 styleTestStart = new Date().getTime();
56 styleTestLoop();
57 }
58
59 function urlStyleTest( selector, prop, val ) {
60 return QUnit.fixurl(
61 mw.config.get( 'wgScriptPath' ) +
62 '/tests/qunit/data/styleTest.css.php?' +
63 $.param( {
64 selector: selector,
65 prop: prop,
66 val: val
67 } )
68 );
69 }
70
71 QUnit.test( 'Basic', 2, function ( assert ) {
72 var isAwesomeDone;
73
74 mw.loader.testCallback = function () {
75 assert.strictEqual( isAwesomeDone, undefined, 'Implementing module is.awesome: isAwesomeDone should still be undefined' );
76 isAwesomeDone = true;
77 };
78
79 mw.loader.implement( 'test.callback', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.js' ) ] );
80
81 return mw.loader.using( 'test.callback', function () {
82 assert.strictEqual( isAwesomeDone, true, 'test.callback module should\'ve caused isAwesomeDone to be true' );
83 delete mw.loader.testCallback;
84
85 }, function () {
86 assert.ok( false, 'Error callback fired while loader.using "test.callback" module' );
87 } );
88 } );
89
90 QUnit.test( 'Object method as module name', 2, function ( assert ) {
91 var isAwesomeDone;
92
93 mw.loader.testCallback = function () {
94 assert.strictEqual( isAwesomeDone, undefined, 'Implementing module hasOwnProperty: isAwesomeDone should still be undefined' );
95 isAwesomeDone = true;
96 };
97
98 mw.loader.implement( 'hasOwnProperty', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.js' ) ], {}, {} );
99
100 return mw.loader.using( 'hasOwnProperty', function () {
101 assert.strictEqual( isAwesomeDone, true, 'hasOwnProperty module should\'ve caused isAwesomeDone to be true' );
102 delete mw.loader.testCallback;
103
104 }, function () {
105 assert.ok( false, 'Error callback fired while loader.using "hasOwnProperty" module' );
106 } );
107 } );
108
109 QUnit.test( '.using( .. ) Promise', 2, function ( assert ) {
110 var isAwesomeDone;
111
112 mw.loader.testCallback = function () {
113 assert.strictEqual( isAwesomeDone, undefined, 'Implementing module is.awesome: isAwesomeDone should still be undefined' );
114 isAwesomeDone = true;
115 };
116
117 mw.loader.implement( 'test.promise', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.js' ) ] );
118
119 return mw.loader.using( 'test.promise' )
120 .done( function () {
121 assert.strictEqual( isAwesomeDone, true, 'test.promise module should\'ve caused isAwesomeDone to be true' );
122 delete mw.loader.testCallback;
123
124 } )
125 .fail( function () {
126 assert.ok( false, 'Error callback fired while loader.using "test.promise" module' );
127 } );
128 } );
129
130 QUnit.test( '.implement( styles={ "css": [text, ..] } )', 2, function ( assert ) {
131 var $element = $( '<div class="mw-test-implement-a"></div>' ).appendTo( '#qunit-fixture' );
132
133 assert.notEqual(
134 $element.css( 'float' ),
135 'right',
136 'style is clear'
137 );
138
139 mw.loader.implement(
140 'test.implement.a',
141 function () {
142 assert.equal(
143 $element.css( 'float' ),
144 'right',
145 'style is applied'
146 );
147 },
148 {
149 all: '.mw-test-implement-a { float: right; }'
150 }
151 );
152
153 return mw.loader.using( 'test.implement.a' );
154 } );
155
156 QUnit.test( '.implement( styles={ "url": { <media>: [url, ..] } } )', 7, function ( assert ) {
157 var $element1 = $( '<div class="mw-test-implement-b1"></div>' ).appendTo( '#qunit-fixture' ),
158 $element2 = $( '<div class="mw-test-implement-b2"></div>' ).appendTo( '#qunit-fixture' ),
159 $element3 = $( '<div class="mw-test-implement-b3"></div>' ).appendTo( '#qunit-fixture' ),
160 done = assert.async();
161
162 assert.notEqual(
163 $element1.css( 'text-align' ),
164 'center',
165 'style is clear'
166 );
167 assert.notEqual(
168 $element2.css( 'float' ),
169 'left',
170 'style is clear'
171 );
172 assert.notEqual(
173 $element3.css( 'text-align' ),
174 'right',
175 'style is clear'
176 );
177
178 mw.loader.implement(
179 'test.implement.b',
180 function () {
181 // Note: done() must only be called when the entire test is
182 // complete. So, make sure that we don't start until *both*
183 // assertStyleAsync calls have completed.
184 var pending = 2;
185 assertStyleAsync( assert, $element2, 'float', 'left', function () {
186 assert.notEqual( $element1.css( 'text-align' ), 'center', 'print style is not applied' );
187
188 pending--;
189 if ( pending === 0 ) {
190 done();
191 }
192 } );
193 assertStyleAsync( assert, $element3, 'float', 'right', function () {
194 assert.notEqual( $element1.css( 'text-align' ), 'center', 'print style is not applied' );
195
196 pending--;
197 if ( pending === 0 ) {
198 done();
199 }
200 } );
201 },
202 {
203 url: {
204 print: [ urlStyleTest( '.mw-test-implement-b1', 'text-align', 'center' ) ],
205 screen: [
206 // bug 40834: Make sure it actually works with more than 1 stylesheet reference
207 urlStyleTest( '.mw-test-implement-b2', 'float', 'left' ),
208 urlStyleTest( '.mw-test-implement-b3', 'float', 'right' )
209 ]
210 }
211 }
212 );
213
214 mw.loader.load( 'test.implement.b' );
215 } );
216
217 // Backwards compatibility
218 QUnit.test( '.implement( styles={ <media>: text } ) (back-compat)', 2, function ( assert ) {
219 var $element = $( '<div class="mw-test-implement-c"></div>' ).appendTo( '#qunit-fixture' );
220
221 assert.notEqual(
222 $element.css( 'float' ),
223 'right',
224 'style is clear'
225 );
226
227 mw.loader.implement(
228 'test.implement.c',
229 function () {
230 assert.equal(
231 $element.css( 'float' ),
232 'right',
233 'style is applied'
234 );
235 },
236 {
237 all: '.mw-test-implement-c { float: right; }'
238 }
239 );
240
241 return mw.loader.using( 'test.implement.c' );
242 } );
243
244 // Backwards compatibility
245 QUnit.test( '.implement( styles={ <media>: [url, ..] } ) (back-compat)', 4, function ( assert ) {
246 var $element = $( '<div class="mw-test-implement-d"></div>' ).appendTo( '#qunit-fixture' ),
247 $element2 = $( '<div class="mw-test-implement-d2"></div>' ).appendTo( '#qunit-fixture' ),
248 done = assert.async();
249
250 assert.notEqual(
251 $element.css( 'float' ),
252 'right',
253 'style is clear'
254 );
255 assert.notEqual(
256 $element2.css( 'text-align' ),
257 'center',
258 'style is clear'
259 );
260
261 mw.loader.implement(
262 'test.implement.d',
263 function () {
264 assertStyleAsync( assert, $element, 'float', 'right', function () {
265 assert.notEqual( $element2.css( 'text-align' ), 'center', 'print style is not applied (bug 40500)' );
266 done();
267 } );
268 },
269 {
270 all: [ urlStyleTest( '.mw-test-implement-d', 'float', 'right' ) ],
271 print: [ urlStyleTest( '.mw-test-implement-d2', 'text-align', 'center' ) ]
272 }
273 );
274
275 mw.loader.load( 'test.implement.d' );
276 } );
277
278 // @import (bug 31676)
279 QUnit.test( '.implement( styles has @import )', 7, function ( assert ) {
280 var isJsExecuted, $element,
281 done = assert.async();
282
283 mw.loader.implement(
284 'test.implement.import',
285 function () {
286 assert.strictEqual( isJsExecuted, undefined, 'script not executed multiple times' );
287 isJsExecuted = true;
288
289 assert.equal( mw.loader.getState( 'test.implement.import' ), 'executing', 'module state during implement() script execution' );
290
291 $element = $( '<div class="mw-test-implement-import">Foo bar</div>' ).appendTo( '#qunit-fixture' );
292
293 assert.equal( mw.msg( 'test-foobar' ), 'Hello Foobar, $1!', 'messages load before script execution' );
294
295 assertStyleAsync( assert, $element, 'float', 'right', function () {
296 assert.equal( $element.css( 'text-align' ), 'center',
297 'CSS styles after the @import rule are working'
298 );
299
300 done();
301 } );
302 },
303 {
304 css: [
305 '@import url(\''
306 + urlStyleTest( '.mw-test-implement-import', 'float', 'right' )
307 + '\');\n'
308 + '.mw-test-implement-import { text-align: center; }'
309 ]
310 },
311 {
312 'test-foobar': 'Hello Foobar, $1!'
313 }
314 );
315
316 mw.loader.using( 'test.implement.import' ).always( function () {
317 assert.strictEqual( isJsExecuted, true, 'script executed' );
318 assert.equal( mw.loader.getState( 'test.implement.import' ), 'ready', 'module state after script execution' );
319 } );
320 } );
321
322 QUnit.test( '.implement( dependency with styles )', 4, function ( assert ) {
323 var $element = $( '<div class="mw-test-implement-e"></div>' ).appendTo( '#qunit-fixture' ),
324 $element2 = $( '<div class="mw-test-implement-e2"></div>' ).appendTo( '#qunit-fixture' );
325
326 assert.notEqual(
327 $element.css( 'float' ),
328 'right',
329 'style is clear'
330 );
331 assert.notEqual(
332 $element2.css( 'float' ),
333 'left',
334 'style is clear'
335 );
336
337 mw.loader.register( [
338 [ 'test.implement.e', '0', [ 'test.implement.e2' ] ],
339 [ 'test.implement.e2', '0' ]
340 ] );
341
342 mw.loader.implement(
343 'test.implement.e',
344 function () {
345 assert.equal(
346 $element.css( 'float' ),
347 'right',
348 'Depending module\'s style is applied'
349 );
350 },
351 {
352 all: '.mw-test-implement-e { float: right; }'
353 }
354 );
355
356 mw.loader.implement(
357 'test.implement.e2',
358 function () {
359 assert.equal(
360 $element2.css( 'float' ),
361 'left',
362 'Dependency\'s style is applied'
363 );
364 },
365 {
366 all: '.mw-test-implement-e2 { float: left; }'
367 }
368 );
369
370 return mw.loader.using( 'test.implement.e' );
371 } );
372
373 QUnit.test( '.implement( only scripts )', 1, function ( assert ) {
374 mw.loader.implement( 'test.onlyscripts', function () {} );
375 assert.strictEqual( mw.loader.getState( 'test.onlyscripts' ), 'ready' );
376 } );
377
378 QUnit.test( '.implement( only messages )', 2, function ( assert ) {
379 assert.assertFalse( mw.messages.exists( 'bug_29107' ), 'Verify that the test message doesn\'t exist yet' );
380
381 // jscs: disable requireCamelCaseOrUpperCaseIdentifiers
382 mw.loader.implement( 'test.implement.msgs', [], {}, { bug_29107: 'loaded' } );
383 // jscs: enable requireCamelCaseOrUpperCaseIdentifiers
384
385 return mw.loader.using( 'test.implement.msgs', function () {
386 assert.ok( mw.messages.exists( 'bug_29107' ), 'Bug 29107: messages-only module should implement ok' );
387 }, function () {
388 assert.ok( false, 'Error callback fired while implementing "test.implement.msgs" module' );
389 } );
390 } );
391
392 QUnit.test( '.implement( empty )', 1, function ( assert ) {
393 mw.loader.implement( 'test.empty' );
394 assert.strictEqual( mw.loader.getState( 'test.empty' ), 'ready' );
395 } );
396
397 QUnit.test( 'Broken indirect dependency', 4, function ( assert ) {
398 // don't emit an error event
399 this.sandbox.stub( mw, 'track' );
400
401 mw.loader.register( [
402 [ 'test.module1', '0' ],
403 [ 'test.module2', '0', [ 'test.module1' ] ],
404 [ 'test.module3', '0', [ 'test.module2' ] ]
405 ] );
406 mw.loader.implement( 'test.module1', function () {
407 throw new Error( 'expected' );
408 }, {}, {} );
409 assert.strictEqual( mw.loader.getState( 'test.module1' ), 'error', 'Expected "error" state for test.module1' );
410 assert.strictEqual( mw.loader.getState( 'test.module2' ), 'error', 'Expected "error" state for test.module2' );
411 assert.strictEqual( mw.loader.getState( 'test.module3' ), 'error', 'Expected "error" state for test.module3' );
412
413 assert.strictEqual( mw.track.callCount, 1 );
414 } );
415
416 QUnit.test( 'Circular dependency', 1, function ( assert ) {
417 mw.loader.register( [
418 [ 'test.circle1', '0', [ 'test.circle2' ] ],
419 [ 'test.circle2', '0', [ 'test.circle3' ] ],
420 [ 'test.circle3', '0', [ 'test.circle1' ] ]
421 ] );
422 assert.throws( function () {
423 mw.loader.using( 'test.circle3' );
424 }, /Circular/, 'Detect circular dependency' );
425 } );
426
427 QUnit.test( 'Out-of-order implementation', 9, function ( assert ) {
428 mw.loader.register( [
429 [ 'test.module4', '0' ],
430 [ 'test.module5', '0', [ 'test.module4' ] ],
431 [ 'test.module6', '0', [ 'test.module5' ] ]
432 ] );
433 mw.loader.implement( 'test.module4', function () {} );
434 assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
435 assert.strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'Expected "registered" state for test.module5' );
436 assert.strictEqual( mw.loader.getState( 'test.module6' ), 'registered', 'Expected "registered" state for test.module6' );
437 mw.loader.implement( 'test.module6', function () {} );
438 assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
439 assert.strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'Expected "registered" state for test.module5' );
440 assert.strictEqual( mw.loader.getState( 'test.module6' ), 'loaded', 'Expected "loaded" state for test.module6' );
441 mw.loader.implement( 'test.module5', function () {} );
442 assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
443 assert.strictEqual( mw.loader.getState( 'test.module5' ), 'ready', 'Expected "ready" state for test.module5' );
444 assert.strictEqual( mw.loader.getState( 'test.module6' ), 'ready', 'Expected "ready" state for test.module6' );
445 } );
446
447 QUnit.test( 'Missing dependency', 13, function ( assert ) {
448 mw.loader.register( [
449 [ 'test.module7', '0' ],
450 [ 'test.module8', '0', [ 'test.module7' ] ],
451 [ 'test.module9', '0', [ 'test.module8' ] ]
452 ] );
453 mw.loader.implement( 'test.module8', function () {} );
454 assert.strictEqual( mw.loader.getState( 'test.module7' ), 'registered', 'Expected "registered" state for test.module7' );
455 assert.strictEqual( mw.loader.getState( 'test.module8' ), 'loaded', 'Expected "loaded" state for test.module8' );
456 assert.strictEqual( mw.loader.getState( 'test.module9' ), 'registered', 'Expected "registered" state for test.module9' );
457 mw.loader.state( 'test.module7', 'missing' );
458 assert.strictEqual( mw.loader.getState( 'test.module7' ), 'missing', 'Expected "missing" state for test.module7' );
459 assert.strictEqual( mw.loader.getState( 'test.module8' ), 'error', 'Expected "error" state for test.module8' );
460 assert.strictEqual( mw.loader.getState( 'test.module9' ), 'error', 'Expected "error" state for test.module9' );
461 mw.loader.implement( 'test.module9', function () {} );
462 assert.strictEqual( mw.loader.getState( 'test.module7' ), 'missing', 'Expected "missing" state for test.module7' );
463 assert.strictEqual( mw.loader.getState( 'test.module8' ), 'error', 'Expected "error" state for test.module8' );
464 assert.strictEqual( mw.loader.getState( 'test.module9' ), 'error', 'Expected "error" state for test.module9' );
465 mw.loader.using(
466 [ 'test.module7' ],
467 function () {
468 assert.ok( false, 'Success fired despite missing dependency' );
469 assert.ok( true, 'QUnit expected() count dummy' );
470 },
471 function ( e, dependencies ) {
472 assert.strictEqual( $.isArray( dependencies ), true, 'Expected array of dependencies' );
473 assert.deepEqual( dependencies, [ 'test.module7' ], 'Error callback called with module test.module7' );
474 }
475 );
476 mw.loader.using(
477 [ 'test.module9' ],
478 function () {
479 assert.ok( false, 'Success fired despite missing dependency' );
480 assert.ok( true, 'QUnit expected() count dummy' );
481 },
482 function ( e, dependencies ) {
483 assert.strictEqual( $.isArray( dependencies ), true, 'Expected array of dependencies' );
484 dependencies.sort();
485 assert.deepEqual(
486 dependencies,
487 [ 'test.module7', 'test.module8', 'test.module9' ],
488 'Error callback called with all three modules as dependencies'
489 );
490 }
491 );
492 } );
493
494 QUnit.test( 'Dependency handling', 5, function ( assert ) {
495 var done = assert.async();
496 mw.loader.register( [
497 // [module, version, dependencies, group, source]
498 [ 'testMissing', '1', [], null, 'testloader' ],
499 [ 'testUsesMissing', '1', [ 'testMissing' ], null, 'testloader' ],
500 [ 'testUsesNestedMissing', '1', [ 'testUsesMissing' ], null, 'testloader' ]
501 ] );
502
503 function verifyModuleStates() {
504 assert.equal( mw.loader.getState( 'testMissing' ), 'missing', 'Module not known to server must have state "missing"' );
505 assert.equal( mw.loader.getState( 'testUsesMissing' ), 'error', 'Module with missing dependency must have state "error"' );
506 assert.equal( mw.loader.getState( 'testUsesNestedMissing' ), 'error', 'Module with indirect missing dependency must have state "error"' );
507 }
508
509 mw.loader.using( [ 'testUsesNestedMissing' ],
510 function () {
511 assert.ok( false, 'Error handler should be invoked.' );
512 assert.ok( true ); // Dummy to reach QUnit expect()
513
514 verifyModuleStates();
515
516 done();
517 },
518 function ( e, badmodules ) {
519 assert.ok( true, 'Error handler should be invoked.' );
520 // As soon as server spits out state('testMissing', 'missing');
521 // it will bubble up and trigger the error callback.
522 // Therefor the badmodules array is not testUsesMissing or testUsesNestedMissing.
523 assert.deepEqual( badmodules, [ 'testMissing' ], 'Bad modules as expected.' );
524
525 verifyModuleStates();
526
527 done();
528 }
529 );
530 } );
531
532 QUnit.test( 'Skip-function handling', 5, function ( assert ) {
533 mw.loader.register( [
534 // [module, version, dependencies, group, source, skip]
535 [ 'testSkipped', '1', [], null, 'testloader', 'return true;' ],
536 [ 'testNotSkipped', '1', [], null, 'testloader', 'return false;' ],
537 [ 'testUsesSkippable', '1', [ 'testSkipped', 'testNotSkipped' ], null, 'testloader' ]
538 ] );
539
540 function verifyModuleStates() {
541 assert.equal( mw.loader.getState( 'testSkipped' ), 'ready', 'Module is ready when skipped' );
542 assert.equal( mw.loader.getState( 'testNotSkipped' ), 'ready', 'Module is ready when not skipped but loaded' );
543 assert.equal( mw.loader.getState( 'testUsesSkippable' ), 'ready', 'Module is ready when skippable dependencies are ready' );
544 }
545
546 return mw.loader.using( [ 'testUsesSkippable' ],
547 function () {
548 assert.ok( true, 'Success handler should be invoked.' );
549 assert.ok( true ); // Dummy to match error handler and reach QUnit expect()
550
551 verifyModuleStates();
552 },
553 function ( e, badmodules ) {
554 assert.ok( false, 'Error handler should not be invoked.' );
555 assert.deepEqual( badmodules, [], 'Bad modules as expected.' );
556
557 verifyModuleStates();
558 }
559 );
560 } );
561
562 QUnit.asyncTest( '.load( "//protocol-relative" ) - T32825', 2, function ( assert ) {
563 // This bug was actually already fixed in 1.18 and later when discovered in 1.17.
564 // Test is for regressions!
565
566 // Forge a URL to the test callback script
567 var target = QUnit.fixurl(
568 mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/qunitOkCall.js'
569 );
570
571 // Confirm that mw.loader.load() works with protocol-relative URLs
572 target = target.replace( /https?:/, '' );
573
574 assert.equal( target.slice( 0, 2 ), '//',
575 'URL must be relative to test relative URLs!'
576 );
577
578 // Async!
579 // The target calls QUnit.start
580 mw.loader.load( target );
581 } );
582
583 QUnit.asyncTest( '.load( "/absolute-path" )', 2, function ( assert ) {
584 // Forge a URL to the test callback script
585 var target = QUnit.fixurl(
586 mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/qunitOkCall.js'
587 );
588
589 // Confirm that mw.loader.load() works with absolute-paths (relative to current hostname)
590 assert.equal( target.slice( 0, 1 ), '/', 'URL is relative to document root' );
591
592 // Async!
593 // The target calls QUnit.start
594 mw.loader.load( target );
595 } );
596
597 QUnit.test( 'Executing race - T112232', 2, function ( assert ) {
598 var done = false;
599
600 // The red herring schedules its CSS buffer first. In T112232, a bug in the
601 // state machine would cause the job for testRaceLoadMe to run with an earlier job.
602 mw.loader.implement(
603 'testRaceRedHerring',
604 function () {},
605 { css: [ '.mw-testRaceRedHerring {}' ] }
606 );
607 mw.loader.implement(
608 'testRaceLoadMe',
609 function () {
610 done = true;
611 },
612 { css: [ '.mw-testRaceLoadMe { float: left; }' ] }
613 );
614
615 mw.loader.load( [ 'testRaceRedHerring', 'testRaceLoadMe' ] );
616 return mw.loader.using( 'testRaceLoadMe', function () {
617 assert.strictEqual( done, true, 'script ran' );
618 assert.strictEqual( mw.loader.getState( 'testRaceLoadMe' ), 'ready', 'state' );
619 } );
620 } );
621
622 QUnit.test( 'require()', 6, function ( assert ) {
623 mw.loader.register( [
624 [ 'test.require1', '0' ],
625 [ 'test.require2', '0' ],
626 [ 'test.require3', '0' ],
627 [ 'test.require4', '0', [ 'test.require3' ] ]
628 ] );
629 mw.loader.implement( 'test.require1', function () {} );
630 mw.loader.implement( 'test.require2', function ( $, jQuery, require, module ) {
631 module.exports = 1;
632 } );
633 mw.loader.implement( 'test.require3', function ( $, jQuery, require, module ) {
634 module.exports = function () {
635 return 'hello world';
636 };
637 } );
638 mw.loader.implement( 'test.require4', function ( $, jQuery, require, module ) {
639 var other = require( 'test.require3' );
640 module.exports = {
641 pizza: function () {
642 return other();
643 }
644 };
645 } );
646 return mw.loader.using( [ 'test.require1', 'test.require2', 'test.require3', 'test.require4' ] )
647 .then( function ( require ) {
648 var module1, module2, module3, module4;
649
650 module1 = require( 'test.require1' );
651 module2 = require( 'test.require2' );
652 module3 = require( 'test.require3' );
653 module4 = require( 'test.require4' );
654
655 assert.strictEqual( typeof module1, 'object', 'export of module with no export' );
656 assert.strictEqual( module2, 1, 'export a number' );
657 assert.strictEqual( module3(), 'hello world', 'export a function' );
658 assert.strictEqual( typeof module4.pizza, 'function', 'export an object' );
659 assert.strictEqual( module4.pizza(), 'hello world', 'module can require other modules' );
660
661 assert.throws( function () {
662 require( '_badmodule' );
663 }, /is not loaded/, 'Requesting non-existent modules throws error.' );
664 } );
665 } );
666
667 QUnit.test( 'require() in debug mode', 1, function ( assert ) {
668 var path = mw.config.get( 'wgScriptPath' );
669 mw.loader.register( [
670 [ 'test.require.define', '0' ],
671 [ 'test.require.callback', '0', [ 'test.require.define' ] ]
672 ] );
673 mw.loader.implement( 'test.require.callback', [ QUnit.fixurl( path + '/tests/qunit/data/requireCallMwLoaderTestCallback.js' ) ] );
674 mw.loader.implement( 'test.require.define', [ QUnit.fixurl( path + '/tests/qunit/data/defineCallMwLoaderTestCallback.js' ) ] );
675
676 return mw.loader.using( 'test.require.callback' ).then( function ( require ) {
677 var exported = require( 'test.require.callback' );
678 assert.strictEqual( exported, 'Require worked.Define worked.',
679 'module.exports worked in debug mode' );
680 }, function () {
681 assert.ok( false, 'Error callback fired while loader.using "test.require.callback" module' );
682 } );
683 } );
684
685 }( mediaWiki, jQuery ) );