resourceloader: Avoid clear/set timer overhead in mw.loader.store.add
[lhc/web/wiklou.git] / tests / qunit / suites / resources / mediawiki / mediawiki.loader.test.js
index de593d5..ae5ddb5 100644 (file)
@@ -1,15 +1,12 @@
 ( function ( mw, $ ) {
        QUnit.module( 'mediawiki.loader', QUnit.newMwEnvironment( {
                setup: function ( assert ) {
-                       mw.loader.store.enabled = false;
-
                        // Expose for load.mock.php
                        mw.loader.testFail = function ( reason ) {
                                assert.ok( false, reason );
                        };
                },
                teardown: function () {
-                       mw.loader.store.enabled = false;
                        // Teardown for StringSet shim test
                        if ( this.nativeSet ) {
                                window.Set = this.nativeSet;
                var done = assert.async();
 
                mw.loader.register( [
-                       [ 'test.circle1', '0', [ 'test.circle2' ] ],
-                       [ 'test.circle2', '0', [ 'test.circle3' ] ],
-                       [ 'test.circle3', '0', [ 'test.circle1' ] ]
+                       [ 'test.set.circleA', '0', [ 'test.set.circleB' ] ],
+                       [ 'test.set.circleB', '0', [ 'test.set.circleC' ] ],
+                       [ 'test.set.circleC', '0', [ 'test.set.circleA' ] ]
                ] );
-               mw.loader.using( 'test.circle3' ).then(
+               mw.loader.using( 'test.set.circleC' ).then(
                        function done() {
                                assert.ok( false, 'Unexpected resolution, expected error.' );
                        },
                mw.redefineFallbacksForTest();
 
                mw.loader.register( [
-                       [ 'test.shim.circle1', '0', [ 'test.shim.circle2' ] ],
-                       [ 'test.shim.circle2', '0', [ 'test.shim.circle3' ] ],
-                       [ 'test.shim.circle3', '0', [ 'test.shim.circle1' ] ]
+                       [ 'test.shim.circleA', '0', [ 'test.shim.circleB' ] ],
+                       [ 'test.shim.circleB', '0', [ 'test.shim.circleC' ] ],
+                       [ 'test.shim.circleC', '0', [ 'test.shim.circleA' ] ]
                ] );
-               mw.loader.using( 'test.shim.circle3' ).then(
+               mw.loader.using( 'test.shim.circleC' ).then(
                        function done() {
                                assert.ok( false, 'Unexpected resolution, expected error.' );
                        },
        QUnit.test( '.load() - Error: Circular dependency', function ( assert ) {
                var capture = [];
                mw.loader.register( [
-                       [ 'test.circleA', '0', [ 'test.circleB' ] ],
-                       [ 'test.circleB', '0', [ 'test.circleC' ] ],
-                       [ 'test.circleC', '0', [ 'test.circleA' ] ]
+                       [ 'test.load.circleA', '0', [ 'test.load.circleB' ] ],
+                       [ 'test.load.circleB', '0', [ 'test.load.circleC' ] ],
+                       [ 'test.load.circleC', '0', [ 'test.load.circleA' ] ]
                ] );
                this.sandbox.stub( mw, 'track', function ( topic, data ) {
                        capture.push( {
                        } );
                } );
 
-               mw.loader.load( 'test.circleC' );
+               mw.loader.load( 'test.load.circleC' );
                assert.deepEqual(
                        [ {
                                topic: 'resourceloader.exception',
-                               error: 'Circular reference detected: test.circleB -> test.circleC',
+                               error: 'Circular reference detected: test.load.circleB -> test.load.circleC',
                                source: 'resolve'
                        } ],
                        capture,
                );
        } );
 
+       QUnit.test( '.load() - Error: Circular dependency (direct)', function ( assert ) {
+               var capture = [];
+               mw.loader.register( [
+                       [ 'test.load.circleDirect', '0', [ 'test.load.circleDirect' ] ]
+               ] );
+               this.sandbox.stub( mw, 'track', function ( topic, data ) {
+                       capture.push( {
+                               topic: topic,
+                               error: data.exception && data.exception.message,
+                               source: data.source
+                       } );
+               } );
+
+               mw.loader.load( 'test.load.circleDirect' );
+               assert.deepEqual(
+                       [ {
+                               topic: 'resourceloader.exception',
+                               error: 'Circular reference detected: test.load.circleDirect -> test.load.circleDirect',
+                               source: 'resolve'
+                       } ],
+                       capture,
+                       'Detect a direct self-dependency'
+               );
+       } );
+
        QUnit.test( '.using() - Error: Unregistered', function ( assert ) {
                var done = assert.async();
 
                assert.strictEqual( mw.loader.getState( 'test.module7' ), 'registered', 'Expected "registered" state for test.module7' );
                assert.strictEqual( mw.loader.getState( 'test.module8' ), 'loaded', 'Expected "loaded" state for test.module8' );
                assert.strictEqual( mw.loader.getState( 'test.module9' ), 'registered', 'Expected "registered" state for test.module9' );
-               mw.loader.state( 'test.module7', 'missing' );
+               mw.loader.state( { 'test.module7': 'missing' } );
                assert.strictEqual( mw.loader.getState( 'test.module7' ), 'missing', 'Expected "missing" state for test.module7' );
                assert.strictEqual( mw.loader.getState( 'test.module8' ), 'error', 'Expected "error" state for test.module8' );
                assert.strictEqual( mw.loader.getState( 'test.module9' ), 'error', 'Expected "error" state for test.module9' );
                        },
                        function ( e, dependencies ) {
                                assert.strictEqual( Array.isArray( dependencies ), true, 'Expected array of dependencies' );
-                               assert.deepEqual( dependencies, [ 'test.module7' ], 'Error callback called with module test.module7' );
+                               assert.deepEqual(
+                                       dependencies,
+                                       [ 'jquery', 'mediawiki.base', 'test.module7' ],
+                                       'Error callback called with module test.module7'
+                               );
                        }
                );
                mw.loader.using(
                                dependencies.sort();
                                assert.deepEqual(
                                        dependencies,
-                                       [ 'test.module7', 'test.module8', 'test.module9' ],
+                                       [ 'jquery', 'mediawiki.base', 'test.module7', 'test.module8', 'test.module9' ],
                                        'Error callback called with all three modules as dependencies'
                                );
                        }
                        },
                        function ( e, badmodules ) {
                                assert.ok( true, 'Error handler should be invoked.' );
-                               // As soon as server spits out state('testMissing', 'missing');
+                               // As soon as server sets state of 'testMissing' to 'missing'
                                // it will bubble up and trigger the error callback.
                                // Therefor the badmodules array is not testUsesMissing or testUsesNestedMissing.
                                assert.deepEqual( badmodules, [ 'testMissing' ], 'Bad modules as expected.' );
 
        QUnit.test( 'Stale response caching - T117587', function ( assert ) {
                var count = 0;
-               mw.loader.store.enabled = true;
+               // Enable store and stub timeout/idle scheduling
+               this.sandbox.stub( mw.loader.store, 'enabled', true );
+               this.sandbox.stub( window, 'setTimeout', function ( fn ) {
+                       fn();
+               } );
+               this.sandbox.stub( mw, 'requestIdleCallback', function ( fn ) {
+                       fn();
+               } );
+
                mw.loader.register( 'test.stale', 'v2' );
                assert.strictEqual( mw.loader.store.get( 'test.stale' ), false, 'Not in store' );
 
                                // After implementing, registry contains version as implemented by the response.
                                assert.strictEqual( mw.loader.getVersion( 'test.stale' ), 'v1', 'Override version' );
                                assert.strictEqual( mw.loader.getState( 'test.stale' ), 'ready' );
-                               assert.ok( mw.loader.store.get( 'test.stale' ), 'In store' );
+                               assert.strictEqual( typeof mw.loader.store.get( 'test.stale' ), 'string', 'In store' );
                        } )
                        .then( function () {
                                // Reset run time, but keep mw.loader.store
 
        QUnit.test( 'Stale response caching - backcompat', function ( assert ) {
                var script = 0;
-               mw.loader.store.enabled = true;
+               // Enable store and stub timeout/idle scheduling
+               this.sandbox.stub( mw.loader.store, 'enabled', true );
+               this.sandbox.stub( window, 'setTimeout', function ( fn ) {
+                       fn();
+               } );
+               this.sandbox.stub( mw, 'requestIdleCallback', function ( fn ) {
+                       fn();
+               } );
+
                mw.loader.register( 'test.stalebc', 'v2' );
                assert.strictEqual( mw.loader.store.get( 'test.stalebc' ), false, 'Not in store' );
 
                        .then( function () {
                                assert.strictEqual( script, 1, 'module script ran' );
                                assert.strictEqual( mw.loader.getState( 'test.stalebc' ), 'ready' );
-                               assert.ok( mw.loader.store.get( 'test.stalebc' ), 'In store' );
+                               assert.strictEqual( typeof mw.loader.store.get( 'test.stalebc' ), 'string', 'In store' );
                        } )
                        .then( function () {
                                // Reset run time, but keep mw.loader.store