jenkins-bot has submitted this change and it was merged. Change subject: Remove WikiGrok version A ......................................................................
Remove WikiGrok version A * Merge the WikiGrokDialog and WikiGrokDialogB dialogs and their test cases * Remove the WikiGrokAbTest class * Fix a bug in the MobileWebWikiGrokError logging implementation that caused it to fail while logging a loading error Change-Id: Iec3921cddfe6fc026b5757eb338efc44232bbf7a --- M includes/Resources.php D resources/abTest/WikiGrokAbTest.js D resources/dialog.b/WikiGrokDialogB.hogan D resources/dialog.b/WikiGrokDialogB.js M resources/dialog.c/WikiGrokDialogC.js M resources/dialog/WikiGrokDialog.hogan M resources/dialog/WikiGrokDialog.js R resources/dialog/tagButton.hogan R resources/dialog/tagButton.less M resources/startup/init.js R resources/startup/wikigrokuser.js D tests/qunit/test_WikiGrokAbTest.js M tests/qunit/test_WikiGrokDialog.js D tests/qunit/test_WikiGrokDialogB.js 14 files changed, 285 insertions(+), 714 deletions(-) Approvals: Bmansurov: Looks good to me, approved jenkins-bot: Verified diff --git a/includes/Resources.php b/includes/Resources.php index 8510091..4a4db9d 100644 --- a/includes/Resources.php +++ b/includes/Resources.php @@ -22,24 +22,13 @@ $wgResourceModules = array_merge( $wgResourceModules, array( - 'ext.wikigrok.abTest' => $wgWikiGrokResourceFileModuleBoilerplate + array( - 'dependencies' => array( - 'mobile.user', - 'jquery.cookie', - ), - 'scripts' => array( - 'abTest/wikigrokuser.js', - 'abTest/WikiGrokAbTest.js', - ), - ), - 'ext.wikigrok.startup' => $wgWikiGrokResourceFileModuleBoilerplate + array( 'dependencies' => array( 'mobile.startup', 'ext.wikigrok.loggingSchema', - 'ext.wikigrok.abTest', ), 'scripts' => array( + 'startup/wikigrokuser.js', 'startup/wikiGrokCampaigns.js', 'startup/init.js', ), @@ -61,12 +50,14 @@ 'ext.wikigrok.startup', 'ext.wikigrok.loggingSchema', 'ext.wikigrok.api', + 'mediawiki.ui.checkbox', ), 'templates' => array( 'Error.hogan' => 'dialog/WikiGrokError.hogan', 'Dialog.hogan' => 'dialog/WikiGrokDialog.hogan', 'WikiGrokMoreInfo/content.hogan' => 'dialog/WikiGrokMoreInfo.hogan', 'Thanks.hogan' => 'dialog/WikiGrokThanks.hogan', + 'tagButton.hogan' => 'dialog/tagButton.hogan', ), 'scripts' => array( 'dialog/WikiGrokDialog.js', @@ -74,30 +65,14 @@ ), 'styles' => array( 'dialog/WikiGrokDialog.less', - ), - ), - - 'ext.wikigrok.dialog.b' => $wgWikiGrokResourceFileModuleBoilerplate + array( - 'dependencies' => array( - 'ext.wikigrok.dialog', - 'mediawiki.ui.checkbox', - ), - 'styles' => array( - 'dialog.b/tagButton.less', - ), - 'templates' => array( - 'Dialog.hogan' => 'dialog.b/WikiGrokDialogB.hogan', - 'tagButton.hogan' => 'dialog.b/tagButton.hogan', - ), - 'scripts' => array( - 'dialog.b/WikiGrokDialogB.js', + 'dialog/tagButton.less', ), ), 'ext.wikigrok.roulette' => $wgWikiGrokResourceFileModuleBoilerplate + array( 'dependencies' => array( 'mobile.overlays', - 'ext.wikigrok.dialog.b', + 'ext.wikigrok.dialog', ), 'scripts' => array( 'roulette/ErrorDrawer.js', @@ -125,7 +100,6 @@ 'ext.wikigrok.dialog.c' => $wgWikiGrokResourceFileModuleBoilerplate + array( 'dependencies' => array( 'ext.wikigrok.roulette', - 'ext.wikigrok.dialog.b', ), 'scripts' => array( 'dialog.c/WikiGrokDialogC.js', diff --git a/resources/abTest/WikiGrokAbTest.js b/resources/abTest/WikiGrokAbTest.js deleted file mode 100644 index a6ce049..0000000 --- a/resources/abTest/WikiGrokAbTest.js +++ /dev/null @@ -1,47 +0,0 @@ -( function ( M ) { - - var Class = M.require( 'Class' ), - - /** - * Represents the WikiGrok A/B test. - * - * @class WikiGrokAbTest - * @extends Class - */ - WikiGrokAbTest = Class.extend( { - - /** - * Initialises a new instance of the WikiGrokAbTest class. - * @method - * @param {Boolean} isEnabled Whether or not the A/B test is enabled - */ - initialize: function ( isEnabled ) { - this.isEnabled = isEnabled; - }, - - /** - * Gets the version of WikiGrok to show to the user. - * @method - * @param {Object} wikiGrokUser The WikiGrok user - * @return {String} `'A'` or `'B'` - */ - getVersion: function ( wikiGrokUser ) { - var lastCharacter = wikiGrokUser.getToken().slice( -1 ); - - return lastCharacter > 'U' ? 'B' : 'A'; - } - } ); - - /** - * Creates a new instance of the WikiGrokAbTest using `wgWikiGrokUIEnable` as - * the `isEnabled` parameter. - * @method - * @return {WikiGrokAbTest} - */ - WikiGrokAbTest.newFromMwConfig = function () { - return new WikiGrokAbTest( mw.config.get( 'wgWikiGrokUIEnable' ) ); - }; - - M.define( 'ext.wikigrok.abTest/WikiGrokAbTest', WikiGrokAbTest ); - -}( mw.mobileFrontend, mw ) ); diff --git a/resources/dialog.b/WikiGrokDialogB.hogan b/resources/dialog.b/WikiGrokDialogB.hogan deleted file mode 100644 index ab4b044..0000000 --- a/resources/dialog.b/WikiGrokDialogB.hogan +++ /dev/null @@ -1,23 +0,0 @@ -<!-- note English language only! --> -<div class="pane content"> - <p class="wg-content">{{contentMsg}}</p> - <p class="wg-buttons"> - {{#buttons}} - <button class="{{classes}}">{{label}}</button> - {{/buttons}} - </p> - <p class="tags"> - </p> - {{{spinner}}} - {{#noticeMsg}} - <p class="wg-notice">{{{noticeMsg}}}</p> - {{/noticeMsg}} - - <div class="footer"> - <span class="license">Submissions are <a href="#/wikigrok/about">released freely</a>.</span> - <button class="mw-ui-button save none">None of these</button> - <button class="mw-ui-button mw-ui-constructive save next">Next</button> - </div> -</div> - -{{>error}} diff --git a/resources/dialog.b/WikiGrokDialogB.js b/resources/dialog.b/WikiGrokDialogB.js deleted file mode 100644 index cce543a..0000000 --- a/resources/dialog.b/WikiGrokDialogB.js +++ /dev/null @@ -1,206 +0,0 @@ -( function ( M, $ ) { - var WikiGrokDialog = M.require( 'ext.wikigrok.dialog/WikiGrokDialog' ), - wikiGrokCampaigns = M.require( 'ext.wikigrok.startup/wikiGrokCampaigns' ), - WikiGrokDialogB; - - /** - * @class WikiGrokDialogB - * @extends WikiGrokDialog - * THIS IS AN EXPERIMENTAL FEATURE THAT MAY BE MOVED TO A SEPARATE EXTENSION. - * This creates the dialog at the bottom of the lead section that appears when a user - * scrolls past the lead. It asks the user to confirm metadata information for use - * in Wikidata (https://www.wikidata.org). - */ - WikiGrokDialogB = WikiGrokDialog.extend( { - version: 'b', - template: mw.template.get( 'ext.wikigrok.dialog.b', 'Dialog.hogan' ), - - /** @inheritdoc */ - initialize: function () { - var self = this; - - WikiGrokDialog.prototype.initialize.apply( this, arguments ); - - // log page impression and widget impression when the widget is shown - this.once( 'show', function () { - self.logPageImpression(); - self.initializeWidgetImpressionLogging(); - - } ); - }, - /** - * Renders a set of buttons to the panel. - * Fetches suggestions' labels from the server. - * Shows panel to user when there are suggestions. - * @method - * @private - */ - _renderSuggestions: function () { - var suggestions, - allSuggestions, - $next, - $none, - self = this; - - allSuggestions = wikiGrokCampaigns.getAllSuggestions(); - // randomly pick 4 suggestions - suggestions = self.chooseRandomItemsFromArray( allSuggestions, 4 ); - - // Now work out the labels if we have some suggestions - if ( suggestions.length ) { - $next = self.$( '.footer .next' ); - $none = self.$( '.footer .none' ); - - // Hard-code the "Next" button width to match the "None" button width. - // That way, when the buttons are switched, the width stays the same. - // This depends on the assumption that the "Next" button text is - // always shorter than the "None" button text. - $next.css( 'width', $none.outerWidth() ); - - self.$( '.tags' ).show(); - $.each( suggestions, function ( i, suggestion ) { - var $tag, tagHtml, templateData, propertyName; - - // Replace some property names - // FIXME: These are not internationalizable and will probably have to - // die once WikiGrok is deployed outside of English Wikipedia - if ( suggestion.campaign.propertyName === 'instance of' ) { - propertyName = 'type'; - } else if ( suggestion.campaign.propertyName === 'original language of work' ) { - propertyName = 'original language'; - } else { - propertyName = suggestion.campaign.propertyName; - } - - templateData = { - id: 'tag-' + suggestion.id, - propName: suggestion.campaign.propertyName, - propId: suggestion.campaign.propertyId, - itemId: suggestion.id, - readable: suggestion.label, - campaignName: suggestion.campaign.name, - campaignText: propertyName, - tagText: suggestion.label - }; - - if ( suggestion.label ) { - tagHtml = mw.template.get( 'ext.wikigrok.dialog.b', 'tagButton.hogan' ) - .render( templateData ); - - $tag = $( tagHtml ) - .on( 'click', function () { - // Activate the tag - $( this ).toggleClass( 'mw-ui-progressive' ); - // If there are any tags active, switch submit button from - // "None" to "Next". - if ( self.$( '.tags .ui-tag-button.mw-ui-progressive' ).length ) { - $none.hide(); - $next.show(); - } else { - $next.hide(); - $none.show(); - } - } ).appendTo( self.$( '.tags' ) ); - } - } ); - - // only show the panel when we have created at least one button - if ( self.$( '.ui-tag-button' ).length ) { - self.$( '.spinner' ).hide(); - self.show(); - } - } - }, - /** - * Show suggestions to the user. - * Also record claims when the user hits the save button. - * FIXME: Please refactor - * @method - * @param {Object} options needed to render - */ - askWikidataQuestion: function ( options ) { - var self = this; - - self.$( '.wg-notice' ).hide(); - self.$( '.wg-buttons' ).hide(); - self.$( '.spinner' ).show(); - self.$( '.wg-content' ).text( 'Select tags that correctly describe ' + options.title ); - self.$( '.footer' ).show(); - - self._renderSuggestions( options.campaign ); - - this.$save = this.$( '.save' ); - this.$save.on( 'click', function () { - var answers = []; - self.$( '.tags .ui-tag-button' ).each( function () { - var $this = $( this ); - answers.push( { - correct: $this.is( '.mw-ui-progressive' ) ? true : null, - prop: $this.data( 'propname' ), - propid: $this.data( 'propid' ), - value: $this.data( 'readable' ), - valueid: $this.data( 'itemid' ), - campaign: $this.data( 'campaignname' ) - } ); - } ); - - self.$( '.tags' ).hide(); - self.$( '.spinner' ).show(); - self.apiWikiGrokResponse.recordClaims( answers ).done( function () { - self.postRecordClaims( options, true ); - } ).fail( function () { - self.handleError( 'no-response-cannot-record-user-input' ); - } ); - self.log( 'widget-click-submit' ); - } ); - - self.$( '.footer .none' ).on( 'click', function () { - self.log( 'widget-click-none' ); - } ); - }, - /** - * @inheritdoc - */ - postRender: function ( options ) { - var self = this; - - self.$( '.tags, .footer, .spinner' ).hide(); - - // show the welcome screen once - if ( !options.beginQuestions ) { - options.beginQuestions = true; - this.$( '.wg-buttons .cancel' ).on( 'click', function () { - self.hide(); - self.log( 'widget-click-nothanks' ); - } ); - this.$( '.wg-buttons .proceed' ).on( 'click', function () { - self.log( 'widget-click-accept' ); - // Proceed with asking the user a metadata question. - self.askWikidataQuestion( options ); - } ); - // Log more info clicks - this.$( '.wg-notice-link' ).on( 'click', function () { - self.log( 'widget-click-moreinfo' ); - } ); - - // hide wikigrok after an error has occurred - this.$( '.pane.error .close' ).on( 'click', function () { - self.hide(); - } ); - - this.reveal( options ); - } - }, - - /** - * @inheritdoc - */ - reveal: function ( options ) { - if ( options.campaign ) { - this.show(); - } - } - } ); - - M.define( 'ext.wikigrok.dialog.b/WikiGrokDialogB', WikiGrokDialogB ); -}( mw.mobileFrontend, jQuery ) ); diff --git a/resources/dialog.c/WikiGrokDialogC.js b/resources/dialog.c/WikiGrokDialogC.js index 452b87e..2b1f167 100644 --- a/resources/dialog.c/WikiGrokDialogC.js +++ b/resources/dialog.c/WikiGrokDialogC.js @@ -1,5 +1,5 @@ ( function ( M, $ ) { - var WikiGrokDialogB = M.require( 'ext.wikigrok.dialog.b/WikiGrokDialogB' ), + var WikiGrokDialog = M.require( 'ext.wikigrok.dialog/WikiGrokDialog' ), wikiGrokRoulette = M.require( 'ext.wikigrok.roulette/wikiGrokRoulette' ), Drawer = M.require( 'Drawer' ), browser = M.require( 'browser' ), @@ -9,13 +9,13 @@ /** * WikiGrok that's fixed at the bottom of the page. * @class WikiGrokDialogC - * @extends WikiGrokDialogB + * @extends WikiGrokDialog */ - WikiGrokDialogC = WikiGrokDialogB.extend( { + WikiGrokDialogC = WikiGrokDialog.extend( { appendToElement: Drawer.prototype.appendToElement, - className: WikiGrokDialogB.prototype.className + ' ' + Drawer.prototype.className, + className: WikiGrokDialog.prototype.className + ' ' + Drawer.prototype.className, version: 'c', - defaults: $.extend( WikiGrokDialogB.prototype.defaults, { + defaults: $.extend( WikiGrokDialog.prototype.defaults, { thanksMsg: 'You just made Wikipedia a little better, thanks! But wait, there is more.', isDrawer: true } ), @@ -98,7 +98,7 @@ // Don't run this when rendering the Badge template if ( !options.beginQuestions ) { options.beginQuestions = true; - WikiGrokDialogB.prototype.postRender.apply( this, arguments ); + WikiGrokDialog.prototype.postRender.apply( this, arguments ); self.askWikidataQuestion( options ); diff --git a/resources/dialog/WikiGrokDialog.hogan b/resources/dialog/WikiGrokDialog.hogan index 4780720..ab4b044 100644 --- a/resources/dialog/WikiGrokDialog.hogan +++ b/resources/dialog/WikiGrokDialog.hogan @@ -1,3 +1,4 @@ +<!-- note English language only! --> <div class="pane content"> <p class="wg-content">{{contentMsg}}</p> <p class="wg-buttons"> @@ -5,9 +6,18 @@ <button class="{{classes}}">{{label}}</button> {{/buttons}} </p> + <p class="tags"> + </p> + {{{spinner}}} {{#noticeMsg}} <p class="wg-notice">{{{noticeMsg}}}</p> {{/noticeMsg}} + + <div class="footer"> + <span class="license">Submissions are <a href="#/wikigrok/about">released freely</a>.</span> + <button class="mw-ui-button save none">None of these</button> + <button class="mw-ui-button mw-ui-constructive save next">Next</button> + </div> </div> {{>error}} diff --git a/resources/dialog/WikiGrokDialog.js b/resources/dialog/WikiGrokDialog.js index 13454ea..aa2e89a 100644 --- a/resources/dialog/WikiGrokDialog.js +++ b/resources/dialog/WikiGrokDialog.js @@ -10,18 +10,20 @@ errorSchema = new Schema( {}, 'MobileWebWikiGrokError' ), WikiGrokDialog, timer = null, - $window = $( window ); + $window = $( window ), + wikiGrokCampaigns = M.require( 'ext.wikigrok.startup/wikiGrokCampaigns' ); /** * @class WikiGrokDialog * @extends Panel * This creates the dialog at the bottom of the lead section that appears when a user * scrolls past the lead. It asks the user to confirm metadata information for use - * in Wikidata (https://www.wikidata.org). + * in Wikidata (https://www.wikidata.org) */ WikiGrokDialog = Panel.extend( { - version: 'a', + version: 'b', className: 'wikigrok', + /** * @cfg {Object} defaults Default options hash. * @cfg {Boolean} defaults.beginQuestions Whether to show questions. @@ -29,6 +31,8 @@ * @cfg {String} defaults.closeMsg Text for the button in an overlay that, when clicked, * dismisses the overlay. * @cfg {String} defaults.contentMsg Message that tells what to do, it's the message + * @cfg {String} defaults.tellMoreMsg Text for the button that the user clicks to find out + * more about WikiGrok, which is shown after they've answered a question * in the first pane of main dialog. * @cfg {Array} defaults.buttons Array of {Object}s that will be used as options to * create buttons. Defaults to 'No, thanks' and 'Okay!' buttons. @@ -85,13 +89,8 @@ } ); Panel.prototype.initialize.apply( this, arguments ); - // log page impression - // Only turn this on for bucketed tests with a relatively small number of - // users, e.g. 10% of readers. - //self.logPageImpression(); - - // log widget impression when the widget is shown this.once( 'show', function () { + //self.logPageImpression(); self.initializeWidgetImpressionLogging(); } ); }, @@ -187,51 +186,147 @@ }, /** - * Creates a question with a yes, no and not sure answer. - * Makes API request to Wikidata to retrieve labels and uses campaigns for that. - * FIXME: No i18n + * Renders 1 to 4 random suggestions to the panel. The panel is only shown to the user when + * there are suggestions. + * + * The suggestions are generated by the WikiGrok backend and made available in the + * wgWikiGrokCampaigns configuration variable. + * * @method - * @param {Object} options needed to render. + * @private + */ + _renderSuggestions: function () { + var suggestions, + allSuggestions, + $next, + $none, + self = this; + + allSuggestions = wikiGrokCampaigns.getAllSuggestions(); + // randomly pick 4 suggestions + suggestions = self.chooseRandomItemsFromArray( allSuggestions, 4 ); + + // Now work out the labels if we have some suggestions + if ( suggestions.length ) { + $next = self.$( '.footer .next' ); + $none = self.$( '.footer .none' ); + + // Hard-code the "Next" button width to match the "None" button width. + // That way, when the buttons are switched, the width stays the same. + // This depends on the assumption that the "Next" button text is + // always shorter than the "None" button text. + $next.css( 'width', $none.outerWidth() ); + + self.$( '.tags' ).show(); + $.each( suggestions, function ( i, suggestion ) { + var $tag, tagHtml, templateData, propertyName; + + // Replace some property names + // FIXME: These are not internationalizable and will probably have to + // die once WikiGrok is deployed outside of English Wikipedia + if ( suggestion.campaign.propertyName === 'instance of' ) { + propertyName = 'type'; + } else if ( suggestion.campaign.propertyName === 'original language of work' ) { + propertyName = 'original language'; + } else { + propertyName = suggestion.campaign.propertyName; + } + + templateData = { + id: 'tag-' + suggestion.id, + propName: suggestion.campaign.propertyName, + propId: suggestion.campaign.propertyId, + itemId: suggestion.id, + readable: suggestion.label, + campaignName: suggestion.campaign.name, + campaignText: propertyName, + tagText: suggestion.label + }; + + if ( suggestion.label ) { + tagHtml = mw.template.get( 'ext.wikigrok.dialog', 'tagButton.hogan' ) + .render( templateData ); + + $tag = $( tagHtml ) + .on( 'click', function () { + // Activate the tag + $( this ).toggleClass( 'mw-ui-progressive' ); + // If there are any tags active, switch submit button from + // "None" to "Next". + if ( self.$( '.tags .ui-tag-button.mw-ui-progressive' ).length ) { + $none.hide(); + $next.show(); + } else { + $next.hide(); + $none.show(); + } + } ).appendTo( self.$( '.tags' ) ); + } + } ); + + // only show the panel when we have created at least one button + if ( self.$( '.ui-tag-button' ).length ) { + self.$( '.spinner' ).hide(); + self.show(); + } + } + }, + + /** + * Show suggestions to the user. + * Also record claims when the user hits the save button. + * FIXME: Please refactor + * @method + * @param {Object} options needed to render */ askWikidataQuestion: function ( options ) { - var self = this, - vowels = [ 'a', 'e', 'i', 'o', 'u' ]; + var self = this; - options.claimId = options.campaign.randomClaimId; - options.claimLabel = options.campaign.questions[options.claimId]; + self.$( '.wg-notice' ).hide(); + self.$( '.wg-buttons' ).hide(); + self.$( '.spinner' ).show(); + self.$( '.wg-content' ).text( 'Select tags that correctly describe ' + options.title ); + self.$( '.footer' ).show(); - if ( options.campaign.name === 'author' ) { - // Hack for English prototype - if ( $.inArray( options.claimLabel.charAt( 0 ), vowels ) === -1 ) { - options.contentMsg = 'Is ' + options.name + ' a ' + options.claimLabel + '?'; - } else { - options.contentMsg = 'Is ' + options.name + ' an ' + options.claimLabel + '?'; + self._renderSuggestions( options.campaign ); + + this.$save = this.$( '.save' ); + this.$save.on( 'click', function () { + var answers = [], + hasCorrectAnswer = false; + + self.$( '.tags .ui-tag-button' ).each( function () { + var $this = $( this ), + correct = $this.is( '.mw-ui-progressive' ); + + if ( !hasCorrectAnswer && correct ) { + hasCorrectAnswer = true; + } + + answers.push( { + correct: correct ? true : null, + prop: $this.data( 'propname' ), + propid: $this.data( 'propid' ), + value: $this.data( 'readable' ), + valueid: $this.data( 'itemid' ), + campaign: $this.data( 'campaignname' ) + } ); + } ); + + self.$( '.tags' ).hide(); + self.$( '.spinner' ).show(); + + if ( !hasCorrectAnswer ) { + self.log( 'widget-click-none' ); } - } else if ( options.campaign.name === 'actor' ) { - options.contentMsg = 'Is ' + options.name + ' a ' + options.claimLabel + '?'; - } else if ( options.campaign.name === 'album' ) { - options.contentMsg = 'Is this a ' + options.claimLabel + '?'; - } - // Re-render with new content for 'Question' step - options.beginQuestions = true; - options.buttons = [ - { - classes: 'yes inline mw-ui-button mw-ui-progressive', - label: 'Yes' - }, - { - classes: 'not-sure inline mw-ui-button', - label: 'Not Sure' - }, - { - classes: 'no inline mw-ui-button mw-ui-progressive', - label: 'No' - } - ]; - options.noticeMsg = 'All submissions are <a class="wg-notice-link" ' + - 'href="#/wikigrok/about">released freely</a>'; - self.render( options ); + self.apiWikiGrokResponse.recordClaims( answers ).done( function () { + self.postRecordClaims( options, true ); + } ).fail( function () { + self.handleError( 'no-response-cannot-record-user-input' ); + } ); + self.log( 'widget-click-submit' ); + } ); }, /** @@ -414,33 +509,18 @@ this.log( 'page-impression' ); } }, + /** * @inheritdoc */ postRender: function ( options ) { var self = this; - // If you're wondering where the DOM insertion happens, look in wikigrokeval.js. + self.$( '.tags, .footer, .spinner' ).hide(); - // Initialize all the buttons and links - // ...for intermediate 'Question' step - if ( options.beginQuestions ) { - this.$( '.wg-buttons .yes' ).on( 'click', function () { - self.log( 'widget-click-submit' ); - options.claimIsCorrect = true; - self.recordClaim( options ); - } ); - this.$( '.wg-buttons .not-sure' ).on( 'click', function () { - self.log( 'widget-click-submit' ); - self.postRecordClaims( options, false ); - } ); - this.$( '.wg-buttons .no' ).on( 'click', function () { - self.log( 'widget-click-submit' ); - options.claimIsCorrect = false; - self.recordClaim( options ); - } ); - // ...for initial 'Intro' step - } else { + // show the welcome screen once + if ( !options.beginQuestions ) { + options.beginQuestions = true; this.$( '.wg-buttons .cancel' ).on( 'click', function () { self.hide(); self.log( 'widget-click-nothanks' ); @@ -454,19 +534,14 @@ this.$( '.wg-notice-link' ).on( 'click', function () { self.log( 'widget-click-moreinfo' ); } ); + + // hide wikigrok after an error has occurred + this.$( '.pane.error .close' ).on( 'click', function () { + self.hide(); + } ); + + this.reveal( options ); } - - // hide wikigrok after an error has occurred - this.$( '.pane.error .close' ).on( 'click', function () { - self.hide(); - } ); - - // render() does a "deep copy" $.extend() on the template data, so we need - // to reset the buttons after each step (since some steps have fewer - // buttons than the initial default). - self.options.buttons = []; - - this.reveal( options ); }, /** diff --git a/resources/dialog.b/tagButton.hogan b/resources/dialog/tagButton.hogan similarity index 100% rename from resources/dialog.b/tagButton.hogan rename to resources/dialog/tagButton.hogan diff --git a/resources/dialog.b/tagButton.less b/resources/dialog/tagButton.less similarity index 100% rename from resources/dialog.b/tagButton.less rename to resources/dialog/tagButton.less diff --git a/resources/startup/init.js b/resources/startup/init.js index 7daa63e..e1bc162 100644 --- a/resources/startup/init.js +++ b/resources/startup/init.js @@ -11,14 +11,9 @@ permittedOnThisDevice = mw.config.get( 'wgWikiGrokUIEnableOnAllDevices' ) || !browser.isWideScreen(), idOverride, versionConfigs = { - A: { + B: { module: 'ext.wikigrok.dialog', view: 'ext.wikigrok.dialog/WikiGrokDialog', - name: 'a' - }, - B: { - module: 'ext.wikigrok.dialog.b', - view: 'ext.wikigrok.dialog.b/WikiGrokDialogB', name: 'b' }, C: { @@ -27,10 +22,9 @@ name: 'c' } }, + DEFAULT_VERSION = 'B', versionConfig, - WikiGrokAbTest = M.require( 'ext.wikigrok.abTest/WikiGrokAbTest' ), - wikiGrokUser = M.require( 'ext.wikigrok.abTest/wikiGrokUser' ), - wikiGrokVersion = query.wikigrokversion, + wikiGrokUser = M.require( 'ext.wikigrok.startup/wikiGrokUser' ), wikiGrokCampaigns = M.require( 'ext.wikigrok.startup/wikiGrokCampaigns' ), campaign = wikiGrokCampaigns.getRandomCampaign(); @@ -57,40 +51,30 @@ * Gets the configuration for the version of WikiGrok to use. * * The `wikigrokversion` query parameter can be used to override this logic, - * `wikigrokversion=a` means that A will always be used. The `wikigrokversion` + * `wikigrokversion=b` means that B will always be used. The `wikigrokversion` * hash takes precedence over the query parameter. For example, * `#wikigrokversion=c` is used for loading wikigrok roulette. * If the override version doesn't exist, then the default version - * (currently A) will be used. + * (currently B) will be used. * - * If the user is eligible to enter the WikiGrok AB test, then the test - * determines which version to use. * @method * @ignore * @return {Object|null} */ function getWikiGrokConfig() { - var versionOverride, - versionConfig = null, - wikiGrokAbTest = WikiGrokAbTest.newFromMwConfig(); + var wikiGrokVersion = query.wikigrokversion || DEFAULT_VERSION; if ( window.location.hash.toLowerCase() === '#wikigrokversion=c' ) { wikiGrokVersion = 'c'; } - // See if there is a query string override or in hash - if ( wikiGrokVersion ) { - versionOverride = wikiGrokVersion.toUpperCase(); + wikiGrokVersion = wikiGrokVersion.toUpperCase(); - if ( versionConfigs.hasOwnProperty( versionOverride ) ) { - versionConfig = versionConfigs[versionOverride]; - } - // Otherwise, see if A/B test is running, and if so, choose a version. - } else if ( wikiGrokAbTest.isEnabled ) { - versionConfig = versionConfigs[wikiGrokAbTest.getVersion( wikiGrokUser )]; + if ( versionConfigs.hasOwnProperty( wikiGrokVersion ) ) { + return versionConfigs[wikiGrokVersion]; } - return versionConfig; + return null; } versionConfig = getWikiGrokConfig(); @@ -177,7 +161,8 @@ taskType: 'version ' + versionConfig.name, taskToken: mw.user.generateRandomSessionId(), userToken: wikiGrokUser.getToken(), - isLoggedIn: !wikiGrokUser.isAnon() + isLoggedIn: !wikiGrokUser.isAnon(), + pageId: mw.config.get( 'wgArticleId' ) }; if ( idOverride ) { data.testing = true; diff --git a/resources/abTest/wikigrokuser.js b/resources/startup/wikigrokuser.js similarity index 94% rename from resources/abTest/wikigrokuser.js rename to resources/startup/wikigrokuser.js index 1db07b4..61ace0c 100644 --- a/resources/abTest/wikigrokuser.js +++ b/resources/startup/wikigrokuser.js @@ -40,6 +40,6 @@ } }, user ); - M.define( 'ext.wikigrok.abTest/wikiGrokUser', wikiGrokUser ); + M.define( 'ext.wikigrok.startup/wikiGrokUser', wikiGrokUser ); } ( mw.mobileFrontend, jQuery, mw ) ); diff --git a/tests/qunit/test_WikiGrokAbTest.js b/tests/qunit/test_WikiGrokAbTest.js deleted file mode 100644 index a4903fa..0000000 --- a/tests/qunit/test_WikiGrokAbTest.js +++ /dev/null @@ -1,34 +0,0 @@ -( function ( M, $ ) { - - var WikiGrokAbTest = M.require( 'WikiGrokAbTest' ), - wikiGrokUser = M.require( 'wikiGrokUser' ), - enabledTest = new WikiGrokAbTest( true ); - - QUnit.module( 'MobileFrontend: modules/wikigrok/WikiGrokAbTest' ); - - QUnit.test( 'isEnabled simply exposes the isEnabled constructor parameter', 2, function ( assert ) { - assert.strictEqual( new WikiGrokAbTest( false ).isEnabled, false ); - assert.strictEqual( new WikiGrokAbTest( true ).isEnabled, true ); - } ); - - QUnit.test( 'getVersion()', 62, function ( assert ) { - - // A map of expected version to last character of the user's token - var dataProvider = { - A: '0123456789ABCDEFGHIJKLMNOPQRSTU'.split( '' ), - B: 'VWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ) - }; - - this.stub( wikiGrokUser, 'getToken' ); - - $.each( dataProvider, function ( expectedVersion, tokens ) { - $.each( tokens, function ( i, token ) { - wikiGrokUser.getToken.returns( token ); - - assert.strictEqual( enabledTest.getVersion( wikiGrokUser ), expectedVersion ); - } ); - } ); - - } ); - -}( mw.mobileFrontend, jQuery ) ); diff --git a/tests/qunit/test_WikiGrokDialog.js b/tests/qunit/test_WikiGrokDialog.js index 0910bbe..e675dd9 100644 --- a/tests/qunit/test_WikiGrokDialog.js +++ b/tests/qunit/test_WikiGrokDialog.js @@ -3,29 +3,40 @@ var WikiGrokDialog = M.require( 'WikiGrokDialog' ), WikiGrokResponseApi = M.require( 'WikiGrokResponseApi' ), wikiGrokCampaigns = M.require( 'wikiGrokCampaigns' ), - settings = M.require( 'settings' ), campaigns = { - album: { - property: 'P31', + actor: { + property: 'P106', questions: { - Q208569: 'studio album', - Q209939: 'live album' - } + Q10798782: 'television actor', + Q10800557: 'film actor' + }, + name: 'actor', + propertyId: 'P106', + propertyName: 'occupation' } }, suggestions = { - album: { - id: 'P31', - list: [ 'Q208569', 'Q209939' ], - name: 'album' + actor: { + id: 'P106', + list: [ 'Q10798782', 'Q10800557' ], + name: 'actor' } }, - pageTitle = M.getCurrentPage().title || 'Some guy'; + pageTitle = 'Some guy'; - function getPagesWithWikiGrokContributions() { - return $.parseJSON( - settings.get( 'pagesWithWikiGrokContributions', false ) || '{}' - ); + /** + * Debug the wikigrok dialogs by showing it in screen + * active when debug=true in location + */ + function debugDialog() { + if ( + mw.mobileFrontend.query && + Boolean( mw.mobileFrontend.query.debug ) === true + ) { + this.wk.remove = function () { + }; + this.wk.prependTo( '#content' ).show(); + } } QUnit.module( 'MobileFrontend: WikiGrokDialog', { @@ -33,16 +44,14 @@ this.wk.remove(); }, setup: function () { - settings.remove( 'pagesWithWikiGrokContributions', false ); - // don't run eventLogging - this.stub( WikiGrokDialog.prototype, 'log' ); - this.stub( WikiGrokDialog.prototype, 'logError' ); - // show method is async and not needed on tests and will interfere with them trigging a call to logPageImpression - this.stub( WikiGrokDialog.prototype, 'show' ); + this.sandbox.stub( WikiGrokDialog.prototype, 'log' ); + this.sandbox.stub( WikiGrokDialog.prototype, 'logError' ); this.sandbox.stub( mw.config, 'get' ).withArgs( 'wgWikiGrokCampaigns' ) .returns( campaigns ); + this.sandbox.stub( WikiGrokResponseApi.prototype, 'recordClaims' ) + .returns( $.Deferred().resolve() ); this.$el = $( '<div id="test">' ); this.wk = new WikiGrokDialog( { @@ -55,6 +64,7 @@ // Set suggestions to go to the second screen. suggestions: suggestions } ); + debugDialog.apply( this ); } } ); @@ -105,18 +115,6 @@ ); } ); - /** - * Debug the wikigrok dialog by showing it in screen - * Use in tests like: - * debugDialog.apply( this ); - */ - /* - function debugDialog() { - this.wk.remove = function() {}; - this.wk.prependTo( '#content' ).show(); - } - */ - QUnit.test( '#UI renders initial screen', 3, function ( assert ) { // Lets check that we've got the info, buttons and 'Tell me more' assert.ok( this.$el.find( '.wg-content' ).text().length > 0 ); @@ -126,85 +124,86 @@ QUnit.test( '#UI clicking no thanks hides the dialog', 1, function ( assert ) { var spy = this.sandbox.stub( WikiGrokDialog.prototype, 'hide' ); - this.$el.find( '.cancel' ).click(); + this.$el.find( '.cancel' ).trigger( 'click' ); assert.ok( spy.called ); } ); - QUnit.test( '#UI clicking OK, takes you to the question dialog', 1, function ( assert ) { - this.$el.find( '.proceed' ).click(); - // the question title is visible - assert.notEqual( this.$el.text().indexOf( 'Is this a' ), -1, 'Question is visible' ); + QUnit.asyncTest( '#UI clicking OK, takes you to the question dialog', function ( assert ) { + QUnit.expect( 3 ); + this.wk.$el.find( '.proceed' ).trigger( 'click' ); + // The name of the page is on the question + assert.ok( this.wk.$el.text().indexOf( pageTitle ) !== -1 ); + // After loading + setTimeout( $.proxy( function () { + // The question is there + var tags = this.wk.$el.find( '.tags .ui-tag-button' ), + labels = tags.find( 'label' ); + assert.strictEqual( tags.length, 2, 'Correct number of tags' ); + assert.strictEqual( labels.first().text(), + campaigns.actor.propertyName, 'Correct label text' ); + QUnit.start(); + }, this ), 0 ); } ); - function answerQuestion( sel ) { - this.sandbox.stub( WikiGrokResponseApi.prototype, 'recordClaims' ) - .returns( $.Deferred().resolve() ); - this.$el.find( sel ).click(); - } + QUnit.asyncTest( '#UI - Question - Answer correct', function ( assert ) { + QUnit.expect( 1 ); + this.wk.$el.find( '.proceed' ).trigger( 'click' ); + this.$el.find( '.ui-tag-button' ).trigger( 'click' ); + this.$el.find( '.save' ).trigger( 'click' ); - QUnit.test( '#UI - Question - Click Yes', 4, function ( assert ) { - this.$el.find( '.proceed' ).click(); - - assert.equal( - getPagesWithWikiGrokContributions()[ pageTitle ], - undefined, - 'User\'s contribution to WikiGrok has not been saved locally yet.' - ); - - answerQuestion.call( this, '.yes' ); - - // I'm in thanks page now! - assert.equal( this.$el.find( '.wg-link' ).length, 1 ); - assert.equal( this.$el.find( '.wg-link>a' ).length, 1 ); - - assert.equal( - getPagesWithWikiGrokContributions()[ pageTitle ], - true, - 'User\'s contribution to WikiGrok has been saved locally correctly.' - ); + setTimeout( $.proxy( function () { + // I'm in thanks page now! + assert.notEqual( this.$el.find( '.wg-link' ).css( 'display' ), 'none' ); + QUnit.start(); + }, this ), 0 ); } ); - QUnit.test( '#UI - Question - Click No', 4, function ( assert ) { - this.$el.find( '.proceed' ).click(); + QUnit.asyncTest( '#UI - Question - Answer incorrect', function ( assert ) { + QUnit.expect( 1 ); + this.wk.$el.find( '.proceed' ).trigger( 'click' ); + this.$el.find( '.save' ).trigger( 'click' ); - assert.equal( - getPagesWithWikiGrokContributions()[ pageTitle ], - undefined, - 'User\'s contribution to WikiGrok has not been saved locally yet.' - ); - - answerQuestion.call( this, '.no' ); - - // I'm in thanks page now! - assert.equal( this.$el.find( '.wg-link' ).length, 1 ); - assert.equal( this.$el.find( '.wg-link>a' ).length, 1 ); - - assert.equal( - getPagesWithWikiGrokContributions()[ pageTitle ], - true, - 'User\'s contribution to WikiGrok has been saved locally correctly.' - ); + setTimeout( $.proxy( function () { + // I'm in thanks page now! + assert.notEqual( this.$el.find( '.wg-link' ).css( 'display' ), 'none' ); + QUnit.start(); + }, this ), 0 ); } ); - QUnit.test( '#UI - Question - Click Not sure', 4, function ( assert ) { - this.$el.find( '.proceed' ).click(); + QUnit.module( 'MobileFrontend: WikiGrokDialog', { + setup: function () { + this.sandbox.stub( WikiGrokDialog.prototype, 'log' ); + this.logErrorSpy = this.sandbox.stub( WikiGrokDialog.prototype, 'logError' ); - assert.equal( - getPagesWithWikiGrokContributions()[ pageTitle ], - undefined, - 'User\'s contribution to WikiGrok has not been saved locally yet.' - ); + this.sandbox.stub( mw.config, 'get' ).withArgs( 'wgWikiGrokCampaigns' ) + .returns( campaigns ); + this.sandbox.stub( WikiGrokResponseApi.prototype, 'recordClaims' ) + .returns( $.Deferred().reject() ); - answerQuestion.call( this, '.not-sure' ); + this.$el = $( '<div id="test">' ); + this.wk = new WikiGrokDialog( { + el: this.$el, + campaign: wikiGrokCampaigns.getRandomCampaign(), + itemId: '1234', + title: pageTitle, + userToken: 'token', + testing: false + } ); + } + } ); - // I'm in thanks page now! - assert.equal( this.$el.find( '.wg-link' ).length, 1 ); - assert.equal( this.$el.find( '.wg-link>a' ).length, 1 ); - assert.equal( - getPagesWithWikiGrokContributions()[ pageTitle ], - true, - 'User\'s contribution to WikiGrok has been saved locally correctly.' - ); + QUnit.asyncTest( '#Handle error', function ( assert ) { + QUnit.expect( 1 ); + var spy = this.sandbox.stub( WikiGrokDialog.prototype, 'handleError' ); + this.wk.reveal( {} ); + this.wk.$el.find( '.proceed' ).trigger( 'click' ); + QUnit.start(); + // After loading + setTimeout( $.proxy( function () { + // save should thrown an error because of recordClaims + this.wk.$el.find( '.save' ).trigger( 'click' ); + assert.ok( spy.called, 'Error is handled.' ); + }, this ), 0 ); } ); }( jQuery, mw.mobileFrontend ) ); diff --git a/tests/qunit/test_WikiGrokDialogB.js b/tests/qunit/test_WikiGrokDialogB.js deleted file mode 100644 index 9b0cc4b..0000000 --- a/tests/qunit/test_WikiGrokDialogB.js +++ /dev/null @@ -1,162 +0,0 @@ -( function ( $, M ) { - - var WikiGrokDialogB = M.require( 'WikiGrokDialogB' ), - WikiGrokResponseApi = M.require( 'WikiGrokResponseApi' ), - wikiGrokCampaigns = M.require( 'wikiGrokCampaigns' ), - campaigns = { - actor: { - property: 'P106', - questions: { - Q10798782: 'television actor', - Q10800557: 'film actor' - }, - name: 'actor', - propertyId: 'P106', - propertyName: 'occupation' - } - }, - suggestions = { - actor: { - id: 'P106', - list: [ 'Q10798782', 'Q10800557' ], - name: 'actor' - } - }, - pageTitle = 'Some guy'; - - /** - * Debug the wikigrok dialogs by showing it in screen - * active when debug=true in location - */ - function debugDialog() { - if ( - mw.mobileFrontend.query && - Boolean( mw.mobileFrontend.query.debug ) === true - ) { - this.wk.remove = function () { - }; - this.wk.prependTo( '#content' ).show(); - } - } - - QUnit.module( 'MobileFrontend: WikiGrokDialogB', { - teardown: function () { - this.wk.remove(); - }, - setup: function () { - // don't run eventLogging - this.sandbox.stub( WikiGrokDialogB.prototype, 'log' ); - this.sandbox.stub( WikiGrokDialogB.prototype, 'logError' ); - - this.sandbox.stub( mw.config, 'get' ).withArgs( 'wgWikiGrokCampaigns' ) - .returns( campaigns ); - this.sandbox.stub( WikiGrokResponseApi.prototype, 'recordClaims' ) - .returns( $.Deferred().resolve() ); - - this.$el = $( '<div id="test">' ); - this.wk = new WikiGrokDialogB( { - el: this.$el, - campaign: wikiGrokCampaigns.getRandomCampaign(), - itemId: '1234', - title: pageTitle, - userToken: 'token', - testing: false, - // Set suggestions to go to the second screen. - suggestions: suggestions - } ); - debugDialog.apply( this ); - } - } ); - - QUnit.test( '#UI renders initial screen', 3, function ( assert ) { - // Lets check that we've got the info, buttons and 'Tell me more' - assert.ok( this.$el.find( '.wg-content' ).text().length > 0 ); - assert.strictEqual( this.$el.find( '.wg-buttons button' ).length, 2 ); - assert.ok( this.$el.find( '.wg-notice>a' ).attr( 'href' ).length > 0 ); - } ); - - QUnit.test( '#UI clicking no thanks hides the dialog', 1, function ( assert ) { - var spy = this.sandbox.stub( WikiGrokDialogB.prototype, 'hide' ); - this.$el.find( '.cancel' ).trigger( 'click' ); - assert.ok( spy.called ); - } ); - - QUnit.asyncTest( '#UI clicking OK, takes you to the question dialog', function ( assert ) { - QUnit.expect( 3 ); - this.wk.$el.find( '.proceed' ).trigger( 'click' ); - // The name of the page is on the question - assert.ok( this.wk.$el.text().indexOf( pageTitle ) !== -1 ); - // After loading - setTimeout( $.proxy( function () { - // The question is there - var tags = this.wk.$el.find( '.tags .ui-tag-button' ), - labels = tags.find( 'label' ); - assert.strictEqual( tags.length, 2, 'Correct number of tags' ); - assert.strictEqual( labels.first().text(), - campaigns.actor.propertyName, 'Correct label text' ); - QUnit.start(); - }, this ), 0 ); - } ); - - QUnit.asyncTest( '#UI - Question - Answer correct', function ( assert ) { - QUnit.expect( 1 ); - this.wk.$el.find( '.proceed' ).trigger( 'click' ); - this.$el.find( '.ui-tag-button' ).trigger( 'click' ); - this.$el.find( '.save' ).trigger( 'click' ); - - setTimeout( $.proxy( function () { - // I'm in thanks page now! - assert.notEqual( this.$el.find( '.wg-link' ).css( 'display' ), 'none' ); - QUnit.start(); - }, this ), 0 ); - } ); - - QUnit.asyncTest( '#UI - Question - Answer incorrect', function ( assert ) { - QUnit.expect( 1 ); - this.wk.$el.find( '.proceed' ).trigger( 'click' ); - this.$el.find( '.save' ).trigger( 'click' ); - - setTimeout( $.proxy( function () { - // I'm in thanks page now! - assert.notEqual( this.$el.find( '.wg-link' ).css( 'display' ), 'none' ); - QUnit.start(); - }, this ), 0 ); - } ); - - QUnit.module( 'MobileFrontend: WikiGrokDialogB', { - setup: function () { - this.sandbox.stub( WikiGrokDialogB.prototype, 'log' ); - this.logErrorSpy = this.sandbox.stub( WikiGrokDialogB.prototype, 'logError' ); - - this.sandbox.stub( mw.config, 'get' ).withArgs( 'wgWikiGrokCampaigns' ) - .returns( campaigns ); - this.sandbox.stub( WikiGrokResponseApi.prototype, 'recordClaims' ) - .returns( $.Deferred().reject() ); - - this.$el = $( '<div id="test">' ); - this.wk = new WikiGrokDialogB( { - el: this.$el, - campaign: wikiGrokCampaigns.getRandomCampaign(), - itemId: '1234', - title: pageTitle, - userToken: 'token', - testing: false - } ); - } - } ); - - QUnit.asyncTest( '#Handle error', function ( assert ) { - QUnit.expect( 1 ); - var spy = this.sandbox.stub( WikiGrokDialogB.prototype, 'handleError' ); - this.wk.reveal( {} ); - this.wk.$el.find( '.proceed' ).trigger( 'click' ); - QUnit.start(); - // After loading - setTimeout( $.proxy( function () { - // save should thrown an error because of recordClaims - this.wk.$el.find( '.save' ).trigger( 'click' ); - assert.ok( spy.called, 'Error is handled.' ); - }, this ), 0 ); - } ); - -}( jQuery, mw.mobileFrontend ) ); -- To view, visit https://gerrit.wikimedia.org/r/191358 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Iec3921cddfe6fc026b5757eb338efc44232bbf7a Gerrit-PatchSet: 6 Gerrit-Project: mediawiki/extensions/WikiGrok Gerrit-Branch: master Gerrit-Owner: Phuedx <g...@samsmith.io> Gerrit-Reviewer: Bmansurov <bmansu...@wikimedia.org> Gerrit-Reviewer: Phuedx <g...@samsmith.io> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits