Gruntfile.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. 'use strict';
  2. module.exports = function ( grunt ) {
  3. grunt.loadNpmTasks( 'grunt-banana-checker' );
  4. grunt.loadNpmTasks( 'grunt-eslint' );
  5. grunt.loadNpmTasks( 'grunt-karma' );
  6. grunt.loadNpmTasks( 'grunt-stylelint' );
  7. const fs = require( 'fs' );
  8. const wgServer = process.env.MW_SERVER;
  9. const wgScriptPath = process.env.MW_SCRIPT_PATH;
  10. const karmaProxy = {};
  11. let qunitURL = wgServer + wgScriptPath + '/index.php?title=Special:JavaScriptTest/qunit/export';
  12. // "MediaWiki" for core, or extension/skin name (e.g. "GrowthExperiments")
  13. const qunitComponent = grunt.option( 'qunit-component' );
  14. const qunitWatch = grunt.option( 'qunit-watch' ) || false;
  15. const qunitWatchFiles = [];
  16. if ( qunitComponent ) {
  17. qunitURL = qunitURL + '&component=' + qunitComponent;
  18. }
  19. if ( qunitWatch ) {
  20. if ( !qunitComponent || qunitComponent === 'MediaWiki' ) {
  21. // MediaWiki core
  22. qunitWatchFiles.push( 'tests/qunit/**' );
  23. qunitWatchFiles.push( 'resources/**' );
  24. } else {
  25. // one extension or skin
  26. const extPath = __dirname + '/extensions/' + qunitComponent + '/extension.json';
  27. const skinPath = __dirname + '/skins/' + qunitComponent + '/skin.json';
  28. // eslint-disable-next-line security/detect-non-literal-fs-filename
  29. if ( fs.existsSync( extPath ) ) {
  30. qunitWatchFiles.push( 'extensions/' + qunitComponent + '/extension.json' );
  31. qunitWatchFiles.push( 'extensions/' + qunitComponent + '/{modules,resources,tests}/**' );
  32. }
  33. // eslint-disable-next-line security/detect-non-literal-fs-filename
  34. if ( fs.existsSync( skinPath ) ) {
  35. qunitWatchFiles.push( 'skins/' + qunitComponent + '/skin.json' );
  36. qunitWatchFiles.push( 'skins/' + qunitComponent + '/{modules,resources,tests}/**' );
  37. }
  38. }
  39. }
  40. karmaProxy[ wgScriptPath ] = {
  41. target: wgServer + wgScriptPath,
  42. changeOrigin: true
  43. };
  44. grunt.initConfig( {
  45. eslint: {
  46. options: {
  47. extensions: [ '.js', '.json', '.vue' ],
  48. cache: true,
  49. fix: grunt.option( 'fix' )
  50. },
  51. all: '.'
  52. },
  53. banana: {
  54. options: {
  55. requireLowerCase: false,
  56. disallowBlankTranslations: false
  57. },
  58. core: 'languages/i18n/',
  59. exif: 'languages/i18n/exif/',
  60. api: 'includes/api/i18n/',
  61. rest: 'includes/Rest/i18n/',
  62. installer: 'includes/installer/i18n/',
  63. paramvalidator: 'includes/libs/ParamValidator/i18n/'
  64. },
  65. stylelint: {
  66. options: {
  67. reportNeedlessDisables: true
  68. },
  69. resources: 'resources/src/**/*.{css,less,vue}',
  70. config: 'mw-config/**/*.css'
  71. },
  72. watch: {
  73. files: [
  74. '.{stylelintrc,eslintrc}.json',
  75. '**/*',
  76. '!{extensions,node_modules,skins,vendor}/**'
  77. ],
  78. tasks: 'test'
  79. },
  80. karma: {
  81. options: {
  82. plugins: [
  83. '@wikimedia/karma-firefox-launcher',
  84. 'karma-*'
  85. ],
  86. customLaunchers: {
  87. ChromeCustom: {
  88. base: 'ChromeHeadless',
  89. // Chrome requires --no-sandbox in Docker/CI.
  90. // WMF CI images expose CHROMIUM_FLAGS which sets that.
  91. flags: process.env.CHROMIUM_FLAGS ? ( process.env.CHROMIUM_FLAGS || '' ).split( ' ' ) : []
  92. }
  93. },
  94. proxies: karmaProxy,
  95. files: [ {
  96. pattern: qunitURL,
  97. type: 'js',
  98. watched: false,
  99. included: true,
  100. served: false
  101. }, ...qunitWatchFiles.map( ( file ) => ( {
  102. pattern: file,
  103. type: 'js',
  104. watched: true,
  105. included: false,
  106. served: false
  107. } ) ) ],
  108. logLevel: ( process.env.ZUUL_PROJECT ? 'DEBUG' : 'INFO' ),
  109. frameworks: [ 'qunit' ],
  110. // Disable autostart because we load modules asynchronously.
  111. client: {
  112. qunit: {
  113. autostart: false
  114. }
  115. },
  116. reporters: [ 'mocha' ],
  117. singleRun: !qunitWatch,
  118. autoWatch: qunitWatch,
  119. // Some tests in extensions don't yield for more than the default 10s (T89075)
  120. browserNoActivityTimeout: 60 * 1000,
  121. // Karma requires Same-Origin (or CORS) by default since v1.1.1
  122. // for better stacktraces. But we load the first request from wgServer
  123. crossOriginAttribute: false
  124. },
  125. main: {
  126. browsers: [ 'ChromeCustom' ]
  127. },
  128. firefox: {
  129. browsers: [ 'FirefoxHeadless' ]
  130. }
  131. }
  132. } );
  133. grunt.registerTask( 'assert-mw-env', function () {
  134. let ok = true;
  135. if ( !process.env.MW_SERVER ) {
  136. grunt.log.error( 'Environment variable MW_SERVER must be set.\n' +
  137. 'Set this like $wgServer, e.g. "http://localhost"'
  138. );
  139. ok = false;
  140. }
  141. if ( !process.env.MW_SCRIPT_PATH ) {
  142. grunt.log.error( 'Environment variable MW_SCRIPT_PATH must be set.\n' +
  143. 'Set this like $wgScriptPath, e.g. "/w"' );
  144. ok = false;
  145. }
  146. return ok;
  147. } );
  148. grunt.registerTask( 'lint', [ 'eslint', 'banana', 'stylelint' ] );
  149. grunt.registerTask( 'qunit', [ 'assert-mw-env', 'karma:main' ] );
  150. };