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" "wikimedia/server"
], ],
"rules": { "rules": {
"yml/block-sequence": "warn",
"yml/plain-scalar": "warn"
} }
} }

View file

@ -1,7 +1,6 @@
instrumentation: instrumentation:
include-all-sources: true include-all-sources: true
excludes: [ excludes:
"resources/dist/*", # Compiled assets - "resources/dist/*" # Compiled assets
"*.js", # Gruntfile.js and webpack.config.js - "*.js" # Gruntfile.js and webpack.config.js
"src/index.js", # Application entry point - 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", "bundlesize": "0.18.1",
"clean-webpack-plugin": "3.0.0", "clean-webpack-plugin": "3.0.0",
"cssjanus": "1.3.1", "cssjanus": "1.3.1",
"eslint-config-wikimedia": "0.25.1", "eslint-config-wikimedia": "0.26.0",
"eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-jquery": "2.7.0",
"expose-loader": "4.1.0", "expose-loader": "4.1.0",
"grunt-banana-checker": "0.11.1", "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 // 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 // footer link. This comes from the boot() action (called `initiallyEnabled` there) and
// the preview() reducer. // the preview() reducer.
// If the preview type has not been enabled, we ignore it as it cannot be disabled (currently) // If the preview type has not been enabled, we ignore it as it cannot be disabled
// by the UI. // (currently) by the UI.
if ( isEnabled && isNewInteraction() ) { if ( isEnabled && isNewInteraction() ) {
return dispatch( fetch( gateway, title, el, token, type ) ); return dispatch( fetch( gateway, title, el, token, type ) );
} }

View file

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

View file

@ -15,7 +15,8 @@ export default function settings( boundActions, render ) {
} }
if ( if (
settingsObj && 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. // the number of settings changed so force it to be repainted.
settingsObj.refresh( newState.settings.previewTypesEnabled ); settingsObj.refresh( newState.settings.previewTypesEnabled );

View file

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

View file

@ -72,7 +72,7 @@ export default function createContainer() {
*/ */
get( name ) { get( name ) {
if ( !this.has( 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 ]; const factory = factories[ name ];

View file

@ -28,7 +28,7 @@ exports.getEditCountBucket = function getEditCountBucket( count ) {
bucket = '1000+'; bucket = '1000+';
} }
return `${bucket} edits`; return `${ bucket } edits`;
}; };
/** /**
@ -58,5 +58,5 @@ exports.getPreviewCountBucket = function getPreviewCountBucket( count ) {
bucket = '21+'; 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 ) { function makeTitleInExtractBold( extract, title ) {
const elements = [], const elements = [],
boldIdentifier = `<bi-${Math.random()}>`, boldIdentifier = `<bi-${ Math.random() }>`,
snip = `<snip-${Math.random()}>`; snip = `<snip-${ Math.random() }>`;
title = title.replace( /\s+/g, ' ' ).trim(); // Remove extra white spaces title = title.replace( /\s+/g, ' ' ).trim(); // Remove extra white spaces
const escapedTitle = mw.util.escapeRegExp( title ); 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 // Remove text in parentheses along with the parentheses
extract = extract.replace( /\s+/, ' ' ); // Remove extra white spaces 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 "*" // Also, the title is escaped of RegExp elements thus can't have "*"
extract = extract.replace( extract = extract.replace(
regExp, regExp,
`$1${snip}${boldIdentifier}$2${snip}$3` `$1${ snip }${ boldIdentifier }$2${ snip }$3`
); );
extract = extract.split( snip ); extract = extract.split( snip );

View file

@ -15,14 +15,14 @@ export default function createReferenceGateway() {
* @return {HTMLElement} * @return {HTMLElement}
*/ */
function scrapeReferenceText( id ) { 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. * 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 * @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 = { const model = {
url: `#${id}`, url: `#${ id }`,
extract: referenceNode.innerHTML, extract: referenceNode.innerHTML,
type: previewTypes.TYPE_REFERENCE, type: previewTypes.TYPE_REFERENCE,
referenceType: scrapeReferenceType( referenceNode ), referenceType: scrapeReferenceType( referenceNode ),

View file

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

View file

@ -154,7 +154,7 @@ function handleDOMEventIfEligible( handler ) {
// If the closest method is not defined, let's return early and // If the closest method is not defined, let's return early and
// understand this better by logging an error. (T340081) // understand this better by logging an error. (T340081)
if ( target && !target.closest ) { 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' ); mw.errorLogger.logError( err, 'error.web-team' );
return; return;
} }
@ -245,7 +245,7 @@ function handleDOMEventIfEligible( handler ) {
// Register default preview type // Register default preview type
mw.popups.register( { mw.popups.register( {
type: previewTypes.TYPE_PAGE, 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, delay: FETCH_COMPLETE_TARGET_DELAY - FETCH_START_DELAY,
gateway: pagePreviewGateway, gateway: pagePreviewGateway,
renderFn: createPagePreview, renderFn: createPagePreview,

View file

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

View file

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

View file

@ -44,6 +44,7 @@ export function getTitle( href, config ) {
// No query params (pretty URL) // No query params (pretty URL)
if ( !queryLength ) { if ( !queryLength ) {
const pattern = mw.util.escapeRegExp( config.get( 'wgArticlePath' ) ).replace( '\\$1', '([^?#]+)' ), 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 ); matches = new RegExp( pattern ).exec( linkHref.path );
// We can't be sure decodeURIComponent() is able to parse every possible match // 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; 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 && return window.CSS &&
typeof CSS.supports === 'function' && typeof CSS.supports === 'function' &&
CSS.supports( 'clip-path', 'polygon(1px 1px)' ); CSS.supports( 'clip-path', 'polygon(1px 1px)' );
/* eslint-enable compat/compat */
} }
/** /**
@ -547,7 +547,7 @@ export function layoutPreview(
thumbnail.height < predefinedLandscapeImageHeight && !supportsCSSClipPath() thumbnail.height < predefinedLandscapeImageHeight && !supportsCSSClipPath()
) { ) {
const popupExtract = popup.querySelector( '.mwe-popups-extract' ); 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: // The following classes are used here:
@ -562,9 +562,9 @@ export function layoutPreview(
// * mwe-popups-no-image-pointer // * mwe-popups-no-image-pointer
popup.classList.add.apply( popup.classList, classes ); popup.classList.add.apply( popup.classList, classes );
popup.style.left = `${layout.offset.left}px`; popup.style.left = `${ layout.offset.left }px`;
popup.style.top = flippedY ? 'auto' : `${layout.offset.top}px`; popup.style.top = flippedY ? 'auto' : `${ layout.offset.top }px`;
popup.style.bottom = flippedY ? `${windowHeight - layout.offset.top}px` : 'auto'; popup.style.bottom = flippedY ? `${ windowHeight - layout.offset.top }px` : 'auto';
if ( hasThumbnail && !supportsCSSClipPath() ) { if ( hasThumbnail && !supportsCSSClipPath() ) {
setThumbnailClipPath( preview, layout ); setThumbnailClipPath( preview, layout );
@ -610,11 +610,11 @@ export function setThumbnailClipPath(
const mask = document.getElementById( maskID ); const mask = document.getElementById( maskID );
mask.setAttribute( mask.setAttribute(
'transform', 'transform',
`matrix(${matrix.scaleX} 0 0 1 ${matrix.translateX} 0)` `matrix(${ matrix.scaleX } 0 0 1 ${ matrix.translateX } 0)`
); );
el.querySelector( 'image' ) 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: // This can produce:
// * popups-settings-option-preview // * popups-settings-option-preview
// * popups-settings-option-reference // * popups-settings-option-reference
name: mw.msg( `popups-settings-option-${id}` ), name: mw.msg( `popups-settings-option-${ id }` ),
// This can produce: // This can produce:
// * popups-settings-option-preview-description // * popups-settings-option-preview-description
// * popups-settings-option-reference-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 ] isChecked: previewTypesEnabled[ id ]
} }
) ); ) );

View file

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

View file

@ -78,5 +78,5 @@ export { defaultExtractWidth }; // for testing
* used for the extract * used for the extract
*/ */
export function getExtractWidth( thumbnail ) { 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-unknown
// * mwe-popups-type-generic // * mwe-popups-type-generic
// * mwe-popups-type-disambiguation // * 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'; container.className = 'mwe-popups-container';
element.appendChild( container ); element.appendChild( container );
return element; return element;

View file

@ -38,7 +38,7 @@ export function renderPreview(
// * popups-icon--preview-unknown // * popups-icon--preview-unknown
// * popups-icon--preview-generic // * popups-icon--preview-generic
// * popups-icon--preview-disambiguation // * 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 ); popup.querySelector( '.mwe-popups-extract' ).setAttribute( 'href', model.url );
const messageElement = popup.querySelector( '.mwe-popups-message' ); const messageElement = popup.querySelector( '.mwe-popups-message' );
if ( message ) { if ( message ) {

View file

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

View file

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

View file

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

View file

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

View file

@ -6,9 +6,6 @@
], ],
"rules": { "rules": {
"no-jquery/no-class-state": "off", "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" "es-x/no-hashbang": "warn"
} }
} }

View file

@ -9,7 +9,9 @@ QUnit.module( 'ext.popups/changeListeners/footerLink @integration', {
// Stub internal usage of mw.message // Stub internal usage of mw.message
mw.message = ( str ) => mw.message = ( str ) =>
( { ( {
text() { return str; } text() {
return str;
}
} ); } );
boundActions.showSettings = this.showSettingsSpy = this.sandbox.spy(); 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 // Stub internal usage of mw.Title.newFromText
mw.Title.newFromText = ( str ) => { mw.Title.newFromText = ( str ) => {
return { 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 ) { 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; delete noPageviewState.pageviews.pageview;
this.changeListener( undefined, newState ); this.changeListener( undefined, newState );

View file

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

View file

@ -26,7 +26,7 @@ QUnit.test( '#getEditCountBucket', ( assert ) => {
assert.strictEqual( assert.strictEqual(
bucket, bucket,
cases[ i ][ 1 ], 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( assert.strictEqual(
bucket, bucket,
cases[ i ][ 1 ], 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 ) { 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' acceptLanguage: 'pl'
} ); } );
const spy = this.sandbox.spy(), 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 ) => { errorCases.forEach( ( data, i ) => {
assert.throws( 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( assert.deepEqual(
gateway.extractPageFromResponse( data[ 0 ] ), gateway.extractPageFromResponse( data[ 0 ] ),
data[ 1 ], 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(), deferred = $.Deferred(),
api = { api = {
get: this.sandbox.stub().returns( get: this.sandbox.stub().returns(
deferred.promise( { abort() { deferred.reject( 'http' ); } } ) deferred.promise( { abort() {
deferred.reject( 'http' );
} } )
) )
}, },
gateway = createMediaWikiApiGateway( api, DEFAULT_CONSTANTS ); gateway = createMediaWikiApiGateway( api, DEFAULT_CONSTANTS );

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -41,9 +41,9 @@ QUnit.module( 'ext.popups#renderer', {
beforeEach() { beforeEach() {
this.sandbox.stub( constants.default, 'BRACKETED_DEVICE_PIXEL_RATIO' ).value( 1 ); this.sandbox.stub( constants.default, 'BRACKETED_DEVICE_PIXEL_RATIO' ).value( 1 );
mw.msg = ( key ) => `<${key}>`; mw.msg = ( key ) => `<${ key }>`;
mw.message = ( key ) => { mw.message = ( key ) => {
return { exists: () => !key.endsWith( 'generic' ), text: () => `<${key}>` }; return { exists: () => !key.endsWith( 'generic' ), text: () => `<${ key }>` };
}; };
mw.html = { mw.html = {
@ -77,7 +77,7 @@ QUnit.test( 'getExtractWidth', ( assert ) => {
], ],
[ { [ {
isNarrow: true, offset: 10 isNarrow: true, offset: 10
}, `${pagePreview.defaultExtractWidth + 10}px` ], }, `${ pagePreview.defaultExtractWidth + 10 }px` ],
[ { [ {
// Fall back to css stylesheet for non-narrow thumbs. // Fall back to css stylesheet for non-narrow thumbs.
isNarrow: false, offset: 100 isNarrow: false, offset: 100
@ -88,7 +88,7 @@ QUnit.test( 'getExtractWidth', ( assert ) => {
assert.strictEqual( assert.strictEqual(
pagePreview.getExtractWidth( case_[ 0 ] ), pagePreview.getExtractWidth( case_[ 0 ] ),
case_[ 1 ], 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 ) => { cases.forEach( ( case_, i ) => {
assert.strictEqual( assert.strictEqual(
$container.find( `${case_[ 0 ]} path` ).attr( 'd' ), $container.find( `${ case_[ 0 ] } path` ).attr( 'd' ),
case_[ 1 ], 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, flippedY: false,
dir 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, flippedY: true,
dir 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, flippedY: true,
dir 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, flippedY: false,
dir 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( assert.strictEqual(
$( preview.el ).css( 'top' ), $( preview.el ).css( 'top' ),
`${layout.offset.top}px`, `${ layout.offset.top }px`,
'Top is correct.' 'Top is correct.'
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'left' ), $( preview.el ).css( 'left' ),
`${layout.offset.left}px`, `${ layout.offset.left }px`,
'Left is correct.' 'Left is correct.'
); );
} ); } );
@ -1080,12 +1080,12 @@ QUnit.test( '#layoutPreview - tall preview, flipped X, has thumbnail', function
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'top' ), $( preview.el ).css( 'top' ),
`${layout.offset.top}px`, `${ layout.offset.top }px`,
'Top is correct.' 'Top is correct.'
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'left' ), $( preview.el ).css( 'left' ),
`${layout.offset.left}px`, `${ layout.offset.left }px`,
'Left is correct.' 'Left is correct.'
); );
assert.false( assert.false(
@ -1124,17 +1124,17 @@ QUnit.test( '#layoutPreview - portrait preview, flipped X, has thumbnail, small
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'top' ), $( preview.el ).css( 'top' ),
`${layout.offset.top}px`, `${ layout.offset.top }px`,
'Top is correct.' 'Top is correct.'
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'left' ), $( preview.el ).css( 'left' ),
`${layout.offset.left}px`, `${ layout.offset.left }px`,
'Left is correct.' 'Left is correct.'
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).find( '.mwe-popups-extract' ).css( 'margin-top' ), $( 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.' 'Extract margin top has been set when preview height is smaller than the predefined landscape image height.'
); );
assert.strictEqual( assert.strictEqual(
@ -1169,12 +1169,12 @@ QUnit.test( '#layoutPreview - portrait preview, flipped X, has thumbnail, big he
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'top' ), $( preview.el ).css( 'top' ),
`${layout.offset.top}px`, `${ layout.offset.top }px`,
'Top is correct.' 'Top is correct.'
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'left' ), $( preview.el ).css( 'left' ),
`${layout.offset.left}px`, `${ layout.offset.left }px`,
'Left is correct.' 'Left is correct.'
); );
assert.strictEqual( assert.strictEqual(
@ -1329,13 +1329,13 @@ QUnit.test( '#layoutPreview - tall preview, has thumbnail, flipped Y', ( assert
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'bottom' ), $( preview.el ).css( 'bottom' ),
`${windowHeight - layout.offset.top}px`, `${ windowHeight - layout.offset.top }px`,
'Bottom is correct.' 'Bottom is correct.'
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'left' ), $( preview.el ).css( 'left' ),
`${layout.offset.left}px`, `${ layout.offset.left }px`,
'Left is correct.' 'Left is correct.'
); );
assert.strictEqual( assert.strictEqual(
@ -1370,12 +1370,12 @@ QUnit.test( '#layoutPreview - tall preview, has thumbnail, flipped X and Y', fun
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'left' ), $( preview.el ).css( 'left' ),
`${layout.offset.left}px`, `${ layout.offset.left }px`,
'Left is correct.' 'Left is correct.'
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'bottom' ), $( preview.el ).css( 'bottom' ),
`${windowHeight - layout.offset.top}px`, `${ windowHeight - layout.offset.top }px`,
'Bottom is correct.' 'Bottom is correct.'
); );
assert.strictEqual( assert.strictEqual(
@ -1406,12 +1406,12 @@ QUnit.test( '#layoutPreview - portrait preview, has thumbnail, flipped X and Y',
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'left' ), $( preview.el ).css( 'left' ),
`${layout.offset.left}px`, `${ layout.offset.left }px`,
'Left is correct.' 'Left is correct.'
); );
assert.strictEqual( assert.strictEqual(
$( preview.el ).css( 'bottom' ), $( preview.el ).css( 'bottom' ),
`${windowHeight - layout.offset.top}px`, `${ windowHeight - layout.offset.top }px`,
'Bottom is correct.' 'Bottom is correct.'
); );
assert.strictEqual( assert.strictEqual(
@ -1458,7 +1458,7 @@ QUnit.test( '#setThumbnailClipPath', function ( assert ) {
assert.strictEqual( assert.strictEqual(
clipPath.getAttribute( 'transform' ), clipPath.getAttribute( 'transform' ),
expected, 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( assert.strictEqual(
renderer.getThumbnailClipPathID( isTall, flippedY, flippedX ), renderer.getThumbnailClipPathID( isTall, flippedY, flippedX ),
expected, 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( assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'x' ), +$( thumbnail.el ).find( 'image' ).attr( 'x' ),
case_.expectedX, case_.expectedX,
`Image element x coordinate is correct. ${case_.message}` `Image element x coordinate is correct. ${ case_.message }`
); );
assert.strictEqual( assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'y' ), +$( thumbnail.el ).find( 'image' ).attr( 'y' ),
case_.expectedY, case_.expectedY,
`Image element y coordinate is correct. ${case_.message}` `Image element y coordinate is correct. ${ case_.message }`
); );
assert.strictEqual( assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'width' ), +$( thumbnail.el ).find( 'image' ).attr( 'width' ),
case_.width, case_.width,
`Image element width is correct. ${case_.message}` `Image element width is correct. ${ case_.message }`
); );
assert.strictEqual( assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'height' ), +$( thumbnail.el ).find( 'image' ).attr( 'height' ),
case_.height, case_.height,
`Image element height is correct. ${case_.message}` `Image element height is correct. ${ case_.message }`
); );
assert.strictEqual( assert.strictEqual(
+$( thumbnail.el ).attr( 'width' ), +$( thumbnail.el ).attr( 'width' ),
case_.expectedSVGWidth, case_.expectedSVGWidth,
`Image SVG width is correct. ${case_.message}` `Image SVG width is correct. ${ case_.message }`
); );
assert.strictEqual( assert.strictEqual(
+$( thumbnail.el ).attr( 'height' ), +$( thumbnail.el ).attr( 'height' ),
case_.expectedSVGHeight, case_.expectedSVGHeight,
`Image SVG height is correct. ${case_.message}` `Image SVG height is correct. ${ case_.message }`
); );
assert.strictEqual( assert.strictEqual(
thumbnail.isNarrow, thumbnail.isNarrow,
case_.expectedIsNarrow, case_.expectedIsNarrow,
`Image isNarrow is correct. ${case_.message}` `Image isNarrow is correct. ${ case_.message }`
); );
assert.strictEqual( assert.strictEqual(
thumbnail.offset, thumbnail.offset,
case_.expectedOffset, 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( assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'x' ), +$( thumbnail.el ).find( 'image' ).attr( 'x' ),
case_.expectedX, case_.expectedX,
`Image x coordinate is correct. ${case_.message}` `Image x coordinate is correct. ${ case_.message }`
); );
assert.strictEqual( assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'y' ), +$( thumbnail.el ).find( 'image' ).attr( 'y' ),
case_.expectedY, case_.expectedY,
`Image y coordinate is correct. ${case_.message}` `Image y coordinate is correct. ${ case_.message }`
); );
assert.strictEqual( assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'width' ), +$( thumbnail.el ).find( 'image' ).attr( 'width' ),
case_.width, case_.width,
`Image element width is correct. ${case_.message}` `Image element width is correct. ${ case_.message }`
); );
assert.strictEqual( assert.strictEqual(
+$( thumbnail.el ).find( 'image' ).attr( 'height' ), +$( thumbnail.el ).find( 'image' ).attr( 'height' ),
case_.height, case_.height,
`Image element height is correct. ${case_.message}` `Image element height is correct. ${ case_.message }`
); );
assert.strictEqual( assert.strictEqual(
+$( thumbnail.el ).attr( 'width' ), +$( thumbnail.el ).attr( 'width' ),
case_.expectedSVGWidth, case_.expectedSVGWidth,
`Image SVG width is correct. ${case_.message}` `Image SVG width is correct. ${ case_.message }`
); );
assert.strictEqual( assert.strictEqual(
+$( thumbnail.el ).attr( 'height' ), +$( thumbnail.el ).attr( 'height' ),
case_.expectedSVGHeight, case_.expectedSVGHeight,
`Image SVG height is correct. ${case_.message}` `Image SVG height is correct. ${ case_.message }`
); );
} ); } );
} ); } );

View file

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

View file

@ -17,6 +17,7 @@ const
POPUPS_MODULE_NAME = 'ext.popups.main'; POPUPS_MODULE_NAME = 'ext.popups.main';
async function makePage( title, path ) { async function makePage( title, path ) {
// eslint-disable-next-line security/detect-non-literal-fs-filename
const content = fs.readFileSync( path, 'utf-8' ); const content = fs.readFileSync( path, 'utf-8' );
const bot = await Api.bot(); const bot = await Api.bot();
await bot.edit( title, content ); await bot.edit( title, content );
@ -24,16 +25,16 @@ async function makePage( title, path ) {
class PopupsPage extends Page { class PopupsPage extends Page {
async setupPagePreviews() { async setupPagePreviews() {
return browser.call( async () => { return browser.call( async () => {
const path = `${__dirname}/../fixtures/`; const path = `${ __dirname }/../fixtures/`;
await makePage( `${TEST_PAGE_POPUPS_TITLE} 2`, `${path}test_page_2.wikitext` ); await makePage( `${ TEST_PAGE_POPUPS_TITLE } 2`, `${ path }test_page_2.wikitext` );
await makePage( TEST_PAGE_POPUPS_TITLE, `${path}test_page.wikitext` ); await makePage( TEST_PAGE_POPUPS_TITLE, `${ path }test_page.wikitext` );
} ); } );
} }
async setupReferencePreviews() { async setupReferencePreviews() {
return browser.call( async () => { return browser.call( async () => {
const path = `${__dirname}/../fixtures/`; const path = `${ __dirname }/../fixtures/`;
await makePage( TEST_REFERENCE_POPUPS_TITLE, `${path}test_page.wikitext` ); await makePage( TEST_REFERENCE_POPUPS_TITLE, `${ path }test_page.wikitext` );
} ); } );
} }
@ -67,7 +68,7 @@ class PopupsPage extends Page {
} }
async dwellReferenceLink( id ) { async dwellReferenceLink( id ) {
await this.dwellLink( `#${id} a` ); await this.dwellLink( `#${ id } a` );
} }
async dwellReferenceInceptionLink() { 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 // Rename source map extensions. Per T173491 files with a .map extension cannot be served
// from prod. // 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 // Accurate source maps at the expense of build time. The source map is intentionally exposed