Limit line length to 80 characters

Enforce it with eslint.

Ignore:
* Comment lines with eslint disable directives
* QUnit test lines as they contain long subjects (QUnit.* (only, test,
  module, skip, etc)
* Strings, since long strings are used extensively in tests
  * Ignore template literals for similar reasons
* Regex literals as they may be too long, but can't be easily
  split in several lines
* Long urls

See bug for more general proposal for eslint-wikimedia-config.

Bug: T185295
Change-Id: I3aacaf46e61a4d96547c513073e179ef997deb09
This commit is contained in:
joakin 2018-01-18 19:48:16 +01:00
parent 64e81b0fee
commit 807100bcca
31 changed files with 394 additions and 212 deletions

View file

@ -19,6 +19,23 @@
},
"rules": {
"dot-notation": [ "error", { "allowKeywords": true } ],
"no-use-before-define": 0
"no-use-before-define": 0,
"max-len": [
"warn",
{
"tabWidth": 2,
// Note: exclude all eslint- directives except for max-len itself
// since `// eslint-disable-line max-len` would cause this line to be
// ignored AND this rule to be disabled which would trigger
// --report-unused-disable-directives. By excluding max-len, the effect
// is to consider the line but disable this rule.
"ignorePattern": "^/// .+|// (?!eslint-.+max-len)eslint-.+|QUnit.",
"ignoreUrls": true,
"ignoreComments": false,
"ignoreRegExpLiterals": true,
"ignoreStrings": true,
"ignoreTemplateLiterals": true
}
]
}
}

Binary file not shown.

View file

@ -131,7 +131,10 @@ export function fetch( gateway, title, el, token ) {
} );
} );
return $.when( request, wait( FETCH_COMPLETE_TARGET_DELAY - FETCH_START_DELAY ) )
return $.when(
request,
wait( FETCH_COMPLETE_TARGET_DELAY - FETCH_START_DELAY )
)
.then( function ( result ) {
dispatch( {
type: types.FETCH_COMPLETE,
@ -141,15 +144,21 @@ export function fetch( gateway, title, el, token ) {
} );
} )
.fail( function ( data, result ) {
// All failures, except those due to being offline or network error, should present "There
// was an issue displaying this preview". e.g.:
// - Show (timeout): data="http" {xhr: {…}, textStatus: "timeout", exception: "timeout"}
// All failures, except those due to being offline or network error,
// should present "There was an issue displaying this preview".
// e.g.:
// - Show (timeout): data="http" {xhr: {…}, textStatus: "timeout",
// exception: "timeout"}
// - Show (bad MW request): data="unknown_action" {error: {…}}
// - Show (RB 4xx): data="http" {xhr: {…}, textStatus: "error", exception: "Bad Request"}
// - Show (RB 5xx): data="http" {xhr: {…}, textStatus: "error", exception: "Service Unavailable"}
// - Suppress (offline or network error): data="http" result={xhr: {…}, textStatus: "error", exception: ""}
var networkError = result && result.xhr && result.xhr.readyState === 0 &&
result.textStatus === 'error' && result.exception === '';
// - Show (RB 4xx): data="http" {xhr: {…}, textStatus: "error",
// exception: "Bad Request"}
// - Show (RB 5xx): data="http" {xhr: {…}, textStatus: "error",
// exception: "Service Unavailable"}
// - Suppress (offline or network error): data="http"
// result={xhr: {…}, textStatus: "error", exception: ""}
var networkError = result && result.xhr &&
result.xhr.readyState === 0 && result.textStatus === 'error' &&
result.exception === '';
if ( !networkError ) {
dispatch( {
// Both FETCH_FAILED and FETCH_END conclude with FETCH_COMPLETE.

View file

@ -4,7 +4,8 @@
var $ = jQuery,
// If bracketedDevicePixelRatio is not available default to 1 (in tests for
// example)
pixelRatio = $.bracketedDevicePixelRatio && $.bracketedDevicePixelRatio() || 1,
pixelRatio = $.bracketedDevicePixelRatio &&
$.bracketedDevicePixelRatio() || 1,
BUCKETS = {
off: 'off',
on: 'on',

View file

@ -55,12 +55,16 @@ function makeTitleInExtractBold( extract, title ) {
// Make title bold in the extract text
// As the extract is html escaped there can be no such string in it
// Also, the title is escaped of RegExp elements thus can't have "*"
extract = extract.replace( regExp, '$1' + snip + boldIdentifier + '$2' + snip + '$3' );
extract = extract.replace(
regExp,
'$1' + snip + boldIdentifier + '$2' + snip + '$3'
);
extract = extract.split( snip );
$.each( extract, function ( index, part ) {
if ( part.indexOf( boldIdentifier ) === 0 ) {
elements.push( $( '<b>' ).text( part.substring( boldIdentifier.length ) ) );
elements.push( $( '<b>' )
.text( part.substring( boldIdentifier.length ) ) );
} else {
elements.push( document.createTextNode( part ) );
}

View file

@ -36,9 +36,11 @@ export default function createGateway( config ) {
case 'mwApiPlain':
return createMediaWikiApiGateway( new mw.Api(), constants );
case 'restbasePlain':
return createRESTBaseGateway( $.ajax, constants, formatters.parsePlainTextResponse );
return createRESTBaseGateway(
$.ajax, constants, formatters.parsePlainTextResponse );
case 'restbaseHTML':
return createRESTBaseGateway( $.ajax, constants, formatters.parseHTMLResponse );
return createRESTBaseGateway(
$.ajax, constants, formatters.parseHTMLResponse );
default:
throw new Error( 'Unknown gateway' );
}

View file

@ -26,7 +26,8 @@ var RESTBASE_ENDPOINT = '/api/rest_v1/page/summary/',
* @param {Function} ajax A function with the same signature as `jQuery.ajax`
* @param {Object} config Configuration that affects the major behavior of the
* gateway.
* @param {Function} extractParser A function that takes response and returns parsed extract
* @param {Function} extractParser A function that takes response and returns
* parsed extract
* @return {RESTBaseGateway}
*/
export default function createRESTBaseGateway( ajax, config, extractParser ) {
@ -76,9 +77,14 @@ export default function createRESTBaseGateway( ajax, config, extractParser ) {
createNullModel( title, new mw.Title( title ).getUrl() )
);
} else {
// The client will choose how to handle these errors which may include those due to HTTP
// 5xx status. The rejection typing matches Fetch failures.
result.reject( 'http', { xhr: jqXHR, textStatus: textStatus, exception: errorThrown } );
// The client will choose how to handle these errors which may
// include those due to HTTP 5xx status. The rejection typing
// matches Fetch failures.
result.reject( 'http', {
xhr: jqXHR,
textStatus: textStatus,
exception: errorThrown
} );
}
}
);
@ -163,6 +169,9 @@ function convertPageToModel( page, thumbSize, extractParser ) {
page.lang,
page.dir,
extractParser( page ),
page.thumbnail ? generateThumbnailData( page.thumbnail, page.originalimage, thumbSize ) : undefined
page.thumbnail ?
generateThumbnailData(
page.thumbnail, page.originalimage, thumbSize
) : undefined
);
}

View file

@ -2,8 +2,9 @@
* What is the bucket for the given user given the enabled sampling rate?
*
* @param {mw.experiments} experiments The `mw.experiments` singleton instance
* @param {Number} experimentGroupSize [0,1] of users of which should be subjected to an
* A/B test. The remaining users will be put in the 'off' bucket.
* @param {Number} experimentGroupSize [0,1] of users of which should be
* subjected to an A/B test. The remaining users will be put in the 'off'
* bucket.
* @param {string} sessionId a unique session token
*
* @return {string} bucket that the user belongs to (off/control/on)

View file

@ -15,7 +15,8 @@ import { fromElement as titleFromElement } from './title';
import { init as rendererInit } from './ui/renderer';
import createExperiments from './experiments';
import { isEnabled as isStatsvEnabled } from './instrumentation/statsv';
import { isEnabled as isEventLoggingEnabled } from './instrumentation/eventLogging';
import { isEnabled as isEventLoggingEnabled }
from './instrumentation/eventLogging';
import changeListeners from './changeListeners';
import * as actions from './actions';
import reducers from './reducers';
@ -116,14 +117,24 @@ function getCurrentTimestamp() {
* @param {EventTracker} eventLoggingTracker
* @param {Function} getCurrentTimestamp
*/
function registerChangeListeners( store, actions, userSettings, settingsDialog, previewBehavior, statsvTracker, eventLoggingTracker, getCurrentTimestamp ) {
function registerChangeListeners(
store, actions, userSettings, settingsDialog, previewBehavior,
statsvTracker, eventLoggingTracker, getCurrentTimestamp
) {
registerChangeListener( store, changeListeners.footerLink( actions ) );
registerChangeListener( store, changeListeners.linkTitle() );
registerChangeListener( store, changeListeners.render( previewBehavior ) );
registerChangeListener( store, changeListeners.statsv( actions, statsvTracker ) );
registerChangeListener( store, changeListeners.syncUserSettings( userSettings ) );
registerChangeListener( store, changeListeners.settings( actions, settingsDialog ) );
registerChangeListener( store, changeListeners.eventLogging( actions, eventLoggingTracker, getCurrentTimestamp ) );
registerChangeListener(
store, changeListeners.statsv( actions, statsvTracker ) );
registerChangeListener(
store, changeListeners.syncUserSettings( userSettings ) );
registerChangeListener(
store, changeListeners.settings( actions, settingsDialog ) );
registerChangeListener(
store,
changeListeners.eventLogging(
actions, eventLoggingTracker, getCurrentTimestamp
) );
}
/*
@ -153,7 +164,9 @@ mw.requestIdleCallback( function () {
isEnabled,
previewBehavior;
userBucket = getUserBucket( mw.experiments, mw.config.get( 'wgPopupsAnonsExperimentalGroupSize' ),
userBucket = getUserBucket(
mw.experiments,
mw.config.get( 'wgPopupsAnonsExperimentalGroupSize' ),
mw.user.sessionId() );
userSettings = createUserSettings( mw.storage );
settingsDialog = createSettingsDialogRenderer();
@ -215,7 +228,9 @@ mw.requestIdleCallback( function () {
var mwTitle = titleFromElement( this, mw.config );
if ( mwTitle ) {
boundActions.linkDwell( mwTitle, this, event, gateway, generateToken );
boundActions.linkDwell(
mwTitle, this, event, gateway, generateToken
);
}
} )
.on( 'mouseout blur', validLinkSelector, function () {

View file

@ -8,9 +8,9 @@ import { BUCKETS } from './../constants';
* session.
* If wgPopupsEventLogging is false this will return false unless debug=true has
* been enabled.
* If an experiment is being run (ie. wgPopupsAnonsExperimentalGroupSize has been
* defined) then event logging will only be enabled for those in the `on` or `control`
* groups.
* If an experiment is being run (ie. wgPopupsAnonsExperimentalGroupSize has
* been defined) then event logging will only be enabled for those in the `on`
* or `control` groups.
* However, if the UA doesn't support [the Beacon API][1], then bucketing is
* disabled.
*

View file

@ -78,7 +78,8 @@ export function createNullModel( title, url ) {
* returned.
*
* @param {Array|undefined|null} extract
* @return {Array|undefined} Array when extract is an not empty array, undefined otherwise
* @return {Array|undefined} Array when extract is an not empty array, undefined
* otherwise
*/
function processExtract( extract ) {
if ( extract === undefined || extract === null || extract.length === 0 ) {

View file

@ -21,12 +21,15 @@ function getBaseData( bootAction ) {
popupEnabled: bootAction.isEnabled,
pageToken: bootAction.pageToken,
sessionToken: bootAction.sessionToken,
previewCountBucket: counts.getPreviewCountBucket( bootAction.user.previewCount ),
previewCountBucket: counts.getPreviewCountBucket(
bootAction.user.previewCount
),
hovercardsSuppressedByGadget: bootAction.isNavPopupsEnabled
};
if ( !bootAction.user.isAnon ) {
result.editCountBucket = counts.getEditCountBucket( bootAction.user.editCount );
result.editCountBucket =
counts.getEditCountBucket( bootAction.user.editCount );
}
return result;
@ -74,7 +77,8 @@ function createEvent( interaction, actionData ) {
*/
function createClosingEvent( interaction ) {
var actionData = {
totalInteractionTime: Math.round( interaction.finished - interaction.started )
totalInteractionTime:
Math.round( interaction.finished - interaction.started )
};
if ( interaction.finalized ) {
@ -84,7 +88,8 @@ function createClosingEvent( interaction ) {
// Has the preview been shown? If so, then, in the context of the
// instrumentation, then the preview has been dismissed by the user
// rather than the user has abandoned the link.
actionData.action = interaction.timeToPreviewShow ? 'dismissed' : 'dwelledButAbandoned';
actionData.action =
interaction.timeToPreviewShow ? 'dismissed' : 'dwelledButAbandoned';
return createEvent( interaction, actionData );
}
@ -206,7 +211,8 @@ export default function eventLogging( state, action ) {
previewCountBucket: counts.getPreviewCountBucket( nextCount )
} ),
interaction: nextState( state.interaction, {
timeToPreviewShow: Math.round( action.timestamp - state.interaction.started )
timeToPreviewShow:
Math.round( action.timestamp - state.interaction.started )
} )
} );
@ -237,7 +243,8 @@ export default function eventLogging( state, action ) {
// Was the user interacting with another link? If so, then log the
// abandoned event.
event: state.interaction ? createClosingEvent( state.interaction ) : undefined
event: state.interaction ?
createClosingEvent( state.interaction ) : undefined
} );
case actionTypes.PREVIEW_DWELL:
@ -254,7 +261,8 @@ export default function eventLogging( state, action ) {
} ),
event: createEvent( state.interaction, {
action: 'opened',
totalInteractionTime: Math.round( action.timestamp - state.interaction.started )
totalInteractionTime:
Math.round( action.timestamp - state.interaction.started )
} )
} );

View file

@ -78,5 +78,8 @@ export function isValid( title, contentNamespaces ) {
* @return {mw.Title|null}
*/
export function fromElement( el, config ) {
return isValid( getTitle( el.href, config ), config.get( 'wgContentNamespaces' ) );
return isValid(
getTitle( el.href, config ),
config.get( 'wgContentNamespaces' )
);
}

View file

@ -93,7 +93,8 @@ export function init() {
*/
export function render( model ) {
// `undefined` is the default extract for null objects.
var preview = model.extract === undefined ? createEmptyPreview( model ) : createPreview( model );
var preview = model.extract === undefined ?
createEmptyPreview( model ) : createPreview( model );
return {
@ -406,7 +407,9 @@ export function createThumbnail( rawThumbnail ) {
* @param {String} clipPath
* @return {jQuery}
*/
export function createThumbnailElement( className, url, x, y, thumbnailWidth, thumbnailHeight, width, height, clipPath ) {
export function createThumbnailElement(
className, url, x, y, thumbnailWidth, thumbnailHeight, width, height, clipPath
) {
var $thumbnailSVGImage, $thumbnail,
nsSvg = 'http://www.w3.org/2000/svg',
nsXlink = 'http://www.w3.org/1999/xlink';
@ -449,12 +452,15 @@ export function createThumbnailElement( className, url, x, y, thumbnailWidth, th
/**
* @param {isPreviewTall} isPreviewTall
* @param {Object} eventData Data related to the event that triggered showing a popup
* @param {Object} eventData Data related to the event that triggered showing
* a popup
* @param {number} eventData.pageX
* @param {number} eventData.pageY
* @param {number} eventData.clientY
* @param {Object} linkData Data related to the link thats used for showing a popup
* @param {ClientRectList} linkData.clientRects list of rectangles defined by four edges
* @param {Object} linkData Data related to the link thats used for showing
* a popup
* @param {ClientRectList} linkData.clientRects list of rectangles defined by
* four edges
* @param {Object} linkData.offset
* @param {number} linkData.width
* @param {number} linkData.height
@ -465,7 +471,9 @@ export function createThumbnailElement( className, url, x, y, thumbnailWidth, th
* @param {number} pokeySize Space reserved for the pokey
* @return {ext.popups.PreviewLayout}
*/
export function createLayout( isPreviewTall, eventData, linkData, windowData, pokeySize ) {
export function createLayout(
isPreviewTall, eventData, linkData, windowData, pokeySize
) {
var flippedX = false,
flippedY = false,
offsetTop = eventData.pageY ?
@ -595,7 +603,9 @@ export function getClasses( preview, layout ) {
* @param {number} predefinedLandscapeImageHeight landscape image height
* @param {number} pokeySize
*/
export function layoutPreview( preview, layout, classes, predefinedLandscapeImageHeight, pokeySize ) {
export function layoutPreview(
preview, layout, classes, predefinedLandscapeImageHeight, pokeySize
) {
var popup = preview.el,
isTall = preview.isTall,
hasThumbnail = preview.hasThumbnail,

View file

@ -124,7 +124,8 @@ export default function createSettingsDialogRenderer() {
*/
function createSettingsDialog() {
var $el,
path = mw.config.get( 'wgExtensionAssetsPath' ) + '/Popups/resources/ext.popups/images/',
path = mw.config.get( 'wgExtensionAssetsPath' ) +
'/Popups/resources/ext.popups/images/',
choices = [
{
id: 'simple',

View file

@ -65,8 +65,9 @@ export default function createUserSettings( storage ) {
/**
* Gets the number of previews that the user has seen.
*
* If the storage isn't available, then -1 is returned.
* If the value in storage is not a number it will override stored value to 0
* - If the storage isn't available, then -1 is returned.
* - If the value in storage is not a number it will override stored value
* to 0
*
* @function
* @name UserSettings#getPreviewCount

View file

@ -126,7 +126,9 @@ QUnit.test( '#linkDwell', function ( assert ) {
activeToken: generateToken()
};
actions.linkDwell( this.title, this.el, event, /* gateway = */ null, generateToken )(
actions.linkDwell(
this.title, this.el, event, /* gateway = */ null, generateToken
)(
dispatch,
this.getState
);
@ -175,7 +177,9 @@ QUnit.test( '#linkDwell doesn\'t continue when previews are disabled', function
activeToken: generateToken()
};
actions.linkDwell( this.title, this.el, event, /* gateway = */ null, generateToken )(
actions.linkDwell(
this.title, this.el, event, /* gateway = */ null, generateToken
)(
dispatch,
this.getState
);
@ -203,7 +207,9 @@ QUnit.test( '#linkDwell doesn\'t continue if the token has changed', function (
activeToken: generateToken()
};
actions.linkDwell( this.title, this.el, event, /* gateway = */ null, generateToken )(
actions.linkDwell(
this.title, this.el, event, /* gateway = */ null, generateToken
)(
dispatch,
this.getState
);
@ -239,7 +245,9 @@ QUnit.test( '#linkDwell dispatches the fetch action', function ( assert ) {
activeToken: generateToken()
};
actions.linkDwell( this.title, this.el, event, /* gateway = */ null, generateToken )(
actions.linkDwell(
this.title, this.el, event, /* gateway = */ null, generateToken
)(
dispatch,
this.getState
);
@ -279,7 +287,9 @@ QUnit.module( 'ext.popups/actions#fetch', {
// Sugar.
this.fetch = function () {
return actions.fetch( that.gateway, that.title, that.el, that.token )( that.dispatch );
return actions.fetch(
that.gateway, that.title, that.el, that.token
)( that.dispatch );
};
}
} );
@ -381,7 +391,10 @@ QUnit.test( 'it should dispatch the FETCH_FAILED action when the request fails',
this.gatewayDeferred.reject( new Error( 'API req failed' ) );
const fetch = this.fetch().catch( function () {
assert.equal( that.dispatch.callCount, 3, 'dispatch called thrice, START, FAILED, and COMPLETE' );
assert.equal(
that.dispatch.callCount, 3,
'dispatch called thrice, START, FAILED, and COMPLETE'
);
assert.deepEqual(
that.dispatch.getCall( 1 ).args[ 0 ],
{
@ -402,7 +415,10 @@ QUnit.test( 'it should dispatch the FETCH_FAILED action when the request fails e
assert.expect( 2 );
const fetch = this.fetch().catch( function () {
assert.equal( that.dispatch.callCount, 3, 'dispatch called thrice, START, FAILED, and COMPLETE' );
assert.equal(
that.dispatch.callCount, 3,
'dispatch called thrice, START, FAILED, and COMPLETE'
);
assert.deepEqual(
that.dispatch.getCall( 1 ).args[ 0 ],
{
@ -498,12 +514,18 @@ QUnit.test( 'it should dispatch an action with previous and current enabled stat
actions.saveSettings( /* enabled = */ true )( dispatch, getState );
assert.ok( getState.calledOnce, 'it should query the global state for the current state' );
assert.ok( dispatch.calledWith( {
type: 'SETTINGS_CHANGE',
wasEnabled: false,
enabled: true
} ), 'it should dispatch the action with the previous and next enabled state' );
assert.ok(
getState.calledOnce,
'it should query the global state for the current state'
);
assert.ok(
dispatch.calledWith( {
type: 'SETTINGS_CHANGE',
wasEnabled: false,
enabled: true
} ),
'it should dispatch the action with the previous and next enabled state'
);
} );
QUnit.module( 'ext.popups/actions#previewShow' );

View file

@ -7,7 +7,10 @@ QUnit.module( 'ext.popups/changeListeners/render', {
show: this.sandbox.stub().returns( $.Deferred().resolve() )
};
this.sandbox.stub( RendererModule, 'render', this.sandbox.stub().returns( this.preview ) );
this.sandbox.stub(
RendererModule, 'render',
this.sandbox.stub().returns( this.preview )
);
}
} );

View file

@ -50,10 +50,14 @@ QUnit.test( 'it should create settings when shouldShow becomes true', function (
this.settings( null, this.defaultState );
this.settings( this.defaultState, this.showState );
assert.ok( this.render.calledWith( 'actions' ), 'The renderer should be called with the actions' );
assert.ok( this.rendered.appendTo.called, 'The rendered object should be in the DOM' );
assert.ok( this.rendered.setEnabled.called, 'The rendered form should be up to date' );
assert.ok( this.rendered.show.called, 'The rendered object should be showed' );
assert.ok( this.render.calledWith( 'actions' ),
'The renderer should be called with the actions' );
assert.ok( this.rendered.appendTo.called,
'The rendered object should be in the DOM' );
assert.ok( this.rendered.setEnabled.called,
'The rendered form should be up to date' );
assert.ok( this.rendered.show.called,
'The rendered object should be showed' );
} );
QUnit.test( 'it should not create settings when shouldShow keeps being true', function ( assert ) {
@ -61,10 +65,14 @@ QUnit.test( 'it should not create settings when shouldShow keeps being true', fu
this.settings( this.defaultState, this.showState );
this.settings( this.showState, this.showState );
assert.ok( this.render.calledOnce, 'The renderer should be called only the first time' );
assert.ok( this.rendered.appendTo.calledOnce, 'The rendered object should be in the DOM' );
assert.ok( this.rendered.show.calledOnce, 'The rendered object should be showed just once' );
assert.notOk( this.rendered.hide.called, 'The rendered object should not be hidden' );
assert.ok( this.render.calledOnce,
'The renderer should be called only the first time' );
assert.ok( this.rendered.appendTo.calledOnce,
'The rendered object should be in the DOM' );
assert.ok( this.rendered.show.calledOnce,
'The rendered object should be showed just once' );
assert.notOk( this.rendered.hide.called,
'The rendered object should not be hidden' );
} );
QUnit.test( 'it should show settings and update the form when shouldShow becomes true', function ( assert ) {
@ -75,11 +83,16 @@ QUnit.test( 'it should show settings and update the form when shouldShow becomes
preview: { enabled: false }
} ) );
assert.ok( this.render.calledOnce, 'The renderer should be called only the first time' );
assert.ok( this.rendered.setEnabled.calledTwice, 'The rendered form should be up to date when shown' );
assert.strictEqual( this.rendered.setEnabled.firstCall.args[ 0 ], true, 'Set enabled should be called with the current enabled state' );
assert.strictEqual( this.rendered.setEnabled.secondCall.args[ 0 ], false, 'Set enabled should be called with the current enabled state' );
assert.ok( this.rendered.show.calledTwice, 'The rendered object should be showed' );
assert.ok( this.render.calledOnce,
'The renderer should be called only the first time' );
assert.ok( this.rendered.setEnabled.calledTwice,
'The rendered form should be up to date when shown' );
assert.strictEqual( this.rendered.setEnabled.firstCall.args[ 0 ], true,
'Set enabled should be called with the current enabled state' );
assert.strictEqual( this.rendered.setEnabled.secondCall.args[ 0 ], false,
'Set enabled should be called with the current enabled state' );
assert.ok( this.rendered.show.calledTwice,
'The rendered object should be showed' );
} );
QUnit.test( 'it should hide settings when shouldShow becomes false', function ( assert ) {
@ -87,7 +100,8 @@ QUnit.test( 'it should hide settings when shouldShow becomes false', function (
this.settings( this.defaultState, this.showState );
this.settings( this.showState, this.defaultState );
assert.ok( this.rendered.hide.calledOnce, 'The rendered object should be hidden' );
assert.ok( this.rendered.hide.calledOnce,
'The rendered object should be hidden' );
} );
QUnit.test( 'it should show help when showHelp becomes true', function ( assert ) {
@ -95,7 +109,8 @@ QUnit.test( 'it should show help when showHelp becomes true', function ( assert
this.settings( this.defaultState, this.showState );
this.settings( this.showState, this.showHelpState );
assert.ok( this.rendered.toggleHelp.calledWith( true ), 'Help should be shown' );
assert.ok( this.rendered.toggleHelp.calledWith( true ),
'Help should be shown' );
} );
QUnit.test( 'it should hide help when showHelp becomes false', function ( assert ) {
@ -104,6 +119,8 @@ QUnit.test( 'it should hide help when showHelp becomes false', function ( assert
this.settings( this.showState, this.showHelpState );
this.settings( this.showHelpState, this.hideHelpState );
assert.equal( this.rendered.toggleHelp.callCount, 2, 'Help should have been toggled on and off' );
assert.deepEqual( this.rendered.toggleHelp.secondCall.args, [ false ], 'Help should be hidden' );
assert.equal( this.rendered.toggleHelp.callCount, 2,
'Help should have been toggled on and off' );
assert.deepEqual( this.rendered.toggleHelp.secondCall.args, [ false ],
'Help should be hidden' );
} );

View file

@ -63,7 +63,8 @@ QUnit.test( '#getPreviewCountBucket', function ( assert ) {
assert.equal(
bucket,
cases[ i ][ 1 ],
'Preview count bucket is "' + bucket + '" when preview count is ' + count + '.'
'Preview count bucket is "' +
bucket + '" when preview count is ' + count + '.'
);
}
} );

View file

@ -6,7 +6,8 @@ QUnit.module( 'gateway/index.js', {
beforeEach: function () {
mediaWiki.Api = function () {};
this.createMediaWikiApiGateway = this.sandbox.stub( MediawikiModule, 'default' );
this.createMediaWikiApiGateway =
this.sandbox.stub( MediawikiModule, 'default' );
this.createRESTBaseGateway = this.sandbox.stub( RestModule, 'default' );
this.config = new Map(); /* global Map */
@ -18,7 +19,8 @@ QUnit.test( 'it uses mediawiki plain text gateway with wgPopupsGateway == "mwApi
createGateway( this.config );
assert.ok( this.createMediaWikiApiGateway.called, 'MW plain text gateway called' );
assert.ok( this.createMediaWikiApiGateway.called,
'MW plain text gateway called' );
} );
QUnit.test( 'it uses rest plain text gateway with wgPopupsGateway == "restbasePlain"', function ( assert ) {
@ -26,7 +28,8 @@ QUnit.test( 'it uses rest plain text gateway with wgPopupsGateway == "restbasePl
createGateway( this.config );
assert.ok( this.createRESTBaseGateway.called, 'REST plain text gateway called' );
assert.ok( this.createRESTBaseGateway.called,
'REST plain text gateway called' );
} );
QUnit.test( 'it uses rest HTML gateway with wgPopupsGateway == "restbaseHTML"', function ( assert ) {

View file

@ -154,7 +154,8 @@ QUnit.test( 'MediaWiki API gateway is correctly converting the page data to a mo
QUnit.test( 'MediaWiki API gateway handles API failure', function ( assert ) {
var api = {
get: this.sandbox.stub().returns( $.Deferred().reject( { status: 400 } ).promise() )
get: this.sandbox.stub()
.returns( $.Deferred().reject( { status: 400 } ).promise() )
},
gateway = createMediaWikiApiGateway( api, DEFAULT_CONSTANTS );

View file

@ -157,7 +157,8 @@ QUnit.test( 'RESTBase gateway doesn\'t stretch thumbnails', function ( assert )
var model,
gateway = createRESTBaseGateway();
model = gateway.convertPageToModel( RESTBASE_RESPONSE, 2000, provideParsedExtract );
model = gateway.convertPageToModel(
RESTBASE_RESPONSE, 2000, provideParsedExtract );
assert.deepEqual(
model.thumbnail,
@ -166,7 +167,11 @@ QUnit.test( 'RESTBase gateway doesn\'t stretch thumbnails', function ( assert )
);
// ---
model = gateway.convertPageToModel( RESTBASE_RESPONSE, RESTBASE_RESPONSE.originalimage.height, provideParsedExtract );
model = gateway.convertPageToModel(
RESTBASE_RESPONSE,
RESTBASE_RESPONSE.originalimage.height,
provideParsedExtract
);
assert.deepEqual(
model.thumbnail,
@ -175,7 +180,8 @@ QUnit.test( 'RESTBase gateway doesn\'t stretch thumbnails', function ( assert )
);
// ---
model = gateway.convertPageToModel( RESTBASE_RESPONSE_WITH_SMALL_IMAGE, 320, provideParsedExtract );
model = gateway.convertPageToModel(
RESTBASE_RESPONSE_WITH_SMALL_IMAGE, 320, provideParsedExtract );
assert.deepEqual(
model.thumbnail,
@ -184,7 +190,8 @@ QUnit.test( 'RESTBase gateway doesn\'t stretch thumbnails', function ( assert )
);
// ---
model = gateway.convertPageToModel( RESTBASE_RESPONSE_WITH_LANDSCAPE_IMAGE, 640, provideParsedExtract );
model = gateway.convertPageToModel(
RESTBASE_RESPONSE_WITH_LANDSCAPE_IMAGE, 640, provideParsedExtract );
assert.deepEqual(
model.thumbnail,
@ -219,7 +226,8 @@ QUnit.test( 'RESTBase gateway stretches SVGs', function ( assert ) {
var model,
gateway = createRESTBaseGateway();
model = gateway.convertPageToModel( SVG_RESTBASE_RESPONSE, 2000, provideParsedExtract );
model = gateway.convertPageToModel(
SVG_RESTBASE_RESPONSE, 2000, provideParsedExtract );
assert.equal(
model.thumbnail.source,
@ -229,7 +237,8 @@ QUnit.test( 'RESTBase gateway stretches SVGs', function ( assert ) {
} );
QUnit.test( 'RESTBase gateway handles API failure', function ( assert ) {
var api = this.sandbox.stub().returns( $.Deferred().reject( { status: 500 } ).promise() ),
var api = this.sandbox.stub()
.returns( $.Deferred().reject( { status: 500 } ).promise() ),
gateway = createRESTBaseGateway( api );
return gateway.getPageSummary( 'Test Title' ).catch( function () {
@ -249,11 +258,13 @@ QUnit.test( 'RESTBase gateway does not treat a 404 as a failure', function ( ass
api = this.sandbox.stub().returns(
$.Deferred().reject( response ).promise()
),
gateway = createRESTBaseGateway( api, DEFAULT_CONSTANTS, provideParsedExtract );
gateway = createRESTBaseGateway(
api, DEFAULT_CONSTANTS, provideParsedExtract );
return gateway.getPageSummary( 'Missing Page' ).then( function ( result ) {
assert.equal( result.title, 'Missing Page', 'Title' );
// Extract is undefined since the parser is only invoked for successful responses.
// Extract is undefined since the parser is only invoked for successful
// responses.
assert.equal( result.extract, undefined, 'Extract' );
} );
} );
@ -262,7 +273,8 @@ QUnit.test( 'RESTBase gateway returns the correct data ', function ( assert ) {
var api = this.sandbox.stub().returns(
$.Deferred().resolve( RESTBASE_RESPONSE ).promise()
),
gateway = createRESTBaseGateway( api, DEFAULT_CONSTANTS, provideParsedExtract );
gateway = createRESTBaseGateway(
api, DEFAULT_CONSTANTS, provideParsedExtract );
return gateway.getPageSummary( 'Test Title' ).then( function ( result ) {
assert.deepEqual( result, RESTBASE_RESPONSE_PREVIEW_MODEL );
@ -272,7 +284,8 @@ QUnit.test( 'RESTBase gateway returns the correct data ', function ( assert ) {
QUnit.test( 'RESTBase gateway handles missing images ', function ( assert ) {
var model,
gateway = createRESTBaseGateway();
model = gateway.convertPageToModel( RESTBASE_RESPONSE_WITHOUT_IMAGE, 300, provideParsedExtract );
model = gateway.convertPageToModel(
RESTBASE_RESPONSE_WITHOUT_IMAGE, 300, provideParsedExtract );
assert.equal(
model.originalimage,
@ -283,20 +296,25 @@ QUnit.test( 'RESTBase gateway handles missing images ', function ( assert ) {
QUnit.test( 'RESTBase gateway handles missing extracts', function ( assert ) {
var api = this.sandbox.stub().returns( $.Deferred().resolve( {} ).promise() ),
gateway = createRESTBaseGateway( api, DEFAULT_CONSTANTS, provideParsedExtract );
gateway = createRESTBaseGateway(
api, DEFAULT_CONSTANTS, provideParsedExtract );
return gateway.getPageSummary( 'Test Title with missing extract' ).then( function ( result ) {
assert.equal( result.title, 'Test Title with missing extract', 'Title' );
assert.equal( result.extract, '!!', 'Extract' );
} );
return gateway.getPageSummary( 'Test Title with missing extract' )
.then( function ( result ) {
assert.equal( result.title, 'Test Title with missing extract', 'Title' );
assert.equal( result.extract, '!!', 'Extract' );
} );
} );
QUnit.test( 'RESTBase gateway handles no content success responses', function ( assert ) {
var api = this.sandbox.stub().returns( $.Deferred().resolve( { status: 204 } ).promise() ),
gateway = createRESTBaseGateway( api, DEFAULT_CONSTANTS, provideParsedExtract );
var api = this.sandbox.stub()
.returns( $.Deferred().resolve( { status: 204 } ).promise() ),
gateway = createRESTBaseGateway(
api, DEFAULT_CONSTANTS, provideParsedExtract );
return gateway.getPageSummary( 'Test Title with empty response' ).then( function ( result ) {
assert.equal( result.title, 'Test Title with empty response', 'Title' );
assert.equal( result.extract, '!!', 'Extract' );
} );
return gateway.getPageSummary( 'Test Title with empty response' )
.then( function ( result ) {
assert.equal( result.title, 'Test Title with empty response', 'Title' );
assert.equal( result.extract, '!!', 'Extract' );
} );
} );

View file

@ -4,7 +4,8 @@ import getUserBucket from '../../src/getUserBucket';
QUnit.module( 'ext.popups#getUserBucket' );
QUnit.test( 'If no users are subject to experiment everyone is bucketed as on', function ( assert ) {
assert.ok( getUserBucket( stubs.createStubExperiments( 'A' ), 0, 'a' ) === 'on' );
assert.ok(
getUserBucket( stubs.createStubExperiments( 'A' ), 0, 'a' ) === 'on' );
} );
QUnit.test( 'Define how experiment size impacts buckets', function ( assert ) {
@ -26,10 +27,16 @@ QUnit.test( 'Define how experiment size impacts buckets', function ( assert ) {
getUserBucket( experiments, test[ 0 ], 'a' );
actualBuckets = spy.getCall( 0 ).args[ 0 ].buckets;
// To avoid precision issues we'll need to test them all individually rather than check
// use calledWith. Otherwise we'll get some false positives.
assert.ok( actualBuckets.off.toFixed( 2 ) === expectedBuckets.off.toFixed( 2 ) );
assert.ok( actualBuckets.on.toFixed( 2 ) === expectedBuckets.on.toFixed( 2 ) );
assert.ok( actualBuckets.control.toFixed( 2 ) === expectedBuckets.control.toFixed( 2 ) );
// To avoid precision issues we'll need to test them all individually
// rather than check use calledWith. Otherwise we'll get some false
// positives.
assert.ok(
actualBuckets.off.toFixed( 2 ) === expectedBuckets.off.toFixed( 2 ) );
assert.ok(
actualBuckets.on.toFixed( 2 ) === expectedBuckets.on.toFixed( 2 ) );
assert.ok(
actualBuckets.control.toFixed( 2 ) ===
expectedBuckets.control.toFixed( 2 )
);
} );
} );

View file

@ -65,7 +65,9 @@ QUnit.test( 'if experiment is running on group is subject to event logging', fun
QUnit.test( 'if experiment is running control group is subject to event logging', function ( assert ) {
assert.ok( this.isEnabled( true, BUCKETS.control ), 'anons are logged' );
assert.notOk( this.isEnabled( false, BUCKETS.control ), 'but not authenticated users' );
assert.notOk(
this.isEnabled( false, BUCKETS.control ),
'but not authenticated users' );
} );
QUnit.test( 'if experiment is running off group is not subject to event logging', function ( assert ) {
@ -85,6 +87,9 @@ QUnit.test( 'it should respect the debug flag always', function ( assert ) {
this.config.set( 'wgPopupsEventLogging', true );
assert.ok( this.isEnabled( true, BUCKETS.off ), 'Even when user is bucketed as off' );
assert.ok( this.isEnabled( false, BUCKETS.off ), 'Even when user is logged in and bucketed as off' );
assert.ok(
this.isEnabled( true, BUCKETS.off ), 'Even when user is bucketed as off' );
assert.ok(
this.isEnabled( false, BUCKETS.off ),
'Even when user is logged in and bucketed as off' );
} );

View file

@ -102,18 +102,23 @@ QUnit.module( 'ext.popups preview @integration', {
{ get: identity }
);
this.dwell = function ( title, el, ev, fetchResponse, resolution = FETCH_RESOLUTION.RESOLVE ) {
this.dwell = function (
title, el, ev, fetchResponse, resolution = FETCH_RESOLUTION.RESOLVE
) {
that.resetWait();
that.actions.linkDwell( title, el, ev, {
getPageSummary: function () {
var method = resolution === FETCH_RESOLUTION.RESOLVE ? 'resolve' : 'reject';
var method = resolution === FETCH_RESOLUTION.RESOLVE ?
'resolve' : 'reject';
return $.Deferred()[ method ]( fetchResponse ).promise();
}
}, function () { return 'pagetoken'; } );
return that.waitPromise;
};
this.dwellAndShowPreview = function ( title, el, ev, fetchResponse, reject = FETCH_RESOLUTION.RESOLVE ) {
this.dwellAndShowPreview = function (
title, el, ev, fetchResponse, reject = FETCH_RESOLUTION.RESOLVE
) {
that.dwell( title, el, ev, fetchResponse, reject );
that.waitDeferred.resolve();
@ -194,21 +199,27 @@ QUnit.test( 'in ACTIVE state, fetch end switches it to DATA', function ( assert
var store = this.store,
el = this.el;
return this.dwellAndShowPreview( this.title, el, 'event', 42 ).then( function () {
var state = store.getState();
assert.equal( state.preview.activeLink, el );
assert.equal( state.preview.shouldShow, true, 'Should show when data has been fetched' );
} );
return this.dwellAndShowPreview( this.title, el, 'event', 42 )
.then( function () {
var state = store.getState();
assert.equal( state.preview.activeLink, el );
assert.equal(
state.preview.shouldShow, true,
'Should show when data has been fetched' );
} );
} );
QUnit.test( 'in ACTIVE state, fetch fail switches it to DATA', function ( assert ) {
var store = this.store,
el = this.el;
return this.dwellAndShowPreview( this.title, el, 'event', 42, FETCH_RESOLUTION.REJECT ).then( function () {
return this.dwellAndShowPreview(
this.title, el, 'event', 42, FETCH_RESOLUTION.REJECT
).then( function () {
var state = store.getState();
assert.equal( state.preview.activeLink, el );
assert.equal( state.preview.shouldShow, true, 'Should show when data couldn\'t be fetched' );
assert.equal( state.preview.shouldShow, true,
'Should show when data couldn\'t be fetched' );
} );
} );
@ -216,12 +227,14 @@ QUnit.test( 'in ACTIVE state, abandon start, and then end, switch it to INACTIVE
var that = this,
el = this.el;
return this.dwellAndShowPreview( this.title, el, 'event', 42 ).then( function () {
return that.abandonAndWait( el );
} ).then( function () {
var state = that.store.getState();
assert.equal( state.preview.activeLink, undefined, 'After abandoning, preview is back to INACTIVE' );
} );
return this.dwellAndShowPreview( this.title, el, 'event', 42 )
.then( function () {
return that.abandonAndWait( el );
} ).then( function () {
var state = that.store.getState();
assert.equal( state.preview.activeLink, undefined,
'After abandoning, preview is back to INACTIVE' );
} );
} );
QUnit.test( 'in ACTIVE state, abandon link, and then dwell preview, should keep it active after all delays', function ( assert ) {

View file

@ -1,4 +1,5 @@
import { createModel, TYPE_PAGE, TYPE_GENERIC } from '../../../src/preview/model';
import { createModel, TYPE_PAGE, TYPE_GENERIC }
from '../../../src/preview/model';
QUnit.module( 'ext.popups.preview#createModel' );

View file

@ -45,7 +45,8 @@ QUnit.test( 'BOOT', function ( assert ) {
state;
expectedEditCountBucket = counts.getEditCountBucket( action.user.editCount );
expectedPreviewCountBucket = counts.getPreviewCountBucket( action.user.previewCount );
expectedPreviewCountBucket =
counts.getPreviewCountBucket( action.user.previewCount );
state = eventLogging( this.initialState, action );
@ -249,81 +250,78 @@ QUnit.test( 'LINK_DWELL doesn\'t start a new interaction under certain condition
);
} );
QUnit.test(
'LINK_DWELL should enqueue a "dismissed" or "dwelledButAbandoned" event under certain conditions',
function ( assert ) {
var token = '0987654321',
now = Date.now(),
state;
QUnit.test( 'LINK_DWELL should enqueue a "dismissed" or "dwelledButAbandoned" event under certain conditions', function ( assert ) {
var token = '0987654321',
now = Date.now(),
state;
// Read: The user dwells on link A, abandons it, and dwells on link B fewer
// than 300 ms after (before the ABANDON_END action is reduced).
state = eventLogging( undefined, {
type: 'LINK_DWELL',
el: this.link,
title: 'Foo',
namespaceID: 1,
token: token,
timestamp: now
} );
// Read: The user dwells on link A, abandons it, and dwells on link B fewer
// than 300 ms after (before the ABANDON_END action is reduced).
state = eventLogging( undefined, {
type: 'LINK_DWELL',
el: this.link,
title: 'Foo',
namespaceID: 1,
token: token,
timestamp: now
} );
state = eventLogging( state, {
type: 'ABANDON_START',
timestamp: now + 250
} );
state = eventLogging( state, {
type: 'ABANDON_START',
timestamp: now + 250
} );
state = eventLogging( state, {
type: 'LINK_DWELL',
el: $( '<a>' ),
title: 'Bar',
namespaceID: 1,
token: '1234567890',
timestamp: now + 500
} );
state = eventLogging( state, {
type: 'LINK_DWELL',
el: $( '<a>' ),
title: 'Bar',
namespaceID: 1,
token: '1234567890',
timestamp: now + 500
} );
assert.deepEqual(
state.event,
{
pageTitleHover: 'Foo',
namespaceIdHover: 1,
linkInteractionToken: '0987654321',
totalInteractionTime: 250, // 250 - 0
action: 'dwelledButAbandoned'
}
);
assert.deepEqual(
state.event,
{
pageTitleHover: 'Foo',
namespaceIdHover: 1,
linkInteractionToken: '0987654321',
totalInteractionTime: 250, // 250 - 0
action: 'dwelledButAbandoned'
}
);
// ---
// ---
state = eventLogging( undefined, {
type: 'LINK_DWELL',
el: this.link,
title: 'Foo',
namespaceID: 1,
token: token,
timestamp: now
} );
state = eventLogging( undefined, {
type: 'LINK_DWELL',
el: this.link,
title: 'Foo',
namespaceID: 1,
token: token,
timestamp: now
} );
state = eventLogging( state, {
type: 'LINK_CLICK',
el: this.link
} );
state = eventLogging( state, {
type: 'LINK_CLICK',
el: this.link
} );
state = eventLogging( state, {
type: 'LINK_DWELL',
el: $( '<a>' ),
title: 'Bar',
namespaceID: 1,
token: 'banana',
timestamp: now + 500
} );
state = eventLogging( state, {
type: 'LINK_DWELL',
el: $( '<a>' ),
title: 'Bar',
namespaceID: 1,
token: 'banana',
timestamp: now + 500
} );
assert.strictEqual(
state.event,
undefined,
'It shouldn\'t enqueue either event if the interaction is finalized.'
);
}
);
assert.strictEqual(
state.event,
undefined,
'It shouldn\'t enqueue either event if the interaction is finalized.'
);
} );
QUnit.test( 'LINK_CLICK should enqueue an "opened" event', function ( assert ) {
var token = '0987654321',

View file

@ -124,7 +124,9 @@ QUnit.test( 'it should return null if the title can\'t be parsed properly', func
QUnit.test( 'it should return null if the title can\'t be parsed properly', function ( assert ) {
window.mediaWiki.Title.newFromText.withArgs( 'title' ).returns( null );
assert.equal( isValid( 'title' ), null );
assert.equal( window.mediaWiki.Title.newFromText.callCount, 1, 'mediaWiki.Title.newFromText called for parsing the title' );
assert.equal(
window.mediaWiki.Title.newFromText.callCount, 1,
'mediaWiki.Title.newFromText called for parsing the title' );
} );
QUnit.test( 'it should return null if the title is not from a content namespace', function ( assert ) {

View file

@ -216,7 +216,8 @@ QUnit.test( 'bindBehavior - preview dwell', function ( assert ) {
preview.el.mouseenter();
assert.ok( behavior.previewDwell.calledOnce, 'Preview dwell is called.' );
assert.notOk( behavior.previewAbandon.called, 'Preview abandon is NOT called.' );
assert.notOk(
behavior.previewAbandon.called, 'Preview abandon is NOT called.' );
assert.notOk( behavior.click.called, 'Click is NOT called.' );
assert.notOk( behavior.showSettings.called, 'Show settings is NOT called.' );
} );
@ -242,9 +243,11 @@ QUnit.test( 'bindBehavior - preview click', function ( assert ) {
preview.el.click();
assert.notOk( behavior.previewDwell.called, 'Preview dwell is NOT called.' );
assert.notOk( behavior.previewAbandon.called, 'Preview abandon is NOT called.' );
assert.notOk(
behavior.previewAbandon.called, 'Preview abandon is NOT called.' );
assert.ok( behavior.click.calledOnce, 'Click is called.' );
assert.notOk( behavior.showSettings.called, 'Settings link click is NOT called.' );
assert.notOk( behavior.showSettings.called,
'Settings link click is NOT called.' );
} );
QUnit.test( 'bindBehavior - settings link click', function ( assert ) {
@ -255,9 +258,11 @@ QUnit.test( 'bindBehavior - settings link click', function ( assert ) {
preview.el.find( '.mwe-popups-settings-icon' ).click();
assert.notOk( behavior.previewDwell.called, 'Preview dwell is NOT called.' );
assert.notOk( behavior.previewAbandon.called, 'Preview abandon is NOT called.' );
assert.notOk(
behavior.previewAbandon.called, 'Preview abandon is NOT called.' );
assert.notOk( behavior.click.called, 'Click is NOT called.' );
assert.ok( behavior.showSettings.calledOnce, 'Settings link click is called.' );
assert.ok(
behavior.showSettings.calledOnce, 'Settings link click is called.' );
} );
QUnit.test( 'bindBehavior - settings link URL', function ( assert ) {
@ -683,7 +688,8 @@ QUnit.test( '#createLayout - portrait preview, mouse event, link is on the top l
height: 827
},
pokeySize = 8,
layout = renderer.createLayout( isPreviewTall, eventData, linkData, windowData, pokeySize );
layout = renderer.createLayout(
isPreviewTall, eventData, linkData, windowData, pokeySize );
assert.deepEqual(
layout,
@ -728,7 +734,8 @@ QUnit.test( '#createLayout - tall preview, mouse event, link is on the bottom ce
height: 827
},
pokeySize = 8,
layout = renderer.createLayout( isPreviewTall, eventData, linkData, windowData, pokeySize );
layout = renderer.createLayout(
isPreviewTall, eventData, linkData, windowData, pokeySize );
assert.deepEqual(
layout,
@ -769,7 +776,8 @@ QUnit.test( '#createLayout - empty preview, keyboard event, link is on the cente
height: 827
},
pokeySize = 8,
layout = renderer.createLayout( isPreviewTall, eventData, linkData, windowData, pokeySize );
layout = renderer.createLayout(
isPreviewTall, eventData, linkData, windowData, pokeySize );
assert.deepEqual(
layout,

View file

@ -90,7 +90,8 @@ QUnit.test( '#getPreviewCount should return the count as a number', function ( a
QUnit.test( '#setPreviewCount should store the count as a string', function ( assert ) {
this.userSettings.setPreviewCount( 222 );
assert.strictEqual( this.storage.get( 'ext.popups.core.previewCount' ), '222' );
assert.strictEqual(
this.storage.get( 'ext.popups.core.previewCount' ), '222' );
} );
QUnit.test( '#getPreviewCount should override value in storage when is not a number', function ( assert ) {