mediawiki.cookie: Export config via packageFiles
authorTimo Tijhof <krinklemail@gmail.com>
Sat, 9 Mar 2019 02:46:38 +0000 (02:46 +0000)
committerAaron Schulz <aschulz@wikimedia.org>
Sun, 10 Mar 2019 00:22:41 +0000 (00:22 +0000)
* Add a private setDefaults() method to allow mocking from
  unit tests.

* Use matching keys between the data export and the API.

* Reduce duplication in the code.

* Access the Sinon stub explicitly instead of via the public path.

* Remove use of the now-redundant QUnit.newMwEnvironment().

Change-Id: I600332cdb738f0b443549948b9f070f3ccfa12aa

resources/Resources.php
resources/src/mediawiki.cookie/index.js
tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js

index cd81363..86bca6c 100644 (file)
@@ -1341,7 +1341,17 @@ return [
                'styles' => 'resources/src/mediawiki.checkboxtoggle.styles.css',
        ],
        'mediawiki.cookie' => [
-               'scripts' => 'resources/src/mediawiki.cookie/index.js',
+               'localBasePath' => "$IP/resources/src/mediawiki.cookie",
+               'remoteBasePath' => "$wgResourceBasePath/resources/src/mediawiki.cookie",
+               'packageFiles' => [
+                       'index.js',
+                       [ 'name' => 'config.json', 'config' => [
+                               'prefix' => 'CookiePrefix',
+                               'domain' => 'CookieDomain',
+                               'path' => 'CookiePath',
+                               'expires' => 'CookieExpiration'
+                       ] ],
+               ],
                'dependencies' => 'jquery.cookie',
                'targets' => [ 'desktop', 'mobile' ],
        ],
index 76038f6..61379ae 100644 (file)
@@ -1,14 +1,21 @@
 ( function () {
        'use strict';
 
+       var config = require( './config.json' ),
+               defaults = {
+                       prefix: config.prefix,
+                       domain: config.domain,
+                       path: config.path,
+                       expires: config.expires,
+                       secure: false
+               };
+
        /**
-        * Provides an API for getting and setting cookies that is
-        * syntactically and functionally similar to the server-side cookie
-        * API (`WebRequest#getCookie` and `WebResponse#setcookie`).
+        * Manage cookies in a way that is syntactically and functionally similar
+        * to the `WebRequest#getCookie` and `WebResponse#setcookie` methods in PHP.
         *
         * @author Sam Smith <samsmith@wikimedia.org>
         * @author Matthew Flaschen <mflaschen@wikimedia.org>
-        * @author Timo Tijhof <krinklemail@gmail.com>
         *
         * @class mw.cookie
         * @singleton
                /**
                 * Set or delete a cookie.
                 *
-                * While this is natural in JavaScript, contrary to `WebResponse#setcookie` in PHP, the
-                * default values for the `options` properties only apply if that property isn't set
-                * already in your options object (e.g. passing `{ secure: null }` or `{ secure: undefined }`
-                * overrides the default value for `options.secure`).
+                * **Note:** If explicitly passing `null` or `undefined` for an options key,
+                * that will override the default. This is natural in JavaScript, but noted
+                * here because it is contrary to MediaWiki's `WebResponse#setcookie()` method
+                * in PHP.
                 *
                 * @param {string} key
                 * @param {string|null} value Value of cookie. If `value` is `null` then this method will
                 *   instead remove a cookie by name of `key`.
                 * @param {Object|Date|number} [options] Options object, or expiry date
-                * @param {Date|number|null} [options.expires] The expiry date of the cookie, or lifetime in seconds.
-                *
-                *   If `options.expires` is null, then a session cookie is set.
-                *
-                *   By default cookie expiration is based on `wgCookieExpiration`. Similar to `WebResponse`
-                *   in PHP, we set a session cookie if `wgCookieExpiration` is 0. And for non-zero values
-                *   it is interpreted as lifetime in seconds.
-                *
+                * @param {Date|number|null} [options.expires=wgCookieExpiration] The expiry date of the cookie,
+                *  or lifetime in seconds. If `options.expires` is null or 0, then a session cookie is set.
                 * @param {string} [options.prefix=wgCookiePrefix] The prefix of the key
                 * @param {string} [options.domain=wgCookieDomain] The domain attribute of the cookie
                 * @param {string} [options.path=wgCookiePath] The path attribute of the cookie
                 *   (Does **not** use the wgCookieSecure configuration variable)
                 */
                set: function ( key, value, options ) {
-                       var config, defaultOptions, date;
-
-                       // wgCookieSecure is not used for now, since 'detect' could not work with
-                       // ResourceLoaderStartUpModule, as module cache is not fragmented by protocol.
-                       config = mw.config.get( [
-                               'wgCookiePrefix',
-                               'wgCookieDomain',
-                               'wgCookiePath',
-                               'wgCookieExpiration'
-                       ] );
+                       var date;
 
-                       defaultOptions = {
-                               prefix: config.wgCookiePrefix,
-                               domain: config.wgCookieDomain,
-                               path: config.wgCookiePath,
-                               secure: false
-                       };
-
-                       // Options argument can also be a shortcut for the expiry
-                       // Expiry can be a Date, number or null
-                       if ( !options || options instanceof Date || typeof options === 'number' ) {
-                               // Also takes care of options = undefined, in which case we also don't need $.extend()
-                               defaultOptions.expires = options;
-                               options = defaultOptions;
-                       } else {
-                               options = $.extend( defaultOptions, options );
+                       // The 'options' parameter may be a shortcut for the expiry.
+                       if ( arguments.length > 2 && ( !options || options instanceof Date || typeof options === 'number' ) ) {
+                               options = { expires: options };
                        }
+                       // Apply defaults
+                       options = $.extend( {}, defaults, options );
 
-                       // Default to using wgCookieExpiration (lifetime in seconds).
-                       // If wgCookieExpiration is 0, that is considered a special value indicating
-                       // all cookies should be session cookies by default.
-                       if ( options.expires === undefined && config.wgCookieExpiration !== 0 ) {
-                               date = new Date();
-                               date.setTime( Number( date ) + ( config.wgCookieExpiration * 1000 ) );
-                               options.expires = date;
+                       // Handle prefix
+                       key = options.prefix + key;
+                       // Don't pass invalid option to $.cookie
+                       delete options.prefix;
+
+                       if ( !options.expires ) {
+                               // Session cookie (null or zero)
+                               // Normalize to absent (undefined) for $.cookie.
+                               delete options.expires;
                        } else if ( typeof options.expires === 'number' ) {
                                // Lifetime in seconds
                                date = new Date();
                                date.setTime( Number( date ) + ( options.expires * 1000 ) );
                                options.expires = date;
-                       } else if ( options.expires === null ) {
-                               // $.cookie makes a session cookie when options.expires is omitted
-                               delete options.expires;
                        }
 
-                       // Process prefix
-                       key = options.prefix + key;
-                       delete options.prefix;
-
-                       // Process value
                        if ( value !== null ) {
                                value = String( value );
                        }
 
-                       // Other options are handled by $.cookie
                        $.cookie( key, value, options );
                },
 
                        var result;
 
                        if ( prefix === undefined || prefix === null ) {
-                               prefix = mw.config.get( 'wgCookiePrefix' );
+                               prefix = defaults.prefix;
                        }
 
                        // Was defaultValue omitted?
                }
        };
 
+       if ( window.QUnit ) {
+               module.exports = {
+                       setDefaults: function ( value ) {
+                               var prev = defaults;
+                               defaults = value;
+                               return prev;
+                       }
+               };
+       }
 }() );
index 4606cbd..b3f04b7 100644 (file)
@@ -2,23 +2,31 @@
 
        var NOW = 9012, // miliseconds
                DEFAULT_DURATION = 5678, // seconds
+               jqcookie,
+               defaults = {
+                       prefix: 'mywiki',
+                       domain: 'example.org',
+                       path: '/path',
+                       expires: DEFAULT_DURATION,
+                       secure: false
+               },
+               setDefaults = require( 'mediawiki.cookie' ).setDefaults,
                expiryDate = new Date();
 
        expiryDate.setTime( NOW + ( DEFAULT_DURATION * 1000 ) );
 
-       QUnit.module( 'mediawiki.cookie', QUnit.newMwEnvironment( {
-               setup: function () {
-                       this.stub( $, 'cookie' ).returns( null );
-
-                       this.sandbox.useFakeTimers( NOW );
+       QUnit.module( 'mediawiki.cookie', {
+               beforeEach: function () {
+                       jqcookie = sinon.stub( $, 'cookie' ).returns( null );
+                       this.clock = sinon.useFakeTimers( NOW );
+                       this.savedDefaults = setDefaults( defaults );
                },
-               config: {
-                       wgCookiePrefix: 'mywiki',
-                       wgCookieDomain: 'example.org',
-                       wgCookiePath: '/path',
-                       wgCookieExpiration: DEFAULT_DURATION
+               afterEach: function () {
+                       jqcookie.restore();
+                       this.clock.restore();
+                       setDefaults( this.savedDefaults );
                }
-       } ) );
+       } );
 
        QUnit.test( 'set( key, value )', function ( assert ) {
                var call;
@@ -26,7 +34,7 @@
                // Simple case
                mw.cookie.set( 'foo', 'bar' );
 
-               call = $.cookie.lastCall.args;
+               call = jqcookie.lastCall.args;
                assert.strictEqual( call[ 0 ], 'mywikifoo' );
                assert.strictEqual( call[ 1 ], 'bar' );
                assert.deepEqual( call[ 2 ], {
                } );
 
                mw.cookie.set( 'foo', null );
-               call = $.cookie.lastCall.args;
+               call = jqcookie.lastCall.args;
                assert.strictEqual( call[ 1 ], null, 'null removes cookie' );
 
                mw.cookie.set( 'foo', undefined );
-               call = $.cookie.lastCall.args;
+               call = jqcookie.lastCall.args;
                assert.strictEqual( call[ 1 ], 'undefined', 'undefined is value' );
 
                mw.cookie.set( 'foo', false );
-               call = $.cookie.lastCall.args;
+               call = jqcookie.lastCall.args;
                assert.strictEqual( call[ 1 ], 'false', 'false is a value' );
 
                mw.cookie.set( 'foo', 0 );
-               call = $.cookie.lastCall.args;
+               call = jqcookie.lastCall.args;
                assert.strictEqual( call[ 1 ], '0', '0 is value' );
        } );
 
                date.setTime( 1234 );
 
                mw.cookie.set( 'foo', 'bar' );
-               options = $.cookie.lastCall.args[ 2 ];
+               options = jqcookie.lastCall.args[ 2 ];
                assert.deepEqual( options.expires, expiryDate, 'default expiration' );
 
                mw.cookie.set( 'foo', 'bar', date );
-               options = $.cookie.lastCall.args[ 2 ];
+               options = jqcookie.lastCall.args[ 2 ];
                assert.strictEqual( options.expires, date, 'custom expiration as Date' );
 
                date = new Date();
                date.setDate( date.getDate() + 1 );
 
                mw.cookie.set( 'foo', 'bar', 86400 );
-               options = $.cookie.lastCall.args[ 2 ];
+               options = jqcookie.lastCall.args[ 2 ];
                assert.deepEqual( options.expires, date, 'custom expiration as lifetime in seconds' );
 
                mw.cookie.set( 'foo', 'bar', null );
-               options = $.cookie.lastCall.args[ 2 ];
+               options = jqcookie.lastCall.args[ 2 ];
                assert.strictEqual( options.expires, undefined, 'null forces session cookie' );
 
-               // Per DefaultSettings.php, when wgCookieExpiration is 0, the default should
-               // be session cookies
-               mw.config.set( 'wgCookieExpiration', 0 );
+               // Per DefaultSettings.php, if wgCookieExpiration is 0,
+               // then the default should be session cookies
+               setDefaults( $.extend( {}, defaults, { expires: 0 } ) );
 
                mw.cookie.set( 'foo', 'bar' );
-               options = $.cookie.lastCall.args[ 2 ];
+               options = jqcookie.lastCall.args[ 2 ];
                assert.strictEqual( options.expires, undefined, 'wgCookieExpiration=0 results in session cookies by default' );
 
                mw.cookie.set( 'foo', 'bar', date );
-               options = $.cookie.lastCall.args[ 2 ];
+               options = jqcookie.lastCall.args[ 2 ];
                assert.strictEqual( options.expires, date, 'custom expiration (with wgCookieExpiration=0)' );
        } );
 
                        secure: true
                } );
 
-               call = $.cookie.lastCall.args;
+               call = jqcookie.lastCall.args;
                assert.strictEqual( call[ 0 ], 'myPrefixfoo' );
                assert.deepEqual( call[ 2 ], {
                        expires: expiryDate,
                        secure: true
                } );
 
-               call = $.cookie.lastCall.args;
+               call = jqcookie.lastCall.args;
                assert.strictEqual( call[ 0 ], 'myPrefixfoo' );
                assert.deepEqual( call[ 2 ], {
                        expires: date,
 
                mw.cookie.get( 'foo' );
 
-               key = $.cookie.lastCall.args[ 0 ];
+               key = jqcookie.lastCall.args[ 0 ];
                assert.strictEqual( key, 'mywikifoo', 'Default prefix' );
 
                mw.cookie.get( 'foo', undefined );
-               key = $.cookie.lastCall.args[ 0 ];
+               key = jqcookie.lastCall.args[ 0 ];
                assert.strictEqual( key, 'mywikifoo', 'Use default prefix for undefined' );
 
                mw.cookie.get( 'foo', null );
-               key = $.cookie.lastCall.args[ 0 ];
+               key = jqcookie.lastCall.args[ 0 ];
                assert.strictEqual( key, 'mywikifoo', 'Use default prefix for null' );
 
                mw.cookie.get( 'foo', '' );
-               key = $.cookie.lastCall.args[ 0 ];
+               key = jqcookie.lastCall.args[ 0 ];
                assert.strictEqual( key, 'foo', 'Don\'t use default prefix for empty string' );
 
                value = mw.cookie.get( 'foo' );
        QUnit.test( 'get( key ) - with value', function ( assert ) {
                var value;
 
-               $.cookie.returns( 'bar' );
+               jqcookie.returns( 'bar' );
 
                value = mw.cookie.get( 'foo' );
                assert.strictEqual( value, 'bar', 'Return value of cookie' );
 
                mw.cookie.get( 'foo', 'bar' );
 
-               key = $.cookie.lastCall.args[ 0 ];
+               key = jqcookie.lastCall.args[ 0 ];
                assert.strictEqual( key, 'barfoo' );
        } );