Skip to content

Commit 6d7550a

Browse files
authored
HUL-48 | Modernize Development Environment and Testing Infrastructure (#49)
* fix: modernize SASS configuration and eliminate deprecation warnings - Update sass-loader to use modern API instead of legacy JS API - Add sass:math and sass:color module imports to SASS files - Replace deprecated ceil(), floor(), and darken() functions with modern equivalents - Configure sass-loader to suppress import deprecation warnings from bootstrap-sass - Add quietDeps and silenceDeprecations options to reduce build noise * chore: modernize test environment with React Testing Library - Add @testing-library/react and @testing-library/dom for modern component testing - Replace deprecated isparta-loader with babel-plugin-istanbul for code coverage - Update chai to v4.3.10 (CommonJS compatible version) - Remove legacy babel-core v5 and associated deprecated testing utilities - Clean up unused testing dependencies and modernize test infrastructure * chore: modernize webpack toolchain - Migrate from webpack-dotenv-plugin to dotenv-webpack for better webpack 5 compatibility - Update webpack-dev-server to v4 API with async/await pattern replacing deprecated callbacks - Modernize webpack development configuration: - Remove deprecated HMR plugins (HotModuleReplacementPlugin, NoEmitOnErrorsPlugin) - Update entry configuration for webpack 5 - Add modern optimization settings with emitOnErrors: false - Update devServer configuration with webpack-dev-server v4 API: - Add client overlay configuration - Update devMiddleware settings - Configure host and allowedHosts for broader accessibility - Add webpack 5 compatible babel-plugin-istanbul for code coverage - Fix ESLint configuration for Node.js environment compatibility * refactor: simplify and modernize Babel configuration - Remove 16 redundant Babel plugins now handled by @babel/preset-env: - Remove @babel/plugin-proposal-class-properties (ES2022 standard) - Remove @babel/plugin-proposal-json-strings (ES2019 standard) - Remove @babel/plugin-proposal-logical-assignment-operators (ES2021 standard) - Remove @babel/plugin-proposal-nullish-coalescing-operator (ES2020 standard) - Remove @babel/plugin-proposal-numeric-separator (ES2021 standard) - Remove @babel/plugin-proposal-optional-chaining (ES2020 standard) - Remove experimental/unused proposal plugins for do-expressions, export-default-from, export-namespace-from, function-bind, function-sent, pipeline-operator, throw-expressions - Remove syntax plugins covered by preset-env (dynamic-import, import-meta) - Replace deprecated @babel/polyfill with modern core-js/stable import - Keep only essential Babel packages: - @babel/plugin-proposal-decorators (legacy mode still needed) - Core Babel packages (@babel/core, presets, eslint-parser) - Simplify .babelrc to use only presets + decorators plugin - Update package.json and yarn.lock to reflect reduced dependency footprint * refactor: modernize webpack asset handling and reduce dependencies - Replace file-loader with Webpack 5 native asset/resource modules - Remove unused url-loader dependency - Update webpack configuration to use modern asset handling for mp4/ogg/svg files * chore: clean up redundant dependencies and polyfills - Remove es6-promise and isomorphic-fetch as they're redundant with core-js - Move sass-loader from dependencies to devDependencies where it belongs - Remove unnecessary polyfill imports from index.js * feat: migrate from Karma to Jest test framework - Replace Karma test runner with Jest 30.2.0 and jsdom environment - Add comprehensive Jest configuration with module mapping and coverage - Convert test syntax from Mocha/Chai to Jest expectations - Remove 9+ legacy Karma packages (karma, karma-*, chai, mocha, puppeteer) - Configure V8 coverage provider to avoid Istanbul compatibility issues - Update ESLint environment from mocha to jest - Modernize test setup with @testing-library/jest-dom matchers * fix: resolve webpack NODE_ENV conflicts and improve Docker compatibility - Remove redundant DefinePlugin NODE_ENV definitions from webpack configs - Use webpack mode setting for NODE_ENV (production for build, development for dev) - Configure dotenv-webpack with safe=false and defaults for Docker builds - Update @babel/core to ^7.22.0 to resolve plugin compatibility issues * chore: update rimraf to v6.1.2 - Updates rimraf from 5.0.10 to 6.1.2 to resolve glob security vulnerability. Fixes high severity command injection issue in transitive dependency. * chore: upgrade eslint to v9.39.2 - Updates eslint from 8.57.0 to 9.39.2 for latest security patches, performance improvements, and bug fixes. * fix: resolve security vulnerabilities with npm audit fix Automatically fixes 8 vulnerabilities: - @babel/runtime: RegExp complexity issue - brace-expansion: RegExp denial of service - glob: Command injection vulnerability - nanoid: Predictable generation results - node-forge: ASN.1 vulnerabilities - on-headers: HTTP header manipulation - webpack: DOM clobbering XSS * fix: resolve @babel/runtime security vulnerability Add yarn resolution to force @babel/runtime ^7.26.10 to fix RegExp complexity vulnerability in transitive dependency from history. * refactor: modernize test structure and Jest configuration - Move component tests to co-located __tests__ directories - Rename test files to follow .test.js/.test.jsx convention - Simplify Jest testMatch patterns using glob syntax - Remove empty .keep files from unused test directories * feat: preserve dist/README.md in clean script - Update clean script to use find command instead of rimraf to selectively remove all files in dist/ directory except README.md, preventing accidental removal of version-controlled documentation. * test: add unit tests for Redux reducers * fix: update API_URL for development environment
1 parent 2771282 commit 6d7550a

30 files changed

Lines changed: 15109 additions & 11152 deletions

.babelrc

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,11 @@
44
"@babel/preset-react"
55
],
66
"plugins": [
7-
"@babel/plugin-syntax-dynamic-import",
8-
"@babel/plugin-syntax-import-meta",
9-
"@babel/plugin-proposal-class-properties",
10-
"@babel/plugin-proposal-json-strings",
117
[
128
"@babel/plugin-proposal-decorators",
139
{
1410
"legacy": true
1511
}
16-
],
17-
"@babel/plugin-proposal-function-sent",
18-
"@babel/plugin-proposal-export-namespace-from",
19-
"@babel/plugin-proposal-numeric-separator",
20-
"@babel/plugin-proposal-throw-expressions",
21-
"@babel/plugin-proposal-export-default-from",
22-
"@babel/plugin-proposal-logical-assignment-operators",
23-
"@babel/plugin-proposal-optional-chaining",
24-
[
25-
"@babel/plugin-proposal-pipeline-operator",
26-
{
27-
"proposal": "minimal"
28-
}
29-
],
30-
"@babel/plugin-proposal-nullish-coalescing-operator",
31-
"@babel/plugin-proposal-do-expressions",
32-
"@babel/plugin-proposal-function-bind"
12+
]
3313
]
3414
}

.eslintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"amd": true,
1616
"es6": true,
1717
"node": true,
18-
"mocha": true
18+
"jest": true
1919
},
2020
"rules": {
2121
"comma-dangle": 1,

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ RUN npm install
1616
FROM appbase AS development
1717
# =======================================================
1818

19-
ARG API_URL=https://palvelukartta-api-test.agw.arodevtest.hel.fi/v2
19+
ARG API_URL=https://palvelukartta.api.test.hel.ninja/v2
2020
ENV API_URL $API_URL
2121

2222
COPY . .

cfg/base.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-undef */
12
'use strict';
23
let path = require('path');
34
let webpack = require('webpack');
@@ -18,6 +19,12 @@ module.exports = {
1819
filename: 'app.js',
1920
publicPath: defaultSettings.publicPath
2021
},
22+
ignoreWarnings: [
23+
{
24+
module: /\.scss$/,
25+
message: /Future import deprecation is not yet active, so silencing it is unnecessary/,
26+
},
27+
],
2128
plugins: [
2229
require('autoprefixer'),
2330
new webpack.LoaderOptionsPlugin({
@@ -35,14 +42,19 @@ module.exports = {
3542
historyApiFallback: true,
3643
hot: true,
3744
port: defaultSettings.port,
45+
client: {
46+
overlay: true
47+
},
3848
devMiddleware: {
3949
publicPath: defaultSettings.publicPath,
40-
}
50+
},
51+
host: '0.0.0.0',
52+
allowedHosts: 'all'
4153
},
4254
resolve: {
43-
extensions: ['', '.js', '.jsx'],
55+
extensions: ['.js', '.jsx'],
4456
fallback: {
45-
"process": require.resolve("process/browser")
57+
"process": require.resolve("process/browser.js")
4658
},
4759
alias: {
4860
actions: `${defaultSettings.srcPath}/actions/`,

cfg/defaults.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-undef */
12
/**
23
* Function that returns default values.
34
* Used because Object.assign does a shallow instead of a deep copy.
@@ -26,9 +27,11 @@ function getDefaultModules() {
2627
use: ['style-loader', 'css-loader', {
2728
loader: 'sass-loader',
2829
options: {
30+
api: 'modern-compiler',
2931
sassOptions: {
3032
outputStyle: 'expanded',
31-
33+
quietDeps: true,
34+
silenceDeprecations: ['import']
3235
}
3336
}
3437
}
@@ -39,9 +42,11 @@ function getDefaultModules() {
3942
use: ['style-loader', 'css-loader', {
4043
loader: 'sass-loader',
4144
options: {
45+
api: 'modern-compiler',
4246
sassOptions: {
4347
outputStyle: 'expanded',
44-
48+
quietDeps: true,
49+
silenceDeprecations: ['import']
4550
}
4651
}
4752
}
@@ -57,7 +62,7 @@ function getDefaultModules() {
5762
},
5863
{
5964
test: /\.(mp4|ogg|svg)$/,
60-
use: 'file-loader'
65+
type: 'asset/resource'
6166
}
6267
]
6368
};

cfg/dev.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-undef */
12
'use strict';
23

34
let path = require('path');
@@ -7,29 +8,26 @@ let defaultSettings = require('./defaults');
78

89
// Add needed plugins here
910
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
10-
const DotenvPlugin = require('webpack-dotenv-plugin');
11+
const Dotenv = require('dotenv-webpack');
1112

1213
let config = Object.assign({}, baseConfig, {
1314
entry: [
14-
'webpack-dev-server/client?http://127.0.0.1:' + defaultSettings.port,
15-
'webpack/hot/only-dev-server',
1615
'./src/index'
1716
],
1817
cache: true,
1918
devtool: 'eval-source-map',
19+
optimization: {
20+
emitOnErrors: false
21+
},
2022
plugins: [
21-
new webpack.HotModuleReplacementPlugin(),
22-
new webpack.NoEmitOnErrorsPlugin(),
23-
new webpack.ProvidePlugin({
24-
$: "jquery",
25-
jQuery: "jquery"
26-
}),
27-
new DotenvPlugin({
28-
sample: './.env.example',
29-
path: './.env'
23+
new webpack.ProvidePlugin({
24+
$: "jquery",
25+
jQuery: "jquery"
3026
}),
31-
new webpack.LoaderOptionsPlugin({
32-
debug: true
27+
new Dotenv({
28+
path: './.env',
29+
safe: './.env.example',
30+
ignoreStub: true
3331
}),
3432
new ReactRefreshWebpackPlugin()
3533
],

cfg/dist.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-undef */
12
'use strict';
23

34
let path = require('path');
@@ -8,25 +9,27 @@ let defaultSettings = require('./defaults');
89

910
// Add needed plugins here
1011
const TerserPlugin = require("terser-webpack-plugin");
11-
const DotenvPlugin = require('webpack-dotenv-plugin');
12+
const Dotenv = require('dotenv-webpack');
1213

1314
let config = Object.assign({}, baseConfig, {
15+
mode: 'production',
1416
entry: path.join(__dirname, '../src/index'),
1517
cache: false,
1618
devtool: 'source-map',
1719
plugins: [
18-
new webpack.DefinePlugin({
19-
'process.env.NODE_ENV': '"production"'
20+
new Dotenv({
21+
path: './.env',
22+
safe: false,
23+
systemvars: true,
24+
defaults: {
25+
'API_URL': 'https://api.hel.fi/servicemap/v2'
26+
}
2027
}),
2128
new webpack.optimize.AggressiveMergingPlugin(),
2229
new webpack.NoEmitOnErrorsPlugin(),
2330
new webpack.ProvidePlugin({
2431
$: "jquery",
2532
jQuery: "jquery"
26-
}),
27-
new DotenvPlugin({
28-
sample: './.env.example',
29-
path: './.env'
3033
})
3134
],
3235
optimization: {

cfg/test.js

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
1+
/* eslint-disable no-undef */
12
'use strict';
23

34
let path = require('path');
4-
let srcPath = path.join(__dirname, '/../src/');
5+
let webpack = require('webpack');
56

67
let baseConfig = require('./base');
78

89
module.exports = {
10+
...baseConfig,
911
devtool: 'eval',
1012
module: {
1113
rules: [
12-
{
13-
test: /\.(js|jsx)$/,
14-
loader: 'isparta',
15-
include: [
16-
path.join(__dirname, '/../src')
17-
]
18-
},
1914
{
2015
test: /\.(png|jpg|gif|woff|woff2|css|sass|scss|less|styl)$/,
2116
loader: 'null-loader'
@@ -28,21 +23,24 @@ module.exports = {
2823
path.join(__dirname, '/../src'),
2924
path.join(__dirname, '/../test')
3025
]
31-
)
26+
),
27+
options: {
28+
presets: ['@babel/preset-env', '@babel/preset-react'],
29+
plugins: ['babel-plugin-istanbul']
30+
}
3231
}
3332
]
3433
},
3534
resolve: {
36-
extensions: [ '', '.js', '.jsx' ],
35+
...baseConfig.resolve,
3736
alias: {
38-
actions: srcPath + 'actions/',
39-
helpers: path.join(__dirname, '/../test/helpers'),
40-
components: srcPath + 'components/',
41-
sources: srcPath + 'sources/',
42-
stores: srcPath + 'stores/',
43-
styles: srcPath + 'styles/',
44-
config: srcPath + 'config/' + process.env.REACT_WEBPACK_ENV
37+
...baseConfig.resolve.alias,
38+
helpers: path.join(__dirname, '/../test/helpers')
4539
}
4640
},
47-
plugins: []
41+
plugins: [
42+
new webpack.ProvidePlugin({
43+
process: 'process/browser.js',
44+
})
45+
]
4846
};

eslint.config.mjs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import js from '@eslint/js';
2+
import react from 'eslint-plugin-react';
3+
import babelParser from '@babel/eslint-parser';
4+
5+
export default [
6+
js.configs.recommended,
7+
{
8+
files: ['src/**/*.{js,jsx}', 'test/**/*.{js,jsx}'],
9+
plugins: {
10+
react,
11+
},
12+
languageOptions: {
13+
parser: babelParser,
14+
parserOptions: {
15+
ecmaVersion: 6,
16+
sourceType: 'module',
17+
requireConfigFile: false,
18+
ecmaFeatures: {
19+
jsx: true,
20+
},
21+
},
22+
globals: {
23+
// Node.js environment
24+
process: 'readonly',
25+
require: 'readonly',
26+
module: 'readonly',
27+
global: 'readonly',
28+
Buffer: 'readonly',
29+
__dirname: 'readonly',
30+
__filename: 'readonly',
31+
// Browser environment
32+
window: 'readonly',
33+
document: 'readonly',
34+
console: 'readonly',
35+
fetch: 'readonly',
36+
localStorage: 'readonly',
37+
navigator: 'readonly',
38+
setTimeout: 'readonly',
39+
clearTimeout: 'readonly',
40+
setInterval: 'readonly',
41+
clearInterval: 'readonly',
42+
// Test environment
43+
describe: 'readonly',
44+
it: 'readonly',
45+
beforeEach: 'readonly',
46+
afterEach: 'readonly',
47+
expect: 'readonly',
48+
},
49+
},
50+
rules: {
51+
'quotes': ['warn', 'single'],
52+
'no-console': 'off',
53+
'no-alert': 'off',
54+
'react/jsx-uses-react': 'warn',
55+
'react/jsx-uses-vars': 'warn',
56+
},
57+
},
58+
];

jest.config.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* eslint-disable no-undef */
2+
module.exports = {
3+
testEnvironment: 'jsdom',
4+
setupFilesAfterEnv: ['<rootDir>/test/setupTests.js'],
5+
testMatch: [
6+
'<rootDir>/{test,src}/**/__tests__/*.test.{js,jsx}'
7+
],
8+
moduleFileExtensions: ['js', 'jsx'],
9+
transform: {
10+
'^.+\\.(js|jsx)$': 'babel-jest',
11+
},
12+
moduleNameMapper: {
13+
'^components/(.*)$': '<rootDir>/src/components/$1',
14+
'^actions/(.*)$': '<rootDir>/src/actions/$1',
15+
'^sources/(.*)$': '<rootDir>/src/sources/$1',
16+
'^stores/(.*)$': '<rootDir>/src/stores/$1',
17+
'^styles/(.*)\\.(css|scss)$': 'identity-obj-proxy',
18+
'^styles/(.*)$': '<rootDir>/src/styles/$1',
19+
'^config$': '<rootDir>/src/config/test',
20+
'\\.(css|scss)$': 'identity-obj-proxy',
21+
'normalize.css/normalize.css': 'identity-obj-proxy'
22+
},
23+
moduleDirectories: ['node_modules', 'src'],
24+
collectCoverage: true,
25+
collectCoverageFrom: [
26+
'src/**/*.{js,jsx}',
27+
'!src/index.js',
28+
'!src/config/**',
29+
],
30+
coverageProvider: 'v8',
31+
coverageDirectory: 'coverage',
32+
coverageReporters: ['text', 'lcov', 'html'],
33+
transformIgnorePatterns: [
34+
'node_modules/(?!(redux-actions|@babel|bootstrap-sass)/)'
35+
],
36+
testPathIgnorePatterns: [
37+
'/node_modules/',
38+
'/dist/'
39+
]
40+
};

0 commit comments

Comments
 (0)