Krinkle has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/323352

Change subject: qunit: Don't assume synchronous Deferred.resolve
......................................................................

qunit: Don't assume synchronous Deferred.resolve

Either return a Promise to QUnit.test (which QUnit will automatically
wait for and also report a test failure if the Promise is rejected),
or manually use assert.async() where needed.

This is in preparation for jQuery 3.0, where $.Deferred callbacks are
processed asynchronously. <https://jquery.com/upgrade-guide/3.0/>

Bug: T124742
Change-Id: If7ee1c6025be70fecc0a93d4ac155da4db6571ab
---
M tests/qunit/mobile.editor.api/test_EditorGateway.js
M tests/qunit/mobile.editor.overlay/test_EditorOverlay.js
M tests/qunit/mobile.nearby/test_NearbyGateway.js
M tests/qunit/mobile.references.gateway/test_ReferencesMobileViewGateway.js
M tests/qunit/mobile.startup/test_OverlayManager.js
M tests/qunit/mobile.watchlist/test_WatchListGateway.js
6 files changed, 139 insertions(+), 120 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/MobileFrontend 
refs/changes/52/323352/1

diff --git a/tests/qunit/mobile.editor.api/test_EditorGateway.js 
b/tests/qunit/mobile.editor.api/test_EditorGateway.js
index 4bf3392..a16d23a 100644
--- a/tests/qunit/mobile.editor.api/test_EditorGateway.js
+++ b/tests/qunit/mobile.editor.api/test_EditorGateway.js
@@ -109,9 +109,10 @@
                gateway.getContent();
                gateway.setContent( 'section 1' );
                assert.strictEqual( gateway.hasChanged, true, 'hasChanged is 
true' );
-               gateway.save( {
+               return gateway.save( {
                        summary: 'summary'
-               } ).done( function () {
+               } ).then( function () {
+                       assert.strictEqual( gateway.hasChanged, false, 'reset 
hasChanged' );
                        assert.ok( postStub.calledWith( {
                                action: 'edit',
                                title: 'test',
@@ -125,7 +126,6 @@
                                starttimestamp: '2013-05-15T00:30:26Z'
                        } ), 'save first section' );
                } );
-               assert.strictEqual( gateway.hasChanged, false, 'reset 
hasChanged' );
        } );
 
        QUnit.test( '#save, new page', 2, function ( assert ) {
@@ -146,9 +146,10 @@
 
                gateway.getContent();
                gateway.setContent( 'section 0' );
-               gateway.save( {
+               return gateway.save( {
                        summary: 'summary'
-               } ).done( function () {
+               } ).then( function () {
+                       assert.strictEqual( gateway.hasChanged, false, 'reset 
hasChanged' );
                        assert.ok( postStub.calledWith( {
                                action: 'edit',
                                title: 'Talk:test',
@@ -161,7 +162,6 @@
                                starttimestamp: undefined
                        } ), 'save lead section' );
                } );
-               assert.strictEqual( gateway.hasChanged, false, 'reset 
hasChanged' );
        } );
 
        QUnit.test( '#save, after #setPrependText', 2, function ( assert ) {
@@ -180,9 +180,10 @@
                ) );
 
                gateway.setPrependText( 'abc' );
-               gateway.save( {
+               return gateway.save( {
                        summary: 'summary'
-               } ).done( function () {
+               } ).then( function () {
+                       assert.strictEqual( gateway.hasChanged, false, 'reset 
hasChanged' );
                        assert.ok( postStub.calledWith( {
                                action: 'edit',
                                title: 'test',
@@ -195,7 +196,6 @@
                                starttimestamp: undefined
                        } ), 'prepend text' );
                } );
-               assert.strictEqual( gateway.hasChanged, false, 'reset 
hasChanged' );
        } );
 
        QUnit.test( '#save, submit CAPTCHA', 2, function ( assert ) {
@@ -216,11 +216,12 @@
 
                gateway.getContent();
                gateway.setContent( 'section 1' );
-               gateway.save( {
+               return gateway.save( {
                        summary: 'summary',
                        captchaId: 123,
                        captchaWord: 'abc'
-               } ).done( function () {
+               } ).then( function () {
+                       assert.strictEqual( gateway.hasChanged, false, 'reset 
hasChanged' );
                        assert.ok( postStub.calledWith( {
                                action: 'edit',
                                title: 'test',
@@ -234,7 +235,6 @@
                                starttimestamp: '2013-05-15T00:30:26Z'
                        } ), 'save first section' );
                } );
-               assert.strictEqual( gateway.hasChanged, false, 'reset 
hasChanged' );
        } );
 
        QUnit.test( '#save, request failure', 2, function ( assert ) {
@@ -244,20 +244,21 @@
                                sectionId: 1
                        } ),
                        doneSpy = this.sandbox.spy(),
-                       failSpy = this.sandbox.spy();
+                       failSpy = this.sandbox.spy(),
+                       done = assert.async();
 
                this.sandbox.stub( mw.Api.prototype, 'post' ).returns( 
$.Deferred().reject() );
 
                gateway.getContent();
                gateway.setContent( 'section 1' );
-
-               gateway.save().done( doneSpy ).fail( failSpy );
-
-               assert.ok( failSpy.calledWith( {
-                       type: 'error',
-                       details: 'http'
-               } ), 'call fail' );
-               assert.ok( !doneSpy.called, 'don\'t call done' );
+               gateway.save().done( doneSpy ).fail( failSpy ).always( function 
() {
+                       assert.ok( failSpy.calledWith( {
+                               type: 'error',
+                               details: 'http'
+                       } ), 'call fail' );
+                       assert.ok( !doneSpy.called, 'don\'t call done' );
+                       done();
+               } );
        } );
 
        QUnit.test( '#save, API failure', 2, function ( assert ) {
@@ -267,7 +268,8 @@
                                sectionId: 1
                        } ),
                        doneSpy = this.sandbox.spy(),
-                       failSpy = this.sandbox.spy();
+                       failSpy = this.sandbox.spy(),
+                       done = assert.async();
 
                this.sandbox.stub( mw.Api.prototype, 'post' ).returns( 
$.Deferred().resolve(
                        {
@@ -279,14 +281,14 @@
 
                gateway.getContent();
                gateway.setContent( 'section 1' );
-
-               gateway.save().done( doneSpy ).fail( failSpy );
-
-               assert.ok( failSpy.calledWith( {
-                       type: 'error',
-                       details: 'error code'
-               } ), 'call fail' );
-               assert.ok( !doneSpy.called, 'don\'t call done' );
+               gateway.save().done( doneSpy ).fail( failSpy ).always( function 
() {
+                       assert.ok( failSpy.calledWith( {
+                               type: 'error',
+                               details: 'error code'
+                       } ), 'call fail' );
+                       assert.ok( !doneSpy.called, 'don\'t call done' );
+                       done();
+               } );
        } );
 
        QUnit.test( '#save, CAPTCHA response with image URL', 2, function ( 
assert ) {
@@ -302,7 +304,8 @@
                                url: 
'/w/index.php?title=Especial:Captcha/image&wpCaptchaId=1852528679'
                        },
                        doneSpy = this.sandbox.spy(),
-                       failSpy = this.sandbox.spy();
+                       failSpy = this.sandbox.spy(),
+                       done = assert.async();
 
                this.sandbox.stub( mw.Api.prototype, 'post' ).returns( 
$.Deferred().resolve( {
                        edit: {
@@ -313,14 +316,14 @@
 
                gateway.getContent();
                gateway.setContent( 'section 1' );
-
-               gateway.save().done( doneSpy ).fail( failSpy );
-
-               assert.ok( failSpy.calledWith( {
-                       type: 'captcha',
-                       details: captcha
-               } ), 'call fail' );
-               assert.ok( !doneSpy.called, 'don\'t call done' );
+               gateway.save().done( doneSpy ).fail( failSpy ).always( function 
() {
+                       assert.ok( failSpy.calledWith( {
+                               type: 'captcha',
+                               details: captcha
+                       } ), 'call fail' );
+                       assert.ok( !doneSpy.called, 'don\'t call done' );
+                       done();
+               } );
        } );
 
        QUnit.test( '#save, AbuseFilter warning', 2, function ( assert ) {
@@ -330,7 +333,8 @@
                                sectionId: 1
                        } ),
                        doneSpy = this.sandbox.spy(),
-                       failSpy = this.sandbox.spy();
+                       failSpy = this.sandbox.spy(),
+                       done = assert.async();
 
                this.sandbox.stub( mw.Api.prototype, 'post' ).returns( 
$.Deferred().resolve( {
                        edit: {
@@ -343,17 +347,17 @@
 
                gateway.getContent();
                gateway.setContent( 'section 1' );
-
-               gateway.save().done( doneSpy ).fail( failSpy );
-
-               assert.ok( failSpy.calledWith( {
-                       type: 'abusefilter',
-                       details: {
-                               type: 'warning',
-                               message: 'horrible desktop-formatted message'
-                       }
-               } ), 'call fail with type and message' );
-               assert.ok( !doneSpy.called, 'don\'t call done' );
+               gateway.save().done( doneSpy ).fail( failSpy ).always( function 
() {
+                       assert.ok( failSpy.calledWith( {
+                               type: 'abusefilter',
+                               details: {
+                                       type: 'warning',
+                                       message: 'horrible desktop-formatted 
message'
+                               }
+                       } ), 'call fail with type and message' );
+                       assert.ok( !doneSpy.called, 'don\'t call done' );
+                       done();
+               } );
        } );
 
        QUnit.test( '#save, AbuseFilter disallow', 2, function ( assert ) {
@@ -363,7 +367,8 @@
                                sectionId: 1
                        } ),
                        doneSpy = this.sandbox.spy(),
-                       failSpy = this.sandbox.spy();
+                       failSpy = this.sandbox.spy(),
+                       done = assert.async();
 
                this.sandbox.stub( mw.Api.prototype, 'post' ).returns( 
$.Deferred().resolve( {
                        edit: {
@@ -376,17 +381,17 @@
 
                gateway.getContent();
                gateway.setContent( 'section 1' );
-
-               gateway.save().done( doneSpy ).fail( failSpy );
-
-               assert.ok( failSpy.calledWith( {
-                       type: 'abusefilter',
-                       details: {
-                               type: 'disallow',
-                               message: 'horrible desktop-formatted message'
-                       }
-               } ), 'call fail with type and message' );
-               assert.ok( !doneSpy.called, 'don\'t call done' );
+               gateway.save().done( doneSpy ).fail( failSpy ).always( function 
() {
+                       assert.ok( failSpy.calledWith( {
+                               type: 'abusefilter',
+                               details: {
+                                       type: 'disallow',
+                                       message: 'horrible desktop-formatted 
message'
+                               }
+                       } ), 'call fail with type and message' );
+                       assert.ok( !doneSpy.called, 'don\'t call done' );
+                       done();
+               } );
        } );
 
        QUnit.test( '#save, AbuseFilter other', 2, function ( assert ) {
@@ -396,7 +401,8 @@
                                sectionId: 1
                        } ),
                        doneSpy = this.sandbox.spy(),
-                       failSpy = this.sandbox.spy();
+                       failSpy = this.sandbox.spy(),
+                       done = assert.async();
 
                this.sandbox.stub( mw.Api.prototype, 'post' ).returns( 
$.Deferred().resolve( {
                        edit: {
@@ -409,17 +415,17 @@
 
                gateway.getContent();
                gateway.setContent( 'section 1' );
-
-               gateway.save().done( doneSpy ).fail( failSpy );
-
-               assert.ok( failSpy.calledWith( {
-                       type: 'abusefilter',
-                       details: {
-                               type: 'other',
-                               message: 'horrible desktop-formatted message'
-                       }
-               } ), 'call fail with type and message' );
-               assert.ok( !doneSpy.called, 'don\'t call done' );
+               gateway.save().done( doneSpy ).fail( failSpy ).always( function 
() {
+                       assert.ok( failSpy.calledWith( {
+                               type: 'abusefilter',
+                               details: {
+                                       type: 'other',
+                                       message: 'horrible desktop-formatted 
message'
+                               }
+                       } ), 'call fail with type and message' );
+                       assert.ok( !doneSpy.called, 'don\'t call done' );
+                       done();
+               } );
        } );
 
        QUnit.test( '#save, extension errors', 2, function ( assert ) {
@@ -429,7 +435,8 @@
                                sectionId: 1
                        } ),
                        doneSpy = this.sandbox.spy(),
-                       failSpy = this.sandbox.spy();
+                       failSpy = this.sandbox.spy(),
+                       done = assert.async();
 
                this.sandbox.stub( mw.Api.prototype, 'post' ).returns( 
$.Deferred().resolve( {
                        edit: {
@@ -440,14 +447,14 @@
 
                gateway.getContent();
                gateway.setContent( 'section 1' );
-
-               gateway.save().done( doneSpy ).fail( failSpy );
-
-               assert.ok( failSpy.calledWith( {
-                       type: 'error',
-                       details: 'testerror'
-               } ), 'call fail with code' );
-               assert.ok( !doneSpy.called, 'don\'t call done' );
+               gateway.save().done( doneSpy ).fail( failSpy ).always( function 
() {
+                       assert.ok( failSpy.calledWith( {
+                               type: 'error',
+                               details: 'testerror'
+                       } ), 'call fail with code' );
+                       assert.ok( !doneSpy.called, 'don\'t call done' );
+                       done();
+               } );
        } );
 
        QUnit.test( '#save, unknown errors', 2, function ( assert ) {
@@ -457,20 +464,22 @@
                                sectionId: 1
                        } ),
                        doneSpy = this.sandbox.spy(),
-                       failSpy = this.sandbox.spy();
+                       failSpy = this.sandbox.spy(),
+                       done = assert.async();
 
                this.sandbox.stub( mw.Api.prototype, 'post' ).returns( 
$.Deferred().resolve( {} ) );
 
                gateway.getContent();
                gateway.setContent( 'section 1' );
 
-               gateway.save().done( doneSpy ).fail( failSpy );
-
-               assert.ok( failSpy.calledWith( {
-                       type: 'error',
-                       details: 'unknown'
-               } ), 'call fail with unknown' );
-               assert.ok( !doneSpy.called, 'don\'t call done' );
+               gateway.save().done( doneSpy ).fail( failSpy ).always( function 
() {
+                       assert.ok( failSpy.calledWith( {
+                               type: 'error',
+                               details: 'unknown'
+                       } ), 'call fail with unknown' );
+                       assert.ok( !doneSpy.called, 'don\'t call done' );
+                       done();
+               } );
        } );
 
        QUnit.test( '#save, without changes', 2, function ( assert ) {
@@ -492,9 +501,9 @@
                gateway.getContent();
                gateway.setContent( 'section' );
                assert.strictEqual( gateway.hasChanged, false, 'hasChanged is 
false' );
-               gateway.save( {
+               return gateway.save( {
                        summary: 'summary'
-               } ).done( function () {
+               } ).then( function () {
                        assert.ok( postStub.calledWith( {
                                action: 'edit',
                                title: 'test',
@@ -593,9 +602,10 @@
 
        QUnit.test( '#save, when token has expired', 2, function ( assert ) {
                var gateway = new EditorGateway( {
-                       api: new mw.Api(),
-                       title: 'MediaWiki:Test.css'
-               } );
+                               api: new mw.Api(),
+                               title: 'MediaWiki:Test.css'
+                       } ),
+                       done = assert.async();
 
                this.sandbox.stub( mw.Api.prototype, 'post' )
                        .onFirstCall().returns( $.Deferred().reject( 'badtoken' 
) )
@@ -611,9 +621,11 @@
 
                gateway.getContent();
                gateway.setContent( 'section 1' );
-               gateway.save();
-               assert.ok( mw.Api.prototype.getToken.calledTwice, 'check the 
spy was called twice' );
-               assert.ok( mw.Api.prototype.post.calledTwice, 'check the spy 
was called twice' );
+               gateway.save().always( function () {
+                       assert.ok( mw.Api.prototype.getToken.calledTwice, 
'check the spy was called twice' );
+                       assert.ok( mw.Api.prototype.post.calledTwice, 'check 
the spy was called twice' );
+                       done();
+               } );
        } );
 
 }( mw.mobileFrontend, jQuery ) );
diff --git a/tests/qunit/mobile.editor.overlay/test_EditorOverlay.js 
b/tests/qunit/mobile.editor.overlay/test_EditorOverlay.js
index bf409a8..b23cbec 100644
--- a/tests/qunit/mobile.editor.overlay/test_EditorOverlay.js
+++ b/tests/qunit/mobile.editor.overlay/test_EditorOverlay.js
@@ -10,11 +10,12 @@
                        this.sandbox.stub( EditorOverlay.prototype, 'log' 
).returns( $.Deferred().resolve() );
                        getContentStub = this.sandbox.stub( 
EditorGateway.prototype, 'getContent' );
                        // the first call returns a getContent deferred for a 
blocked user.
-                       getContentStub.onCall( 0 ).returns( 
$.Deferred().resolve( 'section 0', {
+                       this.dBlockedContent = $.Deferred().resolve( 'section 
0', {
                                blockid: 1,
                                blockedby: 'Test',
                                blockreason: 'Testreason'
-                       } ) );
+                       } );
+                       getContentStub.onCall( 0 ).returns( 
this.dBlockedContent );
                        // all other calls returns a deferred for unblocked 
users.
                        getContentStub.returns( $.Deferred().resolve( 'section 
0', {} ) );
                        this.sandbox.stub( EditorGateway.prototype, 
'getPreview' )
@@ -29,11 +30,13 @@
                        title: 'test.css'
                } );
 
-               assert.strictEqual(
-                       $( '.mw-notification-content' ).text(),
-                       'Your IP address is blocked from editing. The block was 
made by Test for the following reason: Testreason.',
-                       'There is a toast notice, that i am blocked from 
editing'
-               );
+               return this.dBlockedContent.then( function () {
+                       assert.strictEqual(
+                               $( '.mw-notification-content' ).text(),
+                               'Your IP address is blocked from editing. The 
block was made by Test for the following reason: Testreason.',
+                               'There is a toast notice, that i am blocked 
from editing'
+                       );
+               } );
        } );
 
        QUnit.test( '#initialize, with given page and section', 5, function ( 
assert ) {
diff --git a/tests/qunit/mobile.nearby/test_NearbyGateway.js 
b/tests/qunit/mobile.nearby/test_NearbyGateway.js
index 01a439a..279c15a 100644
--- a/tests/qunit/mobile.nearby/test_NearbyGateway.js
+++ b/tests/qunit/mobile.nearby/test_NearbyGateway.js
@@ -93,10 +93,10 @@
        } );
 
        QUnit.test( '#getPages', 6, function ( assert ) {
-               m.getPages( {
+               return m.getPages( {
                        latitude: 37.787,
                        longitude: -122.51
-               } ).done( function ( pages ) {
+               } ).then( function ( pages ) {
                        assert.strictEqual( pages.length, 3 );
                        assert.strictEqual( pages[ 0 ].title, 'Wikimedia 
Foundation' );
                        assert.ok( !pages[ 0 ].thumbnail.isLandscape );
@@ -107,7 +107,7 @@
        } );
 
        QUnit.test( '#getPagesAroundPage', 4, function ( assert ) {
-               m.getPagesAroundPage( 'Wikimedia Foundation' ).done( function ( 
pages ) {
+               return m.getPagesAroundPage( 'Wikimedia Foundation' ).then( 
function ( pages ) {
                        assert.strictEqual( pages.length, 2 );
                        assert.strictEqual( pages[ 1 ].title, 'W San Francisco' 
);
                        assert.strictEqual( pages[ 1 ].thumbnail, undefined );
diff --git 
a/tests/qunit/mobile.references.gateway/test_ReferencesMobileViewGateway.js 
b/tests/qunit/mobile.references.gateway/test_ReferencesMobileViewGateway.js
index cdeb144..79b46ed 100644
--- a/tests/qunit/mobile.references.gateway/test_ReferencesMobileViewGateway.js
+++ b/tests/qunit/mobile.references.gateway/test_ReferencesMobileViewGateway.js
@@ -48,10 +48,11 @@
 
        QUnit.test( 'Gateway only hits api once despite multiple calls', 1, 
function ( assert ) {
                var gatewayHitsApi = new ReferencesMobileViewGateway( this.api, 
new MemoryCache() );
-               gatewayHitsApi.getReferencesLists( this.page );
-               gatewayHitsApi.getReferencesLists( this.page );
-               gatewayHitsApi.getReferencesLists( this.page );
-               assert.strictEqual( this.api.get.calledOnce, true, 'The API 
should only ever be hit once.' );
+               return gatewayHitsApi.getReferencesLists( this.page ).then( 
function () {
+                       gatewayHitsApi.getReferencesLists( this.page );
+                       gatewayHitsApi.getReferencesLists( this.page );
+                       assert.strictEqual( this.api.get.calledOnce, true, 'The 
API should only ever be hit once.' );
+               }.bind( this ) );
        } );
 
        QUnit.test( 'checking good reference', 1, function ( assert ) {
diff --git a/tests/qunit/mobile.startup/test_OverlayManager.js 
b/tests/qunit/mobile.startup/test_OverlayManager.js
index ea5c9ed..075d28d 100644
--- a/tests/qunit/mobile.startup/test_OverlayManager.js
+++ b/tests/qunit/mobile.startup/test_OverlayManager.js
@@ -61,7 +61,10 @@
                        return fakeOverlay;
                } );
 
-               assert.ok( fakeOverlay.show.calledOnce, 'show registered 
overlay' );
+               // Wait for $.ready because OverlayManager#add() does
+               return $.when( $.ready ).then( function () {
+                       assert.ok( fakeOverlay.show.calledOnce, 'show 
registered overlay' );
+               } );
        } );
 
        QUnit.test( '#replaceCurrent', 3, function ( assert ) {
diff --git a/tests/qunit/mobile.watchlist/test_WatchListGateway.js 
b/tests/qunit/mobile.watchlist/test_WatchListGateway.js
index b1dbd0b..5d31283 100644
--- a/tests/qunit/mobile.watchlist/test_WatchListGateway.js
+++ b/tests/qunit/mobile.watchlist/test_WatchListGateway.js
@@ -95,7 +95,7 @@
                this.sandbox.stub( mw.Api.prototype, 'get' )
                        .returns( $.Deferred().resolve( response ) );
 
-               gateway.loadWatchlist().done( function ( pages ) {
+               return gateway.loadWatchlist().then( function ( pages ) {
                        var params = mw.Api.prototype.get.firstCall.args[0];
 
                        assert.strictEqual( params.continue, '', 'It should set 
the continue parameter' );
@@ -121,7 +121,7 @@
                stub = this.sandbox.stub( mw.Api.prototype, 'get' )
                        .returns( $.Deferred().resolve( response1 ) );
 
-               gateway.loadWatchlist().done( function ( pages ) {
+               return gateway.loadWatchlist().then( function ( pages ) {
                        var params = mw.Api.prototype.get.firstCall.args[0];
 
                        assert.strictEqual( params.continue, 'gwrcontinue||', 
'It should set the continue parameter' );
@@ -135,7 +135,7 @@
                        // Let's call for the next page
                        stub.returns( $.Deferred().resolve( response ) );
 
-                       gateway.loadWatchlist().done( function ( pages ) {
+                       return gateway.loadWatchlist().then( function ( pages ) 
{
                                // Albert Einstein should be the first result 
of the next page (not removed)
                                assert.equal( pages.length, 7, 'Albert should 
be in the results' );
                                assert.equal( pages[0].displayTitle, 'Albert 
Einstein', 'First item should be Albert' );
@@ -151,7 +151,7 @@
                                batchcomplete: ''
                        } ) );
 
-               gateway.loadWatchlist().done( function ( pages ) {
+               return gateway.loadWatchlist().then( function ( pages ) {
                        assert.deepEqual( pages, [] );
                } );
        } );
@@ -162,7 +162,7 @@
                this.sandbox.stub( mw.Api.prototype, 'get' )
                        .returns( $.Deferred().resolve( response ) );
 
-               gateway.loadWatchlist().done( function ( pages ) {
+               return gateway.loadWatchlist().then( function ( pages ) {
                        assert.equal( pages[0].isMissing, false, 'Albert 
Einstein page isn\'t marked as new' );
                        assert.equal( pages[6].isMissing, true, 'zzzz page is 
marked as new' );
                } );

-- 
To view, visit https://gerrit.wikimedia.org/r/323352
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: If7ee1c6025be70fecc0a93d4ac155da4db6571ab
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: Krinkle <krinklem...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to