build: Update eslint 0.26.0 and apply fixes

Mainly auto fixes but also getting rid of some straight forward lint
warnings to reduce the wall of issues a bit when running the tests.

Also removing some rule exceptions that seem to got more relaxed
upstream.

Change-Id: Icb4d73374583675be74517e6df6508314d61e8c2
This commit is contained in:
WMDE-Fisch 2024-01-11 10:43:59 +01:00 committed by James D. Forrester
parent 863c442c32
commit a3f3cf9e3f
44 changed files with 1320 additions and 573 deletions

View file

@ -4,7 +4,5 @@
"wikimedia/server"
],
"rules": {
"yml/block-sequence": "warn",
"yml/plain-scalar": "warn"
}
}

View file

@ -1,7 +1,6 @@
instrumentation:
include-all-sources: true
excludes: [
"resources/dist/*", # Compiled assets
"*.js", # Gruntfile.js and webpack.config.js
"src/index.js", # Application entry point
]
excludes:
- "resources/dist/*" # Compiled assets
- "*.js" # Gruntfile.js and webpack.config.js
- src/index.js # Application entry point

1574
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -46,7 +46,7 @@
"bundlesize": "0.18.1",
"clean-webpack-plugin": "3.0.0",
"cssjanus": "1.3.1",
"eslint-config-wikimedia": "0.25.1",
"eslint-config-wikimedia": "0.26.0",
"eslint-plugin-no-jquery": "2.7.0",
"expose-loader": "4.1.0",
"grunt-banana-checker": "0.11.1",

Binary file not shown.

View file

@ -235,8 +235,8 @@ export function linkDwell( title, el, measures, gateway, generateToken, type ) {
// The `enabled` flags allow to disable individual popup types while still showing the
// footer link. This comes from the boot() action (called `initiallyEnabled` there) and
// the preview() reducer.
// If the preview type has not been enabled, we ignore it as it cannot be disabled (currently)
// by the UI.
// If the preview type has not been enabled, we ignore it as it cannot be disabled
// (currently) by the UI.
if ( isEnabled && isNewInteraction() ) {
return dispatch( fetch( gateway, title, el, token, type ) );
}

View file

@ -47,9 +47,10 @@ export default function linkTitle() {
restoreTitleAttr( oldLink );
// FIXME: This will not work on anything other than 'reference' or 'preview' types as
// mw.popups.register does not register the previewType as a key in newState.preview.enabled
// This is not a problem at time of writing (November 2022) but will become a problem if we
// introduce custom preview types that must remove the title attribute.
// mw.popups.register does not register the previewType as a key in
// newState.preview.enabled
// This is not a problem at time of writing (November 2022) but will become a problem if
// we introduce custom preview types that must remove the title attribute.
if ( newState.preview.enabled[ newState.preview.previewType ] ) {
destroyTitleAttr( newState.preview.activeLink );
}

View file

@ -15,7 +15,8 @@ export default function settings( boundActions, render ) {
}
if (
settingsObj &&
Object.keys( oldState.settings.previewTypesEnabled ).length !== Object.keys( newState.settings.previewTypesEnabled ).length
Object.keys( oldState.settings.previewTypesEnabled ).length !==
Object.keys( newState.settings.previewTypesEnabled ).length
) {
// the number of settings changed so force it to be repainted.
settingsObj.refresh( newState.settings.previewTypesEnabled );

View file

@ -22,7 +22,7 @@ export default function syncUserSettings( userSettings ) {
return ( oldState, newState ) => {
Object.keys( newState.preview.enabled ).forEach( ( key ) => {
syncIfChanged(
oldState, newState, `preview.enabled.${key}`,
oldState, newState, `preview.enabled.${ key }`,
( value ) => {
userSettings.storePreviewTypeEnabled( key, value );
}

View file

@ -72,7 +72,7 @@ export default function createContainer() {
*/
get( name ) {
if ( !this.has( name ) ) {
throw new Error( `The service "${name}" hasn't been defined.` );
throw new Error( `The service "${ name }" hasn't been defined.` );
}
const factory = factories[ name ];

View file

@ -28,7 +28,7 @@ exports.getEditCountBucket = function getEditCountBucket( count ) {
bucket = '1000+';
}
return `${bucket} edits`;
return `${ bucket } edits`;
};
/**
@ -58,5 +58,5 @@ exports.getPreviewCountBucket = function getPreviewCountBucket( count ) {
bucket = '21+';
}
return bucket !== undefined ? ( `${bucket} previews` ) : 'unknown';
return bucket !== undefined ? ( `${ bucket } previews` ) : 'unknown';
};

View file

@ -36,12 +36,13 @@ export function formatPlainTextExtract( plainTextExtract, title ) {
*/
function makeTitleInExtractBold( extract, title ) {
const elements = [],
boldIdentifier = `<bi-${Math.random()}>`,
snip = `<snip-${Math.random()}>`;
boldIdentifier = `<bi-${ Math.random() }>`,
snip = `<snip-${ Math.random() }>`;
title = title.replace( /\s+/g, ' ' ).trim(); // Remove extra white spaces
const escapedTitle = mw.util.escapeRegExp( title );
const regExp = new RegExp( `(^|\\s)(${escapedTitle})(|$)`, 'i' );
// eslint-disable-next-line security/detect-non-literal-regexp
const regExp = new RegExp( `(^|\\s)(${ escapedTitle })(|$)`, 'i' );
// Remove text in parentheses along with the parentheses
extract = extract.replace( /\s+/, ' ' ); // Remove extra white spaces
@ -51,7 +52,7 @@ function makeTitleInExtractBold( extract, title ) {
// Also, the title is escaped of RegExp elements thus can't have "*"
extract = extract.replace(
regExp,
`$1${snip}${boldIdentifier}$2${snip}$3`
`$1${ snip }${ boldIdentifier }$2${ snip }$3`
);
extract = extract.split( snip );

View file

@ -15,14 +15,14 @@ export default function createReferenceGateway() {
* @return {HTMLElement}
*/
function scrapeReferenceText( id ) {
const idSelector = `#${CSS.escape( id )}`;
const idSelector = `#${ CSS.escape( id ) }`;
/**
* Same alternative selectors with and without mw- as in the RESTbased endpoint.
*
* @see https://phabricator.wikimedia.org/diffusion/GMOA/browse/master/lib/transformations/references/structureReferenceListContent.js$138
*/
return document.querySelector( `${idSelector} .mw-reference-text, ${idSelector} .reference-text` );
return document.querySelector( `${ idSelector } .mw-reference-text, ${ idSelector } .reference-text` );
}
/**
@ -75,7 +75,7 @@ export default function createReferenceGateway() {
}
const model = {
url: `#${id}`,
url: `#${ id }`,
extract: referenceNode.innerHTML,
type: previewTypes.TYPE_REFERENCE,
referenceType: scrapeReferenceType( referenceNode ),

View file

@ -41,7 +41,7 @@ export default function createRESTBaseGateway( ajax, config, extractParser ) {
return ajax( {
url: endpoint + encodeURIComponent( title ),
headers: {
Accept: `application/json; charset=utf-8; profile="${RESTBASE_PROFILE}"`,
Accept: `application/json; charset=utf-8; profile="${ RESTBASE_PROFILE }"`,
'Accept-Language': config.acceptLanguage
}
} );
@ -156,7 +156,7 @@ function generateThumbnailData( thumbnail, original, thumbSize ) {
return originalIsSafe && original;
}
parts[ parts.length - 1 ] = `${width}px-${filename}`;
parts[ parts.length - 1 ] = `${ width }px-${ filename }`;
return {
source: parts.join( '/' ),

View file

@ -154,7 +154,7 @@ function handleDOMEventIfEligible( handler ) {
// If the closest method is not defined, let's return early and
// understand this better by logging an error. (T340081)
if ( target && !target.closest ) {
const err = new Error( `T340081: Unexpected DOM element ${target.tagName} with nodeType ${target.nodeType}` );
const err = new Error( `T340081: Unexpected DOM element ${ target.tagName } with nodeType ${ target.nodeType }` );
mw.errorLogger.logError( err, 'error.web-team' );
return;
}
@ -245,7 +245,7 @@ function handleDOMEventIfEligible( handler ) {
// Register default preview type
mw.popups.register( {
type: previewTypes.TYPE_PAGE,
selector: `#mw-content-text a[href][title]:not(${excludedLinksSelector})`,
selector: `#mw-content-text a[href][title]:not(${ excludedLinksSelector })`,
delay: FETCH_COMPLETE_TARGET_DELAY - FETCH_START_DELAY,
gateway: pagePreviewGateway,
renderFn: createPagePreview,

View file

@ -9,7 +9,7 @@ import { previewTypes } from '../preview/model';
* @return {boolean} whether the preview type supports being disabled/enabled.
*/
function canShowSettingForPreviewType( type ) {
return mw.message( `popups-settings-option-${type}` ).exists();
return mw.message( `popups-settings-option-${ type }` ).exists();
}
/**
@ -19,7 +19,8 @@ function canShowSettingForPreviewType( type ) {
* @param {Redux.Store} store Popups store
* @param {Function} registerModel allows extensions to register custom preview handlers.
* @param {Function} registerPreviewUI allows extensions to register custom preview renderers.
* @param {Function} registerGatewayForPreviewType allows extensions to register gateways for preview types.
* @param {Function} registerGatewayForPreviewType allows extensions to register gateways for
* preview types.
* @param {Function} registerSetting
* @param {UserSettings} userSettings
* @return {Object} external Popups interface
@ -40,16 +41,19 @@ export default function createMwPopups( store, registerModel, registerPreviewUI,
*
* @typedef {Object} PopupSubtype
* @property {string} type A unique string for identifying the subtype of a page preview
* @property {function(ext.popups.PreviewModel): ext.popups.Preview}[renderFn] How the custom preview type will render the preview.
* If not provided default renderer is used.
* @property {function(ext.popups.PreviewModel): ext.popups.Preview}[renderFn] How the
* custom preview type will render the preview. If not provided default renderer is used.
*
* @typedef {Object} PopupModule
* @property {string} type A unique string for identifying the type of page preview
* @property {string} selector A CSS selector which identifies elements that will display this type of page preview
* @property {string} selector A CSS selector which identifies elements that will display
* this type of page preview
* @property {Gateway} gateway A Gateway for obtaining the preview data.
* @property {function(ext.popups.PreviewModel): ext.popups.Preview}[renderFn] How the custom preview type will render the preview.
* @property {function(ext.popups.PreviewModel): ext.popups.Preview}[renderFn] How the
* custom preview type will render the preview.
* If not provided default renderer is used.
* @property {PopupSubtype[]} subTypes this is for registering types that are subsets of the current type e.g. share the same selector.
* @property {PopupSubtype[]} subTypes this is for registering types that are subsets of
* the current type e.g. share the same selector.
* @property {number} [delay] optional delay between hovering and displaying preview.
* If not defined, delay will be zero.
*/
@ -75,7 +79,7 @@ export default function createMwPopups( store, registerModel, registerPreviewUI,
doNotRequireSummary } = module;
if ( !type || !selector || !gateway ) {
throw new Error(
`Registration of Popups custom preview type "${type}" failed: You must specify a type, a selector, and a gateway.`
`Registration of Popups custom preview type "${ type }" failed: You must specify a type, a selector, and a gateway.`
);
}
registerModel( type, selector, delay );
@ -86,13 +90,17 @@ export default function createMwPopups( store, registerModel, registerPreviewUI,
registerSetting( type, userSettings.isPreviewTypeEnabled( type ) );
} else {
mw.log.warn(
`[Popups] No setting for ${type} registered.
Please create message with key "popups-settings-option-${type}" if this is a mistake.`
`[Popups] No setting for ${ type } registered.
Please create message with key "popups-settings-option-${ type }" if this is a mistake.`
);
}
if ( subTypes ) {
subTypes.forEach( function ( subTypePreview ) {
registerPreviewUI( subTypePreview.type, subTypePreview.renderFn, subTypePreview.doNotRequireSummary );
registerPreviewUI(
subTypePreview.type,
subTypePreview.renderFn,
subTypePreview.doNotRequireSummary
);
} );
}
// Run initialization function if provided.

View file

@ -144,7 +144,9 @@ const registeredPreviewTypes = [];
* @return {string|null} One of the previewTypes.TYPE_ constants
*/
export function getPreviewType( el ) {
const candidates = registeredPreviewTypes.filter( ( type ) => elementMatchesSelector( el, type.selector ) );
const candidates = registeredPreviewTypes.filter(
( type ) => elementMatchesSelector( el, type.selector )
);
// If the filter returned some possibilities, use the last registered one.
if ( candidates.length > 0 ) {

View file

@ -44,6 +44,7 @@ export function getTitle( href, config ) {
// No query params (pretty URL)
if ( !queryLength ) {
const pattern = mw.util.escapeRegExp( config.get( 'wgArticlePath' ) ).replace( '\\$1', '([^?#]+)' ),
// eslint-disable-next-line security/detect-non-literal-regexp
matches = new RegExp( pattern ).exec( linkHref.path );
// We can't be sure decodeURIComponent() is able to parse every possible match
@ -57,7 +58,7 @@ export function getTitle( href, config ) {
title = linkHref.query.title;
}
return title ? `${title}${linkHref.fragment ? `#${linkHref.fragment}` : ''}` : undefined;
return title ? `${ title }${ linkHref.fragment ? `#${ linkHref.fragment }` : '' }` : undefined;
}
/**

View file

@ -182,7 +182,7 @@ function supportsCSSClipPath() {
return window.CSS &&
typeof CSS.supports === 'function' &&
CSS.supports( 'clip-path', 'polygon(1px 1px)' );
/* eslint-enable compat/compat */
}
/**
@ -547,7 +547,7 @@ export function layoutPreview(
thumbnail.height < predefinedLandscapeImageHeight && !supportsCSSClipPath()
) {
const popupExtract = popup.querySelector( '.mwe-popups-extract' );
popupExtract.style.marginTop = `${( thumbnail.height - pointerSpaceSize )}px`;
popupExtract.style.marginTop = `${ ( thumbnail.height - pointerSpaceSize ) }px`;
}
// The following classes are used here:
@ -562,9 +562,9 @@ export function layoutPreview(
// * mwe-popups-no-image-pointer
popup.classList.add.apply( popup.classList, classes );
popup.style.left = `${layout.offset.left}px`;
popup.style.top = flippedY ? 'auto' : `${layout.offset.top}px`;
popup.style.bottom = flippedY ? `${windowHeight - layout.offset.top}px` : 'auto';
popup.style.left = `${ layout.offset.left }px`;
popup.style.top = flippedY ? 'auto' : `${ layout.offset.top }px`;
popup.style.bottom = flippedY ? `${ windowHeight - layout.offset.top }px` : 'auto';
if ( hasThumbnail && !supportsCSSClipPath() ) {
setThumbnailClipPath( preview, layout );
@ -610,11 +610,11 @@ export function setThumbnailClipPath(
const mask = document.getElementById( maskID );
mask.setAttribute(
'transform',
`matrix(${matrix.scaleX} 0 0 1 ${matrix.translateX} 0)`
`matrix(${ matrix.scaleX } 0 0 1 ${ matrix.translateX } 0)`
);
el.querySelector( 'image' )
.setAttribute( 'clip-path', `url(#${maskID})` );
.setAttribute( 'clip-path', `url(#${ maskID })` );
}
}

View file

@ -17,11 +17,11 @@ export function createSettingsDialog( previewTypesEnabled ) {
// This can produce:
// * popups-settings-option-preview
// * popups-settings-option-reference
name: mw.msg( `popups-settings-option-${id}` ),
name: mw.msg( `popups-settings-option-${ id }` ),
// This can produce:
// * popups-settings-option-preview-description
// * popups-settings-option-reference-description
description: mw.msg( `popups-settings-option-${id}-description` ),
description: mw.msg( `popups-settings-option-${ id }-description` ),
isChecked: previewTypesEnabled[ id ]
}
) );

View file

@ -120,7 +120,7 @@ export default function createSettingsDialogRenderer() {
*/
setEnabled( enabled ) {
Object.keys( enabled ).forEach( ( type ) => {
const node = dialog.querySelector( `#mwe-popups-settings-${type}` );
const node = dialog.querySelector( `#mwe-popups-settings-${ type }` );
if ( node ) {
node.checked = enabled[ type ];
}

View file

@ -78,5 +78,5 @@ export { defaultExtractWidth }; // for testing
* used for the extract
*/
export function getExtractWidth( thumbnail ) {
return thumbnail && thumbnail.isNarrow ? `${defaultExtractWidth + thumbnail.offset}px` : '';
return thumbnail && thumbnail.isNarrow ? `${ defaultExtractWidth + thumbnail.offset }px` : '';
}

View file

@ -20,7 +20,7 @@ export function renderPopup( type, container ) {
// * mwe-popups-type-unknown
// * mwe-popups-type-generic
// * mwe-popups-type-disambiguation
element.className = `mwe-popups mwe-popups-type-${type}`;
element.className = `mwe-popups mwe-popups-type-${ type }`;
container.className = 'mwe-popups-container';
element.appendChild( container );
return element;

View file

@ -38,7 +38,7 @@ export function renderPreview(
// * popups-icon--preview-unknown
// * popups-icon--preview-generic
// * popups-icon--preview-disambiguation
popup.querySelector( '.popups-icon' ).classList.add( `popups-icon--preview-${model.type}` );
popup.querySelector( '.popups-icon' ).classList.add( `popups-icon--preview-${ model.type }` );
popup.querySelector( '.mwe-popups-extract' ).setAttribute( 'href', model.url );
const messageElement = popup.querySelector( '.mwe-popups-message' );
if ( message ) {

View file

@ -49,7 +49,7 @@ export function renderReferencePreview(
// * popups-refpreview-news
// * popups-refpreview-note
// * popups-refpreview-web
let titleMsg = mw.message( `popups-refpreview-${type}` );
let titleMsg = mw.message( `popups-refpreview-${ type }` );
if ( !titleMsg.exists() ) {
titleMsg = mw.message( 'popups-refpreview-reference' );
}
@ -64,7 +64,7 @@ export function renderReferencePreview(
// * popups-icon--reference-note
// * popups-icon--reference-web
el.querySelector( '.mwe-popups-title .popups-icon' )
.classList.add( `popups-icon--reference-${type}` );
.classList.add( `popups-icon--reference-${ type }` );
el.querySelector( '.mw-parser-output' )
.innerHTML = model.extract;
@ -74,7 +74,7 @@ export function renderReferencePreview(
( a ) => {
a.target = '_blank';
// Don't let the external site access and possibly manipulate window.opener.location
a.rel = `${a.rel ? `${a.rel} ` : ''}noopener`;
a.rel = `${ a.rel ? `${ a.rel } ` : '' }noopener`;
}
);
@ -160,8 +160,8 @@ export function renderReferencePreview(
hasVerticalScroll = element.scrollHeight > element.clientHeight,
scrollbarWidth = element.offsetWidth - element.clientWidth;
const fade = extract.querySelector( '.mwe-popups-fade' );
fade.style.bottom = hasHorizontalScroll ? `${scrollbarHeight}px` : 0;
fade.style.right = hasVerticalScroll ? `${scrollbarWidth}px` : 0;
fade.style.bottom = hasHorizontalScroll ? `${ scrollbarHeight }px` : 0;
fade.style.right = hasVerticalScroll ? `${ scrollbarWidth }px` : 0;
element.isScrolling = !scrolledToBottom;
extract.classList.toggle( 'mwe-popups-fade-out', element.isScrolling );

View file

@ -60,36 +60,36 @@ export function renderSettingsDialog( model ) {
<div>
<button class='cdx-button cdx-button--weight-quiet cdx-button--icon-only'>
<span class='popups-icon popups-icon--close close'></span>
<span>${closeLabel}</span>
<span>${ closeLabel }</span>
</button>
</div>
<h1>${heading}</h1>
<h1>${ heading }</h1>
<div>
<button class='save cdx-button cdx-button--weight-primary cdx-button--action-progressive'>${saveLabel}</button>
<button class='okay cdx-button cdx-button--weight-primary cdx-button--action-progressive' style='display:none;'>${okLabel}</button>
<button class='save cdx-button cdx-button--weight-primary cdx-button--action-progressive'>${ saveLabel }</button>
<button class='okay cdx-button cdx-button--weight-primary cdx-button--action-progressive' style='display:none;'>${ okLabel }</button>
</div>
</header>
<main id='mwe-popups-settings-form'>
<form>
${choices.map( ( { id, name, description, isChecked } ) => `
${ choices.map( ( { id, name, description, isChecked } ) => `
<p class="cdx-checkbox">
<input
${isChecked ? 'checked' : ''}
value='${id}'
${ isChecked ? 'checked' : '' }
value='${ id }'
type='checkbox'
id='mwe-popups-settings-${id}'
id='mwe-popups-settings-${ id }'
class='cdx-checkbox__input'>
<span class="cdx-checkbox__icon">&nbsp;</span>
<label class="cdx-checkbox__label" for='mwe-popups-settings-${id}'>
<span>${name}</span>
${description}
<label class="cdx-checkbox__label" for='mwe-popups-settings-${ id }'>
<span>${ name }</span>
${ description }
</label>
</p>` ).join( '' )}
</p>` ).join( '' ) }
</form>
</main>
<div class='mwe-popups-settings-help' style='display:none;'>
<div class="popups-icon popups-icon--footer"></div>
<p>${helpText}</p>
<p>${ helpText }</p>
</div>
</section>
`.trim();

View file

@ -130,7 +130,7 @@ function createThumbnailImg( url ) {
* Sets multiple attributes on a node.
*
* @param {HTMLElement} node
* @param {Record<String, String>} attrs
* @param {Record<string, string>} attrs
*/
const addAttributes = ( node, attrs ) => {
Object.keys( attrs ).forEach( ( key ) => {

View file

@ -41,9 +41,11 @@ export default function createUserSettings( storage ) {
*
* @method
* @param {string} previewType
*
* @return {boolean}
*/
isPreviewTypeEnabled( previewType ) {
const storageKey = `mwe-popups-${previewType}-enabled`;
const storageKey = `mwe-popups-${ previewType }-enabled`;
const value = storage.get( storageKey );
return value === null;
},
@ -63,7 +65,7 @@ export default function createUserSettings( storage ) {
action: enabled ? 'anonymousEnabled' : 'anonymousDisabled'
} );
}
const storageKey = `mwe-popups-${previewType}-enabled`;
const storageKey = `mwe-popups-${ previewType }-enabled`;
if ( enabled ) {
storage.remove( storageKey );
} else {

View file

@ -6,9 +6,6 @@
],
"rules": {
"no-jquery/no-class-state": "off",
"no-jquery/no-deferred": "warn",
"no-jquery/no-when": "warn",
"no-jquery/no-extend": "warn",
"es-x/no-hashbang": "warn"
}
}

View file

@ -9,7 +9,9 @@ QUnit.module( 'ext.popups/changeListeners/footerLink @integration', {
// Stub internal usage of mw.message
mw.message = ( str ) =>
( {
text() { return str; }
text() {
return str;
}
} );
boundActions.showSettings = this.showSettingsSpy = this.sandbox.spy();

View file

@ -34,7 +34,9 @@ QUnit.module( 'ext.popups/pageviews', {
// Stub internal usage of mw.Title.newFromText
mw.Title.newFromText = ( str ) => {
return {
getPrefixedDb: () => { return str; }
getPrefixedDb: () => {
return str;
}
};
};
}
@ -67,7 +69,7 @@ QUnit.test( 'it should log the queued event', function ( assert ) {
} );
QUnit.test( 'it should not log something that is not a pageview', function ( assert ) {
const noPageviewState = $.extend( {}, newState );
const noPageviewState = Object.assign( {}, newState );
delete noPageviewState.pageviews.pageview;
this.changeListener( undefined, newState );

View file

@ -39,7 +39,9 @@ QUnit.test( '#get', function ( assert ) {
// ---
assert.throws(
() => { this.container.get( 'bar' ); },
() => {
this.container.get( 'bar' );
},
/The service "bar" hasn't been defined./,
'The container throws an error when no factory exists.'
);

View file

@ -26,7 +26,7 @@ QUnit.test( '#getEditCountBucket', ( assert ) => {
assert.strictEqual(
bucket,
cases[ i ][ 1 ],
`Edit count bucket is "${bucket}" when edit count is ${count}.`
`Edit count bucket is "${ bucket }" when edit count is ${ count }.`
);
}
} );
@ -57,7 +57,7 @@ QUnit.test( '#getPreviewCountBucket', ( assert ) => {
assert.strictEqual(
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

@ -65,7 +65,7 @@ QUnit.module( 'ext.popups/gateway/mediawiki', {
} );
QUnit.test( 'MediaWiki API gateway is called with correct arguments', function ( assert ) {
const config = $.extend( {}, DEFAULT_CONSTANTS, {
const config = Object.assign( {}, DEFAULT_CONSTANTS, {
acceptLanguage: 'pl'
} );
const spy = this.sandbox.spy(),
@ -136,8 +136,10 @@ QUnit.test( 'MediaWiki API gateway is correctly extracting the page data from th
errorCases.forEach( ( data, i ) => {
assert.throws(
() => { gateway.extractPageFromResponse( data ); },
`Case ${i}: the gateway throws an error.`
() => {
gateway.extractPageFromResponse( data );
},
`Case ${ i }: the gateway throws an error.`
);
} );
@ -145,7 +147,7 @@ QUnit.test( 'MediaWiki API gateway is correctly extracting the page data from th
assert.deepEqual(
gateway.extractPageFromResponse( data[ 0 ] ),
data[ 1 ],
`Case ${i}: the gateway extracts the response.`
`Case ${ i }: the gateway extracts the response.`
);
} );
} );
@ -237,7 +239,9 @@ QUnit.test( 'MediaWiki API gateway is abortable', function ( assert ) {
deferred = $.Deferred(),
api = {
get: this.sandbox.stub().returns(
deferred.promise( { abort() { deferred.reject( 'http' ); } } )
deferred.promise( { abort() {
deferred.reject( 'http' );
} } )
)
},
gateway = createMediaWikiApiGateway( api, DEFAULT_CONSTANTS );

View file

@ -6,9 +6,9 @@ QUnit.module( 'ext.popups/gateway/reference', {
global.CSS = {
escape: ( str ) => $.escapeSelector( str )
};
mw.msg = ( key ) => `<${key}>`;
mw.msg = ( key ) => `<${ key }>`;
mw.message = ( key ) => {
return { exists: () => !key.endsWith( 'generic' ), text: () => `<${key}>` };
return { exists: () => !key.endsWith( 'generic' ), text: () => `<${ key }>` };
};
this.$sourceElement = $( '<a>' ).appendTo(

View file

@ -170,13 +170,13 @@ const DEFAULT_CONSTANTS = {
);
function provideParsedExtract( page ) {
return `!${page.extract}!`;
return `!${ page.extract }!`;
}
QUnit.module( 'gateway/rest', {
beforeEach() {
mw.Title = function ( title ) {
this.getUrl = () => `url/${title}`;
this.getUrl = () => `url/${ title }`;
};
},
afterEach() {
@ -185,7 +185,7 @@ QUnit.module( 'gateway/rest', {
} );
QUnit.test( 'RESTBase gateway is called with correct arguments', function ( assert ) {
const config = $.extend( {}, DEFAULT_CONSTANTS, {
const config = Object.assign( {}, DEFAULT_CONSTANTS, {
acceptLanguage: 'pl'
} );

View file

@ -5,7 +5,9 @@ QUnit.module( 'ext.popups.preview.settingsBehavior', {
beforeEach() {
function newFromText( title ) {
return {
getUrl() { return `url/${title}`; }
getUrl() {
return `url/${ title }`;
}
};
}

View file

@ -16,6 +16,7 @@ require( '@babel/register' )( {
} );
require.extensions[ '.svg' ] = ( module, filename ) => {
// eslint-disable-next-line security/detect-non-literal-fs-filename
const svg = fs.readFileSync( filename, { encoding: 'utf8' } );
// eslint-disable-next-line no-underscore-dangle
module._compile( svgInlineLoader( svg ), filename );

View file

@ -72,7 +72,7 @@ export function createStubTitle( namespace, name, fragment = null ) {
return {
namespace,
getPrefixedDb() {
return ( namespace ? `Namespace ${namespace}:` : '' ) + name;
return ( namespace ? `Namespace ${ namespace }:` : '' ) + name;
},
getMainText() {
return name;
@ -81,7 +81,7 @@ export function createStubTitle( namespace, name, fragment = null ) {
return namespace;
},
getUrl() {
return `/wiki/${this.getPrefixedDb()}`;
return `/wiki/${ this.getPrefixedDb() }`;
},
getFragment() {
return fragment;

View file

@ -41,9 +41,9 @@ QUnit.module( 'ext.popups#renderer', {
beforeEach() {
this.sandbox.stub( constants.default, 'BRACKETED_DEVICE_PIXEL_RATIO' ).value( 1 );
mw.msg = ( key ) => `<${key}>`;
mw.msg = ( key ) => `<${ key }>`;
mw.message = ( key ) => {
return { exists: () => !key.endsWith( 'generic' ), text: () => `<${key}>` };
return { exists: () => !key.endsWith( 'generic' ), text: () => `<${ key }>` };
};
mw.html = {
@ -77,7 +77,7 @@ QUnit.test( 'getExtractWidth', ( assert ) => {
],
[ {
isNarrow: true, offset: 10
}, `${pagePreview.defaultExtractWidth + 10}px` ],
}, `${ pagePreview.defaultExtractWidth + 10 }px` ],
[ {
// Fall back to css stylesheet for non-narrow thumbs.
isNarrow: false, offset: 100
@ -88,7 +88,7 @@ QUnit.test( 'getExtractWidth', ( assert ) => {
assert.strictEqual(
pagePreview.getExtractWidth( case_[ 0 ] ),
case_[ 1 ],
`Case ${i}: the expected extract width matches.`
`Case ${ i }: the expected extract width matches.`
);
} );
} );
@ -106,9 +106,9 @@ QUnit.test( 'createPointerMasks', ( assert ) => {
cases.forEach( ( case_, i ) => {
assert.strictEqual(
$container.find( `${case_[ 0 ]} path` ).attr( 'd' ),
$container.find( `${ case_[ 0 ] } path` ).attr( 'd' ),
case_[ 1 ],
`Case ${i}: the SVG's polygons match.`
`Case ${ i }: the SVG's polygons match.`
);
} );
} );
@ -637,7 +637,7 @@ QUnit.test( '#createLayout - portrait preview, mouse event, link is on the top l
flippedY: false,
dir
},
`Case ${i}: the layout is correct.`
`Case ${ i }: the layout is correct.`
);
} );
} );
@ -685,7 +685,7 @@ QUnit.test( '#createLayout - tall preview, mouse event, link is on the bottom ce
flippedY: true,
dir
},
`Case ${i}: the layout is correct. Y is flipped.`
`Case ${ i }: the layout is correct. Y is flipped.`
);
} );
} );
@ -730,7 +730,7 @@ QUnit.test( '#createLayout - empty preview, keyboard event, link is on the cente
flippedY: true,
dir
},
`Case ${i}: the layout is correct. Both X and Y are flipped.`
`Case ${ i }: the layout is correct. Both X and Y are flipped.`
);
} );
} );
@ -778,7 +778,7 @@ QUnit.test( '#createLayout - empty preview, mouse event, popup pointer is in the
flippedY: false,
dir
},
`Case ${i}: the layout is correct.`
`Case ${ i }: the layout is correct.`
);
} );
} );
@ -1045,12 +1045,12 @@ QUnit.test( '#layoutPreview - no thumbnail', ( assert ) => {
assert.strictEqual(
$( preview.el ).css( 'top' ),
`${layout.offset.top}px`,
`${ layout.offset.top }px`,
'Top is correct.'
);
assert.strictEqual(
$( preview.el ).css( 'left' ),
`${layout.offset.left}px`,
`${ layout.offset.left }px`,
'Left is correct.'
);
} );
@ -1080,12 +1080,12 @@ QUnit.test( '#layoutPreview - tall preview, flipped X, has thumbnail', function
);
assert.strictEqual(
$( preview.el ).css( 'top' ),
`${layout.offset.top}px`,
`${ layout.offset.top }px`,
'Top is correct.'
);
assert.strictEqual(
$( preview.el ).css( 'left' ),
`${layout.offset.left}px`,
`${ layout.offset.left }px`,
'Left is correct.'
);
assert.false(
@ -1124,17 +1124,17 @@ QUnit.test( '#layoutPreview - portrait preview, flipped X, has thumbnail, small
);
assert.strictEqual(
$( preview.el ).css( 'top' ),
`${layout.offset.top}px`,
`${ layout.offset.top }px`,
'Top is correct.'
);
assert.strictEqual(
$( preview.el ).css( 'left' ),
`${layout.offset.left}px`,
`${ layout.offset.left }px`,
'Left is correct.'
);
assert.strictEqual(
$( preview.el ).find( '.mwe-popups-extract' ).css( 'margin-top' ),
`${199 - 8}px`, // thumb height - pointer size
`${ 199 - 8 }px`, // thumb height - pointer size
'Extract margin top has been set when preview height is smaller than the predefined landscape image height.'
);
assert.strictEqual(
@ -1169,12 +1169,12 @@ QUnit.test( '#layoutPreview - portrait preview, flipped X, has thumbnail, big he
);
assert.strictEqual(
$( preview.el ).css( 'top' ),
`${layout.offset.top}px`,
`${ layout.offset.top }px`,
'Top is correct.'
);
assert.strictEqual(
$( preview.el ).css( 'left' ),
`${layout.offset.left}px`,
`${ layout.offset.left }px`,
'Left is correct.'
);
assert.strictEqual(
@ -1329,13 +1329,13 @@ QUnit.test( '#layoutPreview - tall preview, has thumbnail, flipped Y', ( assert
assert.strictEqual(
$( preview.el ).css( 'bottom' ),
`${windowHeight - layout.offset.top}px`,
`${ windowHeight - layout.offset.top }px`,
'Bottom is correct.'
);
assert.strictEqual(
$( preview.el ).css( 'left' ),
`${layout.offset.left}px`,
`${ layout.offset.left }px`,
'Left is correct.'
);
assert.strictEqual(
@ -1370,12 +1370,12 @@ QUnit.test( '#layoutPreview - tall preview, has thumbnail, flipped X and Y', fun
);
assert.strictEqual(
$( preview.el ).css( 'left' ),
`${layout.offset.left}px`,
`${ layout.offset.left }px`,
'Left is correct.'
);
assert.strictEqual(
$( preview.el ).css( 'bottom' ),
`${windowHeight - layout.offset.top}px`,
`${ windowHeight - layout.offset.top }px`,
'Bottom is correct.'
);
assert.strictEqual(
@ -1406,12 +1406,12 @@ QUnit.test( '#layoutPreview - portrait preview, has thumbnail, flipped X and Y',
);
assert.strictEqual(
$( preview.el ).css( 'left' ),
`${layout.offset.left}px`,
`${ layout.offset.left }px`,
'Left is correct.'
);
assert.strictEqual(
$( preview.el ).css( 'bottom' ),
`${windowHeight - layout.offset.top}px`,
`${ windowHeight - layout.offset.top }px`,
'Bottom is correct.'
);
assert.strictEqual(
@ -1458,7 +1458,7 @@ QUnit.test( '#setThumbnailClipPath', function ( assert ) {
assert.strictEqual(
clipPath.getAttribute( 'transform' ),
expected,
`Transform is correct for: { isTall: ${isTall}, dir: ${dir} }.`
`Transform is correct for: { isTall: ${ isTall }, dir: ${ dir } }.`
);
} );
} );
@ -1478,7 +1478,7 @@ QUnit.test( '#getThumbnailClipPathID', ( assert ) => {
assert.strictEqual(
renderer.getThumbnailClipPathID( isTall, flippedY, flippedX ),
expected,
`Correct element ID is returned for: { flippedY: ${flippedY}, flippedX: ${flippedX}, isTall: ${isTall} }.`
`Correct element ID is returned for: { flippedY: ${ flippedY }, flippedX: ${ flippedX }, isTall: ${ isTall } }.`
);
} );
} );

View file

@ -88,42 +88,42 @@ QUnit.test( 'createThumbnail - tall image element', ( assert ) => {
assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'x' ),
case_.expectedX,
`Image element x coordinate is correct. ${case_.message}`
`Image element x coordinate is correct. ${ case_.message }`
);
assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'y' ),
case_.expectedY,
`Image element y coordinate is correct. ${case_.message}`
`Image element y coordinate is correct. ${ case_.message }`
);
assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'width' ),
case_.width,
`Image element width is correct. ${case_.message}`
`Image element width is correct. ${ case_.message }`
);
assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'height' ),
case_.height,
`Image element height is correct. ${case_.message}`
`Image element height is correct. ${ case_.message }`
);
assert.strictEqual(
+$( thumbnail.el ).attr( 'width' ),
case_.expectedSVGWidth,
`Image SVG width is correct. ${case_.message}`
`Image SVG width is correct. ${ case_.message }`
);
assert.strictEqual(
+$( thumbnail.el ).attr( 'height' ),
case_.expectedSVGHeight,
`Image SVG height is correct. ${case_.message}`
`Image SVG height is correct. ${ case_.message }`
);
assert.strictEqual(
thumbnail.isNarrow,
case_.expectedIsNarrow,
`Image isNarrow is correct. ${case_.message}`
`Image isNarrow is correct. ${ case_.message }`
);
assert.strictEqual(
thumbnail.offset,
case_.expectedOffset,
`Image offset is correct. ${case_.message}`
`Image offset is correct. ${ case_.message }`
);
} );
} );
@ -210,32 +210,32 @@ QUnit.test( 'createThumbnail - landscape image element', ( assert ) => {
assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'x' ),
case_.expectedX,
`Image x coordinate is correct. ${case_.message}`
`Image x coordinate is correct. ${ case_.message }`
);
assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'y' ),
case_.expectedY,
`Image y coordinate is correct. ${case_.message}`
`Image y coordinate is correct. ${ case_.message }`
);
assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'width' ),
case_.width,
`Image element width is correct. ${case_.message}`
`Image element width is correct. ${ case_.message }`
);
assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'height' ),
case_.height,
`Image element height is correct. ${case_.message}`
`Image element height is correct. ${ case_.message }`
);
assert.strictEqual(
+$( thumbnail.el ).attr( 'width' ),
case_.expectedSVGWidth,
`Image SVG width is correct. ${case_.message}`
`Image SVG width is correct. ${ case_.message }`
);
assert.strictEqual(
+$( thumbnail.el ).attr( 'height' ),
case_.expectedSVGHeight,
`Image SVG height is correct. ${case_.message}`
`Image SVG height is correct. ${ case_.message }`
);
} );
} );

View file

@ -7,7 +7,6 @@
"mw": "readonly"
},
"rules": {
"no-jquery/no-jquery-constructor": "off",
"wdio/no-pause": "warn"
}
}

View file

@ -17,6 +17,7 @@ const
POPUPS_MODULE_NAME = 'ext.popups.main';
async function makePage( title, path ) {
// eslint-disable-next-line security/detect-non-literal-fs-filename
const content = fs.readFileSync( path, 'utf-8' );
const bot = await Api.bot();
await bot.edit( title, content );
@ -24,16 +25,16 @@ async function makePage( title, path ) {
class PopupsPage extends Page {
async setupPagePreviews() {
return browser.call( async () => {
const path = `${__dirname}/../fixtures/`;
await makePage( `${TEST_PAGE_POPUPS_TITLE} 2`, `${path}test_page_2.wikitext` );
await makePage( TEST_PAGE_POPUPS_TITLE, `${path}test_page.wikitext` );
const path = `${ __dirname }/../fixtures/`;
await makePage( `${ TEST_PAGE_POPUPS_TITLE } 2`, `${ path }test_page_2.wikitext` );
await makePage( TEST_PAGE_POPUPS_TITLE, `${ path }test_page.wikitext` );
} );
}
async setupReferencePreviews() {
return browser.call( async () => {
const path = `${__dirname}/../fixtures/`;
await makePage( TEST_REFERENCE_POPUPS_TITLE, `${path}test_page.wikitext` );
const path = `${ __dirname }/../fixtures/`;
await makePage( TEST_REFERENCE_POPUPS_TITLE, `${ path }test_page.wikitext` );
} );
}
@ -67,7 +68,7 @@ class PopupsPage extends Page {
}
async dwellReferenceLink( id ) {
await this.dwellLink( `#${id} a` );
await this.dwellLink( `#${ id } a` );
}
async dwellReferenceInceptionLink() {

View file

@ -95,9 +95,9 @@ module.exports = ( env, argv ) => ( {
// Rename source map extensions. Per T173491 files with a .map extension cannot be served
// from prod.
sourceMapFilename: `[file]${srcMapExt}`,
sourceMapFilename: `[file]${ srcMapExt }`,
devtoolModuleFilenameTemplate: `${PUBLIC_PATH}/[resource-path]`
devtoolModuleFilenameTemplate: `${ PUBLIC_PATH }/[resource-path]`
},
// Accurate source maps at the expense of build time. The source map is intentionally exposed