From f07985c6dee5106da8f381a47214e7349fcd147e Mon Sep 17 00:00:00 2001 From: Stephen Niedzielski Date: Thu, 6 Sep 2018 12:39:58 -0600 Subject: [PATCH 1/4] Hygiene: move page issue group parsing to parser Separate the page issue grouping concern so that changes to parsing don't concern everything else and vice-versa. Bug: T203449, T202349 Change-Id: I7bddb0c53310805ece71b8f7821b1d6ce05cfae9 --- resources/skins.minerva.scripts/pageIssues.js | 3 +-- .../skins.minerva.scripts/pageIssuesParser.js | 14 +++++++++- .../skins.minerva.scripts/pageIssues.test.js | 23 +++++----------- .../pageIssuesParser.test.js | 26 +++++++++++++++++++ 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/resources/skins.minerva.scripts/pageIssues.js b/resources/skins.minerva.scripts/pageIssues.js index 541da68..f57915c 100644 --- a/resources/skins.minerva.scripts/pageIssues.js +++ b/resources/skins.minerva.scripts/pageIssues.js @@ -75,7 +75,6 @@ */ function extractMessage( $box ) { var SELECTOR = '.mbox-text, .ambox-text', - MULTIPLE_SELECTOR = '.mw-collapsible-content', $container = $( '
' ), pageIssue; @@ -95,7 +94,7 @@ return { severity: pageIssue.severity, - isMultiple: $box.parent().is( MULTIPLE_SELECTOR ), + isMultiple: pageIssue.grouped, icon: pageIssue.icon.toHtmlString(), text: $container.html() }; diff --git a/resources/skins.minerva.scripts/pageIssuesParser.js b/resources/skins.minerva.scripts/pageIssuesParser.js index ce27d03..870c08d 100644 --- a/resources/skins.minerva.scripts/pageIssuesParser.js +++ b/resources/skins.minerva.scripts/pageIssuesParser.js @@ -2,6 +2,7 @@ /** * @typedef PageIssue * @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 */ @@ -78,6 +79,7 @@ ].join( '|' ) ) // ..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 // type icons only support one each by ResourceLoader. TYPE_SEVERITY = { @@ -117,6 +119,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 {string} severity An SEVERITY_LEVEL key. @@ -147,6 +157,7 @@ var severity = parseSeverity( box ); return { severity: severity, + grouped: parseGroup( box ), icon: new Icon( { glyphPrefix: 'minerva', name: iconName( box, severity ) @@ -168,7 +179,8 @@ iconName: iconName, test: { parseSeverity: parseSeverity, - parseType: parseType + parseType: parseType, + parseGroup: parseGroup } } ); diff --git a/tests/qunit/skins.minerva.scripts/pageIssues.test.js b/tests/qunit/skins.minerva.scripts/pageIssues.test.js index d6d03b5..51907c9 100644 --- a/tests/qunit/skins.minerva.scripts/pageIssues.test.js +++ b/tests/qunit/skins.minerva.scripts/pageIssues.test.js @@ -1,7 +1,6 @@ ( function ( M ) { var pageIssues = M.require( 'skins.minerva.scripts/pageIssues' ), util = M.require( 'mobile.startup/util' ), - pageIssuesParser = M.require( 'skins.minerva.scripts/pageIssuesParser' ), extractMessage = pageIssues.test.extractMessage, createBanner = pageIssues.test.createBanner, formatPageIssuesSeverity = pageIssues.test.formatPageIssuesSeverity, @@ -109,26 +108,16 @@ } ); - QUnit.test( 'extractMessage', function ( assert ) { - this.sandbox.stub( pageIssuesParser, 'parse' ).returns( - { - severity: 'LOW', - icon: { - toHtmlString: function () { - return ''; - } - } - } - ); + QUnit.test( 'extractMessage', function () { [ [ $( '
' ).html( '
Smelly
' ).appendTo( '
' ), { - severity: 'LOW', + severity: 'DEFAULT', isMultiple: true, - icon: '', + icon: this.sandbox.match.typeOf( 'string' ), text: '

Smelly

' }, 'When the box is a child of mw-collapsible-content it isMultiple' @@ -138,15 +127,15 @@ '
Dirty
' ), { - severity: 'LOW', + severity: 'DEFAULT', isMultiple: false, - icon: '', + icon: this.sandbox.match.typeOf( 'string' ), text: '

Dirty

' }, 'When the box is not child of mw-collapsible-content it !isMultiple' ] ].forEach( function ( test ) { - assert.deepEqual( + sinon.assert.match( // eslint-disable-line no-undef extractMessage( test[ 0 ] ), test[ 1 ], test[ 2 ] diff --git a/tests/qunit/skins.minerva.scripts/pageIssuesParser.test.js b/tests/qunit/skins.minerva.scripts/pageIssuesParser.test.js index 0b020f0..85ca9f6 100644 --- a/tests/qunit/skins.minerva.scripts/pageIssuesParser.test.js +++ b/tests/qunit/skins.minerva.scripts/pageIssuesParser.test.js @@ -70,6 +70,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 ) { var tests = [ [ '', 'DEFAULT', 'issue-generic-defaultColor' ], From 878989bd856169c038cfa2e5e9cfa52847a4f084 Mon Sep 17 00:00:00 2001 From: jdlrobson Date: Tue, 16 Oct 2018 15:46:28 -0700 Subject: [PATCH 2/4] Merge PageIssue and IssueSummary type defs We have two type defs - IssueSummary and PageIssue. I'd like to consolidate these two types by making IssueSummary a combination of the two Change-Id: Ic831b463fa66b0cacdd0b9b79aff741e55c0ec24 --- .../PageIssuesOverlayContent.hogan | 2 +- resources/skins.minerva.scripts/pageIssues.js | 39 +++++----- .../skins.minerva.scripts/pageIssues.test.js | 74 ++++++++++++------- 3 files changed, 66 insertions(+), 49 deletions(-) diff --git a/resources/skins.minerva.scripts/PageIssuesOverlayContent.hogan b/resources/skins.minerva.scripts/PageIssuesOverlayContent.hogan index 67f2a01..a9700da 100644 --- a/resources/skins.minerva.scripts/PageIssuesOverlayContent.hogan +++ b/resources/skins.minerva.scripts/PageIssuesOverlayContent.hogan @@ -2,7 +2,7 @@ {{#issues}}
  • - {{{icon}}} + {{{iconString}}}
    {{{text}}}
  • diff --git a/resources/skins.minerva.scripts/pageIssues.js b/resources/skins.minerva.scripts/pageIssues.js index f57915c..284db03 100644 --- a/resources/skins.minerva.scripts/pageIssues.js +++ b/resources/skins.minerva.scripts/pageIssues.js @@ -9,7 +9,6 @@ NS_TALK = 1, NS_CATEGORY = 14, 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' ), PageIssuesOverlay = M.require( 'skins.minerva.scripts/PageIssuesOverlay' ), @@ -28,9 +27,9 @@ /** * @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 {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. */ @@ -53,16 +52,17 @@ */ function formatPageIssuesSeverity( formattedArr, currentItem, currentIndex, pageIssues ) { var lastItem = pageIssues[ currentIndex - 1 ], + issue = currentItem.issue, lastFormattedIndex = formattedArr.length - 1, lastFormattedValue = formattedArr[ lastFormattedIndex ]; - // If the last and current item `isMultiple`, fold the maxSeverity + // If the last and current item `grouped`, fold the maxSeverity // of the two items into a single value. - if ( lastItem && lastItem.isMultiple && currentItem.isMultiple ) { + if ( lastItem && lastItem.issue && lastItem.issue.grouped && issue.grouped ) { formattedArr[ lastFormattedIndex ] = pageIssuesParser.maxSeverity( - [ lastFormattedValue, currentItem.severity ] + [ lastFormattedValue, issue.severity ] ); } else { - formattedArr.push( currentItem.severity ); + formattedArr.push( issue.severity ); } return formattedArr; } @@ -93,9 +93,9 @@ pageIssue = pageIssuesParser.parse( $box.get( 0 ) ); return { - severity: pageIssue.severity, - isMultiple: pageIssue.grouped, - icon: pageIssue.icon.toHtmlString(), + issue: pageIssue, + // For template compatibility with PageIssuesOverlay + iconString: pageIssue.icon.toHtmlString(), text: $container.html() }; } @@ -137,8 +137,7 @@ issueUrl = section === KEYWORD_ALL_SECTIONS ? '#/issues/' + KEYWORD_ALL_SECTIONS : '#/issues/' + section, selector = 'table.ambox, table.tmbox, table.cmbox, table.fmbox', issues = [], - $link, - severity; + $link; if ( section === KEYWORD_ALL_SECTIONS ) { $metadata = page.$( selector ); @@ -165,14 +164,10 @@ // store it for later allIssues[section] = issues; - if ( $metadata.length && inline ) { - severity = pageIssuesParser.maxSeverity( - issues.map( function ( issue ) { return issue.severity; } ) - ); - new Icon( { - glyphPrefix: 'minerva', - name: pageIssuesParser.iconName( $metadata.get( 0 ), severity ) - } ).prependTo( $metadata.find( '.mbox-text' ) ); + // If issues were extracted and there are inline amboxes, add learn more + // and icon to the UI element. + if ( issues.length && $metadata.length && inline ) { + issues[0].issue.icon.$el.prependTo( $metadata.eq( 0 ).find( '.mbox-text' ) ); $learnMore = $( '' ) .addClass( 'ambox-learn-more' ) .text( mw.msg( 'skin-minerva-issue-learn-more' ) ); @@ -256,7 +251,7 @@ var lastIssue = allIssues[ section ][i - 1]; // If the last issue belongs to a "Multiple issues" template, // 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; } else { acc.push( section ); diff --git a/tests/qunit/skins.minerva.scripts/pageIssues.test.js b/tests/qunit/skins.minerva.scripts/pageIssues.test.js index 51907c9..0a3b38d 100644 --- a/tests/qunit/skins.minerva.scripts/pageIssues.test.js +++ b/tests/qunit/skins.minerva.scripts/pageIssues.test.js @@ -3,32 +3,48 @@ util = M.require( 'mobile.startup/util' ), extractMessage = pageIssues.test.extractMessage, createBanner = pageIssues.test.createBanner, + icon = {}, formatPageIssuesSeverity = pageIssues.test.formatPageIssuesSeverity, MEDIUM_ISSUE = { - severity: 'MEDIUM', - icon: 'i', + issue: { + severity: 'MEDIUM', + icon: icon + }, + iconString: 'i', text: 't' }, MEDIUM_MULTIPLE_ISSUE = { - severity: 'MEDIUM', - isMultiple: true, - icon: 'i', + issue: { + severity: 'MEDIUM', + grouped: true, + icon: icon + }, + iconString: 'i', text: 't' }, LOW_MULTIPLE_ISSUE = { - severity: 'LOW', - isMultiple: true, - icon: 'i', + issue: { + severity: 'LOW', + grouped: true, + icon: icon + }, + iconString: 'i', text: 't' }, LOW_ISSUE = { - severity: 'LOW', - icon: 'i', + issue: { + severity: 'LOW', + icon: icon + }, + iconString: 'i', text: 't' }, HIGH_ISSUE = { - severity: 'HIGH', - icon: 'i', + issue: { + severity: 'HIGH', + icon: icon + }, + iconString: 'i', text: 't' }, getAllIssuesSections = pageIssues.test.getAllIssuesSections, @@ -115,24 +131,30 @@ '
    Smelly
    ' ).appendTo( '
    ' ), { - severity: 'DEFAULT', - isMultiple: true, - icon: this.sandbox.match.typeOf( 'string' ), + issue: { + severity: 'DEFAULT', + grouped: true, + icon: icon + }, + iconString: this.sandbox.match.typeOf( 'string' ), text: '

    Smelly

    ' }, - 'When the box is a child of mw-collapsible-content it isMultiple' + 'When the box is a child of mw-collapsible-content it grouped' ], [ $( '
    ' ).html( '
    Dirty
    ' ), { - severity: 'DEFAULT', - isMultiple: false, - icon: this.sandbox.match.typeOf( 'string' ), + issue: { + severity: 'DEFAULT', + grouped: false, + icon: icon + }, + iconString: this.sandbox.match.typeOf( 'string' ), text: '

    Dirty

    ' }, - 'When the box is not child of mw-collapsible-content it !isMultiple' + 'When the box is not child of mw-collapsible-content it !grouped' ] ].forEach( function ( test ) { sinon.assert.match( // eslint-disable-line no-undef @@ -155,17 +177,17 @@ }; multipleIssues = { 0: [ - util.extend( {}, MEDIUM_ISSUE, { isMultiple: true } ), - util.extend( {}, LOW_ISSUE, { isMultiple: true } ), - util.extend( {}, MEDIUM_ISSUE, { isMultiple: true } ) + util.extend( {}, MEDIUM_ISSUE, { grouped: true } ), + util.extend( {}, LOW_ISSUE, { grouped: true } ), + util.extend( {}, MEDIUM_ISSUE, { grouped: true } ) ] }; multipleIssuesWithDeletion = { 0: [ HIGH_ISSUE, - util.extend( {}, MEDIUM_ISSUE, { isMultiple: true } ), - util.extend( {}, LOW_ISSUE, { isMultiple: true } ), - util.extend( {}, MEDIUM_ISSUE, { isMultiple: true } ) + util.extend( {}, MEDIUM_ISSUE, { grouped: true } ), + util.extend( {}, LOW_ISSUE, { grouped: true } ), + util.extend( {}, MEDIUM_ISSUE, { grouped: true } ) ] }; allIssuesNewTreatment = { From cb2ad9374efcb95d0085bfbb8a75f9f59146f79d Mon Sep 17 00:00:00 2001 From: jdlrobson Date: Tue, 16 Oct 2018 16:02:22 -0700 Subject: [PATCH 3/4] extract moved to parser The extractMessage function has a lot to do with parsing - so this and its tests are moved into the pageIssuesParser. Change-Id: I62d79fbba166eff2c3ca573ef94ff86a269a7f9a --- resources/skins.minerva.scripts/pageIssues.js | 46 +------------------ .../skins.minerva.scripts/pageIssuesParser.js | 46 +++++++++++++++++-- .../skins.minerva.scripts/pageIssues.test.js | 42 ----------------- .../pageIssuesParser.test.js | 45 +++++++++++++++++- 4 files changed, 87 insertions(+), 92 deletions(-) diff --git a/resources/skins.minerva.scripts/pageIssues.js b/resources/skins.minerva.scripts/pageIssues.js index 284db03..23f7749 100644 --- a/resources/skins.minerva.scripts/pageIssues.js +++ b/resources/skins.minerva.scripts/pageIssues.js @@ -25,14 +25,6 @@ isUserRequestingNewTreatment = QUERY_STRING_FLAG === 'b', newTreatmentEnabled = abTest.isB() || isUserRequestingNewTreatment; - /** - * @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. - */ - function isLoggingRequired( pageIssues ) { // No logging necessary when the A/B test is disabled (control group). return !isUserRequestingNewTreatment && abTest.isEnabled() && pageIssues.length; @@ -67,39 +59,6 @@ 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', - $container = $( '
    ' ), - 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 ) { - $( '

    ' ).html( contents ).appendTo( $container ); - } - } ); - - pageIssue = pageIssuesParser.parse( $box.get( 0 ) ); - - return { - issue: pageIssue, - // For template compatibility with PageIssuesOverlay - iconString: pageIssue.icon.toHtmlString(), - text: $container.html() - }; - } - /** * Create a link element that opens the issues overlay. * @@ -152,8 +111,8 @@ $this = $( this ); if ( $this.find( selector ).length === 0 ) { - issue = extractMessage( $this ); - // Some issues after "extractMessage" has been run will have no text. + issue = pageIssuesParser.extract( $this ); + // 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. // These should not be rendered. if ( issue.text ) { @@ -343,7 +302,6 @@ log: pageIssuesLogger.log, test: { formatPageIssuesSeverity: formatPageIssuesSeverity, - extractMessage: extractMessage, getAllIssuesSections: getAllIssuesSections, createBanner: createBanner } diff --git a/resources/skins.minerva.scripts/pageIssuesParser.js b/resources/skins.minerva.scripts/pageIssuesParser.js index 870c08d..cbf85bc 100644 --- a/resources/skins.minerva.scripts/pageIssuesParser.js +++ b/resources/skins.minerva.scripts/pageIssuesParser.js @@ -5,6 +5,13 @@ * @prop {boolean} grouped True if part of a group of multiple issues, false if singular. * @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' ), // Icons are matching the type selector below use a TYPE_* icon. When unmatched, the icon is @@ -165,15 +172,44 @@ }; } + /** + * 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 = $( '

    ' ), + 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 ) { + $( '

    ' ).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 */ M.define( 'skins.minerva.scripts/pageIssuesParser', { - /** - * Extract an icon for use with the issue. - * @param {JQuery.Object} $box element to extract the icon from - * @return {Icon} representing the icon - */ + extract: extract, parse: parse, maxSeverity: maxSeverity, iconName: iconName, diff --git a/tests/qunit/skins.minerva.scripts/pageIssues.test.js b/tests/qunit/skins.minerva.scripts/pageIssues.test.js index 0a3b38d..52aaf53 100644 --- a/tests/qunit/skins.minerva.scripts/pageIssues.test.js +++ b/tests/qunit/skins.minerva.scripts/pageIssues.test.js @@ -1,7 +1,6 @@ ( function ( M ) { var pageIssues = M.require( 'skins.minerva.scripts/pageIssues' ), util = M.require( 'mobile.startup/util' ), - extractMessage = pageIssues.test.extractMessage, createBanner = pageIssues.test.createBanner, icon = {}, formatPageIssuesSeverity = pageIssues.test.formatPageIssuesSeverity, @@ -124,47 +123,6 @@ } ); - QUnit.test( 'extractMessage', function () { - [ - [ - $( '

    ' ).html( - '
    Smelly
    ' - ).appendTo( '
    ' ), - { - issue: { - severity: 'DEFAULT', - grouped: true, - icon: icon - }, - iconString: this.sandbox.match.typeOf( 'string' ), - text: '

    Smelly

    ' - }, - 'When the box is a child of mw-collapsible-content it grouped' - ], - [ - $( '
    ' ).html( - '
    Dirty
    ' - ), - { - issue: { - severity: 'DEFAULT', - grouped: false, - icon: icon - }, - iconString: this.sandbox.match.typeOf( 'string' ), - text: '

    Dirty

    ' - }, - '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( 'getAllIssuesSections', function ( assert ) { var multipleIssuesWithDeletion, multipleIssues, allIssuesOldTreatment, allIssuesNewTreatment; diff --git a/tests/qunit/skins.minerva.scripts/pageIssuesParser.test.js b/tests/qunit/skins.minerva.scripts/pageIssuesParser.test.js index 85ca9f6..367671f 100644 --- a/tests/qunit/skins.minerva.scripts/pageIssuesParser.test.js +++ b/tests/qunit/skins.minerva.scripts/pageIssuesParser.test.js @@ -1,5 +1,7 @@ ( 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' ); @@ -13,6 +15,47 @@ return box; } + QUnit.test( 'extractMessage', function () { + [ + [ + $( '
    ' ).html( + '
    Smelly
    ' + ).appendTo( '
    ' ), + { + issue: { + severity: 'DEFAULT', + grouped: true, + icon: icon + }, + iconString: this.sandbox.match.typeOf( 'string' ), + text: '

    Smelly

    ' + }, + 'When the box is a child of mw-collapsible-content it grouped' + ], + [ + $( '
    ' ).html( + '
    Dirty
    ' + ), + { + issue: { + severity: 'DEFAULT', + grouped: false, + icon: icon + }, + iconString: this.sandbox.match.typeOf( 'string' ), + text: '

    Dirty

    ' + }, + '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 ) { var tests = [ [ '', 'DEFAULT', 'empty' ], From 72cd31f2211f7e8476371de034eb288a63ff2a1b Mon Sep 17 00:00:00 2001 From: jdlrobson Date: Wed, 10 Oct 2018 14:49:25 -0700 Subject: [PATCH 4/4] Remove page issues instrumentation Note, since the page issues code is not feature flagged, the new treatment will only be accessible via query string until T206179 is taken care of. Bug: T206178 Change-Id: I5ab2f3396e642f7b973263e2bb3963e0e82721b3 --- includes/MinervaHooks.php | 2 - resources/skins.minerva.editor/init.js | 2 - .../PageIssuesOverlay.js | 141 +----------------- resources/skins.minerva.scripts/pageIssues.js | 99 +----------- .../skins.minerva.scripts/pageIssuesLogger.js | 114 -------------- skin.json | 2 - .../PageIssuesOverlay.test.js | 57 ------- .../skins.minerva.scripts/pageIssues.test.js | 61 -------- 8 files changed, 10 insertions(+), 468 deletions(-) delete mode 100644 resources/skins.minerva.scripts/pageIssuesLogger.js delete mode 100644 tests/qunit/skins.minerva.scripts/PageIssuesOverlay.test.js diff --git a/includes/MinervaHooks.php b/includes/MinervaHooks.php index 2ede615..b0ad181 100644 --- a/includes/MinervaHooks.php +++ b/includes/MinervaHooks.php @@ -91,7 +91,6 @@ class MinervaHooks { // additional scaffolding (minus initialisation scripts) 'tests/qunit/skins.minerva.scripts/stubs.js', - 'resources/skins.minerva.scripts/pageIssuesLogger.js', 'resources/skins.minerva.scripts/pageIssuesParser.js', 'resources/skins.minerva.scripts/DownloadIcon.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/pageIssuesParser.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.notifications.badge/NotificationBadge.test.js' ], diff --git a/resources/skins.minerva.editor/init.js b/resources/skins.minerva.editor/init.js index 6c63fba..6a83e5a 100644 --- a/resources/skins.minerva.editor/init.js +++ b/resources/skins.minerva.editor/init.js @@ -2,7 +2,6 @@ var router = require( 'mediawiki.router' ), - issues = M.require( 'skins.minerva.scripts/pageIssues' ), overlayManager = M.require( 'skins.minerva.scripts/overlayManager' ), loader = M.require( 'mobile.startup/rlModuleLoader' ), skin = M.require( 'skins.minerva.scripts/skin' ), @@ -32,7 +31,6 @@ */ function onEditLinkClick() { var section = ( new mw.Uri( this.href ) ).query.section || 'all'; - issues.log( { action: 'editClicked' } ); router.navigate( '#/editor/' + section ); // prevent folding section when clicking Edit by stopping propagation return false; diff --git a/resources/skins.minerva.scripts/PageIssuesOverlay.js b/resources/skins.minerva.scripts/PageIssuesOverlay.js index f3418d8..9d47f49 100644 --- a/resources/skins.minerva.scripts/PageIssuesOverlay.js +++ b/resources/skins.minerva.scripts/PageIssuesOverlay.js @@ -13,13 +13,11 @@ * @extends Overlay * * @param {IssueSummary[]} issues list of page issue summaries for display. - * @param {PageIssuesLogger} logger E.g., { log: console.log }. * @param {string} section * @param {number} namespaceID */ - function PageIssuesOverlay( issues, logger, section, namespaceID ) { + function PageIssuesOverlay( issues, section, namespaceID ) { var - options, // 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 // namespace. @@ -27,24 +25,10 @@ getNamespaceHeadingText( namespaceID ) : mwMsg( 'minerva-meta-data-issues-section-header' ); - this.issues = issues; - this.logger = logger; - this.section = section; - - 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 = '' + headingText + ''; - Overlay.call( this, options ); - - this.on( Overlay.EVENT_EXIT, this.onExit.bind( this ) ); + Overlay.call( this, { + issues: issues, + heading: '' + headingText + '' + } ); } OO.mfExtend( PageIssuesOverlay, Overlay, { @@ -54,128 +38,15 @@ */ 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 * @instance */ templatePartials: util.extend( {}, Overlay.prototype.templatePartials, { 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 * @param {number} namespaceID is the namespace to generate heading for diff --git a/resources/skins.minerva.scripts/pageIssues.js b/resources/skins.minerva.scripts/pageIssues.js index 23f7749..63128b7 100644 --- a/resources/skins.minerva.scripts/pageIssues.js +++ b/resources/skins.minerva.scripts/pageIssues.js @@ -1,63 +1,17 @@ ( function ( M, $ ) { - var AB = M.require( 'skins.minerva.scripts/AB' ), - Page = M.require( 'mobile.startup/Page' ), + var Page = M.require( 'mobile.startup/Page' ), allIssues = {}, KEYWORD_ALL_SECTIONS = 'all', config = mw.config, - user = mw.user, NS_MAIN = 0, NS_TALK = 1, NS_CATEGORY = 14, CURRENT_NS = config.get( 'wgNamespaceNumber' ), - pageIssuesLogger = M.require( 'skins.minerva.scripts/pageIssuesLogger' ), pageIssuesParser = M.require( 'skins.minerva.scripts/pageIssuesParser' ), 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' ), - // Per T204746 a user can request the new treatment regardless of test group - isUserRequestingNewTreatment = QUERY_STRING_FLAG === 'b', - newTreatmentEnabled = abTest.isB() || isUserRequestingNewTreatment; - - 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 ], - issue = currentItem.issue, - lastFormattedIndex = formattedArr.length - 1, - lastFormattedValue = formattedArr[ lastFormattedIndex ]; - // If the last and current item `grouped`, fold the maxSeverity - // of the two items into a single value. - if ( lastItem && lastItem.issue && lastItem.issue.grouped && issue.grouped ) { - formattedArr[ lastFormattedIndex ] = pageIssuesParser.maxSeverity( - [ lastFormattedValue, issue.severity ] - ); - } else { - formattedArr.push( issue.severity ); - } - return formattedArr; - } + // T206179 should update this value to enable it + newTreatmentEnabled = QUERY_STRING_FLAG === 'b'; /** * Create a link element that opens the issues overlay. @@ -138,34 +92,12 @@ $learnMore.appendTo( $metadata.find( '.mbox-text' ) ); } $metadata.click( function () { - var pageIssue = pageIssuesParser.parse( this ); - pageIssuesLogger.log( { - action: 'issueClicked', - issuesSeverity: [ pageIssue.severity ], - sectionNumbers: [ section ] - } ); overlayManager.router.navigate( issueUrl ); return false; } ); } else { $link = createLinkElement( labelText ); $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 ) { $link.insertAfter( $( 'h1#section_0' ) ); $metadata.remove(); @@ -269,39 +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. overlayManager.add( new RegExp( '^/issues/(\\d+|' + KEYWORD_ALL_SECTIONS + ')$' ), function ( section ) { return new PageIssuesOverlay( - getIssues( section ), pageIssuesLogger, section, CURRENT_NS ); + getIssues( section ), section, CURRENT_NS ); } ); } M.define( 'skins.minerva.scripts/pageIssues', { 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: { - formatPageIssuesSeverity: formatPageIssuesSeverity, getAllIssuesSections: getAllIssuesSections, createBanner: createBanner } diff --git a/resources/skins.minerva.scripts/pageIssuesLogger.js b/resources/skins.minerva.scripts/pageIssuesLogger.js deleted file mode 100644 index 9756963..0000000 --- a/resources/skins.minerva.scripts/pageIssuesLogger.js +++ /dev/null @@ -1,114 +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 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 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 ); } - ); - - // For backwards compatibility with cached WikimediaEvents code - // (remove me in a weeks time!! T207423) - mwTrack( 'wikimedia.event.ReadingDepthSchema.enable', bucketToGroup( newTreatmentEnabled ) ); - // 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 ) ); diff --git a/skin.json b/skin.json index 7f0f442..8d134be 100644 --- a/skin.json +++ b/skin.json @@ -158,7 +158,6 @@ } }, "EventLoggingSchemas": { - "PageIssues": 18392542, "WebClientError": 18340282 }, "ResourceModules": { @@ -436,7 +435,6 @@ "resources/skins.minerva.scripts/errorLogging.js", "resources/skins.minerva.scripts/preInit.js", "resources/skins.minerva.scripts/DownloadIcon.js", - "resources/skins.minerva.scripts/pageIssuesLogger.js", "resources/skins.minerva.scripts/pageIssuesParser.js", "resources/skins.minerva.scripts/AB.js", "resources/skins.minerva.scripts/PageIssuesOverlay.js", diff --git a/tests/qunit/skins.minerva.scripts/PageIssuesOverlay.test.js b/tests/qunit/skins.minerva.scripts/PageIssuesOverlay.test.js deleted file mode 100644 index 11c7bac..0000000 --- a/tests/qunit/skins.minerva.scripts/PageIssuesOverlay.test.js +++ /dev/null @@ -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 ) ); diff --git a/tests/qunit/skins.minerva.scripts/pageIssues.test.js b/tests/qunit/skins.minerva.scripts/pageIssues.test.js index 52aaf53..63a1dc8 100644 --- a/tests/qunit/skins.minerva.scripts/pageIssues.test.js +++ b/tests/qunit/skins.minerva.scripts/pageIssues.test.js @@ -3,7 +3,6 @@ util = M.require( 'mobile.startup/util' ), createBanner = pageIssues.test.createBanner, icon = {}, - formatPageIssuesSeverity = pageIssues.test.formatPageIssuesSeverity, MEDIUM_ISSUE = { issue: { severity: 'MEDIUM', @@ -12,24 +11,6 @@ iconString: 'i', text: 't' }, - MEDIUM_MULTIPLE_ISSUE = { - issue: { - severity: 'MEDIUM', - grouped: true, - icon: icon - }, - iconString: 'i', - text: 't' - }, - LOW_MULTIPLE_ISSUE = { - issue: { - severity: 'LOW', - grouped: true, - icon: icon - }, - iconString: 'i', - text: 't' - }, LOW_ISSUE = { issue: { severity: 'LOW', @@ -81,48 +62,6 @@ 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( 'getAllIssuesSections', function ( assert ) { var multipleIssuesWithDeletion, multipleIssues, allIssuesOldTreatment, allIssuesNewTreatment;