From: Stas Vilchik Date: Thu, 27 Oct 2016 11:37:55 +0000 (+0200) Subject: improve front end dx (#1331) X-Git-Tag: 6.2-RC1~257 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=cae60cc46c6866315699f5106e363410bc0915e6;p=sonarqube.git improve front end dx (#1331) --- diff --git a/server/sonar-web/.babelrc b/server/sonar-web/.babelrc deleted file mode 100644 index fb5f2613035..00000000000 --- a/server/sonar-web/.babelrc +++ /dev/null @@ -1,28 +0,0 @@ -{ - "presets": ["es2015", "es2016", "react"], - "ignore": [ - "**/libs/**" - ], - "plugins": [ - "transform-class-properties", - "transform-object-rest-spread" - ], - "env": { - "production": { - "plugins": [ - "transform-react-constant-elements" - ] - }, - "development": { - "plugins": [ - ["react-transform", { - "transforms": [{ - "transform": "react-transform-hmr", - "imports": ["react"], - "locals": ["module"] - }] - }] - ] - } - } -} diff --git a/server/sonar-web/config/env.js b/server/sonar-web/config/env.js new file mode 100644 index 00000000000..77aa261dab7 --- /dev/null +++ b/server/sonar-web/config/env.js @@ -0,0 +1,42 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be +// injected into the application via DefinePlugin in Webpack configuration. + +var REACT_APP = /^REACT_APP_/i; + +function getClientEnvironment () { + return Object + .keys(process.env) + .filter(key => REACT_APP.test(key)) + .reduce((env, key) => { + env['process.env.' + key] = JSON.stringify(process.env[key]); + return env; + }, { + // Useful for determining whether we’re running in production mode. + // Most importantly, it switches React into the correct mode. + 'process.env.NODE_ENV': JSON.stringify( + process.env.NODE_ENV || 'development' + ) + }); +} + +module.exports = getClientEnvironment; + diff --git a/server/sonar-web/config/jest/CSSStub.js b/server/sonar-web/config/jest/CSSStub.js new file mode 100644 index 00000000000..7646f65dc87 --- /dev/null +++ b/server/sonar-web/config/jest/CSSStub.js @@ -0,0 +1,21 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +module.exports = {}; + diff --git a/server/sonar-web/config/jest/FileStub.js b/server/sonar-web/config/jest/FileStub.js new file mode 100644 index 00000000000..70a6c197806 --- /dev/null +++ b/server/sonar-web/config/jest/FileStub.js @@ -0,0 +1,20 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +module.exports = "test-file-stub"; diff --git a/server/sonar-web/config/jest/SetupTestEnvironment.js b/server/sonar-web/config/jest/SetupTestEnvironment.js new file mode 100644 index 00000000000..536db746157 --- /dev/null +++ b/server/sonar-web/config/jest/SetupTestEnvironment.js @@ -0,0 +1,24 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +window.baseUrl = ''; +window.t = window.tp = function () { + var args = Array.prototype.slice.call(arguments, 0); + return args.join('.'); +}; diff --git a/server/sonar-web/config/paths.js b/server/sonar-web/config/paths.js index 1484c1b0607..0ce56b422a5 100644 --- a/server/sonar-web/config/paths.js +++ b/server/sonar-web/config/paths.js @@ -18,10 +18,39 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ var path = require('path'); +var fs = require('fs'); -var base = process.env.OUTPUT || path.join(__dirname, '../src/main/webapp'); +// Make sure any symlinks in the project folder are resolved: +// https://github.com/facebookincubator/create-react-app/issues/637 +var appDirectory = fs.realpathSync(process.cwd()); +function resolveApp(relativePath) { + return path.resolve(appDirectory, relativePath); +} +// We support resolving modules according to `NODE_PATH`. +// This lets you use absolute paths in imports inside large monorepos: +// https://github.com/facebookincubator/create-react-app/issues/253. + +// It works similar to `NODE_PATH` in Node itself: +// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders + +// We will export `nodePaths` as an array of absolute paths. +// It will then be used by Webpack configs. +// Jest doesn’t need this because it already handles `NODE_PATH` out of the box. + +var nodePaths = (process.env.NODE_PATH || '') + .split(process.platform === 'win32' ? ';' : ':') + .filter(Boolean) + .map(resolveApp); + +// config after eject: we're in ./config/ module.exports = { - jsBuild: path.join(base, 'js/bundles'), - cssBuild: path.join(base, 'css') + appBuild: resolveApp('src/main/webapp/js/bundles'), + appPackageJson: resolveApp('package.json'), + appSrc: resolveApp('src/main/js'), + cssBuild: resolveApp('src/main/webapp/css'), + appNodeModules: resolveApp('node_modules'), + ownNodeModules: resolveApp('node_modules'), + nodePaths: nodePaths }; + diff --git a/server/sonar-web/config/polyfills.js b/server/sonar-web/config/polyfills.js new file mode 100644 index 00000000000..3b51c254e7a --- /dev/null +++ b/server/sonar-web/config/polyfills.js @@ -0,0 +1,22 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import 'babel-polyfill'; +import 'whatwg-fetch'; + diff --git a/server/sonar-web/config/webpack/webpack.config.base.js b/server/sonar-web/config/webpack/webpack.config.base.js index de369bbcb09..275b0d4c13e 100644 --- a/server/sonar-web/config/webpack/webpack.config.base.js +++ b/server/sonar-web/config/webpack/webpack.config.base.js @@ -1,14 +1,17 @@ /* eslint no-var: 0 */ var path = require('path'); -var webpack = require('webpack'); var autoprefixer = require('autoprefixer'); +var webpack = require('webpack'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); -var autoprefixerOptions = require('./../autoprefixer'); -var paths = require('./../paths'); +var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); +var url = require('url'); +var paths = require('../paths'); +var autoprefixerOptions = require('../autoprefixer'); module.exports = { entry: { 'vendor': [ + require.resolve('../polyfills'), 'jquery', 'underscore', 'd3', @@ -56,7 +59,7 @@ module.exports = { 'widgets': './src/main/js/widgets/widgets.js' }, output: { - path: paths.jsBuild, + path: paths.appBuild, filename: '[name].js' }, plugins: [ @@ -64,9 +67,23 @@ module.exports = { new ExtractTextPlugin('../../css/sonar.css', { allChunks: true }) ], resolve: { - root: path.join(__dirname, '../../src/main/js') + // This allows you to set a fallback for where Webpack should look for modules. + // We read `NODE_PATH` environment variable in `paths.js` and pass paths here. + // We use `fallback` instead of `root` because we want `node_modules` to "win" + // if there any conflicts. This matches Node resolution mechanism. + // https://github.com/facebookincubator/create-react-app/issues/253 + fallback: paths.nodePaths }, module: { + // First, run the linter. + // It's important to do this before Babel processes the JS. + preLoaders: [ + { + test: /\.js$/, + loader: 'eslint', + include: paths.appSrc + } + ], loaders: [ { test: /\.js$/, @@ -103,5 +120,12 @@ module.exports = { }, postcss: function () { return [autoprefixer(autoprefixerOptions)]; + }, + // Some libraries import Node modules but don't use them in the browser. + // Tell Webpack to provide empty mocks for them so importing them works. + node: { + fs: 'empty', + net: 'empty', + tls: 'empty' } }; diff --git a/server/sonar-web/config/webpack/webpack.config.dev.js b/server/sonar-web/config/webpack/webpack.config.dev.js index 117450951a3..6a2560262c4 100644 --- a/server/sonar-web/config/webpack/webpack.config.dev.js +++ b/server/sonar-web/config/webpack/webpack.config.dev.js @@ -18,14 +18,57 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ var webpack = require('webpack'); +var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); +var paths = require('../paths'); var config = require('./webpack.config.base'); +var getClientEnvironment = require('../env'); + +// Webpack uses `publicPath` to determine where the app is being served from. +var publicPath = '/js/bundles/'; + +// Get environment variables to inject into our app. +var env = getClientEnvironment(); + +// This makes the bundle appear split into separate modules in the devtools. +// We don't use source maps here because they can be confusing: +// https://github.com/facebookincubator/create-react-app/issues/343#issuecomment-237241875 +// You may want 'cheap-module-source-map' instead if you prefer source maps. +config.devtool = 'eval'; + +// Include an alternative client for WebpackDevServer. A client's job is to +// connect to WebpackDevServer by a socket and get notified about changes. +// When you save a file, the client will either apply hot updates (in case +// of CSS changes), or refresh the page (in case of JS changes). When you +// make a syntax error, this client will display a syntax error overlay. +// Note: instead of the default WebpackDevServer client, we use a custom one +// to bring better experience for Create React App users. You can replace +// the line below with these two lines if you prefer the stock client: +// require.resolve('webpack-dev-server/client') + '?/', +// require.resolve('webpack/hot/dev-server'), +config.entry.vendor.unshift(require.resolve('react-dev-utils/webpackHotDevClient')); + +// Add /* filename */ comments to generated require()s in the output. +config.output.pathinfo = true; + +// This is the URL that app is served from. +config.output.publicPath = publicPath; -config.devtool = 'cheap-module-eval-source-map'; -config.output.publicPath = '/js/bundles/'; -config.entry.vendor.unshift('webpack-hot-middleware/client'); config.plugins = [].concat(config.plugins, [ - new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"development"' }), - new webpack.HotModuleReplacementPlugin() + // Makes some environment variables available to the JS code, for example: + // if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`. + new webpack.DefinePlugin(env), + // This is necessary to emit hot updates (currently CSS only): + new webpack.HotModuleReplacementPlugin(), + // Watcher doesn't work well if you mistype casing in a path so we use + // a plugin that prints an error when you attempt to do this. + // See https://github.com/facebookincubator/create-react-app/issues/240 + new CaseSensitivePathsPlugin(), + // If you require a missing module and then `npm install` it, you still have + // to restart the development server for Webpack to discover it. This plugin + // makes the discovery automatic so you don't have to restart. + // See https://github.com/facebookincubator/create-react-app/issues/186 + new WatchMissingNodeModulesPlugin(paths.appNodeModules) ]); module.exports = config; diff --git a/server/sonar-web/config/webpack/webpack.config.fast.js b/server/sonar-web/config/webpack/webpack.config.fast.js index 87cc9383b3a..5fcfafc149f 100644 --- a/server/sonar-web/config/webpack/webpack.config.fast.js +++ b/server/sonar-web/config/webpack/webpack.config.fast.js @@ -17,11 +17,6 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -var webpack = require('webpack'); var config = require('./webpack.config.base'); -config.plugins = [].concat(config.plugins, [ - new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"development"' }), -]); - module.exports = config; diff --git a/server/sonar-web/config/webpack/webpack.config.prod.js b/server/sonar-web/config/webpack/webpack.config.prod.js index 7c5c3917986..43c0a672418 100644 --- a/server/sonar-web/config/webpack/webpack.config.prod.js +++ b/server/sonar-web/config/webpack/webpack.config.prod.js @@ -19,16 +19,44 @@ */ var webpack = require('webpack'); var config = require('./webpack.config.base'); +var getClientEnvironment = require('../env'); + +// Get environment variables to inject into our app. +var env = getClientEnvironment(); + +// Assert this just to be safe. +// Development builds of React are slow and not intended for production. +if (env['process.env.NODE_ENV'] !== '"production"') { + throw new Error('Production builds must have NODE_ENV=production.'); +} + +// Don't attempt to continue if there are any errors. +config.bail = true; config.plugins = [].concat(config.plugins, [ - new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }), + // Makes some environment variables available to the JS code, for example: + // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`. + // It is absolutely essential that NODE_ENV was set to production here. + // Otherwise React will be compiled in the very slow development mode. + new webpack.DefinePlugin(env), + // This helps ensure the builds are consistent if source hasn't changed: new webpack.optimize.OccurrenceOrderPlugin(), + // Try to dedupe duplicated modules, if any: new webpack.optimize.DedupePlugin(), + // Minify the code. new webpack.optimize.UglifyJsPlugin({ - compress: { screw_ie8: true, warnings: false }, - mangle: { screw_ie8: true }, - output: { screw_ie8: true, comments: false } - }) + compress: { + screw_ie8: true, // React doesn't support IE8 + warnings: false + }, + mangle: { + screw_ie8: true + }, + output: { + comments: false, + screw_ie8: true + } + }), ]); module.exports = config; diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index 9577d494429..dfa0f76c71d 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -5,79 +5,90 @@ "repository": "SonarSource/sonarqube", "license": "LGPL-3.0", "devDependencies": { - "autoprefixer": "6.2.2", - "babel-core": "6.13.2", - "babel-eslint": "^6.0.4", + "autoprefixer": "6.4.1", + "babel-core": "6.14.0", + "babel-eslint": "6.1.2", "babel-jest": "15.0.0", - "babel-loader": "6.2.4", - "babel-plugin-react-transform": "2.0.2", - "babel-plugin-transform-class-properties": "6.11.5", - "babel-plugin-transform-object-rest-spread": "6.8.0", - "babel-plugin-transform-react-constant-elements": "6.9.1", + "babel-loader": "6.2.5", "babel-polyfill": "6.3.14", - "babel-preset-es2015": "6.13.2", - "babel-preset-es2016": "6.11.3", - "babel-preset-react": "6.11.1", - "babel-register": "6.3.13", + "babel-preset-react-app": "0.2.1", "backbone": "1.2.3", "backbone.marionette": "2.4.3", "blueimp-md5": "1.1.1", + "case-sensitive-paths-webpack-plugin": "1.1.4", "chalk": "1.1.3", "classnames": "2.2.0", "clipboard": "1.5.5", + "connect-history-api-fallback": "1.3.0", "cross-env": "2.0.0", + "cross-spawn": "4.0.0", "css-loader": "0.23.1", "d3": "3.5.6", + "detect-port": "1.0.0", + "dotenv": "2.0.0", "enzyme": "2.2.0", "escape-html": "1.0.3", - "eslint": "^3.4.0", - "eslint-plugin-import": "^1.14.0", - "eslint-plugin-react": "^6.2.0", + "eslint": "3.5.0", + "eslint-config-react-app": "0.2.1", + "eslint-loader": "1.5.0", + "eslint-plugin-flowtype": "2.18.1", + "eslint-plugin-import": "1.12.0", + "eslint-plugin-jsx-a11y": "2.2.2", + "eslint-plugin-react": "6.3.0", "expose-loader": "0.7.1", "express": "4.13.4", "express-http-proxy": "0.6.0", "extract-text-webpack-plugin": "1.0.1", + "file-loader": "0.9.0", + "filesize": "3.3.0", + "find-cache-dir": "0.1.1", + "fs-extra": "0.30.0", + "gzip-size": "3.0.0", "handlebars": "2.0.0", "handlebars-loader": "1.1.4", "history": "2.0.0", "imports-loader": "0.6.5", - "jest-cli": "15.1.0", + "jest": "15.1.1", "jquery": "2.2.0", + "json-loader": "0.5.4", "less": "2.7.1", "less-loader": "2.2.3", "lodash": "4.6.1", "moment": "2.10.6", "numeral": "1.5.3", + "path-exists": "2.1.0", "postcss-loader": "0.8.0", - "react": "15.0.1", - "react-addons-shallow-compare": "15.0.1", - "react-addons-test-utils": "15.0.1", - "react-dom": "15.0.1", + "react": "15.3.2", + "react-addons-shallow-compare": "15.3.2", + "react-addons-test-utils": "15.3.2", + "react-dev-utils": "0.2.1", + "react-dom": "15.3.2", "react-helmet": "3.1.0", "react-redux": "4.4.1", - "react-router": "2.0.1", + "react-router": "2.8.1", "react-router-redux": "4.0.2", "react-select": "1.0.0-beta12", "react-transform-hmr": "1.0.4", "react-virtualized": "8.1.1", + "recursive-readdir": "2.1.0", "redux": "3.3.1", "redux-logger": "2.2.1", "redux-simple-router": "1.0.1", "redux-thunk": "1.0.2", "rimraf": "2.5.4", "script-loader": "0.6.1", + "strip-ansi": "3.0.1", "style-loader": "0.13.0", "underscore": "1.8.3", - "webpack": "1.13.0", - "webpack-dev-middleware": "1.6.1", - "webpack-hot-middleware": "2.10.0", - "whatwg-fetch": "0.10.0" + "webpack": "1.13.2", + "webpack-dev-server": "1.16.1", + "whatwg-fetch": "1.0.0" }, "scripts": { - "start": "node ./scripts/start.js", - "build-fast": "node ./scripts/build.js --fast", - "build": "node ./scripts/build.js", - "test": "cross-env NODE_ENV=test jest", + "start": "node scripts/start.js", + "build-fast": "node scripts/build.js --fast", + "build": "node scripts/build.js", + "test": "node scripts/test.js", "coverage": "npm test -- --coverage", "lint": "eslint src/main/js" }, @@ -90,15 +101,33 @@ "/node_modules", "/tests" ], + "moduleFileExtensions": [ + "jsx", + "js", + "json" + ], "moduleNameMapper": { - "^[./a-zA-Z0-9$_-]+\\.(css|hbs)": "/tests/FileStub.js" + "^.+\\.(hbs|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/config/jest/FileStub.js", + "^.+\\.css$": "/config/jest/CSSStub.js" }, "setupFiles": [ - "/tests/SetupTestEnvironment.js" + "/config/polyfills.js", + "/config/jest/SetupTestEnvironment.js" ], "testPathIgnorePatterns": [ "/node_modules", "/src/main/webapp" ] + }, + "babel": { + "presets": [ + "react-app" + ], + "ignore": [ + "**/libs/**" + ] + }, + "eslintConfig": { + "extends": "react-app" } } diff --git a/server/sonar-web/scripts/build.js b/server/sonar-web/scripts/build.js index 446d45a4d6f..c73a5a9559f 100644 --- a/server/sonar-web/scripts/build.js +++ b/server/sonar-web/scripts/build.js @@ -37,8 +37,8 @@ var config = isFastBuild ? // if you're in it, you don't end up in Trash console.log(chalk.cyan.bold('Cleaning output directories...')); -console.log(paths.jsBuild + '/*'); -rimrafSync(paths.jsBuild + '/*'); +console.log(paths.appBuild + '/*'); +rimrafSync(paths.appBuild + '/*'); console.log(paths.cssBuild + '/*'); rimrafSync(paths.cssBuild + '/*'); diff --git a/server/sonar-web/scripts/start.js b/server/sonar-web/scripts/start.js index 905793318e2..90cd204e9d1 100644 --- a/server/sonar-web/scripts/start.js +++ b/server/sonar-web/scripts/start.js @@ -19,136 +19,263 @@ */ process.env.NODE_ENV = 'development'; -var url = require('url'); -var express = require('express'); -var proxy = require('express-http-proxy'); -var webpack = require('webpack'); -var chalk = require('chalk'); -var config = require('../config/webpack/webpack.config.dev.js'); - -var app = express(); +// Load environment variables from .env file. Surpress warnings using silent +// if this file is missing. dotenv will never modify any environment variables +// that have already been set. +// https://github.com/motdotla/dotenv +require('dotenv').config({ silent: true }); -var DEFAULT_PORT = process.env.PORT || 8080; -var API_HOST = process.env.API_HOST || 'http://localhost:9000'; +var chalk = require('chalk'); +var webpack = require('webpack'); +var WebpackDevServer = require('webpack-dev-server'); +var historyApiFallback = require('connect-history-api-fallback'); +var httpProxyMiddleware = require('http-proxy-middleware'); +var detect = require('detect-port'); +var clearConsole = require('react-dev-utils/clearConsole'); +var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); +var formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); +var prompt = require('react-dev-utils/prompt'); +var config = require('../config/webpack/webpack.config.dev'); +var paths = require('../config/paths'); +// Tools like Cloud9 rely on this. +var DEFAULT_PORT = process.env.PORT || 3000; var compiler; +var handleCompile; -var friendlySyntaxErrorLabel = 'Syntax error:'; +var PROXY_URL = 'http://localhost:9000'; -function isLikelyASyntaxError (message) { - return message.indexOf(friendlySyntaxErrorLabel) !== -1; -} - -// This is a little hacky. -// It would be easier if webpack provided a rich error object. - -function formatMessage (message) { - return message - // Make some common errors shorter: - .replace( - // Babel syntax error - 'Module build failed: SyntaxError:', - friendlySyntaxErrorLabel - ) - .replace( - // Webpack file not found error - /Module not found: Error: Cannot resolve 'file' or 'directory'/, - 'Module not found:' - ) - // Internal stacks are generally useless so we strip them - .replace(/^\s*at\s.*:\d+:\d+[\s\)]*\n/gm, '') // at ... ...:x:y - // Webpack loader names obscure CSS filenames - .replace('./~/css-loader!./~/postcss-loader!', ''); +// You can safely remove this after ejecting. +// We only use this block for testing of Create React App itself: +var isSmokeTest = process.argv.some(arg => arg.indexOf('--smoke-test') > -1); +if (isSmokeTest) { + handleCompile = function (err, stats) { + if (err || stats.hasErrors() || stats.hasWarnings()) { + process.exit(1); + } else { + process.exit(0); + } + }; } -function setupCompiler () { - compiler = webpack(config); +function setupCompiler (host, port, protocol) { + // "Compiler" is a low-level interface to Webpack. + // It lets us listen to some events and provide our own custom messages. + compiler = webpack(config, handleCompile); + // "invalid" event fires when you have changed a file, and Webpack is + // recompiling a bundle. WebpackDevServer takes care to pause serving the + // bundle, so if you refresh, it'll wait instead of serving the old one. + // "invalid" is short for "bundle invalidated", it doesn't imply any errors. compiler.plugin('invalid', function () { - console.log(chalk.cyan.bold('Compiling...')); + clearConsole(); + console.log('Compiling...'); }); + // "done" event fires when Webpack has finished recompiling the bundle. + // Whether or not you have warnings or errors, you will get this event. compiler.plugin('done', function (stats) { - var hasErrors = stats.hasErrors(); - var hasWarnings = stats.hasWarnings(); - if (!hasErrors && !hasWarnings) { - console.log(chalk.green.bold('Compiled successfully!')); - return; - } + clearConsole(); - var json = stats.toJson(); - var formattedErrors = json.errors.map(message => - 'Error in ' + formatMessage(message) - ); - var formattedWarnings = json.warnings.map(message => - 'Warning in ' + formatMessage(message) - ); + // We have switched off the default Webpack output in WebpackDevServer + // options so we are going to "massage" the warnings and errors and present + // them in a readable focused way. + var messages = formatWebpackMessages(stats.toJson({}, true)); + if (!messages.errors.length && !messages.warnings.length) { + console.log(chalk.green('Compiled successfully!')); + console.log(); + console.log('The app is running at:'); + console.log(); + console.log(' ' + chalk.cyan(protocol + '://' + host + ':' + port + '/')); + console.log(); + console.log('Note that the development build is not optimized.'); + console.log('To create a production build, use ' + chalk.cyan('npm run build') + '.'); + console.log(); + } - if (hasErrors) { - console.log(chalk.red.bold('Failed to compile:')); + // If errors exist, only show errors. + if (messages.errors.length) { + console.log(chalk.red('Failed to compile.')); console.log(); - if (formattedErrors.some(isLikelyASyntaxError)) { - // If there are any syntax errors, show just them. - // This prevents a confusing ESLint parsing error - // preceding a much more useful Babel syntax error. - formattedErrors = formattedErrors.filter(isLikelyASyntaxError); - } - formattedErrors.forEach(message => { + messages.errors.forEach(message => { console.log(message); console.log(); }); - // If errors exist, ignore warnings. return; } - if (hasWarnings) { + // Show warnings if no errors were found. + if (messages.warnings.length) { console.log(chalk.yellow('Compiled with warnings.')); console.log(); - formattedWarnings.forEach(message => { + messages.warnings.forEach(message => { console.log(message); console.log(); }); - + // Teach some ESLint tricks. console.log('You may use special comments to disable some warnings.'); - console.log('Use ' + chalk.yellow( - '// eslint-disable-next-line') + ' to ignore the next line.'); - console.log('Use ' + chalk.yellow( - '/* eslint-disable */') + ' to ignore all warnings in a file.'); + console.log('Use ' + chalk.yellow('// eslint-disable-next-line') + ' to ignore the next line.'); + console.log('Use ' + chalk.yellow('/* eslint-disable */') + ' to ignore all warnings in a file.'); } }); } -function runDevServer (port) { - app.use(require('webpack-dev-middleware')(compiler, { - noInfo: true, - publicPath: config.output.publicPath - })); - - app.use(require('webpack-hot-middleware')(compiler)); +// We need to provide a custom onError function for httpProxyMiddleware. +// It allows us to log custom error messages on the console. +function onProxyError (proxy) { + return function (err, req, res) { + var host = req.headers && req.headers.host; + console.log( + chalk.red('Proxy error:') + ' Could not proxy request ' + chalk.cyan(req.url) + + ' from ' + chalk.cyan(host) + ' to ' + chalk.cyan(proxy) + '.' + ); + console.log( + 'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' + + chalk.cyan(err.code) + ').' + ); + console.log(); - app.all('*', proxy(API_HOST, { - forwardPath: function (req) { - return url.parse(req.url).path; + // And immediately send the proper error response to the client. + // Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side. + if (res.writeHead && !res.headersSent) { + res.writeHead(500); } + res.end('Proxy error: Could not proxy request ' + req.url + ' from ' + + host + ' to ' + proxy + ' (' + err.code + ').' + ); + } +} + +function addMiddleware (devServer) { + // `proxy` lets you to specify a fallback server during development. + // Every unrecognized request will be forwarded to it. + var proxy = PROXY_URL; + devServer.use(historyApiFallback({ + // Paths with dots should still use the history fallback. + // See https://github.com/facebookincubator/create-react-app/issues/387. + disableDotRule: true, + // For single page apps, we generally want to fallback to /index.html. + // However we also want to respect `proxy` for API calls. + // So if `proxy` is specified, we need to decide which fallback to use. + // We use a heuristic: if request `accept`s text/html, we pick /index.html. + // Modern browsers include text/html into `accept` header when navigating. + // However API calls like `fetch()` won’t generally accept text/html. + // If this heuristic doesn’t work well for you, don’t use `proxy`. + htmlAcceptHeaders: proxy ? + ['text/html'] : + ['text/html', '*/*'] })); + if (proxy) { + if (typeof proxy !== 'string') { + console.log(chalk.red('When specified, "proxy" in package.json must be a string.')); + console.log(chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".')); + console.log(chalk.red('Either remove "proxy" from package.json, or make it a string.')); + process.exit(1); + } + + // Otherwise, if proxy is specified, we will let it handle any request. + // There are a few exceptions which we won't send to the proxy: + // - /*.hot-update.json (WebpackDevServer uses this too for hot reloading) + // - /sockjs-node/* (WebpackDevServer uses this for hot reloading) + // Tip: use https://jex.im/regulex/ to visualize the regex + var mayProxy = /^(?!\/(.*\.hot-update\.json$|sockjs-node\/)).*$/; + devServer.use(mayProxy, + // Pass the scope regex both to Express and to the middleware for proxying + // of both HTTP and WebSockets to work without false positives. + httpProxyMiddleware(pathname => mayProxy.test(pathname), { + target: proxy, + logLevel: 'silent', + onError: onProxyError(proxy), + secure: false, + changeOrigin: true + }) + ); + } + // Finally, by now we have certainly resolved the URL. + // It may be /index.html, so let the dev server try serving it again. + devServer.use(devServer.middleware); +} + +function runDevServer (host, port, protocol) { + var devServer = new WebpackDevServer(compiler, { + // Silence WebpackDevServer's own logs since they're generally not useful. + // It will still show compile warnings and errors with this setting. + clientLogLevel: 'none', + // By default WebpackDevServer serves physical files from current directory + // in addition to all the virtual build products that it serves from memory. + // This is confusing because those files won’t automatically be available in + // production build folder unless we copy them. However, copying the whole + // project directory is dangerous because we may expose sensitive files. + // Instead, we establish a convention that only files in `public` directory + // get served. Our build script will copy `public` into the `build` folder. + // In `index.html`, you can get URL of `public` folder with %PUBLIC_PATH%: + // + // In JavaScript code, you can access it with `process.env.PUBLIC_URL`. + // Note that we only recommend to use `public` folder as an escape hatch + // for files like `favicon.ico`, `manifest.json`, and libraries that are + // for some reason broken when imported through Webpack. If you just want to + // use an image, put it in `src` and `import` it from JavaScript instead. + contentBase: paths.appPublic, + // Enable hot reloading server. It will provide /sockjs-node/ endpoint + // for the WebpackDevServer client so it can learn when the files were + // updated. The WebpackDevServer client is included as an entry point + // in the Webpack development configuration. Note that only changes + // to CSS are currently hot reloaded. JS changes will refresh the browser. + hot: true, + // It is important to tell WebpackDevServer to use the same "root" path + // as we specified in the config. In development, we always serve from /. + publicPath: config.output.publicPath, + // WebpackDevServer is noisy by default so we emit custom message instead + // by listening to the compiler events with `compiler.plugin` calls above. + quiet: true, + // Reportedly, this avoids CPU overload on some systems. + // https://github.com/facebookincubator/create-react-app/issues/293 + watchOptions: { + ignored: /node_modules/ + }, + // Enable HTTPS if the HTTPS environment variable is set to 'true' + https: protocol === "https", + host: host + }); + + // Our custom middleware proxies requests to /index.html or a remote API. + addMiddleware(devServer); - app.listen(port, 'localhost', function (err) { + // Launch WebpackDevServer. + devServer.listen(port, (err, result) => { if (err) { - console.log(err); - return; + return console.log(err); } - console.log(chalk.green.bold( - 'The app is running at http://localhost:' + port + '/')); - console.log(chalk.cyan.bold('Compiling...')); + clearConsole(); + console.log(chalk.cyan('Starting the development server...')); console.log(); }); } function run (port) { - setupCompiler(); - runDevServer(port); + var protocol = process.env.HTTPS === 'true' ? "https" : "http"; + var host = process.env.HOST || 'localhost'; + setupCompiler(host, port, protocol); + runDevServer(host, port, protocol); } -run(DEFAULT_PORT); +// We attempt to use the default port but if it is busy, we offer the user to +// run on a different port. `detect()` Promise resolves to the next free port. +detect(DEFAULT_PORT).then(port => { + if (port === DEFAULT_PORT) { + run(port); + return; + } + + clearConsole(); + var question = + chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.') + + '\n\nWould you like to run the app on another port instead?'; + prompt(question, true).then(shouldChangePort => { + if (shouldChangePort) { + run(port); + } + }); +}); diff --git a/server/sonar-web/scripts/test.js b/server/sonar-web/scripts/test.js new file mode 100644 index 00000000000..8ec88828387 --- /dev/null +++ b/server/sonar-web/scripts/test.js @@ -0,0 +1,38 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +process.env.NODE_ENV = 'test'; +process.env.PUBLIC_URL = ''; + +// Load environment variables from .env file. Surpress warnings using silent +// if this file is missing. dotenv will never modify any environment variables +// that have already been set. +// https://github.com/motdotla/dotenv +require('dotenv').config({ silent: true }); + +const jest = require('jest'); +const argv = process.argv.slice(2); + +// Watch unless on CI +if (!process.env.CI) { + argv.push('--watch'); +} + +jest.run(argv); + diff --git a/server/sonar-web/src/main/js/apps/background-tasks/__tests__/background-tasks-test.js b/server/sonar-web/src/main/js/apps/background-tasks/__tests__/background-tasks-test.js index b8561256a60..9c882e0ce88 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/__tests__/background-tasks-test.js +++ b/server/sonar-web/src/main/js/apps/background-tasks/__tests__/background-tasks-test.js @@ -23,7 +23,7 @@ import Stats from '../components/Stats'; import Search from '../components/Search'; import { STATUSES, CURRENTS, DEBOUNCE_DELAY, DEFAULT_FILTERS } from '../constants'; import { formatDuration } from '../utils'; -import { change, click } from '../../../../../../tests/utils'; +import { change, click } from '../../../helpers/testUtils'; const stub = jest.fn(); diff --git a/server/sonar-web/src/main/js/apps/projects/store/actions.js b/server/sonar-web/src/main/js/apps/projects/store/actions.js index 0f9328f00cc..5c0f3f8b2e6 100644 --- a/server/sonar-web/src/main/js/apps/projects/store/actions.js +++ b/server/sonar-web/src/main/js/apps/projects/store/actions.js @@ -18,7 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import groupBy from 'lodash/groupBy'; -import keyBy from 'lodash/keyBy'; import { searchProjects } from '../../../api/components'; import { addGlobalErrorMessage } from '../../../components/store/globalMessages'; import { parseError } from '../../code/utils'; diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ThresholdInput-test.js b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ThresholdInput-test.js index 2fdfc7fd9f0..9c332278dc0 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ThresholdInput-test.js +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ThresholdInput-test.js @@ -21,7 +21,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import Select from 'react-select'; import ThresholdInput from '../ThresholdInput'; -import { change } from '../../../../../../../tests/utils'; +import { change } from '../../../../helpers/testUtils'; describe('on strings', () => { it('should render text input', () => { diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogSearch-test.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogSearch-test.js index bbeb1a01c93..f7409cd0be0 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogSearch-test.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogSearch-test.js @@ -21,7 +21,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import ChangelogSearch from '../ChangelogSearch'; import DateInput from '../../../../components/controls/DateInput'; -import { click } from '../../../../../../../tests/utils'; +import { click } from '../../../../helpers/testUtils'; it('should render DateInput', () => { const onFromDateChange = jest.fn(); diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js index 4c52fd60c22..8c46db57211 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js @@ -20,7 +20,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import InputForPassword from '../InputForPassword'; -import { click, submit, change } from '../../../../../../../../tests/utils'; +import { click, change, submit } from '../../../../../helpers/testUtils'; it('should render lock icon, but no form', () => { const onChange = jest.fn(); diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForText-test.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForText-test.js index 326f58058d5..1c2053d42ec 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForText-test.js +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForText-test.js @@ -20,7 +20,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import InputForText from '../InputForText'; -import { change } from '../../../../../../../../tests/utils'; +import { change } from '../../../../../helpers/testUtils'; it('should render textarea', () => { const onChange = jest.fn(); diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/MultiValueInput-test.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/MultiValueInput-test.js index 1e52f7a0018..a24bfc27afc 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/MultiValueInput-test.js +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/MultiValueInput-test.js @@ -21,7 +21,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import MultiValueInput from '../MultiValueInput'; import PrimitiveInput from '../PrimitiveInput'; -import { click } from '../../../../../../../../tests/utils'; +import { click } from '../../../../../helpers/testUtils'; const definition = { multiValues: true }; diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/SimpleInput-test.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/SimpleInput-test.js index 1637dd18f30..99b12e9dbc2 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/SimpleInput-test.js +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/SimpleInput-test.js @@ -20,7 +20,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import SimpleInput from '../SimpleInput'; -import { change } from '../../../../../../../../tests/utils'; +import { change } from '../../../../../helpers/testUtils'; it('should render input', () => { const onChange = jest.fn(); diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.js b/server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.js index 10509ca66a2..191d4a105c4 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.js +++ b/server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.js @@ -20,7 +20,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import Checkbox from '../Checkbox'; -import { click } from '../../../../../../tests/utils'; +import { click } from '../../../helpers/testUtils'; it('should render unchecked', () => { const checkbox = shallow( true}/>); diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBase-test.js b/server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBase-test.js index 475c7a1b392..4ca43d9e78c 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBase-test.js +++ b/server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBase-test.js @@ -20,7 +20,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import FavoriteBase from '../FavoriteBase'; -import { click } from '../../../../../../tests/utils'; +import { click } from '../../../helpers/testUtils'; function renderFavoriteBase (props) { return shallow( diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.js b/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.js index 727938d46bd..d9e85ae9d9f 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.js +++ b/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.js @@ -20,7 +20,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import ListFooter from '../ListFooter'; -import { click } from '../../../../../../tests/utils'; +import { click } from '../../../helpers/testUtils'; it('should render "3 of 5 shown"', () => { const listFooter = shallow(); diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/RadioToggle-test.js b/server/sonar-web/src/main/js/components/controls/__tests__/RadioToggle-test.js index 47955d43ce0..e948fa9dc5e 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/RadioToggle-test.js +++ b/server/sonar-web/src/main/js/components/controls/__tests__/RadioToggle-test.js @@ -20,7 +20,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import RadioToggle from '../RadioToggle'; -import { change } from '../../../../../../tests/utils'; +import { change } from '../../../helpers/testUtils'; function getSample (props) { const options = [ diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js b/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js index cf6c992edca..dfa031bf96d 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js +++ b/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js @@ -20,7 +20,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import Toggle from '../Toggle'; -import { click } from '../../../../../../tests/utils'; +import { click } from '../../../helpers/testUtils'; function getSample (props) { return ( diff --git a/server/sonar-web/src/main/js/helpers/testUtils.js b/server/sonar-web/src/main/js/helpers/testUtils.js new file mode 100644 index 00000000000..03506208367 --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/testUtils.js @@ -0,0 +1,39 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +export const click = element => { + return element.simulate('click', { + target: { blur () {} }, + currentTarget: { blur () {} }, + preventDefault () {} + }); +}; + +export const submit = element => { + return element.simulate('submit', { + preventDefault () {} + }); +}; + +export const change = (element, value) => { + return element.simulate('change', { + target: { value }, + currentTarget: { value } + }); +}; diff --git a/server/sonar-web/src/main/js/main/app.js b/server/sonar-web/src/main/js/main/app.js index 6e41663d2b8..fecb0b26e47 100644 --- a/server/sonar-web/src/main/js/main/app.js +++ b/server/sonar-web/src/main/js/main/app.js @@ -17,11 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import 'babel-polyfill'; import $ from 'jquery'; import _ from 'underscore'; import Backbone from 'backbone'; -import 'whatwg-fetch'; import moment from 'moment'; import './processes'; import Navigation from './nav/app'; diff --git a/server/sonar-web/tests/FileStub.js b/server/sonar-web/tests/FileStub.js deleted file mode 100644 index bff56dc8933..00000000000 --- a/server/sonar-web/tests/FileStub.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -module.exports = 'test-file-stub'; diff --git a/server/sonar-web/tests/SetupTestEnvironment.js b/server/sonar-web/tests/SetupTestEnvironment.js deleted file mode 100644 index 35829391382..00000000000 --- a/server/sonar-web/tests/SetupTestEnvironment.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -require('babel-polyfill'); - -window.baseUrl = ''; -window.t = window.tp = function () { - var args = Array.prototype.slice.call(arguments, 0); - return args.join('.'); -}; diff --git a/server/sonar-web/tests/utils.js b/server/sonar-web/tests/utils.js deleted file mode 100644 index 03506208367..00000000000 --- a/server/sonar-web/tests/utils.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -export const click = element => { - return element.simulate('click', { - target: { blur () {} }, - currentTarget: { blur () {} }, - preventDefault () {} - }); -}; - -export const submit = element => { - return element.simulate('submit', { - preventDefault () {} - }); -}; - -export const change = (element, value) => { - return element.simulate('change', { - target: { value }, - currentTarget: { value } - }); -};