Merge remote-tracking branch 'gerrit/page-issues-cleanup'
Bug: T198765 Bug: T208514 Change-Id: I9467101c9b01cbd7682de859e43e04966760eea6
This commit is contained in:
commit
7b5928d795
@ -91,7 +91,6 @@ class MinervaHooks {
|
|||||||
// additional scaffolding (minus initialisation scripts)
|
// additional scaffolding (minus initialisation scripts)
|
||||||
'tests/qunit/skins.minerva.scripts/stubs.js',
|
'tests/qunit/skins.minerva.scripts/stubs.js',
|
||||||
|
|
||||||
'resources/skins.minerva.scripts/pageIssuesLogger.js',
|
|
||||||
'resources/skins.minerva.scripts/pageIssuesParser.js',
|
'resources/skins.minerva.scripts/pageIssuesParser.js',
|
||||||
'resources/skins.minerva.scripts/DownloadIcon.js',
|
'resources/skins.minerva.scripts/DownloadIcon.js',
|
||||||
'resources/skins.minerva.scripts/AB.js',
|
'resources/skins.minerva.scripts/AB.js',
|
||||||
@ -101,7 +100,6 @@ class MinervaHooks {
|
|||||||
'tests/qunit/skins.minerva.scripts/DownloadIcon.test.js',
|
'tests/qunit/skins.minerva.scripts/DownloadIcon.test.js',
|
||||||
'tests/qunit/skins.minerva.scripts/pageIssuesParser.test.js',
|
'tests/qunit/skins.minerva.scripts/pageIssuesParser.test.js',
|
||||||
'tests/qunit/skins.minerva.scripts/AB.test.js',
|
'tests/qunit/skins.minerva.scripts/AB.test.js',
|
||||||
'tests/qunit/skins.minerva.scripts/PageIssuesOverlay.test.js',
|
|
||||||
'tests/qunit/skins.minerva.scripts/pageIssues.test.js',
|
'tests/qunit/skins.minerva.scripts/pageIssues.test.js',
|
||||||
'tests/qunit/skins.minerva.notifications.badge/NotificationBadge.test.js'
|
'tests/qunit/skins.minerva.notifications.badge/NotificationBadge.test.js'
|
||||||
],
|
],
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
var
|
var
|
||||||
router = require( 'mediawiki.router' ),
|
router = require( 'mediawiki.router' ),
|
||||||
issues = M.require( 'skins.minerva.scripts/pageIssues' ),
|
|
||||||
overlayManager = M.require( 'skins.minerva.scripts/overlayManager' ),
|
overlayManager = M.require( 'skins.minerva.scripts/overlayManager' ),
|
||||||
loader = M.require( 'mobile.startup/rlModuleLoader' ),
|
loader = M.require( 'mobile.startup/rlModuleLoader' ),
|
||||||
skin = M.require( 'skins.minerva.scripts/skin' ),
|
skin = M.require( 'skins.minerva.scripts/skin' ),
|
||||||
@ -34,7 +33,6 @@
|
|||||||
*/
|
*/
|
||||||
function onEditLinkClick() {
|
function onEditLinkClick() {
|
||||||
var section = ( new mw.Uri( this.href ) ).query.section || 'all';
|
var section = ( new mw.Uri( this.href ) ).query.section || 'all';
|
||||||
issues.log( { action: 'editClicked' } );
|
|
||||||
router.navigate( '#/editor/' + section );
|
router.navigate( '#/editor/' + section );
|
||||||
// prevent folding section when clicking Edit by stopping propagation
|
// prevent folding section when clicking Edit by stopping propagation
|
||||||
return false;
|
return false;
|
||||||
|
@ -13,13 +13,11 @@
|
|||||||
* @extends Overlay
|
* @extends Overlay
|
||||||
*
|
*
|
||||||
* @param {IssueSummary[]} issues list of page issue summaries for display.
|
* @param {IssueSummary[]} issues list of page issue summaries for display.
|
||||||
* @param {PageIssuesLogger} logger E.g., { log: console.log }.
|
|
||||||
* @param {string} section
|
* @param {string} section
|
||||||
* @param {number} namespaceID
|
* @param {number} namespaceID
|
||||||
*/
|
*/
|
||||||
function PageIssuesOverlay( issues, logger, section, namespaceID ) {
|
function PageIssuesOverlay( issues, section, namespaceID ) {
|
||||||
var
|
var
|
||||||
options,
|
|
||||||
// Note only the main namespace is expected to make use of section issues, so the
|
// Note only the main namespace is expected to make use of section issues, so the
|
||||||
// heading will always be minerva-meta-data-issues-section-header regardless of
|
// heading will always be minerva-meta-data-issues-section-header regardless of
|
||||||
// namespace.
|
// namespace.
|
||||||
@ -27,24 +25,10 @@
|
|||||||
getNamespaceHeadingText( namespaceID ) :
|
getNamespaceHeadingText( namespaceID ) :
|
||||||
mwMsg( 'minerva-meta-data-issues-section-header' );
|
mwMsg( 'minerva-meta-data-issues-section-header' );
|
||||||
|
|
||||||
this.issues = issues;
|
Overlay.call( this, {
|
||||||
this.logger = logger;
|
issues: issues,
|
||||||
this.section = section;
|
heading: '<strong>' + headingText + '</strong>'
|
||||||
|
} );
|
||||||
options = {};
|
|
||||||
options.issues = issues;
|
|
||||||
|
|
||||||
// Set default logging data
|
|
||||||
this.defaultLoggerData = {};
|
|
||||||
// In the case of KEYWORD_ALL_SECTIONS all issues are in the overlay and the sectionNumbers
|
|
||||||
// field should be no different from the default behaviour.
|
|
||||||
if ( this.section !== KEYWORD_ALL_SECTIONS ) {
|
|
||||||
this.defaultLoggerData.sectionNumbers = [ this.section ];
|
|
||||||
}
|
|
||||||
options.heading = '<strong>' + headingText + '</strong>';
|
|
||||||
Overlay.call( this, options );
|
|
||||||
|
|
||||||
this.on( Overlay.EVENT_EXIT, this.onExit.bind( this ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OO.mfExtend( PageIssuesOverlay, Overlay, {
|
OO.mfExtend( PageIssuesOverlay, Overlay, {
|
||||||
@ -54,128 +38,15 @@
|
|||||||
*/
|
*/
|
||||||
className: 'overlay overlay-issues',
|
className: 'overlay overlay-issues',
|
||||||
|
|
||||||
/**
|
|
||||||
* @memberof PageIssuesOverlay
|
|
||||||
* @instance
|
|
||||||
*/
|
|
||||||
events: util.extend( {}, Overlay.prototype.events, {
|
|
||||||
'click a[href*=redlink]': 'onRedLinkClick',
|
|
||||||
'click a:not(.external):not([href*=edit])': 'onInternalClick',
|
|
||||||
// Only register attempts to edit an existing page (should be the one we are on),
|
|
||||||
// not internal clicks on redlinks to nonexistent pages:
|
|
||||||
'click a[href*="edit"]:not([href*=redlink])': 'onEditClick'
|
|
||||||
} ),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @memberof PageIssuesOverlay
|
* @memberof PageIssuesOverlay
|
||||||
* @instance
|
* @instance
|
||||||
*/
|
*/
|
||||||
templatePartials: util.extend( {}, Overlay.prototype.templatePartials, {
|
templatePartials: util.extend( {}, Overlay.prototype.templatePartials, {
|
||||||
content: mw.template.get( 'skins.minerva.scripts', 'PageIssuesOverlayContent.hogan' )
|
content: mw.template.get( 'skins.minerva.scripts', 'PageIssuesOverlayContent.hogan' )
|
||||||
} ),
|
} )
|
||||||
|
|
||||||
/**
|
|
||||||
* Log data via the associated logger, adding sectionNumbers to override the event default
|
|
||||||
* if applicable.
|
|
||||||
* @param {Object} data
|
|
||||||
* @instance
|
|
||||||
*/
|
|
||||||
log: function ( data ) {
|
|
||||||
this.logger.log( util.extend( {}, this.defaultLoggerData, data ) );
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: an "on enter" state is tracked by the issueClicked log event.
|
|
||||||
* @return {void}
|
|
||||||
*/
|
|
||||||
onExit: function () {
|
|
||||||
var logData = {
|
|
||||||
action: 'modalClose',
|
|
||||||
issuesSeverity: this.issues.map( issueSummaryToSeverity )
|
|
||||||
},
|
|
||||||
currentSection = this.section;
|
|
||||||
// When users close the modal, `sectionNumbers` should correlate to each visible issue
|
|
||||||
// in the modal, provided that this.section is a valid number and not
|
|
||||||
// `KEYWORD_ALL_SECTIONS`.
|
|
||||||
if ( this.section !== KEYWORD_ALL_SECTIONS ) {
|
|
||||||
logData.sectionNumbers = this.issues.map( function () {
|
|
||||||
return currentSection;
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
this.log( logData );
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event that is triggered when an internal link inside the overlay is clicked.
|
|
||||||
* This event will not be triggered if the link contains the edit keyword,
|
|
||||||
* in which case onEditClick will be
|
|
||||||
* fired. This is primarily used for instrumenting page issues (see
|
|
||||||
* https://meta.wikimedia.org/wiki/Schema:PageIssues).
|
|
||||||
* @param {JQuery.Event} ev
|
|
||||||
* @memberof PageIssuesOverlay
|
|
||||||
* @instance
|
|
||||||
*/
|
|
||||||
onInternalClick: function ( ev ) {
|
|
||||||
var severity = parseSeverity( this.$( ev.target ) );
|
|
||||||
this.log( {
|
|
||||||
action: 'modalInternalClicked',
|
|
||||||
issuesSeverity: [ severity ]
|
|
||||||
} );
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event that is triggered when a red link (e.g. a link to a page which doesn't exist)
|
|
||||||
* inside the overlay is clicked.
|
|
||||||
* @param {JQuery.Event} ev
|
|
||||||
* @memberof PageIssuesOverlay
|
|
||||||
* @instance
|
|
||||||
*/
|
|
||||||
onRedLinkClick: function ( ev ) {
|
|
||||||
var severity = parseSeverity( this.$( ev.target ) );
|
|
||||||
this.log( {
|
|
||||||
action: 'modalRedLinkClicked',
|
|
||||||
issuesSeverity: [ severity ]
|
|
||||||
} );
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event that is triggered when an edit link inside the overlay is clicked.
|
|
||||||
* This is primarily
|
|
||||||
* used for instrumenting page issues (see https://meta.wikimedia.org/wiki/Schema:PageIssues).
|
|
||||||
* The event will not be triggered in the case of red links.
|
|
||||||
* See onRedLinkClick for red links.
|
|
||||||
* @param {JQuery.Event} ev
|
|
||||||
* @memberof PageIssuesOverlay
|
|
||||||
* @instance
|
|
||||||
*/
|
|
||||||
onEditClick: function ( ev ) {
|
|
||||||
var severity = parseSeverity( this.$( ev.target ) );
|
|
||||||
this.log( {
|
|
||||||
action: 'modalEditClicked',
|
|
||||||
issuesSeverity: [ severity ]
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain severity associated with a given $target node by looking at associated parent node
|
|
||||||
* (defined by templatePartials, PageIssuesOverlayContent.hogan).
|
|
||||||
*
|
|
||||||
* @param {JQuery.Object} $target
|
|
||||||
* @return {string[]} severity as defined in associated PageIssue
|
|
||||||
*/
|
|
||||||
function parseSeverity( $target ) {
|
|
||||||
return $target.parents( '.issue-notice' ).data( 'severity' );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {IssueSummary} issue
|
|
||||||
* @return {string} A PageIssue.severity.
|
|
||||||
*/
|
|
||||||
function issueSummaryToSeverity( issue ) {
|
|
||||||
return issue.severity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain a suitable heading for the issues overlay based on the namespace
|
* Obtain a suitable heading for the issues overlay based on the namespace
|
||||||
* @param {number} namespaceID is the namespace to generate heading for
|
* @param {number} namespaceID is the namespace to generate heading for
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{{#issues}}
|
{{#issues}}
|
||||||
<li>
|
<li>
|
||||||
<div class="issue-notice" data-severity="{{severity}}">
|
<div class="issue-notice" data-severity="{{severity}}">
|
||||||
{{{icon}}}
|
{{{iconString}}}
|
||||||
<div class="issue-details">{{{text}}}</div>
|
<div class="issue-details">{{{text}}}</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -1,105 +1,17 @@
|
|||||||
( function ( M, $ ) {
|
( function ( M, $ ) {
|
||||||
var AB = M.require( 'skins.minerva.scripts/AB' ),
|
var Page = M.require( 'mobile.startup/Page' ),
|
||||||
Page = M.require( 'mobile.startup/Page' ),
|
|
||||||
allIssues = {},
|
allIssues = {},
|
||||||
KEYWORD_ALL_SECTIONS = 'all',
|
KEYWORD_ALL_SECTIONS = 'all',
|
||||||
config = mw.config,
|
config = mw.config,
|
||||||
user = mw.user,
|
|
||||||
NS_MAIN = 0,
|
NS_MAIN = 0,
|
||||||
NS_TALK = 1,
|
NS_TALK = 1,
|
||||||
NS_CATEGORY = 14,
|
NS_CATEGORY = 14,
|
||||||
CURRENT_NS = config.get( 'wgNamespaceNumber' ),
|
CURRENT_NS = config.get( 'wgNamespaceNumber' ),
|
||||||
Icon = M.require( 'mobile.startup/Icon' ),
|
|
||||||
pageIssuesLogger = M.require( 'skins.minerva.scripts/pageIssuesLogger' ),
|
|
||||||
pageIssuesParser = M.require( 'skins.minerva.scripts/pageIssuesParser' ),
|
pageIssuesParser = M.require( 'skins.minerva.scripts/pageIssuesParser' ),
|
||||||
PageIssuesOverlay = M.require( 'skins.minerva.scripts/PageIssuesOverlay' ),
|
PageIssuesOverlay = M.require( 'skins.minerva.scripts/PageIssuesOverlay' ),
|
||||||
// setup ab test
|
|
||||||
abTest = new AB( {
|
|
||||||
testName: 'WME.PageIssuesAB',
|
|
||||||
// Run AB only on article namespace, otherwise set samplingRate to 0,
|
|
||||||
// forcing user into control (i.e. ignored/not logged) group.
|
|
||||||
samplingRate: ( CURRENT_NS === NS_MAIN ) ? config.get( 'wgMinervaABSamplingRate', 0 ) : 0,
|
|
||||||
sessionId: user.sessionId()
|
|
||||||
} ),
|
|
||||||
QUERY_STRING_FLAG = mw.util.getParamValue( 'minerva-issues' ),
|
QUERY_STRING_FLAG = mw.util.getParamValue( 'minerva-issues' ),
|
||||||
// Per T204746 a user can request the new treatment regardless of test group
|
// T206179 should update this value to enable it
|
||||||
isUserRequestingNewTreatment = QUERY_STRING_FLAG === 'b',
|
newTreatmentEnabled = QUERY_STRING_FLAG === 'b';
|
||||||
newTreatmentEnabled = abTest.isB() || isUserRequestingNewTreatment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} IssueSummary
|
|
||||||
* @prop {string} severity A PageIssue.severity.
|
|
||||||
* @prop {Boolean} isMultiple Whether or not the issue is part of a "multiple issues" template.
|
|
||||||
* @prop {string} icon HTML string.
|
|
||||||
* @prop {string} text HTML string.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function isLoggingRequired( pageIssues ) {
|
|
||||||
// No logging necessary when the A/B test is disabled (control group).
|
|
||||||
return !isUserRequestingNewTreatment && abTest.isEnabled() && pageIssues.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array.reduce callback that returns the severity of page issues.
|
|
||||||
* In the case that a page-issue is part of a "multiple issues" template,
|
|
||||||
* returns the maximum severity for that group of issues.
|
|
||||||
*
|
|
||||||
* @param {array} formattedArr - the return array containing severities
|
|
||||||
* @param {IssueSummary} currentItem current IssueSummary object
|
|
||||||
* @param {number} currentIndex current index of pageIssues
|
|
||||||
* @param {array} pageIssues array of pageIssues
|
|
||||||
*
|
|
||||||
* @return {array} acc
|
|
||||||
*/
|
|
||||||
function formatPageIssuesSeverity( formattedArr, currentItem, currentIndex, pageIssues ) {
|
|
||||||
var lastItem = pageIssues[ currentIndex - 1 ],
|
|
||||||
lastFormattedIndex = formattedArr.length - 1,
|
|
||||||
lastFormattedValue = formattedArr[ lastFormattedIndex ];
|
|
||||||
// If the last and current item `isMultiple`, fold the maxSeverity
|
|
||||||
// of the two items into a single value.
|
|
||||||
if ( lastItem && lastItem.isMultiple && currentItem.isMultiple ) {
|
|
||||||
formattedArr[ lastFormattedIndex ] = pageIssuesParser.maxSeverity(
|
|
||||||
[ lastFormattedValue, currentItem.severity ]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
formattedArr.push( currentItem.severity );
|
|
||||||
}
|
|
||||||
return formattedArr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract a summary message from a cleanup template generated element that is
|
|
||||||
* friendly for mobile display.
|
|
||||||
* @param {Object} $box element to extract the message from
|
|
||||||
* @return {IssueSummary}
|
|
||||||
*/
|
|
||||||
function extractMessage( $box ) {
|
|
||||||
var SELECTOR = '.mbox-text, .ambox-text',
|
|
||||||
MULTIPLE_SELECTOR = '.mw-collapsible-content',
|
|
||||||
$container = $( '<div>' ),
|
|
||||||
pageIssue;
|
|
||||||
|
|
||||||
$box.find( SELECTOR ).each( function () {
|
|
||||||
var contents,
|
|
||||||
$this = $( this );
|
|
||||||
// Clean up talk page boxes
|
|
||||||
$this.find( 'table, .noprint' ).remove();
|
|
||||||
contents = $this.html();
|
|
||||||
|
|
||||||
if ( contents ) {
|
|
||||||
$( '<p>' ).html( contents ).appendTo( $container );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
pageIssue = pageIssuesParser.parse( $box.get( 0 ) );
|
|
||||||
|
|
||||||
return {
|
|
||||||
severity: pageIssue.severity,
|
|
||||||
isMultiple: $box.parent().is( MULTIPLE_SELECTOR ),
|
|
||||||
icon: pageIssue.icon.toHtmlString(),
|
|
||||||
text: $container.html()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a link element that opens the issues overlay.
|
* Create a link element that opens the issues overlay.
|
||||||
@ -138,8 +50,7 @@
|
|||||||
issueUrl = section === KEYWORD_ALL_SECTIONS ? '#/issues/' + KEYWORD_ALL_SECTIONS : '#/issues/' + section,
|
issueUrl = section === KEYWORD_ALL_SECTIONS ? '#/issues/' + KEYWORD_ALL_SECTIONS : '#/issues/' + section,
|
||||||
selector = 'table.ambox, table.tmbox, table.cmbox, table.fmbox',
|
selector = 'table.ambox, table.tmbox, table.cmbox, table.fmbox',
|
||||||
issues = [],
|
issues = [],
|
||||||
$link,
|
$link;
|
||||||
severity;
|
|
||||||
|
|
||||||
if ( section === KEYWORD_ALL_SECTIONS ) {
|
if ( section === KEYWORD_ALL_SECTIONS ) {
|
||||||
$metadata = page.$( selector );
|
$metadata = page.$( selector );
|
||||||
@ -154,8 +65,8 @@
|
|||||||
$this = $( this );
|
$this = $( this );
|
||||||
|
|
||||||
if ( $this.find( selector ).length === 0 ) {
|
if ( $this.find( selector ).length === 0 ) {
|
||||||
issue = extractMessage( $this );
|
issue = pageIssuesParser.extract( $this );
|
||||||
// Some issues after "extractMessage" has been run will have no text.
|
// Some issues after "extract" has been run will have no text.
|
||||||
// For example in Template:Talk header the table will be removed and no issue found.
|
// For example in Template:Talk header the table will be removed and no issue found.
|
||||||
// These should not be rendered.
|
// These should not be rendered.
|
||||||
if ( issue.text ) {
|
if ( issue.text ) {
|
||||||
@ -166,14 +77,10 @@
|
|||||||
// store it for later
|
// store it for later
|
||||||
allIssues[section] = issues;
|
allIssues[section] = issues;
|
||||||
|
|
||||||
if ( $metadata.length && inline ) {
|
// If issues were extracted and there are inline amboxes, add learn more
|
||||||
severity = pageIssuesParser.maxSeverity(
|
// and icon to the UI element.
|
||||||
issues.map( function ( issue ) { return issue.severity; } )
|
if ( issues.length && $metadata.length && inline ) {
|
||||||
);
|
issues[0].issue.icon.$el.prependTo( $metadata.eq( 0 ).find( '.mbox-text' ) );
|
||||||
new Icon( {
|
|
||||||
glyphPrefix: 'minerva',
|
|
||||||
name: pageIssuesParser.iconName( $metadata.get( 0 ), severity )
|
|
||||||
} ).prependTo( $metadata.find( '.mbox-text' ) );
|
|
||||||
$learnMore = $( '<span>' )
|
$learnMore = $( '<span>' )
|
||||||
.addClass( 'ambox-learn-more' )
|
.addClass( 'ambox-learn-more' )
|
||||||
.text( mw.msg( 'skin-minerva-issue-learn-more' ) );
|
.text( mw.msg( 'skin-minerva-issue-learn-more' ) );
|
||||||
@ -185,34 +92,12 @@
|
|||||||
$learnMore.appendTo( $metadata.find( '.mbox-text' ) );
|
$learnMore.appendTo( $metadata.find( '.mbox-text' ) );
|
||||||
}
|
}
|
||||||
$metadata.click( function () {
|
$metadata.click( function () {
|
||||||
var pageIssue = pageIssuesParser.parse( this );
|
|
||||||
pageIssuesLogger.log( {
|
|
||||||
action: 'issueClicked',
|
|
||||||
issuesSeverity: [ pageIssue.severity ],
|
|
||||||
sectionNumbers: [ section ]
|
|
||||||
} );
|
|
||||||
overlayManager.router.navigate( issueUrl );
|
overlayManager.router.navigate( issueUrl );
|
||||||
return false;
|
return false;
|
||||||
} );
|
} );
|
||||||
} else {
|
} else {
|
||||||
$link = createLinkElement( labelText );
|
$link = createLinkElement( labelText );
|
||||||
$link.attr( 'href', '#/issues/' + section );
|
$link.attr( 'href', '#/issues/' + section );
|
||||||
$link.click( function () {
|
|
||||||
pageIssuesLogger.log( {
|
|
||||||
action: 'issueClicked',
|
|
||||||
issuesSeverity: [
|
|
||||||
pageIssuesParser.maxSeverity(
|
|
||||||
getIssues( '0' )
|
|
||||||
.map( function ( issue ) { return issue.severity; } )
|
|
||||||
)
|
|
||||||
],
|
|
||||||
// In the old treatment, an issuesClicked event will always be '0'
|
|
||||||
// as the old treatment is always associated with the lead section and we
|
|
||||||
// are only sending one maximum severity for all of them.
|
|
||||||
// An issuesClicked event should only ever be associated with one issue box.
|
|
||||||
sectionNumbers: [ '0' ]
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
if ( $metadata.length ) {
|
if ( $metadata.length ) {
|
||||||
$link.insertAfter( $( 'h1#section_0' ) );
|
$link.insertAfter( $( 'h1#section_0' ) );
|
||||||
$metadata.remove();
|
$metadata.remove();
|
||||||
@ -257,7 +142,7 @@
|
|||||||
var lastIssue = allIssues[ section ][i - 1];
|
var lastIssue = allIssues[ section ][i - 1];
|
||||||
// If the last issue belongs to a "Multiple issues" template,
|
// If the last issue belongs to a "Multiple issues" template,
|
||||||
// and so does the current one, don't add the current one.
|
// and so does the current one, don't add the current one.
|
||||||
if ( lastIssue && lastIssue.isMultiple && issue.isMultiple ) {
|
if ( lastIssue && lastIssue.grouped && issue.grouped ) {
|
||||||
acc[ acc.length - 1 ] = section;
|
acc[ acc.length - 1 ] = section;
|
||||||
} else {
|
} else {
|
||||||
acc.push( section );
|
acc.push( section );
|
||||||
@ -316,40 +201,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isLoggingRequired( getIssues( KEYWORD_ALL_SECTIONS ) ) ) {
|
|
||||||
// Enable logging of the PageIssues schema, setting up defaults.
|
|
||||||
pageIssuesLogger.subscribe(
|
|
||||||
newTreatmentEnabled,
|
|
||||||
pageIssuesLogger.newPageIssueSchemaData(
|
|
||||||
newTreatmentEnabled,
|
|
||||||
CURRENT_NS,
|
|
||||||
getIssues( KEYWORD_ALL_SECTIONS ).reduce( formatPageIssuesSeverity, [] ),
|
|
||||||
getAllIssuesSections( allIssues )
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Report that the page has been loaded.
|
|
||||||
pageIssuesLogger.log( {
|
|
||||||
action: 'pageLoaded'
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup the overlay route.
|
// Setup the overlay route.
|
||||||
overlayManager.add( new RegExp( '^/issues/(\\d+|' + KEYWORD_ALL_SECTIONS + ')$' ), function ( section ) {
|
overlayManager.add( new RegExp( '^/issues/(\\d+|' + KEYWORD_ALL_SECTIONS + ')$' ), function ( section ) {
|
||||||
return new PageIssuesOverlay(
|
return new PageIssuesOverlay(
|
||||||
getIssues( section ), pageIssuesLogger, section, CURRENT_NS );
|
getIssues( section ), section, CURRENT_NS );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
M.define( 'skins.minerva.scripts/pageIssues', {
|
M.define( 'skins.minerva.scripts/pageIssues', {
|
||||||
init: initPageIssues,
|
init: initPageIssues,
|
||||||
// The logger requires initialization (subscription). Ideally, the logger would be
|
|
||||||
// initialized and passed to initPageIssues() by the client. Since it's not, expose a log
|
|
||||||
// method and hide the subscription call in cleanuptemplates.
|
|
||||||
log: pageIssuesLogger.log,
|
|
||||||
test: {
|
test: {
|
||||||
formatPageIssuesSeverity: formatPageIssuesSeverity,
|
|
||||||
extractMessage: extractMessage,
|
|
||||||
getAllIssuesSections: getAllIssuesSections,
|
getAllIssuesSections: getAllIssuesSections,
|
||||||
createBanner: createBanner
|
createBanner: createBanner
|
||||||
}
|
}
|
||||||
|
@ -1,111 +0,0 @@
|
|||||||
( function ( M, mwConfig, mwTrack, mwTrackSubscribe, mwUser ) {
|
|
||||||
var
|
|
||||||
util = M.require( 'mobile.startup/util' ),
|
|
||||||
EVENT_PAGE_ISSUE_LOG = 'minerva.PageIssuesAB';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines default data for Schema:PageIssues that will be recorded with every event.
|
|
||||||
* @param {boolean} newTreatmentEnabled
|
|
||||||
* @param {number} namespaceId The namespace for the page that has issues.
|
|
||||||
* @param {string[]} pageIssueSeverities An array of PageIssue severities.
|
|
||||||
* @param {array} pageIssuesSections
|
|
||||||
*
|
|
||||||
* @return {Object} A Partial<Schema:PageIssues> Object meant to be mixed with track data.
|
|
||||||
*/
|
|
||||||
function newPageIssueSchemaData(
|
|
||||||
newTreatmentEnabled, namespaceId, pageIssueSeverities, pageIssuesSections
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
pageTitle: mwConfig.get( 'wgTitle' ),
|
|
||||||
namespaceId: namespaceId,
|
|
||||||
pageIdSource: mwConfig.get( 'wgArticleId' ),
|
|
||||||
issuesVersion: bucketToVersion( newTreatmentEnabled ),
|
|
||||||
issuesSeverity: pageIssueSeverities,
|
|
||||||
sectionNumbers: pageIssuesSections,
|
|
||||||
isAnon: mwUser.isAnon(),
|
|
||||||
editCountBucket: getUserEditBuckets(),
|
|
||||||
sessionToken: mwUser.sessionId()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable tracking and add page token to every logged event.
|
|
||||||
* @param {boolean} newTreatmentEnabled
|
|
||||||
* @param {Object} pageIssueSchemaData A Partial<Schema:PageIssues> Object that will be mixed
|
|
||||||
* with track data.
|
|
||||||
* @return {void}
|
|
||||||
*/
|
|
||||||
function subscribe( newTreatmentEnabled, pageIssueSchemaData ) {
|
|
||||||
// set the page token on the request.
|
|
||||||
pageIssueSchemaData.pageToken = mw.user.getPageviewToken();
|
|
||||||
|
|
||||||
// intermediary event bus that extends the event data before being passed to event-logging.
|
|
||||||
mwTrackSubscribe( EVENT_PAGE_ISSUE_LOG, function ( topic, data ) {
|
|
||||||
var mixedData = util.extend( {}, pageIssueSchemaData, data );
|
|
||||||
|
|
||||||
// Although we use strings inside the issues code (due to the usage of the key word
|
|
||||||
// `all`) - these need to be numbers to be validated by the schema
|
|
||||||
mixedData.sectionNumbers = ( mixedData.sectionNumbers || [] ).map(
|
|
||||||
function ( sectionStr ) { return parseInt( sectionStr, 10 ); }
|
|
||||||
);
|
|
||||||
|
|
||||||
// Log readingDepth schema.(ReadingDepth is guarded against multiple enables).
|
|
||||||
// See https://gerrit.wikimedia.org/r/#/c/mediawiki/extensions/WikimediaEvents/+/437686/
|
|
||||||
mwTrack( 'wikimedia.ReadingDepthSchema.enable', bucketToGroup( newTreatmentEnabled ) );
|
|
||||||
// Log PageIssues schema.
|
|
||||||
mwTrack( 'wikimedia.event.PageIssues', mixedData );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {boolean} newTreatmentEnabled
|
|
||||||
* @return {string} The page issues group associated with the treatment bucket.
|
|
||||||
*/
|
|
||||||
function bucketToGroup( newTreatmentEnabled ) {
|
|
||||||
return newTreatmentEnabled ? 'page-issues-b_sample' : 'page-issues-a_sample';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {boolean} newTreatmentEnabled
|
|
||||||
* @return {string} The page issues version associated with the treatment bucket.
|
|
||||||
*/
|
|
||||||
function bucketToVersion( newTreatmentEnabled ) {
|
|
||||||
return newTreatmentEnabled ? 'new2018' : 'old';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts user edit count into a predefined string. Note: these buckets have *nothing* to do
|
|
||||||
* with A/B bucketing.
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function getUserEditBuckets() {
|
|
||||||
var editCount = mwConfig.get( 'wgUserEditCount', 0 );
|
|
||||||
|
|
||||||
if ( editCount === 0 ) { return '0 edits'; }
|
|
||||||
if ( editCount < 5 ) { return '1-4 edits'; }
|
|
||||||
if ( editCount < 100 ) { return '5-99 edits'; }
|
|
||||||
if ( editCount < 1000 ) { return '100-999 edits'; }
|
|
||||||
if ( editCount >= 1000 ) { return '1000+ edits'; }
|
|
||||||
|
|
||||||
// This is unlikely to ever happen. If so, we'll want to cast to a string
|
|
||||||
// that is not accepted and allow EventLogging to complain
|
|
||||||
// about invalid events so we can investigate.
|
|
||||||
return 'error (' + editCount + ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log data to the PageIssuesAB test schema. It's safe to call this function prior to
|
|
||||||
* subscription.
|
|
||||||
* @param {Object} data to log
|
|
||||||
* @return {void}
|
|
||||||
*/
|
|
||||||
function log( data ) {
|
|
||||||
mwTrack( EVENT_PAGE_ISSUE_LOG, data );
|
|
||||||
}
|
|
||||||
|
|
||||||
M.define( 'skins.minerva.scripts/pageIssuesLogger', {
|
|
||||||
newPageIssueSchemaData: newPageIssueSchemaData,
|
|
||||||
subscribe: subscribe,
|
|
||||||
log: log
|
|
||||||
} );
|
|
||||||
}( mw.mobileFrontend, mw.config, mw.track, mw.trackSubscribe, mw.user ) );
|
|
@ -2,8 +2,16 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef PageIssue
|
* @typedef PageIssue
|
||||||
* @prop {string} severity A SEVERITY_LEVEL key.
|
* @prop {string} severity A SEVERITY_LEVEL key.
|
||||||
|
* @prop {boolean} grouped True if part of a group of multiple issues, false if singular.
|
||||||
* @prop {Icon} icon
|
* @prop {Icon} icon
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* @typedef {Object} IssueSummary
|
||||||
|
* @prop {PageIssue} issue
|
||||||
|
* @prop {string} iconString a string representation of icon.
|
||||||
|
* This is kept for template compatibility (our views do not yet support composition).
|
||||||
|
* @prop {string} text HTML string.
|
||||||
|
*/
|
||||||
|
|
||||||
var Icon = M.require( 'mobile.startup/Icon' ),
|
var Icon = M.require( 'mobile.startup/Icon' ),
|
||||||
// Icons are matching the type selector below use a TYPE_* icon. When unmatched, the icon is
|
// Icons are matching the type selector below use a TYPE_* icon. When unmatched, the icon is
|
||||||
@ -78,6 +86,7 @@
|
|||||||
].join( '|' ) )
|
].join( '|' ) )
|
||||||
// ..And everything else that doesn't match is mapped to a "SEVERITY" type.
|
// ..And everything else that doesn't match is mapped to a "SEVERITY" type.
|
||||||
},
|
},
|
||||||
|
GROUPED_PARENT_REGEX = /mw-collapsible-content/,
|
||||||
// Variants supported by specific types. The "severity icon" supports all severities but the
|
// Variants supported by specific types. The "severity icon" supports all severities but the
|
||||||
// type icons only support one each by ResourceLoader.
|
// type icons only support one each by ResourceLoader.
|
||||||
TYPE_SEVERITY = {
|
TYPE_SEVERITY = {
|
||||||
@ -117,6 +126,14 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Element} box
|
||||||
|
* @return {boolean} True if part of a group of multiple issues, false if singular.
|
||||||
|
*/
|
||||||
|
function parseGroup( box ) {
|
||||||
|
return !!box.parentNode && GROUPED_PARENT_REGEX.test( box.parentNode.className );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Element} box
|
* @param {Element} box
|
||||||
* @param {string} severity An SEVERITY_LEVEL key.
|
* @param {string} severity An SEVERITY_LEVEL key.
|
||||||
@ -147,6 +164,7 @@
|
|||||||
var severity = parseSeverity( box );
|
var severity = parseSeverity( box );
|
||||||
return {
|
return {
|
||||||
severity: severity,
|
severity: severity,
|
||||||
|
grouped: parseGroup( box ),
|
||||||
icon: new Icon( {
|
icon: new Icon( {
|
||||||
glyphPrefix: 'minerva',
|
glyphPrefix: 'minerva',
|
||||||
name: iconName( box, severity )
|
name: iconName( box, severity )
|
||||||
@ -154,21 +172,51 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a summary message from a cleanup template generated element that is
|
||||||
|
* friendly for mobile display.
|
||||||
|
* @param {Object} $box element to extract the message from
|
||||||
|
* @return {IssueSummary}
|
||||||
|
*/
|
||||||
|
function extract( $box ) {
|
||||||
|
var SELECTOR = '.mbox-text, .ambox-text',
|
||||||
|
$container = $( '<div>' ),
|
||||||
|
pageIssue;
|
||||||
|
|
||||||
|
$box.find( SELECTOR ).each( function () {
|
||||||
|
var contents,
|
||||||
|
$this = $( this );
|
||||||
|
// Clean up talk page boxes
|
||||||
|
$this.find( 'table, .noprint' ).remove();
|
||||||
|
contents = $this.html();
|
||||||
|
|
||||||
|
if ( contents ) {
|
||||||
|
$( '<p>' ).html( contents ).appendTo( $container );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
pageIssue = parse( $box.get( 0 ) );
|
||||||
|
|
||||||
|
return {
|
||||||
|
issue: pageIssue,
|
||||||
|
// For template compatibility with PageIssuesOverlay
|
||||||
|
iconString: pageIssue.icon.toHtmlString(),
|
||||||
|
text: $container.html()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module skins.minerva.scripts/utils
|
* @module skins.minerva.scripts/utils
|
||||||
*/
|
*/
|
||||||
M.define( 'skins.minerva.scripts/pageIssuesParser', {
|
M.define( 'skins.minerva.scripts/pageIssuesParser', {
|
||||||
/**
|
extract: extract,
|
||||||
* Extract an icon for use with the issue.
|
|
||||||
* @param {JQuery.Object} $box element to extract the icon from
|
|
||||||
* @return {Icon} representing the icon
|
|
||||||
*/
|
|
||||||
parse: parse,
|
parse: parse,
|
||||||
maxSeverity: maxSeverity,
|
maxSeverity: maxSeverity,
|
||||||
iconName: iconName,
|
iconName: iconName,
|
||||||
test: {
|
test: {
|
||||||
parseSeverity: parseSeverity,
|
parseSeverity: parseSeverity,
|
||||||
parseType: parseType
|
parseType: parseType,
|
||||||
|
parseGroup: parseGroup
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
@ -155,7 +155,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EventLoggingSchemas": {
|
"EventLoggingSchemas": {
|
||||||
"PageIssues": 18392542,
|
|
||||||
"WebClientError": 18340282
|
"WebClientError": 18340282
|
||||||
},
|
},
|
||||||
"ResourceModules": {
|
"ResourceModules": {
|
||||||
@ -435,7 +434,6 @@
|
|||||||
"resources/skins.minerva.scripts/errorLogging.js",
|
"resources/skins.minerva.scripts/errorLogging.js",
|
||||||
"resources/skins.minerva.scripts/preInit.js",
|
"resources/skins.minerva.scripts/preInit.js",
|
||||||
"resources/skins.minerva.scripts/DownloadIcon.js",
|
"resources/skins.minerva.scripts/DownloadIcon.js",
|
||||||
"resources/skins.minerva.scripts/pageIssuesLogger.js",
|
|
||||||
"resources/skins.minerva.scripts/pageIssuesParser.js",
|
"resources/skins.minerva.scripts/pageIssuesParser.js",
|
||||||
"resources/skins.minerva.scripts/AB.js",
|
"resources/skins.minerva.scripts/AB.js",
|
||||||
"resources/skins.minerva.scripts/PageIssuesOverlay.js",
|
"resources/skins.minerva.scripts/PageIssuesOverlay.js",
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
( function ( M ) {
|
|
||||||
var PageIssuesOverlay = M.require( 'skins.minerva.scripts/PageIssuesOverlay' );
|
|
||||||
|
|
||||||
QUnit.module( 'Minerva PageIssuesOverlay', {
|
|
||||||
beforeEach: function () {
|
|
||||||
this.logger = {
|
|
||||||
log: this.sandbox.spy()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
QUnit.test( '#log (section=all)', function ( assert ) {
|
|
||||||
var overlay = new PageIssuesOverlay( [], this.logger, 'all', 0 );
|
|
||||||
overlay.onExit();
|
|
||||||
assert.strictEqual( this.logger.log.calledOnce, true, 'Logger called once' );
|
|
||||||
assert.strictEqual(
|
|
||||||
this.logger.log.calledWith( {
|
|
||||||
action: 'modalClose',
|
|
||||||
issuesSeverity: []
|
|
||||||
} ), true, 'sectionNumbers is not set (T202940)'
|
|
||||||
);
|
|
||||||
} );
|
|
||||||
|
|
||||||
QUnit.test( '#log (section=1)', function ( assert ) {
|
|
||||||
var overlay = new PageIssuesOverlay( [
|
|
||||||
{
|
|
||||||
severity: 'MEDIUM'
|
|
||||||
}
|
|
||||||
], this.logger, '1', 0 );
|
|
||||||
overlay.onExit();
|
|
||||||
assert.strictEqual(
|
|
||||||
this.logger.log.calledWith( {
|
|
||||||
action: 'modalClose',
|
|
||||||
issuesSeverity: [ 'MEDIUM' ],
|
|
||||||
sectionNumbers: [ '1' ]
|
|
||||||
} ), true, 'sectionNumbers is set'
|
|
||||||
);
|
|
||||||
} );
|
|
||||||
|
|
||||||
QUnit.test( '#log (section=2) multiple issues', function ( assert ) {
|
|
||||||
var overlay = new PageIssuesOverlay(
|
|
||||||
[ {
|
|
||||||
severity: 'MEDIUM'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
severity: 'LOW'
|
|
||||||
} ], this.logger, '2', 0 );
|
|
||||||
overlay.onExit();
|
|
||||||
assert.strictEqual(
|
|
||||||
this.logger.log.calledWith( {
|
|
||||||
action: 'modalClose',
|
|
||||||
issuesSeverity: [ 'MEDIUM', 'LOW' ],
|
|
||||||
sectionNumbers: [ '2', '2' ]
|
|
||||||
} ), true, 'sectionNumbers is set for each issue'
|
|
||||||
);
|
|
||||||
} );
|
|
||||||
}( mw.mobileFrontend ) );
|
|
@ -1,35 +1,30 @@
|
|||||||
( function ( M ) {
|
( function ( M ) {
|
||||||
var pageIssues = M.require( 'skins.minerva.scripts/pageIssues' ),
|
var pageIssues = M.require( 'skins.minerva.scripts/pageIssues' ),
|
||||||
util = M.require( 'mobile.startup/util' ),
|
util = M.require( 'mobile.startup/util' ),
|
||||||
pageIssuesParser = M.require( 'skins.minerva.scripts/pageIssuesParser' ),
|
|
||||||
extractMessage = pageIssues.test.extractMessage,
|
|
||||||
createBanner = pageIssues.test.createBanner,
|
createBanner = pageIssues.test.createBanner,
|
||||||
formatPageIssuesSeverity = pageIssues.test.formatPageIssuesSeverity,
|
icon = {},
|
||||||
MEDIUM_ISSUE = {
|
MEDIUM_ISSUE = {
|
||||||
severity: 'MEDIUM',
|
issue: {
|
||||||
icon: 'i',
|
severity: 'MEDIUM',
|
||||||
text: 't'
|
icon: icon
|
||||||
},
|
},
|
||||||
MEDIUM_MULTIPLE_ISSUE = {
|
iconString: 'i',
|
||||||
severity: 'MEDIUM',
|
|
||||||
isMultiple: true,
|
|
||||||
icon: 'i',
|
|
||||||
text: 't'
|
|
||||||
},
|
|
||||||
LOW_MULTIPLE_ISSUE = {
|
|
||||||
severity: 'LOW',
|
|
||||||
isMultiple: true,
|
|
||||||
icon: 'i',
|
|
||||||
text: 't'
|
text: 't'
|
||||||
},
|
},
|
||||||
LOW_ISSUE = {
|
LOW_ISSUE = {
|
||||||
severity: 'LOW',
|
issue: {
|
||||||
icon: 'i',
|
severity: 'LOW',
|
||||||
|
icon: icon
|
||||||
|
},
|
||||||
|
iconString: 'i',
|
||||||
text: 't'
|
text: 't'
|
||||||
},
|
},
|
||||||
HIGH_ISSUE = {
|
HIGH_ISSUE = {
|
||||||
severity: 'HIGH',
|
issue: {
|
||||||
icon: 'i',
|
severity: 'HIGH',
|
||||||
|
icon: icon
|
||||||
|
},
|
||||||
|
iconString: 'i',
|
||||||
text: 't'
|
text: 't'
|
||||||
},
|
},
|
||||||
getAllIssuesSections = pageIssues.test.getAllIssuesSections,
|
getAllIssuesSections = pageIssues.test.getAllIssuesSections,
|
||||||
@ -67,93 +62,6 @@
|
|||||||
assert.strictEqual( window.location.hash, '#/issues/' + SECTION );
|
assert.strictEqual( window.location.hash, '#/issues/' + SECTION );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// NOTE: Only for PageIssues AB
|
|
||||||
QUnit.test( 'clicking on the product of createBanner() should trigger a custom event', function ( assert ) {
|
|
||||||
var mockAction = {
|
|
||||||
action: 'issueClicked',
|
|
||||||
issuesSeverity: [ 'MEDIUM' ],
|
|
||||||
sectionNumbers: [ SECTION ]
|
|
||||||
};
|
|
||||||
mw.trackSubscribe( 'minerva.PageIssuesAB', function ( topic, data ) {
|
|
||||||
assert.deepEqual( mockAction, data );
|
|
||||||
} );
|
|
||||||
processedAmbox.click();
|
|
||||||
} );
|
|
||||||
|
|
||||||
QUnit.test( 'formatPageIssuesSeverity', function ( assert ) {
|
|
||||||
var multipleIssues = [
|
|
||||||
MEDIUM_MULTIPLE_ISSUE,
|
|
||||||
LOW_MULTIPLE_ISSUE,
|
|
||||||
LOW_MULTIPLE_ISSUE
|
|
||||||
],
|
|
||||||
multipleSingleIssues = [
|
|
||||||
LOW_ISSUE,
|
|
||||||
HIGH_ISSUE,
|
|
||||||
MEDIUM_ISSUE
|
|
||||||
],
|
|
||||||
mixedMultipleSingle = [
|
|
||||||
HIGH_ISSUE,
|
|
||||||
LOW_MULTIPLE_ISSUE,
|
|
||||||
MEDIUM_MULTIPLE_ISSUE,
|
|
||||||
LOW_ISSUE,
|
|
||||||
MEDIUM_ISSUE,
|
|
||||||
HIGH_ISSUE
|
|
||||||
],
|
|
||||||
testMultiple = multipleIssues.reduce( formatPageIssuesSeverity, [] ),
|
|
||||||
testSingle = multipleSingleIssues.reduce( formatPageIssuesSeverity, [] ),
|
|
||||||
testMixed = mixedMultipleSingle.reduce( formatPageIssuesSeverity, [] );
|
|
||||||
|
|
||||||
assert.deepEqual( testMultiple, [ 'MEDIUM' ], 'Multiple issues return one maxSeverity value' );
|
|
||||||
assert.deepEqual( testSingle, [ 'LOW', 'HIGH', 'MEDIUM' ], 'Single issues return each corresponding severity' );
|
|
||||||
assert.deepEqual( testMixed, [ 'HIGH', 'MEDIUM', 'LOW', 'MEDIUM', 'HIGH' ], 'Mixed single/multiple return one value for multiples' );
|
|
||||||
|
|
||||||
} );
|
|
||||||
|
|
||||||
QUnit.test( 'extractMessage', function ( assert ) {
|
|
||||||
this.sandbox.stub( pageIssuesParser, 'parse' ).returns(
|
|
||||||
{
|
|
||||||
severity: 'LOW',
|
|
||||||
icon: {
|
|
||||||
toHtmlString: function () {
|
|
||||||
return '<icon />';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
[
|
|
||||||
[
|
|
||||||
$( '<div />' ).html(
|
|
||||||
'<div class="mbox-text">Smelly</div>'
|
|
||||||
).appendTo( '<div class="mw-collapsible-content" />' ),
|
|
||||||
{
|
|
||||||
severity: 'LOW',
|
|
||||||
isMultiple: true,
|
|
||||||
icon: '<icon />',
|
|
||||||
text: '<p>Smelly</p>'
|
|
||||||
},
|
|
||||||
'When the box is a child of mw-collapsible-content it isMultiple'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
$( '<div />' ).html(
|
|
||||||
'<div class="mbox-text">Dirty</div>'
|
|
||||||
),
|
|
||||||
{
|
|
||||||
severity: 'LOW',
|
|
||||||
isMultiple: false,
|
|
||||||
icon: '<icon />',
|
|
||||||
text: '<p>Dirty</p>'
|
|
||||||
},
|
|
||||||
'When the box is not child of mw-collapsible-content it !isMultiple'
|
|
||||||
]
|
|
||||||
].forEach( function ( test ) {
|
|
||||||
assert.deepEqual(
|
|
||||||
extractMessage( test[ 0 ] ),
|
|
||||||
test[ 1 ],
|
|
||||||
test[ 2 ]
|
|
||||||
);
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
|
|
||||||
QUnit.test( 'getAllIssuesSections', function ( assert ) {
|
QUnit.test( 'getAllIssuesSections', function ( assert ) {
|
||||||
var multipleIssuesWithDeletion,
|
var multipleIssuesWithDeletion,
|
||||||
multipleIssues, allIssuesOldTreatment, allIssuesNewTreatment;
|
multipleIssues, allIssuesOldTreatment, allIssuesNewTreatment;
|
||||||
@ -166,17 +74,17 @@
|
|||||||
};
|
};
|
||||||
multipleIssues = {
|
multipleIssues = {
|
||||||
0: [
|
0: [
|
||||||
util.extend( {}, MEDIUM_ISSUE, { isMultiple: true } ),
|
util.extend( {}, MEDIUM_ISSUE, { grouped: true } ),
|
||||||
util.extend( {}, LOW_ISSUE, { isMultiple: true } ),
|
util.extend( {}, LOW_ISSUE, { grouped: true } ),
|
||||||
util.extend( {}, MEDIUM_ISSUE, { isMultiple: true } )
|
util.extend( {}, MEDIUM_ISSUE, { grouped: true } )
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
multipleIssuesWithDeletion = {
|
multipleIssuesWithDeletion = {
|
||||||
0: [
|
0: [
|
||||||
HIGH_ISSUE,
|
HIGH_ISSUE,
|
||||||
util.extend( {}, MEDIUM_ISSUE, { isMultiple: true } ),
|
util.extend( {}, MEDIUM_ISSUE, { grouped: true } ),
|
||||||
util.extend( {}, LOW_ISSUE, { isMultiple: true } ),
|
util.extend( {}, LOW_ISSUE, { grouped: true } ),
|
||||||
util.extend( {}, MEDIUM_ISSUE, { isMultiple: true } )
|
util.extend( {}, MEDIUM_ISSUE, { grouped: true } )
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
allIssuesNewTreatment = {
|
allIssuesNewTreatment = {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
( function ( M ) {
|
( function ( M ) {
|
||||||
var pageIssuesParser = M.require( 'skins.minerva.scripts/pageIssuesParser' );
|
var icon = {},
|
||||||
|
pageIssuesParser = M.require( 'skins.minerva.scripts/pageIssuesParser' ),
|
||||||
|
extractMessage = pageIssuesParser.extract;
|
||||||
|
|
||||||
QUnit.module( 'Minerva pageIssuesParser' );
|
QUnit.module( 'Minerva pageIssuesParser' );
|
||||||
|
|
||||||
@ -13,6 +15,47 @@
|
|||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUnit.test( 'extractMessage', function () {
|
||||||
|
[
|
||||||
|
[
|
||||||
|
$( '<div />' ).html(
|
||||||
|
'<div class="mbox-text">Smelly</div>'
|
||||||
|
).appendTo( '<div class="mw-collapsible-content" />' ),
|
||||||
|
{
|
||||||
|
issue: {
|
||||||
|
severity: 'DEFAULT',
|
||||||
|
grouped: true,
|
||||||
|
icon: icon
|
||||||
|
},
|
||||||
|
iconString: this.sandbox.match.typeOf( 'string' ),
|
||||||
|
text: '<p>Smelly</p>'
|
||||||
|
},
|
||||||
|
'When the box is a child of mw-collapsible-content it grouped'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
$( '<div />' ).html(
|
||||||
|
'<div class="mbox-text">Dirty</div>'
|
||||||
|
),
|
||||||
|
{
|
||||||
|
issue: {
|
||||||
|
severity: 'DEFAULT',
|
||||||
|
grouped: false,
|
||||||
|
icon: icon
|
||||||
|
},
|
||||||
|
iconString: this.sandbox.match.typeOf( 'string' ),
|
||||||
|
text: '<p>Dirty</p>'
|
||||||
|
},
|
||||||
|
'When the box is not child of mw-collapsible-content it !grouped'
|
||||||
|
]
|
||||||
|
].forEach( function ( test ) {
|
||||||
|
sinon.assert.match( // eslint-disable-line no-undef
|
||||||
|
extractMessage( test[ 0 ] ),
|
||||||
|
test[ 1 ],
|
||||||
|
test[ 2 ]
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
QUnit.test( 'parseSeverity', function ( assert ) {
|
QUnit.test( 'parseSeverity', function ( assert ) {
|
||||||
var tests = [
|
var tests = [
|
||||||
[ '', 'DEFAULT', 'empty' ],
|
[ '', 'DEFAULT', 'empty' ],
|
||||||
@ -70,6 +113,32 @@
|
|||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
QUnit.test( 'parseGroup', function ( assert ) {
|
||||||
|
var tests = [
|
||||||
|
[ undefined, false, 'orphaned' ],
|
||||||
|
[ '', false, 'ungrouped' ],
|
||||||
|
[ 'mw-collapsible-content', true, 'grouped' ]
|
||||||
|
];
|
||||||
|
tests.forEach( function ( params, i ) {
|
||||||
|
var
|
||||||
|
parentClassName = params[0],
|
||||||
|
expect = params[1],
|
||||||
|
test = params[2],
|
||||||
|
parent,
|
||||||
|
box = newBox( '' );
|
||||||
|
if ( parentClassName !== undefined ) {
|
||||||
|
parent = document.createElement( 'div' );
|
||||||
|
parent.className = parentClassName;
|
||||||
|
parent.appendChild( box );
|
||||||
|
}
|
||||||
|
assert.strictEqual(
|
||||||
|
pageIssuesParser.test.parseGroup( box ),
|
||||||
|
expect,
|
||||||
|
'Result should be the correct grouping; case ' + i + ': ' + test + '.'
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
QUnit.test( 'iconName', function ( assert ) {
|
QUnit.test( 'iconName', function ( assert ) {
|
||||||
var tests = [
|
var tests = [
|
||||||
[ '', 'DEFAULT', 'issue-generic-defaultColor' ],
|
[ '', 'DEFAULT', 'issue-generic-defaultColor' ],
|
||||||
|
Loading…
Reference in New Issue
Block a user