Gruntfile.js
'use strict';
module.exports = function ( grunt ) {
grunt.loadNpmTasks( 'grunt-banana-checker' );
grunt.loadNpmTasks( 'grunt-eslint' );
grunt.loadNpmTasks( 'grunt-karma' );
grunt.loadNpmTasks( 'grunt-stylelint' );
const fs = require( 'fs' );
const wgServer = process.env.MW_SERVER;
const wgScriptPath = process.env.MW_SCRIPT_PATH;
const karmaProxy = {};
let qunitURL = wgServer + wgScriptPath + '/index.php?title=Special:JavaScriptTest/qunit/export';
// "MediaWiki" for core, or extension/skin name (e.g. "GrowthExperiments")
const qunitComponent = grunt.option( 'qunit-component' );
const qunitWatch = grunt.option( 'qunit-watch' ) || false;
const qunitWatchFiles = [];
if ( qunitComponent ) {
qunitURL = qunitURL + '&component=' + qunitComponent;
}
if ( qunitWatch ) {
if ( !qunitComponent || qunitComponent === 'MediaWiki' ) {
// MediaWiki core
qunitWatchFiles.push( 'tests/qunit/**' );
qunitWatchFiles.push( 'resources/**' );
} else {
// one extension or skin
const extPath = __dirname + '/extensions/' + qunitComponent + '/extension.json';
const skinPath = __dirname + '/skins/' + qunitComponent + '/skin.json';
// eslint-disable-next-line security/detect-non-literal-fs-filename
if ( fs.existsSync( extPath ) ) {
qunitWatchFiles.push( 'extensions/' + qunitComponent + '/extension.json' );
qunitWatchFiles.push( 'extensions/' + qunitComponent + '/{modules,resources,tests}/**' );
}
// eslint-disable-next-line security/detect-non-literal-fs-filename
if ( fs.existsSync( skinPath ) ) {
qunitWatchFiles.push( 'skins/' + qunitComponent + '/skin.json' );
qunitWatchFiles.push( 'skins/' + qunitComponent + '/{modules,resources,tests}/**' );
}
}
}
karmaProxy[ wgScriptPath ] = {
target: wgServer + wgScriptPath,
changeOrigin: true
};
grunt.initConfig( {
eslint: {
options: {
extensions: [ '.js', '.json', '.vue' ],
cache: true,
fix: grunt.option( 'fix' )
},
all: '.'
},
banana: {
options: {
requireLowerCase: false,
disallowBlankTranslations: false
},
core: 'languages/i18n/',
exif: 'languages/i18n/exif/',
preferences: 'languages/i18n/preferences/',
api: 'includes/api/i18n/',
rest: 'includes/Rest/i18n/',
installer: 'includes/installer/i18n/',
paramvalidator: 'includes/libs/ParamValidator/i18n/'
},
stylelint: {
options: {
reportNeedlessDisables: true
},
resources: 'resources/src/**/*.{css,less,vue}',
config: 'mw-config/**/*.css'
},
watch: {
files: [
'.{stylelintrc,eslintrc}.json',
'**/*',
'!{extensions,node_modules,skins,vendor}/**'
],
tasks: 'test'
},
karma: {
options: {
plugins: [
'@wikimedia/karma-firefox-launcher',
'karma-*'
],
customLaunchers: {
ChromeCustom: {
base: 'ChromeHeadless',
// Chrome requires --no-sandbox in Docker/CI.
// WMF CI images expose CHROMIUM_FLAGS which sets that.
flags: process.env.CHROMIUM_FLAGS ? ( process.env.CHROMIUM_FLAGS || '' ).split( ' ' ) : []
}
},
proxies: karmaProxy,
files: [ {
pattern: qunitURL,
type: 'js',
watched: false,
included: true,
served: false
}, ...qunitWatchFiles.map( ( file ) => ( {
pattern: file,
type: 'js',
watched: true,
included: false,
served: false
} ) ) ],
logLevel: ( process.env.ZUUL_PROJECT ? 'DEBUG' : 'INFO' ),
frameworks: [ 'qunit' ],
// Disable autostart because we load modules asynchronously.
client: {
qunit: {
autostart: false
}
},
reporters: [ 'mocha' ],
singleRun: !qunitWatch,
autoWatch: qunitWatch,
// Some tests in extensions don't yield for more than the default 10s (T89075)
browserNoActivityTimeout: 60 * 1000,
// Karma requires Same-Origin (or CORS) by default since v1.1.1
// for better stacktraces. But we load the first request from wgServer
crossOriginAttribute: false
},
main: {
browsers: [ 'ChromeCustom' ]
},
firefox: {
browsers: [ 'FirefoxHeadless' ]
}
}
} );
grunt.registerTask( 'assert-mw-env', function () {
let ok = true;
if ( !process.env.MW_SERVER ) {
grunt.log.error( 'Environment variable MW_SERVER must be set.\n' +
'Set this like $wgServer, e.g. "http://localhost"'
);
ok = false;
}
if ( !process.env.MW_SCRIPT_PATH ) {
grunt.log.error( 'Environment variable MW_SCRIPT_PATH must be set.\n' +
'Set this like $wgScriptPath, e.g. "/w"' );
ok = false;
}
return ok;
} );
grunt.registerTask( 'lint', [ 'eslint', 'banana', 'stylelint' ] );
grunt.registerTask( 'qunit', [ 'assert-mw-env', 'karma:main' ] );
};