jenkins-bot has submitted this change and it was merged.

Change subject: Story 1521: Allow switching from VisualEditor to Wikitext editor
......................................................................


Story 1521: Allow switching from VisualEditor to Wikitext editor

Also had to clean up a lot of header CSS in the process.

Also add tests to show expected behaviour of the toggle button in
the VisualEditor overlay.

Change-Id: I6e289d02612eb2093dd7ef0a4026b2dfce493ac0
---
M i18n/en.json
M i18n/qqq.json
M includes/Resources.php
M javascripts/modules/editor/EditorOverlay.js
M javascripts/modules/editor/EditorOverlayBase.js
M javascripts/modules/editor/VisualEditorOverlay.js
M less/common/OverlayNew.less
M less/minerva.less/minerva.variables.less
M less/modules/editor/VisualEditorOverlay.less
M less/modules/editor/editor.less
A less/modules/editor/images/edit-source-normal.png
A less/modules/editor/images/edit-source-normal.svg
A less/modules/editor/images/edit-source-select.png
A less/modules/editor/images/edit-source-select.svg
A less/modules/editor/images/edit-source-toggle.png
A less/modules/editor/images/edit-source-toggle.svg
A less/modules/editor/images/edit-visual-normal.png
A less/modules/editor/images/edit-visual-normal.svg
A less/modules/editor/images/edit-visual-select.png
A less/modules/editor/images/edit-visual-select.svg
A less/modules/editor/images/edit-visual-toggle.png
A less/modules/editor/images/edit-visual-toggle.svg
M templates/OverlayNew.html
M templates/modules/editor/EditorOverlayBase.html
M templates/modules/editor/EditorOverlayHeader.html
M templates/modules/editor/VisualEditorOverlayHeader.html
M templates/modules/search/SearchOverlay.html
M tests/browser/README.mediawiki
M tests/browser/features/editor_ve.feature
M tests/browser/features/editor_wikitext_nosave.feature
M tests/browser/features/step_definitions/editor_steps.rb
M tests/browser/features/step_definitions/editor_ve_steps.rb
M tests/browser/features/support/pages/article_page.rb
33 files changed, 320 insertions(+), 74 deletions(-)

Approvals:
  Jdlrobson: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/i18n/en.json b/i18n/en.json
index 28ff2d7..feb1c60 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -68,10 +68,13 @@
     "mobile-frontend-editor-previewing-page": 
"<strong>Previewing</strong><span> $1</span>",
     "mobile-frontend-editor-redirect-title": "Redirecting to editor...",
     "mobile-frontend-editor-save": "Save",
+    "mobile-frontend-editor-source-editor": "Source editor",
     "mobile-frontend-editor-success": "Success! Your edit was saved.",
     "mobile-frontend-editor-success-landmark-1": "Congratulations, you are now 
a {{SITENAME}} editor!",
     "mobile-frontend-editor-success-new-page": "Congratulations on creating a 
new page!",
     "mobile-frontend-editor-summary-placeholder": "Tell us what you changed 
(optional)",
+    "mobile-frontend-editor-switch-confirm": "You must save your edit before 
switching to another editing mode.",
+    "mobile-frontend-editor-switch-editor": "Switch editor",
     "mobile-frontend-editor-tutorial-alt-summary": "Why not improve $1? Don't 
be afraid of the markup!",
     "mobile-frontend-editor-tutorial-cancel": "No, thanks",
     "mobile-frontend-editor-tutorial-confirm": "Start editing",
@@ -80,6 +83,7 @@
     "mobile-frontend-editor-unavailable-header": "Editor unavailable",
     "mobile-frontend-editor-undo-unsupported": "Undo is not currently 
supported on mobile devices.",
     "mobile-frontend-editor-viewing-source-page": "<strong>Viewing source 
of</strong><span> $1</span>",
+    "mobile-frontend-editor-visual-editor": "Visual editor",
     "mobile-frontend-editor-wait": "Saving edit, please wait.",
     "mobile-frontend-enable-images": "Enable images on mobile site",
     "mobile-frontend-expand-sections-description": "Always expand all sections 
when navigating to a new page.",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 0ff47c0..8aab731 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -92,10 +92,13 @@
     "mobile-frontend-editor-previewing-page": "A heading saying what page is 
being previewed after editing. All text should be wrapped in a STRONG tag 
except the page title itself.\n\nParameters:\n* $1 - page 
title\n{{Related|Mobile-frontend-editor-page}}",
     "mobile-frontend-editor-redirect-title": "Shown as a user is redirected to 
the JavaScript editor after browsing to a URL with action=edit.\n\nUsed as page 
title.",
     "mobile-frontend-editor-save": "Caption for save button on edit 
form.\n\nShould be consistent with the following message:\n* 
{{msg-mw|mobile-frontend-editor-license}}\n{{Identical|Save}}",
+    "mobile-frontend-editor-source-editor": "Label for button that switches to 
the 'source editor' (a WikiText editing interface)",
     "mobile-frontend-editor-success": "Text that displays when a page edit has 
been successfully saved.\n\nUsed as \"toast\" message.\n\nSee also:\n* 
{{msg-mw|Mobile-frontend-editor-success-landmark-1}}",
     "mobile-frontend-editor-success-landmark-1": "Special save success message 
shown to users on their 1st edit.\n\nSee also:\n* 
{{msg-mw|Mobile-frontend-editor-success}}",
     "mobile-frontend-editor-success-new-page": "Text that displays when a new 
page has been successfully saved.\n\nUsed as \"toast\" message.",
     "mobile-frontend-editor-summary-placeholder": "A placeholder for the 
summary input field asking user what they changed (the field is optional).",
+    "mobile-frontend-editor-switch-confirm": "Warning message telling the user 
they must save their changes before switching to a different editing 
interface.",
+    "mobile-frontend-editor-switch-editor": "Label for button that allows the 
user to switch between two different editing interfaces",
     "mobile-frontend-editor-tutorial-alt-summary": "Identical to 
{{msg-mw|Mobile-frontend-editor-tutorial-confirm}}, however shows when the user 
has not expressed an intent to edit so language is more 
suggestive.\n\nParameters:\n* $1 - page title\nSee also:\n* 
{{msg-mw|Mobile-frontend-editor-tutorial-summary}}",
     "mobile-frontend-editor-tutorial-cancel": "Label for button that user can 
click if they do not want to edit.\n{{Identical|No thanks}}",
     "mobile-frontend-editor-tutorial-confirm": "Text for the button a user 
clicks after they have read the editing tutorial and are ready to edit the 
page.\n\nSee also:\n* {{msg-mw|Mobile-frontend-editor-tutorial-summary}}",
@@ -104,6 +107,7 @@
     "mobile-frontend-editor-unavailable-header": "When mobile editing is not 
supported, header of the warning.\n\nFollowed by 
{{msg-mw|Mobile-frontend-editor-unavailable}}.",
     "mobile-frontend-editor-undo-unsupported": "Shown when user attempts to do 
an undo which is currently not supported.",
     "mobile-frontend-editor-viewing-source-page": "A heading saying which 
page's source code is being viewed. All text should be wrapped in a STRONG tag 
except the page title itself.\n\nParameters:\n* $1 - page 
title\n{{Related|Mobile-frontend-editor-page}}",
+    "mobile-frontend-editor-visual-editor": "Label for button that switches to 
the 'visual editor' (a WYSIWYG editing interface). See 
http://www.mediawiki.org/wiki/VisualEditor.";,
     "mobile-frontend-editor-wait": "Text that displays while a page edit is 
being saved.",
     "mobile-frontend-enable-images": "Unused at this time.\n\nSee also:\n* 
{{msg-mw|Mobile-frontend-disable-images}}",
     "mobile-frontend-expand-sections-description": "On settings page 
description for turning on/off expansion of all sections on page load.\n\nSee 
also:\n* {{msg-mw|Mobile-frontend-expand-sections-status}}",
diff --git a/includes/Resources.php b/includes/Resources.php
index ebbd636..40087a6 100644
--- a/includes/Resources.php
+++ b/includes/Resources.php
@@ -349,6 +349,10 @@
                        'mobile-frontend-editor-captcha-try-again',
                        'mobile-frontend-editor-editing-page',
                        'mobile-frontend-editor-previewing-page',
+                       'mobile-frontend-editor-switch-confirm',
+                       'mobile-frontend-editor-visual-editor',
+                       'mobile-frontend-editor-source-editor',
+                       'mobile-frontend-editor-switch-editor',
                ),
        ),
 
diff --git a/javascripts/modules/editor/EditorOverlay.js 
b/javascripts/modules/editor/EditorOverlay.js
index 6ee4ab1..d99f672 100644
--- a/javascripts/modules/editor/EditorOverlay.js
+++ b/javascripts/modules/editor/EditorOverlay.js
@@ -2,7 +2,9 @@
        var EditorOverlayBase = M.require( 'modules/editor/EditorOverlayBase' ),
                popup = M.require( 'toast' ),
                schema = M.require( 'loggingSchemas/mobileWebEditing' ),
+               MobileWebClickTracking = M.require( 
'loggingSchemas/MobileWebClickTracking' ),
                inBetaOrAlpha = M.isBetaGroupMember(),
+               isVisualEditorEnabled = M.isWideScreen() && 
M.isAlphaGroupMember(),
                inKeepGoingCampaign = M.query.campaign === 'mobile-keepgoing',
                inNavSignupCampaign = M.query.campaign === 'leftNavSignup',
                Section = M.require( 'Section' ),
@@ -36,10 +38,15 @@
                                oldId: options.oldId,
                                isNewPage: options.isNewPage
                        } );
-                       this.sectionId = options.sectionId;
                        this.readOnly = options.oldId ? true : false; // If old 
revision, readOnly mode
                        this.funnel = options.funnel;
+                       if ( isVisualEditorEnabled ) {
+                               options.editSwitcher = true;
+                       }
                        this._super( options );
+                       if ( isVisualEditorEnabled ) {
+                               this.initializeSwitcher();
+                       }
                },
 
                postRender: function( options ) {
@@ -63,6 +70,22 @@
                        // make license links open in separate tabs
                        this.$( '.license a' ).attr( 'target', '_blank' );
 
+                       if ( isVisualEditorEnabled ) {
+                               this.$( '.visual-editor' ).on( 'click', 
function() {
+                                       // If changes have been made tell the 
user they have to save first
+                                       if ( !self.api.hasChanged ) {
+                                               MobileWebClickTracking.log( 
'editor-switch-to-visual', options.title );
+                                               // FIXME: Come up with a 
solution that doesn't cause weird behavior
+                                               // when using the close button.
+                                               M.router.navigate( 
'#/VisualEditor/' + options.sectionId );
+                                       } else {
+                                               if ( window.confirm( mw.msg( 
'mobile-frontend-editor-switch-confirm' ) ) ) {
+                                                       self._showPreview();
+                                               }
+                                       }
+                               } );
+                       }
+
                        this.abuseFilterPanel = new 
AbuseFilterPanel().appendTo( this.$( '.panels' ) );
 
                        // If in readOnly mode, make textarea readonly
diff --git a/javascripts/modules/editor/EditorOverlayBase.js 
b/javascripts/modules/editor/EditorOverlayBase.js
index 4f46388..23633fa 100644
--- a/javascripts/modules/editor/EditorOverlayBase.js
+++ b/javascripts/modules/editor/EditorOverlayBase.js
@@ -18,6 +18,9 @@
                        waitMsg: mw.msg( 'mobile-frontend-editor-wait' ),
                        captchaMsg: mw.msg( 
'mobile-frontend-account-create-captcha-placeholder' ),
                        captchaTryAgainMsg: mw.msg( 
'mobile-frontend-editor-captcha-try-again' ),
+                       switchMsg: mw.msg( 
'mobile-frontend-editor-switch-editor' ),
+                       visualEditorMsg: mw.msg( 
'mobile-frontend-editor-visual-editor' ),
+                       sourceEditorMsg: mw.msg( 
'mobile-frontend-editor-source-editor' ),
                },
                template: M.template.get( 'modules/editor/EditorOverlayBase' ),
                className: 'overlay editor-overlay',
@@ -102,6 +105,7 @@
                        this.editCount = user.getEditCount();
                        this.isNewPage = options.isNewPage;
                        this.isNewEditor = options.isNewEditor;
+                       this.sectionId = options.sectionId;
 
                        // pre-fetch keep going with expectation user will go 
on to save
                        if ( this._shouldShowKeepGoingOverlay() ) {
@@ -114,6 +118,29 @@
                        this._super( options );
                        this._showHidden( '.initial-header' );
                },
+               /**
+                * Set up the editor switching interface
+                * The actual behavior of the editor buttons is initialized in 
postRender()
+                */
+               initializeSwitcher: function() {
+                       this.$( '.editor-switcher' ).on( 'click', function( ev 
) {
+                               var $self = $( this );
+                               ev.preventDefault();
+                               // Prevent double toggling
+                               ev.stopPropagation();
+                               // Exit early if switcher is disabled
+                               if ( $self.hasClass( 'disabled' ) ) {
+                                       return false;
+                               }
+                               $self.toggleClass( 'selected' );
+                               $( '.switcher-drop-down' ).toggle();
+                               // If you click outside the drop-down, hide the 
drop-down
+                               $( document ).one( 'click', function() {
+                                       $( '.switcher-drop-down' ).hide();
+                                       $self.removeClass( 'selected' );
+                               } );
+                       } );
+               },
                hide: function( force ) {
                        var confirmMessage = mw.msg( 
'mobile-frontend-editor-cancel-confirm' );
                        if ( force || !this._hasChanged() || window.confirm( 
confirmMessage ) ) {
diff --git a/javascripts/modules/editor/VisualEditorOverlay.js 
b/javascripts/modules/editor/VisualEditorOverlay.js
index 432716d..dd5a2a6 100644
--- a/javascripts/modules/editor/VisualEditorOverlay.js
+++ b/javascripts/modules/editor/VisualEditorOverlay.js
@@ -1,6 +1,7 @@
 ( function( M, $, ve ) {
        var EditorOverlayBase = M.require( 'modules/editor/EditorOverlayBase' ),
                popup = M.require( 'toast' ),
+               MobileWebClickTracking = M.require( 
'loggingSchemas/MobileWebClickTracking' ),
                VisualEditorOverlay;
 
        VisualEditorOverlay = EditorOverlayBase.extend( {
@@ -16,44 +17,61 @@
                        this.hasChanged = false;
                        this.$spinner = self.$( '.spinner' );
                        this.$continueBtn = self.$( '.continue' ).prop( 
'disabled', true );
+                       this.initializeSwitcher();
                },
                show: function() {
                        this._super();
-                       // FIXME: MobileViewTarget does not accept a second 
argument
-                       // FIXME: we have to initialize MobileViewTarget after 
this.$el
-                       // is attached to DOM, maybe we should attach it 
earlier and hide
-                       // overlays in a different way?
-                       this.target = new ve.init.mw.MobileViewTarget( this.$( 
'.surface' ), {
-                               // || undefined so that scrolling is not 
triggered for the lead (0) section
-                               // (which has no header to scroll to)
-                               section: this.options.sectionId || undefined
-                       } );
-                       this.target.activating = true;
-                       this.target.load();
-                       this.target.connect( this, {
-                               save: 'onSave',
-                               saveAsyncBegin: 'showSpinner',
-                               saveAsyncComplete: 'clearSpinner',
-                               saveErrorEmpty: 'onSaveError',
-                               // FIXME: Expand on save errors by having a 
method for each
-                               saveErrorSpamBlacklist: 'onSaveError',
-                               saveErrorAbuseFilter: 'onSaveError',
-                               saveErrorBlocked: 'onSaveError',
-                               saveErrorNewUser: 'onSaveError',
-                               saveErrorCaptcha: 'onSaveErrorCaptcha',
-                               saveErrorUnknown: 'onSaveError',
-                               surfaceReady: 'onSurfaceReady',
-                               loadError: 'onLoadError',
-                               conflictError: 'onConflictError',
-                               showChangesError: 'onShowChangesError',
-                               serializeError: 'onSerializeError'
-                       } );
+                       if ( this.target === undefined ) {
+                               // FIXME: MobileViewTarget does not accept a 
second argument
+                               // FIXME: we have to initialize 
MobileViewTarget after this.$el
+                               // is attached to DOM, maybe we should attach 
it earlier and hide
+                               // overlays in a different way?
+                               this.target = new ve.init.mw.MobileViewTarget( 
this.$( '.surface' ), {
+                                       // || undefined so that scrolling is 
not triggered for the lead (0) section
+                                       // (which has no header to scroll to)
+                                       section: this.options.sectionId || 
undefined
+                               } );
+                               this.target.activating = true;
+                               this.target.load();
+                               this.target.connect( this, {
+                                       save: 'onSave',
+                                       saveAsyncBegin: 'showSpinner',
+                                       saveAsyncComplete: 'clearSpinner',
+                                       saveErrorEmpty: 'onSaveError',
+                                       // FIXME: Expand on save errors by 
having a method for each
+                                       saveErrorSpamBlacklist: 'onSaveError',
+                                       saveErrorAbuseFilter: 'onSaveError',
+                                       saveErrorBlocked: 'onSaveError',
+                                       saveErrorNewUser: 'onSaveError',
+                                       saveErrorCaptcha: 'onSaveErrorCaptcha',
+                                       saveErrorUnknown: 'onSaveError',
+                                       surfaceReady: 'onSurfaceReady',
+                                       loadError: 'onLoadError',
+                                       conflictError: 'onConflictError',
+                                       showChangesError: 'onShowChangesError',
+                                       serializeError: 'onSerializeError'
+                               } );
+                       }
                },
                postRender: function( options ) {
+                       var self = this;
                        // Save button
                        this.$( '.continue' ).on( 'click', $.proxy( this, 
'prepareForSave' ) );
                        this.$( '.submit' ).on( 'click', $.proxy( this, 'save' 
) );
                        this.$( '.back' ).on( 'click', $.proxy( this, 
'switchToEditor' ) );
+                       this.$( '.source-editor' ).on( 'click', function() {
+                               // If changes have been made tell the user they 
have to save first
+                               if ( !self.hasChanged ) {
+                                       MobileWebClickTracking.log( 
'editor-switch-to-source', options.title );
+                                       // FIXME: Come up with a solution that 
doesn't cause weird behavior
+                                       // when using the close button.
+                                       M.router.navigate( '#editor/' + 
options.sectionId );
+                               } else {
+                                       if ( window.confirm( mw.msg( 
'mobile-frontend-editor-switch-confirm' ) ) ) {
+                                               self.prepareForSave();
+                                       }
+                               }
+                       } );
                        this._super( options );
                },
                switchToEditor: function() {
diff --git a/less/common/OverlayNew.less b/less/common/OverlayNew.less
index 6e02edc..a2b8714 100644
--- a/less/common/OverlayNew.less
+++ b/less/common/OverlayNew.less
@@ -133,8 +133,10 @@
                        border-bottom: 1px solid @grayLight;
                }
 
-               > div {
-                       padding: @headerTitleMarginV @headerTitleMarginH;
+               > ul {
+                       li {
+                               display: inline-block;
+                       }
                }
 
                h2 {
@@ -166,6 +168,7 @@
                        background-position: 50%;
                        background-repeat: no-repeat;
                        .background-size( 24px, auto );
+                       cursor: pointer;
 
                        &[disabled] {
                                opacity: .5;
@@ -200,15 +203,10 @@
                padding: @headerMargin @headerMargin 0;
                .box-sizing(border-box);
                background: #fff;
-               // For overlays that do not use position fixed headers e.g. 
ZeroOverlay
-               // FIXME: make those overlays use position fixed headers
-               position: absolute;
+               // FIXME: After this has been deployed for a while, remove the 
'position-fixed'
+               // class from OverlayNew.html.
+               position: fixed;
                top: 0;
-               // has to be here in case has no position: fixed but is 
positioned using
-               // JS on iOS
-               z-index: 5;
-               // prevent horizontal scrollbar when no position: fixed
-               overflow: hidden;
                // make header reappearing less abrupt when scrolling on iOS 
with open
                // keyboard
                opacity: 0;
@@ -229,11 +227,13 @@
                        z-index: 5;
                }
 
+               // FIXME: Combine with .overlay-header above.
                .overlay-header {
                        white-space: nowrap;
 
-                       > div {
-                               padding: (@headerTitleMarginV - @headerMargin) 
@headerTitleMarginH;
+                       .overlay-title,
+                       .overlay-search {
+                               padding: 0 @headerTitleMarginH;
                        }
 
                        button {
diff --git a/less/minerva.less/minerva.variables.less 
b/less/minerva.less/minerva.variables.less
index 5023486..2cbe4fb 100644
--- a/less/minerva.less/minerva.variables.less
+++ b/less/minerva.less/minerva.variables.less
@@ -16,6 +16,10 @@
 @headerTitleMarginV: 1em;
 @headerTitleMarginH: .6em;
 @headerTitleFontSize: 1em;
+// FIXME: Font-size does not equal block height, use an actual height var 
instead. So,
+// @headerHeight: @headerTitleHeight + @headerTitleMarginV * 2 + 
@headerMargin; This will
+// require adjusting a lot of CSS to make sure everything still lines up. 
Since the
+// header design is changing anyway, not implementing this change just yet.
 @headerHeight: @headerTitleFontSize + @headerTitleMarginV * 2 + @headerMargin;
 
 @grayDark: #252525;
diff --git a/less/modules/editor/VisualEditorOverlay.less 
b/less/modules/editor/VisualEditorOverlay.less
index 4190446..cd9eea3 100644
--- a/less/modules/editor/VisualEditorOverlay.less
+++ b/less/modules/editor/VisualEditorOverlay.less
@@ -70,7 +70,7 @@
                }
 
                .oo-ui-tool {
-                       height: 3em;
+                       height: @headerHeight;
                        display: block;
                        float: left;
                        text-indent: -999px;
@@ -101,11 +101,14 @@
                // that we wouldn't be able to use the @headerHeight LESS 
variable.
                .oo-ui-barToolGroup {
                        .oo-ui-tool-link {
-                               height: 3em;
+                               height: 100%;
+                               padding: 0;
+                               .box-sizing( border-box );
 
                                .oo-ui-iconedElement-icon {
-                                       width: 3em;
-                                       height: 3em;
+                                       width: @headerTitleFontSize + 
@headerTitleMarginV * 2;
+                                       height: @headerTitleFontSize + 
@headerTitleMarginV * 2;
+                                       margin-top: @headerMargin;
                                        .background-size( 32px, 32px );
                                }
                        }
diff --git a/less/modules/editor/editor.less b/less/modules/editor/editor.less
index faf832d..1aa1c86 100644
--- a/less/modules/editor/editor.less
+++ b/less/modules/editor/editor.less
@@ -21,6 +21,93 @@
                        }
                }
        }
+
+       .overlay-header {
+               .switcher-container {
+                       padding: 0;
+                       // Have to set an explicit width since we're using 
display:table-cell
+                       width: 5em;
+                       vertical-align: middle;
+                       border-right: 1px solid @grayLight;
+                       position: relative;
+               }
+
+               .icon.editor-switcher {
+                       padding: 0;
+                       .background-image-svg-quick( 
'images/edit-visual-normal' );
+                       background-size: 40px auto;
+                       width: 5em;
+
+                       &.selected {
+                               .background-image-svg-quick( 
'images/edit-visual-select' );
+                       }
+               }
+
+               .icon.editor-switcher:focus {
+                       outline: none;
+               }
+
+               .switcher-drop-down {
+                       display: none;
+                       position: absolute;
+                       top: @headerHeight;
+                       // FIXME: Negative margin is needed in Chrome and 
Safari to match our negative
+                       // margins on the buttons, but this breaks the layout 
in Firefox and IE :(
+                       // Spent a very long time trying to find a CSS solution 
that worked in all
+                       // four browsers, but no luck.
+                       margin-top: -@headerMargin;
+                       // We have to use margin-left instead of left due to a 
rendering bug in
+                       // Firefox. Firefox sets the wrong horizontal 
positioning frame for
+                       // children of elements with position:relative and 
display:table-cell.
+                       margin-left: -1px;
+                       border: 1px solid @grayLight;
+                       white-space: nowrap;
+
+                       > .mw-ui-button {
+                               display: inline-block;
+                               border: none;
+                               border-right: 1px solid @grayLight;
+                               border-radius: 0;
+                               line-height: 2em;
+                               padding: 0.5em 1.4em 0.5em 1em;
+
+                               .icon {
+                                       background-repeat: no-repeat;
+                                       background-position: left center;
+                                       background-size: 30px 30px;
+                                       display: inline-block;
+                                       margin-right: 10px;
+                                       width: 30px;
+                                       height: 30px;
+                                       vertical-align: middle;
+                               }
+
+                               &.visual-editor {
+                                       .icon {
+                                               .background-image-svg-quick( 
'images/edit-visual-normal' );
+
+                                               &.selected {
+                                                       
.background-image-svg-quick( 'images/edit-visual-toggle' );
+                                               }
+                                       }
+                               }
+
+                               &.source-editor {
+                                       .icon {
+                                               .background-image-svg-quick( 
'images/edit-source-normal' );
+
+                                               &.selected {
+                                                       
.background-image-svg-quick( 'images/edit-visual-toggle' );
+                                               }
+                                       }
+                               }
+                       }
+
+                       > div:last-child {
+                               border-right: none;
+                       }
+               }
+       }
 }
 
 .abusefilter-overlay {
diff --git a/less/modules/editor/images/edit-source-normal.png 
b/less/modules/editor/images/edit-source-normal.png
new file mode 100644
index 0000000..a9777c0
--- /dev/null
+++ b/less/modules/editor/images/edit-source-normal.png
Binary files differ
diff --git a/less/modules/editor/images/edit-source-normal.svg 
b/less/modules/editor/images/edit-source-normal.svg
new file mode 100644
index 0000000..931fef4
--- /dev/null
+++ b/less/modules/editor/images/edit-source-normal.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg"; 
width="40" height="40" viewBox="0 0 40 40"><g fill="#3F3F3F"><path d="M27.182 
20.419v1.705h-1.86v10.484h1.86v1.705h-4.024v-13.894h4.024zM33.817 
34.314v-1.705h1.86v-10.485h-1.86v-1.705h4.024v13.895h-4.024z"/></g><path 
fill="#aaa" d="M28.332 
12.605c-.073-1.426-.74-2.808-1.722-3.902-1.006-1.121-2.387-1.982-3.874-2.226-4.361
 3.912-15.4 13.817-19.216 17.239l.013.008-.01-.005-1.54 6.929 
7.104-.783.001.004.005-.004.008-.001-.003-.004c3.84-3.444 14.87-13.34 
19.234-17.255zm-21.308 
13.124c-.547-.548-1.01-.885-1.647-1.322l17.26-15.605c.583.18 1.391.65 1.638 
1.325l-17.251 15.602z"/></svg>
\ No newline at end of file
diff --git a/less/modules/editor/images/edit-source-select.png 
b/less/modules/editor/images/edit-source-select.png
new file mode 100644
index 0000000..b069947
--- /dev/null
+++ b/less/modules/editor/images/edit-source-select.png
Binary files differ
diff --git a/less/modules/editor/images/edit-source-select.svg 
b/less/modules/editor/images/edit-source-select.svg
new file mode 100644
index 0000000..9caeb56
--- /dev/null
+++ b/less/modules/editor/images/edit-source-select.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg"; 
width="40" height="40" viewBox="0 0 40 40"><g fill="#1059E1"><path d="M27.182 
20.419v1.705h-1.86v10.484h1.86v1.705h-4.024v-13.894h4.024zM33.817 
34.314v-1.705h1.86v-10.485h-1.86v-1.705h4.024v13.895h-4.024z"/></g><path 
fill="#1059E1" d="M28.332 
12.605c-.073-1.426-.74-2.808-1.722-3.902-1.006-1.121-2.387-1.982-3.874-2.226-4.361
 3.912-15.4 13.817-19.216 17.239l.013.008-.01-.005-1.54 6.929 
7.104-.783.001.004.005-.004.008-.001-.003-.004c3.84-3.444 14.87-13.34 
19.234-17.255zm-21.308 
13.124c-.547-.548-1.01-.885-1.647-1.322l17.26-15.605c.583.18 1.391.65 1.638 
1.325l-17.251 15.602z"/></svg>
\ No newline at end of file
diff --git a/less/modules/editor/images/edit-source-toggle.png 
b/less/modules/editor/images/edit-source-toggle.png
new file mode 100644
index 0000000..6c908ae
--- /dev/null
+++ b/less/modules/editor/images/edit-source-toggle.png
Binary files differ
diff --git a/less/modules/editor/images/edit-source-toggle.svg 
b/less/modules/editor/images/edit-source-toggle.svg
new file mode 100644
index 0000000..8a36949
--- /dev/null
+++ b/less/modules/editor/images/edit-source-toggle.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg"; 
width="40" height="40" viewBox="0 0 40 40"><g fill="#fff"><path d="M27.182 
20.419v1.705h-1.86v10.484h1.86v1.705h-4.024v-13.894h4.024zM33.817 
34.314v-1.705h1.86v-10.485h-1.86v-1.705h4.024v13.895h-4.024z"/></g><path 
fill="#fff" d="M28.332 
12.605c-.073-1.426-.74-2.808-1.722-3.902-1.006-1.121-2.387-1.982-3.874-2.226-4.361
 3.912-15.4 13.817-19.216 17.239l.013.008-.01-.005-1.54 6.929 
7.104-.783.001.004.005-.004.008-.001-.003-.004c3.84-3.444 14.87-13.34 
19.234-17.255zm-21.308 
13.124c-.547-.548-1.01-.885-1.647-1.322l17.26-15.605c.583.18 1.391.65 1.638 
1.325l-17.251 15.602z"/></svg>
\ No newline at end of file
diff --git a/less/modules/editor/images/edit-visual-normal.png 
b/less/modules/editor/images/edit-visual-normal.png
new file mode 100644
index 0000000..4caad63
--- /dev/null
+++ b/less/modules/editor/images/edit-visual-normal.png
Binary files differ
diff --git a/less/modules/editor/images/edit-visual-normal.svg 
b/less/modules/editor/images/edit-visual-normal.svg
new file mode 100644
index 0000000..4ae4151
--- /dev/null
+++ b/less/modules/editor/images/edit-visual-normal.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg"; 
width="40" height="40" viewBox="0 0 40 40"><path fill="#3F3F3F" d="M28.871 
23.087c-5.108 0-9.14 5.222-9.14 5.222s4.031 5.223 9.14 5.223c3.908 0 9.14-5.224 
9.14-5.224s-5.232-5.221-9.14-5.221zm0 8.476c-1.794 0-3.255-1.458-3.255-3.255 
0-1.794 1.461-3.255 3.255-3.255 1.793 0 3.254 1.46 3.254 3.255s-1.461 
3.255-3.254 3.255zm0-5.153c-1.048 0-1.901.85-1.901 1.899s.851 1.9 1.901 
1.9c1.048 0 1.9-.852 1.9-1.901.001-1.048-.851-1.898-1.9-1.898z"/><path 
fill="#aaa" d="M28.288 
12.584c-.073-1.423-.738-2.803-1.719-3.895-1.004-1.118-2.382-1.978-3.866-2.222l-19.18
 17.206.013.008-.01-.005-1.537 6.916 
7.09-.781.001.004.005-.004.008-.001-.003-.004c3.833-3.437 14.842-13.315 
19.198-17.222zm-21.268 
13.099c-.546-.547-1.008-.884-1.644-1.319l17.228-15.576c.582.18 1.389.649 1.635 
1.322l-17.219 15.573z"/></svg>
\ No newline at end of file
diff --git a/less/modules/editor/images/edit-visual-select.png 
b/less/modules/editor/images/edit-visual-select.png
new file mode 100644
index 0000000..00d22b2
--- /dev/null
+++ b/less/modules/editor/images/edit-visual-select.png
Binary files differ
diff --git a/less/modules/editor/images/edit-visual-select.svg 
b/less/modules/editor/images/edit-visual-select.svg
new file mode 100644
index 0000000..ebe9717
--- /dev/null
+++ b/less/modules/editor/images/edit-visual-select.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg"; 
width="40" height="40" viewBox="0 0 40 40"><g fill="#1059E1"><path d="M28.871 
23.087c-5.108 0-9.14 5.222-9.14 5.222s4.031 5.223 9.14 5.223c3.908 0 9.14-5.224 
9.14-5.224s-5.232-5.221-9.14-5.221zm0 8.476c-1.794 0-3.255-1.458-3.255-3.255 
0-1.794 1.461-3.255 3.255-3.255 1.793 0 3.254 1.46 3.254 3.255s-1.461 
3.255-3.254 3.255zm0-5.153c-1.048 0-1.901.85-1.901 1.899s.851 1.9 1.901 
1.9c1.048 0 1.9-.852 1.9-1.901.001-1.048-.851-1.898-1.9-1.898z"/><path 
d="M28.288 
12.584c-.073-1.423-.738-2.803-1.719-3.895-1.004-1.118-2.382-1.978-3.866-2.222l-19.18
 17.206.013.008-.01-.005-1.537 6.916 
7.09-.781.001.004.005-.004.008-.001-.003-.004c3.833-3.437 14.842-13.315 
19.198-17.222zm-21.268 
13.099c-.546-.547-1.008-.884-1.644-1.319l17.228-15.576c.582.18 1.389.649 1.635 
1.322l-17.219 15.573z"/></g></svg>
\ No newline at end of file
diff --git a/less/modules/editor/images/edit-visual-toggle.png 
b/less/modules/editor/images/edit-visual-toggle.png
new file mode 100644
index 0000000..933f862
--- /dev/null
+++ b/less/modules/editor/images/edit-visual-toggle.png
Binary files differ
diff --git a/less/modules/editor/images/edit-visual-toggle.svg 
b/less/modules/editor/images/edit-visual-toggle.svg
new file mode 100644
index 0000000..cbf0447
--- /dev/null
+++ b/less/modules/editor/images/edit-visual-toggle.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg"; 
width="40" height="40" viewBox="0 0 40 40"><path fill="#fff" d="M28.871 
23.087c-5.108 0-9.14 5.222-9.14 5.222s4.031 5.223 9.14 5.223c3.908 0 9.14-5.224 
9.14-5.224s-5.232-5.221-9.14-5.221zm0 8.476c-1.794 0-3.255-1.458-3.255-3.255 
0-1.794 1.461-3.255 3.255-3.255 1.793 0 3.254 1.46 3.254 3.255s-1.461 
3.255-3.254 3.255zm0-5.153c-1.048 0-1.901.85-1.901 1.899s.851 1.9 1.901 
1.9c1.048 0 1.9-.852 1.9-1.901.001-1.048-.851-1.898-1.9-1.898z"/><path 
fill="#fff" d="M28.288 
12.584c-.073-1.423-.738-2.803-1.719-3.895-1.004-1.118-2.382-1.978-3.866-2.222l-19.18
 17.206.013.008-.01-.005-1.537 6.916 
7.09-.781.001.004.005-.004.008-.001-.003-.004c3.833-3.437 14.842-13.315 
19.198-17.222zm-21.268 
13.099c-.546-.547-1.008-.884-1.644-1.319l17.228-15.576c.582.18 1.389.649 1.635 
1.322l-17.219 15.573z"/></svg>
\ No newline at end of file
diff --git a/templates/OverlayNew.html b/templates/OverlayNew.html
index 6ba27aa..41c1655 100644
--- a/templates/OverlayNew.html
+++ b/templates/OverlayNew.html
@@ -3,7 +3,7 @@
                <ul class="v-border bottom-border">
                        <li><button class="cancel 
icon">{{closeMsg}}</button></li>
                </ul>
-               <div class="bottom-border">
+               <div class="overlay-title bottom-border">
                        <h2>{{{heading}}}</h2>
                </div>
                {{#headerButtons}}
diff --git a/templates/modules/editor/EditorOverlayBase.html 
b/templates/modules/editor/EditorOverlayBase.html
index 29c7901..18ff6b1 100644
--- a/templates/modules/editor/EditorOverlayBase.html
+++ b/templates/modules/editor/EditorOverlayBase.html
@@ -4,7 +4,7 @@
                <ul class="v-border bottom-border">
                        <li><button class="back 
icon">{{keepEditingMsg}}</button></li>
                </ul>
-               <div class="bottom-border">
+               <div class="overlay-title bottom-border">
                        <h2>{{{previewingMsg}}}</h2>
                </div>
                <ul>
@@ -15,7 +15,7 @@
                <ul class="v-border bottom-border">
                        <li><button class="cancel icon" 
disabled>{{closeMsg}}</button></li>
                </ul>
-               <div class="bottom-border">
+               <div class="overlay-title bottom-border">
                        <h2>{{{waitMsg}}}</h2>
                </div>
                <ul class="v-border bottom-border">
diff --git a/templates/modules/editor/EditorOverlayHeader.html 
b/templates/modules/editor/EditorOverlayHeader.html
index b760dc6..e84b234 100644
--- a/templates/modules/editor/EditorOverlayHeader.html
+++ b/templates/modules/editor/EditorOverlayHeader.html
@@ -2,7 +2,16 @@
        <ul class="v-border bottom-border">
                <li><button class="cancel icon">{{closeMsg}}</button></li>
        </ul>
-       <div class="bottom-border">
+       {{#editSwitcher}}
+       <div class="switcher-container bottom-border">
+               <button class="editor-switcher icon">{{switchMsg}}</button>
+               <div class="switcher-drop-down">
+                       <div class="mw-ui-button visual-editor"><span 
class="icon"></span>{{visualEditorMsg}}</div><!--
+                       --><div class="mw-ui-button mw-ui-progressive 
source-editor"><span class="icon selected"></span>{{sourceEditorMsg}}</div>
+               </div>
+       </div>
+       {{/editSwitcher}}
+       <div class="overlay-title bottom-border">
                <h2>{{{editingMsg}}}</h2>
        </div>
        {{^readOnly}}
diff --git a/templates/modules/editor/VisualEditorOverlayHeader.html 
b/templates/modules/editor/VisualEditorOverlayHeader.html
index b8a9342..d92f78c 100644
--- a/templates/modules/editor/VisualEditorOverlayHeader.html
+++ b/templates/modules/editor/VisualEditorOverlayHeader.html
@@ -2,6 +2,15 @@
        <ul class="v-border bottom-border">
                <li><button class="cancel icon">{{closeMsg}}</button></li>
        </ul>
+
+       <div class="switcher-container bottom-border">
+               <button class="editor-switcher icon">{{switchMsg}}</button>
+               <div class="switcher-drop-down">
+                       <div class="mw-ui-button mw-ui-progressive 
visual-editor"><span class="icon selected"></span>{{visualEditorMsg}}</div><!--
+                       --><div class="mw-ui-button source-editor"><span 
class="icon"></span>{{sourceEditorMsg}}</div>
+               </div>
+       </div>
+
        <div class="toolbar bottom-border"></div>
        {{^readOnly}}
        <ul>
diff --git a/templates/modules/search/SearchOverlay.html 
b/templates/modules/search/SearchOverlay.html
index cadf096..797999f 100644
--- a/templates/modules/search/SearchOverlay.html
+++ b/templates/modules/search/SearchOverlay.html
@@ -3,7 +3,7 @@
                <ul class="v-border bottom-border">
                        <li><button class="cancel 
icon">{{closeMsg}}</button></li>
                </ul>
-               <div class="bottom-border">
+               <div class="overlay-search bottom-border">
                        <form method="get" action="{{action}}">
                                <input class="search" type="search" 
name="search" autocomplete="off" placeholder="{{placeholderMsg}}">
                        </form>
diff --git a/tests/browser/README.mediawiki b/tests/browser/README.mediawiki
index f039ba8..7c69417 100644
--- a/tests/browser/README.mediawiki
+++ b/tests/browser/README.mediawiki
@@ -6,6 +6,8 @@
 These include:
 * GeoData extension must be installed for the purpose of the nearby tests
 * Sitename needs to be set to Wikipedia (for purpose of 
login_required_uploads.feature)
+* Ensure you have [//www.mediawiki.org/wiki/Extension:ConfirmEdit 
Extension:ConfirmEdit] installed
+** and setup with FancyCaptcha
 * Create an account Selenium_newuser which has an edit count of 0
 * Create an account and store the username in MEDIAWIKI_USER which has
 an edit count of greater than 0
@@ -15,11 +17,19 @@
 {{#coordinates:43|-75|primary}}
 </pre>
 * Ensure an article called "Foo bar" (without quotes) exists and is not 
protected
-* Ensure you have [//www.mediawiki.org/wiki/Extension:ConfirmEdit 
Extension:ConfirmEdit] installed
-** and setup with FancyCaptcha
 * A "Diff test" page is used by the test suite thus cannot be used for another 
purpose.
 * Ensure there is a San Francisco article
 ** and it has languages
+* Ensure there is a page called "Duel Masters" which has at least 2 sections 
and is editable by MEDIAWIKI_USER
+Sample content:
+<pre>
+== Section 1 ==
+Howdy.
+== Section 2 ==
+Howdy 2.
+== Section 3 ==
+Howdy 3.
+</pre>
 * Ensure there is a Barack Obama article
 ** and it is a protected page
 * Edit MediaWiki:mobile-frontend-terms-text with text 'Terms of use'
diff --git a/tests/browser/features/editor_ve.feature 
b/tests/browser/features/editor_ve.feature
index e92d358..f64507d 100644
--- a/tests/browser/features/editor_ve.feature
+++ b/tests/browser/features/editor_ve.feature
@@ -1,10 +1,27 @@
 @chrome @en.m.wikipedia.beta.wmflabs.org @firefox @login @test2.m.wikipedia.org
 Feature: VisualEditor
 
-Scenario: Toolbar VisualEditor
+Background:
   Given I am in alpha mode
     And I am logged into the mobile website
-    And I am on the "Selenium Edit Test" page
+
+Scenario: Switch from VisualEditor to source editor
+  Given I am on the "Selenium Edit Test" page
+    And I click the edit button
+    And I see the VisualEditor overlay
+    And The VisualEditor overlay has an editor mode switcher button
+    And I click the editor mode switcher button
+  When I click the source editor button
+  Then I see the wikitext editor
+
+Scenario: Ensure we load the correct section
+  Given I am on the "Duel Masters" page
+  When I click the edit button for section 1
+  Then I see the VisualEditor overlay
+    And The URL of the page should contain "#/VisualEditor/1"
+
+Scenario: Toolbar VisualEditor
+  Given I am on the "Selenium Edit Test" page
   When I click the edit button
   Then I see the VisualEditor overlay
     And I see a toolbar in the overlay header
@@ -12,23 +29,19 @@
     And The VisualEditor toolbar has an italic button
 
 Scenario: I can edit a page using VisualEditor
-  Given I am in alpha mode
-    And I am logged into the mobile website
-    And I am on the "Selenium Edit Test" page
-  When I click the edit button
+  Given I am on the "Selenium Edit Test" page
+    And I click the edit button
     And VisualEditor has loaded
     And I type "ABCDEFG" into VisualEditor
     And I click continue
-    And I click submit
+  When I click submit
   Then I do not see the VisualEditor overlay
     And I see a toast notification
 
 Scenario: Going back from save screen in VisualEditor
-  Given I am in alpha mode
-    And I am logged into the mobile website
-    And I am on the "Selenium Edit Test" page
-  When I click the edit button
+  Given I am on the "Selenium Edit Test" page
+    And I click the edit button
     And I type "ABCDEFG" into VisualEditor
     And I click continue
-    And I click the escape button
+  When I click the escape button
   Then I see the VisualEditor
diff --git a/tests/browser/features/editor_wikitext_nosave.feature 
b/tests/browser/features/editor_wikitext_nosave.feature
index 117cb17..a6eda08 100644
--- a/tests/browser/features/editor_wikitext_nosave.feature
+++ b/tests/browser/features/editor_wikitext_nosave.feature
@@ -10,11 +10,11 @@
     Then I see the wikitext editor
 
   Scenario: Closing editor (overlay button)
-    When I click the wikitext editor overlay close button
-    Then I should not see the wikitext editor
+    When I click the editor overlay close button
+    Then I should not see the editor overlay
       And The URL of the page should contain "Nonexistent_page_ijewrcmhvg34773"
 
   Scenario: Closing editor (browser button)
     When I click the browser back button
-    Then I should not see the wikitext editor
+    Then I should not see the editor overlay
       And The URL of the page should contain "Nonexistent_page_ijewrcmhvg34773"
diff --git a/tests/browser/features/step_definitions/editor_steps.rb 
b/tests/browser/features/step_definitions/editor_steps.rb
index 9544243..760ac6a 100644
--- a/tests/browser/features/step_definitions/editor_steps.rb
+++ b/tests/browser/features/step_definitions/editor_steps.rb
@@ -9,21 +9,33 @@
 end
 
 Then(/^I see the wikitext editor$/) do
-  on(ArticlePage).editor_overlay_element.should be_visible
+  on(ArticlePage).editor_textarea_element.when_present.should be_visible
 end
 
-When(/^I click the wikitext editor overlay close button$/) do
+When(/^I click the editor overlay close button$/) do
   on(ArticlePage).editor_overlay_close_button_element.click
 end
 
-Then(/^I should not see the wikitext editor$/) do
+Then(/^I should not see the editor overlay$/) do
   on(ArticlePage).editor_overlay_element.should_not be_visible
 end
 
 When(/^I clear the editor$/) do
-  on(ArticlePage).editor_text_area_element.when_present.clear
+  on(ArticlePage).editor_textarea_element.when_present.clear
+end
+
+When(/^I click the editor mode switcher button$/) do
+  on(ArticlePage).overlay_editor_mode_switcher_element.when_present.click
+end
+
+When(/^I click the source editor button$/) do
+  on(ArticlePage).source_editor_button_element.when_present.click
+end
+
+When(/^I click the visual editor button$/) do
+  on(ArticlePage).visual_editor_button_element.when_present.click
 end
 
 Given(/^I type "(.+)" into the editor$/) do |text|
-  on(ArticlePage).editor_text_area_element.when_present.send_keys(text)
+  on(ArticlePage).editor_textarea_element.when_present.send_keys(text)
 end
diff --git a/tests/browser/features/step_definitions/editor_ve_steps.rb 
b/tests/browser/features/step_definitions/editor_ve_steps.rb
index c17e071..eaa63fb 100644
--- a/tests/browser/features/step_definitions/editor_ve_steps.rb
+++ b/tests/browser/features/step_definitions/editor_ve_steps.rb
@@ -26,9 +26,17 @@
   
on(ArticlePage).overlay_ve_header_toolbar_italic_button_element.when_present.should
 exist
 end
 
+Then(/^The VisualEditor overlay has an editor mode switcher button$/) do
+  on(ArticlePage).overlay_editor_mode_switcher_element.when_present.should 
exist
+end
+
 Given(/^I type "(.+)" into VisualEditor$/) do |text|
   on(ArticlePage) do |page|
     page.editor_ve_element.when_present(15).fire_event("onfocus")
     page.editor_ve_element.when_present.send_keys(text)
   end
 end
+
+Given(/^I click the edit button for section (\d+)$/) do |arg1|
+  on(ArticlePage).link_element(class: "edit-page", index: 
arg1.to_i).when_present.click
+end
diff --git a/tests/browser/features/support/pages/article_page.rb 
b/tests/browser/features/support/pages/article_page.rb
index 8f4eb2f..4c6069b 100644
--- a/tests/browser/features/support/pages/article_page.rb
+++ b/tests/browser/features/support/pages/article_page.rb
@@ -75,8 +75,13 @@
   ul(:page_actions, id:"page-actions")
   a(:nearby_button, css: "#page-secondary-actions .nearby")
 
+  # editor (common)
+  button(:overlay_editor_mode_switcher, css: ".editor-switcher")
+  div(:source_editor_button, css: ".source-editor")
+  div(:visual_editor_button, css: ".visual-editor")
+
   # editor
-  textarea(:editor_text_area, class:"wikitext-editor")
+  textarea(:editor_textarea, class:"wikitext-editor")
   button(:escape_button, class:"back icon")
   button(:continue_button, class:"continue icon")
   button(:submit_button, class:"submit icon")

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I6e289d02612eb2093dd7ef0a4026b2dfce493ac0
Gerrit-PatchSet: 27
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: Jdlrobson <[email protected]>
Gerrit-Reviewer: Cmcmahon <[email protected]>
Gerrit-Reviewer: Jdlrobson <[email protected]>
Gerrit-Reviewer: Kaldari <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to